├── .gitignore ├── Resources └── Space.svg ├── .editorconfig ├── putman.sh ├── Documentation ├── Changelog │ ├── Version 1.md │ ├── Version 4.md │ ├── Version 2.md │ └── Version 3.md ├── Mails │ ├── 1987 January 1st C.md │ ├── 1987 January 1st B.md │ ├── 1987 January 8th.md │ ├── 1987 January 2nd A.md │ ├── 1987 January 3rd.md │ ├── 1986 December 31st A.md │ ├── 1987 January 5th A.md │ ├── 1987 January 5th C.md │ ├── 1987 January 2nd C.md │ ├── 1987 January 13th.md │ ├── 1987 January 1st A.md │ ├── 1986 December 31st B.md │ ├── 1987 January 2nd B.md │ ├── 1987 January 16th.md │ ├── 1987 January 5th B.md │ ├── 1987 January 4th.md │ └── 1987 January 2nd D.md ├── Changelog.md ├── Mail.md ├── Thanks.md ├── Configure.md ├── Installation.md ├── Conversion.md └── Features.md ├── LICENSE ├── cron.h ├── job.c ├── structs.h ├── globals.h ├── README.md ├── funcs.h ├── user.c ├── pathnames.h ├── crontab.1 ├── externs.h ├── cron.8 ├── config.h ├── pw_dup.c ├── macros.h ├── Makefile ├── bitstring.h ├── bitstring.3 ├── popen.c ├── env.c ├── crontab.5 ├── database.c ├── cron.c ├── do_command.c └── entry.c /.gitignore: -------------------------------------------------------------------------------- 1 | cron 2 | crontab 3 | kit 4 | *.o 5 | \#* 6 | -------------------------------------------------------------------------------- /Resources/Space.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | [*] 5 | 6 | end_of_line = lf 7 | 8 | [*.{Makefile,gitignore,c,h,md}] 9 | 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 4 14 | charset = utf-8 15 | 16 | [*.md] 17 | 18 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /putman.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # putman.sh - install a man page according to local custom 4 | # vixie 27dec93 [original] 5 | # 6 | # $Id: putman.sh,v 1.1 1996/12/16 19:39:48 halley Exp $ 7 | 8 | PAGE=$1 9 | DIR=$2 10 | 11 | SECT=`expr $PAGE : '[a-z]*.\([0-9]\)'` 12 | 13 | [ -d $DIR/man$SECT ] && { 14 | set -x 15 | cp $PAGE $DIR/man$SECT/$PAGE 16 | set +x 17 | } || { 18 | set -x 19 | nroff -man $PAGE >$DIR/cat$SECT/`basename $PAGE .$SECT`.0 20 | set +x 21 | } 22 | 23 | exit 0 24 | -------------------------------------------------------------------------------- /Documentation/Changelog/Version 1.md: -------------------------------------------------------------------------------- 1 | 2 | [][#] 3 | [][#] 4 | 5 |
6 | 7 | # Version 1 8 | 9 |
10 | 11 |
12 |
13 | 14 |
15 | 16 | ## 1.0 17 | 18 |   1987 | May 6th   19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 | 27 | [#]: # 28 | -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 1st C.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | **Date:**  5 | 1987 January 1st  6 | Thursday  7 | 10:53:05 PST
8 | **From:**  9 | lll-crg  10 | ames 11 | uw-beaver  12 | uw-nsr  13 | john (John Sambrook 5-7433)  
14 | **To:**  15 | vixie  16 | paul
17 | 18 | 19 | 20 | ### Message 21 | 22 | > How about not hardwiring the default
23 | > environment that cron builds for its
24 | > children in the cron program itself? 25 | > 26 | > Our cron does this and it's the pits because
27 | > we are TZ=PST8PDT not TZ=EST5EDT ! 28 | 29 | ### Reply 30 | 31 | > yeachk. I assure you, I will not hardwire the TZ! -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 1988,1990,1993,2021 by Paul Vixie ("VIXIE") 2 | Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 3 | Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 4 | 5 | Permission to use, copy, modify, and distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 15 | OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 1st B.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | **Date:**  5 | 1987 January 1st  6 | Thursday  7 | 19:22:47
8 | **From:**  9 | lll-lcc  10 | seismo  11 | c3pe  12 | c3engr  13 | charles (Charles Green)  
14 | **To:**  15 | c3pe  16 | decuac  17 | dolqci  18 | vrdxhq  19 | seismo  20 | lll-lcc  21 | vixie  22 | paul
23 | 24 | 25 | 26 | ### Message 27 | 28 | > Well, this isn't a compatible extension, but I have
29 | > in times past wondered about a facility to let you
30 | > start a process at intervals of, say, 17 minutes,
31 | > instead of particular minutes out of each hour. 32 | 33 | ### Reply 34 | 35 | > This was a popular request! -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 8th.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Date:**  4 | 1987 January 8th  5 | Thursday  6 | 23:50:40 PST
7 | **From:**  8 | well  9 | dv (David W. Vezie)
10 | **To:**  11 | ptsfa  12 | vixie  13 | paul
14 | 15 | 16 | 17 | 18 | ### Message 19 | 20 | > 6, have a special notation called 'H' which would
21 | > expand to weekends and holidays (you'd have to
22 | > keep a database somewhere of real holidays), and
23 | > also 'W' for workdays (neither weekend or holiday). 24 | 25 | ### Reply 26 | 27 | > Too much work. 28 | > 29 | > There should be a standard way to define and detect
30 | > holidays under Unix(TM); if there were, I'd use it. 31 | > 32 | > As it is, I'll leave this for someone else to add. 33 | > 34 | > I can see the usefulness; it just
35 | > doesn't quite seem worth it. -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 2nd A.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | **Date:**  5 | 1987 January 2nd  6 | Friday  7 | 09:23:53 CST
8 | **From:**  9 | lll-lcc  10 | seismo  11 | uwvax  12 | astroatc  13 | nicmad  14 | norvax  15 | mann (Tom Mann)  
16 | **To:**  17 | ptsfa  18 | vixie  19 | paul
20 | 21 | 22 | 23 | ### Message 24 | 25 | > I'm not sure if it is in cron (either SysV or BSD ...
26 | > if it is, I haven't figured it out ) but a comment
27 | > feature would SURE BE NICE!. 28 | > 29 | > There are times when I want to comment out an
30 | > entry for a period of time; it might also make it a
31 | > lot more legible. 32 | 33 | ### Reply 34 | 35 | > My cron allows blank lines and standard #-type comments. 36 | > 37 | > I know that one BSD4.2 cron I've used had it.
38 | > I don't know about SysV. -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 3rd.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Date:**  4 | 1987 January 3rd  5 | Saturday  6 | 14:05:13 PST
7 | **From:**  8 | dual  9 | ucbvax  10 | ihnp4  11 | mtuxo  12 | ender
13 | **To:**  14 | ucbvax  15 | dual  16 | ptsfa  17 | vixie  18 | paul
19 | 20 | 21 | 22 | ### Message 23 | 24 | > It would be nice if nonprivileged users can setup personal
25 | > crontab files (~/.cronrc, say) and be able to run personal
26 | > jobs at regular intervals. 27 | 28 | ### Reply 29 | 30 | > this is done, but in the SysV style: the 'crontab' program
31 | > installs a new crontab file for the executing user (can be
32 | > overridden by root for setup of uucp and news). 33 | > 34 | > the advantage of this is that (1) when a crontab is changed,
35 | > the daemon can be informed automatically; and (2) the file
36 | > can be syntax-checked before installation. -------------------------------------------------------------------------------- /Documentation/Mails/1986 December 31st A.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | **Date:**  5 | 1986 December 31  6 | Wednesday  7 | 08:59:31 PST
8 | **From:**  9 | lll-crg  10 | ames  11 | acornrc  12 | bob (Bob Weissman)
13 | **To:**  14 | ptsfa  15 | vixie  16 | paul
17 | 18 | 19 | 20 | ### Message 21 | 22 | > Sure, here's a suggestion: 23 | > 24 | > I'd like to be able to run a program, say, every two hours.
25 | > Current cron requires me to write
26 | > 27 | > 0,2,4,6,8,10,12,14,16,18,20,22 28 | > 29 | > in the hours field. 30 | > 31 | > How about a notation to handle this more elegantly? 32 | 33 | ### Reply 34 | 35 | > Okay, I've allowed 0-22/2 as a means of handling this. 36 | > 37 | > The time specification for my cron is as follows: 38 | > 39 | >    specification = range {"," range}
40 | >    range = (start "-" finish ["/" step]) | single-unit 41 | > 42 | > This allows "1,3,5-7", which the current cron doesn't
43 | > (it won't do a range inside a list), and handles your specific need. -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 5th A.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | **Date:**  5 | 1987 January 5th  6 | Monday  7 | 01:22:17 PST
8 | **From:**  9 | hoptoad  10 | hugh (Hugh Daniel)  
11 | **To:**  12 | ptsfa  13 | vixie  14 | paul
15 | 16 | 17 | 18 | ### Message 19 | 20 | > Hi, I do have a BIG one that I would like. 21 | > 22 | > I want to log ALL output from command
23 | > lines into a file for each line. 24 | > 25 | > Thus I might have a chance of finding
26 | > out why my crontab entry did not work. 27 | > 28 | > This would seem to work best if done by cron, as it
29 | > is now I have a google of shell scripts laying about
30 | > just to put the error output where I can see it. 31 | 32 | ### Reply 33 | 34 | > My cron (and the SysV cron) will send mail to the
35 | > owner of the particular crontab file if a command
36 | > generates any output on stdout or stderr. 37 | > 38 | > This can be irritating, but if you write a script such
39 | > that any output means a problem occurred, you
40 | > can avoid most logfile needs, and not generate
41 | > mail except in unforeseen circumstances. -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 5th C.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | **Date:**  10 | 1987 January 5th  11 | Monday  12 | 12:09:09 GMT
13 | **From:**  14 | guy gorodish@sun (Guy Harris)  
15 | **Message ID:**  16 | guy gorodish@sun (Guy Harris)  
17 | 18 | ### Message 19 | 20 | > Another thing which would be nice is to be
21 | > able to specify which shell to call to give the
22 | > command to. 23 | > 24 | > Well, the obvious choice would be the user's
25 | > shell, but this wouldn't work for accounts like
26 | > "uucico". 27 | 28 | ### Reply 29 | 30 | > I use the owning user's shell, and to handle
31 | > "uucico" I check a list of "acceptable shells"
32 | > (currently compiled in, does anybody mind?),
33 | > substituting a default (compiled in) shell if
34 | > the user's shell isn't on the list. 35 | > 36 | > BTW, "compiled in" means that it's in a .h
37 | > file, easily changed during installation,
38 | > but requiring recompilation to modify. 39 | > 40 | > You don't have to go digging through the code to find it... -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 2nd C.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | **Date:**  5 | 1987 January 2nd  6 | Friday  7 | 17:34:44 GMT
8 | **From:**  9 | pedz@bobkat.UUCP (Pedz Thing)  
10 | 11 | 12 | 13 | ### Message 14 | 15 | > Log files! 16 | > 17 | > It would be nice to be able to specify a log for cron
18 | > itself and also a log for each program's stdout and
19 | > stderr to go to. 20 | > 21 | > The latter can of course be done with > and 2> but
22 | > it would be nice if there could be a single line with
23 | > some sort of pattern like `> /usr/spool/log/%' and
24 | > the command would be substituted for the %. 25 | > 26 | > Another thing which would be nice is to be able to
27 | > specify which shell to call to give the command to. 28 | 29 | ### Reply 30 | 31 | > Log files are done with mail. 32 | > 33 | > The '%' idea could be useful if a different character
34 | > were used (% is special to cron, see man page); 35 | > 36 | > a different directory would have to be chosen, since
37 | > each user has their own crontab file; and something
38 | > intelligent would have to be done in the file naming,
39 | > since the first word of the command might be
40 | > ambiguous (with other commands). 41 | > 42 | > In short, it's too much work.
43 | > Sorry. -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 13th.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Date:**  4 | 1987 January 13th  5 | Tuesday  6 | 16:39:38 EST
7 | **From:**  8 | gatech  9 | akgua  10 | blnt1  11 | jat
12 | 13 | 14 | 15 | ### Message 16 | 17 | > 1) Add some way to tell cron to reread the files, say kill -1 18 | 19 | ### Reply 20 | 21 | > whenever the 'crontab' program is run and updates
22 | > a crontab file, a file /usr/spool/cron/POKECRON is
23 | > created; next time the cron daemon wakes up, it
24 | > sees it, and re-reads the crontab files. 25 | > 26 | > I thought of handling the signal; even implemented it. 27 | > 28 | > Then this clever idea hit me and I ripped
29 | > it all out and added a single IF-statement
30 | > to handle the POKECRON file. 31 | 32 | ### Message 33 | 34 | > 2) Have some kind of retry time so that if a command fails,
35 | > cron will try to execute it again after a certain period. 36 | > 37 | > This is useful if you have some type of cleanup program
38 | > that can run at the scheduled time for some reason
39 | > (such as locked device, unmounted filesystem, etc). 40 | 41 | ### Reply 42 | 43 | > Hmmm, sounds useful. 44 | > 45 | > I could do this by submitting an 'at' job...
46 | > I'll think about it. -------------------------------------------------------------------------------- /cron.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1988,1990,1993,1994,2021 by Paul Vixie ("VIXIE") 3 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 4 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* cron.h - header for vixie's cron 20 | * 21 | * $Id: cron.h,v 1.6 2004/01/23 18:56:42 vixie Exp $ 22 | * 23 | * vix 14nov88 [rest of log is in RCS] 24 | * vix 14jan87 [0 or 7 can be sunday; thanks, mwm@berkeley] 25 | * vix 30dec86 [written] 26 | */ 27 | 28 | #define CRON_VERSION "V4.999" 29 | #include "config.h" 30 | #include "externs.h" 31 | #include "pathnames.h" 32 | #include "macros.h" 33 | #include "structs.h" 34 | #include "funcs.h" 35 | #include "globals.h" 36 | -------------------------------------------------------------------------------- /Documentation/Changelog.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | [][#] 5 | [][#] 6 | 7 |
8 | 9 | # Changelog 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
 1987 /        May        /  6th   
 1988 /  February  /  8th   
 1993 / December  / 29th   
 1997 / September /  7th   
31 | 32 |
33 | 34 |
35 | 36 | 37 | 38 | 39 | [#]: # 40 | -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 1st A.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | **Date:**  5 | 1987 January 1st  6 | Thursday  7 | 10:29:20
8 | **From:**  9 | ames  10 | seismo  11 | dgis  12 | generous (Curtis Generous)  
13 | **To:**  14 | nike  15 | ptsfa  16 | vixie  17 | paul
18 | 19 | 20 | 21 | ### Paul 22 | 23 | > One of the limitations of the present versions of
24 | > cron is the lack of the capability of specifying a
25 | > way to execute a command every n units of time. 26 | > 27 | > Here is a good example: 28 | > 29 | > ```crontab 30 | > # Present method to start up uucico 31 | > 02,12,22,32,42,52 * * * * exec /usr/lib/uucp/uucico -r1 32 | > 33 | > 34 | ># New method ?? (the ':' here is just one possibility for syntax) 35 | >02:10 * * * * exec /usr/lib/uucp/uucico -r1 36 | >``` 37 | > 38 | > This method would prove very helpful for those
39 | > programs that get started every few minutes,
40 | > making the entry long and not easily readable. 41 | > 42 | > The first number would specify the base time,
43 | > and the second number the repetition interval. 44 | 45 | ### Reply 46 | 47 | > Good idea, but bob@acornrc beat you to it. 48 | > 49 | > I used '/' instead of ':'. 50 | > 51 | > This is my personal preference, and seems
52 | > intuitive when you think of the divide operator
53 | > in C... Does anyone have a preference? -------------------------------------------------------------------------------- /Documentation/Mails/1986 December 31st B.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | **Date:**  5 | 1986 December 31  6 | Wednesday  7 | 14:28:19 EST
8 | **From:**  9 | drw@mit-eddie (Dale Worley)  
10 | **To:**  11 | mit-eddie  12 | vixie  13 | paul
14 | 15 | 16 | 17 | ### Message 18 | 19 | > We have a lot of lines in our crontab of the form 20 | > 21 | > ```crontab 22 | > 00 12 * * * su user < /usr/users/user/script.file 23 | > ``` 24 | > 25 | > This barfs (silently!) on our system (Dec Ultrix 1.2 == 4.2bsd) if user's shell is csh. 26 | > 27 | > This, I am told, is because csh requires that
28 | > the environment be set up in certain ways,
29 | > which cron doesn't do. 30 | > 31 | > *Actually, I believe, it is because `/etc/rc`, which runs cron,
32 | > doesn't set up the environment enough for csh to run, and
33 | > cron just inherits the situation.* 34 | > 35 | > Anyway, the point is that if you find out what csh really
36 | > needs in its environment, you might want to set up cron
37 | > to provide some reasonable defaults 38 | > 39 | > *if it isn't supplied by cron's parent.* 40 | > 41 | > Also, could you tell me what csh needs, if
42 | > you find out, so we can hack our `/etc/rc`? 43 | 44 | ### Reply 45 | 46 | > well, the environment IS a problem. 47 | > 48 | > processes that cron forks will inherit the environment
49 | > of the person who ran the cron daemon... 50 | > 51 | > I plan to edit out such useless things as TERMCAP,
52 | > TERM, and the like; supply correct values for HOME,
53 | > USER, CWD, and whatever else comes to mind. 54 | > 55 | > I'll make sure csh works... -------------------------------------------------------------------------------- /Documentation/Changelog/Version 4.md: -------------------------------------------------------------------------------- 1 | 2 | [][#] 3 | [][#] 4 | 5 |
6 | 7 | # Version 4 8 | 9 |
10 | 11 |
12 |
13 | 14 |
15 | 16 | ## 4.1 17 | 18 |   2004 | January   19 | 20 |
21 |
22 | 23 | ## 4.0 24 | 25 |   2000 | November   26 | 27 |
28 |
29 | 30 | ## 4.0 - B1 31 | 32 |   1997 | September 7th   33 | 34 |
35 |
36 | 37 | ## Version 3 ⟷ 4 38 | 39 |
40 | 41 | ### 🚨  Security 42 | 43 | - Check `setuid()` result. 44 | 45 | *Reported by **Thomas Pollet**.* 46 | 47 |
48 | 49 | ### 🧰  Functionality 50 | 51 | - Added `MAIL_FROMUSER` option. 52 | 53 | *See the [`config.h`] comments.* 54 | 55 |
56 | 57 | ### 📦  Miscellaneous 58 | 59 | - Megapatch from **tcmiller** 60 | 61 | *POXIS compliance & more* 62 | 63 |
64 | 65 | ### 🪳  Bugs 66 | 67 | - Fixed stepsize `0` infinite loop. 68 | 69 | - Handle clock jumps. 70 | 71 | *From FreeBSD PR `#24485`* 72 | 73 |
74 | 75 | ### 🎁  Features 76 | 77 | - Give Cron a version number and display it. 78 | 79 | - Add `-n` to Cron for `nofork`. 80 | 81 |
82 | 83 | ### 🔬  Evolution 84 | 85 | - `enum` version of **FreeBSD** fixed to `env-var` settings. 86 | 87 | - Merged in many changes from **BSD** and **Linux**. 88 | 89 | - Changes `#12` to `#17` from **tcmiller**. 90 | 91 | *For **BSD** alignment.* 92 | 93 | 94 |
95 | 96 | ### 💾  Porting 97 | 98 | - Fixed syslog configuration bug. 99 | 100 | - Fixups for nextstep. 101 | 102 |
103 | 104 |
105 | 106 | 107 | 108 | 109 | [`config.h`]: ../../config.h 110 | [#]: # 111 | -------------------------------------------------------------------------------- /job.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1988,1990,1993,1994,2021 by Paul Vixie ("VIXIE") 3 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 4 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #if !defined(lint) && !defined(LINT) 20 | static char rcsid[] = "$Id: job.c,v 1.6 2004/01/23 18:56:43 vixie Exp $"; 21 | #endif 22 | 23 | #include "cron.h" 24 | 25 | typedef struct _job { 26 | struct _job *next; 27 | const entry *e; 28 | const user *u; 29 | } job; 30 | 31 | static job *jhead = NULL, *jtail = NULL; 32 | 33 | void 34 | job_add(const entry *e, const user *u) { 35 | job *j; 36 | 37 | /* if already on queue, keep going */ 38 | for (j = jhead; j != NULL; j = j->next) 39 | if (j->e == e && j->u == u) 40 | return; 41 | 42 | /* build a job queue element */ 43 | if ((j = (job *)malloc(sizeof(job))) == NULL) 44 | return; 45 | j->next = NULL; 46 | j->e = e; 47 | j->u = u; 48 | 49 | /* add it to the tail */ 50 | if (jhead == NULL) 51 | jhead = j; 52 | else 53 | jtail->next = j; 54 | jtail = j; 55 | } 56 | 57 | int 58 | job_runqueue(void) { 59 | job *j, *jn; 60 | int run = 0; 61 | 62 | for (j = jhead; j; j = jn) { 63 | do_command(j->e, j->u); 64 | jn = j->next; 65 | free(j); 66 | run++; 67 | } 68 | jhead = jtail = NULL; 69 | return (run); 70 | } 71 | -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 2nd B.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | **Date:**  5 | 1987 January 2nd  6 | Friday  7 | 14:25:29 EST
8 | **From:**  9 | dual  10 | ucbvax  11 | ihnp4  12 | anvil  13 | es  14 | Robert_Toxen  
15 | **To:**  16 | anvil  17 | ihnp4  18 | ucbvax  19 | dual  20 | ptsfa  21 | vixie  22 | paul
23 | 24 | 25 | 26 | ### Message 27 | 28 | > Here are some suggestions: 29 | > 1. Run it through the C preprocessor via "/lib/". 30 | 31 | ### Reply 32 | 33 | > hmmm. this seems of limited utility, and if you really
34 | > wanted to do it that way, you could do it yourself
35 | > (since users can write to their own crontab files). 36 | > 37 | > I'll add '-' (read stdin) to the crontab
38 | > installer program to facilitate this. 39 | 40 | ### Message 41 | 42 | > 2. Allow specifying every Nth day of
43 | > week, i.e., every second Wednesday. 44 | > 45 | > I did this to calendar by separating the day of week
46 | > (Wed=4, which one to start on and N with slashes). 47 | > 48 | > I took modulo the day of year as a starting point so
49 | > that someone with a desk calendar documenting
50 | > such things can easily determine the offset (second number). 51 | > 52 | > I did this while at SGI; alas I don't have a copy of the code. 53 | 54 | ### Reply 55 | 56 | > I can see how this could be useful,
57 | > but I'm not sure how I'd implement it. 58 | > 59 | > Cron currently doesn't keep track of the last time a given
60 | > command was run; whether the current Wednesday is the
61 | > first or second since the command was last run would be
62 | > pretty hard to figure out. 63 | > 64 | > I'd have to keep a database of commands and their execution
65 | > around, and purge it when the crontab was overwritten. 66 | > 67 | > This is too much work for me, but if someone adds it, let me know. -------------------------------------------------------------------------------- /structs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: structs.h,v 1.7 2004/01/23 18:56:43 vixie Exp $ 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 7 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 8 | * 9 | * Permission to use, copy, modify, and distribute this software for any 10 | * purpose with or without fee is hereby granted, provided that the above 11 | * copyright notice and this permission notice appear in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | typedef struct _entry { 23 | struct _entry *next; 24 | struct passwd *pwd; 25 | char **envp; 26 | char *cmd; 27 | bitstr_t bit_decl(minute, MINUTE_COUNT); 28 | bitstr_t bit_decl(hour, HOUR_COUNT); 29 | bitstr_t bit_decl(dom, DOM_COUNT); 30 | bitstr_t bit_decl(month, MONTH_COUNT); 31 | bitstr_t bit_decl(dow, DOW_COUNT); 32 | int flags; 33 | #define MIN_STAR 0x01 34 | #define HR_STAR 0x02 35 | #define DOM_STAR 0x04 36 | #define DOW_STAR 0x08 37 | #define DOM_LAST 0x10 38 | #define WHEN_REBOOT 0x20 39 | #define DONT_LOG 0x40 40 | } entry; 41 | 42 | /* the crontab database will be a list of the 43 | * following structure, one element per user 44 | * plus one for the system. 45 | * 46 | * These are the crontabs. 47 | */ 48 | 49 | typedef struct _user { 50 | struct _user *next, *prev; /* links */ 51 | char *name; 52 | struct timespec mtim; /* last modtime of crontab */ 53 | entry *crontab; /* this person's crontab */ 54 | } user; 55 | 56 | typedef struct _cron_db { 57 | user *head, *tail; /* links */ 58 | struct timespec mtim; /* last modtime on spooldir */ 59 | } cron_db; 60 | /* in the C tradition, we only create 61 | * variables for the main program, just 62 | * extern them elsewhere. 63 | */ 64 | -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 16th.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Date:**  4 | 1987 January 16th  5 | Friday  6 | 07:44:57 EST
7 | **To:**  8 | nike  9 | ptsfa  10 | vixie  11 | paul
12 | 13 | 14 | 15 | ### Message 16 | 17 | > The System V cron is nice, but
18 | > it has a few annoying features. 19 | > 20 | > One is that its mail files will say that
21 | > the previous message is the output
22 | > of "one of your cron commands." 23 | > 24 | > I wish it would say WHICH cron commmand. 25 | 26 | ### Reply 27 | 28 | > Done. 29 | > 30 | > Also which shell, which user (useful when the
31 | > mail gets forwarded), which home directory,
32 | > and other useful crud. 33 | 34 | ### Message 35 | 36 | > Another problem is with timezones. 37 | > 38 | > It is necessary to specify TZ=PST8PDT (or whatever)
39 | > when you invoke cron (from inittab, or /etc/rc) and
40 | > it is also necessary to add TZ=PST8PDT to each
41 | > crontab line which might need it. 42 | > 43 | > Cron should automatically export its idea of the "TZ"
44 | > to each invoked command, and it should be possible
45 | > to put a line in the crontab file which overrides that
46 | > for every command in the file (e.g., most users are
47 | > on EST, so cron is run with TZ=EST5EDT; but one user
48 | > is usually on PST and wants all of his cron commands
49 | > to run with TZ=PST8PDT). 50 | > 51 | > This might be extended to allow any environment
52 | > variable to be specified once for the whole crontab
53 | > file (e.g., PATH). 54 | 55 | ### Reply 56 | 57 | > Well, since I run the user's shell, you could put this into .cshrc. 58 | > 59 | > generic environment-variable setting could be useful, though. 60 | > 61 | > Since I have to modify the environment anyway, I'll consider this. 62 | 63 | ### Message 64 | 65 | > A log file might be a nice idea, but
66 | > the System V cron log is too verbose. 67 | > 68 | > I seem to remember that cron keeps it open,
69 | > too; so you can't even have something go and
70 | > periodically clean it out. 71 | 72 | ### Reply 73 | 74 | > I don't do `/usr/lib/cron/log`. 75 | > 76 | > I wasn't aware of this file until
77 | > I got all these suggestions. 78 | > 79 | > Do people want this file?
80 | > Tell me! 81 | -------------------------------------------------------------------------------- /globals.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: globals.h,v 1.10 2004/01/23 19:03:33 vixie Exp $ 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2021 by Paul Vixie ("VIXIE") 7 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 8 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 9 | * 10 | * Permission to use, copy, modify, and distribute this software for any 11 | * purpose with or without fee is hereby granted, provided that the above 12 | * copyright notice and this permission notice appear in all copies. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 15 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 17 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 20 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 | */ 22 | 23 | #ifdef MAIN_PROGRAM 24 | # define XTRN 25 | # define INIT(x) = x 26 | #else 27 | # define XTRN extern 28 | # define INIT(x) 29 | #endif 30 | 31 | XTRN const char *copyright[] 32 | #ifdef MAIN_PROGRAM 33 | = { 34 | "@(#) Vixie Cron", 35 | "@(#) Copyright 1988,1989,1990,1993,1994,2021 by Paul Vixie", 36 | "@(#) Copyright 1997,2000 by Internet Software Consortium, Inc.", 37 | "@(#) Copyright 2004 by Internet Systems Consortium, Inc.", 38 | "@(#) Copyright 2023 by Paul Vixie", 39 | "@(#) All rights reserved", 40 | NULL 41 | } 42 | #endif 43 | ; 44 | 45 | XTRN const char *MonthNames[] 46 | #ifdef MAIN_PROGRAM 47 | = { 48 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", 49 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 50 | NULL 51 | } 52 | #endif 53 | ; 54 | 55 | XTRN const char *DowNames[] 56 | #ifdef MAIN_PROGRAM 57 | = { 58 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", 59 | NULL 60 | } 61 | #endif 62 | ; 63 | 64 | XTRN char *ProgramName INIT("amnesia"); 65 | XTRN int LineNumber INIT(0); 66 | XTRN time_t StartTime INIT(0); 67 | XTRN char *Mailer INIT(NULL); 68 | XTRN int NoFork INIT(0); 69 | XTRN const struct timespec ts_zero 70 | #ifdef MAIN_PROGRAM 71 | = {.tv_sec = 0, .tv_nsec = 0} 72 | #endif 73 | ; 74 | #if DEBUGGING 75 | XTRN int DebugFlags INIT(0); 76 | XTRN const char *DebugFlagNames[] 77 | #ifdef MAIN_PROGRAM 78 | = { 79 | "ext", "sch", "proc", "pars", "load", "misc", "test", "bit", 80 | NULL 81 | } 82 | #endif 83 | ; 84 | #else 85 | #define DebugFlags 0 86 | #endif /* DEBUGGING */ 87 | -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 5th B.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Date:**  4 | 1987 January 5th  5 | Monday  6 | 09:29:57
7 | **From:**  8 | ames  9 | seismo  10 | cbmvax  11 | devon  12 | paul  
13 | **To:**  14 | cbmvax  15 | seismo  16 | nike  17 | ptsfa  18 | vixie  19 | paul
20 | 21 | 22 | 23 | 24 | ### Message 25 | 26 | > One problem that has always plagued
27 | > me with cron is the assumed ORing. 28 | > 29 | > I'd like to see some type of ANDing implemented. 30 | > 31 | > I guess I can best describe this by example.
32 | > Say I have the following line in my crontab 33 | > 34 | > file: 35 | > 36 | > ```crontab 37 | > * * 4-31 * 1-6 /usr/bin/command 38 | > ``` 39 | > 40 | > What this does is run 'command' on the 4th thru 31st
41 | > days of the month, AND on Monday thru Saturday;
42 | > which probably means running it every day of the
43 | > month (unless Sunday falls on days 1-3). 44 | > 45 | > This happens because cron runs the command
46 | > if the day-of-month OR the day-of-week is true. 47 | > 48 | > What I'd like to happen with the above line is to run
49 | > the command ONLY on Monday thru Saturday any
50 | > time after the 3rd of the month, e.g. if the
51 | > day-of-month AND the day-of-week are true. 52 | > 53 | > My proposal to you is to implement some
54 | > special chars for the first five fields. 55 | > 56 | > Examples: 57 | > 58 | > ```crontab 59 | > * * !1-3 * 1-6 /usr/bin/command 60 | > ``` 61 | > 62 | > (run command Mon-Sat, but NOT [!] on the first 3 days of the month) 63 | > 64 | > ```crontab 65 | > * * &4-31 * &1-6 /usr/bin/command 66 | > ``` 67 | > 68 | > (run command if day-of-month AND day-of-week are true) 69 | > 70 | > Get the picture? 71 | > 72 | > This would be compatable with existing versions
73 | > of cron (which wouldn't currently be using any
74 | > special characters, so that old crontabs would
75 | > be handled correctly). 76 | 77 | ### Reply 78 | 79 | > This message made me aware of the actual
80 | > boolean expression involved in a crontab entry. 81 | > 82 | > I'd assumed that it was 83 | > 84 | > (minute && hour && DoM && month && DoW) 85 | > 86 | > But it's really 87 | > 88 | > (minute && hour && month && (DoM || DoW)) 89 | > 90 | > I can see some value in changing this, but with a
91 | > fixed order of fields, operators get to be kindof
92 | > unary, which && and || really aren't. 93 | > 94 | > If someone has an idea on a syntax that allows
95 | > useful variations to the standard (&& && && (||))
96 | > default, please suggest. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | # Vixie Cron 7 | 8 | *The **[Cron]** flavor that runs on most systems.* 9 | 10 |
11 |
12 | 13 | [![Button Features]][Features]   14 | [![Button Configure]][Configure]   15 | [![Button Conversion]][Conversion] 16 | 17 | [![Button Changes]][Changes]   18 | [![Button Mails]][Mails]   19 | [![Button Thanks]][Thanks] 20 | 21 |
22 |
23 | 24 | This version of **Cron** is functionally based on
25 | **System V**'s implementation and thus allows
26 | every user to have their own **CronTab** file. 27 | 28 |
29 | 30 | [][#] 31 | [][#] 32 | 33 |
34 |
35 | 36 | ## 📑  Tabs 37 | 38 | All crontab files are stored in a read 39 | protected folders at  `/var/cron/tabs`  40 | 41 |
42 | 43 | ## 📜  At 44 | 45 | **There is no direct support for  `at`** 46 | 47 | However as long as your system 48 | supports it, you can still use  `atrun` 49 | 50 |
51 | 52 | ## 📋  Logging 53 | 54 | A message will be logged for every 55 | command that is run by a CronTab. 56 | 57 |
58 | 59 | ## 🛡  Access 60 | 61 | You can control access to the  `crontab` 62 | command by utilizing the  `allow`  and 63 | `deny`  files in  `/var/cron` 64 | 65 | *The command is used to install crontabs.* 66 | 67 |
68 | 69 | ## 📺  System V 70 | 71 | While it hasn't been tested yet, some effort
72 | has gone into making porting to it easier. 73 | 74 |
75 | 76 | 77 | 78 | 79 | [#]: # 80 | 81 | [Cron]: https://en.wikipedia.org/wiki/Cron 82 | 83 | [Conversion]: Documentation/Conversion.md 84 | [Configure]: Documentation/Configure.md 85 | [Features]: Documentation/Features.md 86 | [Changes]: Documentation/Changelog.md 87 | [Thanks]: Documentation/Thanks.md 88 | [Mails]: Documentation/Mail.md 89 | 90 | 91 | 92 | 93 | [Button Conversion]: https://img.shields.io/badge/Conversion-00B0B9?style=for-the-badge&logo=GitExtensions&logoColor=white 94 | [Button Configure]: https://img.shields.io/badge/Configure-31A8FF?style=for-the-badge&logo=WindowsTerminal&logoColor=white 95 | [Button Features]: https://img.shields.io/badge/Features-68BC71?style=for-the-badge&logo=AddThis&logoColor=white 96 | [Button Changes]: https://img.shields.io/badge/Changelog-66459B?style=for-the-badge&logo=Git&logoColor=white 97 | [Button Thanks]: https://img.shields.io/badge/Credits-d74078?style=for-the-badge&logo=GitHubSponsors&logoColor=white 98 | [Button Mails]: https://img.shields.io/badge/Mail_Archive-yellow?style=for-the-badge&logo=GMail&logoColor=white 99 | -------------------------------------------------------------------------------- /funcs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: funcs.h,v 1.9 2004/01/23 18:56:42 vixie Exp $ 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2021 by Paul Vixie ("VIXIE") 7 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 8 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 9 | * 10 | * Permission to use, copy, modify, and distribute this software for any 11 | * purpose with or without fee is hereby granted, provided that the above 12 | * copyright notice and this permission notice appear in all copies. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 15 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 17 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 20 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 | */ 22 | 23 | /* Notes: 24 | * This file has to be included by cron.h after data structure defs. 25 | * We should reorg this into sections by module. 26 | */ 27 | 28 | void set_cron_uid(void), 29 | set_cron_cwd(void), 30 | load_database(cron_db *), 31 | open_logfile(void), 32 | sigpipe_func(void), 33 | job_add(const entry *, const user *), 34 | do_command(const entry *, const user *), 35 | link_user(cron_db *, user *), 36 | unlink_user(cron_db *, user *), 37 | free_user(user *), 38 | env_free(char **), 39 | unget_char(int, FILE *), 40 | free_entry(entry *), 41 | acquire_daemonlock(int), 42 | skip_comments(FILE *), 43 | log_it(const char *, int, const char *, const char *), 44 | log_close(void); 45 | 46 | int job_runqueue(void), 47 | set_debug_flags(const char *), 48 | get_char(FILE *), 49 | get_string(char *, int, FILE *, char *), 50 | swap_uids(void), 51 | swap_uids_back(void), 52 | load_env(char *, FILE *), 53 | cron_pclose(FILE *), 54 | glue_strings(char *, size_t, const char *, const char *, char), 55 | strcmp_until(const char *, const char *, char), 56 | allowed(const char *, const char *, const char *), 57 | strdtb(char *), 58 | strcountstr(const char *, const char *); 59 | 60 | size_t strlens(const char *, ...); 61 | 62 | char *env_get(char *, char **), 63 | *arpadate(time_t *), 64 | *mkprints(unsigned char *, unsigned int), 65 | *first_word(char *, char *), 66 | **env_init(void), 67 | **env_copy(char **), 68 | **env_set(char **, char *); 69 | 70 | user *load_user(int, struct passwd *, const char *), 71 | *find_user(cron_db *, const char *); 72 | 73 | entry *load_entry(FILE *, void (*)(const char *), 74 | struct passwd *, char **); 75 | 76 | FILE *cron_popen(char *, char *, struct passwd *); 77 | 78 | struct passwd *pw_dup(const struct passwd *); 79 | 80 | #ifndef HAVE_TM_GMTOFF 81 | long get_gmtoff(time_t *, struct tm *); 82 | #endif 83 | -------------------------------------------------------------------------------- /user.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1988,1990,1993,1994,2021 by Paul Vixie ("VIXIE") 3 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 4 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #if !defined(lint) && !defined(LINT) 20 | static char rcsid[] = "$Id: user.c,v 1.5 2004/01/23 18:56:43 vixie Exp $"; 21 | #endif 22 | 23 | /* vix 26jan87 [log is in RCS file] 24 | */ 25 | 26 | #include "cron.h" 27 | 28 | void 29 | free_user(user *u) { 30 | entry *e, *ne; 31 | 32 | free(u->name); 33 | for (e = u->crontab; e != NULL; e = ne) { 34 | ne = e->next; 35 | free_entry(e); 36 | } 37 | free(u); 38 | } 39 | 40 | user * 41 | load_user(int crontab_fd, struct passwd *pw, const char *name) { 42 | char envstr[MAX_ENVSTR]; 43 | FILE *file; 44 | user *u; 45 | entry *e; 46 | int status, save_errno; 47 | char **envp, **tenvp; 48 | 49 | if (!(file = fdopen(crontab_fd, "r"))) { 50 | perror("fdopen on crontab_fd in load_user"); 51 | return (NULL); 52 | } 53 | 54 | Debug(DPARS, ("load_user()\n")) 55 | 56 | /* file is open. build user entry, then read the crontab file. 57 | */ 58 | if ((u = (user *) malloc(sizeof(user))) == NULL) 59 | return (NULL); 60 | if ((u->name = strdup(name)) == NULL) { 61 | save_errno = errno; 62 | free(u); 63 | errno = save_errno; 64 | return (NULL); 65 | } 66 | u->crontab = NULL; 67 | 68 | /* init environment. this will be copied/augmented for each entry. 69 | */ 70 | if ((envp = env_init()) == NULL) { 71 | save_errno = errno; 72 | free(u->name); 73 | free(u); 74 | errno = save_errno; 75 | return (NULL); 76 | } 77 | 78 | /* load the crontab 79 | */ 80 | while ((status = load_env(envstr, file)) >= OK) { 81 | switch (status) { 82 | case ERR: 83 | free_user(u); 84 | u = NULL; 85 | goto done; 86 | case FALSE: 87 | e = load_entry(file, NULL, pw, envp); 88 | if (e) { 89 | e->next = u->crontab; 90 | u->crontab = e; 91 | } 92 | break; 93 | case TRUE: 94 | if ((tenvp = env_set(envp, envstr)) == NULL) { 95 | save_errno = errno; 96 | free_user(u); 97 | u = NULL; 98 | errno = save_errno; 99 | goto done; 100 | } 101 | envp = tenvp; 102 | break; 103 | } 104 | } 105 | 106 | done: 107 | env_free(envp); 108 | fclose(file); 109 | Debug(DPARS, ("...load_user() done\n")) 110 | return (u); 111 | } 112 | -------------------------------------------------------------------------------- /Documentation/Mail.md: -------------------------------------------------------------------------------- 1 | 2 | [][#] 3 | [][#] 4 | 5 |
6 | 7 | # Mail Archive 8 | 9 |
10 | 11 | ``` 12 | This is really old mail that came to me in response 13 | to my 1986 posting to Usenet asking for feature 14 | suggestions before releasing the first version of Cron. 15 | It is presented here for its entertainment value. 16 | -- Vix 17 | ``` 18 | 19 |
20 |
21 | 22 | [][#] 23 | [][#] 24 | 25 |
26 | 27 | # 1986
December 28 | 29 |
30 | 31 | | Day | Mails | 32 | |:---:|:-----:| 33 | | `31st` | [![A]][1986 / 12 / 31 / A]   [![B]][1986 / 12 / 31 / B] 34 | 35 |
36 |
37 | 38 | # 1987
January 39 | 40 |
41 | 42 | | Day | Mails 43 | |:-----:|:----- 44 | | `1st` | [![A]][1987 / 01 / 01 / A]   [![B]][1987 / 01 / 01 / B]   [![C]][1987 / 01 / 01 / C] 45 | | `2nd` | [![A]][1987 / 01 / 02 / A]   [![B]][1987 / 01 / 02 / B]   [![C]][1987 / 01 / 02 / C]   [![D]][1987 / 01 / 02 / D] 46 | | `3rd` | [![A]][1987 / 01 / 03 / A] 47 | | `4th` | [![A]][1987 / 01 / 04 / A] 48 | | `5th` | [![A]][1987 / 01 / 05 / A]   [![B]][1987 / 01 / 05 / B]   [![C]][1987 / 01 / 05 / C] 49 | | `8th` | [![A]][1987 / 01 / 08 / A] 50 | | `13th` | [![A]][1987 / 01 / 13 / A] 51 | | `16th` | [![A]][1987 / 01 / 16 / A] 52 | 53 |
54 | 55 |
56 | 57 | 58 | 59 | 60 | [#]: # 61 | 62 | 63 | 64 | 65 | [1986 / 12 / 31 / A]: Mails/1986%20December%2031st%20A.md 66 | [1986 / 12 / 31 / B]: Mails/1986%20December%2031st%20B.md 67 | 68 | [1987 / 01 / 01 / A]: Mails/1987%20January%201st%20A.md 69 | [1987 / 01 / 01 / B]: Mails/1987%20January%201st%20B.md 70 | [1987 / 01 / 01 / C]: Mails/1987%20January%201st%20C.md 71 | 72 | [1987 / 01 / 02 / A]: Mails/1987%20January%202nd%20A.md 73 | [1987 / 01 / 02 / B]: Mails/1987%20January%202nd%20B.md 74 | [1987 / 01 / 02 / C]: Mails/1987%20January%202nd%20C.md 75 | [1987 / 01 / 02 / D]: Mails/1987%20January%202nd%20D.md 76 | 77 | [1987 / 01 / 03 / A]: Mails/1987%20January%203rd.md 78 | 79 | [1987 / 01 / 04 / A]: Mails/1987%20January%204th.md 80 | 81 | [1987 / 01 / 05 / A]: Mails/1987%20January%205th%20A.md 82 | [1987 / 01 / 05 / B]: Mails/1987%20January%205th%20B.md 83 | [1987 / 01 / 05 / C]: Mails/1987%20January%205th%20C.md 84 | 85 | [1987 / 01 / 08 / A]: Mails/1987%20January%208th.md 86 | 87 | [1987 / 01 / 13 / A]: Mails/1987%20January%2013th.md 88 | 89 | [1987 / 01 / 16 / A]: Mails/1987%20January%2016th.md 90 | 91 | 92 | 93 | 94 | [A]: https://img.shields.io/badge/A-2478b5?style=for-the-badge 95 | [B]: https://img.shields.io/badge/B-red?style=for-the-badge 96 | [C]: https://img.shields.io/badge/C-green?style=for-the-badge 97 | [D]: https://img.shields.io/badge/D-yellow?style=for-the-badge 98 | -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 4th.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | **Date:**  5 | 1987 January 4th  6 | Sunday  7 | 00:42:35 PST
8 | **From:**  9 | Mike Meyer   
10 | **To:**  11 | hplabs  12 | qantel  13 | vixie  14 | paul (Paul Vixie Esq)
15 | 16 | 17 | 18 | *Discussion of RMS/FSF, and mwm's GNU Cron deleted* 19 | 20 | ### Message 21 | 22 | > Oh, yeah - here are the extensions on my cron: 23 | > 24 | > 1) Sunday is both day 0 and day 7, so it
25 | > complies with both SysV and BSD cron. 26 | 27 | ### Reply 28 | 29 | > Good idea. 30 | > I did it too, thanks for informing me. 31 | 32 | ### Message 33 | 34 | > 2) At is integrated into the cron. 35 | > 36 | > Instead of atrun to scan the /usr/spool/at directory,
37 | > at files are put into the /usr/lib/cron directory along
38 | > with users cron files, and cron fabricates a line from
39 | > a crontab file to run them. 40 | > 41 | > This is considered a major win by all who use it. 42 | 43 | ### Reply 44 | 45 | > I don't use 'at', and my cron doesn't do anything with it. 46 | > 47 | > To run 'at', I use 'atrun' the same way the current BSD cron does. 48 | > 49 | > My crontab files are in /usr/spool/cron/crontabs,
50 | > in the SysV tradition -- not in /usr/lib/cron. 51 | > 52 | > This is a configuration parameter, of course. 53 | 54 | ### Message 55 | 56 | > There are two known restrictions: 57 | > 58 | > 1) I don't support any of the SysV security hooks. 59 | > 60 | > I don't have a use for them, and
61 | > RMS didn't like the idea at all :-). 62 | 63 | ### Reply 64 | 65 | > This means cron.allow and cron.deny. 66 | > 67 | > I plan to support them, as they've been quite
68 | > helpful at various HPUX sites I've administered. 69 | 70 | ### Message 71 | 72 | > 2) Cron expects to be able to create files
73 | > with names longer than 14 characters,
74 | > which makes it hard to run on SysV. 75 | > 76 | > At least one person was working on a
77 | > port, but I don't know how it's going. 78 | > 79 | > That might make for a good reason
80 | > for releasing yours, right there. 81 | 82 | ### Reply 83 | 84 | > If someone has SysV (with the 14-character limit),
85 | > they probably won't want my cron, since it doesn't
86 | > add much to the standard version
87 | > (which they may have support for). 88 | > 89 | > My cron is not currently portable to non-BSD systems,
90 | > since it relies on interval timers (I needed to sleep for
91 | > intervals more granular than seconds alone would allow). 92 | > 93 | > The port would be trivial, and I will
94 | > do it if a lot of people ask for it... 95 | 96 | ### Message 97 | 98 | > Oh, yeah - I'm going to see about getting this
99 | > cron integrated into the next 4BSD release. 100 | 101 | ### Reply 102 | 103 | > How does one go about this? 104 | > 105 | > I have a few nifty gadgets I'd like to
106 | > contribute, this cron being one of them... 107 | 108 | *More FSF/GNU discussion deleted* -------------------------------------------------------------------------------- /Documentation/Thanks.md: -------------------------------------------------------------------------------- 1 | 2 | [][#] 3 | [][#] 4 | 5 |
6 | 7 | # Thanks 8 | 9 | `Paul Vixie - 15 January 1990` 10 | 11 |
12 | 13 | Many people have contributed to Cron, 14 | many more than I can remember, in fact. 15 | 16 |
17 |
18 | 19 |
20 | 21 | **[Rich Salz]** and **[Carl Gutekunst]** were 22 | each of enormous help to me in V1. 23 | 24 | Carl for helping me understand UNIX 25 | well enough to write it, and Rich for 26 | helping me get the features right. 27 | 28 |
29 |
30 | 31 |
32 | 33 | **[John Gilmore]** wrote me a wonderful review of version 2, 34 | which took me a whole year to answer, even though it 35 | made me clean up some really awful things in the code. 36 | 37 | *Of course according to John the most awful things are still in there.* 38 | 39 |
40 |
41 | 42 |
43 | 44 | **Paul Close** made a suggestion led to the creation 45 | of  `/etc/crond.pid`  and the mutex locking on it. 46 | 47 |
48 |
49 | 50 |
51 | 52 | **Kevin Braunsdorf of Purdue** made a suggestion 53 | that led to  `@reboot`  and its brothers and sisters. 54 | 55 | He also sent in some diffs that lead Cron towards 56 | compile-ability with **System V**, however without 57 | at (1) capabilities, this cron isn't going to be that 58 | useful on System V. 59 | 60 |
61 |
62 | 63 |
64 | 65 | **Bob Alverson** fixed a silly bug 66 | in the line number counting. 67 | 68 |
69 |
70 | 71 |
72 | 73 | **[Brian Reid]** suggestions led to the run queue 74 | & the source-file labeling in installed crontabs. 75 | 76 |
77 |
78 | 79 |
80 | 81 | **Scott Narveson** ported version 2 to a Sequent, and sent 82 | in the single most useful batch of diffs I've ever gotten. 83 | 84 |
85 | 86 | #### Changes attributable to Scott 87 | 88 |
 Sequent-verse support added 
 ( may also help on Pyramids ) 
    89 |
 Sendmail will not timeout if the 
 cmd is slow to generate output 
90 | 91 |
 Misc changes related to the 
 side effects of  `fclose()` 
  92 |
 Crontab says the correct thing when 
 you do something you shouldn't do 
93 | 94 | 95 |
 Day-of-week names aren't  
 off by one day anymore 
    96 |
 The Crontab ( 5 ) man page is longer 
 and it's content is more informative 
97 | 98 | 99 |
 Null `pw_shell` is dealt with 
 now and defaults to `/bin/sh` 
100 | 101 | 102 |
103 | 104 |
105 | 106 | 107 | 108 | 109 | [#]: # 110 | 111 | [Carl Gutekunst]: https://github.com/alameth 112 | [John Gilmore]: https://en.wikipedia.org/wiki/John_Gilmore_(activist) 113 | [Brian Reid]: https://en.wikipedia.org/wiki/Brian_Reid_(computer_scientist) 114 | [Rich Salz]: https://twitter.com/RichSalz 115 | -------------------------------------------------------------------------------- /pathnames.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1993,1994,2021 by Paul Vixie ("VIXIE") 3 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 4 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * $Id: pathnames.h,v 1.9 2004/01/23 18:56:43 vixie Exp $ 21 | */ 22 | 23 | #ifndef _PATHNAMES_H_ 24 | #define _PATHNAMES_H_ 25 | 26 | #if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX) 27 | # include 28 | #endif /*BSD*/ 29 | 30 | #ifndef CRONDIR 31 | /* CRONDIR is where cron(8) and crontab(1) both chdir 32 | * to; SPOOL_DIR, CRON_ALLOW, CRON_DENY, and LOG_FILE 33 | * are all relative to this directory. 34 | */ 35 | #define CRONDIR "/var/cron" 36 | #endif 37 | 38 | /* SPOOLDIR is where the crontabs live. 39 | * This directory will have its modtime updated 40 | * whenever crontab(1) changes a crontab; this is 41 | * the signal for cron(8) to look at each individual 42 | * crontab file and reload those whose modtimes are 43 | * newer than they were last time around (or which 44 | * didn't exist last time around...) 45 | */ 46 | #define SPOOL_DIR "tabs" 47 | 48 | /* cron allow/deny file. At least cron.deny must 49 | * exist for ordinary users to run crontab. 50 | */ 51 | #define CRON_ALLOW "cron.allow" 52 | #define CRON_DENY "cron.deny" 53 | 54 | /* undefining this turns off logging to a file. If 55 | * neither LOG_FILE or SYSLOG is defined, we don't log. 56 | * If both are defined, we log both ways. Note that if 57 | * LOG_CRON is defined by , LOG_FILE will not 58 | * be used. 59 | */ 60 | #define LOG_FILE "log" 61 | 62 | /* where should the daemon stick its PID? 63 | * PIDDIR must end in '/'. 64 | */ 65 | #ifdef _PATH_VARRUN 66 | # define PIDDIR _PATH_VARRUN 67 | #else 68 | # define PIDDIR "/etc/" 69 | #endif 70 | #define PIDFILE "cron.pid" 71 | #define _PATH_CRON_PID PIDDIR PIDFILE 72 | 73 | /* 4.3BSD-style crontab */ 74 | #define SYSCRONTAB "/etc/crontab" 75 | 76 | /* what editor to use if no EDITOR or VISUAL 77 | * environment variable specified. 78 | */ 79 | #if defined(_PATH_VI) 80 | # define EDITOR _PATH_VI 81 | #else 82 | # define EDITOR "/usr/ucb/vi" 83 | #endif 84 | 85 | #ifndef _PATH_SENDMAIL 86 | # define _PATH_SENDMAIL "/usr/lib/sendmail" 87 | #endif 88 | 89 | #ifndef _PATH_BSHELL 90 | # define _PATH_BSHELL "/bin/sh" 91 | #endif 92 | 93 | #ifndef _PATH_DEFPATH 94 | # define _PATH_DEFPATH "/usr/bin:/bin" 95 | #endif 96 | 97 | #ifndef _PATH_TMP 98 | # define _PATH_TMP "/tmp" 99 | #endif 100 | 101 | #ifndef _PATH_DEVNULL 102 | # define _PATH_DEVNULL "/dev/null" 103 | #endif 104 | 105 | #endif /* _PATHNAMES_H_ */ 106 | -------------------------------------------------------------------------------- /Documentation/Configure.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | [][#] 5 | [][#] 6 | 7 |
8 | 9 | # Configure 10 | 11 | Currently there is no configuration script, as 12 | such you will have to manually edit a couple 13 | of files, however a checklist is provided below. 14 | 15 | 16 |
17 |
18 | 19 | ## Read 20 | 21 | the following sections first. 22 | 23 |
24 | 25 | [![Button Features]][Features]   26 | [![Button Install]][Install]   27 | [![Button Conversion]][Conversion] 28 | 29 |
30 | 31 |
32 |
33 | 34 | ## Edit 35 | 36 | The following files have to be adjusted by 37 | the instructions contained within them. 38 | 39 |
40 | 41 | - [`config.h`] 42 | 43 | The file contains instructions on what to edit. 44 | 45 | Some things are defined in the **Makefile** and 46 | do not need to be changed in the config itself. 47 | 48 | They will be surrounded by  `#ifndef`  guards. 49 | 50 |
51 | 52 | - [`Makefile`] 53 | 54 |
55 |
56 | 57 | [][#] 58 | [][#] 59 | 60 | ## Execute 61 | 62 | Call the following list of commands in order. 63 | 64 |
65 | 66 | 1. **Build** with the program with: 67 | 68 | ```sh 69 | make 70 | ``` 71 | 72 |
73 | 74 | 2. **Install** the built binary with: 75 | 76 | ```sh 77 | su 78 | make install 79 | ``` 80 | 81 | *The **Man Pages** have to be installed manually.* 82 | 83 |
84 | 85 | 3. **Kill** the existing **Cron** process 86 | 87 |
88 | 89 | 4. **Create** new **CronTabs** using: 90 | 91 | ``` 92 | /usr/lib/{crontab,crontab.local} 93 | ``` 94 | 95 |
96 | 97 | #### Root 98 | 99 | You **can** place all of your settings in the root's CronTab. 100 | 101 |
102 | 103 | #### User 104 | 105 | To keep things tidy and manageable, it is advised to 106 | divide your config into purpose based configurations. 107 | 108 | You should also remove all  `su`  commands 109 | and turn lengthy lists into simple ranges. 110 | 111 |
112 | 113 | 5. Start the new **Cron** with root privileges. 114 | 115 |
116 | 117 | 6. Test and watch the daemon track your changes with: 118 | 119 | ```sh 120 | crontab -r 121 | ``` 122 | 123 |
124 | 125 | 7. To keep it your changes, adjust the files found at 126 | the path listed below to use this version of **Cron**. 127 | 128 | ``` 129 | /etc/{rc,rc.local} 130 | ``` 131 | 132 | 133 |
134 | 135 | 136 | 137 | 138 | [`config.h`]: ../config.h 139 | [`Makefile`]: ../Makefile 140 | [#]: # 141 | 142 | [Conversion]: Conversion.md 143 | [Features]: Features.md 144 | [Install]: Installation.md 145 | 146 | 147 | 148 | 149 | [Button Conversion]: https://img.shields.io/badge/Conversion-00B0B9?style=for-the-badge&logo=GitExtensions&logoColor=white 150 | [Button Features]: https://img.shields.io/badge/Features-68BC71?style=for-the-badge&logo=AddThis&logoColor=white 151 | [Button Install]: https://img.shields.io/badge/Install-31A8FF?style=for-the-badge&logo=WindowsTerminal&logoColor=white 152 | -------------------------------------------------------------------------------- /crontab.1: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 1988,1990,1993,2021 by Paul Vixie ("VIXIE") 2 | .\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 3 | .\" Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 4 | .\" 5 | .\" Permission to use, copy, modify, and distribute this software for any 6 | .\" purpose with or without fee is hereby granted, provided that the above 7 | .\" copyright notice and this permission notice appear in all copies. 8 | .\" 9 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 10 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 12 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 15 | .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | .\" 17 | .\" $Id: crontab.1,v 1.7 2004/01/23 19:03:32 vixie Exp $ 18 | .\" 19 | .TH CRONTAB 1 "29 December 1993" 20 | .UC 4 21 | .SH NAME 22 | crontab \- maintain crontab files for individual users (Vixie Cron) 23 | .SH SYNOPSIS 24 | .B crontab 25 | .RB [ -u 26 | .IR user ] " file" 27 | .br 28 | .B crontab 29 | .RB [ -u 30 | .IR user ] 31 | .RB [ -l " | " -r " | " -e ] 32 | .SH DESCRIPTION 33 | .I Crontab 34 | is the program used to install, deinstall or list the tables 35 | used to drive the 36 | .IR cron (8) 37 | daemon in Vixie Cron. Each user can have their own crontab, and though 38 | these are files in /var, they are not intended to be edited directly. 39 | .PP 40 | If the 41 | .I cron.allow 42 | file exists, then you must be listed therein in order to be allowed to use 43 | this command. If the 44 | .I cron.allow 45 | file does not exist but the 46 | .I cron.deny 47 | file does exist, then you must \fBnot\fR be listed in the 48 | .I cron.deny 49 | file in order to use this command. If neither of these files exists, 50 | only the super user will be allowed to use this command. 51 | .PP 52 | If the 53 | .I -u 54 | option is given, it specifies the name of the user whose crontab is to be 55 | tweaked. If this option is not given, 56 | .I crontab 57 | examines "your" crontab, i.e., the crontab of the person executing the 58 | command. Note that 59 | .IR su (8) 60 | can confuse 61 | .I crontab 62 | and that if you are running inside of 63 | .IR su (8) 64 | you should always use the 65 | .I -u 66 | option for safety's sake. 67 | .PP 68 | The first form of this command is used to install a new crontab from some 69 | named file or standard input if the pseudo-filename ``-'' is given. 70 | .PP 71 | The 72 | .I -l 73 | option causes the current crontab to be displayed on standard output. 74 | .PP 75 | The 76 | .I -r 77 | option causes the current crontab to be removed. 78 | .PP 79 | The 80 | .I -e 81 | option is used to edit the current crontab using the editor specified by 82 | the \s-1VISUAL\s+1 or \s-1EDITOR\s+1 environment variables. After you exit 83 | from the editor, the modified crontab will be installed automatically. 84 | .SH "SEE ALSO" 85 | crontab(5), cron(8) 86 | .SH FILES 87 | .nf 88 | /var/cron/cron.allow 89 | /var/cron/cron.deny 90 | .fi 91 | .SH STANDARDS 92 | The 93 | .I crontab 94 | command conforms to IEEE Std1003.2-1992 (``POSIX''). This new command syntax 95 | differs from previous versions of Vixie Cron, as well as from the classic 96 | SVR3 syntax. 97 | .SH DIAGNOSTICS 98 | A fairly informative usage message appears if you run it with a bad command 99 | line. 100 | .SH AUTHOR 101 | .nf 102 | Paul Vixie 103 | -------------------------------------------------------------------------------- /externs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1993,1994,2021 by Paul Vixie ("VIXIE") 3 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 4 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* reorder these #include's at your peril */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #ifndef isascii 32 | #define isascii(c) ((unsigned)(c)<=0177) 33 | #endif 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #if defined(SYSLOG) 51 | # include 52 | #endif 53 | 54 | #if defined(LOGIN_CAP) 55 | # include 56 | #endif /*LOGIN_CAP*/ 57 | 58 | #if defined(BSD_AUTH) 59 | # include 60 | #endif /*BSD_AUTH*/ 61 | 62 | #define DIR_T struct dirent 63 | #define WAIT_T int 64 | #define SIG_T sig_t 65 | #define TIME_T time_t 66 | #define PID_T pid_t 67 | 68 | #ifndef TZNAME_ALREADY_DEFINED 69 | extern char *tzname[2]; 70 | #endif 71 | #define TZONE(tm) tzname[(tm).tm_isdst] 72 | 73 | #if (defined(BSD)) && (BSD >= 198606) || defined(__linux) 74 | # define HAVE_FCHOWN 75 | # define HAVE_FCHMOD 76 | #endif 77 | 78 | #if (defined(BSD)) && (BSD >= 199103) || defined(__linux) 79 | # define HAVE_SAVED_UIDS 80 | #endif 81 | 82 | #define MY_UID(pw) getuid() 83 | #define MY_GID(pw) getgid() 84 | 85 | /* getopt() isn't part of POSIX. some systems define it in anyway. 86 | * of those that do, some complain that our definition is different and some 87 | * do not. to add to the misery and confusion, some systems define getopt() 88 | * in ways that we cannot predict or comprehend, yet do not define the adjunct 89 | * external variables needed for the interface. 90 | */ 91 | #if (!defined(BSD) || (BSD < 198911)) 92 | int getopt(int, char * const *, const char *); 93 | #endif 94 | 95 | #if (!defined(BSD) || (BSD < 199103)) 96 | extern char *optarg; 97 | extern int optind, opterr, optopt; 98 | #endif 99 | 100 | /* digital unix needs this but does not give us a way to identify it. 101 | */ 102 | extern int flock(int, int); 103 | 104 | /* not all systems who provide flock() provide these definitions. 105 | */ 106 | #ifndef LOCK_SH 107 | # define LOCK_SH 1 108 | #endif 109 | #ifndef LOCK_EX 110 | # define LOCK_EX 2 111 | #endif 112 | #ifndef LOCK_NB 113 | # define LOCK_NB 4 114 | #endif 115 | #ifndef LOCK_UN 116 | # define LOCK_UN 8 117 | #endif 118 | 119 | #ifndef WCOREDUMP 120 | # define WCOREDUMP(st) (((st) & 0200) != 0) 121 | #endif 122 | -------------------------------------------------------------------------------- /Documentation/Changelog/Version 2.md: -------------------------------------------------------------------------------- 1 | 2 | [][#] 3 | [][#] 4 | 5 |
6 | 7 | # Version 2 8 | 9 |
10 | 11 |
12 |
13 | 14 |
15 | 16 | ## 2.2 17 | 18 |   1992   19 | 20 |
21 |
22 | 23 | ## 2.1 24 | 25 |   1991 | May 29th   26 | 27 |
28 |
29 | 30 | ## 2.0 31 | 32 |   1990 | July 5th   33 | 34 |
35 |
36 | 37 | ## 2.0 - Beta 38 | 39 |   1988 | December 9th   40 | 41 |
42 |
43 | 44 | ## Version 1 ⟷ 2 45 | 46 |   1988 | February 8th   47 | 48 |
49 | 50 | Many changes were made in a rash of activity 51 | about six months ago, the exact list of which 52 | is no longer that clear in my memory. 53 | 54 | I know that version 1 used a file called  `POKECRON` , 55 | in the  `/usr/spool/cron`  directory, to tell if it was 56 | time to re-read all CronTab configs. 57 | 58 | Version 2 uses the modification time of config 59 | files in the CronTab directory, where it re-reads 60 | those files whose modification time has changed. 61 | 62 | Note that the crontab (1) command will do a  `utimes`  call to 63 | check if the modification time of the directory has changed. 64 | 65 | This is done since the filename / inode will often remain 66 | the same after a replacement and the modification time 67 | wouldn't change in that case. 68 | 69 |
70 | 71 | ###   1988 | February 8th   72 | 73 |
74 | 75 | **Toerless Eckert** gave me the idea to increase the 76 | environment variable string limit to fix problems 77 | some people had with their  `PATH`  files. 78 | 79 | `100`  🠖  `1000` 80 | 81 |
82 | 83 |
84 | 85 | [][#] 86 | [][#] 87 | 88 |
89 | 90 | ###   1988 | February 16th   91 | 92 |
93 | 94 | - Moved  `/usr/spool/cron/crontabs` 95 | to  `/usr/lib/cron/tabs` 96 | 97 |
98 | 99 | - Added  `allow`  /  `deny` 100 | 101 | ⤷  `/usr/lib/cron/{allow,deny}` 102 | 103 | ⤷  Since the **System V** naming for this depends on  `at` 104 |      using the same directory, which would be stupid. 105 | 106 |
107 | 108 | *Hint, use  `/usr/{lib,spool}/at`* 109 | 110 |
111 | 112 | ###   1988 | February 22nd   113 | 114 |
115 | 116 | Use  `getpwent()`  to read  `passwds` 117 | and try to open each CronTab config. 118 | 119 |                       🠗 120 | 121 | Read the `spool` directory for crontabs
122 | & look each one up using `getpwnam()` 123 | 124 |
125 | 126 | ###   1988 | December 9th   127 | 128 |
129 | 130 | - Now syncs to  `00`  after every minute 131 | 132 | ⤷ Makes **Cron** predictable. 133 | 134 |
135 | 136 | - Added logging to  `/var/cron/log` 137 | 138 |
139 | 140 | ###   1990 | April 14th   141 | 142 |
143 | 144 | *Changes since December 1989* 145 | 146 | - Fixed a number of bugs reported by **John Gilmore**. 147 | 148 | - Added  `syslog`  as per **Keith Bostic**. 149 | 150 | - Added security features: 151 | 152 | Commands can only be run by, the owner
153 | of / with write permission to, the crontab. 154 | 155 | *Not working well yet* 156 | 157 | 158 |
159 | 160 |
161 | 162 | 163 | 164 | 165 | [#]: # 166 | -------------------------------------------------------------------------------- /cron.8: -------------------------------------------------------------------------------- 1 | .\" Copyright 1988,1990,1993,1996,2021 by Paul Vixie ("VIXIE") 2 | .\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 3 | .\" Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 4 | .\" 5 | .\" Permission to use, copy, modify, and distribute this software for any 6 | .\" purpose with or without fee is hereby granted, provided that the above 7 | .\" copyright notice and this permission notice appear in all copies. 8 | .\" 9 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 10 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 12 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 15 | .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | .\" 17 | .\" $Id: cron.8,v 1.8 2004/01/23 19:03:32 vixie Exp $ 18 | .\" 19 | .TH CRON 8 "10 January 1996"" 20 | .UC 4 21 | .SH NAME 22 | cron \- daemon to execute scheduled commands (Vixie Cron) 23 | .SH SYNOPSIS 24 | .B cron 25 | .RB [ \-M 26 | .IR mailer ] 27 | .RB [ \-n ] 28 | .RB [ \-x 29 | .IR flag [ , ... ] ] 30 | .SH DESCRIPTION 31 | .I Cron 32 | should be started from /etc/rc or /etc/rc.local. It will return immediately, 33 | so you don't need to start it with '&'. The \-n option changes this default 34 | behavior causing it to run in the foreground. This can be useful when 35 | starting it out of init. 36 | .PP 37 | .I Cron 38 | searches /var/cron/tabs for crontab files which are named after accounts in 39 | /etc/passwd; crontabs found are loaded into memory. 40 | .I Cron 41 | also searches for /etc/crontab which is in a different format (see 42 | .IR crontab (5)). 43 | .I Cron 44 | then wakes up every minute, examining all stored crontabs, checking each 45 | command to see if it should be run in the current minute. When executing 46 | commands, any output is mailed to the owner of the crontab (or to the user 47 | named in the MAILTO environment variable in the crontab, if such exists). 48 | .PP 49 | The default mailer command is /usr/sbin/sendmail unless overridden with the 50 | \-M command line option when 51 | .I Cron 52 | is invoked. If exactly one %s is present this command string, it will be 53 | replaced by the user name of the invoking cron tab. 54 | .PP 55 | Additionally, 56 | .I cron 57 | checks each minute to see if its spool directory's modtime (or the modtime 58 | on 59 | .IR /etc/crontab ) 60 | has changed, and if it has, 61 | .I cron 62 | will then examine the modtime on all crontabs and reload those which have 63 | changed. Thus 64 | .I cron 65 | need not be restarted whenever a crontab file is modified. Note that the 66 | .IR Crontab (1) 67 | command updates the modtime of the spool directory whenever it changes a 68 | crontab. 69 | .SS Daylight Saving Time and other time changes 70 | Local time changes of less than three hours, such as those caused 71 | by the start or end of Daylight Saving Time, are handled specially. 72 | This only applies to jobs that run at a specific time and jobs that 73 | are run with a granularity greater than one hour. Jobs that run 74 | more frequently are scheduled normally. 75 | .PP 76 | If time has moved forward, those jobs that would have run in the 77 | interval that has been skipped will be run immediately. 78 | Conversely, if time has moved backward, care is taken to avoid running 79 | jobs twice. 80 | .PP 81 | Time changes of more than 3 hours are considered to be corrections to 82 | the clock or timezone, and the new time is used immediately. 83 | .SH SIGNALS 84 | On receipt of a \s-2SIGHUP\s+2, the cron daemon will close and reopen its 85 | log file. This is useful in scripts which rotate and age log files. 86 | Naturally this is not relevant if cron was built to use 87 | .IR syslog (3). 88 | .SH CAVEATS 89 | In this version of 90 | .BR cron , 91 | /etc/crontab must not be readable or writable by any user other than root. 92 | In other words, it should be mode 0600. 93 | .SH "SEE ALSO" 94 | .IR crontab (1), 95 | .IR crontab (5) 96 | .SH AUTHOR 97 | .nf 98 | Paul Vixie 99 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1988,1990,1993,1994,2021 by Paul Vixie ("VIXIE") 3 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 4 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* config.h - configurables for Vixie Cron 20 | * 21 | * $Id: config.h,v 1.11 2006/07/11 02:44:58 vixie Exp $ 22 | */ 23 | 24 | /* 25 | * these are site-dependent 26 | */ 27 | 28 | #ifndef DEBUGGING 29 | #define DEBUGGING 1 /* 1 or 0 -- do you want debugging code built in? */ 30 | #endif 31 | 32 | /* 33 | * choose one of these mailer commands. some use 34 | * /bin/mail for speed; it makes biff bark but doesn't 35 | * do aliasing. sendmail does do aliasing but is 36 | * a hog for short messages. aliasing is not needed 37 | * if you make use of the MAILTO= feature in crontabs. 38 | * (hint: MAILTO= was added for this reason). 39 | */ 40 | 41 | #define MAILFMT "%s -FCronDaemon -odi -oem -oi -t" /*-*/ 42 | /* -Fx = Set full-name of sender 43 | * -odi = Option Deliverymode Interactive 44 | * -oem = Option Errors Mailedtosender 45 | * -oi = Ignore "." alone on a line 46 | * -t = Get recipient from headers 47 | */ 48 | #define MAILARG _PATH_SENDMAIL /*-*/ 49 | 50 | /* #define MAILFMT "%s -d %s" /*-*/ 51 | /* -d = undocumented but common flag: deliver locally? 52 | */ 53 | /* #define MAILARG "/bin/mail",mailto 54 | 55 | /* #define MAILFMT "%s -mlrxto %s" /*-*/ 56 | /* #define MAILARG "/usr/mmdf/bin/submit",mailto /*-*/ 57 | 58 | /* #define MAIL_DATE /*-*/ 59 | /* should we include an ersatz Date: header in 60 | * generated mail? if you are using sendmail 61 | * as the mailer, it is better to let sendmail 62 | * generate the Date: header. 63 | */ 64 | 65 | /* #define MAIL_FROMUSER /*-*/ 66 | /* use this if you want all cron-job e-mail to come 67 | * from the executing user rather than from "root". 68 | */ 69 | 70 | /* if you want to use syslog(3) instead of appending 71 | * to CRONDIR/LOG_FILE (/var/cron/log, e.g.), define 72 | * SYSLOG here. Note that quite a bit of logging 73 | * info is written, and that you probably don't want 74 | * to use this on 4.2bsd since everything goes in 75 | * /usr/spool/mqueue/syslog. On 4.[34]bsd you can 76 | * tell /etc/syslog.conf to send cron's logging to 77 | * a separate file. 78 | * 79 | * Note that if this and LOG_FILE in "pathnames.h" 80 | * are both defined, then logging will go to both 81 | * places. 82 | */ 83 | #define SYSLOG /*-*/ 84 | 85 | /* if you want cron to capitalize its name in ps 86 | * when running a job. Does not work on SYSV. 87 | */ 88 | /*#define CAPITALIZE_FOR_PS /*-*/ 89 | 90 | /* if you have a tm_gmtoff member in struct tm. 91 | * If not, we will have to compute the value ourselves. 92 | */ 93 | /*#define HAVE_TM_GMTOFF /*-*/ 94 | 95 | /* if your OS supports a BSD-style login.conf file */ 96 | /*#define LOGIN_CAP /*-*/ 97 | 98 | /* if your OS supports BSD authentication */ 99 | /*#define BSD_AUTH /*-*/ 100 | 101 | /* Define this to run crontab setgid instead of 102 | * setuid root. Group access will be used to read 103 | * the tabs/atjobs dirs and the allow/deny files. 104 | * If this is not defined then crontab and at 105 | * must be setuid root. 106 | */ 107 | /*#define CRON_GROUP "crontab" /*-*/ 108 | -------------------------------------------------------------------------------- /pw_dup.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2000,2002 Todd C. Miller 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL 9 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 10 | * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE 11 | * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* 18 | * Copyright (c) 2021 by Paul Vixie ("VIXIE") 19 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 20 | * 21 | * Permission to use, copy, modify, and distribute this software for any 22 | * purpose with or without fee is hereby granted, provided that the above 23 | * copyright notice and this permission notice appear in all copies. 24 | * 25 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 26 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 27 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 28 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 29 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 30 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 31 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 32 | */ 33 | #if !defined(lint) && !defined(LINT) 34 | static char rcsid[] = "$Id: pw_dup.c,v 1.2 2004/01/23 18:56:43 vixie Exp $"; 35 | #endif 36 | 37 | #include 38 | 39 | #if !defined(OpenBSD) || OpenBSD < 200105 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "config.h" 47 | 48 | struct passwd * 49 | pw_dup(const struct passwd *pw) { 50 | char *cp; 51 | size_t nsize, psize, csize, gsize, dsize, ssize, total; 52 | struct passwd *newpw; 53 | 54 | /* Allocate in one big chunk for easy freeing */ 55 | total = sizeof(struct passwd); 56 | if (pw->pw_name) { 57 | nsize = strlen(pw->pw_name) + 1; 58 | total += nsize; 59 | } 60 | if (pw->pw_passwd) { 61 | psize = strlen(pw->pw_passwd) + 1; 62 | total += psize; 63 | } 64 | #ifdef LOGIN_CAP 65 | if (pw->pw_class) { 66 | csize = strlen(pw->pw_class) + 1; 67 | total += csize; 68 | } 69 | #endif /* LOGIN_CAP */ 70 | if (pw->pw_gecos) { 71 | gsize = strlen(pw->pw_gecos) + 1; 72 | total += gsize; 73 | } 74 | if (pw->pw_dir) { 75 | dsize = strlen(pw->pw_dir) + 1; 76 | total += dsize; 77 | } 78 | if (pw->pw_shell) { 79 | ssize = strlen(pw->pw_shell) + 1; 80 | total += ssize; 81 | } 82 | if ((cp = malloc(total)) == NULL) 83 | return (NULL); 84 | newpw = (struct passwd *)cp; 85 | 86 | /* 87 | * Copy in passwd contents and make strings relative to space 88 | * at the end of the buffer. 89 | */ 90 | (void)memcpy(newpw, pw, sizeof(struct passwd)); 91 | cp += sizeof(struct passwd); 92 | if (pw->pw_name) { 93 | (void)memcpy(cp, pw->pw_name, nsize); 94 | newpw->pw_name = cp; 95 | cp += nsize; 96 | } 97 | if (pw->pw_passwd) { 98 | (void)memcpy(cp, pw->pw_passwd, psize); 99 | newpw->pw_passwd = cp; 100 | cp += psize; 101 | } 102 | #ifdef LOGIN_CAP 103 | if (pw->pw_class) { 104 | (void)memcpy(cp, pw->pw_class, csize); 105 | newpw->pw_class = cp; 106 | cp += csize; 107 | } 108 | #endif /* LOGIN_CAP */ 109 | if (pw->pw_gecos) { 110 | (void)memcpy(cp, pw->pw_gecos, gsize); 111 | newpw->pw_gecos = cp; 112 | cp += gsize; 113 | } 114 | if (pw->pw_dir) { 115 | (void)memcpy(cp, pw->pw_dir, dsize); 116 | newpw->pw_dir = cp; 117 | cp += dsize; 118 | } 119 | if (pw->pw_shell) { 120 | (void)memcpy(cp, pw->pw_shell, ssize); 121 | newpw->pw_shell = cp; 122 | cp += ssize; 123 | } 124 | 125 | return (newpw); 126 | } 127 | 128 | #endif /* !OpenBSD || OpenBSD < 200105 */ 129 | -------------------------------------------------------------------------------- /macros.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: macros.h,v 1.9 2004/01/23 18:56:43 vixie Exp $ 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2021 by Paul Vixie ("VIXIE") 7 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 8 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 9 | * 10 | * Permission to use, copy, modify, and distribute this software for any 11 | * purpose with or without fee is hereby granted, provided that the above 12 | * copyright notice and this permission notice appear in all copies. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 15 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 17 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 20 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 | */ 22 | 23 | /* these are really immutable, and are 24 | * defined for symbolic convenience only 25 | * TRUE, FALSE, and ERR must be distinct 26 | * ERR must be < OK. 27 | */ 28 | #define TRUE 1 29 | #define FALSE 0 30 | /* system calls return this on success */ 31 | #define OK 0 32 | /* or this on error */ 33 | #define ERR (-1) 34 | 35 | /* turn this on to get '-x' code */ 36 | #ifndef DEBUGGING 37 | #define DEBUGGING FALSE 38 | #endif 39 | 40 | #define INIT_PID 1 /* parent of orphans */ 41 | #define READ_PIPE 0 /* which end of a pipe pair do you read? */ 42 | #define WRITE_PIPE 1 /* or write to? */ 43 | #define STDIN 0 /* what is stdin's file descriptor? */ 44 | #define STDOUT 1 /* stdout's? */ 45 | #define STDERR 2 /* stderr's? */ 46 | #define ERROR_EXIT 1 /* exit() with this will scare the shell */ 47 | #define OK_EXIT 0 /* exit() with this is considered 'normal' */ 48 | #define MAX_FNAME 100 /* max length of internally generated fn */ 49 | #define MAX_COMMAND 1000 /* max length of internally generated cmd */ 50 | #define MAX_ENVSTR 1000 /* max length of envvar=value\0 strings */ 51 | #define MAX_TEMPSTR 100 /* obvious */ 52 | #define MAX_UNAME 33 /* max length of username, should be overkill */ 53 | #define ROOT_UID 0 /* don't change this, it really must be root */ 54 | #define ROOT_USER "root" /* ditto */ 55 | 56 | /* NOTE: these correspond to DebugFlagNames, 57 | * defined below. 58 | */ 59 | #define DEXT 0x0001 /* extend flag for other debug masks */ 60 | #define DSCH 0x0002 /* scheduling debug mask */ 61 | #define DPROC 0x0004 /* process control debug mask */ 62 | #define DPARS 0x0008 /* parsing debug mask */ 63 | #define DLOAD 0x0010 /* database loading debug mask */ 64 | #define DMISC 0x0020 /* misc debug mask */ 65 | #define DTEST 0x0040 /* test mode: don't execute any commands */ 66 | 67 | #define PPC_NULL ((const char **)NULL) 68 | 69 | #ifndef MAXHOSTNAMELEN 70 | #define MAXHOSTNAMELEN 64 71 | #endif 72 | 73 | #define Is_Blank(c) ((c) == '\t' || (c) == ' ') 74 | 75 | #define Skip_Blanks(c, f) \ 76 | while (Is_Blank(c)) \ 77 | c = get_char(f); 78 | 79 | #define Skip_Nonblanks(c, f) \ 80 | while (c!='\t' && c!=' ' && c!='\n' && c != EOF) \ 81 | c = get_char(f); 82 | 83 | #if DEBUGGING 84 | # define Debug(mask, message) \ 85 | if ((DebugFlags & (mask)) != 0) \ 86 | printf message; 87 | #else /* !DEBUGGING */ 88 | # define Debug(mask, message) \ 89 | ; 90 | #endif /* DEBUGGING */ 91 | 92 | #define MkUpper(ch) (islower(ch) ? toupper(ch) : ch) 93 | #define Set_LineNum(ln) {Debug(DPARS|DEXT,("linenum=%d\n",ln)); \ 94 | LineNumber = ln; \ 95 | } 96 | 97 | #ifdef HAVE_TM_GMTOFF 98 | #define get_gmtoff(c, t) ((t)->tm_gmtoff) 99 | #endif 100 | 101 | #define SECONDS_PER_MINUTE 60 102 | #define SECONDS_PER_HOUR 3600 103 | #define SECONDS_PER_DAY 86400 104 | 105 | #define FIRST_MINUTE 0 106 | #define LAST_MINUTE 59 107 | #define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1) 108 | 109 | #define FIRST_HOUR 0 110 | #define LAST_HOUR 23 111 | #define HOUR_COUNT (LAST_HOUR - FIRST_HOUR + 1) 112 | 113 | #define FIRST_DOM 1 114 | #define LAST_DOM 31 115 | #define DOM_COUNT (LAST_DOM - FIRST_DOM + 1) 116 | 117 | #define FIRST_MONTH 1 118 | #define LAST_MONTH 12 119 | #define MONTH_COUNT (LAST_MONTH - FIRST_MONTH + 1) 120 | 121 | /* note on DOW: 0 and 7 are both Sunday, for compatibility reasons. */ 122 | #define FIRST_DOW 0 123 | #define LAST_DOW 7 124 | #define DOW_COUNT (LAST_DOW - FIRST_DOW + 1) 125 | 126 | /* 127 | * Because crontab/at files may be owned by their respective users we 128 | * take extreme care in opening them. If the OS lacks the O_NOFOLLOW 129 | * we will just have to live without it. In order for this to be an 130 | * issue an attacker would have to subvert group CRON_GROUP. 131 | */ 132 | #ifndef O_NOFOLLOW 133 | #define O_NOFOLLOW 0 134 | #endif 135 | -------------------------------------------------------------------------------- /Documentation/Installation.md: -------------------------------------------------------------------------------- 1 | 2 | [][#] 3 | [][#] 4 | 5 |
6 | 7 | # Installation 8 | 9 |
10 | 11 |
12 |
13 | 14 | 1. Read the notes in the  [`Makefile`] 15 | 16 |
17 | 18 | 2. Edit the area marked  `configurable stuff` 19 | 20 |
21 | 22 | 3. Adjust the marked area in  [`config.h`] 23 | 24 |
25 | 26 | 4. Also take a look at  [`pathnames.h`] 27 | 28 |
29 | 30 | 5. Ensure you have a  `/var`  directory or 31 | a  `/usr/var`  folder that is linked to it. 32 | 33 | ```sh 34 | # Creates the /var directory 35 | mkdir /var 36 | ``` 37 | 38 | ```sh 39 | # Creates the /usr/var folder & links it 40 | mkdir /usr/var 41 | ln -s /usr/var /var 42 | ``` 43 | 44 |
45 | 46 | 6. Unless you adjust your  [`Makefile`],  you will also need: 47 | 48 | -   `/usr/local/etc` 49 | -   `/usr/local/bin` 50 | 51 | *These will have to be created by hand.* 52 | 53 |
54 | 55 | 7. I keep my **Man Pages** at  `/usr/local/man` , however since 56 | you probably won't have the source files, this might not apply. 57 | 58 | Therefore you may have to put the man pages into  `/usr/man/manl` , 59 | which will be hard since this will cause name collisions to occur. 60 | 61 | *Note that the man command was originally written by Bill Joy* 62 | *before he left Berkeley, and it contains no AT&T code, so it is in* 63 | *UUNET's archive of freely-distributable BSD code.* 64 | 65 | > **Note** 66 | > `/usr/include/paths.h`  on some **Linux** systems 67 | > shows  `_PATH_SENDMAIL`  to be  `/usr/bin/sendmail` 68 | > even though  `sendmail`  is installed in  `/usr/lib` 69 | > 70 | > *You should check this out.* 71 | 72 |
73 | 74 | [][#] 75 | [][#] 76 | 77 | 8. Built the binary with: 78 | 79 | ```sh 80 | make all 81 | ``` 82 | 83 |
84 | 85 | 9. Install the built program: 86 | 87 | ```sh 88 | su 89 | make install 90 | ``` 91 | 92 | *Note that if I can get you to 'su and say' something just* 93 | *by asking, you have a very serious security problem on* 94 | *your system and you should look into it.* 95 | 96 | Edit your  `/usr/lib/crontab`  file into small pieces. 97 | [*» Check the conversion guide.*][Conversion] 98 | 99 |
100 | 101 | ### Examples 102 | 103 | ```sh 104 | crontab -u uucp -r /usr/lib/uucp/crontab.src 105 | ``` 106 | 107 | ```sh 108 | crontab -u news -r /usr/lib/news/crontab.src 109 | ``` 110 | 111 | ```sh 112 | crontab -u root -r /usr/adm/crontab.src 113 | ``` 114 | 115 |
116 | 117 | #### Notes 118 | 119 | **①**  While installing a config with  `crontab -r`  it's content is copied, 120 |      this means that any changes to the file won't affect the installed 121 |      CronTab until re-installing it. 122 | 123 | **②**  If no user is specified, the one executing the command will be used. 124 | 125 |      The  `-u`  option requires root permissions, however on most 126 |      BSD system being the super user will not automatically make 127 |      CronTab think of you as root, so still specify the user. 128 | 129 | **③**  The  `-r`  is used to replace specified configurable. 130 | 131 |
132 | 133 | 10. Kill your existing Cron daemon: 134 | 135 | ```sh 136 | ps aux # Look for /etc/cron 137 | ``` 138 | 139 |
140 | 141 | 11. Find and comment out any lines starting with 142 | `/etc/cron`  in  `/etc/rc`  /  `/etc/rc.local` 143 | 144 | Insert a new line starting your Cron daemon, 145 | usually  `/usr/local/etc/cron` ➔ [`Makefile`] 146 | 147 |
148 | 149 | 11. The Cron daemon is started and automatically forked with: 150 | 151 | ```sh 152 | /usr/local/etc/cron # Adjust to the path your Cron's path 153 | ``` 154 | 155 | > **Note** 156 | > For those people unfortunate enough to be stuck on a 157 | > AT&T UNIX, you will need the public-domain `libndir`, found 158 | > in the **B News** source and in any `comp.sources.unix` archive. 159 | > 160 | > *You will also need to hack the code some.* 161 | 162 |
163 | 164 | 165 | 166 | 167 | [`pathnames.h`]: ../pathnames.h 168 | [Conversion]: Conversion.md 169 | [`Makefile`]: ../Makefile 170 | [`config.h`]: ../config.h 171 | [#]: # 172 | -------------------------------------------------------------------------------- /Documentation/Mails/1987 January 2nd D.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | **Date:**  6 | 1987 January 2nd  7 | Friday  8 | 19:26:16 EST
9 | **From:**  10 | hplabs  11 | ames  12 | ut-sally  13 | ut-ngp  14 | bigtex  15 | james  
16 | **To:**  17 | vixie  18 | paul
19 | 20 | 21 | 22 | ### Message 23 | 24 | > Yes!!!
25 | > There are several critical failures in System V cron... 26 | > 27 | > 1. Pass all variables in cron's environment into the
28 | > environment of things cron starts up, or at least
29 | > into the crontab entries started up (at jobs will
30 | > inherit the environment of the user). 31 | > 32 | > If nothing else it is critically important
33 | > that the TZ variable be passed on. 34 | > 35 | > PATH should be passed on too. 36 | > 37 | > Basically, passing environment values allows
38 | > one to design a standard environment with
39 | > TZ and PATH and have that run by everything. 40 | > 41 | > If anyone tells you this is no big deal, consider
42 | > what happens when uucico is started by cron in
43 | > CA to make a long distance phone link... 44 | > 45 | > Unless the administrator is really on his/her toes,
46 | > calls scheduled at 5pm will really go at two in the
47 | > afternoon, needlessly incurring huge phone bills,
48 | > all because System V refuses to pass the TZ from
49 | > its environment down. 50 | > 51 | > There are work arounds, but only
52 | > putting it in cron will really work. 53 | > 54 | > This is not a security hole. 55 | 56 | ### Reply 57 | 58 | > delete TERM and TERMCAP; modify HOME, USER,
59 | > and CWD; pass TZ and PATH through undisturbed. 60 | > 61 | > any other requests out there? 62 | > 63 | > BSD doesn't have this problem -- TZ is passed
64 | > right on through if you define it in the shell
65 | > before starting my cron daemon. 66 | > 67 | > However, the BSD I'm running this on
68 | > doesn't need TZ to be defined anyway... 69 | > 70 | > The default in the kernel has been just fine so far... 71 | > 72 | > But just the same, if/when I port to SysV (I guess
73 | > I really should), I'll make sure this works right. 74 | > 75 | > I guess I've been spoiled. 76 | > 77 | > HPUX is SysV-based, and I never had a
78 | > problem with cron and TZ when I used it. 79 | 80 | ### Message 81 | 82 | > 2. A way to avoid logging stuff in /usr/lib/cron/log. 83 | > 84 | > I have a cron entry run uudemon.hr every 10 minutes.
85 | > This is 144 times/day. 86 | > 87 | > Each run generates three lines of text, for a
88 | > total of 432 lines of text I don't want to see. 89 | > 90 | > Obviously this should be optional, but it would be
91 | > nice if there were a way to flag an entry so nice it
92 | > wasn't logged at all unless there was an error. 93 | 94 | ### Reply 95 | 96 | > I don't know nothin' 'bout no /usr/lib/cron/log. 97 | > 98 | > What is this file? 99 | > 100 | > I don't see any reason to create log entries,
101 | > given the mail-the-output behaviour. 102 | > 103 | > Opinions, anyone? 104 | 105 | ### Message 106 | 107 | > I will come up with other ideas no doubt,
108 | > but I can always implement them myself. 109 | 110 | ### Reply 111 | 112 | > That's what I like about PD software. 113 | > 114 | > Please send me the diffs! 115 | 116 | ### Message 117 | 118 | > The other problem you have is making
119 | > sure you can run standard crontabs. 120 | > 121 | > I would suggest something like this:
122 | > 123 | > if the command part of the entry starts with
124 | > an unescaped -, then flags and options follow
125 | > immediately thereafter. 126 | > 127 | > As in: 128 | > 129 | > ```crontab 130 | > 2,12,22,32,42,52 * * * * -q /usr/lib/uucp/uudemon.hr 131 | > ``` 132 | > 133 | > This could mean do not log the uudemon.hr
134 | > run unless there is a problem of some kind. 135 | > 136 | > This is probably safe as not many filenames
137 | > start with "-", and those that do are already
138 | > a problem for people. 139 | 140 | ### Reply 141 | 142 | > Since I don't plan on supporting /usr/lib/cron/log
143 | > in ANY form unless many people request it, I won't
144 | > be needing -q as you've defined it. 145 | > 146 | > I could use something like this to avoid sending
147 | > mail on errors, for the occasional script that you
148 | > don't want to bullet-proof. 149 | > 150 | > The compatibility issue is CRITICAL. 151 | > 152 | > The 4.3BSD crontab format is a crime against the
153 | > whole philosophy of Unix(TM), in my opinion. 154 | 155 | ### Message 156 | 157 | > One other minor thing to consider is the ulimit: 158 | > 159 | > can different users get different
160 | > ulimits for their crontab entries? 161 | 162 | ### Reply 163 | 164 | > Boy I'm ignorant today. 165 | > 166 | > What's a ulimit, and what should
167 | > I do with it in a crontab? 168 | > 169 | > Suggestions, enlightenment, etc ?? -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## 2 | ## Copyright (c) 1988,1990,1993,1994,2021 by Paul Vixie ("VIXIE") 3 | ## Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 4 | ## Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 5 | ## 6 | ## Permission to use, copy, modify, and distribute this software for any 7 | ## purpose with or without fee is hereby granted, provided that the above 8 | ## copyright notice and this permission notice appear in all copies. 9 | ## 10 | ## THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 11 | ## WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | ## MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 13 | ## ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | ## WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | ## ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | ## OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | ## 18 | 19 | # Makefile for VIXIE cron 20 | # 21 | # $Id: Makefile,v 1.9 2004/01/23 18:56:42 vixie Exp $ 22 | # 23 | # vix 03mar88 [moved to RCS, rest of log is in there] 24 | # vix 30mar87 [goodbye, time.c; hello, getopt] 25 | # vix 12feb87 [cleanup for distribution] 26 | # vix 30dec86 [written] 27 | 28 | # NOTES: 29 | # 'make' can be done by anyone 30 | # 'make install' must be done by root 31 | # 32 | # this package needs getopt(3), bitstring(3), and BSD install(8). 33 | # 34 | # the configurable stuff in this makefile consists of compilation 35 | # options (use -O, cron runs forever) and destination directories. 36 | # SHELL is for the 'augumented make' systems where 'make' imports 37 | # SHELL from the environment and then uses it to run its commands. 38 | # if your environment SHELL variable is /bin/csh, make goes real 39 | # slow and sometimes does the wrong thing. 40 | # 41 | # this package needs the 'bitstring macros' library, which is 42 | # available from me or from the comp.sources.unix archive. if you 43 | # put 'bitstring.h' in a non-standard place (i.e., not intuited by 44 | # cc(1)), you will have to define INCLUDE to set the include 45 | # directory for cc. INCLUDE should be `-Isomethingorother'. 46 | # 47 | # there's more configuration info in config.h; edit that first! 48 | 49 | #################################### begin configurable stuff 50 | #<> 51 | DESTROOT = $(DESTDIR)/usr 52 | DESTSBIN = $(DESTROOT)/sbin 53 | DESTBIN = $(DESTROOT)/bin 54 | DESTMAN = $(DESTROOT)/share/man 55 | #<> 56 | INCLUDE = -I. 57 | #INCLUDE = 58 | #<> 59 | LIBS = 60 | #<> 61 | #CDEBUG = -O 62 | CDEBUG = -g 63 | #<> 64 | LINTFLAGS = -hbxa $(INCLUDE) $(DEBUGGING) 65 | #<> 66 | CWARN = -Wall -Wno-unused -Wno-comment 67 | #<> 68 | DEFS = 69 | #(SGI IRIX systems need this) 70 | #DEFS = -D_BSD_SIGNALS -Dconst= 71 | #<> 72 | #INSTALL = installbsd 73 | INSTALL = install 74 | #<> 75 | LDFLAGS = 76 | #################################### end configurable stuff 77 | 78 | SHELL = /bin/sh 79 | CFLAGS = $(CDEBUG) $(CWARN) $(INCLUDE) $(DEFS) 80 | 81 | INFOS = \ 82 | Documentation/Installation.md \ 83 | Documentation/Conversion.md \ 84 | Documentation/Changelog.md \ 85 | Documentation/Configure.md \ 86 | Documentation/Features.md \ 87 | Documentation/Thanks.md \ 88 | Documentation/Mail.md \ 89 | README.md 90 | 91 | MANPAGES = bitstring.3 crontab.5 crontab.1 cron.8 putman.sh 92 | HEADERS = bitstring.h cron.h config.h pathnames.h externs.h \ 93 | macros.h structs.h funcs.h globals.h 94 | SOURCES = cron.c crontab.c database.c do_command.c entry.c \ 95 | env.c job.c user.c popen.c misc.c pw_dup.c 96 | SHAR_SOURCE = $(INFOS) $(MANPAGES) Makefile $(HEADERS) $(SOURCES) 97 | LINT_CRON = cron.c database.c user.c entry.c \ 98 | misc.c job.c do_command.c env.c popen.c pw_dup.c 99 | LINT_CRONTAB = crontab.c misc.c entry.c env.c 100 | CRON_OBJ = cron.o database.o user.o entry.o job.o do_command.o \ 101 | misc.o env.o popen.o pw_dup.o 102 | CRONTAB_OBJ = crontab.o misc.o entry.o env.o pw_dup.o 103 | 104 | all : cron crontab 105 | 106 | lint : 107 | lint $(LINTFLAGS) $(LINT_CRON) $(LIBS) \ 108 | |grep -v "constant argument to NOT" 2>&1 109 | lint $(LINTFLAGS) $(LINT_CRONTAB) $(LIBS) \ 110 | |grep -v "constant argument to NOT" 2>&1 111 | 112 | cron : $(CRON_OBJ) 113 | $(CC) $(LDFLAGS) -o cron $(CRON_OBJ) $(LIBS) 114 | 115 | crontab : $(CRONTAB_OBJ) 116 | $(CC) $(LDFLAGS) -o crontab $(CRONTAB_OBJ) $(LIBS) 117 | 118 | install : all 119 | $(INSTALL) -c -m 111 -o root -s cron $(DESTSBIN)/ 120 | $(INSTALL) -c -m 4111 -o root -s crontab $(DESTBIN)/ 121 | # $(INSTALL) -c -m 111 -o root -g crontab -s cron $(DESTSBIN)/ 122 | # $(INSTALL) -c -m 2111 -o root -g crontab -s crontab $(DESTBIN)/ 123 | sh putman.sh crontab.1 $(DESTMAN) 124 | sh putman.sh cron.8 $(DESTMAN) 125 | sh putman.sh crontab.5 $(DESTMAN) 126 | 127 | distclean : clean 128 | rm -f *.orig *.rej *.BAK *.CKP *~ #* 129 | rm -f a.out core tags 130 | 131 | clean : 132 | rm -f *.o 133 | rm -f cron crontab 134 | 135 | tags :; ctags ${SOURCES} 136 | 137 | kit : $(SHAR_SOURCE) 138 | shar $(SHAR_SOURCE) >kit 139 | 140 | $(CRON_OBJ) : cron.h config.h externs.h pathnames.h Makefile 141 | $(CRONTAB_OBJ) : cron.h config.h externs.h pathnames.h Makefile 142 | -------------------------------------------------------------------------------- /Documentation/Conversion.md: -------------------------------------------------------------------------------- 1 | 2 | [][#] 3 | [][#] 4 | 5 |
6 | 7 | # Conversion 8 | 9 | *of **BSD** **Crontab** files.* 10 | 11 |  BSD 4.2    12 |  BSD 4.3   13 | 14 |
15 | 16 |
17 | 18 |
19 | 20 | ## 📦  Backup 21 | 22 | While **Cron** doesn't use  `/usr/lib/crontab`  config files anymore, 23 | you should keep it or make a backup in case something goes south. 24 | 25 |
26 |
27 | 28 | ## 🪓  Separation 29 | 30 | The main goal of the conversion is split your existing config 31 | into smaller bite sized pieces that each fulfill a specific role. 32 | 33 | *The most significant feature of this Cron is the ability 34 | to move `news` & `uucp` commands into files owned 35 | and maintained by those users.* 36 | 37 |
38 |
39 | 40 | ## 🛡  Super User 41 | 42 | Please remove all the `su` commands from your config. 43 | 44 | *On `BSD 4.3`, there's no need for `su` since the username* 45 | *appears in the command, however I would recommend* 46 | *using separate **Crontabs** for separate environments.* 47 | 48 |
49 |
50 | 51 | ## 🥇  Root 52 | 53 | Most commands in your most **CronTabs** 54 | 55 | 56 | **Are** run by **Root** 57 | **Have** to be run by **Root** 58 | **Should** continue be run by **Root** 59 | 60 |
61 |
62 | 63 | ## 🏗  Structure 64 | 65 | The **recommended** folder layout for your configs. 66 | 67 |
68 | 69 | ### Copying 70 | 71 | The  `crontab`  command copies all used configs 72 | into a protected folder at  `/SPOOL_DIR` 73 | 74 | This means that it generally doesn't matter where 75 | you keep your configs, however choosing a good 76 | location helps to keep things tidy. 77 | 78 |
79 | 80 | ### Naming 81 | 82 | It's recommended to name your config files 83 | 84 | `crontab.src` 85 | 86 |
87 | 88 | | Type | Suggested Location 89 | |:----:|:------------------: 90 | | ***Root*** | `/etc/crontab.src`  **or**  
`/usr/adm/crontab.src` 91 | | ***News*** | `/usr/lib/news/crontab.src` 92 | | ***UUCP*** | `/usr/lib/uucp/crontab.src` 93 | 94 |
95 | 96 |
97 |
98 | 99 | [][#] 100 | [][#] 101 | 102 |
103 | 104 | ## 📥  Installation 105 | 106 | Install your configs with the  `crontab`  command,
107 | probably with  `-u `  ( See man pages ) 108 | 109 | The  `crontab`  command can also be used 110 | to examine, replace and delete a **CronTab**. 111 | 112 |
113 |
114 | 115 | ## 🎬  Examples 116 | 117 | ### 4.2 118 | 119 | *On this version your config might look like this* 120 | 121 | ```crontab 122 | 5 * * * * su uucp < /usr/lib/uucp/uudemon.hr 123 | 10 4 * * * su uucp < /usr/lib/uucp/uudemon.day 124 | 15 5 * * 0 su uucp < /usr/lib/uucp/uudemon.wk 125 | ``` 126 | 127 | *Or this* 128 | 129 | ```crontab 130 | 5 * * * * echo /usr/lib/uucp/uudemon.hr | su uucp 131 | 10 4 * * * echo /usr/lib/uucp/uudemon.day | su uucp 132 | 15 5 * * 0 echo /usr/lib/uucp/uudemon.wk | su uucp 133 | ``` 134 | 135 |
136 | 137 | ### 4.3 138 | 139 | *Here they might look a little bit better already* 140 | 141 | ```crontab 142 | 5 * * * * uucp /usr/lib/uucp/uudemon.hr 143 | 10 4 * * * uucp /usr/lib/uucp/uudemon.day 144 | 15 5 * * 0 uucp /usr/lib/uucp/uudemon.wk 145 | ``` 146 | 147 |
148 | 149 | ### Converted 150 | 151 | In the updated version you would want to create use a dedicated file like 152 | `/usr/lib/uucp/crontab.src`  to put the  `uucp`  specific commands into. 153 | 154 |
155 | 156 | *It might look something like this:* 157 | 158 | ```crontab 159 | # UUCP's Crontab 160 | # at /usr/lib/uucp/crontab.src 161 | 162 | SHELL = /bin/sh 163 | PATH = /usr/lib/uucp:/bin:/usr/bin 164 | HOME = /usr/lib/uucp 165 | 166 | 5 * * * * uudemon.hr 167 | 10 4 * * * uudemon.day 168 | 15 5 * * 0 uudemon.wk 169 | ``` 170 | 171 |
172 | 173 | ### Alternative 174 | 175 | If you run a  `BSD 4.2`  derived **Cron**, you can of course just 176 | install your current **CronTab** in toto as the root's **CronTab**. 177 | 178 | It would work exactly the way your current one does, 179 | barring the additional steps in installing / changing it. 180 | 181 | There would still be advantages to this **Cron** , 182 | mainly getting mail every time there is some 183 | output from your **Cron** commands. 184 | 185 |
186 | 187 |
188 |
189 | 190 | [][#] 191 | [][#] 192 | 193 |
194 | 195 | ## ✉  Mail 196 | 197 | It is likely you will find your commands to generate 198 | a lot of output, after installing this version of **Cron**. 199 | 200 | To mitigate this, you can redirect all **expected** 201 | output to a per-execution log file, to only keep 202 | the output from the last execution. 203 | 204 | *This way only **unexpected** output will be mailed to you.* 205 | 206 | This might take a while to get right, however once it 207 | works, it will be very convenient and worth the effort. 208 | 209 |
210 | 211 |
212 | 213 | 214 | 215 | 216 | [#]: # 217 | -------------------------------------------------------------------------------- /bitstring.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1989, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * This code is derived from software contributed to Berkeley by 6 | * Paul Vixie. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. All advertising materials mentioning features or use of this software 17 | * must display the following acknowledgement: 18 | * This product includes software developed by the University of 19 | * California, Berkeley and its contributors. 20 | * 4. Neither the name of the University nor the names of its contributors 21 | * may be used to endorse or promote products derived from this software 22 | * without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 | * SUCH DAMAGE. 35 | * 36 | * @(#)bitstring.h 8.1 (Berkeley) 7/19/93 37 | */ 38 | 39 | #ifndef _BITSTRING_H_ 40 | #define _BITSTRING_H_ 41 | 42 | typedef unsigned char bitstr_t; 43 | 44 | /* internal macros */ 45 | /* byte of the bitstring bit is in */ 46 | #define _bit_byte(bit) \ 47 | ((bit) >> 3) 48 | 49 | /* mask for the bit within its byte */ 50 | #define _bit_mask(bit) \ 51 | (1 << ((bit)&0x7)) 52 | 53 | /* external macros */ 54 | /* bytes in a bitstring of nbits bits */ 55 | #define bitstr_size(nbits) \ 56 | ((((nbits) - 1) >> 3) + 1) 57 | 58 | /* allocate a bitstring */ 59 | #define bit_alloc(nbits) \ 60 | (bitstr_t *)calloc(1, \ 61 | (unsigned int)bitstr_size(nbits) * sizeof(bitstr_t)) 62 | 63 | /* allocate a bitstring on the stack */ 64 | #define bit_decl(name, nbits) \ 65 | (name)[bitstr_size(nbits)] 66 | 67 | /* is bit N of bitstring name set? */ 68 | #define bit_test(name, bit) \ 69 | ((name)[_bit_byte(bit)] & _bit_mask(bit)) 70 | 71 | /* set bit N of bitstring name */ 72 | #define bit_set(name, bit) \ 73 | (name)[_bit_byte(bit)] |= _bit_mask(bit) 74 | 75 | /* clear bit N of bitstring name */ 76 | #define bit_clear(name, bit) \ 77 | (name)[_bit_byte(bit)] &= ~_bit_mask(bit) 78 | 79 | /* clear bits start ... stop in bitstring */ 80 | #define bit_nclear(name, start, stop) { \ 81 | register bitstr_t *_name = name; \ 82 | register int _start = start, _stop = stop; \ 83 | register int _startbyte = _bit_byte(_start); \ 84 | register int _stopbyte = _bit_byte(_stop); \ 85 | if (_startbyte == _stopbyte) { \ 86 | _name[_startbyte] &= ((0xff >> (8 - (_start&0x7))) | \ 87 | (0xff << ((_stop&0x7) + 1))); \ 88 | } else { \ 89 | _name[_startbyte] &= 0xff >> (8 - (_start&0x7)); \ 90 | while (++_startbyte < _stopbyte) \ 91 | _name[_startbyte] = 0; \ 92 | _name[_stopbyte] &= 0xff << ((_stop&0x7) + 1); \ 93 | } \ 94 | } 95 | 96 | /* set bits start ... stop in bitstring */ 97 | #define bit_nset(name, start, stop) { \ 98 | register bitstr_t *_name = name; \ 99 | register int _start = start, _stop = stop; \ 100 | register int _startbyte = _bit_byte(_start); \ 101 | register int _stopbyte = _bit_byte(_stop); \ 102 | if (_startbyte == _stopbyte) { \ 103 | _name[_startbyte] |= ((0xff << (_start&0x7)) & \ 104 | (0xff >> (7 - (_stop&0x7)))); \ 105 | } else { \ 106 | _name[_startbyte] |= 0xff << ((_start)&0x7); \ 107 | while (++_startbyte < _stopbyte) \ 108 | _name[_startbyte] = 0xff; \ 109 | _name[_stopbyte] |= 0xff >> (7 - (_stop&0x7)); \ 110 | } \ 111 | } 112 | 113 | /* find first bit clear in name */ 114 | #define bit_ffc(name, nbits, value) { \ 115 | register bitstr_t *_name = name; \ 116 | register int _byte, _nbits = nbits; \ 117 | register int _stopbyte = _bit_byte(_nbits), _value = -1; \ 118 | for (_byte = 0; _byte <= _stopbyte; ++_byte) \ 119 | if (_name[_byte] != 0xff) { \ 120 | _value = _byte << 3; \ 121 | for (_stopbyte = _name[_byte]; (_stopbyte&0x1); \ 122 | ++_value, _stopbyte >>= 1); \ 123 | break; \ 124 | } \ 125 | *(value) = _value; \ 126 | } 127 | 128 | /* find first bit set in name */ 129 | #define bit_ffs(name, nbits, value) { \ 130 | register bitstr_t *_name = name; \ 131 | register int _byte, _nbits = nbits; \ 132 | register int _stopbyte = _bit_byte(_nbits), _value = -1; \ 133 | for (_byte = 0; _byte <= _stopbyte; ++_byte) \ 134 | if (_name[_byte]) { \ 135 | _value = _byte << 3; \ 136 | for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \ 137 | ++_value, _stopbyte >>= 1); \ 138 | break; \ 139 | } \ 140 | *(value) = _value; \ 141 | } 142 | 143 | #endif /* !_BITSTRING_H_ */ 144 | -------------------------------------------------------------------------------- /bitstring.3: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 1989, 1991, 1993 2 | .\" The Regents of the University of California. All rights reserved. 3 | .\" 4 | .\" This code is derived from software contributed to Berkeley by 5 | .\" Paul Vixie. 6 | .\" 7 | .\" Redistribution and use in source and binary forms, with or without 8 | .\" modification, are permitted provided that the following conditions 9 | .\" are met: 10 | .\" 1. Redistributions of source code must retain the above copyright 11 | .\" notice, this list of conditions and the following disclaimer. 12 | .\" 2. Redistributions in binary form must reproduce the above copyright 13 | .\" notice, this list of conditions and the following disclaimer in the 14 | .\" documentation and/or other materials provided with the distribution. 15 | .\" 3. All advertising materials mentioning features or use of this software 16 | .\" must display the following acknowledgement: 17 | .\" This product includes software developed by the University of 18 | .\" California, Berkeley and its contributors. 19 | .\" 4. Neither the name of the University nor the names of its contributors 20 | .\" may be used to endorse or promote products derived from this software 21 | .\" without specific prior written permission. 22 | .\" 23 | .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 | .\" SUCH DAMAGE. 34 | .\" 35 | .\" @(#)bitstring.3 8.1 (Berkeley) 7/19/93 36 | .\" 37 | .TH BITSTRING 3 "July 19, 1993" 38 | .SH NAME 39 | \fBbit_alloc\fP, 40 | \fBbit_clear\fP, 41 | \fBbit_decl\fP, 42 | \fBbit_ffs\fP, 43 | \fBbit_nclear\fP, 44 | \fBbit_nset,\fP 45 | \fBbit_set\fP, 46 | \fBbitstr_size\fP, 47 | \fBbit_test\fP 48 | \- bit-string manipulation macros 49 | .SH SYNOPSIS 50 | .nf 51 | \fB#include \fP 52 | 53 | \f2bitstr_t\f1 \f2*\f1 54 | \fBbit_alloc\fP(\f2int\f1 \f2nbits\f1); 55 | 56 | \fBbit_decl\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2nbits\f1); 57 | 58 | \fBbit_clear\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2bit\f1); 59 | 60 | \fBbit_ffc\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2nbits\f1, \f2int\f1 \f2*value\f1); 61 | 62 | \fBbit_ffs\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2nbits\f1, \f2int\f1 \f2*value\f1); 63 | 64 | \fBbit_nclear\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2start\f1, \f2int\f1 \f2stop\f1); 65 | 66 | \fBbit_nset\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2start\f1, \f2int\f1 \f2stop\f1); 67 | 68 | \fBbit_set\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2bit\f1); 69 | 70 | \fBbitstr_size\fP(\f2int\f1 \f2nbits\f1); 71 | 72 | \fBbit_test\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2bit\f1); 73 | .fi 74 | .SH DESCRIPTION 75 | These macros operate on strings of bits. 76 | .PP 77 | The macro \fBbit_alloc\fP() returns a pointer of type 78 | ``\f2bitstr_t\f1 \f2*\f1'' to sufficient space to store 79 | .I nbits 80 | bits, or NULL if no space is available. 81 | .PP 82 | The macro \fBbit_decl\fP() allocates sufficient space to store 83 | .I nbits 84 | bits on the stack. 85 | .PP 86 | The macro \fBbitstr_size\fP() returns the number of elements of type 87 | .I bitstr_t 88 | necessary to store 89 | .I nbits 90 | bits. 91 | This is useful for copying bit strings. 92 | .PP 93 | The macros 94 | \fBbit_clear\fP() 95 | and 96 | \fBbit_set\fP() 97 | clear or set the zero-based numbered bit 98 | .IR bit , 99 | in the bit string 100 | .IR name . 101 | .PP 102 | The 103 | \fBbit_nset\fP() 104 | and 105 | \fBbit_nclear\fP() 106 | macros set or clear the zero-based numbered bits from 107 | .I start 108 | to 109 | .I stop 110 | in the bit string 111 | .IR name . 112 | .PP 113 | The 114 | \fBbit_test\fP() macro evaluates to non-zero if the zero-based numbered bit 115 | .I bit 116 | of bit string 117 | .I name 118 | is set, and zero otherwise. 119 | .PP 120 | The \fBbit_ffs\fP() macro stores in the location referenced by 121 | .I value 122 | the zero-based number of the first bit set in the array of 123 | .I nbits 124 | bits referenced by 125 | .IR name . 126 | If no bits are set, the location referenced by 127 | .I value 128 | is set to \-1. 129 | .PP 130 | The macro \fBbit_ffc\fP() stores in the location referenced by 131 | .I value 132 | the zero-based number of the first bit not set in the array of 133 | .I nbits 134 | bits referenced by 135 | .IR name . 136 | If all bits are set, the location referenced by 137 | .I value 138 | is set to \-1. 139 | .PP 140 | The arguments to these macros are evaluated only once and may safely 141 | have side effects. 142 | .SH EXAMPLE 143 | .nf 144 | .in +5 145 | #include 146 | #include 147 | 148 | #define LPR_BUSY_BIT 0 149 | #define LPR_FORMAT_BIT 1 150 | #define LPR_DOWNLOAD_BIT 2 151 | #define LPR_AVAILABLE_BIT 9 152 | #define LPR_MAX_BITS 10 153 | 154 | make_lpr_available() 155 | { 156 | bitstr_t bit_decl(bitlist, LPR_MAX_BITS); 157 | ... 158 | bit_nclear(bitlist, 0, LPR_MAX_BITS - 1); 159 | ... 160 | if (!bit_test(bitlist, LPR_BUSY_BIT)) { 161 | bit_clear(bitlist, LPR_FORMAT_BIT); 162 | bit_clear(bitlist, LPR_DOWNLOAD_BIT); 163 | bit_set(bitlist, LPR_AVAILABLE_BIT); 164 | } 165 | } 166 | .fi 167 | .SH SEE ALSO 168 | malloc(3) 169 | -------------------------------------------------------------------------------- /popen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1988, 1993, 1994 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * This code is derived from software written by Ken Arnold and 6 | * published in UNIX Review, Vol. 6, No. 8. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. All advertising materials mentioning features or use of this software 17 | * must display the following acknowledgement: 18 | * This product includes software developed by the University of 19 | * California, Berkeley and its contributors. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | * SUCH DAMAGE. 32 | * 33 | */ 34 | 35 | /* this came out of the ftpd sources; it's been modified to avoid the 36 | * globbing stuff since we don't need it. also execvp instead of execv. 37 | */ 38 | 39 | #ifndef lint 40 | #if 0 41 | static sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94"; 42 | #else 43 | static char rcsid[] = "$Id: popen.c,v 1.6 2003/02/16 04:40:01 vixie Exp $"; 44 | #endif 45 | #endif /* not lint */ 46 | 47 | #include "cron.h" 48 | 49 | #define MAX_ARGV 100 50 | #define MAX_GARGV 1000 51 | 52 | /* 53 | * Special version of popen which avoids call to shell. This ensures noone 54 | * may create a pipe to a hidden program as a side effect of a list or dir 55 | * command. 56 | */ 57 | static PID_T *pids; 58 | static int fds; 59 | 60 | FILE * 61 | cron_popen(char *program, char *type, struct passwd *pw) { 62 | char *cp; 63 | FILE *iop; 64 | int argc, pdes[2]; 65 | PID_T pid; 66 | char *argv[MAX_ARGV]; 67 | 68 | if ((*type != 'r' && *type != 'w') || type[1] != '\0') 69 | return (NULL); 70 | 71 | if (!pids) { 72 | if ((fds = sysconf(_SC_OPEN_MAX)) <= 0) 73 | return (NULL); 74 | if (!(pids = (PID_T *)malloc((size_t)(fds * sizeof(PID_T))))) 75 | return (NULL); 76 | bzero(pids, fds * sizeof(PID_T)); 77 | } 78 | if (pipe(pdes) < 0) 79 | return (NULL); 80 | 81 | /* break up string into pieces */ 82 | for (argc = 0, cp = program; argc < MAX_ARGV - 1; cp = NULL) 83 | if (!(argv[argc++] = strtok(cp, " \t\n"))) 84 | break; 85 | argv[MAX_ARGV-1] = NULL; 86 | 87 | switch (pid = vfork()) { 88 | case -1: /* error */ 89 | (void)close(pdes[0]); 90 | (void)close(pdes[1]); 91 | return (NULL); 92 | /* NOTREACHED */ 93 | case 0: /* child */ 94 | if (pw) { 95 | #ifdef LOGIN_CAP 96 | if (setusercontext(0, pw, pw->pw_uid, LOGIN_SETALL) < 0) { 97 | fprintf(stderr, 98 | "setusercontext failed for %s\n", 99 | pw->pw_name); 100 | _exit(ERROR_EXIT); 101 | } 102 | #else 103 | if (setgid(pw->pw_gid) < 0 || 104 | initgroups(pw->pw_name, pw->pw_gid) < 0) { 105 | fprintf(stderr, 106 | "unable to set groups for %s\n", 107 | pw->pw_name); 108 | _exit(1); 109 | } 110 | #if (defined(BSD)) && (BSD >= 199103) 111 | setlogin(pw->pw_name); 112 | #endif /* BSD */ 113 | if (setuid(pw->pw_uid)) { 114 | fprintf(stderr, 115 | "unable to set uid for %s\n", 116 | pw->pw_name); 117 | _exit(1); 118 | } 119 | #endif /* LOGIN_CAP */ 120 | } 121 | if (*type == 'r') { 122 | if (pdes[1] != STDOUT) { 123 | dup2(pdes[1], STDOUT); 124 | (void)close(pdes[1]); 125 | } 126 | dup2(STDOUT, STDERR); /* stderr too! */ 127 | (void)close(pdes[0]); 128 | } else { 129 | if (pdes[0] != STDIN) { 130 | dup2(pdes[0], STDIN); 131 | (void)close(pdes[0]); 132 | } 133 | (void)close(pdes[1]); 134 | } 135 | execvp(argv[0], argv); 136 | _exit(1); 137 | } 138 | 139 | /* parent; assume fdopen can't fail... */ 140 | if (*type == 'r') { 141 | iop = fdopen(pdes[0], type); 142 | (void)close(pdes[1]); 143 | } else { 144 | iop = fdopen(pdes[1], type); 145 | (void)close(pdes[0]); 146 | } 147 | pids[fileno(iop)] = pid; 148 | 149 | return (iop); 150 | } 151 | 152 | int 153 | cron_pclose(FILE *iop) { 154 | int fdes; 155 | PID_T pid; 156 | WAIT_T status; 157 | sigset_t sigset, osigset; 158 | 159 | /* 160 | * pclose returns -1 if stream is not associated with a 161 | * `popened' command, or, if already `pclosed'. 162 | */ 163 | if (pids == 0 || pids[fdes = fileno(iop)] == 0) 164 | return (-1); 165 | (void)fclose(iop); 166 | sigemptyset(&sigset); 167 | sigaddset(&sigset, SIGINT); 168 | sigaddset(&sigset, SIGQUIT); 169 | sigaddset(&sigset, SIGHUP); 170 | sigprocmask(SIG_BLOCK, &sigset, &osigset); 171 | while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR) 172 | continue; 173 | sigprocmask(SIG_SETMASK, &osigset, NULL); 174 | pids[fdes] = 0; 175 | if (pid < 0) 176 | return (pid); 177 | if (WIFEXITED(status)) 178 | return (WEXITSTATUS(status)); 179 | return (1); 180 | } 181 | -------------------------------------------------------------------------------- /env.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1988,1990,1993,1994,2021 by Paul Vixie ("VIXIE") 3 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 4 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #if !defined(lint) && !defined(LINT) 20 | static char rcsid[] = "$Id: env.c,v 1.10 2004/01/23 18:56:42 vixie Exp $"; 21 | #endif 22 | 23 | #include "cron.h" 24 | 25 | char ** 26 | env_init(void) { 27 | char **p = (char **) malloc(sizeof(char **)); 28 | 29 | if (p != NULL) 30 | p[0] = NULL; 31 | return (p); 32 | } 33 | 34 | void 35 | env_free(char **envp) { 36 | char **p; 37 | 38 | for (p = envp; *p != NULL; p++) 39 | free(*p); 40 | free(envp); 41 | } 42 | 43 | char ** 44 | env_copy(char **envp) { 45 | int count, i, save_errno; 46 | char **p; 47 | 48 | for (count = 0; envp[count] != NULL; count++) 49 | NULL; 50 | p = (char **) malloc((count+1) * sizeof(char *)); /* 1 for the NULL */ 51 | if (p != NULL) { 52 | for (i = 0; i < count; i++) 53 | if ((p[i] = strdup(envp[i])) == NULL) { 54 | save_errno = errno; 55 | while (--i >= 0) 56 | free(p[i]); 57 | free(p); 58 | errno = save_errno; 59 | return (NULL); 60 | } 61 | p[count] = NULL; 62 | } 63 | return (p); 64 | } 65 | 66 | char ** 67 | env_set(char **envp, char *envstr) { 68 | int count, found; 69 | char **p, *envtmp; 70 | 71 | /* 72 | * count the number of elements, including the null pointer; 73 | * also set 'found' to -1 or index of entry if already in here. 74 | */ 75 | found = -1; 76 | for (count = 0; envp[count] != NULL; count++) { 77 | if (!strcmp_until(envp[count], envstr, '=')) 78 | found = count; 79 | } 80 | count++; /* for the NULL */ 81 | 82 | if (found != -1) { 83 | /* 84 | * it exists already, so just free the existing setting, 85 | * save our new one there, and return the existing array. 86 | */ 87 | if ((envtmp = strdup(envstr)) == NULL) 88 | return (NULL); 89 | free(envp[found]); 90 | envp[found] = envtmp; 91 | return (envp); 92 | } 93 | 94 | /* 95 | * it doesn't exist yet, so resize the array, move null pointer over 96 | * one, save our string over the old null pointer, and return resized 97 | * array. 98 | */ 99 | if ((envtmp = strdup(envstr)) == NULL) 100 | return (NULL); 101 | p = (char **) realloc((void *) envp, 102 | (size_t) ((count+1) * sizeof(char **))); 103 | if (p == NULL) { 104 | free(envtmp); 105 | return (NULL); 106 | } 107 | p[count] = p[count-1]; 108 | p[count-1] = envtmp; 109 | return (p); 110 | } 111 | 112 | /* The following states are used by load_env(), traversed in order: */ 113 | enum env_state { 114 | NAMEI, /* First char of NAME, may be quote */ 115 | NAME, /* Subsequent chars of NAME */ 116 | EQ1, /* After end of name, looking for '=' sign */ 117 | EQ2, /* After '=', skipping whitespace */ 118 | VALUEI, /* First char of VALUE, may be quote */ 119 | VALUE, /* Subsequent chars of VALUE */ 120 | FINI, /* All done, skipping trailing whitespace */ 121 | ERROR, /* Error */ 122 | }; 123 | 124 | /* return ERR = end of file 125 | * FALSE = not an env setting (file was repositioned) 126 | * TRUE = was an env setting 127 | */ 128 | int 129 | load_env(char *envstr, FILE *f) { 130 | long filepos; 131 | int fileline; 132 | enum env_state state; 133 | char name[MAX_ENVSTR], val[MAX_ENVSTR]; 134 | char quotechar, *c, *str; 135 | 136 | filepos = ftell(f); 137 | fileline = LineNumber; 138 | skip_comments(f); 139 | if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n")) 140 | return (ERR); 141 | 142 | Debug(DPARS, ("load_env, read <%s>\n", envstr)) 143 | 144 | bzero(name, sizeof name); 145 | bzero(val, sizeof val); 146 | str = name; 147 | state = NAMEI; 148 | quotechar = '\0'; 149 | c = envstr; 150 | while (state != ERROR && *c) { 151 | switch (state) { 152 | case NAMEI: 153 | case VALUEI: 154 | if (*c == '\'' || *c == '"') 155 | quotechar = *c++; 156 | state++; 157 | /* FALLTHROUGH */ 158 | case NAME: 159 | case VALUE: 160 | if (quotechar) { 161 | if (*c == quotechar) { 162 | state++; 163 | c++; 164 | break; 165 | } 166 | if (state == NAME && *c == '=') { 167 | state = ERROR; 168 | break; 169 | } 170 | } else { 171 | if (state == NAME) { 172 | if (isspace((unsigned char)*c)) { 173 | c++; 174 | state++; 175 | break; 176 | } 177 | if (*c == '=') { 178 | state++; 179 | break; 180 | } 181 | } 182 | } 183 | *str++ = *c++; 184 | break; 185 | 186 | case EQ1: 187 | if (*c == '=') { 188 | state++; 189 | str = val; 190 | quotechar = '\0'; 191 | } else { 192 | if (!isspace((unsigned char)*c)) 193 | state = ERROR; 194 | } 195 | c++; 196 | break; 197 | 198 | case EQ2: 199 | case FINI: 200 | if (isspace((unsigned char)*c)) 201 | c++; 202 | else 203 | state++; 204 | break; 205 | 206 | default: 207 | abort(); 208 | } 209 | } 210 | if (state != FINI && !(state == VALUE && !quotechar)) { 211 | Debug(DPARS, ("load_env, not an env var, state = %d\n", state)) 212 | fseek(f, filepos, 0); 213 | Set_LineNum(fileline); 214 | return (FALSE); 215 | } 216 | if (state == VALUE) { 217 | /* End of unquoted value: trim trailing whitespace */ 218 | c = val + strlen(val); 219 | while (c > val && isspace((unsigned char)c[-1])) 220 | *(--c) = '\0'; 221 | } 222 | 223 | /* 2 fields from parser; looks like an env setting */ 224 | 225 | /* 226 | * This can't overflow because get_string() limited the size of the 227 | * name and val fields. Still, it doesn't hurt to be careful... 228 | */ 229 | if (!glue_strings(envstr, MAX_ENVSTR, name, val, '=')) 230 | return (FALSE); 231 | Debug(DPARS, ("load_env, <%s> <%s> -> <%s>\n", name, val, envstr)) 232 | return (TRUE); 233 | } 234 | 235 | char * 236 | env_get(char *name, char **envp) { 237 | int len = strlen(name); 238 | char *p, *q; 239 | 240 | while ((p = *envp++) != NULL) { 241 | if (!(q = strchr(p, '='))) 242 | continue; 243 | if ((q - p) == len && !strncmp(p, name, len)) 244 | return (q+1); 245 | } 246 | return (NULL); 247 | } 248 | -------------------------------------------------------------------------------- /crontab.5: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 1988,1990,1993,1994,2021 by Paul Vixie ("VIXIE") 2 | .\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 3 | .\" Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 4 | .\" 5 | .\" Permission to use, copy, modify, and distribute this software for any 6 | .\" purpose with or without fee is hereby granted, provided that the above 7 | .\" copyright notice and this permission notice appear in all copies. 8 | .\" 9 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 10 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 12 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 15 | .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | .\" 17 | .\" $Id: crontab.5,v 1.6 2004/01/23 19:03:33 vixie Exp $ 18 | .\" 19 | .TH CRONTAB 5 "24 January 1994" 20 | .UC 4 21 | .SH NAME 22 | crontab \- tables for driving cron (Vixie Cron) 23 | .SH DESCRIPTION 24 | A 25 | .I crontab 26 | file contains instructions to the 27 | .IR cron (8) 28 | daemon of the general form: ``run this command at this time on this date''. 29 | Each user has their own crontab, and commands in any given crontab will be 30 | executed as the user who owns the crontab. Uucp and News will usually have 31 | their own crontabs, eliminating the need for explicitly running 32 | .IR su (1) 33 | as part of a cron command. 34 | .PP 35 | Blank lines and leading spaces and tabs are ignored. Lines whose first 36 | non-space character is a pound-sign (#) are comments, and are ignored. 37 | Note that comments are not allowed on the same line as cron commands, since 38 | they will be taken to be part of the command. Similarly, comments are not 39 | allowed on the same line as environment variable settings. 40 | .PP 41 | An active line in a crontab will be either an environment setting or a cron 42 | command. An environment setting is of the form, 43 | .PP 44 | name = value 45 | .PP 46 | where the spaces around the equal-sign (=) are optional, and any subsequent 47 | non-leading spaces in 48 | .I value 49 | will be part of the value assigned to 50 | .IR name . 51 | The 52 | .I value 53 | string may be placed in quotes (single or double, but matching) to preserve 54 | leading or trailing blanks. 55 | .PP 56 | Several environment variables are set up 57 | automatically by the 58 | .IR cron (8) 59 | daemon. 60 | SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd 61 | line of the crontab's owner. 62 | HOME and SHELL may be overridden by settings in the crontab; LOGNAME may not. 63 | .PP 64 | (Another note: the LOGNAME variable is sometimes called USER on BSD systems... 65 | on these systems, USER will be set also.) 66 | .PP 67 | In addition to LOGNAME, HOME, and SHELL, 68 | .IR cron (8) 69 | will look at MAILTO if it has any reason to send mail as a result of running 70 | commands in ``this'' crontab. If MAILTO is defined (and non-empty), mail is 71 | sent to the user so named. If MAILTO is defined but empty (MAILTO=""), no 72 | mail will be sent. Otherwise mail is sent to the owner of the crontab. This 73 | option is useful if you decide on /bin/mail instead of /usr/lib/sendmail as 74 | your mailer when you install cron -- /bin/mail doesn't do aliasing, and UUCP 75 | usually doesn't read its mail. 76 | .PP 77 | The format of a cron command is very much the V7 standard, with a number of 78 | upward-compatible extensions. Each line has five time and date fields, 79 | followed by a user name if this is the system crontab file, 80 | followed by a command. Commands are executed by 81 | .IR cron (8) 82 | when the minute, hour, and month of year fields match the current time, 83 | .I and 84 | when at least one of the two day fields (day of month, or day of week) 85 | match the current time (see ``Note'' below). 86 | .IR cron (8) 87 | examines cron entries once every minute. 88 | The time and date fields are: 89 | .IP 90 | .ta 1.5i 91 | field allowed values 92 | .br 93 | ----- -------------- 94 | .br 95 | minute 0-59 96 | .br 97 | hour 0-23 98 | .br 99 | day of month 1-31 (or $, see below) 100 | .br 101 | month 0-12 (or names, see below) 102 | .br 103 | day of week 0-7 (0 or 7 is Sun, or use names) 104 | .br 105 | .PP 106 | A field may be an asterisk (*), which always stands for ``first\-last''. 107 | .PP 108 | Ranges of numbers are allowed. Ranges are two numbers separated 109 | with a hyphen. The specified range is inclusive. For example, 110 | 8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10 111 | and 11. 112 | .PP 113 | Lists are allowed. A list is a set of numbers (or ranges) 114 | separated by commas. Examples: ``1,2,5,9'', ``0-4,8-12''. 115 | .PP 116 | Step values can be used in conjunction with ranges. Following 117 | a range with ``/'' specifies skips of the number's value 118 | through the range. For example, ``0-23/2'' can be used in the hours 119 | field to specify command execution every other hour (the alternative 120 | in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). Steps are 121 | also permitted after an asterisk, so if you want to say ``every two 122 | hours'', just use ``*/2''. 123 | .PP 124 | Names can also be used for the ``month'' and ``day of week'' 125 | fields. Use the first three letters of the particular 126 | day or month (case doesn't matter). Ranges of names are not allowed. 127 | .PP 128 | The ``sixth'' field (the rest of the line) specifies the command to be 129 | run. 130 | The entire command portion of the line, up to a newline or % 131 | character, will be executed by /bin/sh or by the shell 132 | specified in the SHELL variable of the cronfile. 133 | Percent-signs (%) in the command, unless escaped with backslash 134 | (\\), will be changed into newline characters, and all data 135 | after the first % will be sent to the command as standard 136 | input. 137 | .PP 138 | Note: The day of a command's execution can be specified by two 139 | fields \(em day of month, and day of week. If both fields are 140 | restricted (i.e., are not *), the command will be run when 141 | .I either 142 | field matches the current time. For example, 143 | .br 144 | ``30 4 1,15 * 5'' 145 | would cause a command to be run at 4:30 am on the 1st and 15th of each 146 | month, plus every Friday. 147 | .PP 148 | Note: The day-of-month can be given as $, in which case it matches 149 | the last day of the current month. 150 | .SH EXAMPLE CRON FILE 151 | .nf 152 | # use /bin/sh to run commands, no matter what /etc/passwd says 153 | SHELL=/bin/sh 154 | # mail any output to `paul', no matter whose crontab this is 155 | MAILTO=paul 156 | # 157 | # run five minutes after midnight, every day 158 | 5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1 159 | # run at 2:15pm on the first of every month -- output mailed to paul 160 | 15 14 1 * * $HOME/bin/monthly 161 | # run at 10 pm on weekdays, annoy Joe 162 | 0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?% 163 | 23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday" 164 | 5 4 * * sun echo "run at 5 after 4 every sunday" 165 | .fi 166 | .SH SEE ALSO 167 | cron(8), crontab(1) 168 | .SH EXTENSIONS 169 | When specifying day of week, both day 0 and day 7 will be considered Sunday. 170 | BSD and ATT seem to disagree about this. 171 | .PP 172 | Lists and ranges are allowed to co-exist in the same field. "1-3,7-9" would 173 | be rejected by ATT or BSD cron -- they want to see "1-3" or "7,8,9" ONLY. 174 | .PP 175 | Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9". 176 | .PP 177 | Names of months or days of the week can be specified by name. 178 | .PP 179 | Environment variables can be set in the crontab. In BSD or ATT, the 180 | environment handed to child processes is basically the one from /etc/rc. 181 | .PP 182 | Command output is mailed to the crontab owner (BSD can't do this), can be 183 | mailed to a person other than the crontab owner (SysV can't do this), or the 184 | feature can be turned off and no mail will be sent at all (SysV can't do this 185 | either). 186 | .SH AUTHOR 187 | .nf 188 | Paul Vixie 189 | -------------------------------------------------------------------------------- /Documentation/Features.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | [][#] 5 | [][#] 6 | 7 | 8 |
9 | 10 | # Features 11 | 12 | Comparison of ISC to 13 | BSD 4.2 / 3 & System V 14 | 15 |
16 | 17 |
18 |
19 | 20 | ## Environment Variables 21 | 22 | These can be set on a per CronTab basis. 23 | 24 |
25 | 26 | ### Syntax 27 | 28 | ```env 29 | SHELL = /bin/sh 30 | PATH = .:/bin:/usr/bin 31 | Test = 'This is a test' 32 | Hello = "World" 33 | Values = With Spaces 34 | ``` 35 | 36 |
37 | 38 | ### Variables 39 | 40 | *with special meanings.* 41 | 42 |
43 | 44 | #### $\huge\texttt{\textcolor{SkyBlue}{LOGNAME}}$ 45 | 46 |
 47 | Defaults to data in the users `passwd` entry
 48 | 
49 | 50 |
51 | 52 | #### $\huge\texttt{\textcolor{LimeGreen}{MAILTO}}$ 53 | 54 |
 55 | If set, will mail the output of Cron to
 56 | the user with the specified login name
 57 | 
58 | Useful if you decide to use BINMAIL in `cron.h` 59 | as it doesn't know anything about aliasing 60 |
61 | 62 |
63 | 64 | #### $\huge\texttt{\textcolor{SkyBlue}{SHELL}}$ 65 | 66 |
 67 | Defaults to `/bin/sh`
 68 | 
69 | 70 |
71 | 72 | #### $\huge\texttt{\textcolor{LimeGreen}{USER}}$ 73 | 74 |
 75 | Read from the users `passwd`
 76 | entry and cannot be changed
 77 | 
78 | 79 |
80 | 81 | #### $\huge\texttt{\textcolor{SkyBlue}{HOME}}$ 82 | 83 |
 84 | Defaults to data in the users `passwd` entry
 85 | 
86 | 87 |
88 | 89 | #### $\huge\texttt{\textcolor{LimeGreen}{PATH}}$ 90 | 91 |
 92 | Contains the systems paths
 93 | 
94 | 95 |
96 | 97 | #### $\huge\texttt{\textcolor{SkyBlue}{TZ}}$ 98 | 99 |
100 | Can be set, but ignored other
101 | than for the command it runs
102 | 
103 | 104 |
105 |
106 | 107 | [][#] 108 | [][#] 109 | 110 | ```math 111 | \definecolor{Day-A}{RGB}{169,88,78} 112 | \definecolor{Day-B}{RGB}{156,96,101} 113 | \definecolor{Day-C}{RGB}{141,103,125} 114 | \definecolor{Day-D}{RGB}{129,110,147} 115 | \definecolor{Day-E}{RGB}{113,119,174} 116 | \definecolor{Day-F}{RGB}{102,124,191} 117 | \definecolor{Day-G}{RGB}{83,134,223} 118 | ``` 119 | 120 | ## Weekdays 121 | 122 | can be specified with their first  `3` 123 | letters in whatever case you prefer. 124 | 125 |   $\huge\texttt{\textcolor{Day-A}{Mon}}$     126 |   $\huge\texttt{\textcolor{Day-B}{Tue}}$     127 |   $\huge\texttt{\textcolor{Day-C}{Wen}}$     128 |   $\huge\texttt{\textcolor{Day-D}{Thu}}$     129 |   $\huge\texttt{\textcolor{Day-E}{Fri}}$     130 |   $\huge\texttt{\textcolor{Day-F}{Sat}}$     131 |   $\huge\texttt{\textcolor{Day-F}{Sun}}$   132 | 133 |
134 |
135 | 136 | ```math 137 | \definecolor{Month-A}{RGB}{102,191,148} 138 | \definecolor{Month-B}{RGB}{118,190,136} 139 | \definecolor{Month-C}{RGB}{136,190,125} 140 | \definecolor{Month-D}{RGB}{155,189,111} 141 | \definecolor{Month-E}{RGB}{175,189,98} 142 | \definecolor{Month-F}{RGB}{197,188,83} 143 | ``` 144 | 145 | ## Months 146 | 147 | can be specified with their first  `3` 148 | letters in whatever case you prefer. 149 | 150 |   $\huge\texttt{\textcolor{Month-A}{Jan}}$     151 |   $\huge\texttt{\textcolor{Month-B}{Feb}}$     152 |   $\huge\texttt{\textcolor{Month-C}{Mar}}$     153 |   $\huge\texttt{\textcolor{Month-D}{Apr}}$     154 |   $\huge\texttt{\textcolor{Month-E}{May}}$     155 |   $\huge\texttt{\textcolor{Month-F}{Jun}}$   156 | 157 |   $\huge\texttt{\textcolor{Month-A}{Jul}}$     158 |   $\huge\texttt{\textcolor{Month-B}{Aug}}$     159 |   $\huge\texttt{\textcolor{Month-C}{Sep}}$     160 |   $\huge\texttt{\textcolor{Month-D}{Oct}}$     161 |   $\huge\texttt{\textcolor{Month-E}{Nov}}$     162 |   $\huge\texttt{\textcolor{Month-F}{Dec}}$     163 | 164 |
165 |
166 | 167 | ## Mixing 168 | 169 | Unlike standard Cron, ranges & lists can be mixed. 170 | 171 | ``` 172 | 1,3-5 173 | ``` 174 | 175 |
176 |
177 | 178 | ## Stepping 179 | 180 | Ranges can specify a value to step width. 181 | 182 | ``` 183 | 10-16/2 ≍ 10,12,14,16 184 | 10-16/3 ≍ 10,13,16 185 | ``` 186 | 187 |
188 |
189 | 190 | ## Sunday 191 | 192 | The day can be specified with both  `0`  and  `7` 193 | which **BSD** and **ATT** apparently disagree about. 194 | 195 |
196 |
197 | 198 | ## User 199 | 200 | Every user has their own CronTab config, 201 | as it is also done in **System V**'s Cron flavor. 202 | 203 |
204 | 205 | ### BSD 4.2 206 | 207 | In this version of BSD, only the root 208 | user could have a CronTab config. 209 | 210 |
211 | 212 | ### BSD 4.3 213 | 214 | - The **CronTab** format was made incompatible. 215 | 216 | - Non-root UIDs could run the command. 217 | 218 | - Only root could edit the **CronTab** file. 219 | 220 |
221 |
222 | 223 | ## System V 224 | 225 | The  `crontab`  command is loosely compatible with 226 | **System V**, but has been extended with more options. 227 | 228 | Running the command without arguments 229 | shows a summary on how to use of the it. 230 | 231 | [][#] 232 | [][#] 233 | 234 |
235 |
236 | 237 | ## Comments 238 | 239 | - Must be on a line by themselves 240 | 241 | - Can have leading whitespace 242 | 243 | - `#`  starts the comment 244 | 245 |
246 |
247 | 248 | ## Auto Reloading 249 | 250 | If the  `crontab`  command makes any changes, 251 | the Cron daemon will reload all tables before the 252 | next iteration automatically. 253 | 254 |
255 | 256 | ### Other Crons 257 | 258 | - May require you have to kill and restart the daemon. 259 | 260 | - May unnecessarily read & parse the config continuously. 261 | 262 |
263 |
264 | 265 | ## Access 266 | 267 | The **CronTab** files cannot be read nor modified other 268 | than with the  `crontab`  /  `cron`  commands, this is 269 | to allow for automatic reloading. 270 | 271 | This shouldn't pose a problem however, as  `crontab` 272 | allows you to adjust everything in your own **CronTab**. 273 | 274 | With root you can of course access anyone's **CronTab**. 275 | 276 |
277 |
278 | 279 | ## MailTo 280 | 281 | Any output generated by commands to  `stdout`  and 282 | `stderr`  will be mailed to the owner of the **CronTab**. 283 | 284 | Alternatively the  `MAILTO`  variable is used. 285 | ⤷ Check the **[Environment Variable]** section. 286 | 287 |
288 | 289 | ### Header 290 | 291 | The email messages header will include: 292 | 293 | - The command that was run by Cron 294 | 295 | - The list of passed environment variables of 296 | which the following will always be present: 297 | 298 | `SHELL`    `HOME`    `USER`    `LOGNAME` 299 | 300 | *LogName is only passed on System V* 301 | 302 |
303 |
304 | 305 | [][#] 306 | [][#] 307 | 308 | ## DOM / DOW 309 | 310 | The situation is odd. 311 | 312 |
313 | 314 | `* * 1,15 * Sun`  will run on: 315 | 316 | 1st  |  15th  |  Sundays 317 | 318 |
319 | 320 | `* * * * Sun`  will *only* run on: 321 | 322 | Sundays 323 | 324 |
325 | 326 | `* * 1,15 * *`  will *only* run on: 327 | 328 | 1st  |  15th 329 | 330 |
331 | 332 | This is why we keep  `e -> dow_star`  &  `e -> dom_star` 333 | 334 | I didn't think up this behavior, it's how **Cron** 335 | has always worked but the documentation 336 | hasn't been very clear. 337 | 338 | I have been told that some **AT&T Crons** do not 339 | act this way and do the more reasonable thing, 340 | which is - in my honest opinion - to  `or`  the 341 | various field - matches together. 342 | 343 | In that sense this **Cron** isn't completely 344 | similar to some of the **AT&T Crons** . 345 | 346 |
347 | 348 | 349 | 350 | 351 | [Environment Variable]: EnvironmentVariables 352 | [#]: # 353 | -------------------------------------------------------------------------------- /database.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1988,1990,1993,1994,2021 by Paul Vixie ("VIXIE") 3 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 4 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #if !defined(lint) && !defined(LINT) 20 | static char rcsid[] = "$Id: database.c,v 1.7 2004/01/23 18:56:42 vixie Exp $"; 21 | #endif 22 | 23 | /* vix 26jan87 [RCS has the log] 24 | */ 25 | 26 | #include "cron.h" 27 | 28 | #define TMAX(a,b) (is_greater_than(a,b)?(a):(b)) 29 | #define TEQUAL(a,b) (a.tv_sec == b.tv_sec && a.tv_nsec == b.tv_nsec) 30 | 31 | static bool 32 | is_greater_than(struct timespec left, struct timespec right) { 33 | if (left.tv_sec > right.tv_sec) 34 | return TRUE; 35 | else if (left.tv_sec < right.tv_sec) 36 | return FALSE; 37 | return left.tv_nsec > right.tv_nsec; 38 | } 39 | 40 | 41 | static void process_crontab(const char *, const char *, 42 | const char *, struct stat *, 43 | cron_db *, cron_db *); 44 | 45 | void 46 | load_database(cron_db *old_db) { 47 | struct stat statbuf, syscron_stat; 48 | cron_db new_db; 49 | DIR_T *dp; 50 | DIR *dir; 51 | user *u, *nu; 52 | 53 | Debug(DLOAD, ("[%ld] load_database()\n", (long)getpid())) 54 | 55 | /* before we start loading any data, do a stat on SPOOL_DIR 56 | * so that if anything changes as of this moment (i.e., before we've 57 | * cached any of the database), we'll see the changes next time. 58 | */ 59 | if (stat(SPOOL_DIR, &statbuf) < OK) { 60 | log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR); 61 | (void) exit(ERROR_EXIT); 62 | } 63 | 64 | /* track system crontab file 65 | */ 66 | if (stat(SYSCRONTAB, &syscron_stat) < OK) 67 | syscron_stat.st_mtim = ts_zero; 68 | 69 | /* if spooldir's mtime has not changed, we don't need to fiddle with 70 | * the database. 71 | * 72 | * Note that old_db->mtime is initialized to 0 in main(), and 73 | * so is guaranteed to be different than the stat() mtime the first 74 | * time this function is called. 75 | */ 76 | if (TEQUAL(old_db->mtim, TMAX(statbuf.st_mtim, syscron_stat.st_mtim))) { 77 | Debug(DLOAD, ("[%ld] spool dir mtime unch, no load needed.\n", 78 | (long)getpid())) 79 | return; 80 | } 81 | 82 | /* something's different. make a new database, moving unchanged 83 | * elements from the old database, reloading elements that have 84 | * actually changed. Whatever is left in the old database when 85 | * we're done is chaff -- crontabs that disappeared. 86 | */ 87 | new_db.mtim = TMAX(statbuf.st_mtim, syscron_stat.st_mtim); 88 | new_db.head = new_db.tail = NULL; 89 | 90 | if (!TEQUAL(syscron_stat.st_mtim, ts_zero)) 91 | process_crontab("root", NULL, SYSCRONTAB, &syscron_stat, 92 | &new_db, old_db); 93 | 94 | /* we used to keep this dir open all the time, for the sake of 95 | * efficiency. however, we need to close it in every fork, and 96 | * we fork a lot more often than the mtime of the dir changes. 97 | */ 98 | if (!(dir = opendir(SPOOL_DIR))) { 99 | log_it("CRON", getpid(), "OPENDIR FAILED", SPOOL_DIR); 100 | (void) exit(ERROR_EXIT); 101 | } 102 | 103 | while (NULL != (dp = readdir(dir))) { 104 | char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1]; 105 | 106 | /* avoid file names beginning with ".". this is good 107 | * because we would otherwise waste two guaranteed calls 108 | * to getpwnam() for . and .., and also because user names 109 | * starting with a period are just too nasty to consider. 110 | */ 111 | if (dp->d_name[0] == '.') 112 | continue; 113 | 114 | if (strlen(dp->d_name) >= sizeof fname) 115 | continue; /* XXX log? */ 116 | (void) strcpy(fname, dp->d_name); 117 | 118 | if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, 119 | fname, '/')) 120 | continue; /* XXX log? */ 121 | 122 | process_crontab(fname, fname, tabname, 123 | &statbuf, &new_db, old_db); 124 | } 125 | closedir(dir); 126 | 127 | /* if we don't do this, then when our children eventually call 128 | * getpwnam() in do_command.c's child_process to verify MAILTO=, 129 | * they will screw us up (and v-v). 130 | */ 131 | endpwent(); 132 | 133 | /* whatever's left in the old database is now junk. 134 | */ 135 | Debug(DLOAD, ("unlinking old database:\n")) 136 | for (u = old_db->head; u != NULL; u = nu) { 137 | Debug(DLOAD, ("\t%s\n", u->name)) 138 | nu = u->next; 139 | unlink_user(old_db, u); 140 | free_user(u); 141 | } 142 | 143 | /* overwrite the database control block with the new one. 144 | */ 145 | *old_db = new_db; 146 | Debug(DLOAD, ("load_database is done\n")) 147 | } 148 | 149 | void 150 | link_user(cron_db *db, user *u) { 151 | if (db->head == NULL) 152 | db->head = u; 153 | if (db->tail) 154 | db->tail->next = u; 155 | u->prev = db->tail; 156 | u->next = NULL; 157 | db->tail = u; 158 | } 159 | 160 | void 161 | unlink_user(cron_db *db, user *u) { 162 | if (u->prev == NULL) 163 | db->head = u->next; 164 | else 165 | u->prev->next = u->next; 166 | 167 | if (u->next == NULL) 168 | db->tail = u->prev; 169 | else 170 | u->next->prev = u->prev; 171 | } 172 | 173 | user * 174 | find_user(cron_db *db, const char *name) { 175 | user *u; 176 | 177 | for (u = db->head; u != NULL; u = u->next) 178 | if (strcmp(u->name, name) == 0) 179 | break; 180 | return (u); 181 | } 182 | 183 | static void 184 | process_crontab(const char *uname, const char *fname, const char *tabname, 185 | struct stat *statbuf, cron_db *new_db, cron_db *old_db) 186 | { 187 | struct passwd *pw = NULL; 188 | int crontab_fd = OK - 1; 189 | user *u; 190 | 191 | if (fname == NULL) { 192 | /* must be set to something for logging purposes. 193 | */ 194 | fname = "*system*"; 195 | } else if ((pw = getpwnam(uname)) == NULL) { 196 | /* file doesn't have a user in passwd file. 197 | */ 198 | log_it(fname, getpid(), "ORPHAN", "no passwd entry"); 199 | goto next_crontab; 200 | } 201 | 202 | if ((crontab_fd = open(tabname, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0)) < OK) { 203 | /* crontab not accessible? 204 | */ 205 | log_it(fname, getpid(), "CAN'T OPEN", tabname); 206 | goto next_crontab; 207 | } 208 | 209 | if (fstat(crontab_fd, statbuf) < OK) { 210 | log_it(fname, getpid(), "FSTAT FAILED", tabname); 211 | goto next_crontab; 212 | } 213 | if (!S_ISREG(statbuf->st_mode)) { 214 | log_it(fname, getpid(), "NOT REGULAR", tabname); 215 | goto next_crontab; 216 | } 217 | if ((statbuf->st_mode & 07777) != 0600) { 218 | log_it(fname, getpid(), "BAD FILE MODE", tabname); 219 | goto next_crontab; 220 | } 221 | if (statbuf->st_uid != ROOT_UID && (pw == NULL || 222 | statbuf->st_uid != pw->pw_uid || strcmp(uname, pw->pw_name) != 0)) { 223 | log_it(fname, getpid(), "WRONG FILE OWNER", tabname); 224 | goto next_crontab; 225 | } 226 | if (statbuf->st_nlink != 1) { 227 | log_it(fname, getpid(), "BAD LINK COUNT", tabname); 228 | goto next_crontab; 229 | } 230 | 231 | Debug(DLOAD, ("\t%s:", fname)) 232 | u = find_user(old_db, fname); 233 | if (u != NULL) { 234 | /* if crontab has not changed since we last read it 235 | * in, then we can just use our existing entry. 236 | */ 237 | if (TEQUAL(u->mtim, statbuf->st_mtim)) { 238 | Debug(DLOAD, (" [no change, using old data]")) 239 | unlink_user(old_db, u); 240 | link_user(new_db, u); 241 | goto next_crontab; 242 | } 243 | 244 | /* before we fall through to the code that will reload 245 | * the user, let's deallocate and unlink the user in 246 | * the old database. This is more a point of memory 247 | * efficiency than anything else, since all leftover 248 | * users will be deleted from the old database when 249 | * we finish with the crontab... 250 | */ 251 | Debug(DLOAD, (" [delete old data]")) 252 | unlink_user(old_db, u); 253 | free_user(u); 254 | log_it(fname, getpid(), "RELOAD", tabname); 255 | } 256 | u = load_user(crontab_fd, pw, fname); 257 | if (u != NULL) { 258 | u->mtim = statbuf->st_mtim; 259 | link_user(new_db, u); 260 | } 261 | 262 | next_crontab: 263 | if (crontab_fd >= OK) { 264 | Debug(DLOAD, (" [done]\n")) 265 | close(crontab_fd); 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /Documentation/Changelog/Version 3.md: -------------------------------------------------------------------------------- 1 | 2 | [][#] 3 | [][#] 4 | 5 |
6 | 7 | # Version 3 8 | 9 |
10 | 11 |
12 |
13 | 14 |
15 | 16 | ## Patch 2 17 | 18 |   1994 | December 12th   19 | 20 | - `gethostname()`  is now available in  `compat.c` 21 | 22 | - Fixed various  `flock()`  problems. 23 | 24 | - Fixed environment importing. 25 | 26 | - `Coherent`  is now supported. 27 | 28 |
29 |
30 | 31 | ## 3.1 32 | 33 |   1993 | Some time after ..   34 | 35 |
36 |
37 | 38 | ## 3.0 39 | 40 |   1993 | December 27th   41 | 42 |
43 |
44 | 45 | ## Version 2 ⟷ 3 46 | 47 |   1993 | December 29th   48 | 49 |
50 | 51 | - The  `crontab`  command now conforms to `POSIX 1003.2` 52 | 53 | This means that when you install this Cron and you 54 | have any  `crontab`  command lines floating around 55 | in shell scripts such as  `/etc/rc`  or  `/etc/rc.local` 56 | you will need to change them. 57 | 58 |
59 | 60 | - I have integrated several changes made by **BSDi** for their 61 | `BSD / 386`  operating system, these were offered to me 62 | before I started consulting for them, so it is safe to say 63 | that they were intended for publication. 64 | 65 |
66 | 67 | For compatibility with  `BSD 4.3`  the name of 68 | the daemon was changed:  `crond`  🠖  `cron` 69 | 70 |
71 | 72 | For the same reason, support for reading 73 | the  `/etc/crontab`  file has been added. 74 | 75 | It differs from other configs, in that every 76 | entry has an extra field for a username. 77 | 78 | ``` 79 |
266 | 267 |
268 | 269 | 270 | 271 | 272 | [`database.c`]: ../../database.c 273 | [`cron.h`]: ../../cron.h 274 | [`misc.c`]: ../../misc.c 275 | 276 | [#]: # 277 | -------------------------------------------------------------------------------- /cron.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1988,1990,1993,1994,2021 by Paul Vixie ("VIXIE") 3 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 4 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #if !defined(lint) && !defined(LINT) 20 | static char rcsid[] = "$Id: cron.c,v 1.12 2004/01/23 18:56:42 vixie Exp $"; 21 | #endif 22 | 23 | #define MAIN_PROGRAM 24 | 25 | #include "cron.h" 26 | 27 | enum timejump { negative, small, medium, large }; 28 | 29 | static void usage(void), 30 | run_reboot_jobs(cron_db *), 31 | find_jobs(int, cron_db *, int, int), 32 | set_time(int), 33 | cron_sleep(int), 34 | sigchld_handler(int), 35 | sighup_handler(int), 36 | sigchld_reaper(void), 37 | quit(int), 38 | parse_args(int c, char *v[]); 39 | 40 | static volatile sig_atomic_t got_sighup, got_sigchld; 41 | static int timeRunning, virtualTime, clockTime; 42 | static long GMToff; 43 | 44 | static void 45 | usage(void) { 46 | const char **dflags; 47 | 48 | fprintf(stderr, "usage: %s [-n] [-x [", ProgramName); 49 | for (dflags = DebugFlagNames; *dflags; dflags++) 50 | fprintf(stderr, "%s%s", *dflags, dflags[1] ? "," : "]"); 51 | fprintf(stderr, "]\n"); 52 | exit(ERROR_EXIT); 53 | } 54 | 55 | int 56 | main(int argc, char *argv[]) { 57 | struct sigaction sact; 58 | cron_db database; 59 | int fd; 60 | 61 | ProgramName = argv[0]; 62 | 63 | setlocale(LC_ALL, ""); 64 | 65 | #if defined(BSD) 66 | setlinebuf(stdout); 67 | setlinebuf(stderr); 68 | #endif 69 | 70 | NoFork = 0; 71 | parse_args(argc, argv); 72 | 73 | bzero((char *)&sact, sizeof sact); 74 | sigemptyset(&sact.sa_mask); 75 | sact.sa_flags = 0; 76 | #ifdef SA_RESTART 77 | sact.sa_flags |= SA_RESTART; 78 | #endif 79 | sact.sa_handler = sigchld_handler; 80 | (void) sigaction(SIGCHLD, &sact, NULL); 81 | sact.sa_handler = sighup_handler; 82 | (void) sigaction(SIGHUP, &sact, NULL); 83 | sact.sa_handler = quit; 84 | (void) sigaction(SIGINT, &sact, NULL); 85 | (void) sigaction(SIGTERM, &sact, NULL); 86 | 87 | acquire_daemonlock(0); 88 | set_cron_uid(); 89 | set_cron_cwd(); 90 | 91 | if (putenv("PATH="_PATH_DEFPATH) < 0) { 92 | log_it("CRON", getpid(), "DEATH", "can't malloc"); 93 | exit(1); 94 | } 95 | 96 | /* if there are no debug flags turned on, fork as a daemon should. 97 | */ 98 | if (DebugFlags) { 99 | #if DEBUGGING 100 | (void) fprintf(stderr, "[%ld] cron started\n", (long)getpid()); 101 | #endif 102 | } else if (NoFork == 0) { 103 | switch (fork()) { 104 | case -1: 105 | log_it("CRON",getpid(),"DEATH","can't fork"); 106 | exit(0); 107 | break; 108 | case 0: 109 | /* child process */ 110 | (void) setsid(); 111 | if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) >= 0) { 112 | (void) dup2(fd, STDIN); 113 | (void) dup2(fd, STDOUT); 114 | (void) dup2(fd, STDERR); 115 | if (fd != STDERR) 116 | (void) close(fd); 117 | } 118 | log_it("CRON",getpid(),"STARTUP",CRON_VERSION); 119 | break; 120 | default: 121 | /* parent process should just die */ 122 | _exit(0); 123 | } 124 | } 125 | 126 | acquire_daemonlock(0); 127 | database.head = NULL; 128 | database.tail = NULL; 129 | database.mtim = ts_zero; 130 | load_database(&database); 131 | set_time(TRUE); 132 | run_reboot_jobs(&database); 133 | timeRunning = virtualTime = clockTime; 134 | 135 | /* 136 | * Too many clocks, not enough time (Al. Einstein) 137 | * These clocks are in minutes since the epoch, adjusted for timezone. 138 | * virtualTime: is the time it *would* be if we woke up 139 | * promptly and nobody ever changed the clock. It is 140 | * monotonically increasing... unless a timejump happens. 141 | * At the top of the loop, all jobs for 'virtualTime' have run. 142 | * timeRunning: is the time we last awakened. 143 | * clockTime: is the time when set_time was last called. 144 | */ 145 | while (TRUE) { 146 | int timeDiff; 147 | enum timejump wakeupKind; 148 | 149 | /* ... wait for the time (in minutes) to change ... */ 150 | do { 151 | cron_sleep(timeRunning + 1); 152 | set_time(FALSE); 153 | } while (clockTime == timeRunning); 154 | timeRunning = clockTime; 155 | 156 | /* 157 | * Calculate how the current time differs from our virtual 158 | * clock. Classify the change into one of 4 cases. 159 | */ 160 | timeDiff = timeRunning - virtualTime; 161 | 162 | /* shortcut for the most common case */ 163 | if (timeDiff == 1) { 164 | virtualTime = timeRunning; 165 | find_jobs(virtualTime, &database, TRUE, TRUE); 166 | } else { 167 | if (timeDiff > (3*MINUTE_COUNT) || 168 | timeDiff < -(3*MINUTE_COUNT)) 169 | wakeupKind = large; 170 | else if (timeDiff > 5) 171 | wakeupKind = medium; 172 | else if (timeDiff > 0) 173 | wakeupKind = small; 174 | else 175 | wakeupKind = negative; 176 | 177 | switch (wakeupKind) { 178 | case small: 179 | /* 180 | * case 1: timeDiff is a small positive number 181 | * (wokeup late) run jobs for each virtual 182 | * minute until caught up. 183 | */ 184 | Debug(DSCH, ("[%ld], normal case %d minutes to go\n", 185 | (long)getpid(), timeDiff)) 186 | do { 187 | if (job_runqueue()) 188 | sleep(10); 189 | virtualTime++; 190 | find_jobs(virtualTime, &database, 191 | TRUE, TRUE); 192 | } while (virtualTime < timeRunning); 193 | break; 194 | 195 | case medium: 196 | /* 197 | * case 2: timeDiff is a medium-sized positive 198 | * number, for example because we went to DST 199 | * run wildcard jobs once, then run any 200 | * fixed-time jobs that would otherwise be 201 | * skipped if we use up our minute (possible, 202 | * if there are a lot of jobs to run) go 203 | * around the loop again so that wildcard jobs 204 | * have a chance to run, and we do our 205 | * housekeeping. 206 | */ 207 | Debug(DSCH, ("[%ld], DST begins %d minutes to go\n", 208 | (long)getpid(), timeDiff)) 209 | /* run wildcard jobs for current minute */ 210 | find_jobs(timeRunning, &database, TRUE, FALSE); 211 | 212 | /* run fixed-time jobs for each minute missed */ 213 | do { 214 | if (job_runqueue()) 215 | sleep(10); 216 | virtualTime++; 217 | find_jobs(virtualTime, &database, 218 | FALSE, TRUE); 219 | set_time(FALSE); 220 | } while (virtualTime< timeRunning && 221 | clockTime == timeRunning); 222 | break; 223 | 224 | case negative: 225 | /* 226 | * case 3: timeDiff is a small or medium-sized 227 | * negative num, eg. because of DST ending. 228 | * Just run the wildcard jobs. The fixed-time 229 | * jobs probably have already run, and should 230 | * not be repeated. Virtual time does not 231 | * change until we are caught up. 232 | */ 233 | Debug(DSCH, ("[%ld], DST ends %d minutes to go\n", 234 | (long)getpid(), timeDiff)) 235 | find_jobs(timeRunning, &database, TRUE, FALSE); 236 | break; 237 | default: 238 | /* 239 | * other: time has changed a *lot*, 240 | * jump virtual time, and run everything 241 | */ 242 | Debug(DSCH, ("[%ld], clock jumped\n", 243 | (long)getpid())) 244 | virtualTime = timeRunning; 245 | find_jobs(timeRunning, &database, TRUE, TRUE); 246 | } 247 | } 248 | 249 | /* Jobs to be run (if any) are loaded; clear the queue. */ 250 | job_runqueue(); 251 | 252 | /* Check to see if we received a signal while running jobs. */ 253 | if (got_sighup) { 254 | got_sighup = 0; 255 | log_close(); 256 | } 257 | if (got_sigchld) { 258 | got_sigchld = 0; 259 | sigchld_reaper(); 260 | } 261 | load_database(&database); 262 | } 263 | } 264 | 265 | static void 266 | run_reboot_jobs(cron_db *db) { 267 | user *u; 268 | entry *e; 269 | 270 | for (u = db->head; u != NULL; u = u->next) { 271 | for (e = u->crontab; e != NULL; e = e->next) { 272 | if (e->flags & WHEN_REBOOT) 273 | job_add(e, u); 274 | } 275 | } 276 | (void) job_runqueue(); 277 | } 278 | 279 | static void 280 | find_jobs(int vtime, cron_db *db, int doWild, int doNonWild) { 281 | const time_t virtualSecond = vtime * SECONDS_PER_MINUTE; 282 | const time_t virtualTomorrow = virtualSecond + SECONDS_PER_DAY; 283 | struct tm now, tom; 284 | const struct tm * const now_r = gmtime_r(&virtualSecond, &now); 285 | const struct tm * const tom_r = gmtime_r(&virtualTomorrow, &tom); 286 | 287 | /* make 0-based values out of these so we can use them as indicies 288 | */ 289 | const int minute = now.tm_min -FIRST_MINUTE; 290 | const int hour = now.tm_hour -FIRST_HOUR; 291 | const int dom = now.tm_mday -FIRST_DOM; 292 | const int month = now.tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; 293 | const int dow = now.tm_wday -FIRST_DOW; 294 | 295 | Debug(DSCH, ("[%ld] tick(%d,%d,%d,%d,%d) %s %s\n", 296 | (long)getpid(), minute, hour, dom, month, dow, 297 | doWild?" ":"No wildcard",doNonWild?" ":"Wildcard only")) 298 | 299 | /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the 300 | * first and fifteenth AND every Sunday; '* * * * Sun' will run *only* 301 | * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this 302 | * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre. 303 | * like many bizarre things, it's the standard. 304 | */ 305 | const bool is_lastdom = (tom.tm_mday == 1); 306 | for (const user *u = db->head; u != NULL; u = u->next) { 307 | for (const entry *e = u->crontab; e != NULL; e = e->next) { 308 | Debug(DSCH|DEXT, ("user [%s:%ld:%ld:...] cmd=\"%s\"\n", 309 | e->pwd->pw_name, (long)e->pwd->pw_uid, 310 | (long)e->pwd->pw_gid, e->cmd)) 311 | bool thisdom = bit_test(e->dom, dom) || 312 | (is_lastdom && (e->flags & DOM_LAST) != 0); 313 | bool thisdow = bit_test(e->dow, dow); 314 | if (bit_test(e->minute, minute) && 315 | bit_test(e->hour, hour) && 316 | bit_test(e->month, month) && 317 | ((e->flags & (DOM_STAR|DOW_STAR)) != 0 318 | ? (thisdom && thisdow) 319 | : (thisdom || thisdow)) 320 | ) { 321 | if ((doNonWild && 322 | (e->flags & (MIN_STAR|HR_STAR)) == 0) || 323 | (doWild && 324 | (e->flags & (MIN_STAR|HR_STAR)) != 0) 325 | ) 326 | job_add(e, u); 327 | } 328 | } 329 | } 330 | } 331 | 332 | /* 333 | * Set StartTime and clockTime to the current time. 334 | * These are used for computing what time it really is right now. 335 | * Note that clockTime is a unix wallclock time converted to minutes. 336 | */ 337 | static void 338 | set_time(int initialize) { 339 | struct tm tm; 340 | static int isdst; 341 | 342 | StartTime = time(NULL); 343 | 344 | /* We adjust the time to GMT so we can catch DST changes. */ 345 | tm = *localtime(&StartTime); 346 | if (initialize || tm.tm_isdst != isdst) { 347 | isdst = tm.tm_isdst; 348 | GMToff = get_gmtoff(&StartTime, &tm); 349 | Debug(DSCH, ("[%ld] GMToff=%ld\n", 350 | (long)getpid(), (long)GMToff)) 351 | } 352 | clockTime = (StartTime + GMToff) / (time_t)SECONDS_PER_MINUTE; 353 | } 354 | 355 | /* 356 | * Try to just hit the next minute. 357 | */ 358 | static void 359 | cron_sleep(int target) { 360 | time_t t1, t2; 361 | int seconds_to_wait; 362 | 363 | t1 = time(NULL) + GMToff; 364 | seconds_to_wait = (int)(target * SECONDS_PER_MINUTE - t1) + 1; 365 | Debug(DSCH, ("[%ld] Target time=%ld, sec-to-wait=%d\n", 366 | (long)getpid(), (long)target*SECONDS_PER_MINUTE, seconds_to_wait)) 367 | 368 | while (seconds_to_wait > 0 && seconds_to_wait < 65) { 369 | sleep((unsigned int) seconds_to_wait); 370 | 371 | /* 372 | * Check to see if we were interrupted by a signal. 373 | * If so, service the signal(s) then continue sleeping 374 | * where we left off. 375 | */ 376 | if (got_sighup) { 377 | got_sighup = 0; 378 | log_close(); 379 | } 380 | if (got_sigchld) { 381 | got_sigchld = 0; 382 | sigchld_reaper(); 383 | } 384 | t2 = time(NULL) + GMToff; 385 | seconds_to_wait -= (int)(t2 - t1); 386 | t1 = t2; 387 | } 388 | } 389 | 390 | static void 391 | sighup_handler(int x) { 392 | got_sighup = 1; 393 | } 394 | 395 | static void 396 | sigchld_handler(int x) { 397 | got_sigchld = 1; 398 | } 399 | 400 | static void 401 | quit(int x) { 402 | (void) unlink(_PATH_CRON_PID); 403 | _exit(0); 404 | } 405 | 406 | static void 407 | sigchld_reaper(void) { 408 | WAIT_T waiter; 409 | PID_T pid; 410 | 411 | do { 412 | pid = waitpid(-1, &waiter, WNOHANG); 413 | switch (pid) { 414 | case -1: 415 | if (errno == EINTR) 416 | continue; 417 | Debug(DPROC, 418 | ("[%ld] sigchld...no children\n", 419 | (long)getpid())) 420 | break; 421 | case 0: 422 | Debug(DPROC, 423 | ("[%ld] sigchld...no dead kids\n", 424 | (long)getpid())) 425 | break; 426 | default: 427 | Debug(DPROC, 428 | ("[%ld] sigchld...pid #%ld died, stat=%d\n", 429 | (long)getpid(), (long)pid, WEXITSTATUS(waiter))) 430 | break; 431 | } 432 | } while (pid > 0); 433 | } 434 | 435 | static void 436 | parse_args(int argc, char *argv[]) { 437 | int argch; 438 | 439 | while (-1 != (argch = getopt(argc, argv, "nM:x:"))) { 440 | switch (argch) { 441 | default: 442 | usage(); 443 | case 'x': 444 | if (!set_debug_flags(optarg)) 445 | usage(); 446 | break; 447 | case 'M': 448 | if (strlen(optarg) == 0) 449 | usage(); 450 | Mailer = optarg; 451 | break; 452 | case 'n': 453 | NoFork = 1; 454 | break; 455 | } 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /do_command.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1988,1990,1993,1994,2021 by Paul Vixie ("VIXIE") 3 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 4 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #if !defined(lint) && !defined(LINT) 20 | static char rcsid[] = "$Id: do_command.c,v 1.12 2021/02/07 00:20:00 vixie Exp $"; 21 | #endif 22 | 23 | #include "cron.h" 24 | 25 | static void child_process(const entry *, const user *); 26 | static int safe_p(const char *, const char *); 27 | 28 | void 29 | do_command(const entry *e, const user *u) { 30 | Debug(DPROC, ("[%ld] do_command(%s, (%s,%ld,%ld))\n", 31 | (long)getpid(), e->cmd, u->name, 32 | (long)e->pwd->pw_uid, (long)e->pwd->pw_gid)) 33 | 34 | /* fork to become asynchronous -- parent process is done immediately, 35 | * and continues to run the normal cron code, which means return to 36 | * tick(). the child and grandchild don't leave this function, alive. 37 | * 38 | * vfork() is unsuitable, since we have much to do, and the parent 39 | * needs to be able to run off and fork other processes. 40 | */ 41 | switch (fork()) { 42 | case -1: 43 | log_it("CRON", getpid(), "error", "can't fork"); 44 | break; 45 | case 0: 46 | /* child process */ 47 | acquire_daemonlock(1); 48 | child_process(e, u); 49 | Debug(DPROC, ("[%ld] child process done, exiting\n", 50 | (long)getpid())) 51 | _exit(OK_EXIT); 52 | break; 53 | default: 54 | /* parent process */ 55 | break; 56 | } 57 | Debug(DPROC, ("[%ld] main process returning to work\n",(long)getpid())) 58 | } 59 | 60 | static void 61 | child_process(const entry *e, const user *u) { 62 | int stdin_pipe[2], stdout_pipe[2]; 63 | char *input_data, *usernm, *mailto; 64 | int children = 0; 65 | 66 | Debug(DPROC, ("[%ld] child_process('%s')\n", (long)getpid(), e->cmd)) 67 | 68 | #ifdef CAPITALIZE_FOR_PS 69 | /* mark ourselves as different to PS command watchers by upshifting 70 | * our program name. This has no effect on some kernels. 71 | */ 72 | /*local*/{ 73 | char *pch; 74 | 75 | for (pch = ProgramName; *pch; pch++) 76 | *pch = MkUpper(*pch); 77 | } 78 | #endif /* CAPITALIZE_FOR_PS */ 79 | 80 | /* discover some useful and important environment settings 81 | */ 82 | usernm = e->pwd->pw_name; 83 | mailto = env_get("MAILTO", e->envp); 84 | 85 | /* our parent is watching for our death by catching SIGCHLD. we 86 | * do not care to watch for our childrens' deaths this way -- we 87 | * use wait() explicitly. so we have to reset the signal (which 88 | * was inherited from the parent). 89 | */ 90 | (void) signal(SIGCHLD, SIG_DFL); 91 | 92 | /* create some pipes to talk to our future child 93 | */ 94 | pipe(stdin_pipe); /* child's stdin */ 95 | pipe(stdout_pipe); /* child's stdout */ 96 | 97 | /* since we are a forked process, we can modify the command string 98 | * we were passed -- nobody else is going to use it again, right? 99 | * 100 | * if a % is present in the command, previous characters are the 101 | * command, and subsequent characters are the additional input to 102 | * the command. An escaped % will have the escape character stripped 103 | * from it. Subsequent %'s will be transformed into newlines, 104 | * but that happens later. 105 | */ 106 | /*local*/{ 107 | int escaped = FALSE; 108 | int ch; 109 | char *p; 110 | 111 | for (input_data = p = e->cmd; 112 | (ch = *input_data) != '\0'; 113 | input_data++, p++) { 114 | if (p != input_data) 115 | *p = ch; 116 | if (escaped) { 117 | if (ch == '%') 118 | *--p = ch; 119 | escaped = FALSE; 120 | continue; 121 | } 122 | if (ch == '\\') { 123 | escaped = TRUE; 124 | continue; 125 | } 126 | if (ch == '%') { 127 | *input_data++ = '\0'; 128 | break; 129 | } 130 | } 131 | *p = '\0'; 132 | } 133 | 134 | /* fork again, this time so we can exec the user's command. 135 | */ 136 | switch (vfork()) { 137 | case -1: 138 | log_it("CRON", getpid(), "error", "can't vfork"); 139 | exit(ERROR_EXIT); 140 | /*NOTREACHED*/ 141 | case 0: 142 | Debug(DPROC, ("[%ld] grandchild process vfork()'ed\n", 143 | (long)getpid())) 144 | 145 | /* write a log message. we've waited this long to do it 146 | * because it was not until now that we knew the PID that 147 | * the actual user command shell was going to get and the 148 | * PID is part of the log message. 149 | */ 150 | if ((e->flags & DONT_LOG) == 0) { 151 | char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); 152 | 153 | log_it(usernm, getpid(), "CMD", x); 154 | free(x); 155 | } 156 | 157 | /* that's the last thing we'll log. close the log files. 158 | */ 159 | log_close(); 160 | 161 | /* get new pgrp, void tty, etc. 162 | */ 163 | (void) setsid(); 164 | 165 | /* close the pipe ends that we won't use. this doesn't affect 166 | * the parent, who has to read and write them; it keeps the 167 | * kernel from recording us as a potential client TWICE -- 168 | * which would keep it from sending SIGPIPE in otherwise 169 | * appropriate circumstances. 170 | */ 171 | close(stdin_pipe[WRITE_PIPE]); 172 | close(stdout_pipe[READ_PIPE]); 173 | 174 | /* grandchild process. make std{in,out} be the ends of 175 | * pipes opened by our daddy; make stderr go to stdout. 176 | */ 177 | if (stdin_pipe[READ_PIPE] != STDIN) { 178 | dup2(stdin_pipe[READ_PIPE], STDIN); 179 | close(stdin_pipe[READ_PIPE]); 180 | } 181 | if (stdout_pipe[WRITE_PIPE] != STDOUT) { 182 | dup2(stdout_pipe[WRITE_PIPE], STDOUT); 183 | close(stdout_pipe[WRITE_PIPE]); 184 | } 185 | dup2(STDOUT, STDERR); 186 | 187 | /* set our directory, uid and gid. Set gid first, since once 188 | * we set uid, we've lost root privledges. 189 | */ 190 | #ifdef LOGIN_CAP 191 | { 192 | #ifdef BSD_AUTH 193 | auth_session_t *as; 194 | #endif 195 | login_cap_t *lc; 196 | char **p; 197 | extern char **environ; 198 | 199 | if ((lc = login_getclass(e->pwd->pw_class)) == NULL) { 200 | fprintf(stderr, 201 | "unable to get login class for %s\n", 202 | e->pwd->pw_name); 203 | _exit(ERROR_EXIT); 204 | } 205 | if (setusercontext(lc, e->pwd, e->pwd->pw_uid, LOGIN_SETALL) < 0) { 206 | fprintf(stderr, 207 | "setusercontext failed for %s\n", 208 | e->pwd->pw_name); 209 | _exit(ERROR_EXIT); 210 | } 211 | #ifdef BSD_AUTH 212 | as = auth_open(); 213 | if (as == NULL || auth_setpwd(as, e->pwd) != 0) { 214 | fprintf(stderr, "can't malloc\n"); 215 | _exit(ERROR_EXIT); 216 | } 217 | if (auth_approval(as, lc, usernm, "cron") <= 0) { 218 | fprintf(stderr, "approval failed for %s\n", 219 | e->pwd->pw_name); 220 | _exit(ERROR_EXIT); 221 | } 222 | auth_close(as); 223 | #endif /* BSD_AUTH */ 224 | login_close(lc); 225 | 226 | /* If no PATH specified in crontab file but 227 | * we just added one via login.conf, add it to 228 | * the crontab environment. 229 | */ 230 | if (env_get("PATH", e->envp) == NULL && environ != NULL) { 231 | for (p = environ; *p; p++) { 232 | if (strncmp(*p, "PATH=", 5) == 0) { 233 | e->envp = env_set(e->envp, *p); 234 | break; 235 | } 236 | } 237 | } 238 | } 239 | #else 240 | setgid(e->pwd->pw_gid); 241 | initgroups(usernm, e->pwd->pw_gid); 242 | #if (defined(BSD)) && (BSD >= 199103) 243 | setlogin(usernm); 244 | #endif /* BSD */ 245 | if (setuid(e->pwd->pw_uid) < 0) { 246 | perror("setuid"); 247 | _exit(ERROR_EXIT); 248 | } 249 | /* we aren't root after this... */ 250 | 251 | #endif /* LOGIN_CAP */ 252 | chdir(env_get("HOME", e->envp)); 253 | 254 | /* 255 | * Exec the command. 256 | */ 257 | { 258 | char *shell = env_get("SHELL", e->envp); 259 | 260 | # if DEBUGGING 261 | if (DebugFlags & DTEST) { 262 | fprintf(stderr, 263 | "debug DTEST is on, not exec'ing command.\n"); 264 | fprintf(stderr, 265 | "\tcmd='%s' shell='%s'\n", e->cmd, shell); 266 | _exit(OK_EXIT); 267 | } 268 | # endif /*DEBUGGING*/ 269 | execle(shell, shell, "-c", e->cmd, (char *)0, e->envp); 270 | fprintf(stderr, "execl: couldn't exec `%s'\n", shell); 271 | perror("execl"); 272 | _exit(ERROR_EXIT); 273 | } 274 | break; 275 | default: 276 | /* parent process */ 277 | break; 278 | } 279 | 280 | children++; 281 | 282 | /* middle process, child of original cron, parent of process running 283 | * the user's command. 284 | */ 285 | 286 | Debug(DPROC, ("[%ld] child continues, closing pipes\n",(long)getpid())) 287 | 288 | /* close the ends of the pipe that will only be referenced in the 289 | * grandchild process... 290 | */ 291 | close(stdin_pipe[READ_PIPE]); 292 | close(stdout_pipe[WRITE_PIPE]); 293 | 294 | /* write, to the pipe connected to child's stdin, any input specified 295 | * after a % in the crontab entry. while we copy, convert any 296 | * additional %'s to newlines. when done, if some characters were 297 | * written and the last one wasn't a newline, write a newline. 298 | * 299 | * Note that if the input data won't fit into one pipe buffer (2K 300 | * or 4K on most BSD systems), and the child doesn't read its stdin, 301 | * we would block here. thus we must fork again. 302 | */ 303 | 304 | if (*input_data && fork() == 0) { 305 | FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); 306 | int need_newline = FALSE; 307 | int escaped = FALSE; 308 | int ch; 309 | 310 | Debug(DPROC, ("[%ld] child2 sending data to grandchild\n", 311 | (long)getpid())) 312 | 313 | /* close the pipe we don't use, since we inherited it and 314 | * are part of its reference count now. 315 | */ 316 | close(stdout_pipe[READ_PIPE]); 317 | 318 | /* translation: 319 | * \% -> % 320 | * % -> \n 321 | * \x -> \x for all x != % 322 | */ 323 | while ((ch = *input_data++) != '\0') { 324 | if (escaped) { 325 | if (ch != '%') 326 | putc('\\', out); 327 | } else { 328 | if (ch == '%') 329 | ch = '\n'; 330 | } 331 | 332 | if (!(escaped = (ch == '\\'))) { 333 | putc(ch, out); 334 | need_newline = (ch != '\n'); 335 | } 336 | } 337 | if (escaped) 338 | putc('\\', out); 339 | if (need_newline) 340 | putc('\n', out); 341 | 342 | /* close the pipe, causing an EOF condition. fclose causes 343 | * stdin_pipe[WRITE_PIPE] to be closed, too. 344 | */ 345 | fclose(out); 346 | 347 | Debug(DPROC, ("[%ld] child2 done sending to grandchild\n", 348 | (long)getpid())) 349 | exit(0); 350 | } 351 | 352 | /* close the pipe to the grandkiddie's stdin, since its wicked uncle 353 | * ernie back there has it open and will close it when he's done. 354 | */ 355 | close(stdin_pipe[WRITE_PIPE]); 356 | 357 | children++; 358 | 359 | /* read output from the grandchild. its stderr has been redirected to 360 | * it's stdout, which has been redirected to our pipe. if there is any 361 | * output, we'll be mailing it to the user whose crontab this is... 362 | * when the grandchild exits, we'll get EOF. 363 | */ 364 | 365 | Debug(DPROC, ("[%ld] child reading output from grandchild\n", 366 | (long)getpid())) 367 | 368 | /*local*/{ 369 | FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); 370 | int ch = getc(in); 371 | 372 | if (ch != EOF) { 373 | FILE *mail; 374 | int bytes = 1; 375 | int status = 0; 376 | 377 | Debug(DPROC|DEXT, 378 | ("[%ld] got data (%x:%c) from grandchild\n", 379 | (long)getpid(), ch, ch)) 380 | 381 | /* get name of recipient. this is MAILTO if set to a 382 | * valid local username; USER otherwise. 383 | */ 384 | if (mailto) { 385 | /* MAILTO was present in the environment 386 | */ 387 | if (!*mailto) { 388 | /* ... but it's empty. set to NULL 389 | */ 390 | mailto = NULL; 391 | } 392 | } else { 393 | /* MAILTO not present, set to USER. 394 | */ 395 | mailto = usernm; 396 | } 397 | 398 | /* if the resulting mailto isn't safe, don't use it. 399 | */ 400 | if (mailto != NULL && !safe_p(usernm, mailto)) 401 | mailto = NULL; 402 | 403 | /* if we are supposed to be mailing, MAILTO will 404 | * be non-NULL. only in this case should we set 405 | * up the mail command and subjects and stuff... 406 | */ 407 | if (mailto != NULL) { 408 | char mailcmd[MAX_COMMAND] = ""; 409 | const char *msg = NULL; 410 | 411 | if (Mailer != NULL) { 412 | if (strcountstr(Mailer, "%s") == 1) { 413 | if (strlens(Mailer, 414 | mailto, 415 | NULL) 416 | - strlen("%s") 417 | + sizeof "" 418 | > sizeof mailcmd) { 419 | msg = "Mailer ovf 1"; 420 | } else { 421 | (void) sprintf(mailcmd, 422 | Mailer, 423 | mailto); 424 | } 425 | } else { 426 | if (strlen(Mailer) + sizeof "" 427 | > sizeof mailcmd) { 428 | msg = "Mailer ovf 2"; 429 | } else { 430 | (void) strcpy(mailcmd, 431 | Mailer); 432 | } 433 | } 434 | } else { 435 | if (strlens(MAILFMT, MAILARG, NULL) 436 | + sizeof "" 437 | > sizeof mailcmd) { 438 | msg = "mailcmd too long"; 439 | } else { 440 | (void) sprintf(mailcmd, 441 | MAILFMT, 442 | MAILARG); 443 | } 444 | } 445 | if (msg != NULL) { 446 | fputs(msg, stderr); 447 | fputc('\n', stderr); 448 | (void) _exit(ERROR_EXIT); 449 | } 450 | mail = cron_popen(mailcmd, "w", e->pwd); 451 | if (mail == NULL) { 452 | perror(mailcmd); 453 | mailto = NULL; 454 | } 455 | } 456 | 457 | /* if we succeeded in getting a mailer opened up, 458 | * send the headers and first character of body. 459 | */ 460 | if (mailto != NULL) { 461 | char hostname[MAXHOSTNAMELEN]; 462 | char **env; 463 | 464 | gethostname(hostname, MAXHOSTNAMELEN); 465 | #ifdef MAIL_FROMUSER 466 | fprintf(mail, "From: %s\n", usernm); 467 | #else 468 | fprintf(mail, "From: root (Cron Daemon)\n"); 469 | #endif 470 | fprintf(mail, "To: %s\n", mailto); 471 | fprintf(mail, "Subject: Cron <%s@%s> %s\n", 472 | usernm, first_word(hostname, "."), 473 | e->cmd); 474 | #ifdef MAIL_DATE 475 | fprintf(mail, "Date: %s\n", 476 | arpadate(&StartTime)); 477 | #endif /*MAIL_DATE*/ 478 | for (env = e->envp; *env; env++) 479 | fprintf(mail, "X-Cron-Env: <%s>\n", 480 | *env); 481 | fprintf(mail, "\n"); 482 | 483 | /* this was the first char from the pipe 484 | */ 485 | putc(ch, mail); 486 | } 487 | 488 | /* we have to read the input pipe no matter whether 489 | * we mail or not, but obviously we only write to 490 | * mail pipe if we ARE mailing. 491 | */ 492 | 493 | while (EOF != (ch = getc(in))) { 494 | bytes++; 495 | if (mailto != NULL) 496 | putc(ch, mail); 497 | } 498 | 499 | /* only close pipe if we opened it -- i.e., we're 500 | * mailing... 501 | */ 502 | 503 | if (mailto != NULL) { 504 | Debug(DPROC, ("[%ld] closing pipe to mail\n", 505 | (long)getpid())) 506 | /* Note: the pclose will probably see 507 | * the termination of the grandchild 508 | * in addition to the mail process, since 509 | * it (the grandchild) is likely to exit 510 | * after closing its stdout. 511 | */ 512 | status = cron_pclose(mail); 513 | } 514 | 515 | /* if there was output and we could not mail it, 516 | * log the facts so the poor user can figure out 517 | * what's going on. 518 | */ 519 | if (mailto && status) { 520 | char buf[MAX_TEMPSTR]; 521 | 522 | sprintf(buf, 523 | "mailed %d byte%s of output but got status 0x%04x\n", 524 | bytes, (bytes==1)?"":"s", 525 | status); 526 | log_it(usernm, getpid(), "MAIL", buf); 527 | } 528 | 529 | } /*if data from grandchild*/ 530 | 531 | Debug(DPROC, ("[%ld] got EOF from grandchild\n", 532 | (long)getpid())) 533 | 534 | fclose(in); /* also closes stdout_pipe[READ_PIPE] */ 535 | } 536 | 537 | /* wait for children to die. 538 | */ 539 | for (; children > 0; children--) { 540 | WAIT_T waiter; 541 | PID_T pid; 542 | 543 | Debug(DPROC, ("[%ld] waiting for grandchild #%d to finish\n", 544 | (long)getpid(), children)) 545 | while ((pid = wait(&waiter)) < OK && errno == EINTR) 546 | ; 547 | if (pid < OK) { 548 | Debug(DPROC, 549 | ("[%ld] no more grandchildren--mail written?\n", 550 | (long)getpid())) 551 | break; 552 | } 553 | Debug(DPROC, ("[%ld] grandchild #%ld finished, status=%04x", 554 | (long)getpid(), (long)pid, WEXITSTATUS(waiter))) 555 | if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) 556 | Debug(DPROC, (", dumped core")) 557 | Debug(DPROC, ("\n")) 558 | } 559 | } 560 | 561 | static int 562 | safe_p(const char *usernm, const char *s) { 563 | static const char safe_delim[] = "@!:%-.,"; /* conservative! */ 564 | const char *t; 565 | int ch, first; 566 | 567 | for (t = s, first = 1; (ch = *t++) != '\0'; first = 0) { 568 | if (isascii(ch) && isprint(ch) && 569 | (isalnum(ch) || (!first && strchr(safe_delim, ch)))) 570 | continue; 571 | log_it(usernm, getpid(), "UNSAFE", s); 572 | return (FALSE); 573 | } 574 | return (TRUE); 575 | } 576 | -------------------------------------------------------------------------------- /entry.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1988,1990,1993,1994,2021 by Paul Vixie ("VIXIE") 3 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 4 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND VIXIE DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VIXIE BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #if !defined(lint) && !defined(LINT) 20 | static char rcsid[] = "$Id: entry.c,v 1.17 2004/01/23 18:56:42 vixie Exp $"; 21 | #endif 22 | 23 | /* vix 26jan87 [RCS'd; rest of log is in RCS file] 24 | * vix 01jan87 [added line-level error recovery] 25 | * vix 31dec86 [added /step to the from-to range, per bob@acornrc] 26 | * vix 30dec86 [written] 27 | */ 28 | 29 | #include "cron.h" 30 | 31 | typedef enum ecode { 32 | e_none, e_minute, e_hour, e_dom, e_month, e_dow, 33 | e_cmd, e_timespec, e_username, e_option, e_memory 34 | } ecode_e; 35 | 36 | static const char *ecodes[] = 37 | { 38 | "no error", 39 | "bad minute", 40 | "bad hour", 41 | "bad day-of-month", 42 | "bad month", 43 | "bad day-of-week", 44 | "bad command", 45 | "bad time specifier", 46 | "bad username", 47 | "bad option", 48 | "out of memory" 49 | }; 50 | 51 | static int get_list(bitstr_t *, int, int, const char *[], int, FILE *), 52 | get_range(bitstr_t *, int, int, const char *[], int, FILE *), 53 | get_number(int *, int, const char *[], int, FILE *, const char *), 54 | set_element(bitstr_t *, int, int, int), 55 | set_range(bitstr_t *, int, int, int, int, int); 56 | 57 | void 58 | free_entry(entry *e) { 59 | free(e->cmd); 60 | free(e->pwd); 61 | env_free(e->envp); 62 | free(e); 63 | } 64 | 65 | /* return NULL if eof or syntax error occurs; 66 | * otherwise return a pointer to a new entry. 67 | */ 68 | entry * 69 | load_entry(FILE *file, void (*error_func)(const char *), 70 | struct passwd *pw, char **envp) 71 | { 72 | /* this function reads one crontab entry -- the next -- from a file. 73 | * it skips any leading blank lines, ignores comments, and returns 74 | * NULL if for any reason the entry can't be read and parsed. 75 | * 76 | * the entry is also parsed here. 77 | * 78 | * syntax: 79 | * user crontab: 80 | * minutes hours doms months dows cmd\n 81 | * system crontab (/etc/crontab): 82 | * minutes hours doms months dows USERNAME cmd\n 83 | */ 84 | 85 | ecode_e ecode = e_none; 86 | entry *e; 87 | int ch; 88 | char cmd[MAX_COMMAND]; 89 | char envstr[MAX_ENVSTR]; 90 | char **tenvp; 91 | 92 | Debug(DPARS, ("load_entry()...about to eat comments\n")) 93 | 94 | skip_comments(file); 95 | 96 | ch = get_char(file); 97 | if (ch == EOF) 98 | return (NULL); 99 | 100 | /* ch is now the first useful character of a useful line. 101 | * it may be an @special or it may be the first character 102 | * of a list of minutes. 103 | */ 104 | 105 | e = (entry *) calloc(sizeof(entry), sizeof(char)); 106 | 107 | if (ch == '@') { 108 | /* all of these should be flagged and load-limited; i.e., 109 | * instead of @hourly meaning "0 * * * *" it should mean 110 | * "close to the front of every hour but not 'til the 111 | * system load is low". Problems are: how do you know 112 | * what "low" means? (save me from /etc/cron.conf!) and: 113 | * how to guarantee low variance (how low is low?), which 114 | * means how to we run roughly every hour -- seems like 115 | * we need to keep a history or let the first hour set 116 | * the schedule, which means we aren't load-limited 117 | * anymore. too much for my overloaded brain. (vix, jan90) 118 | * HINT 119 | */ 120 | ch = get_string(cmd, MAX_COMMAND, file, " \t\n"); 121 | if (!strcmp("reboot", cmd)) { 122 | e->flags |= WHEN_REBOOT; 123 | } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){ 124 | set_element(e->minute, FIRST_MINUTE, LAST_MINUTE, 125 | FIRST_MINUTE); 126 | set_element(e->hour, FIRST_HOUR, LAST_HOUR, 127 | FIRST_HOUR); 128 | set_element(e->dom, FIRST_DOM, LAST_DOM, 129 | FIRST_DOM); 130 | set_element(e->month, FIRST_MONTH, LAST_MONTH, 131 | FIRST_MONTH); 132 | set_range(e->dow, FIRST_DOW, LAST_DOW, 133 | FIRST_DOW, LAST_DOW, 1); 134 | e->flags |= DOW_STAR; 135 | } else if (!strcmp("monthly", cmd)) { 136 | set_element(e->minute, FIRST_MINUTE, LAST_MINUTE, 137 | FIRST_MINUTE); 138 | set_element(e->hour, FIRST_HOUR, LAST_HOUR, 139 | FIRST_HOUR); 140 | set_element(e->dom, FIRST_DOM, LAST_DOM, 141 | FIRST_DOM); 142 | set_range(e->month, FIRST_MONTH, LAST_MONTH, 143 | FIRST_MONTH, LAST_MONTH, 1); 144 | set_range(e->dow, FIRST_DOW, LAST_DOW, 145 | FIRST_DOW, LAST_DOW, 1); 146 | e->flags |= DOW_STAR; 147 | } else if (!strcmp("weekly", cmd)) { 148 | set_element(e->minute, FIRST_MINUTE, LAST_MINUTE, 149 | FIRST_MINUTE); 150 | set_element(e->hour, FIRST_HOUR, LAST_HOUR, 151 | FIRST_HOUR); 152 | set_range(e->dom, FIRST_DOM, LAST_DOM, 153 | FIRST_DOM, LAST_DOM, 1); 154 | set_range(e->month, FIRST_MONTH, LAST_MONTH, 155 | FIRST_MONTH, LAST_MONTH, 1); 156 | set_element(e->dow, FIRST_DOW, LAST_DOW, 157 | FIRST_DOW); 158 | e->flags |= DOW_STAR; 159 | } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) { 160 | set_element(e->minute, FIRST_MINUTE, LAST_MINUTE, 161 | FIRST_MINUTE); 162 | set_element(e->hour, FIRST_HOUR, LAST_HOUR, 163 | FIRST_HOUR); 164 | set_range(e->dom, FIRST_DOM, LAST_DOM, 165 | FIRST_DOM, LAST_DOM, 1); 166 | set_range(e->month, FIRST_MONTH, LAST_MONTH, 167 | FIRST_MONTH, LAST_MONTH, 1); 168 | set_range(e->dow, FIRST_DOW, LAST_DOW, 169 | FIRST_DOW, LAST_DOW, 1); 170 | } else if (!strcmp("hourly", cmd)) { 171 | set_element(e->minute, FIRST_MINUTE, LAST_MINUTE, 172 | FIRST_MINUTE); 173 | set_range(e->hour, FIRST_HOUR, LAST_HOUR, 174 | FIRST_HOUR, LAST_HOUR, 1); 175 | set_range(e->dom, FIRST_DOM, LAST_DOM, 176 | FIRST_DOM, LAST_DOM, 1); 177 | set_range(e->month, FIRST_MONTH, LAST_MONTH, 178 | FIRST_MONTH, LAST_MONTH, 1); 179 | set_range(e->dow, FIRST_DOW, LAST_DOW, 180 | FIRST_DOW, LAST_DOW, 1); 181 | e->flags |= HR_STAR; 182 | } else { 183 | ecode = e_timespec; 184 | goto eof; 185 | } 186 | /* Advance past whitespace between shortcut and 187 | * username/command. 188 | */ 189 | Skip_Blanks(ch, file); 190 | if (ch == EOF || ch == '\n') { 191 | ecode = e_cmd; 192 | goto eof; 193 | } 194 | } else { 195 | Debug(DPARS, ("load_entry()...about to parse numerics\n")) 196 | 197 | if (ch == '*') 198 | e->flags |= MIN_STAR; 199 | ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, 200 | PPC_NULL, ch, file); 201 | if (ch == EOF) { 202 | ecode = e_minute; 203 | goto eof; 204 | } 205 | 206 | /* hours 207 | */ 208 | 209 | if (ch == '*') 210 | e->flags |= HR_STAR; 211 | ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR, 212 | PPC_NULL, ch, file); 213 | if (ch == EOF) { 214 | ecode = e_hour; 215 | goto eof; 216 | } 217 | 218 | /* DOM (days of month) 219 | */ 220 | 221 | if (ch == '$') { 222 | ch = get_char(file); 223 | if (!Is_Blank(ch)) { 224 | ecode = e_dom; 225 | goto eof; 226 | } 227 | Skip_Blanks(ch, file); 228 | e->flags |= DOM_LAST; 229 | } else { 230 | if (ch == '*') 231 | e->flags |= DOM_STAR; 232 | ch = get_list(e->dom, FIRST_DOM, LAST_DOM, 233 | PPC_NULL, ch, file); 234 | } 235 | if (ch == EOF) { 236 | ecode = e_dom; 237 | goto eof; 238 | } 239 | 240 | /* month 241 | */ 242 | 243 | ch = get_list(e->month, FIRST_MONTH, LAST_MONTH, 244 | MonthNames, ch, file); 245 | if (ch == EOF) { 246 | ecode = e_month; 247 | goto eof; 248 | } 249 | 250 | /* DOW (days of week) 251 | */ 252 | 253 | if (ch == '*') 254 | e->flags |= DOW_STAR; 255 | ch = get_list(e->dow, FIRST_DOW, LAST_DOW, 256 | DowNames, ch, file); 257 | if (ch == EOF) { 258 | ecode = e_dow; 259 | goto eof; 260 | } 261 | } 262 | 263 | /* make sundays equivalent */ 264 | if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) { 265 | bit_set(e->dow, 0); 266 | bit_set(e->dow, 7); 267 | } 268 | 269 | /* check for permature EOL and catch a common typo */ 270 | if (ch == '\n' || ch == '*') { 271 | ecode = e_cmd; 272 | goto eof; 273 | } 274 | 275 | /* ch is the first character of a command, or a username */ 276 | unget_char(ch, file); 277 | 278 | if (!pw) { 279 | char *username = cmd; /* temp buffer */ 280 | 281 | Debug(DPARS, ("load_entry()...about to parse username\n")) 282 | ch = get_string(username, MAX_COMMAND, file, " \t\n"); 283 | 284 | Debug(DPARS, ("load_entry()...got %s\n",username)) 285 | if (ch == EOF || ch == '\n' || ch == '*') { 286 | ecode = e_cmd; 287 | goto eof; 288 | } 289 | 290 | /* Need to have consumed blanks before checking for options 291 | * below. */ 292 | Skip_Blanks(ch, file) 293 | unget_char(ch, file); 294 | 295 | pw = getpwnam(username); 296 | if (pw == NULL) { 297 | ecode = e_username; 298 | goto eof; 299 | } 300 | Debug(DPARS, ("load_entry()...uid %ld, gid %ld\n", 301 | (long)pw->pw_uid, (long)pw->pw_gid)) 302 | } 303 | 304 | if ((e->pwd = pw_dup(pw)) == NULL) { 305 | ecode = e_memory; 306 | goto eof; 307 | } 308 | bzero(e->pwd->pw_passwd, strlen(e->pwd->pw_passwd)); 309 | 310 | /* copy and fix up environment. some variables are just defaults and 311 | * others are overrides. 312 | */ 313 | if ((e->envp = env_copy(envp)) == NULL) { 314 | ecode = e_memory; 315 | goto eof; 316 | } 317 | if (!env_get("SHELL", e->envp)) { 318 | if (glue_strings(envstr, sizeof envstr, "SHELL", 319 | _PATH_BSHELL, '=')) { 320 | if ((tenvp = env_set(e->envp, envstr)) == NULL) { 321 | ecode = e_memory; 322 | goto eof; 323 | } 324 | e->envp = tenvp; 325 | } else 326 | log_it("CRON", getpid(), "error", "can't set SHELL"); 327 | } 328 | if (!env_get("HOME", e->envp)) { 329 | if (glue_strings(envstr, sizeof envstr, "HOME", 330 | pw->pw_dir, '=')) { 331 | if ((tenvp = env_set(e->envp, envstr)) == NULL) { 332 | ecode = e_memory; 333 | goto eof; 334 | } 335 | e->envp = tenvp; 336 | } else 337 | log_it("CRON", getpid(), "error", "can't set HOME"); 338 | } 339 | #ifndef LOGIN_CAP 340 | /* If login.conf is in used we will get the default PATH later. */ 341 | if (!env_get("PATH", e->envp)) { 342 | if (glue_strings(envstr, sizeof envstr, "PATH", 343 | _PATH_DEFPATH, '=')) { 344 | if ((tenvp = env_set(e->envp, envstr)) == NULL) { 345 | ecode = e_memory; 346 | goto eof; 347 | } 348 | e->envp = tenvp; 349 | } else 350 | log_it("CRON", getpid(), "error", "can't set PATH"); 351 | } 352 | #endif /* LOGIN_CAP */ 353 | if (glue_strings(envstr, sizeof envstr, "LOGNAME", 354 | pw->pw_name, '=')) { 355 | if ((tenvp = env_set(e->envp, envstr)) == NULL) { 356 | ecode = e_memory; 357 | goto eof; 358 | } 359 | e->envp = tenvp; 360 | } else 361 | log_it("CRON", getpid(), "error", "can't set LOGNAME"); 362 | #if defined(BSD) || defined(__linux) 363 | if (glue_strings(envstr, sizeof envstr, "USER", 364 | pw->pw_name, '=')) { 365 | if ((tenvp = env_set(e->envp, envstr)) == NULL) { 366 | ecode = e_memory; 367 | goto eof; 368 | } 369 | e->envp = tenvp; 370 | } else 371 | log_it("CRON", getpid(), "error", "can't set USER"); 372 | #endif 373 | 374 | Debug(DPARS, ("load_entry()...about to parse command\n")) 375 | 376 | /* If the first character of the command is '-' it is a cron option. 377 | */ 378 | while ((ch = get_char(file)) == '-') { 379 | switch (ch = get_char(file)) { 380 | case 'q': 381 | e->flags |= DONT_LOG; 382 | Skip_Nonblanks(ch, file) 383 | break; 384 | default: 385 | ecode = e_option; 386 | goto eof; 387 | } 388 | Skip_Blanks(ch, file) 389 | if (ch == EOF || ch == '\n') { 390 | ecode = e_cmd; 391 | goto eof; 392 | } 393 | } 394 | unget_char(ch, file); 395 | 396 | /* Everything up to the next \n or EOF is part of the command... 397 | * too bad we don't know in advance how long it will be, since we 398 | * need to malloc a string for it... so, we limit it to MAX_COMMAND. 399 | */ 400 | ch = get_string(cmd, MAX_COMMAND, file, "\n"); 401 | 402 | /* a file without a \n before the EOF is rude, so we'll complain... 403 | */ 404 | if (ch == EOF) { 405 | ecode = e_cmd; 406 | goto eof; 407 | } 408 | 409 | /* got the command in the 'cmd' string; save it in *e. 410 | */ 411 | if ((e->cmd = strdup(cmd)) == NULL) { 412 | ecode = e_memory; 413 | goto eof; 414 | } 415 | 416 | Debug(DPARS, ("load_entry()...returning successfully\n")) 417 | 418 | /* success, fini, return pointer to the entry we just created... 419 | */ 420 | return (e); 421 | 422 | eof: 423 | if (e->envp) 424 | env_free(e->envp); 425 | if (e->pwd) 426 | free(e->pwd); 427 | if (e->cmd) 428 | free(e->cmd); 429 | free(e); 430 | while (ch != '\n' && !feof(file)) 431 | ch = get_char(file); 432 | if (ecode != e_none && error_func != NULL) 433 | (*error_func)(ecodes[(int)ecode]); 434 | return (NULL); 435 | } 436 | 437 | static int 438 | get_list(bitstr_t *bits, int low, int high, const char *names[], 439 | int ch, FILE *file) 440 | { 441 | int done; 442 | 443 | /* we know that we point to a non-blank character here; 444 | * must do a Skip_Blanks before we exit, so that the 445 | * next call (or the code that picks up the cmd) can 446 | * assume the same thing. 447 | */ 448 | 449 | Debug(DPARS|DEXT, ("get_list()...entered\n")) 450 | 451 | /* list = range {"," range} 452 | */ 453 | 454 | /* clear the bit string, since the default is 'off'. 455 | */ 456 | bit_nclear(bits, 0, (high-low)); 457 | 458 | /* process all ranges 459 | */ 460 | done = FALSE; 461 | while (!done) { 462 | if (EOF == (ch = get_range(bits, low, high, names, ch, file))) 463 | return (EOF); 464 | if (ch == ',') 465 | ch = get_char(file); 466 | else 467 | done = TRUE; 468 | } 469 | 470 | /* exiting. skip to some blanks, then skip over the blanks. 471 | */ 472 | Skip_Nonblanks(ch, file) 473 | Skip_Blanks(ch, file) 474 | 475 | Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch)) 476 | 477 | return (ch); 478 | } 479 | 480 | 481 | static int 482 | get_range(bitstr_t *bits, int low, int high, const char *names[], 483 | int ch, FILE *file) 484 | { 485 | /* range = number | number "-" number [ "/" number ] 486 | */ 487 | 488 | int num1, num2, num3; 489 | 490 | Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n")) 491 | 492 | if (ch == '*') { 493 | /* '*' means "first-last" but can still be modified by /step 494 | */ 495 | num1 = low; 496 | num2 = high; 497 | ch = get_char(file); 498 | if (ch == EOF) 499 | return (EOF); 500 | } else { 501 | ch = get_number(&num1, low, names, ch, file, ",- \t\n"); 502 | if (ch == EOF) 503 | return (EOF); 504 | 505 | if (ch != '-') { 506 | /* not a range, it's a single number. 507 | */ 508 | if (EOF == set_element(bits, low, high, num1)) { 509 | unget_char(ch, file); 510 | return (EOF); 511 | } 512 | return (ch); 513 | } else { 514 | /* eat the dash 515 | */ 516 | ch = get_char(file); 517 | if (ch == EOF) 518 | return (EOF); 519 | 520 | /* get the number following the dash 521 | */ 522 | ch = get_number(&num2, low, names, ch, file, "/, \t\n"); 523 | if (ch == EOF || num1 > num2) 524 | return (EOF); 525 | } 526 | } 527 | 528 | /* check for step size 529 | */ 530 | if (ch == '/') { 531 | /* eat the slash 532 | */ 533 | ch = get_char(file); 534 | if (ch == EOF) 535 | return (EOF); 536 | 537 | /* get the step size -- note: we don't pass the 538 | * names here, because the number is not an 539 | * element id, it's a step size. 'low' is 540 | * sent as a 0 since there is no offset either. 541 | */ 542 | ch = get_number(&num3, 0, PPC_NULL, ch, file, ", \t\n"); 543 | if (ch == EOF || num3 == 0) 544 | return (EOF); 545 | } else { 546 | /* no step. default==1. 547 | */ 548 | num3 = 1; 549 | } 550 | 551 | /* range. set all elements from num1 to num2, stepping 552 | * by num3. (the step is a downward-compatible extension 553 | * proposed conceptually by bob@acornrc, syntactically 554 | * designed then implemented by paul vixie). 555 | */ 556 | if (EOF == set_range(bits, low, high, num1, num2, num3)) { 557 | unget_char(ch, file); 558 | return (EOF); 559 | } 560 | 561 | return (ch); 562 | } 563 | 564 | static int 565 | get_number(int *numptr, int low, const char *names[], int ch, FILE *file, 566 | const char *terms) { 567 | char temp[MAX_TEMPSTR], *pc; 568 | int len, i; 569 | 570 | pc = temp; 571 | len = 0; 572 | 573 | /* first look for a number */ 574 | while (isdigit((unsigned char)ch)) { 575 | if (++len >= MAX_TEMPSTR) 576 | goto bad; 577 | *pc++ = ch; 578 | ch = get_char(file); 579 | } 580 | *pc = '\0'; 581 | if (len != 0) { 582 | /* got a number, check for valid terminator */ 583 | if (!strchr(terms, ch)) 584 | goto bad; 585 | i = atoi(temp); 586 | if (i < 0) 587 | goto bad; 588 | *numptr = i; 589 | return (ch); 590 | } 591 | 592 | /* no numbers, look for a string if we have any */ 593 | if (names) { 594 | while (isalpha((unsigned char)ch)) { 595 | if (++len >= MAX_TEMPSTR) 596 | goto bad; 597 | *pc++ = ch; 598 | ch = get_char(file); 599 | } 600 | *pc = '\0'; 601 | if (len != 0 && strchr(terms, ch)) { 602 | for (i = 0; names[i] != NULL; i++) { 603 | Debug(DPARS|DEXT, 604 | ("get_num, compare(%s,%s)\n", names[i], 605 | temp)) 606 | if (!strcasecmp(names[i], temp)) { 607 | *numptr = i+low; 608 | return (ch); 609 | } 610 | } 611 | } 612 | } 613 | 614 | bad: 615 | unget_char(ch, file); 616 | return (EOF); 617 | } 618 | 619 | static int 620 | set_element(bitstr_t *bits, int low, int high, int number) { 621 | Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number)) 622 | 623 | if (number < low || number > high) 624 | return (EOF); 625 | number -= low; 626 | 627 | bit_set(bits, number); 628 | return (OK); 629 | } 630 | 631 | static int 632 | set_range(bitstr_t *bits, int low, int high, int start, int stop, int step) { 633 | Debug(DPARS|DEXT, ("set_range(?,%d,%d,%d,%d,%d)\n", 634 | low, high, start, stop, step)) 635 | 636 | if (start < low || stop > high) 637 | return (EOF); 638 | start -= low; 639 | stop -= low; 640 | 641 | if (step <= 1 || step > stop) { 642 | bit_nset(bits, start, stop); 643 | } else { 644 | for (int i = start; i <= stop; i += step) 645 | bit_set(bits, i); 646 | } 647 | return (OK); 648 | } 649 | --------------------------------------------------------------------------------