From f23031c8e329de7b5931f304f9531101e138114f Mon Sep 17 00:00:00 2001 From: Jay Larson Date: Mon, 4 Jun 2018 16:59:38 -0500 Subject: [PATCH] First check in --- Makefile | 79 + SNAP/README | 5 + SNAP/cron.init | 53 + SNAP/crontab | 11 + SNAP/usher | 21 + SRC/cron_4.1.shar | 6714 ++++++++++++++++++++++++++++++++ SRC/cron_4.1.shar.sha1 | 1 + SRC/patches/01_add-crond.patch | 194 + SRC/patches/README | 3 + 9 files changed, 7081 insertions(+) create mode 100644 Makefile create mode 100644 SNAP/README create mode 100755 SNAP/cron.init create mode 100644 SNAP/crontab create mode 100755 SNAP/usher create mode 100755 SRC/cron_4.1.shar create mode 100644 SRC/cron_4.1.shar.sha1 create mode 100644 SRC/patches/01_add-crond.patch create mode 100644 SRC/patches/README diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3b997ba --- /dev/null +++ b/Makefile @@ -0,0 +1,79 @@ +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation here: +# (http://www.gnu.org/licenses/gpl-2.0.html) +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +ARCH = x86_64 +DEPENDS = dash,glibc +BUILDDEPS = +URL = http://ftp.isc.org/isc/cron/ +REPO = main +BRIEF = daemon to execute scheduled commands +DESC = An implimentation of cron based on the SysV cron (aka Vixie cron) +SNAPVER = 0 + +ARCHIVE := $(PWD)/$(shell ls SRC/*.shar|tail -1) +SRCDIR := cron-$(shell echo $(ARCHIVE)|egrep -o '[0-9]\.[0-9]') +PATCHDIR := $(PWD)/SRC/patches +VERSION := $(shell echo $(SRCDIR)|egrep -o '[0-9].*'|sed 's/^-//')-$(SNAPVER) + +include /usr/share/snap/Makefile.snaplinux + +$(SRCDIR)/Makefile: $(ARCHIVE) + @install -v -d $(SRCDIR) ./ && \ + cd $(SRCDIR) && sh $(ARCHIVE) + @touch $(SRCDIR)/Makefile + +$(SRCDIR)/cron: $(SRCDIR)/Makefile + @cd $(SRCDIR) && \ + for patch in `find $(PATCHDIR) -name \*.patch|sort`; do \ + patch --verbose -Np1 -i $$patch; \ + done + @cd $(SRCDIR) && make all + +$(ROOT): $(SRCDIR)/cron + @if [ -d $(ROOT) ]; then \ + touch $(ROOT); \ + else \ + mkdir -v $(ROOT); \ + fi + + @install -v -m 0755 -d $(ROOT)/bin && \ + install -v -m 0755 -d $(ROOT)/etc && \ + install -v -m 0755 -d $(ROOT)/etc/cron.hourly && \ + install -v -m 0755 -d $(ROOT)/etc/cron.d && \ + install -v -m 0755 -d $(ROOT)/etc/cron.daily && \ + install -v -m 0755 -d $(ROOT)/etc/cron.monthly && \ + install -v -m 0755 -d $(ROOT)/etc/cron.weekly && \ + install -v -m 0755 -d $(ROOT)/etc/default && \ + install -v -m 0755 -d $(ROOT)/etc/init.d && \ + install -v -m 0755 -d $(ROOT)/sbin && \ + install -v -m 0755 -d $(ROOT)/usr/share/cron && \ + install -v -m 0755 -d $(ROOT)/usr/share/man/man1 && \ + install -v -m 0755 -d $(ROOT)/usr/share/man/man5 && \ + install -v -m 0755 -d $(ROOT)/usr/share/man/man8 && \ + install -v -m 0755 -d $(ROOT)/var/spool/cron && \ + install -v -m 1730 -d $(ROOT)/var/spool/cron/crontabs && \ + cd $(SRCDIR) && make install DESTROOT=$(ROOT) DESTDIR=$(ROOT) && \ + install -v -m 644 crontab.1 $(ROOT)/usr/share/man/man1/crontab.1 && \ + install -v -m 644 crontab.5 $(ROOT)/usr/share/man/man5/crontab.5 && \ + install -v -m 644 cron.8 $(ROOT)/usr/share/man/man8/cron.8 && \ + install -v -m 755 $(SNAPDIR)/cron.init $(ROOT)/etc/init.d/cron + install -v -m 600 $(SNAPDIR)/crontab $(ROOT)/usr/share/cron/crontab + +test: $(ROOT) + @cd $(SRCDIR); \ + make check + +clean: + @rm -rvf $(ROOT) \ + $(SNAPINFO) \ + $(MANIFEST) \ + $(FILES) \ + $(SRCDIR) + diff --git a/SNAP/README b/SNAP/README new file mode 100644 index 0000000..cb756b1 --- /dev/null +++ b/SNAP/README @@ -0,0 +1,5 @@ +This is the directory where the manifest, snapinfo, +and files.tar.gz files will be created. It is also +where the usher file should be placed if it is +required by the package. Any other files that need +to be included could also be placed here. diff --git a/SNAP/cron.init b/SNAP/cron.init new file mode 100755 index 0000000..c3c7aca --- /dev/null +++ b/SNAP/cron.init @@ -0,0 +1,53 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: cron +# Required-Start: $remote_fs $syslog $time +# Required-Stop: $remote_fs $syslog $time +# Should-Start: $network $named slapd autofs ypbind nscd nslcd +# Should-Stop: $network $named slapd autofs ypbind nscd nslcd +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Regular background program processing daemon +# Description: cron is a standard UNIX program that runs user-specified +# programs at periodic scheduled times. vixie cron adds a +# number of features to the basic UNIX cron, including better +# security and more powerful configuration options. +### END INIT INFO + +DAEMON=/sbin/cron +PIDFILE=/var/run/cron.pid + +. /lib/lsb/init-functions +[ -r /etc/default/cron ] && . /etc/default/cron + +case "$1" in + start) + log_init_msg "Starting cron" + start_daemon -p "$PIDFILE" "$DAEMON" "$CRONARGS" && log_success_msg || \ + log_failure_msg + ;; + stop) + log_init_msg "Stopping cron" + killproc -p "$PIDFILE" "$DAEMON" && log_success_msg || log_failure_msg + ;; + restart) + $0 stop + sleep 1 + $0 start + ;; + status) + pid=$(pidofproc -p "$PIDFILE" "$DAEMON") + + if [ "$?" -ne 0 ]; then + echo "cron not running" + else + echo "cron running with PID: $pid" + fi + ;; + *) + echo "Usage: $0 [start|stop|restart|status]" + exit 1 + ;; +esac + +exit 0 diff --git a/SNAP/crontab b/SNAP/crontab new file mode 100644 index 0000000..d44f314 --- /dev/null +++ b/SNAP/crontab @@ -0,0 +1,11 @@ +SHELL=/bin/sh +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +# we use the runall script to execute any scripts in the cron.hourly, +# cron.daily, cron.weekly, and cron.monthly directories + +# m h dom mon dow user command +17 * * * * root cd / && runall /etc/cron.hourly +25 6 * * * root cd / && runall /etc/cron.daily +47 6 * * 7 root cd / && runall /etc/cron.weekly +52 6 1 * * root cd / && runall /etc/cron.monthly diff --git a/SNAP/usher b/SNAP/usher new file mode 100755 index 0000000..83d04ca --- /dev/null +++ b/SNAP/usher @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e + +case $1 in + preinst) + exit 0 + ;; + postinst) + [ -f "/run/cron.pid" ] && /etc/init.d/cron restart + [ -f "/etc/crontab" ] || cp /usr/share/cron/crontab /etc/crontab + exit 0 + ;; + prerm) + [ -f "/run/cron.pid" ] && /etc/init.d/cron stop + exit 0 + ;; + postrm) + exit 0 + ;; +esac diff --git a/SRC/cron_4.1.shar b/SRC/cron_4.1.shar new file mode 100755 index 0000000..5f268c9 --- /dev/null +++ b/SRC/cron_4.1.shar @@ -0,0 +1,6714 @@ +# 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 " 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/". +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 +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 +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 \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 +X#include +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 ``/'' 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 +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 +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 +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#<> +XDESTROOT = $(DESTDIR)/usr +XDESTSBIN = $(DESTROOT)/sbin +XDESTBIN = $(DESTROOT)/bin +XDESTMAN = $(DESTROOT)/share/man +X#<> +XINCLUDE = -I. +X#INCLUDE = +X#<> +XLIBS = +X#<> +X#CDEBUG = -O +XCDEBUG = -g +X#<> +XLINTFLAGS = -hbxa $(INCLUDE) $(DEBUGGING) +X#<> +XCC = gcc -Wall -Wno-unused -Wno-comment +X#<> +XDEFS = +X#(SGI IRIX systems need this) +X#DEFS = -D_BSD_SIGNALS -Dconst= +X#<> +X#INSTALL = installbsd +XINSTALL = install +X#<> +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 +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 , 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 +X#include +X#include +X#include +X#include +X#include +X#include +X +X#include +X#include +X#ifndef isascii +X#define isascii(c) ((unsigned)(c)<=0177) +X#endif +X#include +X#include +X#include +X#include +X#include +X#include +X#include +X#include +X#include +X#include +X#include +X#include +X#include +X#include +X +X#if defined(SYSLOG) +X# include +X#endif +X +X#if defined(LOGIN_CAP) +X# include +X#endif /*LOGIN_CAP*/ +X +X#if defined(BSD_AUTH) +X# include +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 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 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 +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 +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 +X +X#if !defined(OpenBSD) || OpenBSD < 200105 +X +X#include +X#include +X#include +X#include +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 + diff --git a/SRC/cron_4.1.shar.sha1 b/SRC/cron_4.1.shar.sha1 new file mode 100644 index 0000000..db06657 --- /dev/null +++ b/SRC/cron_4.1.shar.sha1 @@ -0,0 +1 @@ +77c52a492b075a77f87eded9dfdbf4bf9ba0ac91 diff --git a/SRC/patches/01_add-crond.patch b/SRC/patches/01_add-crond.patch new file mode 100644 index 0000000..d89001b --- /dev/null +++ b/SRC/patches/01_add-crond.patch @@ -0,0 +1,194 @@ +diff -Naur a/cron.8 b/cron.8 +--- a/cron.8 2018-06-03 19:09:42.562436425 -0500 ++++ b/cron.8 2018-06-03 21:38:57.535075370 -0500 +@@ -30,17 +30,18 @@ + .RB [ \-n ] + .SH DESCRIPTION + .I Cron +-should be started from /etc/rc or /etc/rc.local. It will return immediately, +-so you don't need to start it with '&'. The \-n option changes this default +-behavior causing it to run in the foreground. This can be useful when +-starting it out of init. ++should normally be started via the init.d script. It will return immediately. ++The \-n option changes this default behavior causing it to run in the ++foreground. This can be useful when starting it out of init. + .PP + .I Cron +-searches /var/cron/tabs for crontab files which are named after accounts in +-/etc/passwd; crontabs found are loaded into memory. ++searches /var/spool/cron/crontabs for crontab files which are named after ++accounts in /etc/passwd; crontabs found are loaded into memory. + .I Cron + also searches for /etc/crontab which is in a different format (see +-.IR crontab (5)). ++.IR crontab ++(5)). This version of cron also looks in /etc/cron.d ++for files and parses them in the same manner as /etc/crontab. + .I Cron + then wakes up every minute, examining all stored crontabs, checking each + command to see if it should be run in the current minute. When executing +@@ -49,10 +50,8 @@ + .PP + Additionally, + .I cron +-checks each minute to see if its spool directory's modtime (or the modtime +-on +-.IR /etc/crontab ) +-has changed, and if it has, ++checks each minute to see if the modtime has changed for the spool directory, ++/etc/crontab, or /etc/cron.d. If it has, + .I cron + will then examine the modtime on all crontabs and reload those which have + changed. Thus +@@ -84,7 +83,8 @@ + In this version of + .BR cron , + /etc/crontab must not be readable or writable by any user other than root. +-In other words, it should be mode 0600. ++In other words, it should be mode 0600. The same holds true for files in ++/etc/cron.d. + .SH "SEE ALSO" + .IR crontab (1), + .IR crontab (5) +diff -Naur a/crontab.1 b/crontab.1 +--- a/crontab.1 2018-06-03 19:09:42.562436425 -0500 ++++ b/crontab.1 2018-06-03 21:22:44.319733805 -0500 +@@ -88,8 +88,8 @@ + crontab(5), cron(8) + .SH FILES + .nf +-/var/cron/cron.allow +-/var/cron/cron.deny ++/var/spool/cron/cron.allow ++/var/spool/cron/cron.deny + .fi + .SH STANDARDS + The +diff -Naur a/database.c b/database.c +--- a/database.c 2018-06-03 19:09:42.582437220 -0500 ++++ b/database.c 2018-06-03 20:49:21.555262391 -0500 +@@ -28,7 +28,7 @@ + + #include "cron.h" + +-#define TMAX(a,b) ((a)>(b)?(a):(b)) ++#define TMAX(a,b,c) ((a)>(b)?(((a)>(c))?a:c):((b)>(c)?(b):(c))) + + static void process_crontab(const char *, const char *, + const char *, struct stat *, +@@ -36,7 +36,7 @@ + + void + load_database(cron_db *old_db) { +- struct stat statbuf, syscron_stat; ++ struct stat statbuf, syscron_stat, crond_stat; + cron_db new_db; + DIR_T *dp; + DIR *dir; +@@ -53,6 +53,13 @@ + (void) exit(ERROR_EXIT); + } + ++ /* we do the same for CROND_DIR ++ */ ++ if (stat(CROND_DIR, &crond_stat) < OK) { ++ log_it("CRON", getpid(), "STAT FAILED", CROND_DIR); ++ (void) exit(ERROR_EXIT); ++ } ++ + /* track system crontab file + */ + if (stat(SYSCRONTAB, &syscron_stat) < OK) +@@ -65,7 +72,7 @@ + * so is guaranteed to be different than the stat() mtime the first + * time this function is called. + */ +- if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime)) { ++ if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime, crond_stat.st_mtime)) { + Debug(DLOAD, ("[%ld] spool dir mtime unch, no load needed.\n", + (long)getpid())) + return; +@@ -76,7 +83,7 @@ + * actually changed. Whatever is left in the old database when + * we're done is chaff -- crontabs that disappeared. + */ +- new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime); ++ new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime, crond_stat.st_mtime); + new_db.head = new_db.tail = NULL; + + if (syscron_stat.st_mtime) +@@ -116,6 +123,37 @@ + } + closedir(dir); + ++ /* we perform the same task now for CROND_DIR ++ */ ++ if (!(dir = opendir(CROND_DIR))) { ++ log_it("CRON", getpid(), "OPENDIR FAILED", CROND_DIR); ++ (void) exit(ERROR_EXIT); ++ } ++ ++ while (NULL != (dp = readdir(dir))) { ++ char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1]; ++ ++ /* avoid file names beginning with ".". this is good ++ * because we would otherwise waste two guaranteed calls ++ * to getpwnam() for . and .., and also because user names ++ * starting with a period are just too nasty to consider. ++ */ ++ if (dp->d_name[0] == '.') ++ continue; ++ ++ if (strlen(dp->d_name) >= sizeof fname) ++ continue; /* XXX log? */ ++ (void) strcpy(fname, dp->d_name); ++ ++ if (!glue_strings(tabname, sizeof tabname, CROND_DIR, ++ fname, '/')) ++ continue; /* XXX log? */ ++ ++ process_crontab("root", NULL, tabname, &crond_stat, ++ &new_db, old_db); ++ } ++ closedir(dir); ++ + /* if we don't do this, then when our children eventually call + * getpwnam() in do_command.c's child_process to verify MAILTO=, + * they will screw us up (and v-v). +@@ -207,7 +245,7 @@ + goto next_crontab; + } + if ((statbuf->st_mode & 07777) != 0600) { +- log_it(fname, getpid(), "BAD FILE MODE", tabname); ++ log_it(fname, getpid(), "BAD FILE MODE (expected 0600)", tabname); + goto next_crontab; + } + if (statbuf->st_uid != ROOT_UID && (pw == NULL || +diff -Naur a/pathnames.h b/pathnames.h +--- a/pathnames.h 2018-06-03 19:09:42.570436703 -0500 ++++ b/pathnames.h 2018-06-03 21:04:02.563618663 -0500 +@@ -35,7 +35,7 @@ + * to; SPOOL_DIR, CRON_ALLOW, CRON_DENY, and LOG_FILE + * are all relative to this directory. + */ +-#define CRONDIR "/var/cron" ++#define CRONDIR "/var/spool/cron" + #endif + + /* SPOOLDIR is where the crontabs live. +@@ -46,7 +46,14 @@ + * newer than they were last time around (or which + * didn't exist last time around...) + */ +-#define SPOOL_DIR "tabs" ++#define SPOOL_DIR "crontabs" ++ ++ /* CROND_DIR is where the system wide extentions ++ * to /etc/crontab are placed. These are to be ++ * treated similarly to the files from SPOOL_DIR ++ * except they will be parsed as /etc/crontab is ++ */ ++#define CROND_DIR "/etc/cron.d" + + /* cron allow/deny file. At least cron.deny must + * exist for ordinary users to run crontab. diff --git a/SRC/patches/README b/SRC/patches/README new file mode 100644 index 0000000..976577a --- /dev/null +++ b/SRC/patches/README @@ -0,0 +1,3 @@ +Place any patch files here and preface each with a +number indicating the order of execution. Patch +files are expected to use a .patch extension.