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 |
1987 / May / 6th ⟼
16 |
17 |
18 |
19 |
1988 / February / 8th ⟼
20 |
21 |
22 |
23 |
1993 / December / 29th ⟼
24 |
25 |
26 |
27 |
1997 / September / 7th ⟼
28 |
29 |
30 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------