Files
cron/SRC/cron_4.1.shar
2018-06-04 16:59:38 -05:00

6715 lines
201 KiB
Plaintext
Executable File

# This is a shell archive. Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file". Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
# README
# CHANGES
# FEATURES
# INSTALL
# CONVERSION
# THANKS
# MAIL
# bitstring.3
# crontab.5
# crontab.1
# cron.8
# putman.sh
# Makefile
# bitstring.h
# cron.h
# config.h
# pathnames.h
# externs.h
# macros.h
# structs.h
# funcs.h
# globals.h
# cron.c
# crontab.c
# database.c
# do_command.c
# entry.c
# env.c
# job.c
# user.c
# popen.c
# misc.c
# pw_dup.c
#
echo x - README
sed 's/^X//' >README << 'END-of-README'
X#/* Copyright 1988,1990,1993 by Paul Vixie
X# * All rights reserved
X# */
X
X##
X## Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X## Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X##
X## Permission to use, copy, modify, and distribute this software for any
X## purpose with or without fee is hereby granted, provided that the above
X## copyright notice and this permission notice appear in all copies.
X##
X## THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X## WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X## MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X## ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X## WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X## ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X## OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X##
X
XISC Cron - January 2004
X[V4.0 was November, 2000]
X[V4.0b1 was September 7, 1997]
X[V3.1 was some time after 1993]
X[V3.0 was December 27, 1993]
X[V2.2 was some time in 1992]
X[V2.1 was May 29, 1991]
X[V2.0 was July 5, 1990]
X[V2.0-beta was December 9, 1988]
X[V1.0 was May 6, 1987]
Xftp://ftp.isc.org/isc/cron/
X
XThis is a version of 'cron' that is known to run on most systems. It
Xis functionally based on the SysV cron, which means that each user can have
Xtheir own crontab file (all crontab files are stored in a read-protected
Xdirectory, usually /var/cron/tabs). No direct support is provided for
X'at'; you can continue to run 'atrun' from the crontab as you have been
Xdoing. If you don't have atrun (i.e., System V) you are in trouble.
X
XA messages is logged each time a command is executed; also, the files
X"allow" and "deny" in /var/cron can be used to control access to the
X"crontab" command (which installs crontabs). It hasn't been tested on
XSysV, although some effort has gone into making the port an easy one.
X
XTo use this: Sorry, folks, there is no cutesy 'Configure' script. You'll
Xhave to go edit a couple of files... So, here's the checklist:
X
X Read all the FEATURES, INSTALL, and CONVERSION files
X Edit config.h
X Edit Makefile
X (both of these files have instructions inside; note that
X some things in config.h are definable in Makefile and are
X therefore surrounded by #ifndef...#endif)
X 'make'
X 'su' and 'make install'
X (you may have to install the man pages by hand)
X kill your existing cron process
X (actually you can run your existing cron if you want, but why?)
X build new crontabs using /usr/lib/{crontab,crontab.local}
X (either put them all in "root"'s crontab, or divide it up
X and rip out all the 'su' commands, collapse the lengthy
X lists into ranges with steps -- basically, this step is
X as much work as you want to make it)
X start up the new cron
X (must be done as root)
X watch it. test it with 'crontab -r' and watch the daemon track your
X changes.
X if you like it, change your /etc/{rc,rc.local} to use it instead of
X the old one.
X
X$Id: README,v 1.6 2004/01/23 19:03:32 vixie Exp $
END-of-README
echo x - CHANGES
sed 's/^X//' >CHANGES << 'END-of-CHANGES'
XISC Cron V4
X
X9. [misc] megapatch from tcmiller (posix compliance, etc).
X8. [bug] fix stepsize 0 infinite loop.
X7. [evol] "enum" version of freebsd fix to env-var settings.
X6. [evol] changes #12-17 from tcmiller (for *BSD alignment).
X5. [feature] give cron a version number and display it.
X4. [bug] handle clock jumps (from freebsd pr# 24485)
X3. [feature] add -n to cron (for nofork).
X2. [evol] merge in many changes from *BSD and Linux.
X1. [port] fixups for nextstep, plus a syslog configuration bug.
X
X--------
X
XVixie Cron V3.0 Patch 2 notes
XPaul Vixie
X12-Dec-1994
X
X3. "Coherent" now supported. All kinds of flock() damage was repaired.
X2. gethostname() now available in compat.c for systems that need it.
X1. There was a problem in the way the environment was being imported.
X
XVixie Cron Changes from V2 to V3
XPaul Vixie
X29-Dec-1993
X
XThe crontab command now conforms to POSIX 1003.2. This means that when you
Xinstall it, if you have any "crontab" command lines floating around in shell
Xscripts (such as /etc/rc or /etc/rc.local), you will need to change them.
X
XI have integrated several changes made by BSDi for their BSD/386 operating
Xsystem; these were offerred to me before I started consulting for them, so
Xit is safe to say that they were intended for publication. Most notably,
Xthe name of the cron daemon has changed from "crond" to "cron". This was
Xdone for compatibility with 4.3BSD. Another change made for the same reason
Xis the ability to read in an /etc/crontab file which has an extra field in
Xeach entry, between the time fields and the command. This field is a user
Xname, and it permits the /etc/crontab command to contain commands which are
Xto be run by any user on the system. /etc/crontab is not "installed" via
Xthe crontab(1) command; it is automatically read at startup time and it will
Xbe reread whenever it changes.
X
XI also added a "-e" option to crontab(1). Nine people also sent me diffs
Xto add this option, but I had already implemented it on my own. I actually
Xreleased an interrim version (V2.2, I think) for limited testing, and got a
Xchance to fix a bad security bug in the "-e" option thanks to XXX.
X
XThe daemon used to be extraordinarily sloppy in its use of file descriptors.
XA heck of a lot of them were left open in spawned jobs, which caused problems
Xfor the daemon and also caused problems with the spawned jobs if they were
Xshell scripts since "sh" and "csh" have traditionally used hidden file
Xdescriptors to pass information to subshells, and cron was causing them to
Xthink they were subshells. If you had trouble with "sh" or "csh" scripts in
XV2, chances are good that V3 will fix your problems.
X
XAbout a dozen people have reminded me that I forgot to initialize
X"crontab_fd" in database.c. Keith Cantrell was the first, so he gets the
Xpoint.
X
XSteve Simmons reminded me that once an account has been deleted from the
Xsystem, "crontab -u USER -d" will not work. My solution is to suggest to
Xall of you that before you delete a user's account, you first delete that
Xuser's crontab file if any. From cron's point of view, usernames can never
Xbe treated as arbitrary strings. Either they are valid user names, or they
Xare not. I will not make an exception for the "-d" case, for security
Xreasons that I consider reasonable. It is trivial for a root user to delete
Xthe entry by hand if necessary.
X
XDan O'Neil reminded me that I forgot to reset "log_fd" in misc.c. A lot of
Xothers also reminded me of this, but Dan gets the point. I didn't fix it
Xthere, since the real bug was that it should have been open in the parent.
X
XPeter Kabal reminded me that I forgot to "#ifdef DEBUGGING" some code in
Xmisc.c. Hans Trompert actually told me first, but Peter sent the patch so
Xhe gets the point.
X
XRussell Nelson told me that I'd forgotten to "#include <syslog.h>" in misc.c,
Xwhich explains why a lot of other people complained that it wasn't using
Xsyslog even when they configured it that way :-). Steve Simmons told me
Xfirst, though, so he gets the point.
X
XAn interrim version of the daemon tried to "stat" every file before
Xexecuting it; this turned out to be a horribly bad idea since finding the
Xname of a file from a shell command is a hard job (that's why we have
Xshells, right?) I removed this bogus code. Dave Burgess gets the point.
X
XDennis R. Conley sent a suggestion for MMDF systems, which I've added to the
Xcomments in cron.h.
X
XMike Heisler noted that I use comments in the CONVERSION file which are
Xdocumented as illegal in the man pages. Thanks, Mike.
X
XIrving Wolfe sent me some very cheerful changes for a NeXT system, but I
Xconsider the system itself broken and I can't bring myself to #ifdef for
Xsomething as screwed up as this system seems to be. However, various others
Xdid send me smaller patches which appear to have cause cron to build and run
Xcorrectly on (the latest) NeXT machines, with or without the "-posix" CFLAG.
XIrving also asked for a per-job MAILTO, and this was finally added later when
XI integrated the BSD/386 changes contributed by BSDi, and generalized some of
Xthe parsing.
X
XLots of folks complained that the autogenerated "Date:" header wasn't in
XARPA format. I didn't understand this -- either folks will use Sendmail and
Xnot generate a Date: at all (since Sendmail will do it), or folks will use
Xsomething other than Sendmail which won't care about Date: formats. But
XI've "fixed" it anyway...
X
XSeveral people suggested that "*" should be able to take a "/step". One person
Xsuggested that "N/step" ought to mean "N-last/step", but that's stretching things
Xa bit far. "*/step" seems quite intuitive to me, so I've added it. Colin Plumb
Xsent in the first and most polite request for this feature.
X
XAs with every release of Cron, BIND, and seemingly everything else I do, one
Xuser stands out with the most critical but also the most useful analysis.
XCron V3's high score belongs to Peter Holzer, who sent in the nicest looking
Xpatch for the "%" interpretation problem and also helped me understand a
Xtricky bit of badness in the "log_fd" problem.
X
Xagulbra@flode.nvg.unit.no wins the honors for being the first to point out the
Xnasty security hole in "crontab -r". 'Nuff said.
X
XSeveral folks pointed out that log_it() needed to exist even if logging was
Xdisabled. Some day I will create a tool that will compile a subsystem with
Xevery possible combination and permutation of #ifdef options, but meanwhile
Xthanks to everybody.
X
Xjob_runqueue() was using storage after freeing it, since Jordan told me back
Xin 1983 that C let you do that, and I believed him in 1986 when I wrote all
Xthis junk. Linux was the first to die from this error, and the Linux people
Xsent me the most amazing, um, collection of patches for this problem. Thanks
Xfor all the fish.
X
XJeremy Bettis reminded me that popen() isn't safe. I grabbed Ken Arnold's
Xversion of popen/pclose from the ftpd and hacked it to taste. We're safe now,
Xfrom this at least.
X
XBranko Lankester sent me a very timely and helpful fix for a looming security
Xproblem in my "crontab -e" implementation.
X
X--------
X
XVixie Cron Changes from V1 to V2
XPaul Vixie
X8-Feb-1988
X
XMany changes were made in a rash of activity about six months ago, the exact
Xlist of which is no longer clear in my memory. I know that V1 used a file
Xcalled POKECRON in /usr/spool/cron to tell it that it was time to re-read
Xall the crontab files; V2 uses the modtime the crontab directory as a flag to
Xcheck out the crontab files; those whose modtime has changed will be re-read,
Xand the others left alone. Note that the crontab(1) command will do a utimes
Xcall to make sure the mtime of the dir changes, since the filename/inode will
Xoften remain the same after a replacement and the mtime wouldn't change in
Xthat case.
X
X8-Feb-88: made it possible to use much larger environment variable strings.
X V1 allowed 100 characters; V2 allows 1000. This was needed for PATH
X variables on some systems. Thanks to Toerless Eckert for this idea.
X E-mail: UUCP: ...pyramid!fauern!faui10!eckert
X
X16-Feb-88: added allow/deny, moved /usr/spool/cron/crontabs to
X /usr/lib/cron/tabs. allow and deny are /usr/lib/cron/{allow,deny},
X since the sysv naming for this depends on 'at' using the same
X dir, which would be stupid (hint: use /usr/{lib,spool}/at).
X
X22-Feb-88: made it read the spool directory for crontabs and look each one
X up using getpwnam() rather than reading all passwds with getpwent()
X and trying to open each crontab.
X
X9-Dec-88: made it sync to :00 after the minute, makes cron predictable.
X added logging to /var/cron/log.
X
X14-Apr-90: (actually, changes since December 1989)
X fixed a number of bugs reported from the net and from John Gilmore.
X added syslog per Keith Bostic. security features including not
X being willing to run a command owned or writable by other than
X the owner of the crontab 9not working well yet)
END-of-CHANGES
echo x - FEATURES
sed 's/^X//' >FEATURES << 'END-of-FEATURES'
X$Id: FEATURES,v 1.2 2004/01/23 18:56:42 vixie Exp $
X
XFeatures of ISC cron relative to BSD 4.[23] and SysV crons:
X
X-- Environment variables can be set in each crontab. SHELL, USER,
X LOGNAME, and HOME are set from the user's passwd entry; all except
X USER can be changed in the crontab. PATH is especially useful to
X set there. TZ can be set, but cron ignores it other than passing
X it on through to the commands it runs. Format is
X
X variable=value
X
X Blanks surrounding the '=' will be eaten; other blanks in value are
X okay. Leading or trailing blanks can be preserved by quoting, single
X or double quotes are okay, just so they match.
X
X PATH=.:/bin:/usr/bin
X SHELL=/bin/sh
X FOOBAR = this is a long blanky example
X
X Above, FOOBAR would get "this is a long blanky example" as its value.
X
X SHELL and HOME will be used when it's time to run a command; if
X you don't set them, HOME defaults to your /etc/passwd entry
X and SHELL defaults to /bin/sh.
X
X MAILTO, if set to the login name of a user on your system, will be the
X person that cron mails the output of commands in that crontab. This is
X useful if you decide on BINMAIL when configuring cron.h, since binmail
X doesn't know anything about aliasing.
X
X-- Weekdays can be specified by name. Case is not significant, but only
X the first three letters should be specified.
X
X-- Months can likewise be specified by name. Three letters only.
X
X-- Ranges and lists can be mixed. Standard crons won't allow '1,3-5'.
X
X-- Ranges can specify 'step' values. '10-16/2' is like '10,12,14,16'.
X
X-- Sunday is both day 0 and day 7 -- apparently BSD and ATT disagree
X about this.
X
X-- Each user gets their own crontab file. This is a win over BSD 4.2,
X where only root has one, and over BSD 4.3, where they made the crontab
X format incompatible and although the commands can be run by non-root
X uid's, root is still the only one who can edit the crontab file. This
X feature mimics the SysV cron.
X
X-- The 'crontab' command is loosely compatible with SysV, but has more
X options which just generally make more sense. Running crontab with
X no arguments will print a cute little summary of the command syntax.
X
X-- Comments and blank lines are allowed in the crontab file. Comments
X must be on a line by themselves; leading whitespace is ignored, and
X a '#' introduces the comment.
X
X-- (big win) If the `crontab' command changes anything in any crontab,
X the 'cron' daemon will reload all the tables before running the
X next iteration. In some crons, you have to kill and restart the
X daemon whenever you change a crontab. In other crons, the crontab
X file is reread and reparsed every minute even if it didn't change.
X
X-- In order to support the automatic reload, the crontab files are not
X readable or writable except by 'crontab' or 'cron'. This is not a
X problem, since 'crontab' will let you do pretty much whatever you
X want to your own crontab, or if you are root, to anybody's crontab.
X
X-- If any output is generated by a command (on stdout OR stderr), it will
X be mailed to the owner of the crontab that contained the command (or
X MAILTO, see discussion of environment variables, above). The headers
X of the mail message will include the command that was run, and a
X complete list of the environment that was passed to it, which will
X contain (at least) the USER (LOGNAME on SysV), HOME, and SHELL.
X
X-- the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
X first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
X on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
X is why we keep 'e->dow_star' and 'e->dom_star'. I didn't think up
X this behaviour; it's how cron has always worked but the documentation
X hasn't been very clear. I have been told that some AT&T crons do not
X act this way and do the more reasonable thing, which is (IMHO) to "or"
X the various field-matches together. In that sense this cron may not
X be completely similar to some AT&T crons.
END-of-FEATURES
echo x - INSTALL
sed 's/^X//' >INSTALL << 'END-of-INSTALL'
X/* Copyright 1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X$Id: INSTALL,v 1.4 2004/01/23 18:56:42 vixie Exp $
X
XRead the comments at the top of the Makefile, then edit the area marked
X'configurable stuff'.
X
XEdit config.h. The stuff I expect you to change is down a bit from the
Xtop of the file, but it's clearly marked. Also look at pathnames.h.
X
XYou don't have to create the /var/cron or /var/cron/tabs directories, since
Xboth the daemon and the `crontab' program will do this the first time they
Xrun if they don't exist. You do need to have a /var, though -- just "mkdir
X/var" if you don't have one, or you can "mkdir /usr/var; ln -s /usr/var /var"
Xif you expect your /var to have a lot of stuff in it.
X
XYou will also need /usr/local/etc and /usr/local/bin directories unless you
Xchange the Makefile. These will have to be created by hand, but if you are
Xa long-time Usenet user you probably have them already. /usr/local/man is
Xwhere I keep my man pages, but I have the source for `man' and you probably
Xdo not. Therefore you may have to put the man pages into /usr/man/manl,
Xwhich will be hard since there will be name collisions. (Note that the man
Xcommand was originally written by Bill Joy before he left Berkeley, and it
Xcontains no AT&T code, so it is in UUNET's archive of freely-distributable
XBSD code.)
X
XLINUX note: /usr/include/paths.h on some linux systems shows _PATH_SENDMAIL
X to be /usr/bin/sendmail even though sendmail is installed in /usr/lib.
X you should check this out.
X
Xsay:
X make all
X
Xsu and say:
X make install
X
XNote that if I can get you to "su and say" something just by asking, you have
Xa very serious security problem on your system and you should look into it.
X
XEdit your /usr/lib/crontab file into little pieces -- see the CONVERSION file
Xfor help on this.
X
XUse the `crontab' command to install all the little pieces you just created.
XSome examples (see below before trying any of these!)
X
X crontab -u uucp -r /usr/lib/uucp/crontab.src
X crontab -u news -r /usr/lib/news/crontab.src
X crontab -u root -r /usr/adm/crontab.src
X
XNotes on above examples: (1) the .src files are copied at the time the
Xcommand is issued; changing the source files later will have no effect until
Xthey are reinstalled with another `crontab -r' command. (2) The crontab
Xcommand will affect the crontab of the person using the command unless `-u
XUSER' is given; `-u' only works for root. When using most `su' commands
Xunder most BSD's, `crontab' will still think of you as yourself even though
Xyou may think of yourself as root -- so use `-u' liberally. (3) the `-r'
Xoption stands for `replace'; check the man page for crontab(1) for other
Xpossibilities.
X
XKill your existing cron daemon -- do `ps aux' and look for /etc/cron.
X
XEdit your /etc/rc or /etc/rc.local, looking for the line that starts up
X/etc/cron. Comment it out and add a line to start the new cron daemon
X-- usually /usr/local/etc/cron, unless you changed it in the Makefile.
X
XStart up this cron daemon yourself as root. Just type /usr/local/etc/cron
X(or whatever); no '&' is needed since the daemon forks itself and the
Xprocess you executed returns immediately.
X
XATT notes: for those people unfortunate enough to be stuck on a AT&T UNIX,
Xyou will need the public-domain "libndir", found in the B News source and in
Xany comp.sources.unix archive. You will also need to hack the code some.
END-of-INSTALL
echo x - CONVERSION
sed 's/^X//' >CONVERSION << 'END-of-CONVERSION'
X$Id: CONVERSION,v 1.1.1.1 1996/12/16 19:39:48 halley Exp $
X
XConversion of BSD 4.[23] crontab files:
X
XEdit your current crontab (/usr/lib/crontab) into little pieces, with each
Xusers' commands in a different file. This is different on 4.2 and 4.3,
Xbut I'll get to that below. The biggest feature of this cron is that you
Xcan move 'news' and 'uucp' cron commands into files owned and maintainable
Xby those two users. You also get to rip all the fancy 'su' footwork out
Xof the cron commands. On 4.3, there's no need for the 'su' stuff since the
Xuser name appears on each command -- but I'd still rather have separate
Xcrontabs with seperate environments and so on.
X
XLeave the original /usr/lib/crontab! This cron doesn't use it, so you may
Xas well keep it around for a while in case something goes wakko with this
Xfancy version.
X
XMost commands in most crontabs are run by root, have to run by root, and
Xshould continue to be run by root. They still have to be in their own file;
XI recommend /etc/crontab.src or /usr/adm/crontab.src.
X
X'uucp's commands need their own file; how about /usr/lib/uucp/crontab.src?
X'news' also, perhaps in /usr/lib/news/crontab.src...
X
XI say `how about' and `perhaps' because it really doesn't matter to anyone
X(except you) where you put the crontab source files. The `crontab' command
XCOPIES them into a protected directory (CRONDIR/SPOOL_DIR in cron.h), named
Xafter the user whose crontab it is. If you want to examine, replace, or
Xdelete a crontab, the `crontab' command does all of those things. The
Xvarious `crontab.src' (my suggested name for them) files are just source
Xfiles---they have to be copied to SPOOLDIR using `crontab' before they'll be
Xexecuted.
X
XOn 4.2, your crontab might have a few lines like this:
X
X 5 * * * * su uucp < /usr/lib/uucp/uudemon.hr
X 10 4 * * * su uucp < /usr/lib/uucp/uudemon.day
X 15 5 * * 0 su uucp < /usr/lib/uucp/uudemon.wk
X
X...or like this:
X
X 5 * * * * echo /usr/lib/uucp/uudemon.hr | su uucp
X 10 4 * * * echo /usr/lib/uucp/uudemon.day | su uucp
X 15 5 * * 0 echo /usr/lib/uucp/uudemon.wk | su uucp
X
XOn 4.3, they'd look a little bit better, but not much:
X
X 5 * * * * uucp /usr/lib/uucp/uudemon.hr
X 10 4 * * * uucp /usr/lib/uucp/uudemon.day
X 15 5 * * 0 uucp /usr/lib/uucp/uudemon.wk
X
XFor this cron, you'd create /usr/lib/uucp/crontab.src (or wherever you want
Xto keep uucp's commands) which would look like this:
X
X # /usr/lib/uucp/crontab.src - uucp's crontab
X #
X PATH=/usr/lib/uucp:/bin:/usr/bin
X SHELL=/bin/sh
X HOME=/usr/lib/uucp
X #
X 5 * * * * uudemon.hr
X 10 4 * * * uudemon.day
X 15 5 * * 0 uudemon.wk
X
XThe application to the `news' cron commands (if any) is left for you to
Xfigure out. Likewise if there are any other cruddy-looking 'su' commands in
Xyour crontab commands, you don't need them anymore: just find a good place
Xto put the `crontab.src' (or whatever you want to call it) file for that
Xuser, put the cron commands into it, and install it using the `crontab'
Xcommand (probably with "-u USERNAME", but see the man page).
X
XIf you run a 4.2-derived cron, you could of course just install your current
Xcrontab in toto as root's crontab. It would work exactly the way your
Xcurrent one does, barring the extra steps in installing or changing it.
XThere would still be advantages to this cron, mostly that you get mail if
Xthere is any output from your cron commands.
X
XOne note about getting mail from cron: you will probably find, after you
Xinstall this version of cron, that your cron commands are generating a lot
Xof irritating output. The work-around for this is to redirect all EXPECTED
Xoutput to a per-execution log file, which you can examine if you want to
Xsee the output from the "last time" a command was executed; if you get any
XUNEXPECTED output, it will be mailed to you. This takes a while to get
Xright, but it's amazingly convenient. Trust me.
X
END-of-CONVERSION
echo x - THANKS
sed 's/^X//' >THANKS << 'END-of-THANKS'
X15 January 1990
XPaul Vixie
X
XMany people have contributed to cron. Many more than I can remember, in fact.
XRich Salz and Carl Gutekunst were each of enormous help to me in V1; Carl for
Xhelping me understand UNIX well enough to write it, and Rich for helping me
Xget the features right.
X
XJohn Gilmore wrote me a wonderful review of V2, which took me a whole year to
Xanswer even though it made me clean up some really awful things in the code.
X(According to John the most awful things are still in here, of course.)
X
XPaul Close made a suggestion which led to /etc/crond.pid and the mutex locking
Xon it. Kevin Braunsdorf of Purdue made a suggestion that led to @reboot and
Xits brothers and sisters; he also sent some diffs that lead cron toward compil-
Xability with System V, though without at(1) capabilities, this cron isn't going
Xto be that useful on System V. Bob Alverson fixed a silly bug in the line
Xnumber counting. Brian Reid made suggestions which led to the run queue and
Xthe source-file labelling in installed crontabs.
X
XScott Narveson ported V2 to a Sequent, and sent in the most useful single batch
Xof diffs I got from anybody. Changes attributable to Scott are:
X -> sendmail won't time out if the command is slow to generate output
X -> day-of-week names aren't off by one anymore
X -> crontab says the right thing if you do something you shouldn't do
X -> crontab(5) man page is longer and more informative
X -> misc changes related to the side effects of fclose()
X -> Sequent "universe" support added (may also help on Pyramids)
X -> null pw_shell is dealt with now; default is /bin/sh
END-of-THANKS
echo x - MAIL
sed 's/^X//' >MAIL << 'END-of-MAIL'
X[ this is really old mail that came to me in response to my 1986 posting
X to usenet asking for feature suggestions before releasing the first
X version of cron. it is presented here for its entertainment value.
X --vix ]
X
X$Id: MAIL,v 1.1.1.1 1996/12/16 19:39:47 halley Exp $
X
XFrom ptsfa!lll-crg!ames!acornrc!bob Wed Dec 31 10:07:08 1986
XDate: Wed, 31 Dec 86 08:59:31 pst
XFrom: lll-crg!ames!acornrc!bob (Bob Weissman)
XTo: ptsfa!vixie!paul
XStatus: RO
X
XSure, here's a suggestion: I'd like to be able to run a program, say,
Xevery two hours. Current cron requires me to write
X0,2,4,6,8,10,12,14,16,18,20,22 in the hours field. How about a notation
Xto handle this more elegantly?
X
X<< Okay, I've allowed 0-22/2 as a means of handling this.
X The time specification for my cron is as follows:
X specification = range {"," range}
X range = (start "-" finish ["/" step]) | single-unit
X This allows "1,3,5-7", which the current cron doesn't (it won't
X do a range inside a list), and handles your specific need. >>
X
XFrom drw@mit-eddie Wed Dec 31 18:25:27 1986
XDate: Wed, 31 Dec 86 14:28:19 est
XFrom: drw@mit-eddie (Dale Worley)
XTo: mit-eddie!vixie!paul
XStatus: RO
X
XWe have a lot of lines in our crontab of the form
X
X 00 12 * * * su user < /usr/users/user/script.file
X
XThis barfs (silently!) on our system (Dec Ultrix 1.2 == 4.2bsd) if
Xuser's shell is csh. This, I am told, is because csh requires that
Xthe environment be set up in certain ways, which cron doesn't do.
X(Actually, I believe, it is because /etc/rc, which runs cron, doesn't
Xset up the environment enough for csh to run, and cron just inherits
Xthe situation.) Anyway, the point is that if you find out what csh
Xreally needs in its environment, you might want to set up cron to
Xprovide some reasonable defaults (if it isn't supplied by cron's
Xparent). Also, could you tell me what csh needs, if you find out, so
Xwe can hack our /etc/rc?
X
X<< well, the environment IS a problem. processes that cron forks
X will inherit the environment of the person who ran the cron
X daemon... I plan to edit out such useless things as TERMCAP,
X TERM, and the like; supply correct values for HOME, USER, CWD,
X and whatever else comes to mind. I'll make sure csh works... >>
XFrom ptsfa!ames!seismo!dgis!generous Thu Jan 1 07:33:17 1987
XDate: Thu Jan 1 10:29:20 1987
XFrom: ames!seismo!dgis!generous (Curtis Generous)
XTo: nike!ptsfa!vixie!paul
XStatus: RO
X
XPaul:
X
XOne of the limitations of the present versions of cron is the lack
Xof the capability of specifying a way to execute a command every
Xn units of time.
X
XHere is a good example:
X
X# Present method to start up uucico
X02,12,22,32,42,52 * * * * exec /usr/lib/uucp/uucico -r1
X
X# New method ?? (the ':' here is just one possibility for syntax)
X02:10 * * * * exec /usr/lib/uucp/uucico -r1
X
XThis method would prove very helpful for those programs that get started
Xevery few minutes, making the entry long and not easily readable. The first
Xnumber would specify the base time, and the second number the repetition
Xinterval.
X
X<< Good idea, but bob@acornrc beat you to it. I used '/' instead of
X ':'. This is my personal preference, and seems intuitive when you
X think of the divide operator in C... Does anyone have a preference? >>
X
XFrom ptsfa!lll-lcc!seismo!decuac!c3pe!c3engr!charles Thu Jan 1 17:04:24 1987
XFrom: lll-lcc!seismo!c3pe!c3engr!charles (Charles Green)
XTo: c3pe!decuac!dolqci!vrdxhq!seismo!lll-lcc!ptsfa!vixie!paul
XDate: Thu Jan 1 19:22:47 1987
XStatus: RO
X
XWell, this isn't a compatible extension, but I have in times past wondered
Xabout a facility to let you start a process at intervals of, say, 17 minutes,
Xinstead of particular minutes out of each hour.
X
X<< This was a popular request! >>
X
XFrom seismo!uwvax!astroatc!nicmad!norvax!mann Sun Jan 4 13:04:01 1987
XDate: Fri, 2 Jan 87 09:23:53 cst
XFrom: lll-lcc!seismo!uwvax!astroatc!nicmad!norvax!mann (Tom Mann)
XTo: ptsfa!vixie!paul
XStatus: RO
X
XI'm not sure if it is in cron (either SysV or BSD ... if it is, I haven't
Xfigured it out ) but a comment feature would SURE BE NICE!.
XThere are times when I want to comment out an entry
Xfor a period of time; it might also make it a lot more legible.
X
X<< My cron allows blank lines and standard #-type comments. I know
X that one BSD4.2 cron I've used had it. I don't know about SysV. >>
X
XFrom ptsfa!hoptoad!hugh Mon Jan 5 10:26:46 1987
XDate: Mon, 5 Jan 87 01:22:17 PST
XFrom: hoptoad!hugh (Hugh Daniel)
XTo: ptsfa!vixie!paul
XStatus: RO
X
X Hi, I do have a BIG one that I would like. I want to log ALL output
Xfrom command lines into a file for each line. Thus I might have a chance
Xof finding out why my crontab entry did not work.
X This would seem to work best if done by cron, as it is now I have a google
Xof shell scripts laying about just to put the error output where I can see
Xit.
X
X<< My cron (and the SysV cron) will send mail to the owner of the
X particular crontab file if a command generates any output on stdout
X or stderr. This can be irritating, but if you write a script such
X that any output means a problem occurred, you can avoid most logfile
X needs, and not generate mail except in unforeseen circumstances. >>
X
XFrom ptsfa!dual!ucbvax!ihnp4!anvil!es!Robert_Toxen Mon Jan 5 13:08:46 1987
XFrom: dual!ucbvax!ihnp4!anvil!es!Robert_Toxen
XDate: Fri, 2 Jan 87 14:25:29 EST
XTo: anvil!ihnp4!ucbvax!dual!ptsfa!vixie!paul
XStatus: RO
X
XHere are some suggestions:
X1. Run it through the C preprocessor via "/lib/<whatever>".
X
X<< hmmm. this seems of limited utility, and if you really wanted
X to do it that way, you could do it yourself (since users can
X write to their own crontab files). I'll add '-' (read stdin)
X to the crontab installer program to facilitate this. >>
X
X2. Allow specifying every Nth day of week, i.e., every second Wednesday.
X I did this to calendar by separating the day of week (Wed=4, which one
X to start on and N with slashes). I took modulo the day of year as a
X starting point so that someone with a desk calendar documenting such
X things can easily determine the offset (second number). I did this
X while at SGI; alas I don't have a copy of the code.
X
X<< I can see how this could be useful, but I'm not sure how I'd
X implement it. Cron currently doesn't keep track of the last time
X a given command was run; whether the current Wednesday is the first
X or second since the command was last run would be pretty hard to
X figure out. I'd have to keep a database of commands and their
X execution around, and purge it when the crontab was overwritten.
X This is too much work for me, but if someone adds it, let me know. >>
X
XFrom ptsfa!ames!seismo!cbmvax!devon!paul Tue Jan 6 05:50:17 1987
XFrom: ames!seismo!cbmvax!devon!paul
XTo: cbmvax!seismo!nike!ptsfa!vixie!paul
XDate: Mon Jan 5 09:29:57 1987
XStatus: RO
X
XOne problem that has always plagued me with cron is the assumed ORing.
XI'd like to see some type of ANDing implemented. I guess I can best
Xdescribe this by example. Say I have the following line in my crontab
Xfile:
X
X* * 4-31 * 1-6 /usr/bin/command
X
XWhat this does is run 'command' on the 4th thru 31st days of the
Xmonth, AND on Monday thru Saturday; which probably means running it
Xevery day of the month (unless Sunday falls on days 1-3). This
Xhappens because cron runs the command if the day-of-month OR the
Xday-of-week is true.
X
XWhat I'd like to happen with the above line is to run the command ONLY
Xon Monday thru Saturday any time after the 3rd of the month, e.g. if
Xthe day-of-month AND the day-of-week are true.
X
XMy proposal to you is to implement some special chars for the first
Xfive fields. Examples:
X
X* * !1-3 * 1-6 /usr/bin/command
X
X(run command Mon-Sat, but NOT [!] on the first 3 days of the month)
X
X* * &4-31 * &1-6 /usr/bin/command
X
X(run command if day-of-month AND day-of-week are true)
X
XGet the picture? This would be compatable with existing versions of
Xcron (which wouldn't currently be using any special characters, so
Xthat old crontabs would be handled correctly).
X
X<< This message made me aware of the actual boolean expression involved
X in a crontab entry. I'd assumed that it was
X (minute && hour && DoM && month && DoW)
X But it's really
X (minute && hour && month && (DoM || DoW))
X
X I can see some value in changing this, but with a fixed order of
X fields, operators get to be kindof unary, which && and || really
X aren't. If someone has an idea on a syntax that allows useful
X variations to the standard (&& && && (||)) default, please suggest. >>
X
XFrom bobkat!pedz Tue Jan 6 20:02:10 1987
XFrom: pedz@bobkat.UUCP (Pedz Thing)
XDate: 2 Jan 87 17:34:44 GMT
XStatus: RO
X
XLog files! It would be nice to be able to specify a log for cron
Xitself and also a log for each program's stdout and stderr to go to.
XThe latter can of course be done with > and 2> but it would be nice if
Xthere could be a single line with some sort of pattern like
X`> /usr/spool/log/%' and the command would be substituted for the %.
XAnother thing which would be nice is to be able to specify which shell
Xto call to give the command to.
X
X<< Log files are done with mail. The '%' idea could be useful if
X a different character were used (% is special to cron, see man
X page); a different directory would have to be chosen, since each
X user has their own crontab file; and something intelligent would
X have to be done in the file naming, since the first word of the
X command might be ambiguous (with other commands). In short, it's
X too much work. Sorry. >>
X
XFrom guy%gorodish@sun Tue Jan 6 20:03:13 1987
XFrom: guy%gorodish@sun (Guy Harris)
XMessage-ID: <10944@sun.uucp>
XDate: 5 Jan 87 12:09:09 GMT
XReferences: <429@vixie.UUCP> <359@bobkat.UUCP>
XSender: news@sun.uucp
XStatus: RO
X
X> Another thing which would be nice is to be able to specify which shell
X> to call to give the command to.
X
XWell, the obvious choice would be the user's shell, but this wouldn't work
Xfor accounts like "uucico".
X
X<< I use the owning user's shell, and to handle "uucico" I check a
X list of "acceptable shells" (currently compiled in, does anybody
X mind?), substituting a default (compiled in) shell if the user's
X shell isn't on the list.
X
X BTW, "compiled in" means that it's in a .h file, easily changed
X during installation, but requiring recompilation to modify. You
X don't have to go digging through the code to find it... >>
X
XFrom qantel!hplabs!ucbvax!mwm@violet.berkeley.edu Tue Jan 6 21:24:48 1987
XTo: hplabs!qantel!vixie!paul (Paul Vixie Esq)
XDate: 04 Jan 87 00:42:35 PST (Sun)
XFrom: Mike Meyer <mwm@violet.berkeley.edu>
XStatus: RO
X
X<<[Discussion of RMS/FSF, and mwm's GNU Cron deleted]>>
X
XOh, yeah - here are the extensions on my cron:
X
X1) Sunday is both day 0 and day 7, so it complies with both SysV and
XBSD cron.
X
X<< Good idea. I did it too, thanks for informing me. >>
X
X2) At is integrated into the cron. Instead of atrun to scan the
X/usr/spool/at directory, at files are put into the /usr/lib/cron
Xdirectory along with users cron files, and cron fabricates a line from
Xa crontab file to run them. This is considered a major win by all who
Xuse it.
X
X<< I don't use 'at', and my cron doesn't do anything with it. To run
X 'at', I use 'atrun' the same way the current BSD cron does. My
X crontab files are in /usr/spool/cron/crontabs, in the SysV
X tradition -- not in /usr/lib/cron. This is a configuration
X parameter, of course. >>
X
XThere are two known restrictions:
X
X1) I don't support any of the SysV security hooks. I don't have a use
Xfor them, and RMS didn't like the idea at all :-).
X
X<< This means cron.allow and cron.deny. I plan to support them, as
X they've been quite helpful at various HPUX sites I've administered. >>
X
X2) Cron expects to be able to create files with names longer than 14
Xcharacters, which makes it hard to run on SysV. At least one person
Xwas working on a port, but I don't know how it's going. That might
Xmake for a good reason for releasing yours, right there.
X
X<< If someone has SysV (with the 14-character limit), they probably
X won't want my cron, since it doesn't add much to the standard
X version (which they may have support for). My cron is not currently
X portable to non-BSD systems, since it relies on interval timers (I
X needed to sleep for intervals more granular than seconds alone would
X allow). The port would be trivial, and I will do it if a lot of
X people ask for it... >>
X
XOh, yeah - I'm going to see about getting this cron integrated into
Xthe next 4BSD release.
X
X<< How does one go about this? I have a few nifty gadgets I'd like
X to contribute, this cron being one of them... >>
X
X<<[more FSF/GNU discussion deleted]>>
X
XFrom qantel!hplabs!ames!ut-sally!ut-ngp!melpad!bigtex!james Tue Jan 6 21:24:57 1987
XPosted-Date: Fri, 2 Jan 87 19:26:16 est
XDate: Fri, 2 Jan 87 19:26:16 est
XFrom: hplabs!ames!ut-sally!ut-ngp!bigtex!james
XTo: vixie!paul
XStatus: RO
X
XYes!!! There are several critical failures in System V cron...
X
X1. Pass all variables in cron's environment into the environment of things
X cron starts up, or at least into the crontab entries started up (at jobs
X will inherit the environment of the user). If nothing else it is critically
X important that the TZ variable be passed on. PATH should be passed on too.
X Basically, passing environment values allows one to design a standard
X environment with TZ and PATH and have that run by everything. If anyone
X tells you this is no big deal, consider what happens when uucico is
X started by cron in CA to make a long distance phone link... Unless the
X administrator is really on his/her toes, calls scheduled at 5pm will really
X go at two in the afternoon, needlessly incurring huge phone bills, all
X because System V refuses to pass the TZ from its environment down. There
X are work arounds, but only putting it in cron will really work. This is
X not a security hole.
X
X<< delete TERM and TERMCAP; modify HOME, USER, and CWD; pass TZ and
X PATH through undisturbed. any other requests out there?
X
X BSD doesn't have this problem -- TZ is passed right on through if
X you define it in the shell before starting my cron daemon. However,
X the BSD I'm running this on doesn't need TZ to be defined anyway...
X The default in the kernel has been just fine so far... But just the
X same, if/when I port to SysV (I guess I really should), I'll make
X sure this works right.
X
X I guess I've been spoiled. HPUX is SysV-based, and I never had a
X problem with cron and TZ when I used it. >>
X
X2. A way to avoid logging stuff in /usr/lib/cron/log. I have a cron entry
X run uudemon.hr every 10 minutes. This is 144 times/day. Each run generates
X three lines of text, for a total of 432 lines of text I don't want to see.
X Obviously this should be optional, but it would be nice if there were a
X way to flag an entry so that it wasn't logged at all unless there was an
X error.
X
X<< I don't know nothin' 'bout no /usr/lib/cron/log. What is this file?
X I don't see any reason to create log entries, given the mail-the-
X output behaviour. Opinions, anyone? >>
X
XI will come up with other ideas no doubt, but I can always implement them
Xmyself.
X
X<< That's what I like about PD software. Please send me the diffs! >>
X
XThe other problem you have is making sure you can run standard
Xcrontabs. I would suggest something like this: if the command part of the
Xentry starts with an unescaped -, then flags and options follow immediately
Xthereafter. As in:
X
X2,12,22,32,42,52 * * * * -q /usr/lib/uucp/uudemon.hr
X
XThis could mean do not log the uudemon.hr run unless there is a problem of
Xsome kind. This is probably safe as not many filenames start with "-", and
Xthose that do are already a problem for people.
X
X<< Since I don't plan on supporting /usr/lib/cron/log in ANY form unless
X many people request it, I won't be needing -q as you've defined it.
X I could use something like this to avoid sending mail on errors, for
X the occasional script that you don't want to bullet-proof.
X
X The compatibility issue is CRITICAL. The 4.3BSD crontab format is
X a crime against the whole philosophy of Unix(TM), in my opinion. >>
X
XOne other minor thing to consider is the ulimit: can different users get
Xdifferent ulimits for their crontab entries?
X
X<< Boy I'm ignorant today. What's a ulimit, and what should I do with
X it in a crontab? Suggestions, enlightenment, etc ?? >>
X
XFrom qantel!lll-crg!ames!uw-beaver!uw-nsr!john Tue Jan 6 23:32:44 1987
XDate: Thu, 1 Jan 87 10:53:05 pst
XFrom: lll-crg!ames!uw-beaver!uw-nsr!john (John Sambrook 5-7433)
XTo: vixie!paul
XStatus: RO
X
XHow about not hardwiring the default environment that cron builds for its
Xchildren in the cron program itself? Our cron does this and it's the pits
Xbecause we are TZ=PST8PDT not TZ=EST5EDT !
X
X<< yeachk. I assure you, I will not hardwire the TZ! >>
XFrom ptsfa!well!dv Fri Jan 9 04:01:50 1987
XDate: Thu, 8 Jan 87 23:50:40 pst
XFrom: well!dv (David W. Vezie)
XTo: ptsfa!vixie!paul
XStatus: RO
X
X6, have a special notation called 'H' which would expand to weekends
X and holidays (you'd have to keep a database somewhere of real
X holidays), and also 'W' for workdays (neither weekend or holiday).
X
X<< Too much work. There should be a standard way to define and
X detect holidays under Unix(TM); if there were, I'd use it. As
X it is, I'll leave this for someone else to add.
X
X I can see the usefulness; it just doesn't quite seem worth it. >>
XFrom qantel!gatech!akgua!blnt1!jat Wed Jan 14 20:00:40 1987
XDate: Tue, 13 Jan 87 16:39:38 EST
XFrom: gatech!akgua!blnt1!jat
XStatus: RO
X
X1) Add some way to tell cron to reread the files, say kill -1 <pid>
X
X<< whenever the 'crontab' program is run and updates a crontab file,
X a file /usr/spool/cron/POKECRON is created; next time the cron
X daemon wakes up, it sees it, and re-reads the crontab files.
X
X I thought of handling the signal; even implemented it. Then this
X clever idea hit me and I ripped it all out and added a single
X IF-statement to handle the POKECRON file. >>
X
X2) Have some kind of retry time so that if a command fails, cron will try to
X execute it again after a certain period. This is useful if you have some
X type of cleanup program that can run at the scheduled time for some reason
X (such as locked device, unmounted filesystem, etc).
X
X<< Hmmm, sounds useful. I could do this by submitting an 'at' job...
X I'll think about it. >>
XFrom ptsfa!dual!ucbvax!ihnp4!mtuxo!ender Sat Jan 3 16:54:00 1987
XFrom: dual!ucbvax!ihnp4!mtuxo!ender
XDate: Sat, 3 Jan 87 14:05:13 PST
XTo: ucbvax!dual!ptsfa!vixie!paul
XStatus: RO
X
XIt would be nice if nonprivileged users can setup personal crontab files
X(~/.cronrc, say) and be able to run personal jobs at regular intervals.
X
X<< this is done, but in the SysV style: the 'crontab' program installs
X a new crontab file for the executing user (can be overridden by root
X for setup of uucp and news). the advantage of this is that (1) when
X a crontab is changed, the daemon can be informed automatically; and
X (2) the file can be syntax-checked before installation. >>
XFrom ptsfa!ames!seismo!ihnp4!lcc!richard Fri Jan 16 04:47:33 1987
XDate: Fri, 16 Jan 87 07:44:57 EST
XTo: nike!ptsfa!vixie!paul
XStatus: RO
X
XThe System V cron is nice, but it has a few annoying features. One is that
Xits mail files will say that the previous message is the output of "one of your
Xcron commands." I wish it would say WHICH cron commmand.
X
X<< Done. Also which shell, which user (useful when the mail gets
X forwarded), which home directory, and other useful crud. >>
X
XAnother problem is with timezones. It is necessary to specify TZ=PST8PDT (or
Xwhatever) when you invoke cron (from inittab, or /etc/rc) and it is also
Xnecessary to add TZ=PST8PDT to each crontab line which might need it. Cron
Xshould automatically export its idea of the "TZ" to each invoked command, and
Xit should be possible to put a line in the crontab file which overrides that
Xfor every command in the file (e.g., most users are on EST, so cron is run
Xwith TZ=EST5EDT; but one user is usually on PST and wants all of his cron
Xcommands to run with TZ=PST8PDT). This might be extended to allow any
Xenvironment variable to be specified once for the whole crontab file (e.g.,
XPATH).
X
X<< Well, since I run the user's shell, you could put this into .cshrc.
X generic environment-variable setting could be useful, though. Since
X I have to modify the environment anyway, I'll consider this. >>
X
XA log file might be a nice idea, but the System V cron log is too verbose.
XI seem to remember that cron keeps it open, too; so you can't even have
Xsomething go and periodically clean it out.
X
X<< I don't do /usr/lib/cron/log. I wasn't aware of this file until I
X got all these suggestions. Do people want this file? Tell me! >>
END-of-MAIL
echo x - bitstring.3
sed 's/^X//' >bitstring.3 << 'END-of-bitstring.3'
X.\" Copyright (c) 1989, 1991, 1993
X.\" The Regents of the University of California. All rights reserved.
X.\"
X.\" This code is derived from software contributed to Berkeley by
X.\" Paul Vixie.
X.\" Redistribution and use in source and binary forms, with or without
X.\" modification, are permitted provided that the following conditions
X.\" are met:
X.\" 1. Redistributions of source code must retain the above copyright
X.\" notice, this list of conditions and the following disclaimer.
X.\" 2. Redistributions in binary form must reproduce the above copyright
X.\" notice, this list of conditions and the following disclaimer in the
X.\" documentation and/or other materials provided with the distribution.
X.\" 3. All advertising materials mentioning features or use of this software
X.\" must display the following acknowledgement:
X.\" This product includes software developed by the University of
X.\" California, Berkeley and its contributors.
X.\" 4. Neither the name of the University nor the names of its contributors
X.\" may be used to endorse or promote products derived from this software
X.\" without specific prior written permission.
X.\"
X.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X.\" SUCH DAMAGE.
X.\"
X.\" @(#)bitstring.3 8.1 (Berkeley) 7/19/93
X.\"
X.TH BITSTRING 3 "July 19, 1993"
X.SH NAME
X\fBbit_alloc\fP,
X\fBbit_clear\fP,
X\fBbit_decl\fP,
X\fBbit_ffs\fP,
X\fBbit_nclear\fP,
X\fBbit_nset,\fP
X\fBbit_set\fP,
X\fBbitstr_size\fP,
X\fBbit_test\fP
X\- bit-string manipulation macros
X.SH SYNOPSIS
X.nf
X\fB#include <bitstring.h>\fP
X
X\f2bitstr_t\f1 \f2*\f1
X\fBbit_alloc\fP(\f2int\f1 \f2nbits\f1);
X
X\fBbit_decl\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2nbits\f1);
X
X\fBbit_clear\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2bit\f1);
X
X\fBbit_ffc\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2nbits\f1, \f2int\f1 \f2*value\f1);
X
X\fBbit_ffs\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2nbits\f1, \f2int\f1 \f2*value\f1);
X
X\fBbit_nclear\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2start\f1, \f2int\f1 \f2stop\f1);
X
X\fBbit_nset\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2start\f1, \f2int\f1 \f2stop\f1);
X
X\fBbit_set\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2bit\f1);
X
X\fBbitstr_size\fP(\f2int\f1 \f2nbits\f1);
X
X\fBbit_test\fP(\f2bit_str\f1 \f2name\f1, \f2int\f1 \f2bit\f1);
X.fi
X.SH DESCRIPTION
XThese macros operate on strings of bits.
X.PP
XThe macro \fBbit_alloc\fP() returns a pointer of type
X``\f2bitstr_t\f1 \f2*\f1'' to sufficient space to store
X.I nbits
Xbits, or NULL if no space is available.
X.PP
XThe macro \fBbit_decl\fP() allocates sufficient space to store
X.I nbits
Xbits on the stack.
X.PP
XThe macro \fBbitstr_size\fP() returns the number of elements of type
X.I bitstr_t
Xnecessary to store
X.I nbits
Xbits.
XThis is useful for copying bit strings.
X.PP
XThe macros
X\fBbit_clear\fP()
Xand
X\fBbit_set\fP()
Xclear or set the zero-based numbered bit
X.IR bit ,
Xin the bit string
X.IR name .
X.PP
XThe
X\fBbit_nset\fP()
Xand
X\fBbit_nclear\fP()
Xmacros set or clear the zero-based numbered bits from
X.I start
Xto
X.I stop
Xin the bit string
X.IR name .
X.PP
XThe
X\fBbit_test\fP() macro evaluates to non-zero if the zero-based numbered bit
X.I bit
Xof bit string
X.I name
Xis set, and zero otherwise.
X.PP
XThe \fBbit_ffs\fP() macro stores in the location referenced by
X.I value
Xthe zero-based number of the first bit set in the array of
X.I nbits
Xbits referenced by
X.IR name .
XIf no bits are set, the location referenced by
X.I value
Xis set to \-1.
X.PP
XThe macro \fBbit_ffc\fP() stores in the location referenced by
X.I value
Xthe zero-based number of the first bit not set in the array of
X.I nbits
Xbits referenced by
X.IR name .
XIf all bits are set, the location referenced by
X.I value
Xis set to \-1.
X.PP
XThe arguments to these macros are evaluated only once and may safely
Xhave side effects.
X.SH EXAMPLE
X.nf
X.in +5
X#include <limits.h>
X#include <bitstring.h>
X
X#define LPR_BUSY_BIT 0
X#define LPR_FORMAT_BIT 1
X#define LPR_DOWNLOAD_BIT 2
X#define LPR_AVAILABLE_BIT 9
X#define LPR_MAX_BITS 10
X
Xmake_lpr_available()
X{
X bitstr_t bit_decl(bitlist, LPR_MAX_BITS);
X ...
X bit_nclear(bitlist, 0, LPR_MAX_BITS - 1);
X ...
X if (!bit_test(bitlist, LPR_BUSY_BIT)) {
X bit_clear(bitlist, LPR_FORMAT_BIT);
X bit_clear(bitlist, LPR_DOWNLOAD_BIT);
X bit_set(bitlist, LPR_AVAILABLE_BIT);
X }
X}
X.fi
X.SH SEE ALSO
Xmalloc(3)
END-of-bitstring.3
echo x - crontab.5
sed 's/^X//' >crontab.5 << 'END-of-crontab.5'
X.\"/* Copyright 1988,1990,1993,1994 by Paul Vixie
X.\" * All rights reserved
X.\" */
X.\"
X.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X.\" Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X.\"
X.\" Permission to use, copy, modify, and distribute this software for any
X.\" purpose with or without fee is hereby granted, provided that the above
X.\" copyright notice and this permission notice appear in all copies.
X.\"
X.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X.\"
X.\" $Id: crontab.5,v 1.6 2004/01/23 19:03:33 vixie Exp $
X.\"
X.TH CRONTAB 5 "24 January 1994"
X.UC 4
X.SH NAME
Xcrontab \- tables for driving cron (ISC Cron V4.1)
X.SH DESCRIPTION
XA
X.I crontab
Xfile contains instructions to the
X.IR cron (8)
Xdaemon of the general form: ``run this command at this time on this date''.
XEach user has their own crontab, and commands in any given crontab will be
Xexecuted as the user who owns the crontab. Uucp and News will usually have
Xtheir own crontabs, eliminating the need for explicitly running
X.IR su (1)
Xas part of a cron command.
X.PP
XBlank lines and leading spaces and tabs are ignored. Lines whose first
Xnon-space character is a pound-sign (#) are comments, and are ignored.
XNote that comments are not allowed on the same line as cron commands, since
Xthey will be taken to be part of the command. Similarly, comments are not
Xallowed on the same line as environment variable settings.
X.PP
XAn active line in a crontab will be either an environment setting or a cron
Xcommand. An environment setting is of the form,
X.PP
X name = value
X.PP
Xwhere the spaces around the equal-sign (=) are optional, and any subsequent
Xnon-leading spaces in
X.I value
Xwill be part of the value assigned to
X.IR name .
XThe
X.I value
Xstring may be placed in quotes (single or double, but matching) to preserve
Xleading or trailing blanks.
X.PP
XSeveral environment variables are set up
Xautomatically by the
X.IR cron (8)
Xdaemon.
XSHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd
Xline of the crontab's owner.
XHOME and SHELL may be overridden by settings in the crontab; LOGNAME may not.
X.PP
X(Another note: the LOGNAME variable is sometimes called USER on BSD systems...
Xon these systems, USER will be set also.)
X.PP
XIn addition to LOGNAME, HOME, and SHELL,
X.IR cron (8)
Xwill look at MAILTO if it has any reason to send mail as a result of running
Xcommands in ``this'' crontab. If MAILTO is defined (and non-empty), mail is
Xsent to the user so named. If MAILTO is defined but empty (MAILTO=""), no
Xmail will be sent. Otherwise mail is sent to the owner of the crontab. This
Xoption is useful if you decide on /bin/mail instead of /usr/lib/sendmail as
Xyour mailer when you install cron -- /bin/mail doesn't do aliasing, and UUCP
Xusually doesn't read its mail.
X.PP
XThe format of a cron command is very much the V7 standard, with a number of
Xupward-compatible extensions. Each line has five time and date fields,
Xfollowed by a user name if this is the system crontab file,
Xfollowed by a command. Commands are executed by
X.IR cron (8)
Xwhen the minute, hour, and month of year fields match the current time,
X.I and
Xwhen at least one of the two day fields (day of month, or day of week)
Xmatch the current time (see ``Note'' below).
X.IR cron (8)
Xexamines cron entries once every minute.
XThe time and date fields are:
X.IP
X.ta 1.5i
Xfield allowed values
X.br
X----- --------------
X.br
Xminute 0-59
X.br
Xhour 0-23
X.br
Xday of month 1-31
X.br
Xmonth 0-12 (or names, see below)
X.br
Xday of week 0-7 (0 or 7 is Sun, or use names)
X.br
X.PP
XA field may be an asterisk (*), which always stands for ``first\-last''.
X.PP
XRanges of numbers are allowed. Ranges are two numbers separated
Xwith a hyphen. The specified range is inclusive. For example,
X8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10
Xand 11.
X.PP
XLists are allowed. A list is a set of numbers (or ranges)
Xseparated by commas. Examples: ``1,2,5,9'', ``0-4,8-12''.
X.PP
XStep values can be used in conjunction with ranges. Following
Xa range with ``/<number>'' specifies skips of the number's value
Xthrough the range. For example, ``0-23/2'' can be used in the hours
Xfield to specify command execution every other hour (the alternative
Xin the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). Steps are
Xalso permitted after an asterisk, so if you want to say ``every two
Xhours'', just use ``*/2''.
X.PP
XNames can also be used for the ``month'' and ``day of week''
Xfields. Use the first three letters of the particular
Xday or month (case doesn't matter). Ranges or
Xlists of names are not allowed.
X.PP
XThe ``sixth'' field (the rest of the line) specifies the command to be
Xrun.
XThe entire command portion of the line, up to a newline or %
Xcharacter, will be executed by /bin/sh or by the shell
Xspecified in the SHELL variable of the cronfile.
XPercent-signs (%) in the command, unless escaped with backslash
X(\\), will be changed into newline characters, and all data
Xafter the first % will be sent to the command as standard
Xinput.
X.PP
XNote: The day of a command's execution can be specified by two
Xfields \(em day of month, and day of week. If both fields are
Xrestricted (ie, aren't *), the command will be run when
X.I either
Xfield matches the current time. For example,
X.br
X``30 4 1,15 * 5''
Xwould cause a command to be run at 4:30 am on the 1st and 15th of each
Xmonth, plus every Friday.
X.SH EXAMPLE CRON FILE
X.nf
X# use /bin/sh to run commands, no matter what /etc/passwd says
XSHELL=/bin/sh
X# mail any output to `paul', no matter whose crontab this is
XMAILTO=paul
X#
X# run five minutes after midnight, every day
X5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
X# run at 2:15pm on the first of every month -- output mailed to paul
X15 14 1 * * $HOME/bin/monthly
X# run at 10 pm on weekdays, annoy Joe
X0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?%
X23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday"
X5 4 * * sun echo "run at 5 after 4 every sunday"
X.fi
X.SH SEE ALSO
Xcron(8), crontab(1)
X.SH EXTENSIONS
XWhen specifying day of week, both day 0 and day 7 will be considered Sunday.
XBSD and ATT seem to disagree about this.
X.PP
XLists and ranges are allowed to co-exist in the same field. "1-3,7-9" would
Xbe rejected by ATT or BSD cron -- they want to see "1-3" or "7,8,9" ONLY.
X.PP
XRanges can include "steps", so "1-9/2" is the same as "1,3,5,7,9".
X.PP
XNames of months or days of the week can be specified by name.
X.PP
XEnvironment variables can be set in the crontab. In BSD or ATT, the
Xenvironment handed to child processes is basically the one from /etc/rc.
X.PP
XCommand output is mailed to the crontab owner (BSD can't do this), can be
Xmailed to a person other than the crontab owner (SysV can't do this), or the
Xfeature can be turned off and no mail will be sent at all (SysV can't do this
Xeither).
X.SH AUTHOR
X.nf
XPaul Vixie <vixie@isc.org>
END-of-crontab.5
echo x - crontab.1
sed 's/^X//' >crontab.1 << 'END-of-crontab.1'
X.\"/* Copyright 1988,1990,1993 by Paul Vixie
X.\" * All rights reserved
X.\" */
X.\"
X.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X.\" Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X.\"
X.\" Permission to use, copy, modify, and distribute this software for any
X.\" purpose with or without fee is hereby granted, provided that the above
X.\" copyright notice and this permission notice appear in all copies.
X.\"
X.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X.\"
X.\" $Id: crontab.1,v 1.7 2004/01/23 19:03:32 vixie Exp $
X.\"
X.TH CRONTAB 1 "29 December 1993"
X.UC 4
X.SH NAME
Xcrontab \- maintain crontab files for individual users (ISC Cron V4.1)
X.SH SYNOPSIS
X.B crontab
X.RB [ -u
X.IR user ] " file"
X.br
X.B crontab
X.RB [ -u
X.IR user ]
X.RB [ -l " | " -r " | " -e ]
X.SH DESCRIPTION
X.I Crontab
Xis the program used to install, deinstall or list the tables
Xused to drive the
X.IR cron (8)
Xdaemon in ISC Cron. Each user can have their own crontab, and though
Xthese are files in /var, they are not intended to be edited directly.
X.PP
XIf the
X.I cron.allow
Xfile exists, then you must be listed therein in order to be allowed to use
Xthis command. If the
X.I cron.allow
Xfile does not exist but the
X.I cron.deny
Xfile does exist, then you must \fBnot\fR be listed in the
X.I cron.deny
Xfile in order to use this command. If neither of these files exists,
Xonly the super user will be allowed to use this command.
X.PP
XIf the
X.I -u
Xoption is given, it specifies the name of the user whose crontab is to be
Xtweaked. If this option is not given,
X.I crontab
Xexamines "your" crontab, i.e., the crontab of the person executing the
Xcommand. Note that
X.IR su (8)
Xcan confuse
X.I crontab
Xand that if you are running inside of
X.IR su (8)
Xyou should always use the
X.I -u
Xoption for safety's sake.
X.PP
XThe first form of this command is used to install a new crontab from some
Xnamed file or standard input if the pseudo-filename ``-'' is given.
X.PP
XThe
X.I -l
Xoption causes the current crontab to be displayed on standard output.
X.PP
XThe
X.I -r
Xoption causes the current crontab to be removed.
X.PP
XThe
X.I -e
Xoption is used to edit the current crontab using the editor specified by
Xthe \s-1VISUAL\s+1 or \s-1EDITOR\s+1 environment variables. After you exit
Xfrom the editor, the modified crontab will be installed automatically.
X.SH "SEE ALSO"
Xcrontab(5), cron(8)
X.SH FILES
X.nf
X/var/cron/cron.allow
X/var/cron/cron.deny
X.fi
X.SH STANDARDS
XThe
X.I crontab
Xcommand conforms to IEEE Std1003.2-1992 (``POSIX''). This new command syntax
Xdiffers from previous versions of Vixie Cron, as well as from the classic
XSVR3 syntax.
X.SH DIAGNOSTICS
XA fairly informative usage message appears if you run it with a bad command
Xline.
X.SH AUTHOR
X.nf
XPaul Vixie <vixie@isc.org>
END-of-crontab.1
echo x - cron.8
sed 's/^X//' >cron.8 << 'END-of-cron.8'
X.\"/* Copyright 1988,1990,1993,1996 by Paul Vixie
X.\" * All rights reserved
X.\" */
X.\"
X.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X.\" Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X.\"
X.\" Permission to use, copy, modify, and distribute this software for any
X.\" purpose with or without fee is hereby granted, provided that the above
X.\" copyright notice and this permission notice appear in all copies.
X.\"
X.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X.\"
X.\" $Id: cron.8,v 1.8 2004/01/23 19:03:32 vixie Exp $
X.\"
X.TH CRON 8 "10 January 1996""
X.UC 4
X.SH NAME
Xcron \- daemon to execute scheduled commands (ISC Cron V4.1)
X.SH SYNOPSIS
X.B cron
X.RB [ \-l
X.IR load_avg ]
X.RB [ \-n ]
X.SH DESCRIPTION
X.I Cron
Xshould be started from /etc/rc or /etc/rc.local. It will return immediately,
Xso you don't need to start it with '&'. The \-n option changes this default
Xbehavior causing it to run in the foreground. This can be useful when
Xstarting it out of init.
X.PP
X.I Cron
Xsearches /var/cron/tabs for crontab files which are named after accounts in
X/etc/passwd; crontabs found are loaded into memory.
X.I Cron
Xalso searches for /etc/crontab which is in a different format (see
X.IR crontab (5)).
X.I Cron
Xthen wakes up every minute, examining all stored crontabs, checking each
Xcommand to see if it should be run in the current minute. When executing
Xcommands, any output is mailed to the owner of the crontab (or to the user
Xnamed in the MAILTO environment variable in the crontab, if such exists).
X.PP
XAdditionally,
X.I cron
Xchecks each minute to see if its spool directory's modtime (or the modtime
Xon
X.IR /etc/crontab )
Xhas changed, and if it has,
X.I cron
Xwill then examine the modtime on all crontabs and reload those which have
Xchanged. Thus
X.I cron
Xneed not be restarted whenever a crontab file is modified. Note that the
X.IR Crontab (1)
Xcommand updates the modtime of the spool directory whenever it changes a
Xcrontab.
X.SS Daylight Saving Time and other time changes
XLocal time changes of less than three hours, such as those caused
Xby the start or end of Daylight Saving Time, are handled specially.
XThis only applies to jobs that run at a specific time and jobs that
Xare run with a granularity greater than one hour. Jobs that run
Xmore frequently are scheduled normally.
X.PP
XIf time has moved forward, those jobs that would have run in the
Xinterval that has been skipped will be run immediately.
XConversely, if time has moved backward, care is taken to avoid running
Xjobs twice.
X.PP
XTime changes of more than 3 hours are considered to be corrections to
Xthe clock or timezone, and the new time is used immediately.
X.SH SIGNALS
XOn receipt of a \s-2SIGHUP\s+2, the cron daemon will close and reopen its
Xlog file. This is useful in scripts which rotate and age log files.
XNaturally this is not relevant if cron was built to use
X.IR syslog (3).
X.SH CAVEATS
XIn this version of
X.BR cron ,
X/etc/crontab must not be readable or writable by any user other than root.
XIn other words, it should be mode 0600.
X.SH "SEE ALSO"
X.IR crontab (1),
X.IR crontab (5)
X.SH AUTHOR
X.nf
XPaul Vixie <vixie@isc.org>
END-of-cron.8
echo x - putman.sh
sed 's/^X//' >putman.sh << 'END-of-putman.sh'
X#!/bin/sh
X
X# putman.sh - install a man page according to local custom
X# vixie 27dec93 [original]
X#
X# $Id: putman.sh,v 1.1.1.1 1996/12/16 19:39:48 halley Exp $
X
XPAGE=$1
XDIR=$2
X
XSECT=`expr $PAGE : '[a-z]*.\([0-9]\)'`
X
X[ -d $DIR/man$SECT ] && {
X set -x
X cp $PAGE $DIR/man$SECT/$PAGE
X set +x
X} || {
X set -x
X nroff -man $PAGE >$DIR/cat$SECT/`basename $PAGE .$SECT`.0
X set +x
X}
X
Xexit 0
END-of-putman.sh
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
X#/* Copyright 1988,1990,1993,1994 by Paul Vixie
X# * All rights reserved
X# */
X
X##
X## Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X## Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X##
X## Permission to use, copy, modify, and distribute this software for any
X## purpose with or without fee is hereby granted, provided that the above
X## copyright notice and this permission notice appear in all copies.
X##
X## THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X## WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X## MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X## ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X## WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X## ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X## OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X##
X
X# Makefile for ISC cron
X#
X# $Id: Makefile,v 1.9 2004/01/23 18:56:42 vixie Exp $
X#
X# vix 03mar88 [moved to RCS, rest of log is in there]
X# vix 30mar87 [goodbye, time.c; hello, getopt]
X# vix 12feb87 [cleanup for distribution]
X# vix 30dec86 [written]
X
X# NOTES:
X# 'make' can be done by anyone
X# 'make install' must be done by root
X#
X# this package needs getopt(3), bitstring(3), and BSD install(8).
X#
X# the configurable stuff in this makefile consists of compilation
X# options (use -O, cron runs forever) and destination directories.
X# SHELL is for the 'augumented make' systems where 'make' imports
X# SHELL from the environment and then uses it to run its commands.
X# if your environment SHELL variable is /bin/csh, make goes real
X# slow and sometimes does the wrong thing.
X#
X# this package needs the 'bitstring macros' library, which is
X# available from me or from the comp.sources.unix archive. if you
X# put 'bitstring.h' in a non-standard place (i.e., not intuited by
X# cc(1)), you will have to define INCLUDE to set the include
X# directory for cc. INCLUDE should be `-Isomethingorother'.
X#
X# there's more configuration info in config.h; edit that first!
X
X#################################### begin configurable stuff
X#<<DESTROOT is assumed to have ./etc, ./bin, and ./man subdirectories>>
XDESTROOT = $(DESTDIR)/usr
XDESTSBIN = $(DESTROOT)/sbin
XDESTBIN = $(DESTROOT)/bin
XDESTMAN = $(DESTROOT)/share/man
X#<<need bitstring.h>>
XINCLUDE = -I.
X#INCLUDE =
X#<<need getopt()>>
XLIBS =
X#<<optimize or debug?>>
X#CDEBUG = -O
XCDEBUG = -g
X#<<lint flags of choice?>>
XLINTFLAGS = -hbxa $(INCLUDE) $(DEBUGGING)
X#<<want to use a nonstandard CC?>>
XCC = gcc -Wall -Wno-unused -Wno-comment
X#<<manifest defines>>
XDEFS =
X#(SGI IRIX systems need this)
X#DEFS = -D_BSD_SIGNALS -Dconst=
X#<<the name of the BSD-like install program>>
X#INSTALL = installbsd
XINSTALL = install
X#<<any special load flags>>
XLDFLAGS =
X#################################### end configurable stuff
X
XSHELL = /bin/sh
XCFLAGS = $(CDEBUG) $(INCLUDE) $(DEFS)
X
XINFOS = README CHANGES FEATURES INSTALL CONVERSION THANKS MAIL
XMANPAGES = bitstring.3 crontab.5 crontab.1 cron.8 putman.sh
XHEADERS = bitstring.h cron.h config.h pathnames.h externs.h \
X macros.h structs.h funcs.h globals.h
XSOURCES = cron.c crontab.c database.c do_command.c entry.c \
X env.c job.c user.c popen.c misc.c pw_dup.c
XSHAR_SOURCE = $(INFOS) $(MANPAGES) Makefile $(HEADERS) $(SOURCES)
XLINT_CRON = cron.c database.c user.c entry.c \
X misc.c job.c do_command.c env.c popen.c pw_dup.c
XLINT_CRONTAB = crontab.c misc.c entry.c env.c
XCRON_OBJ = cron.o database.o user.o entry.o job.o do_command.o \
X misc.o env.o popen.o pw_dup.o
XCRONTAB_OBJ = crontab.o misc.o entry.o env.o pw_dup.o
X
Xall : cron crontab
X
Xlint :
X lint $(LINTFLAGS) $(LINT_CRON) $(LIBS) \
X |grep -v "constant argument to NOT" 2>&1
X lint $(LINTFLAGS) $(LINT_CRONTAB) $(LIBS) \
X |grep -v "constant argument to NOT" 2>&1
X
Xcron : $(CRON_OBJ)
X $(CC) $(LDFLAGS) -o cron $(CRON_OBJ) $(LIBS)
X
Xcrontab : $(CRONTAB_OBJ)
X $(CC) $(LDFLAGS) -o crontab $(CRONTAB_OBJ) $(LIBS)
X
Xinstall : all
X $(INSTALL) -c -m 111 -o root -s cron $(DESTSBIN)/
X $(INSTALL) -c -m 4111 -o root -s crontab $(DESTBIN)/
X# $(INSTALL) -c -m 111 -o root -g crontab -s cron $(DESTSBIN)/
X# $(INSTALL) -c -m 2111 -o root -g crontab -s crontab $(DESTBIN)/
X sh putman.sh crontab.1 $(DESTMAN)
X sh putman.sh cron.8 $(DESTMAN)
X sh putman.sh crontab.5 $(DESTMAN)
X
Xdistclean : clean
X rm -f *.orig *.rej *.BAK *.CKP *~ #*
X rm -f a.out core tags
X
Xclean :
X rm -f *.o
X rm -f cron crontab
X
Xtags :; ctags ${SOURCES}
X
Xkit : $(SHAR_SOURCE)
X shar $(SHAR_SOURCE) >kit
X
X$(CRON_OBJ) : cron.h config.h externs.h pathnames.h Makefile
X$(CRONTAB_OBJ) : cron.h config.h externs.h pathnames.h Makefile
END-of-Makefile
echo x - bitstring.h
sed 's/^X//' >bitstring.h << 'END-of-bitstring.h'
X/*
X * Copyright (c) 1989, 1993
X * The Regents of the University of California. All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Paul Vixie.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X * notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X * notice, this list of conditions and the following disclaimer in the
X * documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X * must display the following acknowledgement:
X * This product includes software developed by the University of
X * California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X * may be used to endorse or promote products derived from this software
X * without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X * @(#)bitstring.h 8.1 (Berkeley) 7/19/93
X */
X
X#ifndef _BITSTRING_H_
X#define _BITSTRING_H_
X
Xtypedef unsigned char bitstr_t;
X
X/* internal macros */
X /* byte of the bitstring bit is in */
X#define _bit_byte(bit) \
X ((bit) >> 3)
X
X /* mask for the bit within its byte */
X#define _bit_mask(bit) \
X (1 << ((bit)&0x7))
X
X/* external macros */
X /* bytes in a bitstring of nbits bits */
X#define bitstr_size(nbits) \
X ((((nbits) - 1) >> 3) + 1)
X
X /* allocate a bitstring */
X#define bit_alloc(nbits) \
X (bitstr_t *)calloc(1, \
X (unsigned int)bitstr_size(nbits) * sizeof(bitstr_t))
X
X /* allocate a bitstring on the stack */
X#define bit_decl(name, nbits) \
X (name)[bitstr_size(nbits)]
X
X /* is bit N of bitstring name set? */
X#define bit_test(name, bit) \
X ((name)[_bit_byte(bit)] & _bit_mask(bit))
X
X /* set bit N of bitstring name */
X#define bit_set(name, bit) \
X (name)[_bit_byte(bit)] |= _bit_mask(bit)
X
X /* clear bit N of bitstring name */
X#define bit_clear(name, bit) \
X (name)[_bit_byte(bit)] &= ~_bit_mask(bit)
X
X /* clear bits start ... stop in bitstring */
X#define bit_nclear(name, start, stop) { \
X register bitstr_t *_name = name; \
X register int _start = start, _stop = stop; \
X register int _startbyte = _bit_byte(_start); \
X register int _stopbyte = _bit_byte(_stop); \
X if (_startbyte == _stopbyte) { \
X _name[_startbyte] &= ((0xff >> (8 - (_start&0x7))) | \
X (0xff << ((_stop&0x7) + 1))); \
X } else { \
X _name[_startbyte] &= 0xff >> (8 - (_start&0x7)); \
X while (++_startbyte < _stopbyte) \
X _name[_startbyte] = 0; \
X _name[_stopbyte] &= 0xff << ((_stop&0x7) + 1); \
X } \
X}
X
X /* set bits start ... stop in bitstring */
X#define bit_nset(name, start, stop) { \
X register bitstr_t *_name = name; \
X register int _start = start, _stop = stop; \
X register int _startbyte = _bit_byte(_start); \
X register int _stopbyte = _bit_byte(_stop); \
X if (_startbyte == _stopbyte) { \
X _name[_startbyte] |= ((0xff << (_start&0x7)) & \
X (0xff >> (7 - (_stop&0x7)))); \
X } else { \
X _name[_startbyte] |= 0xff << ((_start)&0x7); \
X while (++_startbyte < _stopbyte) \
X _name[_startbyte] = 0xff; \
X _name[_stopbyte] |= 0xff >> (7 - (_stop&0x7)); \
X } \
X}
X
X /* find first bit clear in name */
X#define bit_ffc(name, nbits, value) { \
X register bitstr_t *_name = name; \
X register int _byte, _nbits = nbits; \
X register int _stopbyte = _bit_byte(_nbits), _value = -1; \
X for (_byte = 0; _byte <= _stopbyte; ++_byte) \
X if (_name[_byte] != 0xff) { \
X _value = _byte << 3; \
X for (_stopbyte = _name[_byte]; (_stopbyte&0x1); \
X ++_value, _stopbyte >>= 1); \
X break; \
X } \
X *(value) = _value; \
X}
X
X /* find first bit set in name */
X#define bit_ffs(name, nbits, value) { \
X register bitstr_t *_name = name; \
X register int _byte, _nbits = nbits; \
X register int _stopbyte = _bit_byte(_nbits), _value = -1; \
X for (_byte = 0; _byte <= _stopbyte; ++_byte) \
X if (_name[_byte]) { \
X _value = _byte << 3; \
X for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \
X ++_value, _stopbyte >>= 1); \
X break; \
X } \
X *(value) = _value; \
X}
X
X#endif /* !_BITSTRING_H_ */
END-of-bitstring.h
echo x - cron.h
sed 's/^X//' >cron.h << 'END-of-cron.h'
X/* Copyright 1988,1990,1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X/* cron.h - header for vixie's cron
X *
X * $Id: cron.h,v 1.6 2004/01/23 18:56:42 vixie Exp $
X *
X * vix 14nov88 [rest of log is in RCS]
X * vix 14jan87 [0 or 7 can be sunday; thanks, mwm@berkeley]
X * vix 30dec86 [written]
X */
X
X#define CRON_VERSION "V5.0"
X#include "config.h"
X#include "externs.h"
X#include "pathnames.h"
X#include "macros.h"
X#include "structs.h"
X#include "funcs.h"
X#include "globals.h"
END-of-cron.h
echo x - config.h
sed 's/^X//' >config.h << 'END-of-config.h'
X/* Copyright 1988,1990,1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X/* config.h - configurables for ISC Cron
X *
X * $Id: config.h,v 1.10 2004/01/23 18:56:42 vixie Exp $
X */
X
X/*
X * these are site-dependent
X */
X
X#ifndef DEBUGGING
X#define DEBUGGING 1 /* 1 or 0 -- do you want debugging code built in? */
X#endif
X
X /*
X * choose one of these mailer commands. some use
X * /bin/mail for speed; it makes biff bark but doesn't
X * do aliasing. sendmail does do aliasing but is
X * a hog for short messages. aliasing is not needed
X * if you make use of the MAILTO= feature in crontabs.
X * (hint: MAILTO= was added for this reason).
X */
X
X#define MAILFMT "%s -FCronDaemon -odi -oem -oi -t" /*-*/
X /* -Fx = Set full-name of sender
X * -odi = Option Deliverymode Interactive
X * -oem = Option Errors Mailedtosender
X * -oi = Ignore "." alone on a line
X * -t = Get recipient from headers
X */
X#define MAILARG _PATH_SENDMAIL /*-*/
X
X/* #define MAILFMT "%s -d %s" /*-*/
X /* -d = undocumented but common flag: deliver locally?
X */
X/* #define MAILARG "/bin/mail",mailto
X
X/* #define MAILFMT "%s -mlrxto %s" /*-*/
X/* #define MAILARG "/usr/mmdf/bin/submit",mailto /*-*/
X
X/* #define MAIL_DATE /*-*/
X /* should we include an ersatz Date: header in
X * generated mail? if you are using sendmail
X * as the mailer, it is better to let sendmail
X * generate the Date: header.
X */
X
X /* if you want to use syslog(3) instead of appending
X * to CRONDIR/LOG_FILE (/var/cron/log, e.g.), define
X * SYSLOG here. Note that quite a bit of logging
X * info is written, and that you probably don't want
X * to use this on 4.2bsd since everything goes in
X * /usr/spool/mqueue/syslog. On 4.[34]bsd you can
X * tell /etc/syslog.conf to send cron's logging to
X * a separate file.
X *
X * Note that if this and LOG_FILE in "pathnames.h"
X * are both defined, then logging will go to both
X * places.
X */
X#define SYSLOG /*-*/
X
X /* if you want cron to capitalize its name in ps
X * when running a job. Does not work on SYSV.
X */
X/*#define CAPITALIZE_FOR_PS /*-*/
X
X /* if you have a tm_gmtoff member in struct tm.
X * If not, we will have to compute the value ourselves.
X */
X/*#define HAVE_TM_GMTOFF /*-*/
X
X /* if your OS supports a BSD-style login.conf file */
X/*#define LOGIN_CAP /*-*/
X
X /* if your OS supports BSD authentication */
X/*#define BSD_AUTH /*-*/
X
X /* Define this to run crontab setgid instead of
X * setuid root. Group access will be used to read
X * the tabs/atjobs dirs and the allow/deny files.
X * If this is not defined then crontab and at
X * must be setuid root.
X */
X/*#define CRON_GROUP "crontab" /*-*/
END-of-config.h
echo x - pathnames.h
sed 's/^X//' >pathnames.h << 'END-of-pathnames.h'
X/* Copyright 1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X/*
X * $Id: pathnames.h,v 1.9 2004/01/23 18:56:43 vixie Exp $
X */
X
X#ifndef _PATHNAMES_H_
X#define _PATHNAMES_H_
X
X#if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX)
X# include <paths.h>
X#endif /*BSD*/
X
X#ifndef CRONDIR
X /* CRONDIR is where cron(8) and crontab(1) both chdir
X * to; SPOOL_DIR, CRON_ALLOW, CRON_DENY, and LOG_FILE
X * are all relative to this directory.
X */
X#define CRONDIR "/var/cron"
X#endif
X
X /* SPOOLDIR is where the crontabs live.
X * This directory will have its modtime updated
X * whenever crontab(1) changes a crontab; this is
X * the signal for cron(8) to look at each individual
X * crontab file and reload those whose modtimes are
X * newer than they were last time around (or which
X * didn't exist last time around...)
X */
X#define SPOOL_DIR "tabs"
X
X /* cron allow/deny file. At least cron.deny must
X * exist for ordinary users to run crontab.
X */
X#define CRON_ALLOW "cron.allow"
X#define CRON_DENY "cron.deny"
X
X /* undefining this turns off logging to a file. If
X * neither LOG_FILE or SYSLOG is defined, we don't log.
X * If both are defined, we log both ways. Note that if
X * LOG_CRON is defined by <syslog.h>, LOG_FILE will not
X * be used.
X */
X#define LOG_FILE "log"
X
X /* where should the daemon stick its PID?
X * PIDDIR must end in '/'.
X */
X#ifdef _PATH_VARRUN
X# define PIDDIR _PATH_VARRUN
X#else
X# define PIDDIR "/etc/"
X#endif
X#define PIDFILE "cron.pid"
X#define _PATH_CRON_PID PIDDIR PIDFILE
X
X /* 4.3BSD-style crontab */
X#define SYSCRONTAB "/etc/crontab"
X
X /* what editor to use if no EDITOR or VISUAL
X * environment variable specified.
X */
X#if defined(_PATH_VI)
X# define EDITOR _PATH_VI
X#else
X# define EDITOR "/usr/ucb/vi"
X#endif
X
X#ifndef _PATH_SENDMAIL
X# define _PATH_SENDMAIL "/usr/lib/sendmail"
X#endif
X
X#ifndef _PATH_BSHELL
X# define _PATH_BSHELL "/bin/sh"
X#endif
X
X#ifndef _PATH_DEFPATH
X# define _PATH_DEFPATH "/usr/bin:/bin"
X#endif
X
X#ifndef _PATH_TMP
X# define _PATH_TMP "/tmp"
X#endif
X
X#ifndef _PATH_DEVNULL
X# define _PATH_DEVNULL "/dev/null"
X#endif
X
X#endif /* _PATHNAMES_H_ */
END-of-pathnames.h
echo x - externs.h
sed 's/^X//' >externs.h << 'END-of-externs.h'
X/* Copyright 1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X/* reorder these #include's at your peril */
X
X#include <sys/param.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/wait.h>
X#include <sys/fcntl.h>
X#include <sys/file.h>
X#include <sys/stat.h>
X
X#include <bitstring.h>
X#include <ctype.h>
X#ifndef isascii
X#define isascii(c) ((unsigned)(c)<=0177)
X#endif
X#include <dirent.h>
X#include <errno.h>
X#include <fcntl.h>
X#include <grp.h>
X#include <locale.h>
X#include <pwd.h>
X#include <signal.h>
X#include <stdarg.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X#include <time.h>
X#include <unistd.h>
X#include <utime.h>
X
X#if defined(SYSLOG)
X# include <syslog.h>
X#endif
X
X#if defined(LOGIN_CAP)
X# include <login_cap.h>
X#endif /*LOGIN_CAP*/
X
X#if defined(BSD_AUTH)
X# include <bsd_auth.h>
X#endif /*BSD_AUTH*/
X
X#define DIR_T struct dirent
X#define WAIT_T int
X#define SIG_T sig_t
X#define TIME_T time_t
X#define PID_T pid_t
X
X#ifndef TZNAME_ALREADY_DEFINED
Xextern char *tzname[2];
X#endif
X#define TZONE(tm) tzname[(tm).tm_isdst]
X
X#if (defined(BSD)) && (BSD >= 198606) || defined(__linux)
X# define HAVE_FCHOWN
X# define HAVE_FCHMOD
X#endif
X
X#if (defined(BSD)) && (BSD >= 199103) || defined(__linux)
X# define HAVE_SAVED_UIDS
X#endif
X
X#define MY_UID(pw) getuid()
X#define MY_GID(pw) getgid()
X
X/* getopt() isn't part of POSIX. some systems define it in <stdlib.h> anyway.
X * of those that do, some complain that our definition is different and some
X * do not. to add to the misery and confusion, some systems define getopt()
X * in ways that we cannot predict or comprehend, yet do not define the adjunct
X * external variables needed for the interface.
X */
X#if (!defined(BSD) || (BSD < 198911))
Xint getopt(int, char * const *, const char *);
X#endif
X
X#if (!defined(BSD) || (BSD < 199103))
Xextern char *optarg;
Xextern int optind, opterr, optopt;
X#endif
X
X/* digital unix needs this but does not give us a way to identify it.
X */
Xextern int flock(int, int);
X
X/* not all systems who provide flock() provide these definitions.
X */
X#ifndef LOCK_SH
X# define LOCK_SH 1
X#endif
X#ifndef LOCK_EX
X# define LOCK_EX 2
X#endif
X#ifndef LOCK_NB
X# define LOCK_NB 4
X#endif
X#ifndef LOCK_UN
X# define LOCK_UN 8
X#endif
X
X#ifndef WCOREDUMP
X# define WCOREDUMP(st) (((st) & 0200) != 0)
X#endif
END-of-externs.h
echo x - macros.h
sed 's/^X//' >macros.h << 'END-of-macros.h'
X/*
X * $Id: macros.h,v 1.9 2004/01/23 18:56:43 vixie Exp $
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X /* these are really immutable, and are
X * defined for symbolic convenience only
X * TRUE, FALSE, and ERR must be distinct
X * ERR must be < OK.
X */
X#define TRUE 1
X#define FALSE 0
X /* system calls return this on success */
X#define OK 0
X /* or this on error */
X#define ERR (-1)
X
X /* turn this on to get '-x' code */
X#ifndef DEBUGGING
X#define DEBUGGING FALSE
X#endif
X
X#define INIT_PID 1 /* parent of orphans */
X#define READ_PIPE 0 /* which end of a pipe pair do you read? */
X#define WRITE_PIPE 1 /* or write to? */
X#define STDIN 0 /* what is stdin's file descriptor? */
X#define STDOUT 1 /* stdout's? */
X#define STDERR 2 /* stderr's? */
X#define ERROR_EXIT 1 /* exit() with this will scare the shell */
X#define OK_EXIT 0 /* exit() with this is considered 'normal' */
X#define MAX_FNAME 100 /* max length of internally generated fn */
X#define MAX_COMMAND 1000 /* max length of internally generated cmd */
X#define MAX_ENVSTR 1000 /* max length of envvar=value\0 strings */
X#define MAX_TEMPSTR 100 /* obvious */
X#define MAX_UNAME 33 /* max length of username, should be overkill */
X#define ROOT_UID 0 /* don't change this, it really must be root */
X#define ROOT_USER "root" /* ditto */
X
X /* NOTE: these correspond to DebugFlagNames,
X * defined below.
X */
X#define DEXT 0x0001 /* extend flag for other debug masks */
X#define DSCH 0x0002 /* scheduling debug mask */
X#define DPROC 0x0004 /* process control debug mask */
X#define DPARS 0x0008 /* parsing debug mask */
X#define DLOAD 0x0010 /* database loading debug mask */
X#define DMISC 0x0020 /* misc debug mask */
X#define DTEST 0x0040 /* test mode: don't execute any commands */
X
X#define PPC_NULL ((const char **)NULL)
X
X#ifndef MAXHOSTNAMELEN
X#define MAXHOSTNAMELEN 64
X#endif
X
X#define Skip_Blanks(c, f) \
X while (c == '\t' || c == ' ') \
X c = get_char(f);
X
X#define Skip_Nonblanks(c, f) \
X while (c!='\t' && c!=' ' && c!='\n' && c != EOF) \
X c = get_char(f);
X
X#if DEBUGGING
X# define Debug(mask, message) \
X if ((DebugFlags & (mask)) != 0) \
X printf message;
X#else /* !DEBUGGING */
X# define Debug(mask, message) \
X ;
X#endif /* DEBUGGING */
X
X#define MkUpper(ch) (islower(ch) ? toupper(ch) : ch)
X#define Set_LineNum(ln) {Debug(DPARS|DEXT,("linenum=%d\n",ln)); \
X LineNumber = ln; \
X }
X
X#ifdef HAVE_TM_GMTOFF
X#define get_gmtoff(c, t) ((t)->tm_gmtoff)
X#endif
X
X#define SECONDS_PER_MINUTE 60
X#define SECONDS_PER_HOUR 3600
X
X#define FIRST_MINUTE 0
X#define LAST_MINUTE 59
X#define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1)
X
X#define FIRST_HOUR 0
X#define LAST_HOUR 23
X#define HOUR_COUNT (LAST_HOUR - FIRST_HOUR + 1)
X
X#define FIRST_DOM 1
X#define LAST_DOM 31
X#define DOM_COUNT (LAST_DOM - FIRST_DOM + 1)
X
X#define FIRST_MONTH 1
X#define LAST_MONTH 12
X#define MONTH_COUNT (LAST_MONTH - FIRST_MONTH + 1)
X
X/* note on DOW: 0 and 7 are both Sunday, for compatibility reasons. */
X#define FIRST_DOW 0
X#define LAST_DOW 7
X#define DOW_COUNT (LAST_DOW - FIRST_DOW + 1)
X
X/*
X * Because crontab/at files may be owned by their respective users we
X * take extreme care in opening them. If the OS lacks the O_NOFOLLOW
X * we will just have to live without it. In order for this to be an
X * issue an attacker would have to subvert group CRON_GROUP.
X */
X#ifndef O_NOFOLLOW
X#define O_NOFOLLOW 0
X#endif
END-of-macros.h
echo x - structs.h
sed 's/^X//' >structs.h << 'END-of-structs.h'
X/*
X * $Id: structs.h,v 1.7 2004/01/23 18:56:43 vixie Exp $
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
Xtypedef struct _entry {
X struct _entry *next;
X struct passwd *pwd;
X char **envp;
X char *cmd;
X bitstr_t bit_decl(minute, MINUTE_COUNT);
X bitstr_t bit_decl(hour, HOUR_COUNT);
X bitstr_t bit_decl(dom, DOM_COUNT);
X bitstr_t bit_decl(month, MONTH_COUNT);
X bitstr_t bit_decl(dow, DOW_COUNT);
X int flags;
X#define MIN_STAR 0x01
X#define HR_STAR 0x02
X#define DOM_STAR 0x04
X#define DOW_STAR 0x08
X#define WHEN_REBOOT 0x10
X#define DONT_LOG 0x20
X} entry;
X
X /* the crontab database will be a list of the
X * following structure, one element per user
X * plus one for the system.
X *
X * These are the crontabs.
X */
X
Xtypedef struct _user {
X struct _user *next, *prev; /* links */
X char *name;
X time_t mtime; /* last modtime of crontab */
X entry *crontab; /* this person's crontab */
X} user;
X
Xtypedef struct _cron_db {
X user *head, *tail; /* links */
X time_t mtime; /* last modtime on spooldir */
X} cron_db;
X /* in the C tradition, we only create
X * variables for the main program, just
X * extern them elsewhere.
X */
END-of-structs.h
echo x - funcs.h
sed 's/^X//' >funcs.h << 'END-of-funcs.h'
X/*
X * $Id: funcs.h,v 1.9 2004/01/23 18:56:42 vixie Exp $
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X/* Notes:
X * This file has to be included by cron.h after data structure defs.
X * We should reorg this into sections by module.
X */
X
Xvoid set_cron_uid(void),
X set_cron_cwd(void),
X load_database(cron_db *),
X open_logfile(void),
X sigpipe_func(void),
X job_add(entry *, user *),
X do_command(entry *, user *),
X link_user(cron_db *, user *),
X unlink_user(cron_db *, user *),
X free_user(user *),
X env_free(char **),
X unget_char(int, FILE *),
X free_entry(entry *),
X acquire_daemonlock(int),
X skip_comments(FILE *),
X log_it(const char *, int, const char *, const char *),
X log_close(void);
X
Xint job_runqueue(void),
X set_debug_flags(const char *),
X get_char(FILE *),
X get_string(char *, int, FILE *, char *),
X swap_uids(void),
X swap_uids_back(void),
X load_env(char *, FILE *),
X cron_pclose(FILE *),
X glue_strings(char *, size_t, const char *, const char *, char),
X strcmp_until(const char *, const char *, char),
X allowed(const char * ,const char * ,const char *),
X strdtb(char *);
X
Xsize_t strlens(const char *, ...);
X
Xchar *env_get(char *, char **),
X *arpadate(time_t *),
X *mkprints(unsigned char *, unsigned int),
X *first_word(char *, char *),
X **env_init(void),
X **env_copy(char **),
X **env_set(char **, char *);
X
Xuser *load_user(int, struct passwd *, const char *),
X *find_user(cron_db *, const char *);
X
Xentry *load_entry(FILE *, void (*)(), struct passwd *, char **);
X
XFILE *cron_popen(char *, char *, struct passwd *);
X
Xstruct passwd *pw_dup(const struct passwd *);
X
X#ifndef HAVE_TM_GMTOFF
Xlong get_gmtoff(time_t *, struct tm *);
X#endif
END-of-funcs.h
echo x - globals.h
sed 's/^X//' >globals.h << 'END-of-globals.h'
X/*
X * $Id: globals.h,v 1.10 2004/01/23 19:03:33 vixie Exp $
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X#ifdef MAIN_PROGRAM
X# define XTRN
X# define INIT(x) = x
X#else
X# define XTRN extern
X# define INIT(x)
X#endif
X
XXTRN const char *copyright[]
X#ifdef MAIN_PROGRAM
X = {
X "@(#) ISC Cron V4.1",
X "@(#) Copyright 1988,1989,1990,1993,1994 by Paul Vixie",
X "@(#) Copyright 1997,2000 by Internet Software Consortium, Inc.",
X "@(#) Copyright 2004 by Internet Systems Consortium, Inc.",
X "@(#) All rights reserved",
X NULL
X }
X#endif
X ;
X
XXTRN const char *MonthNames[]
X#ifdef MAIN_PROGRAM
X = {
X "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
X NULL
X }
X#endif
X ;
X
XXTRN const char *DowNames[]
X#ifdef MAIN_PROGRAM
X = {
X "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
X NULL
X }
X#endif
X ;
X
XXTRN char *ProgramName INIT("amnesia");
XXTRN int LineNumber INIT(0);
XXTRN time_t StartTime INIT(0);
XXTRN int NoFork INIT(0);
X
X#if DEBUGGING
XXTRN int DebugFlags INIT(0);
XXTRN const char *DebugFlagNames[]
X#ifdef MAIN_PROGRAM
X = {
X "ext", "sch", "proc", "pars", "load", "misc", "test", "bit",
X NULL
X }
X#endif
X ;
X#else
X#define DebugFlags 0
X#endif /* DEBUGGING */
END-of-globals.h
echo x - cron.c
sed 's/^X//' >cron.c << 'END-of-cron.c'
X/* Copyright 1988,1990,1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X#if !defined(lint) && !defined(LINT)
Xstatic char rcsid[] = "$Id: cron.c,v 1.12 2004/01/23 18:56:42 vixie Exp $";
X#endif
X
X#define MAIN_PROGRAM
X
X#include "cron.h"
X
Xenum timejump { negative, small, medium, large };
X
Xstatic void usage(void),
X run_reboot_jobs(cron_db *),
X find_jobs(int, cron_db *, int, int),
X set_time(int),
X cron_sleep(int),
X sigchld_handler(int),
X sighup_handler(int),
X sigchld_reaper(void),
X quit(int),
X parse_args(int c, char *v[]);
X
Xstatic volatile sig_atomic_t got_sighup, got_sigchld;
Xstatic int timeRunning, virtualTime, clockTime;
Xstatic long GMToff;
X
Xstatic void
Xusage(void) {
X const char **dflags;
X
X fprintf(stderr, "usage: %s [-n] [-x [", ProgramName);
X for (dflags = DebugFlagNames; *dflags; dflags++)
X fprintf(stderr, "%s%s", *dflags, dflags[1] ? "," : "]");
X fprintf(stderr, "]\n");
X exit(ERROR_EXIT);
X}
X
Xint
Xmain(int argc, char *argv[]) {
X struct sigaction sact;
X cron_db database;
X int fd;
X
X ProgramName = argv[0];
X
X setlocale(LC_ALL, "");
X
X#if defined(BSD)
X setlinebuf(stdout);
X setlinebuf(stderr);
X#endif
X
X NoFork = 0;
X parse_args(argc, argv);
X
X bzero((char *)&sact, sizeof sact);
X sigemptyset(&sact.sa_mask);
X sact.sa_flags = 0;
X#ifdef SA_RESTART
X sact.sa_flags |= SA_RESTART;
X#endif
X sact.sa_handler = sigchld_handler;
X (void) sigaction(SIGCHLD, &sact, NULL);
X sact.sa_handler = sighup_handler;
X (void) sigaction(SIGHUP, &sact, NULL);
X sact.sa_handler = quit;
X (void) sigaction(SIGINT, &sact, NULL);
X (void) sigaction(SIGTERM, &sact, NULL);
X
X acquire_daemonlock(0);
X set_cron_uid();
X set_cron_cwd();
X
X if (putenv("PATH="_PATH_DEFPATH) < 0) {
X log_it("CRON", getpid(), "DEATH", "can't malloc");
X exit(1);
X }
X
X /* if there are no debug flags turned on, fork as a daemon should.
X */
X if (DebugFlags) {
X#if DEBUGGING
X (void) fprintf(stderr, "[%ld] cron started\n", (long)getpid());
X#endif
X } else if (NoFork == 0) {
X switch (fork()) {
X case -1:
X log_it("CRON",getpid(),"DEATH","can't fork");
X exit(0);
X break;
X case 0:
X /* child process */
X (void) setsid();
X if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) >= 0) {
X (void) dup2(fd, STDIN);
X (void) dup2(fd, STDOUT);
X (void) dup2(fd, STDERR);
X if (fd != STDERR)
X (void) close(fd);
X }
X log_it("CRON",getpid(),"STARTUP",CRON_VERSION);
X break;
X default:
X /* parent process should just die */
X _exit(0);
X }
X }
X
X acquire_daemonlock(0);
X database.head = NULL;
X database.tail = NULL;
X database.mtime = (time_t) 0;
X load_database(&database);
X set_time(TRUE);
X run_reboot_jobs(&database);
X timeRunning = virtualTime = clockTime;
X
X /*
X * Too many clocks, not enough time (Al. Einstein)
X * These clocks are in minutes since the epoch, adjusted for timezone.
X * virtualTime: is the time it *would* be if we woke up
X * promptly and nobody ever changed the clock. It is
X * monotonically increasing... unless a timejump happens.
X * At the top of the loop, all jobs for 'virtualTime' have run.
X * timeRunning: is the time we last awakened.
X * clockTime: is the time when set_time was last called.
X */
X while (TRUE) {
X int timeDiff;
X enum timejump wakeupKind;
X
X /* ... wait for the time (in minutes) to change ... */
X do {
X cron_sleep(timeRunning + 1);
X set_time(FALSE);
X } while (clockTime == timeRunning);
X timeRunning = clockTime;
X
X /*
X * Calculate how the current time differs from our virtual
X * clock. Classify the change into one of 4 cases.
X */
X timeDiff = timeRunning - virtualTime;
X
X /* shortcut for the most common case */
X if (timeDiff == 1) {
X virtualTime = timeRunning;
X find_jobs(virtualTime, &database, TRUE, TRUE);
X } else {
X if (timeDiff > (3*MINUTE_COUNT) ||
X timeDiff < -(3*MINUTE_COUNT))
X wakeupKind = large;
X else if (timeDiff > 5)
X wakeupKind = medium;
X else if (timeDiff > 0)
X wakeupKind = small;
X else
X wakeupKind = negative;
X
X switch (wakeupKind) {
X case small:
X /*
X * case 1: timeDiff is a small positive number
X * (wokeup late) run jobs for each virtual
X * minute until caught up.
X */
X Debug(DSCH, ("[%ld], normal case %d minutes to go\n",
X (long)getpid(), timeDiff))
X do {
X if (job_runqueue())
X sleep(10);
X virtualTime++;
X find_jobs(virtualTime, &database,
X TRUE, TRUE);
X } while (virtualTime < timeRunning);
X break;
X
X case medium:
X /*
X * case 2: timeDiff is a medium-sized positive
X * number, for example because we went to DST
X * run wildcard jobs once, then run any
X * fixed-time jobs that would otherwise be
X * skipped if we use up our minute (possible,
X * if there are a lot of jobs to run) go
X * around the loop again so that wildcard jobs
X * have a chance to run, and we do our
X * housekeeping.
X */
X Debug(DSCH, ("[%ld], DST begins %d minutes to go\n",
X (long)getpid(), timeDiff))
X /* run wildcard jobs for current minute */
X find_jobs(timeRunning, &database, TRUE, FALSE);
X
X /* run fixed-time jobs for each minute missed */
X do {
X if (job_runqueue())
X sleep(10);
X virtualTime++;
X find_jobs(virtualTime, &database,
X FALSE, TRUE);
X set_time(FALSE);
X } while (virtualTime< timeRunning &&
X clockTime == timeRunning);
X break;
X
X case negative:
X /*
X * case 3: timeDiff is a small or medium-sized
X * negative num, eg. because of DST ending.
X * Just run the wildcard jobs. The fixed-time
X * jobs probably have already run, and should
X * not be repeated. Virtual time does not
X * change until we are caught up.
X */
X Debug(DSCH, ("[%ld], DST ends %d minutes to go\n",
X (long)getpid(), timeDiff))
X find_jobs(timeRunning, &database, TRUE, FALSE);
X break;
X default:
X /*
X * other: time has changed a *lot*,
X * jump virtual time, and run everything
X */
X Debug(DSCH, ("[%ld], clock jumped\n",
X (long)getpid()))
X virtualTime = timeRunning;
X find_jobs(timeRunning, &database, TRUE, TRUE);
X }
X }
X
X /* Jobs to be run (if any) are loaded; clear the queue. */
X job_runqueue();
X
X /* Check to see if we received a signal while running jobs. */
X if (got_sighup) {
X got_sighup = 0;
X log_close();
X }
X if (got_sigchld) {
X got_sigchld = 0;
X sigchld_reaper();
X }
X load_database(&database);
X }
X}
X
Xstatic void
Xrun_reboot_jobs(cron_db *db) {
X user *u;
X entry *e;
X
X for (u = db->head; u != NULL; u = u->next) {
X for (e = u->crontab; e != NULL; e = e->next) {
X if (e->flags & WHEN_REBOOT)
X job_add(e, u);
X }
X }
X (void) job_runqueue();
X}
X
Xstatic void
Xfind_jobs(int vtime, cron_db *db, int doWild, int doNonWild) {
X time_t virtualSecond = vtime * SECONDS_PER_MINUTE;
X struct tm *tm = gmtime(&virtualSecond);
X int minute, hour, dom, month, dow;
X user *u;
X entry *e;
X
X /* make 0-based values out of these so we can use them as indicies
X */
X minute = tm->tm_min -FIRST_MINUTE;
X hour = tm->tm_hour -FIRST_HOUR;
X dom = tm->tm_mday -FIRST_DOM;
X month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
X dow = tm->tm_wday -FIRST_DOW;
X
X Debug(DSCH, ("[%ld] tick(%d,%d,%d,%d,%d) %s %s\n",
X (long)getpid(), minute, hour, dom, month, dow,
X doWild?" ":"No wildcard",doNonWild?" ":"Wildcard only"))
X
X /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
X * first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
X * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
X * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre.
X * like many bizarre things, it's the standard.
X */
X for (u = db->head; u != NULL; u = u->next) {
X for (e = u->crontab; e != NULL; e = e->next) {
X Debug(DSCH|DEXT, ("user [%s:%ld:%ld:...] cmd=\"%s\"\n",
X e->pwd->pw_name, (long)e->pwd->pw_uid,
X (long)e->pwd->pw_gid, e->cmd))
X if (bit_test(e->minute, minute) &&
X bit_test(e->hour, hour) &&
X bit_test(e->month, month) &&
X ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
X ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
X : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
X )
X ) {
X if ((doNonWild &&
X !(e->flags & (MIN_STAR|HR_STAR))) ||
X (doWild && (e->flags & (MIN_STAR|HR_STAR))))
X job_add(e, u);
X }
X }
X }
X}
X
X/*
X * Set StartTime and clockTime to the current time.
X * These are used for computing what time it really is right now.
X * Note that clockTime is a unix wallclock time converted to minutes.
X */
Xstatic void
Xset_time(int initialize) {
X struct tm tm;
X static int isdst;
X
X StartTime = time(NULL);
X
X /* We adjust the time to GMT so we can catch DST changes. */
X tm = *localtime(&StartTime);
X if (initialize || tm.tm_isdst != isdst) {
X isdst = tm.tm_isdst;
X GMToff = get_gmtoff(&StartTime, &tm);
X Debug(DSCH, ("[%ld] GMToff=%ld\n",
X (long)getpid(), (long)GMToff))
X }
X clockTime = (StartTime + GMToff) / (time_t)SECONDS_PER_MINUTE;
X}
X
X/*
X * Try to just hit the next minute.
X */
Xstatic void
Xcron_sleep(int target) {
X time_t t1, t2;
X int seconds_to_wait;
X
X t1 = time(NULL) + GMToff;
X seconds_to_wait = (int)(target * SECONDS_PER_MINUTE - t1) + 1;
X Debug(DSCH, ("[%ld] Target time=%ld, sec-to-wait=%d\n",
X (long)getpid(), (long)target*SECONDS_PER_MINUTE, seconds_to_wait))
X
X while (seconds_to_wait > 0 && seconds_to_wait < 65) {
X sleep((unsigned int) seconds_to_wait);
X
X /*
X * Check to see if we were interrupted by a signal.
X * If so, service the signal(s) then continue sleeping
X * where we left off.
X */
X if (got_sighup) {
X got_sighup = 0;
X log_close();
X }
X if (got_sigchld) {
X got_sigchld = 0;
X sigchld_reaper();
X }
X t2 = time(NULL) + GMToff;
X seconds_to_wait -= (int)(t2 - t1);
X t1 = t2;
X }
X}
X
Xstatic void
Xsighup_handler(int x) {
X got_sighup = 1;
X}
X
Xstatic void
Xsigchld_handler(int x) {
X got_sigchld = 1;
X}
X
Xstatic void
Xquit(int x) {
X (void) unlink(_PATH_CRON_PID);
X _exit(0);
X}
X
Xstatic void
Xsigchld_reaper(void) {
X WAIT_T waiter;
X PID_T pid;
X
X do {
X pid = waitpid(-1, &waiter, WNOHANG);
X switch (pid) {
X case -1:
X if (errno == EINTR)
X continue;
X Debug(DPROC,
X ("[%ld] sigchld...no children\n",
X (long)getpid()))
X break;
X case 0:
X Debug(DPROC,
X ("[%ld] sigchld...no dead kids\n",
X (long)getpid()))
X break;
X default:
X Debug(DPROC,
X ("[%ld] sigchld...pid #%ld died, stat=%d\n",
X (long)getpid(), (long)pid, WEXITSTATUS(waiter)))
X break;
X }
X } while (pid > 0);
X}
X
Xstatic void
Xparse_args(int argc, char *argv[]) {
X int argch;
X
X while (-1 != (argch = getopt(argc, argv, "nx:"))) {
X switch (argch) {
X default:
X usage();
X case 'x':
X if (!set_debug_flags(optarg))
X usage();
X break;
X case 'n':
X NoFork = 1;
X break;
X }
X }
X}
END-of-cron.c
echo x - crontab.c
sed 's/^X//' >crontab.c << 'END-of-crontab.c'
X/* Copyright 1988,1990,1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X#if !defined(lint) && !defined(LINT)
Xstatic char rcsid[] = "$Id: crontab.c,v 1.12 2004/01/23 18:56:42 vixie Exp $";
X#endif
X
X/* crontab - install and manage per-user crontab files
X * vix 02may87 [RCS has the rest of the log]
X * vix 26jan87 [original]
X */
X
X#define MAIN_PROGRAM
X
X#include "cron.h"
X
X#define NHEADER_LINES 3
X
Xenum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace };
X
X#if DEBUGGING
Xstatic char *Options[] = { "???", "list", "delete", "edit", "replace" };
Xstatic char *getoptargs = "u:lerx:";
X#else
Xstatic char *getoptargs = "u:ler";
X#endif
X
Xstatic PID_T Pid;
Xstatic char User[MAX_UNAME], RealUser[MAX_UNAME];
Xstatic char Filename[MAX_FNAME], TempFilename[MAX_FNAME];
Xstatic FILE *NewCrontab;
Xstatic int CheckErrorCount;
Xstatic enum opt_t Option;
Xstatic struct passwd *pw;
Xstatic void list_cmd(void),
X delete_cmd(void),
X edit_cmd(void),
X poke_daemon(void),
X check_error(const char *),
X parse_args(int c, char *v[]),
X die(int);
Xstatic int replace_cmd(void);
X
Xstatic void
Xusage(const char *msg) {
X fprintf(stderr, "%s: usage error: %s\n", ProgramName, msg);
X fprintf(stderr, "usage:\t%s [-u user] file\n", ProgramName);
X fprintf(stderr, "\t%s [-u user] [ -e | -l | -r ]\n", ProgramName);
X fprintf(stderr, "\t\t(default operation is replace, per 1003.2)\n");
X fprintf(stderr, "\t-e\t(edit user's crontab)\n");
X fprintf(stderr, "\t-l\t(list user's crontab)\n");
X fprintf(stderr, "\t-r\t(delete user's crontab)\n");
X exit(ERROR_EXIT);
X}
X
Xint
Xmain(int argc, char *argv[]) {
X int exitstatus;
X
X Pid = getpid();
X ProgramName = argv[0];
X
X setlocale(LC_ALL, "");
X
X#if defined(BSD)
X setlinebuf(stderr);
X#endif
X parse_args(argc, argv); /* sets many globals, opens a file */
X set_cron_cwd();
X if (!allowed(RealUser, CRON_ALLOW, CRON_DENY)) {
X fprintf(stderr,
X "You (%s) are not allowed to use this program (%s)\n",
X User, ProgramName);
X fprintf(stderr, "See crontab(1) for more information\n");
X log_it(RealUser, Pid, "AUTH", "crontab command not allowed");
X exit(ERROR_EXIT);
X }
X exitstatus = OK_EXIT;
X switch (Option) {
X case opt_unknown:
X exitstatus = ERROR_EXIT;
X break;
X case opt_list:
X list_cmd();
X break;
X case opt_delete:
X delete_cmd();
X break;
X case opt_edit:
X edit_cmd();
X break;
X case opt_replace:
X if (replace_cmd() < 0)
X exitstatus = ERROR_EXIT;
X break;
X default:
X abort();
X }
X exit(exitstatus);
X /*NOTREACHED*/
X}
X
Xstatic void
Xparse_args(int argc, char *argv[]) {
X int argch;
X
X if (!(pw = getpwuid(getuid()))) {
X fprintf(stderr, "%s: your UID isn't in the passwd file.\n",
X ProgramName);
X fprintf(stderr, "bailing out.\n");
X exit(ERROR_EXIT);
X }
X if (strlen(pw->pw_name) >= sizeof User) {
X fprintf(stderr, "username too long\n");
X exit(ERROR_EXIT);
X }
X strcpy(User, pw->pw_name);
X strcpy(RealUser, User);
X Filename[0] = '\0';
X Option = opt_unknown;
X while (-1 != (argch = getopt(argc, argv, getoptargs))) {
X switch (argch) {
X#if DEBUGGING
X case 'x':
X if (!set_debug_flags(optarg))
X usage("bad debug option");
X break;
X#endif
X case 'u':
X if (MY_UID(pw) != ROOT_UID) {
X fprintf(stderr,
X "must be privileged to use -u\n");
X exit(ERROR_EXIT);
X }
X if (!(pw = getpwnam(optarg))) {
X fprintf(stderr, "%s: user `%s' unknown\n",
X ProgramName, optarg);
X exit(ERROR_EXIT);
X }
X if (strlen(optarg) >= sizeof User)
X usage("username too long");
X (void) strcpy(User, optarg);
X break;
X case 'l':
X if (Option != opt_unknown)
X usage("only one operation permitted");
X Option = opt_list;
X break;
X case 'r':
X if (Option != opt_unknown)
X usage("only one operation permitted");
X Option = opt_delete;
X break;
X case 'e':
X if (Option != opt_unknown)
X usage("only one operation permitted");
X Option = opt_edit;
X break;
X default:
X usage("unrecognized option");
X }
X }
X
X endpwent();
X
X if (Option != opt_unknown) {
X if (argv[optind] != NULL)
X usage("no arguments permitted after this option");
X } else {
X if (argv[optind] != NULL) {
X Option = opt_replace;
X if (strlen(argv[optind]) >= sizeof Filename)
X usage("filename too long");
X (void) strcpy (Filename, argv[optind]);
X } else
X usage("file name must be specified for replace");
X }
X
X if (Option == opt_replace) {
X /* we have to open the file here because we're going to
X * chdir(2) into /var/cron before we get around to
X * reading the file.
X */
X if (!strcmp(Filename, "-"))
X NewCrontab = stdin;
X else {
X /* relinquish the setuid status of the binary during
X * the open, lest nonroot users read files they should
X * not be able to read. we can't use access() here
X * since there's a race condition. thanks go out to
X * Arnt Gulbrandsen <agulbra@pvv.unit.no> for spotting
X * the race.
X */
X
X if (swap_uids() < OK) {
X perror("swapping uids");
X exit(ERROR_EXIT);
X }
X if (!(NewCrontab = fopen(Filename, "r"))) {
X perror(Filename);
X exit(ERROR_EXIT);
X }
X if (swap_uids_back() < OK) {
X perror("swapping uids back");
X exit(ERROR_EXIT);
X }
X }
X }
X
X Debug(DMISC, ("user=%s, file=%s, option=%s\n",
X User, Filename, Options[(int)Option]))
X}
X
Xstatic void
Xlist_cmd(void) {
X char n[MAX_FNAME];
X FILE *f;
X int ch;
X
X log_it(RealUser, Pid, "LIST", User);
X if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) {
X fprintf(stderr, "path too long\n");
X exit(ERROR_EXIT);
X }
X if (!(f = fopen(n, "r"))) {
X if (errno == ENOENT)
X fprintf(stderr, "no crontab for %s\n", User);
X else
X perror(n);
X exit(ERROR_EXIT);
X }
X
X /* file is open. copy to stdout, close.
X */
X Set_LineNum(1)
X while (EOF != (ch = get_char(f)))
X putchar(ch);
X fclose(f);
X}
X
Xstatic void
Xdelete_cmd(void) {
X char n[MAX_FNAME];
X
X log_it(RealUser, Pid, "DELETE", User);
X if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) {
X fprintf(stderr, "path too long\n");
X exit(ERROR_EXIT);
X }
X if (unlink(n) != 0) {
X if (errno == ENOENT)
X fprintf(stderr, "no crontab for %s\n", User);
X else
X perror(n);
X exit(ERROR_EXIT);
X }
X poke_daemon();
X}
X
Xstatic void
Xcheck_error(const char *msg) {
X CheckErrorCount++;
X fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg);
X}
X
Xstatic void
Xedit_cmd(void) {
X char n[MAX_FNAME], q[MAX_TEMPSTR], *editor;
X FILE *f;
X int ch, t, x;
X struct stat statbuf;
X struct utimbuf utimebuf;
X WAIT_T waiter;
X PID_T pid, xpid;
X
X log_it(RealUser, Pid, "BEGIN EDIT", User);
X if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) {
X fprintf(stderr, "path too long\n");
X exit(ERROR_EXIT);
X }
X if (!(f = fopen(n, "r"))) {
X if (errno != ENOENT) {
X perror(n);
X exit(ERROR_EXIT);
X }
X fprintf(stderr, "no crontab for %s - using an empty one\n",
X User);
X if (!(f = fopen(_PATH_DEVNULL, "r"))) {
X perror(_PATH_DEVNULL);
X exit(ERROR_EXIT);
X }
X }
X
X if (fstat(fileno(f), &statbuf) < 0) {
X perror("fstat");
X goto fatal;
X }
X utimebuf.actime = statbuf.st_atime;
X utimebuf.modtime = statbuf.st_mtime;
X
X /* Turn off signals. */
X (void)signal(SIGHUP, SIG_IGN);
X (void)signal(SIGINT, SIG_IGN);
X (void)signal(SIGQUIT, SIG_IGN);
X
X if (!glue_strings(Filename, sizeof Filename, _PATH_TMP,
X "crontab.XXXXXXXXXX", '/')) {
X fprintf(stderr, "path too long\n");
X goto fatal;
X }
X if (-1 == (t = mkstemp(Filename))) {
X perror(Filename);
X goto fatal;
X }
X#ifdef HAS_FCHOWN
X if (fchown(t, MY_UID(pw), MY_GID(pw)) < 0) {
X perror("fchown");
X goto fatal;
X }
X#else
X if (chown(Filename, MY_UID(pw), MY_GID(pw)) < 0) {
X perror("chown");
X goto fatal;
X }
X#endif
X if (!(NewCrontab = fdopen(t, "r+"))) {
X perror("fdopen");
X goto fatal;
X }
X
X Set_LineNum(1)
X
X /* ignore the top few comments since we probably put them there.
X */
X x = 0;
X while (EOF != (ch = get_char(f))) {
X if ('#' != ch) {
X putc(ch, NewCrontab);
X break;
X }
X while (EOF != (ch = get_char(f)))
X if (ch == '\n')
X break;
X if (++x >= NHEADER_LINES)
X break;
X }
X
X /* copy the rest of the crontab (if any) to the temp file.
X */
X if (EOF != ch)
X while (EOF != (ch = get_char(f)))
X putc(ch, NewCrontab);
X fclose(f);
X if (fflush(NewCrontab) < OK) {
X perror(Filename);
X exit(ERROR_EXIT);
X }
X utime(Filename, &utimebuf);
X again:
X rewind(NewCrontab);
X if (ferror(NewCrontab)) {
X fprintf(stderr, "%s: error while writing new crontab to %s\n",
X ProgramName, Filename);
X fatal:
X unlink(Filename);
X exit(ERROR_EXIT);
X }
X
X if (((editor = getenv("VISUAL")) == NULL || *editor == '\0') &&
X ((editor = getenv("EDITOR")) == NULL || *editor == '\0')) {
X editor = EDITOR;
X }
X
X /* we still have the file open. editors will generally rewrite the
X * original file rather than renaming/unlinking it and starting a
X * new one; even backup files are supposed to be made by copying
X * rather than by renaming. if some editor does not support this,
X * then don't use it. the security problems are more severe if we
X * close and reopen the file around the edit.
X */
X
X switch (pid = fork()) {
X case -1:
X perror("fork");
X goto fatal;
X case 0:
X /* child */
X if (setgid(MY_GID(pw)) < 0) {
X perror("setgid(getgid())");
X exit(ERROR_EXIT);
X }
X if (setuid(MY_UID(pw)) < 0) {
X perror("setuid(getuid())");
X exit(ERROR_EXIT);
X }
X if (chdir(_PATH_TMP) < 0) {
X perror(_PATH_TMP);
X exit(ERROR_EXIT);
X }
X if (!glue_strings(q, sizeof q, editor, Filename, ' ')) {
X fprintf(stderr, "%s: editor command line too long\n",
X ProgramName);
X exit(ERROR_EXIT);
X }
X execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", q, (char *)0);
X perror(editor);
X exit(ERROR_EXIT);
X /*NOTREACHED*/
X default:
X /* parent */
X break;
X }
X
X /* parent */
X for (;;) {
X xpid = waitpid(pid, &waiter, WUNTRACED);
X if (xpid == -1) {
X if (errno != EINTR)
X fprintf(stderr, "%s: waitpid() failed waiting for PID %ld from \"%s\": %s\n",
X ProgramName, (long)pid, editor, strerror(errno));
X } else if (xpid != pid) {
X fprintf(stderr, "%s: wrong PID (%ld != %ld) from \"%s\"\n",
X ProgramName, (long)xpid, (long)pid, editor);
X goto fatal;
X } else if (WIFSTOPPED(waiter)) {
X kill(getpid(), WSTOPSIG(waiter));
X } else if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) {
X fprintf(stderr, "%s: \"%s\" exited with status %d\n",
X ProgramName, editor, WEXITSTATUS(waiter));
X goto fatal;
X } else if (WIFSIGNALED(waiter)) {
X fprintf(stderr,
X "%s: \"%s\" killed; signal %d (%score dumped)\n",
X ProgramName, editor, WTERMSIG(waiter),
X WCOREDUMP(waiter) ?"" :"no ");
X goto fatal;
X } else
X break;
X }
X (void)signal(SIGHUP, SIG_DFL);
X (void)signal(SIGINT, SIG_DFL);
X (void)signal(SIGQUIT, SIG_DFL);
X if (fstat(t, &statbuf) < 0) {
X perror("fstat");
X goto fatal;
X }
X if (utimebuf.modtime == statbuf.st_mtime) {
X fprintf(stderr, "%s: no changes made to crontab\n",
X ProgramName);
X goto remove;
X }
X fprintf(stderr, "%s: installing new crontab\n", ProgramName);
X switch (replace_cmd()) {
X case 0:
X break;
X case -1:
X for (;;) {
X printf("Do you want to retry the same edit? ");
X fflush(stdout);
X q[0] = '\0';
X (void) fgets(q, sizeof q, stdin);
X switch (q[0]) {
X case 'y':
X case 'Y':
X goto again;
X case 'n':
X case 'N':
X goto abandon;
X default:
X fprintf(stderr, "Enter Y or N\n");
X }
X }
X /*NOTREACHED*/
X case -2:
X abandon:
X fprintf(stderr, "%s: edits left in %s\n",
X ProgramName, Filename);
X goto done;
X default:
X fprintf(stderr, "%s: panic: bad switch() in replace_cmd()\n",
X ProgramName);
X goto fatal;
X }
X remove:
X unlink(Filename);
X done:
X log_it(RealUser, Pid, "END EDIT", User);
X}
X
X/* returns 0 on success
X * -1 on syntax error
X * -2 on install error
X */
Xstatic int
Xreplace_cmd(void) {
X char n[MAX_FNAME], envstr[MAX_ENVSTR];
X FILE *tmp;
X int ch, eof, fd;
X int error = 0;
X entry *e;
X uid_t file_owner;
X time_t now = time(NULL);
X char **envp = env_init();
X
X if (envp == NULL) {
X fprintf(stderr, "%s: Cannot allocate memory.\n", ProgramName);
X return (-2);
X }
X
X if (!glue_strings(TempFilename, sizeof TempFilename, SPOOL_DIR,
X "tmp.XXXXXXXXXX", '/')) {
X TempFilename[0] = '\0';
X fprintf(stderr, "path too long\n");
X return (-2);
X }
X if ((fd = mkstemp(TempFilename)) == -1 || !(tmp = fdopen(fd, "w+"))) {
X perror(TempFilename);
X if (fd != -1) {
X close(fd);
X unlink(TempFilename);
X }
X TempFilename[0] = '\0';
X return (-2);
X }
X
X (void) signal(SIGHUP, die);
X (void) signal(SIGINT, die);
X (void) signal(SIGQUIT, die);
X
X /* write a signature at the top of the file.
X *
X * VERY IMPORTANT: make sure NHEADER_LINES agrees with this code.
X */
X fprintf(tmp, "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n");
X fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now));
X fprintf(tmp, "# (Cron version %s -- %s)\n", CRON_VERSION, rcsid);
X
X /* copy the crontab to the tmp
X */
X rewind(NewCrontab);
X Set_LineNum(1)
X while (EOF != (ch = get_char(NewCrontab)))
X putc(ch, tmp);
X ftruncate(fileno(tmp), ftell(tmp)); /* XXX redundant with "w+"? */
X fflush(tmp); rewind(tmp);
X
X if (ferror(tmp)) {
X fprintf(stderr, "%s: error while writing new crontab to %s\n",
X ProgramName, TempFilename);
X fclose(tmp);
X error = -2;
X goto done;
X }
X
X /* check the syntax of the file being installed.
X */
X
X /* BUG: was reporting errors after the EOF if there were any errors
X * in the file proper -- kludged it by stopping after first error.
X * vix 31mar87
X */
X Set_LineNum(1 - NHEADER_LINES)
X CheckErrorCount = 0; eof = FALSE;
X while (!CheckErrorCount && !eof) {
X switch (load_env(envstr, tmp)) {
X case ERR:
X /* check for data before the EOF */
X if (envstr[0] != '\0') {
X Set_LineNum(LineNumber + 1);
X check_error("premature EOF");
X }
X eof = TRUE;
X break;
X case FALSE:
X e = load_entry(tmp, check_error, pw, envp);
X if (e)
X free(e);
X break;
X case TRUE:
X break;
X }
X }
X
X if (CheckErrorCount != 0) {
X fprintf(stderr, "errors in crontab file, can't install.\n");
X fclose(tmp);
X error = -1;
X goto done;
X }
X
X file_owner = (getgid() == getegid()) ? ROOT_UID : pw->pw_uid;
X
X#ifdef HAS_FCHOWN
X if (fchown(fileno(tmp), file_owner, -1) < OK) {
X perror("fchown");
X fclose(tmp);
X error = -2;
X goto done;
X }
X#else
X if (chown(TempFilename, file_owner, -1) < OK) {
X perror("chown");
X fclose(tmp);
X error = -2;
X goto done;
X }
X#endif
X
X if (fclose(tmp) == EOF) {
X perror("fclose");
X error = -2;
X goto done;
X }
X
X if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) {
X fprintf(stderr, "path too long\n");
X error = -2;
X goto done;
X }
X if (rename(TempFilename, n)) {
X fprintf(stderr, "%s: error renaming %s to %s\n",
X ProgramName, TempFilename, n);
X perror("rename");
X error = -2;
X goto done;
X }
X TempFilename[0] = '\0';
X log_it(RealUser, Pid, "REPLACE", User);
X
X poke_daemon();
X
Xdone:
X (void) signal(SIGHUP, SIG_DFL);
X (void) signal(SIGINT, SIG_DFL);
X (void) signal(SIGQUIT, SIG_DFL);
X if (TempFilename[0]) {
X (void) unlink(TempFilename);
X TempFilename[0] = '\0';
X }
X return (error);
X}
X
Xstatic void
Xpoke_daemon(void) {
X if (utime(SPOOL_DIR, NULL) < OK) {
X fprintf(stderr, "crontab: can't update mtime on spooldir\n");
X perror(SPOOL_DIR);
X return;
X }
X}
X
Xstatic void
Xdie(int x) {
X if (TempFilename[0])
X (void) unlink(TempFilename);
X _exit(ERROR_EXIT);
X}
END-of-crontab.c
echo x - database.c
sed 's/^X//' >database.c << 'END-of-database.c'
X/* Copyright 1988,1990,1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X#if !defined(lint) && !defined(LINT)
Xstatic char rcsid[] = "$Id: database.c,v 1.7 2004/01/23 18:56:42 vixie Exp $";
X#endif
X
X/* vix 26jan87 [RCS has the log]
X */
X
X#include "cron.h"
X
X#define TMAX(a,b) ((a)>(b)?(a):(b))
X
Xstatic void process_crontab(const char *, const char *,
X const char *, struct stat *,
X cron_db *, cron_db *);
X
Xvoid
Xload_database(cron_db *old_db) {
X struct stat statbuf, syscron_stat;
X cron_db new_db;
X DIR_T *dp;
X DIR *dir;
X user *u, *nu;
X
X Debug(DLOAD, ("[%ld] load_database()\n", (long)getpid()))
X
X /* before we start loading any data, do a stat on SPOOL_DIR
X * so that if anything changes as of this moment (i.e., before we've
X * cached any of the database), we'll see the changes next time.
X */
X if (stat(SPOOL_DIR, &statbuf) < OK) {
X log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR);
X (void) exit(ERROR_EXIT);
X }
X
X /* track system crontab file
X */
X if (stat(SYSCRONTAB, &syscron_stat) < OK)
X syscron_stat.st_mtime = 0;
X
X /* if spooldir's mtime has not changed, we don't need to fiddle with
X * the database.
X *
X * Note that old_db->mtime is initialized to 0 in main(), and
X * so is guaranteed to be different than the stat() mtime the first
X * time this function is called.
X */
X if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime)) {
X Debug(DLOAD, ("[%ld] spool dir mtime unch, no load needed.\n",
X (long)getpid()))
X return;
X }
X
X /* something's different. make a new database, moving unchanged
X * elements from the old database, reloading elements that have
X * actually changed. Whatever is left in the old database when
X * we're done is chaff -- crontabs that disappeared.
X */
X new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime);
X new_db.head = new_db.tail = NULL;
X
X if (syscron_stat.st_mtime)
X process_crontab("root", NULL, SYSCRONTAB, &syscron_stat,
X &new_db, old_db);
X
X /* we used to keep this dir open all the time, for the sake of
X * efficiency. however, we need to close it in every fork, and
X * we fork a lot more often than the mtime of the dir changes.
X */
X if (!(dir = opendir(SPOOL_DIR))) {
X log_it("CRON", getpid(), "OPENDIR FAILED", SPOOL_DIR);
X (void) exit(ERROR_EXIT);
X }
X
X while (NULL != (dp = readdir(dir))) {
X char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1];
X
X /* avoid file names beginning with ".". this is good
X * because we would otherwise waste two guaranteed calls
X * to getpwnam() for . and .., and also because user names
X * starting with a period are just too nasty to consider.
X */
X if (dp->d_name[0] == '.')
X continue;
X
X if (strlen(dp->d_name) >= sizeof fname)
X continue; /* XXX log? */
X (void) strcpy(fname, dp->d_name);
X
X if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR,
X fname, '/'))
X continue; /* XXX log? */
X
X process_crontab(fname, fname, tabname,
X &statbuf, &new_db, old_db);
X }
X closedir(dir);
X
X /* if we don't do this, then when our children eventually call
X * getpwnam() in do_command.c's child_process to verify MAILTO=,
X * they will screw us up (and v-v).
X */
X endpwent();
X
X /* whatever's left in the old database is now junk.
X */
X Debug(DLOAD, ("unlinking old database:\n"))
X for (u = old_db->head; u != NULL; u = nu) {
X Debug(DLOAD, ("\t%s\n", u->name))
X nu = u->next;
X unlink_user(old_db, u);
X free_user(u);
X }
X
X /* overwrite the database control block with the new one.
X */
X *old_db = new_db;
X Debug(DLOAD, ("load_database is done\n"))
X}
X
Xvoid
Xlink_user(cron_db *db, user *u) {
X if (db->head == NULL)
X db->head = u;
X if (db->tail)
X db->tail->next = u;
X u->prev = db->tail;
X u->next = NULL;
X db->tail = u;
X}
X
Xvoid
Xunlink_user(cron_db *db, user *u) {
X if (u->prev == NULL)
X db->head = u->next;
X else
X u->prev->next = u->next;
X
X if (u->next == NULL)
X db->tail = u->prev;
X else
X u->next->prev = u->prev;
X}
X
Xuser *
Xfind_user(cron_db *db, const char *name) {
X user *u;
X
X for (u = db->head; u != NULL; u = u->next)
X if (strcmp(u->name, name) == 0)
X break;
X return (u);
X}
X
Xstatic void
Xprocess_crontab(const char *uname, const char *fname, const char *tabname,
X struct stat *statbuf, cron_db *new_db, cron_db *old_db)
X{
X struct passwd *pw = NULL;
X int crontab_fd = OK - 1;
X user *u;
X
X if (fname == NULL) {
X /* must be set to something for logging purposes.
X */
X fname = "*system*";
X } else if ((pw = getpwnam(uname)) == NULL) {
X /* file doesn't have a user in passwd file.
X */
X log_it(fname, getpid(), "ORPHAN", "no passwd entry");
X goto next_crontab;
X }
X
X if ((crontab_fd = open(tabname, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0)) < OK) {
X /* crontab not accessible?
X */
X log_it(fname, getpid(), "CAN'T OPEN", tabname);
X goto next_crontab;
X }
X
X if (fstat(crontab_fd, statbuf) < OK) {
X log_it(fname, getpid(), "FSTAT FAILED", tabname);
X goto next_crontab;
X }
X if (!S_ISREG(statbuf->st_mode)) {
X log_it(fname, getpid(), "NOT REGULAR", tabname);
X goto next_crontab;
X }
X if ((statbuf->st_mode & 07777) != 0600) {
X log_it(fname, getpid(), "BAD FILE MODE", tabname);
X goto next_crontab;
X }
X if (statbuf->st_uid != ROOT_UID && (pw == NULL ||
X statbuf->st_uid != pw->pw_uid || strcmp(uname, pw->pw_name) != 0)) {
X log_it(fname, getpid(), "WRONG FILE OWNER", tabname);
X goto next_crontab;
X }
X if (statbuf->st_nlink != 1) {
X log_it(fname, getpid(), "BAD LINK COUNT", tabname);
X goto next_crontab;
X }
X
X Debug(DLOAD, ("\t%s:", fname))
X u = find_user(old_db, fname);
X if (u != NULL) {
X /* if crontab has not changed since we last read it
X * in, then we can just use our existing entry.
X */
X if (u->mtime == statbuf->st_mtime) {
X Debug(DLOAD, (" [no change, using old data]"))
X unlink_user(old_db, u);
X link_user(new_db, u);
X goto next_crontab;
X }
X
X /* before we fall through to the code that will reload
X * the user, let's deallocate and unlink the user in
X * the old database. This is more a point of memory
X * efficiency than anything else, since all leftover
X * users will be deleted from the old database when
X * we finish with the crontab...
X */
X Debug(DLOAD, (" [delete old data]"))
X unlink_user(old_db, u);
X free_user(u);
X log_it(fname, getpid(), "RELOAD", tabname);
X }
X u = load_user(crontab_fd, pw, fname);
X if (u != NULL) {
X u->mtime = statbuf->st_mtime;
X link_user(new_db, u);
X }
X
X next_crontab:
X if (crontab_fd >= OK) {
X Debug(DLOAD, (" [done]\n"))
X close(crontab_fd);
X }
X}
END-of-database.c
echo x - do_command.c
sed 's/^X//' >do_command.c << 'END-of-do_command.c'
X/* Copyright 1988,1990,1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X#if !defined(lint) && !defined(LINT)
Xstatic char rcsid[] = "$Id: do_command.c,v 1.9 2004/01/23 18:56:42 vixie Exp $";
X#endif
X
X#include "cron.h"
X
Xstatic void child_process(entry *, user *);
Xstatic int safe_p(const char *, const char *);
X
Xvoid
Xdo_command(entry *e, user *u) {
X Debug(DPROC, ("[%ld] do_command(%s, (%s,%ld,%ld))\n",
X (long)getpid(), e->cmd, u->name,
X (long)e->pwd->pw_uid, (long)e->pwd->pw_gid))
X
X /* fork to become asynchronous -- parent process is done immediately,
X * and continues to run the normal cron code, which means return to
X * tick(). the child and grandchild don't leave this function, alive.
X *
X * vfork() is unsuitable, since we have much to do, and the parent
X * needs to be able to run off and fork other processes.
X */
X switch (fork()) {
X case -1:
X log_it("CRON", getpid(), "error", "can't fork");
X break;
X case 0:
X /* child process */
X acquire_daemonlock(1);
X child_process(e, u);
X Debug(DPROC, ("[%ld] child process done, exiting\n",
X (long)getpid()))
X _exit(OK_EXIT);
X break;
X default:
X /* parent process */
X break;
X }
X Debug(DPROC, ("[%ld] main process returning to work\n",(long)getpid()))
X}
X
Xstatic void
Xchild_process(entry *e, user *u) {
X int stdin_pipe[2], stdout_pipe[2];
X char *input_data, *usernm, *mailto;
X int children = 0;
X
X Debug(DPROC, ("[%ld] child_process('%s')\n", (long)getpid(), e->cmd))
X
X#ifdef CAPITALIZE_FOR_PS
X /* mark ourselves as different to PS command watchers by upshifting
X * our program name. This has no effect on some kernels.
X */
X /*local*/{
X char *pch;
X
X for (pch = ProgramName; *pch; pch++)
X *pch = MkUpper(*pch);
X }
X#endif /* CAPITALIZE_FOR_PS */
X
X /* discover some useful and important environment settings
X */
X usernm = e->pwd->pw_name;
X mailto = env_get("MAILTO", e->envp);
X
X /* our parent is watching for our death by catching SIGCHLD. we
X * do not care to watch for our children's deaths this way -- we
X * use wait() explicitly. so we have to reset the signal (which
X * was inherited from the parent).
X */
X (void) signal(SIGCHLD, SIG_DFL);
X
X /* create some pipes to talk to our future child
X */
X pipe(stdin_pipe); /* child's stdin */
X pipe(stdout_pipe); /* child's stdout */
X
X /* since we are a forked process, we can diddle the command string
X * we were passed -- nobody else is going to use it again, right?
X *
X * if a % is present in the command, previous characters are the
X * command, and subsequent characters are the additional input to
X * the command. An escaped % will have the escape character stripped
X * from it. Subsequent %'s will be transformed into newlines,
X * but that happens later.
X */
X /*local*/{
X int escaped = FALSE;
X int ch;
X char *p;
X
X for (input_data = p = e->cmd;
X (ch = *input_data) != '\0';
X input_data++, p++) {
X if (p != input_data)
X *p = ch;
X if (escaped) {
X if (ch == '%')
X *--p = ch;
X escaped = FALSE;
X continue;
X }
X if (ch == '\\') {
X escaped = TRUE;
X continue;
X }
X if (ch == '%') {
X *input_data++ = '\0';
X break;
X }
X }
X *p = '\0';
X }
X
X /* fork again, this time so we can exec the user's command.
X */
X switch (vfork()) {
X case -1:
X log_it("CRON", getpid(), "error", "can't vfork");
X exit(ERROR_EXIT);
X /*NOTREACHED*/
X case 0:
X Debug(DPROC, ("[%ld] grandchild process vfork()'ed\n",
X (long)getpid()))
X
X /* write a log message. we've waited this long to do it
X * because it was not until now that we knew the PID that
X * the actual user command shell was going to get and the
X * PID is part of the log message.
X */
X if ((e->flags & DONT_LOG) == 0) {
X char *x = mkprints((u_char *)e->cmd, strlen(e->cmd));
X
X log_it(usernm, getpid(), "CMD", x);
X free(x);
X }
X
X /* that's the last thing we'll log. close the log files.
X */
X log_close();
X
X /* get new pgrp, void tty, etc.
X */
X (void) setsid();
X
X /* close the pipe ends that we won't use. this doesn't affect
X * the parent, who has to read and write them; it keeps the
X * kernel from recording us as a potential client TWICE --
X * which would keep it from sending SIGPIPE in otherwise
X * appropriate circumstances.
X */
X close(stdin_pipe[WRITE_PIPE]);
X close(stdout_pipe[READ_PIPE]);
X
X /* grandchild process. make std{in,out} be the ends of
X * pipes opened by our daddy; make stderr go to stdout.
X */
X if (stdin_pipe[READ_PIPE] != STDIN) {
X dup2(stdin_pipe[READ_PIPE], STDIN);
X close(stdin_pipe[READ_PIPE]);
X }
X if (stdout_pipe[WRITE_PIPE] != STDOUT) {
X dup2(stdout_pipe[WRITE_PIPE], STDOUT);
X close(stdout_pipe[WRITE_PIPE]);
X }
X dup2(STDOUT, STDERR);
X
X /* set our directory, uid and gid. Set gid first, since once
X * we set uid, we've lost root privledges.
X */
X#ifdef LOGIN_CAP
X {
X#ifdef BSD_AUTH
X auth_session_t *as;
X#endif
X login_cap_t *lc;
X char **p;
X extern char **environ;
X
X if ((lc = login_getclass(e->pwd->pw_class)) == NULL) {
X fprintf(stderr,
X "unable to get login class for %s\n",
X e->pwd->pw_name);
X _exit(ERROR_EXIT);
X }
X if (setusercontext(lc, e->pwd, e->pwd->pw_uid, LOGIN_SETALL) < 0) {
X fprintf(stderr,
X "setusercontext failed for %s\n",
X e->pwd->pw_name);
X _exit(ERROR_EXIT);
X }
X#ifdef BSD_AUTH
X as = auth_open();
X if (as == NULL || auth_setpwd(as, e->pwd) != 0) {
X fprintf(stderr, "can't malloc\n");
X _exit(ERROR_EXIT);
X }
X if (auth_approval(as, lc, usernm, "cron") <= 0) {
X fprintf(stderr, "approval failed for %s\n",
X e->pwd->pw_name);
X _exit(ERROR_EXIT);
X }
X auth_close(as);
X#endif /* BSD_AUTH */
X login_close(lc);
X
X /* If no PATH specified in crontab file but
X * we just added one via login.conf, add it to
X * the crontab environment.
X */
X if (env_get("PATH", e->envp) == NULL && environ != NULL) {
X for (p = environ; *p; p++) {
X if (strncmp(*p, "PATH=", 5) == 0) {
X e->envp = env_set(e->envp, *p);
X break;
X }
X }
X }
X }
X#else
X setgid(e->pwd->pw_gid);
X initgroups(usernm, e->pwd->pw_gid);
X#if (defined(BSD)) && (BSD >= 199103)
X setlogin(usernm);
X#endif /* BSD */
X setuid(e->pwd->pw_uid); /* we aren't root after this... */
X
X#endif /* LOGIN_CAP */
X chdir(env_get("HOME", e->envp));
X
X /*
X * Exec the command.
X */
X {
X char *shell = env_get("SHELL", e->envp);
X
X# if DEBUGGING
X if (DebugFlags & DTEST) {
X fprintf(stderr,
X "debug DTEST is on, not exec'ing command.\n");
X fprintf(stderr,
X "\tcmd='%s' shell='%s'\n", e->cmd, shell);
X _exit(OK_EXIT);
X }
X# endif /*DEBUGGING*/
X execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);
X fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
X perror("execl");
X _exit(ERROR_EXIT);
X }
X break;
X default:
X /* parent process */
X break;
X }
X
X children++;
X
X /* middle process, child of original cron, parent of process running
X * the user's command.
X */
X
X Debug(DPROC, ("[%ld] child continues, closing pipes\n",(long)getpid()))
X
X /* close the ends of the pipe that will only be referenced in the
X * grandchild process...
X */
X close(stdin_pipe[READ_PIPE]);
X close(stdout_pipe[WRITE_PIPE]);
X
X /*
X * write, to the pipe connected to child's stdin, any input specified
X * after a % in the crontab entry. while we copy, convert any
X * additional %'s to newlines. when done, if some characters were
X * written and the last one wasn't a newline, write a newline.
X *
X * Note that if the input data won't fit into one pipe buffer (2K
X * or 4K on most BSD systems), and the child doesn't read its stdin,
X * we would block here. thus we must fork again.
X */
X
X if (*input_data && fork() == 0) {
X FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
X int need_newline = FALSE;
X int escaped = FALSE;
X int ch;
X
X Debug(DPROC, ("[%ld] child2 sending data to grandchild\n",
X (long)getpid()))
X
X /* close the pipe we don't use, since we inherited it and
X * are part of its reference count now.
X */
X close(stdout_pipe[READ_PIPE]);
X
X /* translation:
X * \% -> %
X * % -> \n
X * \x -> \x for all x != %
X */
X while ((ch = *input_data++) != '\0') {
X if (escaped) {
X if (ch != '%')
X putc('\\', out);
X } else {
X if (ch == '%')
X ch = '\n';
X }
X
X if (!(escaped = (ch == '\\'))) {
X putc(ch, out);
X need_newline = (ch != '\n');
X }
X }
X if (escaped)
X putc('\\', out);
X if (need_newline)
X putc('\n', out);
X
X /* close the pipe, causing an EOF condition. fclose causes
X * stdin_pipe[WRITE_PIPE] to be closed, too.
X */
X fclose(out);
X
X Debug(DPROC, ("[%ld] child2 done sending to grandchild\n",
X (long)getpid()))
X exit(0);
X }
X
X /* close the pipe to the grandkiddie's stdin, since its wicked uncle
X * ernie back there has it open and will close it when he's done.
X */
X close(stdin_pipe[WRITE_PIPE]);
X
X children++;
X
X /*
X * read output from the grandchild. it's stderr has been redirected to
X * it's stdout, which has been redirected to our pipe. if there is any
X * output, we'll be mailing it to the user whose crontab this is...
X * when the grandchild exits, we'll get EOF.
X */
X
X Debug(DPROC, ("[%ld] child reading output from grandchild\n",
X (long)getpid()))
X
X /*local*/{
X FILE *in = fdopen(stdout_pipe[READ_PIPE], "r");
X int ch = getc(in);
X
X if (ch != EOF) {
X FILE *mail;
X int bytes = 1;
X int status = 0;
X
X Debug(DPROC|DEXT,
X ("[%ld] got data (%x:%c) from grandchild\n",
X (long)getpid(), ch, ch))
X
X /* get name of recipient. this is MAILTO if set to a
X * valid local username; USER otherwise.
X */
X if (mailto) {
X /* MAILTO was present in the environment
X */
X if (!*mailto) {
X /* ... but it's empty. set to NULL
X */
X mailto = NULL;
X }
X } else {
X /* MAILTO not present, set to USER.
X */
X mailto = usernm;
X }
X
X /* if we are supposed to be mailing, MAILTO will
X * be non-NULL. only in this case should we set
X * up the mail command and subjects and stuff...
X */
X
X if (mailto && safe_p(usernm, mailto)) {
X char **env;
X char mailcmd[MAX_COMMAND];
X char hostname[MAXHOSTNAMELEN];
X
X gethostname(hostname, MAXHOSTNAMELEN);
X if (strlens(MAILFMT, MAILARG, NULL) + 1
X >= sizeof mailcmd) {
X fprintf(stderr, "mailcmd too long\n");
X (void) _exit(ERROR_EXIT);
X }
X (void)sprintf(mailcmd, MAILFMT, MAILARG);
X if (!(mail = cron_popen(mailcmd, "w", e->pwd))) {
X perror(mailcmd);
X (void) _exit(ERROR_EXIT);
X }
X fprintf(mail, "From: root (Cron Daemon)\n");
X fprintf(mail, "To: %s\n", mailto);
X fprintf(mail, "Subject: Cron <%s@%s> %s\n",
X usernm, first_word(hostname, "."),
X e->cmd);
X#ifdef MAIL_DATE
X fprintf(mail, "Date: %s\n",
X arpadate(&StartTime));
X#endif /*MAIL_DATE*/
X for (env = e->envp; *env; env++)
X fprintf(mail, "X-Cron-Env: <%s>\n",
X *env);
X fprintf(mail, "\n");
X
X /* this was the first char from the pipe
X */
X putc(ch, mail);
X }
X
X /* we have to read the input pipe no matter whether
X * we mail or not, but obviously we only write to
X * mail pipe if we ARE mailing.
X */
X
X while (EOF != (ch = getc(in))) {
X bytes++;
X if (mailto)
X putc(ch, mail);
X }
X
X /* only close pipe if we opened it -- i.e., we're
X * mailing...
X */
X
X if (mailto) {
X Debug(DPROC, ("[%ld] closing pipe to mail\n",
X (long)getpid()))
X /* Note: the pclose will probably see
X * the termination of the grandchild
X * in addition to the mail process, since
X * it (the grandchild) is likely to exit
X * after closing its stdout.
X */
X status = cron_pclose(mail);
X }
X
X /* if there was output and we could not mail it,
X * log the facts so the poor user can figure out
X * what's going on.
X */
X if (mailto && status) {
X char buf[MAX_TEMPSTR];
X
X sprintf(buf,
X "mailed %d byte%s of output but got status 0x%04x\n",
X bytes, (bytes==1)?"":"s",
X status);
X log_it(usernm, getpid(), "MAIL", buf);
X }
X
X } /*if data from grandchild*/
X
X Debug(DPROC, ("[%ld] got EOF from grandchild\n",
X (long)getpid()))
X
X fclose(in); /* also closes stdout_pipe[READ_PIPE] */
X }
X
X /* wait for children to die.
X */
X for (; children > 0; children--) {
X WAIT_T waiter;
X PID_T pid;
X
X Debug(DPROC, ("[%ld] waiting for grandchild #%d to finish\n",
X (long)getpid(), children))
X while ((pid = wait(&waiter)) < OK && errno == EINTR)
X ;
X if (pid < OK) {
X Debug(DPROC,
X ("[%ld] no more grandchildren--mail written?\n",
X (long)getpid()))
X break;
X }
X Debug(DPROC, ("[%ld] grandchild #%ld finished, status=%04x",
X (long)getpid(), (long)pid, WEXITSTATUS(waiter)))
X if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
X Debug(DPROC, (", dumped core"))
X Debug(DPROC, ("\n"))
X }
X}
X
Xstatic int
Xsafe_p(const char *usernm, const char *s) {
X static const char safe_delim[] = "@!:%-.,"; /* conservative! */
X const char *t;
X int ch, first;
X
X for (t = s, first = 1; (ch = *t++) != '\0'; first = 0) {
X if (isascii(ch) && isprint(ch) &&
X (isalnum(ch) || (!first && strchr(safe_delim, ch))))
X continue;
X log_it(usernm, getpid(), "UNSAFE", s);
X return (FALSE);
X }
X return (TRUE);
X}
END-of-do_command.c
echo x - entry.c
sed 's/^X//' >entry.c << 'END-of-entry.c'
X/*
X * Copyright 1988,1990,1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X#if !defined(lint) && !defined(LINT)
Xstatic char rcsid[] = "$Id: entry.c,v 1.17 2004/01/23 18:56:42 vixie Exp $";
X#endif
X
X/* vix 26jan87 [RCS'd; rest of log is in RCS file]
X * vix 01jan87 [added line-level error recovery]
X * vix 31dec86 [added /step to the from-to range, per bob@acornrc]
X * vix 30dec86 [written]
X */
X
X#include "cron.h"
X
Xtypedef enum ecode {
X e_none, e_minute, e_hour, e_dom, e_month, e_dow,
X e_cmd, e_timespec, e_username, e_option, e_memory
X} ecode_e;
X
Xstatic const char *ecodes[] =
X {
X "no error",
X "bad minute",
X "bad hour",
X "bad day-of-month",
X "bad month",
X "bad day-of-week",
X "bad command",
X "bad time specifier",
X "bad username",
X "bad option",
X "out of memory"
X };
X
Xstatic int get_list(bitstr_t *, int, int, const char *[], int, FILE *),
X get_range(bitstr_t *, int, int, const char *[], int, FILE *),
X get_number(int *, int, const char *[], int, FILE *, const char *),
X set_element(bitstr_t *, int, int, int);
X
Xvoid
Xfree_entry(entry *e) {
X free(e->cmd);
X free(e->pwd);
X env_free(e->envp);
X free(e);
X}
X
X/* return NULL if eof or syntax error occurs;
X * otherwise return a pointer to a new entry.
X */
Xentry *
Xload_entry(FILE *file, void (*error_func)(), struct passwd *pw, char **envp) {
X /* this function reads one crontab entry -- the next -- from a file.
X * it skips any leading blank lines, ignores comments, and returns
X * NULL if for any reason the entry can't be read and parsed.
X *
X * the entry is also parsed here.
X *
X * syntax:
X * user crontab:
X * minutes hours doms months dows cmd\n
X * system crontab (/etc/crontab):
X * minutes hours doms months dows USERNAME cmd\n
X */
X
X ecode_e ecode = e_none;
X entry *e;
X int ch;
X char cmd[MAX_COMMAND];
X char envstr[MAX_ENVSTR];
X char **tenvp;
X
X Debug(DPARS, ("load_entry()...about to eat comments\n"))
X
X skip_comments(file);
X
X ch = get_char(file);
X if (ch == EOF)
X return (NULL);
X
X /* ch is now the first useful character of a useful line.
X * it may be an @special or it may be the first character
X * of a list of minutes.
X */
X
X e = (entry *) calloc(sizeof(entry), sizeof(char));
X
X if (ch == '@') {
X /* all of these should be flagged and load-limited; i.e.,
X * instead of @hourly meaning "0 * * * *" it should mean
X * "close to the front of every hour but not 'til the
X * system load is low". Problems are: how do you know
X * what "low" means? (save me from /etc/cron.conf!) and:
X * how to guarantee low variance (how low is low?), which
X * means how to we run roughly every hour -- seems like
X * we need to keep a history or let the first hour set
X * the schedule, which means we aren't load-limited
X * anymore. too much for my overloaded brain. (vix, jan90)
X * HINT
X */
X ch = get_string(cmd, MAX_COMMAND, file, " \t\n");
X if (!strcmp("reboot", cmd)) {
X e->flags |= WHEN_REBOOT;
X } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
X bit_set(e->minute, 0);
X bit_set(e->hour, 0);
X bit_set(e->dom, 0);
X bit_set(e->month, 0);
X bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
X e->flags |= DOW_STAR;
X } else if (!strcmp("monthly", cmd)) {
X bit_set(e->minute, 0);
X bit_set(e->hour, 0);
X bit_set(e->dom, 0);
X bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
X bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
X e->flags |= DOW_STAR;
X } else if (!strcmp("weekly", cmd)) {
X bit_set(e->minute, 0);
X bit_set(e->hour, 0);
X bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
X bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
X bit_set(e->dow, 0);
X e->flags |= DOW_STAR;
X } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
X bit_set(e->minute, 0);
X bit_set(e->hour, 0);
X bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
X bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
X bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
X } else if (!strcmp("hourly", cmd)) {
X bit_set(e->minute, 0);
X bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1));
X bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
X bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
X bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
X e->flags |= HR_STAR;
X } else {
X ecode = e_timespec;
X goto eof;
X }
X /* Advance past whitespace between shortcut and
X * username/command.
X */
X Skip_Blanks(ch, file);
X if (ch == EOF || ch == '\n') {
X ecode = e_cmd;
X goto eof;
X }
X } else {
X Debug(DPARS, ("load_entry()...about to parse numerics\n"))
X
X if (ch == '*')
X e->flags |= MIN_STAR;
X ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
X PPC_NULL, ch, file);
X if (ch == EOF) {
X ecode = e_minute;
X goto eof;
X }
X
X /* hours
X */
X
X if (ch == '*')
X e->flags |= HR_STAR;
X ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR,
X PPC_NULL, ch, file);
X if (ch == EOF) {
X ecode = e_hour;
X goto eof;
X }
X
X /* DOM (days of month)
X */
X
X if (ch == '*')
X e->flags |= DOM_STAR;
X ch = get_list(e->dom, FIRST_DOM, LAST_DOM,
X PPC_NULL, ch, file);
X if (ch == EOF) {
X ecode = e_dom;
X goto eof;
X }
X
X /* month
X */
X
X ch = get_list(e->month, FIRST_MONTH, LAST_MONTH,
X MonthNames, ch, file);
X if (ch == EOF) {
X ecode = e_month;
X goto eof;
X }
X
X /* DOW (days of week)
X */
X
X if (ch == '*')
X e->flags |= DOW_STAR;
X ch = get_list(e->dow, FIRST_DOW, LAST_DOW,
X DowNames, ch, file);
X if (ch == EOF) {
X ecode = e_dow;
X goto eof;
X }
X }
X
X /* make sundays equivalent */
X if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) {
X bit_set(e->dow, 0);
X bit_set(e->dow, 7);
X }
X
X /* check for permature EOL and catch a common typo */
X if (ch == '\n' || ch == '*') {
X ecode = e_cmd;
X goto eof;
X }
X
X /* ch is the first character of a command, or a username */
X unget_char(ch, file);
X
X if (!pw) {
X char *username = cmd; /* temp buffer */
X
X Debug(DPARS, ("load_entry()...about to parse username\n"))
X ch = get_string(username, MAX_COMMAND, file, " \t\n");
X
X Debug(DPARS, ("load_entry()...got %s\n",username))
X if (ch == EOF || ch == '\n' || ch == '*') {
X ecode = e_cmd;
X goto eof;
X }
X
X pw = getpwnam(username);
X if (pw == NULL) {
X ecode = e_username;
X goto eof;
X }
X Debug(DPARS, ("load_entry()...uid %ld, gid %ld\n",
X (long)pw->pw_uid, (long)pw->pw_gid))
X }
X
X if ((e->pwd = pw_dup(pw)) == NULL) {
X ecode = e_memory;
X goto eof;
X }
X bzero(e->pwd->pw_passwd, strlen(e->pwd->pw_passwd));
X
X /* copy and fix up environment. some variables are just defaults and
X * others are overrides.
X */
X if ((e->envp = env_copy(envp)) == NULL) {
X ecode = e_memory;
X goto eof;
X }
X if (!env_get("SHELL", e->envp)) {
X if (glue_strings(envstr, sizeof envstr, "SHELL",
X _PATH_BSHELL, '=')) {
X if ((tenvp = env_set(e->envp, envstr)) == NULL) {
X ecode = e_memory;
X goto eof;
X }
X e->envp = tenvp;
X } else
X log_it("CRON", getpid(), "error", "can't set SHELL");
X }
X if (!env_get("HOME", e->envp)) {
X if (glue_strings(envstr, sizeof envstr, "HOME",
X pw->pw_dir, '=')) {
X if ((tenvp = env_set(e->envp, envstr)) == NULL) {
X ecode = e_memory;
X goto eof;
X }
X e->envp = tenvp;
X } else
X log_it("CRON", getpid(), "error", "can't set HOME");
X }
X#ifndef LOGIN_CAP
X /* If login.conf is in used we will get the default PATH later. */
X if (!env_get("PATH", e->envp)) {
X if (glue_strings(envstr, sizeof envstr, "PATH",
X _PATH_DEFPATH, '=')) {
X if ((tenvp = env_set(e->envp, envstr)) == NULL) {
X ecode = e_memory;
X goto eof;
X }
X e->envp = tenvp;
X } else
X log_it("CRON", getpid(), "error", "can't set PATH");
X }
X#endif /* LOGIN_CAP */
X if (glue_strings(envstr, sizeof envstr, "LOGNAME",
X pw->pw_name, '=')) {
X if ((tenvp = env_set(e->envp, envstr)) == NULL) {
X ecode = e_memory;
X goto eof;
X }
X e->envp = tenvp;
X } else
X log_it("CRON", getpid(), "error", "can't set LOGNAME");
X#if defined(BSD) || defined(__linux)
X if (glue_strings(envstr, sizeof envstr, "USER",
X pw->pw_name, '=')) {
X if ((tenvp = env_set(e->envp, envstr)) == NULL) {
X ecode = e_memory;
X goto eof;
X }
X e->envp = tenvp;
X } else
X log_it("CRON", getpid(), "error", "can't set USER");
X#endif
X
X Debug(DPARS, ("load_entry()...about to parse command\n"))
X
X /* If the first character of the command is '-' it is a cron option.
X */
X while ((ch = get_char(file)) == '-') {
X switch (ch = get_char(file)) {
X case 'q':
X e->flags |= DONT_LOG;
X Skip_Nonblanks(ch, file)
X break;
X default:
X ecode = e_option;
X goto eof;
X }
X Skip_Blanks(ch, file)
X if (ch == EOF || ch == '\n') {
X ecode = e_cmd;
X goto eof;
X }
X }
X unget_char(ch, file);
X
X /* Everything up to the next \n or EOF is part of the command...
X * too bad we don't know in advance how long it will be, since we
X * need to malloc a string for it... so, we limit it to MAX_COMMAND.
X */
X ch = get_string(cmd, MAX_COMMAND, file, "\n");
X
X /* a file without a \n before the EOF is rude, so we'll complain...
X */
X if (ch == EOF) {
X ecode = e_cmd;
X goto eof;
X }
X
X /* got the command in the 'cmd' string; save it in *e.
X */
X if ((e->cmd = strdup(cmd)) == NULL) {
X ecode = e_memory;
X goto eof;
X }
X
X Debug(DPARS, ("load_entry()...returning successfully\n"))
X
X /* success, fini, return pointer to the entry we just created...
X */
X return (e);
X
X eof:
X if (e->envp)
X env_free(e->envp);
X if (e->pwd)
X free(e->pwd);
X if (e->cmd)
X free(e->cmd);
X free(e);
X while (ch != '\n' && !feof(file))
X ch = get_char(file);
X if (ecode != e_none && error_func)
X (*error_func)(ecodes[(int)ecode]);
X return (NULL);
X}
X
Xstatic int
Xget_list(bitstr_t *bits, int low, int high, const char *names[],
X int ch, FILE *file)
X{
X int done;
X
X /* we know that we point to a non-blank character here;
X * must do a Skip_Blanks before we exit, so that the
X * next call (or the code that picks up the cmd) can
X * assume the same thing.
X */
X
X Debug(DPARS|DEXT, ("get_list()...entered\n"))
X
X /* list = range {"," range}
X */
X
X /* clear the bit string, since the default is 'off'.
X */
X bit_nclear(bits, 0, (high-low+1));
X
X /* process all ranges
X */
X done = FALSE;
X while (!done) {
X if (EOF == (ch = get_range(bits, low, high, names, ch, file)))
X return (EOF);
X if (ch == ',')
X ch = get_char(file);
X else
X done = TRUE;
X }
X
X /* exiting. skip to some blanks, then skip over the blanks.
X */
X Skip_Nonblanks(ch, file)
X Skip_Blanks(ch, file)
X
X Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch))
X
X return (ch);
X}
X
X
Xstatic int
Xget_range(bitstr_t *bits, int low, int high, const char *names[],
X int ch, FILE *file)
X{
X /* range = number | number "-" number [ "/" number ]
X */
X
X int i, num1, num2, num3;
X
X Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n"))
X
X if (ch == '*') {
X /* '*' means "first-last" but can still be modified by /step
X */
X num1 = low;
X num2 = high;
X ch = get_char(file);
X if (ch == EOF)
X return (EOF);
X } else {
X ch = get_number(&num1, low, names, ch, file, ",- \t\n");
X if (ch == EOF)
X return (EOF);
X
X if (ch != '-') {
X /* not a range, it's a single number.
X */
X if (EOF == set_element(bits, low, high, num1)) {
X unget_char(ch, file);
X return (EOF);
X }
X return (ch);
X } else {
X /* eat the dash
X */
X ch = get_char(file);
X if (ch == EOF)
X return (EOF);
X
X /* get the number following the dash
X */
X ch = get_number(&num2, low, names, ch, file, "/, \t\n");
X if (ch == EOF || num1 > num2)
X return (EOF);
X }
X }
X
X /* check for step size
X */
X if (ch == '/') {
X /* eat the slash
X */
X ch = get_char(file);
X if (ch == EOF)
X return (EOF);
X
X /* get the step size -- note: we don't pass the
X * names here, because the number is not an
X * element id, it's a step size. 'low' is
X * sent as a 0 since there is no offset either.
X */
X ch = get_number(&num3, 0, PPC_NULL, ch, file, ", \t\n");
X if (ch == EOF || num3 == 0)
X return (EOF);
X } else {
X /* no step. default==1.
X */
X num3 = 1;
X }
X
X /* range. set all elements from num1 to num2, stepping
X * by num3. (the step is a downward-compatible extension
X * proposed conceptually by bob@acornrc, syntactically
X * designed then implemented by paul vixie).
X */
X for (i = num1; i <= num2; i += num3)
X if (EOF == set_element(bits, low, high, i)) {
X unget_char(ch, file);
X return (EOF);
X }
X
X return (ch);
X}
X
Xstatic int
Xget_number(int *numptr, int low, const char *names[], int ch, FILE *file,
X const char *terms) {
X char temp[MAX_TEMPSTR], *pc;
X int len, i;
X
X pc = temp;
X len = 0;
X
X /* first look for a number */
X while (isdigit((unsigned char)ch)) {
X if (++len >= MAX_TEMPSTR)
X goto bad;
X *pc++ = ch;
X ch = get_char(file);
X }
X *pc = '\0';
X if (len != 0) {
X /* got a number, check for valid terminator */
X if (!strchr(terms, ch))
X goto bad;
X *numptr = atoi(temp);
X return (ch);
X }
X
X /* no numbers, look for a string if we have any */
X if (names) {
X while (isalpha((unsigned char)ch)) {
X if (++len >= MAX_TEMPSTR)
X goto bad;
X *pc++ = ch;
X ch = get_char(file);
X }
X *pc = '\0';
X if (len != 0 && strchr(terms, ch)) {
X for (i = 0; names[i] != NULL; i++) {
X Debug(DPARS|DEXT,
X ("get_num, compare(%s,%s)\n", names[i],
X temp))
X if (!strcasecmp(names[i], temp)) {
X *numptr = i+low;
X return (ch);
X }
X }
X }
X }
X
Xbad:
X unget_char(ch, file);
X return (EOF);
X}
X
Xstatic int
Xset_element(bitstr_t *bits, int low, int high, int number) {
X Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number))
X
X if (number < low || number > high)
X return (EOF);
X
X bit_set(bits, (number-low));
X return (OK);
X}
END-of-entry.c
echo x - env.c
sed 's/^X//' >env.c << 'END-of-env.c'
X/* Copyright 1988,1990,1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X#if !defined(lint) && !defined(LINT)
Xstatic char rcsid[] = "$Id: env.c,v 1.10 2004/01/23 18:56:42 vixie Exp $";
X#endif
X
X#include "cron.h"
X
Xchar **
Xenv_init(void) {
X char **p = (char **) malloc(sizeof(char **));
X
X if (p != NULL)
X p[0] = NULL;
X return (p);
X}
X
Xvoid
Xenv_free(char **envp) {
X char **p;
X
X for (p = envp; *p != NULL; p++)
X free(*p);
X free(envp);
X}
X
Xchar **
Xenv_copy(char **envp) {
X int count, i, save_errno;
X char **p;
X
X for (count = 0; envp[count] != NULL; count++)
X NULL;
X p = (char **) malloc((count+1) * sizeof(char *)); /* 1 for the NULL */
X if (p != NULL) {
X for (i = 0; i < count; i++)
X if ((p[i] = strdup(envp[i])) == NULL) {
X save_errno = errno;
X while (--i >= 0)
X free(p[i]);
X free(p);
X errno = save_errno;
X return (NULL);
X }
X p[count] = NULL;
X }
X return (p);
X}
X
Xchar **
Xenv_set(char **envp, char *envstr) {
X int count, found;
X char **p, *envtmp;
X
X /*
X * count the number of elements, including the null pointer;
X * also set 'found' to -1 or index of entry if already in here.
X */
X found = -1;
X for (count = 0; envp[count] != NULL; count++) {
X if (!strcmp_until(envp[count], envstr, '='))
X found = count;
X }
X count++; /* for the NULL */
X
X if (found != -1) {
X /*
X * it exists already, so just free the existing setting,
X * save our new one there, and return the existing array.
X */
X if ((envtmp = strdup(envstr)) == NULL)
X return (NULL);
X free(envp[found]);
X envp[found] = envtmp;
X return (envp);
X }
X
X /*
X * it doesn't exist yet, so resize the array, move null pointer over
X * one, save our string over the old null pointer, and return resized
X * array.
X */
X if ((envtmp = strdup(envstr)) == NULL)
X return (NULL);
X p = (char **) realloc((void *) envp,
X (size_t) ((count+1) * sizeof(char **)));
X if (p == NULL) {
X free(envtmp);
X return (NULL);
X }
X p[count] = p[count-1];
X p[count-1] = envtmp;
X return (p);
X}
X
X/* The following states are used by load_env(), traversed in order: */
Xenum env_state {
X NAMEI, /* First char of NAME, may be quote */
X NAME, /* Subsequent chars of NAME */
X EQ1, /* After end of name, looking for '=' sign */
X EQ2, /* After '=', skipping whitespace */
X VALUEI, /* First char of VALUE, may be quote */
X VALUE, /* Subsequent chars of VALUE */
X FINI, /* All done, skipping trailing whitespace */
X ERROR, /* Error */
X};
X
X/* return ERR = end of file
X * FALSE = not an env setting (file was repositioned)
X * TRUE = was an env setting
X */
Xint
Xload_env(char *envstr, FILE *f) {
X long filepos;
X int fileline;
X enum env_state state;
X char name[MAX_ENVSTR], val[MAX_ENVSTR];
X char quotechar, *c, *str;
X
X filepos = ftell(f);
X fileline = LineNumber;
X skip_comments(f);
X if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n"))
X return (ERR);
X
X Debug(DPARS, ("load_env, read <%s>\n", envstr))
X
X bzero(name, sizeof name);
X bzero(val, sizeof val);
X str = name;
X state = NAMEI;
X quotechar = '\0';
X c = envstr;
X while (state != ERROR && *c) {
X switch (state) {
X case NAMEI:
X case VALUEI:
X if (*c == '\'' || *c == '"')
X quotechar = *c++;
X state++;
X /* FALLTHROUGH */
X case NAME:
X case VALUE:
X if (quotechar) {
X if (*c == quotechar) {
X state++;
X c++;
X break;
X }
X if (state == NAME && *c == '=') {
X state = ERROR;
X break;
X }
X } else {
X if (state == NAME) {
X if (isspace((unsigned char)*c)) {
X c++;
X state++;
X break;
X }
X if (*c == '=') {
X state++;
X break;
X }
X }
X }
X *str++ = *c++;
X break;
X
X case EQ1:
X if (*c == '=') {
X state++;
X str = val;
X quotechar = '\0';
X } else {
X if (!isspace((unsigned char)*c))
X state = ERROR;
X }
X c++;
X break;
X
X case EQ2:
X case FINI:
X if (isspace((unsigned char)*c))
X c++;
X else
X state++;
X break;
X
X default:
X abort();
X }
X }
X if (state != FINI && !(state == VALUE && !quotechar)) {
X Debug(DPARS, ("load_env, not an env var, state = %d\n", state))
X fseek(f, filepos, 0);
X Set_LineNum(fileline);
X return (FALSE);
X }
X if (state == VALUE) {
X /* End of unquoted value: trim trailing whitespace */
X c = val + strlen(val);
X while (c > val && isspace((unsigned char)c[-1]))
X *(--c) = '\0';
X }
X
X /* 2 fields from parser; looks like an env setting */
X
X /*
X * This can't overflow because get_string() limited the size of the
X * name and val fields. Still, it doesn't hurt to be careful...
X */
X if (!glue_strings(envstr, MAX_ENVSTR, name, val, '='))
X return (FALSE);
X Debug(DPARS, ("load_env, <%s> <%s> -> <%s>\n", name, val, envstr))
X return (TRUE);
X}
X
Xchar *
Xenv_get(char *name, char **envp) {
X int len = strlen(name);
X char *p, *q;
X
X while ((p = *envp++) != NULL) {
X if (!(q = strchr(p, '=')))
X continue;
X if ((q - p) == len && !strncmp(p, name, len))
X return (q+1);
X }
X return (NULL);
X}
END-of-env.c
echo x - job.c
sed 's/^X//' >job.c << 'END-of-job.c'
X/* Copyright 1988,1990,1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X#if !defined(lint) && !defined(LINT)
Xstatic char rcsid[] = "$Id: job.c,v 1.6 2004/01/23 18:56:43 vixie Exp $";
X#endif
X
X#include "cron.h"
X
Xtypedef struct _job {
X struct _job *next;
X entry *e;
X user *u;
X} job;
X
Xstatic job *jhead = NULL, *jtail = NULL;
X
Xvoid
Xjob_add(entry *e, user *u) {
X job *j;
X
X /* if already on queue, keep going */
X for (j = jhead; j != NULL; j = j->next)
X if (j->e == e && j->u == u)
X return;
X
X /* build a job queue element */
X if ((j = (job *)malloc(sizeof(job))) == NULL)
X return;
X j->next = NULL;
X j->e = e;
X j->u = u;
X
X /* add it to the tail */
X if (jhead == NULL)
X jhead = j;
X else
X jtail->next = j;
X jtail = j;
X}
X
Xint
Xjob_runqueue(void) {
X job *j, *jn;
X int run = 0;
X
X for (j = jhead; j; j = jn) {
X do_command(j->e, j->u);
X jn = j->next;
X free(j);
X run++;
X }
X jhead = jtail = NULL;
X return (run);
X}
END-of-job.c
echo x - user.c
sed 's/^X//' >user.c << 'END-of-user.c'
X/* Copyright 1988,1990,1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X#if !defined(lint) && !defined(LINT)
Xstatic char rcsid[] = "$Id: user.c,v 1.5 2004/01/23 18:56:43 vixie Exp $";
X#endif
X
X/* vix 26jan87 [log is in RCS file]
X */
X
X#include "cron.h"
X
Xvoid
Xfree_user(user *u) {
X entry *e, *ne;
X
X free(u->name);
X for (e = u->crontab; e != NULL; e = ne) {
X ne = e->next;
X free_entry(e);
X }
X free(u);
X}
X
Xuser *
Xload_user(int crontab_fd, struct passwd *pw, const char *name) {
X char envstr[MAX_ENVSTR];
X FILE *file;
X user *u;
X entry *e;
X int status, save_errno;
X char **envp, **tenvp;
X
X if (!(file = fdopen(crontab_fd, "r"))) {
X perror("fdopen on crontab_fd in load_user");
X return (NULL);
X }
X
X Debug(DPARS, ("load_user()\n"))
X
X /* file is open. build user entry, then read the crontab file.
X */
X if ((u = (user *) malloc(sizeof(user))) == NULL)
X return (NULL);
X if ((u->name = strdup(name)) == NULL) {
X save_errno = errno;
X free(u);
X errno = save_errno;
X return (NULL);
X }
X u->crontab = NULL;
X
X /* init environment. this will be copied/augmented for each entry.
X */
X if ((envp = env_init()) == NULL) {
X save_errno = errno;
X free(u->name);
X free(u);
X errno = save_errno;
X return (NULL);
X }
X
X /* load the crontab
X */
X while ((status = load_env(envstr, file)) >= OK) {
X switch (status) {
X case ERR:
X free_user(u);
X u = NULL;
X goto done;
X case FALSE:
X e = load_entry(file, NULL, pw, envp);
X if (e) {
X e->next = u->crontab;
X u->crontab = e;
X }
X break;
X case TRUE:
X if ((tenvp = env_set(envp, envstr)) == NULL) {
X save_errno = errno;
X free_user(u);
X u = NULL;
X errno = save_errno;
X goto done;
X }
X envp = tenvp;
X break;
X }
X }
X
X done:
X env_free(envp);
X fclose(file);
X Debug(DPARS, ("...load_user() done\n"))
X return (u);
X}
END-of-user.c
echo x - popen.c
sed 's/^X//' >popen.c << 'END-of-popen.c'
X/*
X * Copyright (c) 1988, 1993, 1994
X * The Regents of the University of California. All rights reserved.
X *
X * This code is derived from software written by Ken Arnold and
X * published in UNIX Review, Vol. 6, No. 8.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X * notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X * notice, this list of conditions and the following disclaimer in the
X * documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X * must display the following acknowledgement:
X * This product includes software developed by the University of
X * California, Berkeley and its contributors.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X */
X
X/* this came out of the ftpd sources; it's been modified to avoid the
X * globbing stuff since we don't need it. also execvp instead of execv.
X */
X
X#ifndef lint
X#if 0
Xstatic sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94";
X#else
Xstatic char rcsid[] = "$Id: popen.c,v 1.6 2003/02/16 04:40:01 vixie Exp $";
X#endif
X#endif /* not lint */
X
X#include "cron.h"
X
X#define MAX_ARGV 100
X#define MAX_GARGV 1000
X
X/*
X * Special version of popen which avoids call to shell. This ensures noone
X * may create a pipe to a hidden program as a side effect of a list or dir
X * command.
X */
Xstatic PID_T *pids;
Xstatic int fds;
X
XFILE *
Xcron_popen(char *program, char *type, struct passwd *pw) {
X char *cp;
X FILE *iop;
X int argc, pdes[2];
X PID_T pid;
X char *argv[MAX_ARGV];
X
X if ((*type != 'r' && *type != 'w') || type[1] != '\0')
X return (NULL);
X
X if (!pids) {
X if ((fds = sysconf(_SC_OPEN_MAX)) <= 0)
X return (NULL);
X if (!(pids = (PID_T *)malloc((size_t)(fds * sizeof(PID_T)))))
X return (NULL);
X bzero(pids, fds * sizeof(PID_T));
X }
X if (pipe(pdes) < 0)
X return (NULL);
X
X /* break up string into pieces */
X for (argc = 0, cp = program; argc < MAX_ARGV - 1; cp = NULL)
X if (!(argv[argc++] = strtok(cp, " \t\n")))
X break;
X argv[MAX_ARGV-1] = NULL;
X
X switch (pid = vfork()) {
X case -1: /* error */
X (void)close(pdes[0]);
X (void)close(pdes[1]);
X return (NULL);
X /* NOTREACHED */
X case 0: /* child */
X if (pw) {
X#ifdef LOGIN_CAP
X if (setusercontext(0, pw, pw->pw_uid, LOGIN_SETALL) < 0) {
X fprintf(stderr,
X "setusercontext failed for %s\n",
X pw->pw_name);
X _exit(ERROR_EXIT);
X }
X#else
X if (setgid(pw->pw_gid) < 0 ||
X initgroups(pw->pw_name, pw->pw_gid) < 0) {
X fprintf(stderr,
X "unable to set groups for %s\n",
X pw->pw_name);
X _exit(1);
X }
X#if (defined(BSD)) && (BSD >= 199103)
X setlogin(pw->pw_name);
X#endif /* BSD */
X if (setuid(pw->pw_uid)) {
X fprintf(stderr,
X "unable to set uid for %s\n",
X pw->pw_name);
X _exit(1);
X }
X#endif /* LOGIN_CAP */
X }
X if (*type == 'r') {
X if (pdes[1] != STDOUT) {
X dup2(pdes[1], STDOUT);
X (void)close(pdes[1]);
X }
X dup2(STDOUT, STDERR); /* stderr too! */
X (void)close(pdes[0]);
X } else {
X if (pdes[0] != STDIN) {
X dup2(pdes[0], STDIN);
X (void)close(pdes[0]);
X }
X (void)close(pdes[1]);
X }
X execvp(argv[0], argv);
X _exit(1);
X }
X
X /* parent; assume fdopen can't fail... */
X if (*type == 'r') {
X iop = fdopen(pdes[0], type);
X (void)close(pdes[1]);
X } else {
X iop = fdopen(pdes[1], type);
X (void)close(pdes[0]);
X }
X pids[fileno(iop)] = pid;
X
X return (iop);
X}
X
Xint
Xcron_pclose(FILE *iop) {
X int fdes;
X PID_T pid;
X WAIT_T status;
X sigset_t sigset, osigset;
X
X /*
X * pclose returns -1 if stream is not associated with a
X * `popened' command, or, if already `pclosed'.
X */
X if (pids == 0 || pids[fdes = fileno(iop)] == 0)
X return (-1);
X (void)fclose(iop);
X sigemptyset(&sigset);
X sigaddset(&sigset, SIGINT);
X sigaddset(&sigset, SIGQUIT);
X sigaddset(&sigset, SIGHUP);
X sigprocmask(SIG_BLOCK, &sigset, &osigset);
X while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR)
X continue;
X sigprocmask(SIG_SETMASK, &osigset, NULL);
X pids[fdes] = 0;
X if (pid < 0)
X return (pid);
X if (WIFEXITED(status))
X return (WEXITSTATUS(status));
X return (1);
X}
END-of-popen.c
echo x - misc.c
sed 's/^X//' >misc.c << 'END-of-misc.c'
X/* Copyright 1988,1990,1993,1994 by Paul Vixie
X * All rights reserved
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X#if !defined(lint) && !defined(LINT)
Xstatic char rcsid[] = "$Id: misc.c,v 1.16 2004/01/23 18:56:43 vixie Exp $";
X#endif
X
X/* vix 26jan87 [RCS has the rest of the log]
X * vix 30dec86 [written]
X */
X
X#include "cron.h"
X#include <limits.h>
X
X#if defined(SYSLOG) && defined(LOG_FILE)
X# undef LOG_FILE
X#endif
X
X#if defined(LOG_DAEMON) && !defined(LOG_CRON)
X# define LOG_CRON LOG_DAEMON
X#endif
X
X#ifndef FACILITY
X#define FACILITY LOG_CRON
X#endif
X
Xstatic int LogFD = ERR;
X
X#if defined(SYSLOG)
Xstatic int syslog_open = FALSE;
X#endif
X
X/*
X * glue_strings is the overflow-safe equivalent of
X * sprintf(buffer, "%s%c%s", a, separator, b);
X *
X * returns 1 on success, 0 on failure. 'buffer' MUST NOT be used if
X * glue_strings fails.
X */
Xint
Xglue_strings(char *buffer, size_t buffer_size, const char *a, const char *b,
X char separator)
X{
X char *buf;
X char *buf_end;
X
X if (buffer_size <= 0)
X return (0);
X buf_end = buffer + buffer_size;
X buf = buffer;
X
X for ( /* nothing */; buf < buf_end && *a != '\0'; buf++, a++ )
X *buf = *a;
X if (buf == buf_end)
X return (0);
X if (separator != '/' || buf == buffer || buf[-1] != '/')
X *buf++ = separator;
X if (buf == buf_end)
X return (0);
X for ( /* nothing */; buf < buf_end && *b != '\0'; buf++, b++ )
X *buf = *b;
X if (buf == buf_end)
X return (0);
X *buf = '\0';
X return (1);
X}
X
Xint
Xstrcmp_until(const char *left, const char *right, char until) {
X while (*left && *left != until && *left == *right) {
X left++;
X right++;
X }
X
X if ((*left=='\0' || *left == until) &&
X (*right=='\0' || *right == until)) {
X return (0);
X }
X return (*left - *right);
X}
X
X/* strdtb(s) - delete trailing blanks in string 's' and return new length
X */
Xint
Xstrdtb(char *s) {
X char *x = s;
X
X /* scan forward to the null
X */
X while (*x)
X x++;
X
X /* scan backward to either the first character before the string,
X * or the last non-blank in the string, whichever comes first.
X */
X do {x--;}
X while (x >= s && isspace((unsigned char)*x));
X
X /* one character beyond where we stopped above is where the null
X * goes.
X */
X *++x = '\0';
X
X /* the difference between the position of the null character and
X * the position of the first character of the string is the length.
X */
X return (x - s);
X}
X
Xint
Xset_debug_flags(const char *flags) {
X /* debug flags are of the form flag[,flag ...]
X *
X * if an error occurs, print a message to stdout and return FALSE.
X * otherwise return TRUE after setting ERROR_FLAGS.
X */
X
X#if !DEBUGGING
X
X printf("this program was compiled without debugging enabled\n");
X return (FALSE);
X
X#else /* DEBUGGING */
X
X const char *pc = flags;
X
X DebugFlags = 0;
X
X while (*pc) {
X const char **test;
X int mask;
X
X /* try to find debug flag name in our list.
X */
X for (test = DebugFlagNames, mask = 1;
X *test != NULL && strcmp_until(*test, pc, ',');
X test++, mask <<= 1)
X NULL;
X
X if (!*test) {
X fprintf(stderr,
X "unrecognized debug flag <%s> <%s>\n",
X flags, pc);
X return (FALSE);
X }
X
X DebugFlags |= mask;
X
X /* skip to the next flag
X */
X while (*pc && *pc != ',')
X pc++;
X if (*pc == ',')
X pc++;
X }
X
X if (DebugFlags) {
X int flag;
X
X fprintf(stderr, "debug flags enabled:");
X
X for (flag = 0; DebugFlagNames[flag]; flag++)
X if (DebugFlags & (1 << flag))
X fprintf(stderr, " %s", DebugFlagNames[flag]);
X fprintf(stderr, "\n");
X }
X
X return (TRUE);
X
X#endif /* DEBUGGING */
X}
X
Xvoid
Xset_cron_uid(void) {
X#if defined(BSD) || defined(POSIX)
X if (seteuid(ROOT_UID) < OK) {
X perror("seteuid");
X exit(ERROR_EXIT);
X }
X#else
X if (setuid(ROOT_UID) < OK) {
X perror("setuid");
X exit(ERROR_EXIT);
X }
X#endif
X}
X
Xvoid
Xset_cron_cwd(void) {
X struct stat sb;
X struct group *grp = NULL;
X
X#ifdef CRON_GROUP
X grp = getgrnam(CRON_GROUP);
X#endif
X /* first check for CRONDIR ("/var/cron" or some such)
X */
X if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
X perror(CRONDIR);
X if (OK == mkdir(CRONDIR, 0710)) {
X fprintf(stderr, "%s: created\n", CRONDIR);
X stat(CRONDIR, &sb);
X } else {
X fprintf(stderr, "%s: ", CRONDIR);
X perror("mkdir");
X exit(ERROR_EXIT);
X }
X }
X if (!S_ISDIR(sb.st_mode)) {
X fprintf(stderr, "'%s' is not a directory, bailing out.\n",
X CRONDIR);
X exit(ERROR_EXIT);
X }
X if (chdir(CRONDIR) < OK) {
X fprintf(stderr, "cannot chdir(%s), bailing out.\n", CRONDIR);
X perror(CRONDIR);
X exit(ERROR_EXIT);
X }
X
X /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
X */
X if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
X perror(SPOOL_DIR);
X if (OK == mkdir(SPOOL_DIR, 0700)) {
X fprintf(stderr, "%s: created\n", SPOOL_DIR);
X stat(SPOOL_DIR, &sb);
X } else {
X fprintf(stderr, "%s: ", SPOOL_DIR);
X perror("mkdir");
X exit(ERROR_EXIT);
X }
X }
X if (!S_ISDIR(sb.st_mode)) {
X fprintf(stderr, "'%s' is not a directory, bailing out.\n",
X SPOOL_DIR);
X exit(ERROR_EXIT);
X }
X if (grp != NULL) {
X if (sb.st_gid != grp->gr_gid)
X chown(SPOOL_DIR, -1, grp->gr_gid);
X if (sb.st_mode != 01730)
X chmod(SPOOL_DIR, 01730);
X }
X}
X
X/* acquire_daemonlock() - write our PID into /etc/cron.pid, unless
X * another daemon is already running, which we detect here.
X *
X * note: main() calls us twice; once before forking, once after.
X * we maintain static storage of the file pointer so that we
X * can rewrite our PID into _PATH_CRON_PID after the fork.
X */
Xvoid
Xacquire_daemonlock(int closeflag) {
X static int fd = -1;
X char buf[3*MAX_FNAME];
X const char *pidfile;
X char *ep;
X long otherpid;
X ssize_t num;
X
X if (closeflag) {
X /* close stashed fd for child so we don't leak it. */
X if (fd != -1) {
X close(fd);
X fd = -1;
X }
X return;
X }
X
X if (fd == -1) {
X pidfile = _PATH_CRON_PID;
X /* Initial mode is 0600 to prevent flock() race/DoS. */
X if ((fd = open(pidfile, O_RDWR|O_CREAT, 0600)) == -1) {
X sprintf(buf, "can't open or create %s: %s",
X pidfile, strerror(errno));
X fprintf(stderr, "%s: %s\n", ProgramName, buf);
X log_it("CRON", getpid(), "DEATH", buf);
X exit(ERROR_EXIT);
X }
X
X if (flock(fd, LOCK_EX|LOCK_NB) < OK) {
X int save_errno = errno;
X
X bzero(buf, sizeof(buf));
X if ((num = read(fd, buf, sizeof(buf) - 1)) > 0 &&
X (otherpid = strtol(buf, &ep, 10)) > 0 &&
X ep != buf && *ep == '\n' && otherpid != LONG_MAX) {
X sprintf(buf,
X "can't lock %s, otherpid may be %ld: %s",
X pidfile, otherpid, strerror(save_errno));
X } else {
X sprintf(buf,
X "can't lock %s, otherpid unknown: %s",
X pidfile, strerror(save_errno));
X }
X sprintf(buf, "can't lock %s, otherpid may be %ld: %s",
X pidfile, otherpid, strerror(save_errno));
X fprintf(stderr, "%s: %s\n", ProgramName, buf);
X log_it("CRON", getpid(), "DEATH", buf);
X exit(ERROR_EXIT);
X }
X (void) fchmod(fd, 0644);
X (void) fcntl(fd, F_SETFD, 1);
X }
X
X sprintf(buf, "%ld\n", (long)getpid());
X (void) lseek(fd, (off_t)0, SEEK_SET);
X num = write(fd, buf, strlen(buf));
X (void) ftruncate(fd, num);
X
X /* abandon fd even though the file is open. we need to keep
X * it open and locked, but we don't need the handles elsewhere.
X */
X}
X
X/* get_char(file) : like getc() but increment LineNumber on newlines
X */
Xint
Xget_char(FILE *file) {
X int ch;
X
X ch = getc(file);
X if (ch == '\n')
X Set_LineNum(LineNumber + 1)
X return (ch);
X}
X
X/* unget_char(ch, file) : like ungetc but do LineNumber processing
X */
Xvoid
Xunget_char(int ch, FILE *file) {
X ungetc(ch, file);
X if (ch == '\n')
X Set_LineNum(LineNumber - 1)
X}
X
X/* get_string(str, max, file, termstr) : like fgets() but
X * (1) has terminator string which should include \n
X * (2) will always leave room for the null
X * (3) uses get_char() so LineNumber will be accurate
X * (4) returns EOF or terminating character, whichever
X */
Xint
Xget_string(char *string, int size, FILE *file, char *terms) {
X int ch;
X
X while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) {
X if (size > 1) {
X *string++ = (char) ch;
X size--;
X }
X }
X
X if (size > 0)
X *string = '\0';
X
X return (ch);
X}
X
X/* skip_comments(file) : read past comment (if any)
X */
Xvoid
Xskip_comments(FILE *file) {
X int ch;
X
X while (EOF != (ch = get_char(file))) {
X /* ch is now the first character of a line.
X */
X
X while (ch == ' ' || ch == '\t')
X ch = get_char(file);
X
X if (ch == EOF)
X break;
X
X /* ch is now the first non-blank character of a line.
X */
X
X if (ch != '\n' && ch != '#')
X break;
X
X /* ch must be a newline or comment as first non-blank
X * character on a line.
X */
X
X while (ch != '\n' && ch != EOF)
X ch = get_char(file);
X
X /* ch is now the newline of a line which we're going to
X * ignore.
X */
X }
X if (ch != EOF)
X unget_char(ch, file);
X}
X
X/* int in_file(const char *string, FILE *file, int error)
X * return TRUE if one of the lines in file matches string exactly,
X * FALSE if no lines match, and error on error.
X */
Xstatic int
Xin_file(const char *string, FILE *file, int error)
X{
X char line[MAX_TEMPSTR];
X char *endp;
X
X if (fseek(file, 0L, SEEK_SET))
X return (error);
X while (fgets(line, MAX_TEMPSTR, file)) {
X if (line[0] != '\0') {
X endp = &line[strlen(line) - 1];
X if (*endp != '\n')
X return (error);
X *endp = '\0';
X if (0 == strcmp(line, string))
X return (TRUE);
X }
X }
X if (ferror(file))
X return (error);
X return (FALSE);
X}
X
X/* int allowed(const char *username, const char *allow_file, const char *deny_file)
X * returns TRUE if (allow_file exists and user is listed)
X * or (deny_file exists and user is NOT listed).
X * root is always allowed.
X */
Xint
Xallowed(const char *username, const char *allow_file, const char *deny_file) {
X FILE *fp;
X int isallowed;
X
X if (strcmp(username, ROOT_USER) == 0)
X return (TRUE);
X isallowed = FALSE;
X if ((fp = fopen(allow_file, "r")) != NULL) {
X isallowed = in_file(username, fp, FALSE);
X fclose(fp);
X } else if ((fp = fopen(deny_file, "r")) != NULL) {
X isallowed = !in_file(username, fp, FALSE);
X fclose(fp);
X }
X return (isallowed);
X}
X
Xvoid
Xlog_it(const char *username, PID_T xpid, const char *event, const char *detail) {
X#if defined(LOG_FILE) || DEBUGGING
X PID_T pid = xpid;
X#endif
X#if defined(LOG_FILE)
X char *msg;
X TIME_T now = time((TIME_T) 0);
X struct tm *t = localtime(&now);
X#endif /*LOG_FILE*/
X
X#if defined(LOG_FILE)
X /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation.
X */
X msg = malloc(strlen(username)
X + strlen(event)
X + strlen(detail)
X + MAX_TEMPSTR);
X if (msg == NULL)
X return;
X
X if (LogFD < OK) {
X LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
X if (LogFD < OK) {
X fprintf(stderr, "%s: can't open log file\n",
X ProgramName);
X perror(LOG_FILE);
X } else {
X (void) fcntl(LogFD, F_SETFD, 1);
X }
X }
X
X /* we have to sprintf() it because fprintf() doesn't always write
X * everything out in one chunk and this has to be atomically appended
X * to the log file.
X */
X sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n",
X username,
X t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, pid,
X event, detail);
X
X /* we have to run strlen() because sprintf() returns (char*) on old BSD
X */
X if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) {
X if (LogFD >= OK)
X perror(LOG_FILE);
X fprintf(stderr, "%s: can't write to log file\n", ProgramName);
X write(STDERR, msg, strlen(msg));
X }
X
X free(msg);
X#endif /*LOG_FILE*/
X
X#if defined(SYSLOG)
X if (!syslog_open) {
X# ifdef LOG_DAEMON
X openlog(ProgramName, LOG_PID, FACILITY);
X# else
X openlog(ProgramName, LOG_PID);
X# endif
X syslog_open = TRUE; /* assume openlog success */
X }
X
X syslog(LOG_INFO, "(%s) %s (%s)", username, event, detail);
X
X#endif /*SYSLOG*/
X
X#if DEBUGGING
X if (DebugFlags) {
X fprintf(stderr, "log_it: (%s %ld) %s (%s)\n",
X username, (long)pid, event, detail);
X }
X#endif
X}
X
Xvoid
Xlog_close(void) {
X if (LogFD != ERR) {
X close(LogFD);
X LogFD = ERR;
X }
X#if defined(SYSLOG)
X closelog();
X syslog_open = FALSE;
X#endif /*SYSLOG*/
X}
X
X/* char *first_word(char *s, char *t)
X * return pointer to first word
X * parameters:
X * s - string we want the first word of
X * t - terminators, implicitly including \0
X * warnings:
X * (1) this routine is fairly slow
X * (2) it returns a pointer to static storage
X */
Xchar *
Xfirst_word(char *s, char *t) {
X static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */
X static int retsel = 0;
X char *rb, *rp;
X
X /* select a return buffer */
X retsel = 1-retsel;
X rb = &retbuf[retsel][0];
X rp = rb;
X
X /* skip any leading terminators */
X while (*s && (NULL != strchr(t, *s))) {
X s++;
X }
X
X /* copy until next terminator or full buffer */
X while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
X *rp++ = *s++;
X }
X
X /* finish the return-string and return it */
X *rp = '\0';
X return (rb);
X}
X
X/* warning:
X * heavily ascii-dependent.
X */
Xvoid
Xmkprint(dst, src, len)
X char *dst;
X unsigned char *src;
X int len;
X{
X /*
X * XXX
X * We know this routine can't overflow the dst buffer because mkprints()
X * allocated enough space for the worst case.
X */
X while (len-- > 0)
X {
X unsigned char ch = *src++;
X
X if (ch < ' ') { /* control character */
X *dst++ = '^';
X *dst++ = ch + '@';
X } else if (ch < 0177) { /* printable */
X *dst++ = ch;
X } else if (ch == 0177) { /* delete/rubout */
X *dst++ = '^';
X *dst++ = '?';
X } else { /* parity character */
X sprintf(dst, "\\%03o", ch);
X dst += 4;
X }
X }
X *dst = '\0';
X}
X
X/* warning:
X * returns a pointer to malloc'd storage, you must call free yourself.
X */
Xchar *
Xmkprints(src, len)
X unsigned char *src;
X unsigned int len;
X{
X char *dst = malloc(len*4 + 1);
X
X if (dst)
X mkprint(dst, src, len);
X
X return (dst);
X}
X
X#ifdef MAIL_DATE
X/* Sat, 27 Feb 1993 11:44:51 -0800 (CST)
X * 1234567890123456789012345678901234567
X */
Xchar *
Xarpadate(clock)
X time_t *clock;
X{
X time_t t = clock ? *clock : time((TIME_T) 0);
X struct tm tm = *localtime(&t);
X long gmtoff = get_gmtoff(&t, &tm);
X int hours = gmtoff / SECONDS_PER_HOUR;
X int minutes = (gmtoff - (hours * SECONDS_PER_HOUR)) / SECONDS_PER_MINUTE;
X static char ret[64]; /* zone name might be >3 chars */
X
X (void) sprintf(ret, "%s, %2d %s %2d %02d:%02d:%02d %.2d%.2d (%s)",
X DowNames[tm.tm_wday],
X tm.tm_mday,
X MonthNames[tm.tm_mon],
X tm.tm_year + 1900,
X tm.tm_hour,
X tm.tm_min,
X tm.tm_sec,
X hours,
X minutes,
X TZONE(*tm));
X return (ret);
X}
X#endif /*MAIL_DATE*/
X
X#ifdef HAVE_SAVED_UIDS
Xstatic uid_t save_euid;
Xstatic gid_t save_egid;
X
Xint swap_uids(void) {
X save_egid = getegid();
X save_euid = geteuid();
X return ((setegid(getgid()) || seteuid(getuid())) ? -1 : 0);
X}
X
Xint swap_uids_back(void) {
X return ((setegid(getgid()) || seteuid(getuid())) ? -1 : 0);
X}
X
X#else /*HAVE_SAVED_UIDS*/
X
Xint swap_uids(void) {
X return ((setregid(getegid(), getgid()) || setreuid(geteuid(), getuid()))
X ? -1 : 0);
X}
X
Xint swap_uids_back(void) {
X return (swap_uids());
X}
X#endif /*HAVE_SAVED_UIDS*/
X
Xsize_t
Xstrlens(const char *last, ...) {
X va_list ap;
X size_t ret = 0;
X const char *str;
X
X va_start(ap, last);
X for (str = last; str != NULL; str = va_arg(ap, const char *))
X ret += strlen(str);
X va_end(ap);
X return (ret);
X}
X
X/* Return the offset from GMT in seconds (algorithm taken from sendmail).
X *
X * warning:
X * clobbers the static storage space used by localtime() and gmtime().
X * If the local pointer is non-NULL it *must* point to a local copy.
X */
X#ifndef HAVE_TM_GMTOFF
Xlong get_gmtoff(time_t *clock, struct tm *local)
X{
X struct tm gmt;
X long offset;
X
X gmt = *gmtime(clock);
X if (local == NULL)
X local = localtime(clock);
X
X offset = (local->tm_sec - gmt.tm_sec) +
X ((local->tm_min - gmt.tm_min) * 60) +
X ((local->tm_hour - gmt.tm_hour) * 3600);
X
X /* Timezone may cause year rollover to happen on a different day. */
X if (local->tm_year < gmt.tm_year)
X offset -= 24 * 3600;
X else if (local->tm_year > gmt.tm_year)
X offset -= 24 * 3600;
X else if (local->tm_yday < gmt.tm_yday)
X offset -= 24 * 3600;
X else if (local->tm_yday > gmt.tm_yday)
X offset += 24 * 3600;
X
X return (offset);
X}
X#endif /* HAVE_TM_GMTOFF */
END-of-misc.c
echo x - pw_dup.c
sed 's/^X//' >pw_dup.c << 'END-of-pw_dup.c'
X/*
X * Copyright (c) 2000,2002 Todd C. Miller <Todd.Miller@courtesan.com>
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
X * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
X * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
X * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
X * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X
X/*
X * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
X * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X */
X#if !defined(lint) && !defined(LINT)
Xstatic char rcsid[] = "$Id: pw_dup.c,v 1.2 2004/01/23 18:56:43 vixie Exp $";
X#endif
X
X#include <sys/param.h>
X
X#if !defined(OpenBSD) || OpenBSD < 200105
X
X#include <pwd.h>
X#include <stdlib.h>
X#include <stdio.h>
X#include <string.h>
X
X#include "config.h"
X
Xstruct passwd *
Xpw_dup(const struct passwd *pw) {
X char *cp;
X size_t nsize, psize, csize, gsize, dsize, ssize, total;
X struct passwd *newpw;
X
X /* Allocate in one big chunk for easy freeing */
X total = sizeof(struct passwd);
X if (pw->pw_name) {
X nsize = strlen(pw->pw_name) + 1;
X total += nsize;
X }
X if (pw->pw_passwd) {
X psize = strlen(pw->pw_passwd) + 1;
X total += psize;
X }
X#ifdef LOGIN_CAP
X if (pw->pw_class) {
X csize = strlen(pw->pw_class) + 1;
X total += csize;
X }
X#endif /* LOGIN_CAP */
X if (pw->pw_gecos) {
X gsize = strlen(pw->pw_gecos) + 1;
X total += gsize;
X }
X if (pw->pw_dir) {
X dsize = strlen(pw->pw_dir) + 1;
X total += dsize;
X }
X if (pw->pw_shell) {
X ssize = strlen(pw->pw_shell) + 1;
X total += ssize;
X }
X if ((cp = malloc(total)) == NULL)
X return (NULL);
X newpw = (struct passwd *)cp;
X
X /*
X * Copy in passwd contents and make strings relative to space
X * at the end of the buffer.
X */
X (void)memcpy(newpw, pw, sizeof(struct passwd));
X cp += sizeof(struct passwd);
X if (pw->pw_name) {
X (void)memcpy(cp, pw->pw_name, nsize);
X newpw->pw_name = cp;
X cp += nsize;
X }
X if (pw->pw_passwd) {
X (void)memcpy(cp, pw->pw_passwd, psize);
X newpw->pw_passwd = cp;
X cp += psize;
X }
X#ifdef LOGIN_CAP
X if (pw->pw_class) {
X (void)memcpy(cp, pw->pw_class, csize);
X newpw->pw_class = cp;
X cp += csize;
X }
X#endif /* LOGIN_CAP */
X if (pw->pw_gecos) {
X (void)memcpy(cp, pw->pw_gecos, gsize);
X newpw->pw_gecos = cp;
X cp += gsize;
X }
X if (pw->pw_dir) {
X (void)memcpy(cp, pw->pw_dir, dsize);
X newpw->pw_dir = cp;
X cp += dsize;
X }
X if (pw->pw_shell) {
X (void)memcpy(cp, pw->pw_shell, ssize);
X newpw->pw_shell = cp;
X cp += ssize;
X }
X
X return (newpw);
X}
X
X#endif /* !OpenBSD || OpenBSD < 200105 */
END-of-pw_dup.c
exit