6715 lines
201 KiB
Plaintext
Executable File
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
|
|
|