diff --git a/usr.sbin/cron/cron/compat.h b/usr.sbin/cron/cron/compat.h deleted file mode 100644 index ba32a31b417e..000000000000 --- a/usr.sbin/cron/cron/compat.h +++ /dev/null @@ -1,136 +0,0 @@ -/* Copyright 1993,1994 by Paul Vixie - * All rights reserved - * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. - * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul - */ - -/* - * $FreeBSD$ - */ - -#ifndef __P -# ifdef __STDC__ -# define __P(x) x -# else -# define __P(x) () -# define const -# endif -#endif - -#if defined(UNIXPC) || defined(unixpc) -# define UNIXPC 1 -# define ATT 1 -#endif - -#if defined(hpux) || defined(_hpux) || defined(__hpux) -# define HPUX 1 -# define seteuid(e) setresuid(-1,e,-1) -# define setreuid(r,e) setresuid(r,e,-1) -#endif - -#if defined(_IBMR2) -# define AIX 1 -#endif - -#if defined(__convex__) -# define CONVEX 1 -#endif - -#if defined(sgi) || defined(_sgi) || defined(__sgi) -# define IRIX 1 -/* IRIX 4 hdrs are broken: one cannot #include both - * and because they disagree on system(), perror(). - * Therefore we must zap the "const" keyword BEFORE including - * either of them. - */ -# define const -#endif - -#if defined(_UNICOS) -# define UNICOS 1 -#endif - -#ifndef POSIX -# if (BSD >= 199103) || defined(__linux) || defined(ultrix) || defined(AIX) ||\ - defined(HPUX) || defined(CONVEX) || defined(IRIX) -# define POSIX -# endif -#endif - -#ifndef BSD -# if defined(ultrix) -# define BSD 198902 -# endif -#endif - -/*****************************************************************/ - -#if (!defined(BSD) || (BSD < 198902)) && !defined(__linux) && \ - !defined(IRIX) && !defined(NeXT) && !defined(HPUX) -# define NEED_STRCASECMP -#endif - -#if (!defined(BSD) || (BSD < 198911)) && !defined(__linux) &&\ - !defined(IRIX) && !defined(UNICOS) && !defined(HPUX) -# define NEED_STRDUP -#endif - -#if (!defined(BSD) || (BSD < 198911)) && !defined(POSIX) && !defined(NeXT) -# define NEED_STRERROR -#endif - -#if defined(HPUX) || defined(AIX) || defined(UNIXPC) -# define NEED_FLOCK -#endif - -#ifndef POSIX -# define NEED_SETSID -#endif - -#if (defined(POSIX) && !defined(BSD)) && !defined(__linux) -# define NEED_GETDTABLESIZE -#endif - -#ifdef POSIX -#include -#ifdef _POSIX_SAVED_IDS -# define HAVE_SAVED_UIDS -#endif -#endif - -#if !defined(ATT) && !defined(__linux) && !defined(IRIX) && !defined(UNICOS) -# define USE_SIGCHLD -#endif - -#if !defined(AIX) && !defined(UNICOS) -# define SYS_TIME_H 1 -#else -# define SYS_TIME_H 0 -#endif - -#if defined(BSD) && !defined(POSIX) -# define USE_UTIMES -#endif - -#if defined(AIX) || defined(HPUX) || defined(IRIX) -# define NEED_SETENV -#endif - -#if !defined(UNICOS) && !defined(UNIXPC) -# define HAS_FCHOWN -#endif - -#if !defined(UNICOS) && !defined(UNIXPC) -# define HAS_FCHMOD -#endif diff --git a/usr.sbin/cron/cron/config.h b/usr.sbin/cron/cron/config.h index 6d77da27580d..ea30c8def194 100644 --- a/usr.sbin/cron/cron/config.h +++ b/usr.sbin/cron/cron/config.h @@ -1,87 +1,87 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ /* config.h - configurables for Vixie Cron * - * $FreeBSD$ + * $Id: config.h,v 1.2 1998/08/14 00:32:36 vixie Exp $ */ -#if !defined(_PATH_SENDMAIL) -# define _PATH_SENDMAIL "/usr/lib/sendmail" -#endif /*SENDMAIL*/ - /* * these are site-dependent */ #ifndef DEBUGGING #define DEBUGGING 1 /* 1 or 0 -- do you want debugging code built in? */ #endif /* - * choose one of these MAILCMD commands. I use + * choose one of these mailer commands. some use * /bin/mail for speed; it makes biff bark but doesn't - * do aliasing. /usr/lib/sendmail does aliasing but is + * do aliasing. sendmail does do aliasing but is * a hog for short messages. aliasing is not needed * if you make use of the MAILTO= feature in crontabs. * (hint: MAILTO= was added for this reason). */ -#define MAILCMD _PATH_SENDMAIL /*-*/ -#define MAILARGS "%s -FCronDaemon -odi -oem -oi -t" /*-*/ +#define MAILFMT "%s -FCronDaemon -odi -oem -oi -t" /*-*/ /* -Fx = set full-name of sender * -odi = Option Deliverymode Interactive * -oem = Option Errors Mailedtosender - * -oi = Option dot message terminator - * -t = read recipients from header of message + * -or0s = Option Readtimeout -- don't time out + * -t = Get recipient from headers */ +#define MAILARG _PATH_SENDMAIL /*-*/ -/* #define MAILCMD "/bin/mail" */ /*-*/ -/* #define MAILARGS "%s -d %s" */ /*-*/ +/* #define MAILFMT "%s -d %s" */ /*-*/ /* -d = undocumented but common flag: deliver locally? */ +/* #define MAILARG "/bin/mail",mailto */ -/* #define MAILCMD "/usr/mmdf/bin/submit" */ /*-*/ -/* #define MAILARGS "%s -mlrxto %s" */ /*-*/ +/* #define MAILFMT "%s -mlrxto %s" */ /*-*/ +/* #define MAILARG "/usr/mmdf/bin/submit",mailto */ /*-*/ /* #define MAIL_DATE */ /*-*/ /* should we include an ersatz Date: header in * generated mail? if you are using sendmail - * for MAILCMD, it is better to let sendmail + * as the mailer, it is better to let sendmail * generate the Date: header. */ /* if ALLOW_FILE and DENY_FILE are not defined or are * defined but neither exists, should crontab(1) be * usable only by root? */ -/* #define ALLOW_ONLY_ROOT */ /*-*/ +/*#define ALLOW_ONLY_ROOT */ /*-*/ /* if you want to use syslog(3) instead of appending * to CRONDIR/LOG_FILE (/var/cron/log, e.g.), define * SYSLOG here. Note that quite a bit of logging * info is written, and that you probably don't want * to use this on 4.2bsd since everything goes in * /usr/spool/mqueue/syslog. On 4.[34]bsd you can * tell /etc/syslog.conf to send cron's logging to * a separate file. * * Note that if this and LOG_FILE in "pathnames.h" * are both defined, then logging will go to both * places. */ #define SYSLOG /*-*/ diff --git a/usr.sbin/cron/cron/cron.8 b/usr.sbin/cron/cron/cron.8 index 15e930439564..782bbce2fb6e 100644 --- a/usr.sbin/cron/cron/cron.8 +++ b/usr.sbin/cron/cron/cron.8 @@ -1,236 +1,238 @@ -.\"/* Copyright 1988,1990,1993 by Paul Vixie +.\"/* Copyright 1988,1990,1993,1996 by Paul Vixie .\" * All rights reserved -.\" * -.\" * Distribute freely, except: don't remove my name from the source or -.\" * documentation (don't take credit for my work), mark your changes (don't -.\" * get me blamed for your possible bugs), don't alter or remove this -.\" * notice. May be sold if buildable source is provided to buyer. No -.\" * warrantee of any kind, express or implied, is included with this -.\" * software; use at your own risk, responsibility for damages (if any) to -.\" * anyone resulting from the use of this software rests entirely with the -.\" * user. -.\" * -.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and -.\" * I'll try to keep a version up to date. I can be reached as follows: -.\" * Paul Vixie uunet!decwrl!vixie!paul .\" */ .\" -.\" $FreeBSD$ +.\"Copyright (c) 1997 by Internet Software Consortium +.\" +.\"Permission to use, copy, modify, and distribute this software for any +.\"purpose with or without fee is hereby granted, provided that the above +.\"copyright notice and this permission notice appear in all copies. +.\" +.\"THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS +.\"ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +.\"OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE +.\"CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +.\"DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +.\"PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +.\"ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +.\"SOFTWARE. +.\" +.\" $Id: cron.8,v 1.2 1998/08/14 00:32:36 vixie Exp $ .\" .Dd February 9, 2022 .Dt CRON 8 .Os .Sh NAME .Nm cron .Nd daemon to execute scheduled commands (Vixie Cron) .Sh SYNOPSIS .Nm .Op Fl j Ar jitter .Op Fl J Ar rootjitter .Op Fl m Ar mailto .Op Fl n .Op Fl s .Op Fl o .Op Fl x Ar debugflag Ns Op , Ns Ar ... .Sh DESCRIPTION The .Nm utility should be started from .Pa /etc/rc or .Pa /etc/rc.local . It will return immediately, so you do not need to start it with '&'. .Pp The .Nm utility searches .Pa /var/cron/tabs for crontab files which are named after accounts in .Pa /etc/passwd ; crontabs found are loaded into memory. The .Nm utility also searches for .Pa /etc/crontab and files in .Pa /etc/cron.d and .Pa /usr/local/etc/cron.d which are in a different format (see .Xr crontab 5 ) . .Pp The .Nm utility then wakes up every minute, examining all stored crontabs, checking each command to see if it should be run in the current minute. Before running a command from a per-account crontab file, .Nm checks the status of the account with .Xr pam 3 and skips the command if the account is unavailable, e.g., locked out or expired. Commands from .Pa /etc/crontab bypass this check. When executing commands, any output is mailed to the owner of the crontab (or to the user named in the .Ev MAILTO environment variable in the crontab, if such exists). The from address of this mail may be set with the .Ev MAILFROM environment variable. .Pp Additionally, .Nm checks each minute to see if its spool directory's modification time (or the modification time on .Pa /etc/crontab ) has changed, and if it has, .Nm will then examine the modification time on all crontabs and reload those which have changed. Thus .Nm need not be restarted whenever a crontab file is modified. Note that the .Xr crontab 1 command updates the modification time of the spool directory whenever it changes a crontab. .Pp Available options: .Bl -tag -width indent .It Fl j Ar jitter Enable time jitter. Prior to executing commands, .Nm will sleep a random number of seconds in the range from 0 to .Ar jitter . This will not affect superuser jobs (see .Fl J ) . A value for .Ar jitter must be between 0 and 60 inclusive. Default is 0, which effectively disables time jitter. .Pp This option can help to smooth down system load spikes during moments when a lot of jobs are likely to start at once, e.g., at the beginning of the first minute of each hour. .It Fl J Ar rootjitter Enable time jitter for superuser jobs. The same as .Fl j except that it will affect jobs run by the superuser only. .It Fl m Ar mailto Overrides the default recipient for .Nm mail. Each .Xr crontab 5 without .Ev MAILTO explicitly set will send mail to the .Ar mailto mailbox. Sending mail will be disabled by default if .Ar mailto set to a null string, usually specified in a shell as .Li '' or .Li \*q\*q . .It Fl n Do not daemonize; run in foreground instead. .It Fl s Enable special handling of situations when the GMT offset of the local timezone changes, such as the switches between the standard time and daylight saving time. .Pp The jobs run during the GMT offset changes time as intuitively expected. If a job falls into a time interval that disappears (for example, during the switch from standard time) to daylight saving time or is duplicated (for example, during the reverse switch), then it is handled in one of two ways: .Pp The first case is for the jobs that run every at hour of a time interval overlapping with the disappearing or duplicated interval. In other words, if the job had run within one hour before the GMT offset change (and cron was not restarted nor the .Xr crontab 5 changed after that) or would run after the change at the next hour. They work as always, skip the skipped time or run in the added time as usual. .Pp The second case is for the jobs that run less frequently. They are executed exactly once, they are not skipped nor executed twice (unless cron is restarted or the user's .Xr crontab 5 is changed during such a time interval). If an interval disappears due to the GMT offset change, such jobs are executed at the same absolute point of time as they would be in the old time zone. For example, if exactly one hour disappears, this point would be during the next hour at the first minute that is specified for them in .Xr crontab 5 . .It Fl o Disable the special handling of situations when the GMT offset of the local timezone changes, to be compatible with the old (default) behavior. If both options .Fl o and .Fl s are specified, the option specified last wins. .It Fl x Ar debugflag Ns Op , Ns Ar ... Enable writing of debugging information to standard output. One or more of the following comma separated .Ar debugflag identifiers must be specified: .Pp .Bl -tag -width ".Cm proc" -compact .It Cm bit currently not used .It Cm ext make the other debug flags more verbose .It Cm load be verbose when loading crontab files .It Cm misc be verbose about miscellaneous one-off events .It Cm pars be verbose about parsing individual crontab lines .It Cm proc be verbose about the state of the process, including all of its offspring .It Cm sch be verbose when iterating through the scheduling algorithms .It Cm test trace through the execution, but do not perform any actions .El .El .Sh FILES .Bl -tag -width /usr/local/etc/cron.d -compact .It Pa /etc/crontab System crontab file .It Pa /etc/cron.d Directory for optional/modularized system crontab files. .It Pa /etc/pam.d/cron .Xr pam.conf 5 configuration file for .Nm .It Pa /usr/local/etc/cron.d Directory for third-party package provided crontab files. .It Pa /var/cron/tabs Directory for personal crontab files .El .Sh SEE ALSO .Xr crontab 1 , .Xr pam 3 , .Xr crontab 5 , .Xr pam.conf 5 , .Xr periodic 8 .Sh AUTHORS .An Paul Vixie Aq Mt paul@vix.com diff --git a/usr.sbin/cron/cron/cron.c b/usr.sbin/cron/cron/cron.c index 90bae65fd1ee..46ed984711a5 100644 --- a/usr.sbin/cron/cron/cron.c +++ b/usr.sbin/cron/cron/cron.c @@ -1,589 +1,568 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ #if !defined(lint) && !defined(LINT) static const char rcsid[] = - "$FreeBSD$"; + "$Id: cron.c,v 1.3 1998/08/14 00:32:36 vixie Exp $"; #endif #define MAIN_PROGRAM - #include "cron.h" #include -#include -#if SYS_TIME_H -# include -#else -# include -#endif - static void usage(void), run_reboot_jobs(cron_db *), cron_tick(cron_db *, int), cron_sync(int), cron_sleep(cron_db *, int), cron_clean(cron_db *), -#ifdef USE_SIGCHLD sigchld_handler(int), -#endif sighup_handler(int), parse_args(int c, char *v[]); static int run_at_secres(cron_db *); static void find_interval_entry(pid_t); static cron_db database; static time_t last_time = 0; static int dst_enabled = 0; static int dont_daemonize = 0; struct pidfh *pfh; static void usage(void) { #if DEBUGGING - char **dflags; + const char **dflags; #endif fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] " "[-m mailto] [-n] [-s] [-o] [-x debugflag[,...]]\n"); #if DEBUGGING fprintf(stderr, "\ndebugflags: "); - for(dflags = DebugFlagNames; *dflags; dflags++) { + for (dflags = DebugFlagNames; *dflags; dflags++) { fprintf(stderr, "%s ", *dflags); } fprintf(stderr, "\n"); #endif exit(ERROR_EXIT); } static void open_pidfile(void) { - char pidfile[MAX_FNAME]; + const char *pidfile = PIDDIR PIDFILE; char buf[MAX_TEMPSTR]; int otherpid; - (void) snprintf(pidfile, sizeof(pidfile), PIDFILE, PIDDIR); pfh = pidfile_open(pidfile, 0600, &otherpid); if (pfh == NULL) { if (errno == EEXIST) { snprintf(buf, sizeof(buf), "cron already running, pid: %d", otherpid); } else { snprintf(buf, sizeof(buf), "can't open or create %s: %s", pidfile, strerror(errno)); } log_it("CRON", getpid(), "DEATH", buf); errx(ERROR_EXIT, "%s", buf); } } int main(int argc, char *argv[]) { int runnum; int secres1, secres2; struct tm *tm; ProgramName = argv[0]; #if defined(BSD) setlinebuf(stdout); setlinebuf(stderr); #endif parse_args(argc, argv); -#ifdef USE_SIGCHLD (void) signal(SIGCHLD, sigchld_handler); -#else - (void) signal(SIGCLD, SIG_IGN); -#endif (void) signal(SIGHUP, sighup_handler); open_pidfile(); set_cron_uid(); set_cron_cwd(); -#if defined(POSIX) - setenv("PATH", _PATH_DEFPATH, 1); -#endif + putenv("PATH="_PATH_DEFPATH); /* if there are no debug flags turned on, fork as a daemon should. */ # if DEBUGGING if (DebugFlags) { # else if (0) { # endif (void) fprintf(stderr, "[%d] cron started\n", getpid()); } else if (dont_daemonize == 0) { if (daemon(1, 0) == -1) { pidfile_remove(pfh); log_it("CRON",getpid(),"DEATH","can't become daemon"); exit(0); } } if (madvise(NULL, 0, MADV_PROTECT) != 0) log_it("CRON", getpid(), "WARNING", "madvise() failed"); pidfile_write(pfh); database.head = NULL; database.tail = NULL; database.mtime = (time_t) 0; load_database(&database); secres1 = secres2 = run_at_secres(&database); cron_sync(secres1); run_reboot_jobs(&database); runnum = 0; while (TRUE) { # if DEBUGGING /* if (!(DebugFlags & DTEST)) */ # endif /*DEBUGGING*/ cron_sleep(&database, secres1); if (secres1 == 0 || runnum % 60 == 0) { load_database(&database); secres2 = run_at_secres(&database); if (secres2 != secres1) { secres1 = secres2; if (secres1 != 0) { runnum = 0; } else { /* * Going from 1 sec to 60 sec res. If we * are already at minute's boundary, so * let it run, otherwise schedule for the * next minute. */ tm = localtime(&TargetTime); if (tm->tm_sec > 0) { cron_sync(secres2); continue; } } } } /* do this iteration */ cron_tick(&database, secres1); /* sleep 1 or 60 seconds */ TargetTime += (secres1 != 0) ? 1 : 60; runnum += 1; } } - static void run_reboot_jobs(cron_db *db) { - register user *u; - register entry *e; + user *u; + entry *e; - for (u = db->head; u != NULL; u = u->next) { - for (e = u->crontab; e != NULL; e = e->next) { + for (u = db->head; u != NULL; u = u->next) { + for (e = u->crontab; e != NULL; e = e->next) { if (e->flags & WHEN_REBOOT) { job_add(e, u); } if (e->flags & INTERVAL) { e->lastexit = TargetTime; } } } (void) job_runqueue(); } - static void cron_tick(cron_db *db, int secres) { - static struct tm lasttm; - static time_t diff = 0, /* time difference in seconds from the last offset change */ - difflimit = 0; /* end point for the time zone correction */ - struct tm otztm; /* time in the old time zone */ - int otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow; - register struct tm *tm = localtime(&TargetTime); - register int second, minute, hour, dom, month, dow; - register user *u; - register entry *e; + static struct tm lasttm; + /* time difference in seconds from the last offset change */ + static time_t diff = 0; + /* end point for the time zone correction */ + static time_t difflimit = 0; + /* time in the old time zone */ + struct tm otztm; + int otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow; + struct tm *tm = localtime(&TargetTime); + int second, minute, hour, dom, month, dow; + user *u; + entry *e; /* make 0-based values out of these so we can use them as indices */ second = (secres == 0) ? 0 : tm->tm_sec -FIRST_SECOND; minute = tm->tm_min -FIRST_MINUTE; hour = tm->tm_hour -FIRST_HOUR; dom = tm->tm_mday -FIRST_DOM; month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; dow = tm->tm_wday -FIRST_DOW; Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n", getpid(), second, minute, hour, dom, month, dow)) if (dst_enabled && last_time != 0 && TargetTime > last_time /* exclude stepping back */ && tm->tm_gmtoff != lasttm.tm_gmtoff ) { diff = tm->tm_gmtoff - lasttm.tm_gmtoff; if ( diff > 0 ) { /* ST->DST */ /* mark jobs for an earlier run */ difflimit = TargetTime + diff; - for (u = db->head; u != NULL; u = u->next) { - for (e = u->crontab; e != NULL; e = e->next) { + for (u = db->head; u != NULL; u = u->next) { + for (e = u->crontab; e != NULL; e = e->next) { e->flags &= ~NOT_UNTIL; if ( e->lastrun >= TargetTime ) e->lastrun = 0; /* not include the ends of hourly ranges */ if ( e->lastrun < TargetTime - 3600 ) e->flags |= RUN_AT; else e->flags &= ~RUN_AT; } } } else { /* diff < 0 : DST->ST */ /* mark jobs for skipping */ difflimit = TargetTime - diff; - for (u = db->head; u != NULL; u = u->next) { - for (e = u->crontab; e != NULL; e = e->next) { + for (u = db->head; u != NULL; u = u->next) { + for (e = u->crontab; e != NULL; e = e->next) { e->flags |= NOT_UNTIL; e->flags &= ~RUN_AT; } } } } if (diff != 0) { /* if the time was reset of the end of special zone is reached */ if (last_time == 0 || TargetTime >= difflimit) { /* disable the TZ switch checks */ diff = 0; difflimit = 0; - for (u = db->head; u != NULL; u = u->next) { - for (e = u->crontab; e != NULL; e = e->next) { + for (u = db->head; u != NULL; u = u->next) { + for (e = u->crontab; e != NULL; e = e->next) { e->flags &= ~(RUN_AT|NOT_UNTIL); } } } else { /* get the time in the old time zone */ time_t difftime = TargetTime + tm->tm_gmtoff - diff; gmtime_r(&difftime, &otztm); /* make 0-based values out of these so we can use them as indices */ otzsecond = (secres == 0) ? 0 : otztm.tm_sec -FIRST_SECOND; otzminute = otztm.tm_min -FIRST_MINUTE; otzhour = otztm.tm_hour -FIRST_HOUR; otzdom = otztm.tm_mday -FIRST_DOM; otzmonth = otztm.tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; otzdow = otztm.tm_wday -FIRST_DOW; } } /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the * first and fifteenth AND every Sunday; '* * * * Sun' will run *only* * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre. * like many bizarre things, it's the standard. */ - for (u = db->head; u != NULL; u = u->next) { - for (e = u->crontab; e != NULL; e = e->next) { + for (u = db->head; u != NULL; u = u->next) { + for (e = u->crontab; e != NULL; e = e->next) { Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n", env_get("LOGNAME", e->envp), e->uid, e->gid, e->cmd)) if (e->flags & INTERVAL) { if (e->lastexit > 0 && TargetTime >= e->lastexit + e->interval) job_add(e, u); continue; } if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) { - if (bit_test(e->second, otzsecond) - && bit_test(e->minute, otzminute) - && bit_test(e->hour, otzhour) - && bit_test(e->month, otzmonth) - && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) + if (bit_test(e->second, otzsecond) && + bit_test(e->minute, otzminute) && + bit_test(e->hour, otzhour) && + bit_test(e->month, otzmonth) && + ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) ? (bit_test(e->dow,otzdow) && bit_test(e->dom,otzdom)) : (bit_test(e->dow,otzdow) || bit_test(e->dom,otzdom)) ) ) { if ( e->flags & RUN_AT ) { e->flags &= ~RUN_AT; e->lastrun = TargetTime; job_add(e, u); continue; } else e->flags &= ~NOT_UNTIL; } else if ( e->flags & NOT_UNTIL ) continue; } - if (bit_test(e->second, second) - && bit_test(e->minute, minute) - && bit_test(e->hour, hour) - && bit_test(e->month, month) - && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) + if (bit_test(e->second, second) && + bit_test(e->minute, minute) && + bit_test(e->hour, hour) && + bit_test(e->month, month) && + ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) ? (bit_test(e->dow,dow) && bit_test(e->dom,dom)) : (bit_test(e->dow,dow) || bit_test(e->dom,dom)) ) ) { e->flags &= ~RUN_AT; e->lastrun = TargetTime; job_add(e, u); } } } last_time = TargetTime; lasttm = *tm; } - /* the task here is to figure out how long it's going to be until :00 of the * following minute and initialize TargetTime to this value. TargetTime * will subsequently slide 60 seconds at a time, with correction applied * implicitly in cron_sleep(). it would be nice to let cron execute in * the "current minute" before going to sleep, but by restarting cron you * could then get it to execute a given minute's jobs more than once. * instead we have the chance of missing a minute's jobs completely, but * that's something sysadmin's know to expect what with crashing computers.. */ static void cron_sync(int secres) { - struct tm *tm; + struct tm *tm; TargetTime = time((time_t*)0); if (secres != 0) { TargetTime += 1; } else { tm = localtime(&TargetTime); TargetTime += (60 - tm->tm_sec); } } static void timespec_subtract(struct timespec *result, struct timespec *x, struct timespec *y) { *result = *x; result->tv_sec -= y->tv_sec; result->tv_nsec -= y->tv_nsec; if (result->tv_nsec < 0) { result->tv_sec--; result->tv_nsec += 1000000000; } } static void cron_sleep(cron_db *db, int secres) { int seconds_to_wait; int rval; struct timespec ctime, ttime, stime, remtime; /* * Loop until we reach the top of the next minute, sleep when possible. */ for (;;) { clock_gettime(CLOCK_REALTIME, &ctime); ttime.tv_sec = TargetTime; ttime.tv_nsec = 0; timespec_subtract(&stime, &ttime, &ctime); /* * If the seconds_to_wait value is insane, jump the cron */ if (stime.tv_sec < -600 || stime.tv_sec > 600) { cron_clean(db); cron_sync(secres); continue; } seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 : stime.tv_sec; Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n", getpid(), (long)TargetTime, seconds_to_wait)) /* * If we've run out of wait time or there are no jobs left * to run, break */ if (stime.tv_sec < 0) break; if (job_runqueue() == 0) { Debug(DSCH, ("[%d] sleeping for %d seconds\n", getpid(), seconds_to_wait)) for (;;) { rval = nanosleep(&stime, &remtime); if (rval == 0 || errno != EINTR) break; stime.tv_sec = remtime.tv_sec; stime.tv_nsec = remtime.tv_nsec; } } } } /* if the time was changed abruptly, clear the flags related * to the daylight time switch handling to avoid strange effects */ static void cron_clean(cron_db *db) { user *u; entry *e; last_time = 0; for (u = db->head; u != NULL; u = u->next) { for (e = u->crontab; e != NULL; e = e->next) { e->flags &= ~(RUN_AT|NOT_UNTIL); } } } -#ifdef USE_SIGCHLD static void sigchld_handler(int x) { - WAIT_T waiter; - PID_T pid; + WAIT_T waiter; + PID_T pid; for (;;) { -#ifdef POSIX pid = waitpid(-1, &waiter, WNOHANG); -#else - pid = wait3(&waiter, WNOHANG, (struct rusage *)0); -#endif switch (pid) { case -1: Debug(DPROC, ("[%d] sigchld...no children\n", getpid())) return; case 0: Debug(DPROC, ("[%d] sigchld...no dead kids\n", getpid())) return; default: find_interval_entry(pid); Debug(DPROC, ("[%d] sigchld...pid #%d died, stat=%d\n", getpid(), pid, WEXITSTATUS(waiter))) } } } -#endif /*USE_SIGCHLD*/ - static void sighup_handler(int x) { log_close(); } - static void parse_args(int argc, char *argv[]) { - int argch; - char *endp; + int argch; + char *endp; while ((argch = getopt(argc, argv, "j:J:m:nosx:")) != -1) { switch (argch) { case 'j': Jitter = strtoul(optarg, &endp, 10); if (*optarg == '\0' || *endp != '\0' || Jitter > 60) errx(ERROR_EXIT, "bad value for jitter: %s", optarg); break; case 'J': RootJitter = strtoul(optarg, &endp, 10); if (*optarg == '\0' || *endp != '\0' || RootJitter > 60) errx(ERROR_EXIT, "bad value for root jitter: %s", optarg); break; case 'm': defmailto = optarg; break; case 'n': dont_daemonize = 1; break; case 'o': dst_enabled = 0; break; case 's': dst_enabled = 1; break; case 'x': if (!set_debug_flags(optarg)) usage(); break; default: usage(); } } } static int run_at_secres(cron_db *db) { user *u; entry *e; for (u = db->head; u != NULL; u = u->next) { for (e = u->crontab; e != NULL; e = e->next) { if ((e->flags & (SEC_RES | INTERVAL)) != 0) return 1; } } return 0; } static void find_interval_entry(pid_t pid) { user *u; entry *e; for (u = database.head; u != NULL; u = u->next) { for (e = u->crontab; e != NULL; e = e->next) { if ((e->flags & INTERVAL) && e->child == pid) { e->lastexit = time(NULL); e->child = 0; break; } } } } diff --git a/usr.sbin/cron/cron/cron.h b/usr.sbin/cron/cron/cron.h index f0f9e88d6b59..67d91b57750e 100644 --- a/usr.sbin/cron/cron/cron.h +++ b/usr.sbin/cron/cron/cron.h @@ -1,318 +1,37 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ /* cron.h - header for vixie's cron * - * $FreeBSD$ + * $Id: cron.h,v 1.3 1998/08/14 00:32:37 vixie Exp $ * * vix 14nov88 [rest of log is in RCS] * vix 14jan87 [0 or 7 can be sunday; thanks, mwm@berkeley] * vix 30dec86 [written] */ -/* reorder these #include's at your peril */ - -#include -#include -#include "compat.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pathnames.h" #include "config.h" #include "externs.h" - - /* these are really immutable, and are - * defined for symbolic convenience only - * TRUE, FALSE, and ERR must be distinct - * ERR must be < OK. - */ -#define TRUE 1 -#define FALSE 0 - /* system calls return this on success */ -#define OK 0 - /* or this on error */ -#define ERR (-1) - - /* turn this on to get '-x' code */ -#ifndef DEBUGGING -#define DEBUGGING FALSE -#endif - -#define READ_PIPE 0 /* which end of a pipe pair do you read? */ -#define WRITE_PIPE 1 /* or write to? */ -#define STDIN 0 /* what is stdin's file descriptor? */ -#define STDOUT 1 /* stdout's? */ -#define STDERR 2 /* stderr's? */ -#define ERROR_EXIT 1 /* exit() with this will scare the shell */ -#define OK_EXIT 0 /* exit() with this is considered 'normal' */ -#define MAX_FNAME 100 /* max length of internally generated fn */ -#define MAX_COMMAND 1000 /* max length of internally generated cmd */ -#define MAX_ENVSTR 1000 /* max length of envvar=value\0 strings */ -#define MAX_TEMPSTR 100 /* obvious */ -#define ROOT_UID 0 /* don't change this, it really must be root */ -#define ROOT_USER "root" /* ditto */ -#define SYS_NAME "*system*" /* magic owner name for system crontab */ - - /* NOTE: these correspond to DebugFlagNames, - * defined below. - */ -#define DEXT 0x0001 /* extend flag for other debug masks */ -#define DSCH 0x0002 /* scheduling debug mask */ -#define DPROC 0x0004 /* process control debug mask */ -#define DPARS 0x0008 /* parsing debug mask */ -#define DLOAD 0x0010 /* database loading debug mask */ -#define DMISC 0x0020 /* misc debug mask */ -#define DTEST 0x0040 /* test mode: don't execute any commands */ -#define DBIT 0x0080 /* bit twiddling shown (long) */ - -#define CRON_TAB(u) "%s/%s", SPOOL_DIR, u -#define REG register -#define PPC_NULL ((char **)NULL) - -#ifndef MAXHOSTNAMELEN -#define MAXHOSTNAMELEN 256 -#endif - -#define Skip_Blanks(c, f) \ - while (c == '\t' || c == ' ') \ - c = get_char(f); - -#define Skip_Nonblanks(c, f) \ - while (c!='\t' && c!=' ' && c!='\n' && c != EOF) \ - c = get_char(f); - -#define Skip_Line(c, f) \ - do {c = get_char(f);} while (c != '\n' && c != EOF); - -#if DEBUGGING -# define Debug(mask, message) \ - if ( (DebugFlags & (mask) ) == (mask) ) \ - printf message; -#else /* !DEBUGGING */ -# define Debug(mask, message) \ - ; -#endif /* DEBUGGING */ - -#define MkLower(ch) (isupper(ch) ? tolower(ch) : ch) -#define MkUpper(ch) (islower(ch) ? toupper(ch) : ch) -#define Set_LineNum(ln) {Debug(DPARS|DEXT,("linenum=%d\n",ln)); \ - LineNumber = ln; \ - } - -#define FIRST_SECOND 0 -#define LAST_SECOND 59 -#define SECOND_COUNT (LAST_SECOND - FIRST_SECOND + 1) - -#define FIRST_MINUTE 0 -#define LAST_MINUTE 59 -#define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1) - -#define FIRST_HOUR 0 -#define LAST_HOUR 23 -#define HOUR_COUNT (LAST_HOUR - FIRST_HOUR + 1) - -#define FIRST_DOM 1 -#define LAST_DOM 31 -#define DOM_COUNT (LAST_DOM - FIRST_DOM + 1) - -#define FIRST_MONTH 1 -#define LAST_MONTH 12 -#define MONTH_COUNT (LAST_MONTH - FIRST_MONTH + 1) - -/* note on DOW: 0 and 7 are both Sunday, for compatibility reasons. */ -#define FIRST_DOW 0 -#define LAST_DOW 7 -#define DOW_COUNT (LAST_DOW - FIRST_DOW + 1) - -#ifdef LOGIN_CAP -/* see init.c */ -#define RESOURCE_RC "daemon" -#endif - - /* each user's crontab will be held as a list of - * the following structure. - * - * These are the cron commands. - */ - -typedef struct _entry { - struct _entry *next; - uid_t uid; - gid_t gid; -#ifdef LOGIN_CAP - char *class; -#endif - char **envp; - char *cmd; - union { - struct { - bitstr_t bit_decl(second, SECOND_COUNT); - bitstr_t bit_decl(minute, MINUTE_COUNT); - bitstr_t bit_decl(hour, HOUR_COUNT); - bitstr_t bit_decl(dom, DOM_COUNT); - bitstr_t bit_decl(month, MONTH_COUNT); - bitstr_t bit_decl(dow, DOW_COUNT); - }; - struct { - time_t lastexit; - time_t interval; - pid_t child; - }; - }; - int flags; -#define DOM_STAR 0x01 -#define DOW_STAR 0x02 -#define WHEN_REBOOT 0x04 -#define RUN_AT 0x08 -#define NOT_UNTIL 0x10 -#define SEC_RES 0x20 -#define INTERVAL 0x40 -#define DONT_LOG 0x80 -#define MAIL_WHEN_ERR 0x100 - time_t lastrun; -} entry; - - /* the crontab database will be a list of the - * following structure, one element per user - * plus one for the system. - * - * These are the crontabs. - */ - -typedef struct _user { - struct _user *next, *prev; /* links */ - char *name; - time_t mtime; /* last modtime of crontab */ - entry *crontab; /* this person's crontab */ -} user; - -typedef struct _cron_db { - user *head, *tail; /* links */ - time_t mtime; /* last modtime on spooldir */ -} cron_db; - - -void set_cron_uid(void), - set_cron_cwd(void), - load_database(cron_db *), - open_logfile(void), - sigpipe_func(void), - job_add(entry *, user *), - do_command(entry *, user *), - link_user(cron_db *, user *), - unlink_user(cron_db *, user *), - free_user(user *), - env_free(char **), - unget_char(int, FILE *), - free_entry(entry *), - skip_comments(FILE *), - log_it(char *, int, char *, const char *), - log_close(void); - -int job_runqueue(void), - set_debug_flags(char *), - get_char(FILE *), - get_string(char *, int, FILE *, char *), - swap_uids(void), - swap_uids_back(void), - load_env(char *, FILE *), - cron_pclose(FILE *), - strcmp_until(char *, char *, int), - allowed(char *), - strdtb(char *); - -char *env_get(char *, char **), - *arpadate(time_t *), - *mkprints(unsigned char *, unsigned int), - *first_word(char *, char *), - **env_init(void), - **env_copy(char **), - **env_set(char **, char *); - -user *load_user(int, struct passwd *, char *), - *find_user(cron_db *, char *); - -entry *load_entry(FILE *, void (*)(char *), - struct passwd *, char **); - -FILE *cron_popen(char *, char *, entry *, PID_T *); - - - /* in the C tradition, we only create - * variables for the main program, just - * extern them elsewhere. - */ - -#ifdef MAIN_PROGRAM -# if !defined(LINT) && !defined(lint) -char *copyright[] = { - "@(#) Copyright 1988,1989,1990,1993,1994 by Paul Vixie", - "@(#) All rights reserved" - }; -# endif - -char *MonthNames[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", - NULL - }; - -char *DowNames[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", - NULL - }; - -char *ProgramName, - *defmailto; -int LineNumber; -unsigned Jitter, - RootJitter; -time_t TargetTime; - -# if DEBUGGING -int DebugFlags; -char *DebugFlagNames[] = { /* sync with #defines */ - "ext", "sch", "proc", "pars", "load", "misc", "test", "bit", - NULL /* NULL must be last element */ - }; -# endif /* DEBUGGING */ -#else /*MAIN_PROGRAM*/ -extern char *copyright[], - *MonthNames[], - *DowNames[], - *ProgramName, - *defmailto; -extern int LineNumber; -extern unsigned Jitter, - RootJitter; -extern time_t TargetTime; -extern struct pidfh *pfh; -# if DEBUGGING -extern int DebugFlags; -extern char *DebugFlagNames[]; -# endif /* DEBUGGING */ -#endif /*MAIN_PROGRAM*/ +#include "pathnames.h" +#include "macros.h" +#include "structs.h" +#include "funcs.h" +#include "globals.h" diff --git a/usr.sbin/cron/cron/database.c b/usr.sbin/cron/cron/database.c index 80b749f8d955..35e5fad3524d 100644 --- a/usr.sbin/cron/cron/database.c +++ b/usr.sbin/cron/cron/database.c @@ -1,322 +1,316 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ #if !defined(lint) && !defined(LINT) static const char rcsid[] = - "$FreeBSD$"; + "$Id: database.c,v 1.3 1998/08/14 00:32:38 vixie Exp $"; #endif /* vix 26jan87 [RCS has the log] */ - #include "cron.h" -#include -#include -#include - #define TMAX(a,b) ((a)>(b)?(a):(b)) - -static void process_crontab(char *, char *, char *, - struct stat *, - cron_db *, cron_db *); - +static void process_crontab(const char *, const char *, + const char *, struct stat *, + cron_db *, cron_db *); void load_database(cron_db *old_db) { - DIR *dir; - struct stat statbuf; - struct stat syscron_stat, st; - time_t maxmtime; - DIR_T *dp; - cron_db new_db; - user *u, *nu; + struct stat statbuf, syscron_stat, st; + cron_db new_db; + DIR_T *dp; + DIR *dir; + user *u, *nu; + time_t maxmtime; struct { const char *name; struct stat st; } syscrontabs [] = { { SYSCRONTABS }, { LOCALSYSCRONTABS } }; int i, ret; Debug(DLOAD, ("[%d] load_database()\n", getpid())) /* before we start loading any data, do a stat on SPOOL_DIR * so that if anything changes as of this moment (i.e., before we've * cached any of the database), we'll see the changes next time. */ if (stat(SPOOL_DIR, &statbuf) < OK) { log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR); (void) exit(ERROR_EXIT); } /* track system crontab file */ if (stat(SYSCRONTAB, &syscron_stat) < OK) syscron_stat.st_mtime = 0; maxmtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime); for (i = 0; i < nitems(syscrontabs); i++) { if (stat(syscrontabs[i].name, &syscrontabs[i].st) != -1) { maxmtime = TMAX(syscrontabs[i].st.st_mtime, maxmtime); /* Traverse into directory */ if (!(dir = opendir(syscrontabs[i].name))) continue; while (NULL != (dp = readdir(dir))) { if (dp->d_name[0] == '.') continue; ret = fstatat(dirfd(dir), dp->d_name, &st, 0); if (ret != 0 || !S_ISREG(st.st_mode)) continue; maxmtime = TMAX(st.st_mtime, maxmtime); } closedir(dir); } else { syscrontabs[i].st.st_mtime = 0; } } /* if spooldir's mtime has not changed, we don't need to fiddle with * the database. * * Note that old_db->mtime is initialized to 0 in main(), and * so is guaranteed to be different than the stat() mtime the first * time this function is called. */ if (old_db->mtime == maxmtime) { Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n", getpid())) return; } /* something's different. make a new database, moving unchanged * elements from the old database, reloading elements that have * actually changed. Whatever is left in the old database when * we're done is chaff -- crontabs that disappeared. */ new_db.mtime = maxmtime; new_db.head = new_db.tail = NULL; if (syscron_stat.st_mtime) { process_crontab("root", SYS_NAME, SYSCRONTAB, &syscron_stat, &new_db, old_db); } for (i = 0; i < nitems(syscrontabs); i++) { char tabname[MAXPATHLEN]; if (syscrontabs[i].st.st_mtime == 0) continue; if (!(dir = opendir(syscrontabs[i].name))) { log_it("CRON", getpid(), "OPENDIR FAILED", syscrontabs[i].name); (void) exit(ERROR_EXIT); } while (NULL != (dp = readdir(dir))) { if (dp->d_name[0] == '.') continue; if (fstatat(dirfd(dir), dp->d_name, &st, 0) == 0 && !S_ISREG(st.st_mode)) continue; snprintf(tabname, sizeof(tabname), "%s/%s", syscrontabs[i].name, dp->d_name); process_crontab("root", SYS_NAME, tabname, &syscrontabs[i].st, &new_db, old_db); } closedir(dir); } /* we used to keep this dir open all the time, for the sake of * efficiency. however, we need to close it in every fork, and * we fork a lot more often than the mtime of the dir changes. */ if (!(dir = opendir(SPOOL_DIR))) { log_it("CRON", getpid(), "OPENDIR FAILED", SPOOL_DIR); (void) exit(ERROR_EXIT); } while (NULL != (dp = readdir(dir))) { - char fname[MAXNAMLEN+1], - tabname[MAXNAMLEN+1]; + 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; (void) strncpy(fname, dp->d_name, sizeof(fname)); fname[sizeof(fname)-1] = '\0'; - (void) snprintf(tabname, sizeof tabname, CRON_TAB(fname)); + + if (snprintf(tabname, sizeof tabname, CRON_TAB(fname)) + >= sizeof(tabname)) + continue; /* XXX log? */ process_crontab(fname, fname, tabname, &statbuf, &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). */ endpwent(); /* whatever's left in the old database is now junk. */ Debug(DLOAD, ("unlinking old database:\n")) for (u = old_db->head; u != NULL; u = nu) { Debug(DLOAD, ("\t%s\n", u->name)) nu = u->next; unlink_user(old_db, u); free_user(u); } /* overwrite the database control block with the new one. */ *old_db = new_db; Debug(DLOAD, ("load_database is done\n")) } - void link_user(cron_db *db, user *u) { if (db->head == NULL) db->head = u; if (db->tail) db->tail->next = u; u->prev = db->tail; u->next = NULL; db->tail = u; } - void unlink_user(cron_db *db, user *u) { if (u->prev == NULL) db->head = u->next; else u->prev->next = u->next; if (u->next == NULL) db->tail = u->prev; else u->next->prev = u->prev; } - user * -find_user(cron_db *db, char *name) +find_user(cron_db *db, const char *name) { - user *u; + user *u; for (u = db->head; u != NULL; u = u->next) - if (!strcmp(u->name, name)) + if (strcmp(u->name, name) == 0) break; - return u; + return (u); } - static void -process_crontab(char *uname, char *fname, char *tabname, struct stat *statbuf, - cron_db *new_db, cron_db *old_db) +process_crontab(const char *uname, const char *fname, const char *tabname, + struct stat *statbuf, cron_db *new_db, cron_db *old_db) { - struct passwd *pw = NULL; - int crontab_fd = OK - 1; - user *u; - entry *e; - time_t now; + struct passwd *pw = NULL; + int crontab_fd = OK - 1; + user *u; + entry *e; + time_t now; - if (strcmp(fname, SYS_NAME) && !(pw = getpwnam(uname))) { + if (strcmp(fname, SYS_NAME) != 0 && !(pw = getpwnam(uname))) { /* file doesn't have a user in passwd file. */ log_it(fname, getpid(), "ORPHAN", "no passwd entry"); goto next_crontab; } if ((crontab_fd = open(tabname, O_RDONLY, 0)) < OK) { /* crontab not accessible? */ log_it(fname, getpid(), "CAN'T OPEN", tabname); goto next_crontab; } if (fstat(crontab_fd, statbuf) < OK) { log_it(fname, getpid(), "FSTAT FAILED", tabname); goto next_crontab; } Debug(DLOAD, ("\t%s:", fname)) u = find_user(old_db, fname); if (u != NULL) { /* if crontab has not changed since we last read it * in, then we can just use our existing entry. */ if (u->mtime == statbuf->st_mtime) { Debug(DLOAD, (" [no change, using old data]")) unlink_user(old_db, u); link_user(new_db, u); goto next_crontab; } /* before we fall through to the code that will reload * the user, let's deallocate and unlink the user in * the old database. This is more a point of memory * efficiency than anything else, since all leftover * users will be deleted from the old database when * we finish with the crontab... */ Debug(DLOAD, (" [delete old data]")) unlink_user(old_db, u); free_user(u); log_it(fname, getpid(), "RELOAD", tabname); } u = load_user(crontab_fd, pw, fname); if (u != NULL) { u->mtime = statbuf->st_mtime; /* * TargetTime == 0 when we're initially populating the database, * and TargetTime > 0 any time after that (i.e. we're reloading * cron.d/ files because they've been created/modified). In the * latter case, we should check for any interval jobs and run * them 'n' seconds from the time the job was loaded/reloaded. * Otherwise, they will not be run until cron is restarted. */ if (TargetTime != 0) { now = time(NULL); for (e = u->crontab; e != NULL; e = e->next) { if ((e->flags & INTERVAL) != 0) e->lastexit = now; } } link_user(new_db, u); } -next_crontab: + next_crontab: if (crontab_fd >= OK) { Debug(DLOAD, (" [done]\n")) close(crontab_fd); } } diff --git a/usr.sbin/cron/cron/do_command.c b/usr.sbin/cron/cron/do_command.c index 13e147e5f3df..43b3269d3087 100644 --- a/usr.sbin/cron/cron/do_command.c +++ b/usr.sbin/cron/cron/do_command.c @@ -1,679 +1,667 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ #if !defined(lint) && !defined(LINT) static const char rcsid[] = - "$FreeBSD$"; + "$Id: do_command.c,v 1.3 1998/08/14 00:32:39 vixie Exp $"; #endif - #include "cron.h" -#include -#if defined(sequent) -# include -#endif -#if defined(SYSLOG) -# include -#endif #if defined(LOGIN_CAP) # include #endif #ifdef PAM # include # include #endif - static void child_process(entry *, user *); - static WAIT_T wait_on_child(PID_T, const char *); extern char *environ; void do_command(entry *e, user *u) { pid_t pid; Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n", getpid(), e->cmd, u->name, e->uid, e->gid)) /* fork to become asynchronous -- parent process is done immediately, * and continues to run the normal cron code, which means return to * tick(). the child and grandchild don't leave this function, alive. */ switch ((pid = fork())) { case -1: - log_it("CRON",getpid(),"error","can't fork"); + log_it("CRON", getpid(), "error", "can't fork"); if (e->flags & INTERVAL) e->lastexit = time(NULL); break; case 0: /* child process */ pidfile_close(pfh); child_process(e, u); Debug(DPROC, ("[%d] child process done, exiting\n", getpid())) _exit(OK_EXIT); break; default: /* parent process */ Debug(DPROC, ("[%d] main process forked child #%d, " "returning to work\n", getpid(), pid)) if (e->flags & INTERVAL) { e->lastexit = 0; e->child = pid; } break; } Debug(DPROC, ("[%d] main process returning to work\n", getpid())) } static void child_process(entry *e, user *u) { - int stdin_pipe[2], stdout_pipe[2]; - register char *input_data; - char *usernm, *mailto, *mailfrom; - PID_T jobpid, stdinjob, mailpid; - register FILE *mail; - register int bytes = 1; - int status = 0; - const char *homedir = NULL; + int stdin_pipe[2], stdout_pipe[2]; + char *input_data; + const char *usernm, *mailto, *mailfrom; + PID_T jobpid, stdinjob, mailpid; + FILE *mail; + int bytes = 1; + int status = 0; + const char *homedir = NULL; # if defined(LOGIN_CAP) - struct passwd *pwd; + struct passwd *pwd; login_cap_t *lc; # endif Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd)) /* mark ourselves as different to PS command watchers by upshifting * our program name. This has no effect on some kernels. */ setproctitle("running job"); /* discover some useful and important environment settings */ usernm = env_get("LOGNAME", e->envp); mailto = env_get("MAILTO", e->envp); mailfrom = env_get("MAILFROM", e->envp); #ifdef PAM /* use PAM to see if the user's account is available, * i.e., not locked or expired or whatever. skip this * for system tasks from /etc/crontab -- they can run * as any user. */ if (strcmp(u->name, SYS_NAME)) { /* not equal */ pam_handle_t *pamh = NULL; int pam_err; struct pam_conv pamc = { .conv = openpam_nullconv, .appdata_ptr = NULL }; Debug(DPROC, ("[%d] checking account with PAM\n", getpid())) /* u->name keeps crontab owner name while LOGNAME is the name * of user to run command on behalf of. they should be the * same for a task from a per-user crontab. */ if (strcmp(u->name, usernm)) { log_it(usernm, getpid(), "username ambiguity", u->name); exit(ERROR_EXIT); } pam_err = pam_start("cron", usernm, &pamc, &pamh); if (pam_err != PAM_SUCCESS) { log_it("CRON", getpid(), "error", "can't start PAM"); exit(ERROR_EXIT); } pam_err = pam_acct_mgmt(pamh, PAM_SILENT); /* Expired password shouldn't prevent the job from running. */ if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD) { log_it(usernm, getpid(), "USER", "account unavailable"); exit(ERROR_EXIT); } pam_end(pamh, pam_err); } #endif -#ifdef USE_SIGCHLD /* our parent is watching for our death by catching SIGCHLD. we * do not care to watch for our children's deaths this way -- we * use wait() explicitly. so we have to disable the signal (which * was inherited from the parent). */ (void) signal(SIGCHLD, SIG_DFL); -#else - /* on system-V systems, we are ignoring SIGCLD. we have to stop - * ignoring it now or the wait() in cron_pclose() won't work. - * because of this, we have to wait() for our children here, as well. - */ - (void) signal(SIGCLD, SIG_DFL); -#endif /*BSD*/ /* create some pipes to talk to our future child */ if (pipe(stdin_pipe) != 0 || pipe(stdout_pipe) != 0) { log_it("CRON", getpid(), "error", "can't pipe"); exit(ERROR_EXIT); } /* since we are a forked process, we can diddle the command string * we were passed -- nobody else is going to use it again, right? * * if a % is present in the command, previous characters are the * command, and subsequent characters are the additional input to * the command. Subsequent %'s will be transformed into newlines, * but that happens later. * * If there are escaped %'s, remove the escape character. */ /*local*/{ - register int escaped = FALSE; - register int ch; - register char *p; + int escaped = FALSE; + int ch; + char *p; - for (input_data = p = e->cmd; (ch = *input_data); + for (input_data = p = e->cmd; + (ch = *input_data) != '\0'; input_data++, p++) { if (p != input_data) - *p = ch; + *p = ch; if (escaped) { if (ch == '%' || ch == '\\') *--p = ch; escaped = FALSE; continue; } if (ch == '\\') { escaped = TRUE; continue; } if (ch == '%') { *input_data++ = '\0'; break; } } *p = '\0'; } /* fork again, this time so we can exec the user's command. */ switch (jobpid = fork()) { case -1: - log_it("CRON",getpid(),"error","can't fork"); + log_it("CRON", getpid(), "error", "can't fork"); exit(ERROR_EXIT); /*NOTREACHED*/ case 0: Debug(DPROC, ("[%d] grandchild process fork()'ed\n", getpid())) if (e->uid == ROOT_UID) Jitter = RootJitter; if (Jitter != 0) { srandom(getpid()); sleep(random() % Jitter); } /* write a log message. we've waited this long to do it * because it was not until now that we knew the PID that * the actual user command shell was going to get and the * PID is part of the log message. */ if ((e->flags & DONT_LOG) == 0) { char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); log_it(usernm, getpid(), "CMD", x); free(x); } /* that's the last thing we'll log. close the log files. */ #ifdef SYSLOG closelog(); #endif /* get new pgrp, void tty, etc. */ (void) setsid(); /* close the pipe ends that we won't use. this doesn't affect * the parent, who has to read and write them; it keeps the * kernel from recording us as a potential client TWICE -- * which would keep it from sending SIGPIPE in otherwise * appropriate circumstances. */ close(stdin_pipe[WRITE_PIPE]); close(stdout_pipe[READ_PIPE]); /* grandchild process. make std{in,out} be the ends of * pipes opened by our daddy; make stderr go to stdout. */ close(STDIN); dup2(stdin_pipe[READ_PIPE], STDIN); close(STDOUT); dup2(stdout_pipe[WRITE_PIPE], STDOUT); close(STDERR); dup2(STDOUT, STDERR); /* close the pipes we just dup'ed. The resources will remain. */ close(stdin_pipe[READ_PIPE]); close(stdout_pipe[WRITE_PIPE]); environ = NULL; # if defined(LOGIN_CAP) /* Set user's entire context, but note that PATH will * be overridden later */ if ((pwd = getpwnam(usernm)) == NULL) pwd = getpwuid(e->uid); lc = NULL; if (pwd != NULL) { if (pwd->pw_dir != NULL && pwd->pw_dir[0] != '\0') { homedir = strdup(pwd->pw_dir); if (homedir == NULL) { warn("strdup"); _exit(ERROR_EXIT); } } pwd->pw_gid = e->gid; if (e->class != NULL) lc = login_getclass(e->class); } if (pwd && setusercontext(lc, pwd, e->uid, LOGIN_SETALL) == 0) (void) endpwent(); else { /* fall back to the old method */ (void) endpwent(); # endif /* set our directory, uid and gid. Set gid first, * since once we set uid, we've lost root privileges. */ if (setgid(e->gid) != 0) { log_it(usernm, getpid(), "error", "setgid failed"); _exit(ERROR_EXIT); } -# if defined(BSD) if (initgroups(usernm, e->gid) != 0) { log_it(usernm, getpid(), "error", "initgroups failed"); _exit(ERROR_EXIT); } -# endif if (setlogin(usernm) != 0) { log_it(usernm, getpid(), "error", "setlogin failed"); _exit(ERROR_EXIT); } if (setuid(e->uid) != 0) { log_it(usernm, getpid(), "error", "setuid failed"); _exit(ERROR_EXIT); } /* we aren't root after this..*/ #if defined(LOGIN_CAP) } if (lc != NULL) login_close(lc); #endif /* For compatibility, we chdir to the value of HOME if it was * specified explicitly in the crontab file, but not if it was * set in the environment by some other mechanism. We chdir to * the homedir given by the pw entry otherwise. * * If !LOGIN_CAP, then HOME is always set in e->envp. * * XXX: probably should also consult PAM. */ { char *new_home = env_get("HOME", e->envp); if (new_home != NULL && new_home[0] != '\0') chdir(new_home); else if (homedir != NULL) chdir(homedir); else chdir("/"); } /* exec the command. Note that SHELL is not respected from * either login.conf or pw_shell, only an explicit setting * in the crontab. (default of _PATH_BSHELL is supplied when * setting up the entry) */ { char *shell = env_get("SHELL", e->envp); char **p; /* Apply the environment from the entry, overriding * existing values (this will always set LOGNAME and * SHELL). putenv should not fail unless malloc does. */ for (p = e->envp; *p; ++p) { if (putenv(*p) != 0) { warn("putenv"); _exit(ERROR_EXIT); } } /* HOME in login.conf overrides pw, and HOME in the * crontab overrides both. So set pw's value only if * nothing was already set (overwrite==0). */ if (homedir != NULL && setenv("HOME", homedir, 0) < 0) { warn("setenv(HOME)"); _exit(ERROR_EXIT); } /* PATH in login.conf is respected, but the crontab * overrides; set a default value only if nothing * already set. */ if (setenv("PATH", _PATH_DEFPATH, 0) < 0) { warn("setenv(PATH)"); _exit(ERROR_EXIT); } # if DEBUGGING if (DebugFlags & DTEST) { fprintf(stderr, "debug DTEST is on, not exec'ing command.\n"); fprintf(stderr, "\tcmd='%s' shell='%s'\n", e->cmd, shell); _exit(OK_EXIT); } # endif /*DEBUGGING*/ execl(shell, shell, "-c", e->cmd, (char *)NULL); warn("execl: couldn't exec `%s'", shell); _exit(ERROR_EXIT); } break; default: /* parent process */ break; } /* middle process, child of original cron, parent of process running * the user's command. */ Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid())) /* close the ends of the pipe that will only be referenced in the * grandchild process... */ close(stdin_pipe[READ_PIPE]); close(stdout_pipe[WRITE_PIPE]); /* * write, to the pipe connected to child's stdin, any input specified * after a % in the crontab entry. while we copy, convert any * additional %'s to newlines. when done, if some characters were * written and the last one wasn't a newline, write a newline. * * Note that if the input data won't fit into one pipe buffer (2K * or 4K on most BSD systems), and the child doesn't read its stdin, * we would block here. thus we must fork again. */ if (*input_data && (stdinjob = fork()) == 0) { - register FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); - register int need_newline = FALSE; - register int escaped = FALSE; - register int ch; + FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); + int need_newline = FALSE; + int escaped = FALSE; + int ch; if (out == NULL) { warn("fdopen failed in child2"); _exit(ERROR_EXIT); } Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid())) /* close the pipe we don't use, since we inherited it and * are part of its reference count now. */ close(stdout_pipe[READ_PIPE]); /* translation: * \% -> % * % -> \n * \x -> \x for all x != % */ - while ((ch = *input_data++)) { + while ((ch = *input_data++) != '\0') { if (escaped) { if (ch != '%') putc('\\', out); } else { if (ch == '%') ch = '\n'; } if (!(escaped = (ch == '\\'))) { putc(ch, out); need_newline = (ch != '\n'); } } if (escaped) putc('\\', out); if (need_newline) putc('\n', out); /* close the pipe, causing an EOF condition. fclose causes * stdin_pipe[WRITE_PIPE] to be closed, too. */ fclose(out); Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid())) exit(0); } /* close the pipe to the grandkiddie's stdin, since its wicked uncle * ernie back there has it open and will close it when he's done. */ close(stdin_pipe[WRITE_PIPE]); /* * read output from the grandchild. it's stderr has been redirected to * it's stdout, which has been redirected to our pipe. if there is any * output, we'll be mailing it to the user whose crontab this is... * when the grandchild exits, we'll get EOF. */ Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid())) /*local*/{ - register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); - register int ch; + FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); + int ch; if (in == NULL) { warn("fdopen failed in child"); _exit(ERROR_EXIT); } mail = NULL; ch = getc(in); if (ch != EOF) { Debug(DPROC|DEXT, ("[%d] got data (%x:%c) from grandchild\n", getpid(), ch, ch)) /* get name of recipient. this is MAILTO if set to a * valid local username; USER otherwise. */ if (mailto == NULL) { /* MAILTO not present, set to USER, * unless globally overridden. */ if (defmailto) mailto = defmailto; else mailto = usernm; } if (mailto && *mailto == '\0') mailto = NULL; /* if we are supposed to be mailing, MAILTO will * be non-NULL. only in this case should we set * up the mail command and subjects and stuff... */ if (mailto) { - register char **env; - auto char mailcmd[MAX_COMMAND]; - auto char hostname[MAXHOSTNAMELEN]; + char **env; + char mailcmd[MAX_COMMAND]; + char hostname[MAXHOSTNAMELEN]; if (gethostname(hostname, MAXHOSTNAMELEN) == -1) hostname[0] = '\0'; hostname[sizeof(hostname) - 1] = '\0'; - (void) snprintf(mailcmd, sizeof(mailcmd), - MAILARGS, MAILCMD); + if (snprintf(mailcmd, sizeof(mailcmd), MAILFMT, + MAILARG) >= sizeof(mailcmd)) { + warnx("mail command too long"); + (void) _exit(ERROR_EXIT); + } if (!(mail = cron_popen(mailcmd, "w", e, &mailpid))) { - warn("%s", MAILCMD); + warn("%s", mailcmd); (void) _exit(ERROR_EXIT); } if (mailfrom == NULL || *mailfrom == '\0') fprintf(mail, "From: Cron Daemon <%s@%s>\n", usernm, hostname); else fprintf(mail, "From: Cron Daemon <%s>\n", mailfrom); fprintf(mail, "To: %s\n", mailto); fprintf(mail, "Subject: Cron <%s@%s> %s\n", usernm, first_word(hostname, "."), e->cmd); -# if defined(MAIL_DATE) +#ifdef MAIL_DATE fprintf(mail, "Date: %s\n", arpadate(&TargetTime)); -# endif /* MAIL_DATE */ +#endif /*MAIL_DATE*/ for (env = e->envp; *env; env++) fprintf(mail, "X-Cron-Env: <%s>\n", *env); fprintf(mail, "\n"); /* this was the first char from the pipe */ putc(ch, mail); } /* we have to read the input pipe no matter whether * we mail or not, but obviously we only write to * mail pipe if we ARE mailing. */ while (EOF != (ch = getc(in))) { bytes++; if (mail) putc(ch, mail); } } /*if data from grandchild*/ Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid())) /* also closes stdout_pipe[READ_PIPE] */ fclose(in); } /* wait for children to die. */ if (jobpid > 0) { - WAIT_T waiter; + WAIT_T waiter; waiter = wait_on_child(jobpid, "grandchild command job"); /* If everything went well, and -n was set, _and_ we have mail, * we won't be mailing... so shoot the messenger! */ if (WIFEXITED(waiter) && WEXITSTATUS(waiter) == 0 && (e->flags & MAIL_WHEN_ERR) == MAIL_WHEN_ERR && mail) { Debug(DPROC, ("[%d] %s executed successfully, mail suppressed\n", getpid(), "grandchild command job")) kill(mailpid, SIGKILL); (void)fclose(mail); mail = NULL; } - /* only close pipe if we opened it -- i.e., we're * mailing... */ if (mail) { Debug(DPROC, ("[%d] closing pipe to mail\n", getpid())) /* Note: the pclose will probably see * the termination of the grandchild * in addition to the mail process, since * it (the grandchild) is likely to exit * after closing its stdout. */ status = cron_pclose(mail); /* if there was output and we could not mail it, * log the facts so the poor user can figure out * what's going on. */ if (status) { char buf[MAX_TEMPSTR]; snprintf(buf, sizeof(buf), "mailed %d byte%s of output but got status 0x%04x\n", bytes, (bytes==1)?"":"s", status); log_it(usernm, getpid(), "MAIL", buf); } } } if (*input_data && stdinjob > 0) wait_on_child(stdinjob, "grandchild stdinjob"); } static WAIT_T -wait_on_child(PID_T childpid, const char *name) { - WAIT_T waiter; - PID_T pid; +wait_on_child(PID_T childpid, const char *name) +{ + WAIT_T waiter; + PID_T pid; Debug(DPROC, ("[%d] waiting for %s (%d) to finish\n", getpid(), name, childpid)) #ifdef POSIX while ((pid = waitpid(childpid, &waiter, 0)) < 0 && errno == EINTR) #else while ((pid = wait4(childpid, &waiter, 0, NULL)) < 0 && errno == EINTR) #endif ; if (pid < OK) return waiter; Debug(DPROC, ("[%d] %s (%d) finished, status=%04x", getpid(), name, pid, WEXITSTATUS(waiter))) if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) Debug(DPROC, (", dumped core")) Debug(DPROC, ("\n")) return waiter; } diff --git a/usr.sbin/cron/cron/externs.h b/usr.sbin/cron/cron/externs.h index 8b1ff71b36e2..343848240f4f 100644 --- a/usr.sbin/cron/cron/externs.h +++ b/usr.sbin/cron/cron/externs.h @@ -1,143 +1,128 @@ -/* $FreeBSD$ */ - /* Copyright 1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ -#if defined(POSIX) || defined(ATT) -# include -# include -# include -# include -# define DIR_T struct dirent -# define WAIT_T int -# define WAIT_IS_INT 1 +/* reorder these #include's at your peril */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(SYSLOG) +# include +#endif + +#if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX) +# include +#endif /*BSD*/ + +#if !defined(_PATH_SENDMAIL) +# define _PATH_SENDMAIL "/usr/lib/sendmail" +#endif /*SENDMAIL*/ + +#if defined(__bsdi__) && (_BSDI_VERSION > 199510) +#include +#endif /* __bsdi__ */ + +#define DIR_T struct dirent +#define WAIT_T int +#define SIG_T sig_t +#define TIME_T time_t +#define PID_T pid_t + +#ifndef TZNAME_ALREADY_DEFINED extern char *tzname[2]; -# define TZONE(tm) tzname[(tm).tm_isdst] #endif +#define TZONE(tm) tzname[(tm).tm_isdst] -#if defined(UNIXPC) -# undef WAIT_T -# undef WAIT_IS_INT -# define WAIT_T union wait +#if (BSD >= 198606) +# define HAVE_FCHOWN +# define HAVE_FCHMOD #endif -#if defined(POSIX) -# define SIG_T sig_t -# define TIME_T time_t -# define PID_T pid_t +#if (BSD >= 199103) +# define HAVE_SAVED_UIDS #endif -#if defined(ATT) -# define SIG_T void -# define TIME_T long -# define PID_T int -#endif +#define MY_UID(pw) getuid() +#define MY_GID(pw) getgid() -#if !defined(POSIX) && !defined(ATT) -/* classic BSD */ -extern time_t time(); -extern unsigned sleep(); -extern struct tm *localtime(); -extern struct passwd *getpwnam(); -extern int errno; -extern void perror(), exit(), free(); -extern char *getenv(), *strcpy(), *strchr(), *strtok(); -extern void *malloc(), *realloc(); -# define SIG_T void -# define TIME_T long -# define PID_T int -# define WAIT_T union wait -# define DIR_T struct direct -# include -# define TZONE(tm) (tm).tm_zone +#if !defined(AIX) && !defined(UNICOS) +# define SYS_TIME_H 1 +#else +# define SYS_TIME_H 0 #endif /* getopt() isn't part of POSIX. some systems define it in anyway. * of those that do, some complain that our definition is different and some * do not. to add to the misery and confusion, some systems define getopt() * in ways that we cannot predict or comprehend, yet do not define the adjunct * external variables needed for the interface. */ -#if (!defined(BSD) || (BSD < 198911)) && !defined(ATT) && !defined(UNICOS) +#if (!defined(BSD) || (BSD < 198911)) int getopt(int, char * const *, const char *); #endif #if (!defined(BSD) || (BSD < 199103)) extern char *optarg; extern int optind, opterr, optopt; #endif -#if WAIT_IS_INT -# ifndef WEXITSTATUS -# define WEXITSTATUS(x) (((x) >> 8) & 0xff) -# endif -# ifndef WTERMSIG -# define WTERMSIG(x) ((x) & 0x7f) -# endif -# ifndef WCOREDUMP -# define WCOREDUMP(x) ((x) & 0x80) -# endif -#else /*WAIT_IS_INT*/ -# ifndef WEXITSTATUS -# define WEXITSTATUS(x) ((x).w_retcode) -# endif -# ifndef WTERMSIG -# define WTERMSIG(x) ((x).w_termsig) -# endif -# ifndef WCOREDUMP -# define WCOREDUMP(x) ((x).w_coredump) -# endif -#endif /*WAIT_IS_INT*/ - -#ifndef WIFSIGNALED -#define WIFSIGNALED(x) (WTERMSIG(x) != 0) -#endif -#ifndef WIFEXITED -#define WIFEXITED(x) (WTERMSIG(x) == 0) -#endif - -#ifdef NEED_STRCASECMP -extern int strcasecmp(char *, char *); -#endif - -#ifdef NEED_STRDUP -extern char *strdup(char *); -#endif - -#ifdef NEED_STRERROR -extern char *strerror(int); -#endif - -#ifdef NEED_FLOCK +/* digital unix needs this but does not give us a way to identify it. + */ extern int flock(int, int); + +/* not all systems who provice flock() provide these definitions. + */ +#ifndef LOCK_SH # define LOCK_SH 1 -# define LOCK_EX 2 -# define LOCK_NB 4 -# define LOCK_UN 8 #endif - -#ifdef NEED_SETSID -extern int setsid(void); +#ifndef LOCK_EX +# define LOCK_EX 2 #endif - -#ifdef NEED_GETDTABLESIZE -extern int getdtablesize(void); +#ifndef LOCK_NB +# define LOCK_NB 4 #endif - -#ifdef NEED_SETENV -extern int setenv(char *, char *, int); +#ifndef LOCK_UN +# define LOCK_UN 8 #endif diff --git a/usr.sbin/cron/cron/funcs.h b/usr.sbin/cron/cron/funcs.h new file mode 100644 index 000000000000..8a29513a0b31 --- /dev/null +++ b/usr.sbin/cron/cron/funcs.h @@ -0,0 +1,70 @@ +/* + * $Id: funcs.h,v 1.1 1998/08/14 00:31:24 vixie Exp $ + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* Notes: + * This file has to be included by cron.h after data structure defs. + * We should reorg this into sections by module. + */ + +void set_cron_uid(void), + set_cron_cwd(void), + load_database(cron_db *), + open_logfile(void), + sigpipe_func(void), + job_add(entry *, user *), + do_command(entry *, user *), + link_user(cron_db *, user *), + unlink_user(cron_db *, user *), + free_user(user *), + env_free(char **), + unget_char(int, FILE *), + free_entry(entry *), + skip_comments(FILE *), + log_it(const char *, int, const char *, const char *), + log_close(void); + +int job_runqueue(void), + set_debug_flags(char *), + get_char(FILE *), + get_string(char *, int, FILE *, char *), + swap_uids(void), + swap_uids_back(void), + load_env(char *, FILE *), + cron_pclose(FILE *), + strcmp_until(const char *, const char *, int), + allowed(char *), + strdtb(char *); + +char *env_get(char *, char **), + *arpadate(time_t *), + *mkprints(unsigned char *, unsigned int), + *first_word(char *, char *), + **env_init(void), + **env_copy(char **), + **env_set(char **, char *); + +user *load_user(int, struct passwd *, const char *), + *find_user(cron_db *, const char *); + +entry *load_entry(FILE *, void (*)(const char *), + struct passwd *, char **); + +FILE *cron_popen(char *, char *, entry *, PID_T *); diff --git a/usr.sbin/cron/cron/globals.h b/usr.sbin/cron/cron/globals.h new file mode 100644 index 000000000000..3d1797905590 --- /dev/null +++ b/usr.sbin/cron/cron/globals.h @@ -0,0 +1,78 @@ +/* + * $Id: globals.h,v 1.1 1998/08/14 00:31:23 vixie Exp $ + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifdef MAIN_PROGRAM +# define XTRN +# define INIT(x) = x +#else +# define XTRN extern +# define INIT(x) +#endif + +XTRN const char *copyright[] +#ifdef MAIN_PROGRAM + = { + "@(#) Copyright 1988,1989,1990,1993,1994 by Paul Vixie", + "@(#) Copyright 1997 by Internet Software Consortium", + "@(#) All rights reserved", + NULL + } +#endif + ; + +XTRN const char *MonthNames[] +#ifdef MAIN_PROGRAM + = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun",\ + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",\ + NULL + } +#endif + ; + +XTRN const char *DowNames[] +#ifdef MAIN_PROGRAM + = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",\ + NULL + } +#endif + ; + +XTRN const char *ProgramName INIT("amnesia"); +XTRN const char *defmailto; +XTRN int LineNumber INIT(0); +XTRN unsigned Jitter; +XTRN unsigned RootJitter; +XTRN time_t TargetTime INIT(0); +XTRN struct pidfh *pfh; + +#if DEBUGGING +XTRN int DebugFlags INIT(0); +XTRN const char *DebugFlagNames[] +#ifdef MAIN_PROGRAM + = { + "ext", "sch", "proc", "pars", "load", "misc", "test", "bit",\ + NULL + } +#endif + ; +#endif /* DEBUGGING */ diff --git a/usr.sbin/cron/cron/job.c b/usr.sbin/cron/cron/job.c index 7ba7643c2c65..c2f6e993225c 100644 --- a/usr.sbin/cron/cron/job.c +++ b/usr.sbin/cron/cron/job.c @@ -1,74 +1,80 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ #if !defined(lint) && !defined(LINT) -static const char rcsid[] = - "$FreeBSD$"; +static const char rcsid[] = "$Id: job.c,v 1.2 1998/08/14 00:32:40 vixie Exp $"; #endif #include "cron.h" typedef struct _job { struct _job *next; entry *e; user *u; } job; static job *jhead = NULL, *jtail = NULL; void job_add(entry *e, user *u) { - register job *j; + job *j; /* if already on queue, keep going */ - for (j=jhead; j; j=j->next) - if (j->e == e && j->u == u) { return; } + for (j = jhead; j != NULL; j = j->next) + if (j->e == e && j->u == u) + return; /* build a job queue element */ if ((j = (job*)malloc(sizeof(job))) == NULL) return; j->next = (job*) NULL; j->e = e; j->u = u; /* add it to the tail */ - if (!jhead) { jhead=j; } - else { jtail->next=j; } + if (jhead == NULL) + jhead = j; + else + jtail->next = j; jtail = j; } int job_runqueue(void) { - register job *j, *jn; - register int run = 0; + job *j, *jn; + int run = 0; - for (j=jhead; j; j=jn) { + for (j = jhead; j; j = jn) { do_command(j->e, j->u); jn = j->next; free(j); run++; } jhead = jtail = NULL; - return run; + return (run); } diff --git a/usr.sbin/cron/cron/macros.h b/usr.sbin/cron/cron/macros.h new file mode 100644 index 000000000000..6ce680818b31 --- /dev/null +++ b/usr.sbin/cron/cron/macros.h @@ -0,0 +1,134 @@ +/* + * $Id: macros.h,v 1.1 1998/08/14 00:31:24 vixie Exp $ + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + + /* these are really immutable, and are + * defined for symbolic convenience only + * TRUE, FALSE, and ERR must be distinct + * ERR must be < OK. + */ +#define TRUE 1 +#define FALSE 0 + /* system calls return this on success */ +#define OK 0 + /* or this on error */ +#define ERR (-1) + + /* turn this on to get '-x' code */ +#ifndef DEBUGGING +#define DEBUGGING FALSE +#endif + +#define READ_PIPE 0 /* which end of a pipe pair do you read? */ +#define WRITE_PIPE 1 /* or write to? */ +#define STDIN 0 /* what is stdin's file descriptor? */ +#define STDOUT 1 /* stdout's? */ +#define STDERR 2 /* stderr's? */ +#define ERROR_EXIT 1 /* exit() with this will scare the shell */ +#define OK_EXIT 0 /* exit() with this is considered 'normal' */ +#define MAX_FNAME 100 /* max length of internally generated fn */ +#define MAX_COMMAND 1000 /* max length of internally generated cmd */ +#define MAX_ENVSTR 1000 /* max length of envvar=value\0 strings */ +#define MAX_TEMPSTR 100 /* obvious */ +#define ROOT_UID 0 /* don't change this, it really must be root */ +#define ROOT_USER "root" /* ditto */ +#define SYS_NAME "*system*" /* magic owner name for system crontab */ + + /* NOTE: these correspond to DebugFlagNames, + * defined below. + */ +#define DEXT 0x0001 /* extend flag for other debug masks */ +#define DSCH 0x0002 /* scheduling debug mask */ +#define DPROC 0x0004 /* process control debug mask */ +#define DPARS 0x0008 /* parsing debug mask */ +#define DLOAD 0x0010 /* database loading debug mask */ +#define DMISC 0x0020 /* misc debug mask */ +#define DTEST 0x0040 /* test mode: don't execute any commands */ +#define DBIT 0x0080 /* bit twiddling shown (long) */ + +#define CRON_TAB(u) "%s/%s", SPOOL_DIR, u +#define PPC_NULL ((const char **)NULL) + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + +#define Skip_Blanks(c, f) \ + while (c == '\t' || c == ' ') \ + c = get_char(f); + +#define Skip_Nonblanks(c, f) \ + while (c!='\t' && c!=' ' && c!='\n' && c != EOF) \ + c = get_char(f); + +#define Skip_Line(c, f) \ + do {c = get_char(f);} while (c != '\n' && c != EOF); + +#if DEBUGGING +# define Debug(mask, message) \ + if ( (DebugFlags & (mask) ) == (mask) ) \ + printf message; +#else /* !DEBUGGING */ +# define Debug(mask, message) \ + ; +#endif /* DEBUGGING */ + +#define MkLower(ch) (isupper(ch) ? tolower(ch) : ch) +#define MkUpper(ch) (islower(ch) ? toupper(ch) : ch) +#define Set_LineNum(ln) {Debug(DPARS|DEXT,("linenum=%d\n",ln)); \ + LineNumber = ln; \ + } + +#define FIRST_SECOND 0 +#define LAST_SECOND 59 +#define SECOND_COUNT (LAST_SECOND - FIRST_SECOND + 1) + +#define FIRST_MINUTE 0 +#define LAST_MINUTE 59 +#define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1) + +#define FIRST_HOUR 0 +#define LAST_HOUR 23 +#define HOUR_COUNT (LAST_HOUR - FIRST_HOUR + 1) + +#define FIRST_DOM 1 +#define LAST_DOM 31 +#define DOM_COUNT (LAST_DOM - FIRST_DOM + 1) + +#define FIRST_MONTH 1 +#define LAST_MONTH 12 +#define MONTH_COUNT (LAST_MONTH - FIRST_MONTH + 1) + +/* note on DOW: 0 and 7 are both Sunday, for compatibility reasons. */ +#define FIRST_DOW 0 +#define LAST_DOW 7 +#define DOW_COUNT (LAST_DOW - FIRST_DOW + 1) + +#ifdef LOGIN_CAP +/* see init.c */ +#define RESOURCE_RC "daemon" +#endif + + /* each user's crontab will be held as a list of + * the following structure. + * + * These are the cron commands. + */ + diff --git a/usr.sbin/cron/cron/pathnames.h b/usr.sbin/cron/cron/pathnames.h index b0edbd897a28..2906d8728692 100644 --- a/usr.sbin/cron/cron/pathnames.h +++ b/usr.sbin/cron/cron/pathnames.h @@ -1,67 +1,69 @@ /* Copyright 1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ /* - * $FreeBSD$ + * $Id: pathnames.h,v 1.4 1998/08/14 00:32:41 vixie Exp $ */ -#if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX) -# include -#endif /*BSD*/ - #ifndef CRONDIR /* CRONDIR is where crond(8) and crontab(1) both chdir * to; SPOOL_DIR, ALLOW_FILE, DENY_FILE, and LOG_FILE * are all relative to this directory. */ #define CRONDIR "/var/cron" #endif /* SPOOLDIR is where the crontabs live. * This directory will have its modtime updated * whenever crontab(1) changes a crontab; this is * the signal for crond(8) to look at each individual * crontab file and reload those whose modtimes are * newer than they were last time around (or which * didn't exist last time around...) */ #define SPOOL_DIR "tabs" /* undefining these turns off their features. note * that ALLOW_FILE and DENY_FILE must both be defined * in order to enable the allow/deny code. If neither * LOG_FILE or SYSLOG is defined, we don't log. If - * both are defined, we log both ways. + * both are defined, we log both ways. Note that if + * LOG_CRON is defined by , LOG_FILE will not + * be used. */ -#define ALLOW_FILE "allow" /*-*/ -#define DENY_FILE "deny" /*-*/ -/*#define LOG_FILE "log"*/ /*-*/ +#define ALLOW_FILE "allow" +#define DENY_FILE "deny" +#define LOG_FILE "log" /* where should the daemon stick its PID? */ #define PIDDIR _PATH_VARRUN -#define PIDFILE "%scron.pid" +#define PIDFILE "cron.pid" /* 4.3BSD-style crontab */ #define SYSCRONTAB "/etc/crontab" #define SYSCRONTABS "/etc/cron.d" #define LOCALSYSCRONTABS _PATH_LOCALBASE "/etc/cron.d" /* what editor to use if no EDITOR or VISUAL * environment variable specified. */ #define EDITOR _PATH_VI diff --git a/usr.sbin/cron/cron/popen.c b/usr.sbin/cron/cron/popen.c index ee8323c49873..dfa90252fe22 100644 --- a/usr.sbin/cron/cron/popen.c +++ b/usr.sbin/cron/cron/popen.c @@ -1,246 +1,239 @@ /* * Copyright (c) 1988 The Regents of the University of California. * All rights reserved. * * This code is derived from software written by Ken Arnold and * published in UNIX Review, Vol. 6, No. 8. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * */ /* this came out of the ftpd sources; it's been modified to avoid the * globbing stuff since we don't need it. also execvp instead of execv. */ #ifndef lint +static const char rcsid[] = + "$Id: popen.c,v 1.3 1998/08/14 00:32:41 vixie Exp $"; #if 0 -static char sccsid[] = "@(#)popen.c 5.7 (Berkeley) 2/14/89"; +static const char sccsid[] = "@(#)popen.c 5.7 (Berkeley) 2/14/89"; #endif -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include "cron.h" -#include -#include -#include -#if defined(SYSLOG) -# include -#endif #if defined(LOGIN_CAP) # include #endif - #define MAX_ARGS 100 #define WANT_GLOBBING 0 /* * Special version of popen which avoids call to shell. This insures no one * may create a pipe to a hidden program as a side effect of a list or dir * command. */ static PID_T *pids; static int fds; FILE * cron_popen(char *program, char *type, entry *e, PID_T *pidptr) { - register char *cp; + char *cp; FILE *iop; int argc, pdes[2]; PID_T pid; char *usernm; char *argv[MAX_ARGS + 1]; # if defined(LOGIN_CAP) struct passwd *pwd; login_cap_t *lc; # endif #if WANT_GLOBBING char **pop, *vv[2]; int gargc; char *gargv[1000]; extern char **glob(), **copyblk(); #endif - if ((*type != 'r' && *type != 'w') || type[1]) - return(NULL); + if ((*type != 'r' && *type != 'w') || type[1] != '\0') + return (NULL); if (!pids) { - if ((fds = getdtablesize()) <= 0) - return(NULL); + if ((fds = sysconf(_SC_OPEN_MAX)) <= 0) + return (NULL); if (!(pids = calloc(fds, sizeof(PID_T)))) - return(NULL); + return (NULL); } if (pipe(pdes) < 0) - return(NULL); + return (NULL); /* break up string into pieces */ for (argc = 0, cp = program; argc < MAX_ARGS; cp = NULL) if (!(argv[argc++] = strtok(cp, " \t\n"))) break; argv[MAX_ARGS] = NULL; #if WANT_GLOBBING /* glob each piece */ gargv[0] = argv[0]; for (gargc = argc = 1; argv[argc]; argc++) { if (!(pop = glob(argv[argc]))) { /* globbing failed */ vv[0] = argv[argc]; vv[1] = NULL; pop = copyblk(vv); } argv[argc] = (char *)pop; /* save to free later */ while (*pop && gargc < 1000) gargv[gargc++] = *pop++; } gargv[gargc] = NULL; #endif iop = NULL; switch(pid = fork()) { case -1: /* error */ (void)close(pdes[0]); (void)close(pdes[1]); goto pfree; /* NOTREACHED */ case 0: /* child */ if (e != NULL) { #ifdef SYSLOG closelog(); #endif /* get new pgrp, void tty, etc. */ (void) setsid(); } if (*type == 'r') { /* Do not share our parent's stdin */ (void)close(0); (void)open(_PATH_DEVNULL, O_RDWR); if (pdes[1] != 1) { dup2(pdes[1], 1); dup2(pdes[1], 2); /* stderr, too! */ (void)close(pdes[1]); } (void)close(pdes[0]); } else { if (pdes[0] != 0) { dup2(pdes[0], 0); (void)close(pdes[0]); } /* Hack: stdout gets revoked */ (void)close(1); (void)open(_PATH_DEVNULL, O_RDWR); (void)close(2); (void)open(_PATH_DEVNULL, O_RDWR); (void)close(pdes[1]); } if (e != NULL) { /* Set user's entire context, but skip the environment * as cron provides a separate interface for this */ usernm = env_get("LOGNAME", e->envp); # if defined(LOGIN_CAP) if ((pwd = getpwnam(usernm)) == NULL) pwd = getpwuid(e->uid); lc = NULL; if (pwd != NULL) { pwd->pw_gid = e->gid; if (e->class != NULL) lc = login_getclass(e->class); } if (pwd && setusercontext(lc, pwd, e->uid, LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)) == 0) (void) endpwent(); else { /* fall back to the old method */ (void) endpwent(); # endif /* * Set our directory, uid and gid. Set gid * first since once we set uid, we've lost * root privileges. */ if (setgid(e->gid) != 0) _exit(ERROR_EXIT); # if defined(BSD) if (initgroups(usernm, e->gid) != 0) _exit(ERROR_EXIT); # endif if (setlogin(usernm) != 0) _exit(ERROR_EXIT); if (setuid(e->uid) != 0) _exit(ERROR_EXIT); /* we aren't root after this..*/ #if defined(LOGIN_CAP) } if (lc != NULL) login_close(lc); #endif chdir(env_get("HOME", e->envp)); } #if WANT_GLOBBING execvp(gargv[0], gargv); #else execvp(argv[0], argv); #endif _exit(1); } /* parent; assume fdopen can't fail... */ if (*type == 'r') { iop = fdopen(pdes[0], type); (void)close(pdes[1]); } else { iop = fdopen(pdes[1], type); (void)close(pdes[0]); } pids[fileno(iop)] = pid; pfree: #if WANT_GLOBBING for (argc = 1; argv[argc] != NULL; argc++) { /* blkfree((char **)argv[argc]); */ free((char *)argv[argc]); } #endif *pidptr = pid; - return(iop); + return (iop); } int cron_pclose(FILE *iop) { - register int fdes; + int fdes; int omask; WAIT_T stat_loc; PID_T pid; /* * pclose returns -1 if stream is not associated with a * `popened' command, or, if already `pclosed'. */ if (pids == 0 || pids[fdes = fileno(iop)] == 0) - return(-1); + return (-1); (void)fclose(iop); omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1) ; (void)sigsetmask(omask); pids[fdes] = 0; return (pid == -1 ? -1 : WEXITSTATUS(stat_loc)); } diff --git a/usr.sbin/cron/cron/structs.h b/usr.sbin/cron/cron/structs.h new file mode 100644 index 000000000000..c9644e95064d --- /dev/null +++ b/usr.sbin/cron/cron/structs.h @@ -0,0 +1,81 @@ +/* + * $Id: structs.h,v 1.1 1998/08/14 00:31:24 vixie Exp $ + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +typedef struct _entry { + struct _entry *next; + uid_t uid; + gid_t gid; +#ifdef LOGIN_CAP + char *class; +#endif + char **envp; + char *cmd; + union { + struct { + bitstr_t bit_decl(second, SECOND_COUNT); + bitstr_t bit_decl(minute, MINUTE_COUNT); + bitstr_t bit_decl(hour, HOUR_COUNT); + bitstr_t bit_decl(dom, DOM_COUNT); + bitstr_t bit_decl(month, MONTH_COUNT); + bitstr_t bit_decl(dow, DOW_COUNT); + }; + struct { + time_t lastexit; + time_t interval; + pid_t child; + }; + }; + int flags; +#define DOM_STAR 0x01 +#define DOW_STAR 0x02 +#define WHEN_REBOOT 0x04 +#define DONT_LOG 0x08 +#define NOT_UNTIL 0x10 +#define SEC_RES 0x20 +#define INTERVAL 0x40 +#define RUN_AT 0x80 +#define MAIL_WHEN_ERR 0x100 + time_t lastrun; +} entry; + + /* the crontab database will be a list of the + * following structure, one element per user + * plus one for the system. + * + * These are the crontabs. + */ + +typedef struct _user { + struct _user *next, *prev; /* links */ + char *name; + time_t mtime; /* last modtime of crontab */ + entry *crontab; /* this person's crontab */ +} user; + +typedef struct _cron_db { + user *head, *tail; /* links */ + time_t mtime; /* last modtime on spooldir */ +} cron_db; + /* in the C tradition, we only create + * variables for the main program, just + * extern them elsewhere. + */ + diff --git a/usr.sbin/cron/cron/user.c b/usr.sbin/cron/cron/user.c index ce8e08fccf20..01fc6a01529a 100644 --- a/usr.sbin/cron/cron/user.c +++ b/usr.sbin/cron/cron/user.c @@ -1,124 +1,126 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ #if !defined(lint) && !defined(LINT) -static const char rcsid[] = - "$FreeBSD$"; +static const char rcsid[] = "$Id: user.c,v 1.2 1998/08/14 00:32:41 vixie Exp $"; #endif /* vix 26jan87 [log is in RCS file] */ - #include "cron.h" static char *User_name; void free_user(user *u) { - entry *e, *ne; + entry *e, *ne; free(u->name); for (e = u->crontab; e != NULL; e = ne) { ne = e->next; free_entry(e); } free(u); } static void -log_error(char *msg) +log_error(const char *msg) { log_it(User_name, getpid(), "PARSE", msg); } /* NULL pw implies syscrontab */ user * -load_user(int crontab_fd, struct passwd *pw, char *name) +load_user(int crontab_fd, struct passwd *pw, const char *name) { - char envstr[MAX_ENVSTR]; - FILE *file; - user *u; - entry *e; - int status; - char **envp, **tenvp; + char envstr[MAX_ENVSTR]; + FILE *file; + user *u; + entry *e; + int status; + char **envp, **tenvp; if (!(file = fdopen(crontab_fd, "r"))) { warn("fdopen on crontab_fd in load_user"); - return NULL; + return (NULL); } Debug(DPARS, ("load_user()\n")) /* file is open. build user entry, then read the crontab file. */ if ((u = (user *) malloc(sizeof(user))) == NULL) { errno = ENOMEM; - return NULL; + return (NULL); } if ((u->name = strdup(name)) == NULL) { free(u); errno = ENOMEM; - return NULL; + return (NULL); } u->crontab = NULL; /* * init environment. this will be copied/augmented for each entry. */ if ((envp = env_init()) == NULL) { free(u->name); free(u); - return NULL; + return (NULL); } /* * load the crontab */ while ((status = load_env(envstr, file)) >= OK) { switch (status) { case ERR: free_user(u); u = NULL; goto done; case FALSE: User_name = u->name; /* for log_error */ e = load_entry(file, log_error, pw, envp); if (e) { e->next = u->crontab; u->crontab = e; } break; case TRUE: if ((tenvp = env_set(envp, envstr))) { envp = tenvp; } else { free_user(u); u = NULL; goto done; } break; } } done: env_free(envp); fclose(file); Debug(DPARS, ("...load_user() done\n")) - return u; + return (u); } diff --git a/usr.sbin/cron/crontab/crontab.1 b/usr.sbin/cron/crontab/crontab.1 index f4792bf78cc4..00e188c0e87c 100644 --- a/usr.sbin/cron/crontab/crontab.1 +++ b/usr.sbin/cron/crontab/crontab.1 @@ -1,151 +1,153 @@ .\"/* Copyright 1988,1990,1993 by Paul Vixie .\" * All rights reserved -.\" * -.\" * Distribute freely, except: don't remove my name from the source or -.\" * documentation (don't take credit for my work), mark your changes (don't -.\" * get me blamed for your possible bugs), don't alter or remove this -.\" * notice. May be sold if buildable source is provided to buyer. No -.\" * warrantee of any kind, express or implied, is included with this -.\" * software; use at your own risk, responsibility for damages (if any) to -.\" * anyone resulting from the use of this software rests entirely with the -.\" * user. -.\" * -.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and -.\" * I'll try to keep a version up to date. I can be reached as follows: -.\" * Paul Vixie uunet!decwrl!vixie!paul .\" */ .\" -.\" $FreeBSD$ +.\"Copyright (c) 1997 by Internet Software Consortium +.\" +.\"Permission to use, copy, modify, and distribute this software for any +.\"purpose with or without fee is hereby granted, provided that the above +.\"copyright notice and this permission notice appear in all copies. +.\" +.\"THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS +.\"ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +.\"OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE +.\"CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +.\"DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +.\"PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +.\"ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +.\"SOFTWARE. +.\" +.\" $Id: crontab.1,v 1.2 1998/08/14 00:32:37 vixie Exp $ .\" .Dd December 20, 2016 .Dt CRONTAB 1 .Os .Sh NAME .Nm crontab .Nd maintain crontab files for individual users (V3) .Sh SYNOPSIS .Nm .Op Fl u Ar user .Ar file .Nm .Op Fl u Ar user { .Fl l | .Fl r Op Fl f | .Fl e } .Sh DESCRIPTION The .Nm utility is the program used to install, deinstall or list the tables used to drive the .Xr cron 8 daemon in Vixie Cron. Each user can have their own crontab, and though these are files in .Pa /var , they are not intended to be edited directly. .Pp If the .Pa allow file exists, then you must be listed therein in order to be allowed to use this command. If the .Pa allow file does not exist but the .Pa deny file does exist, then you must .Em not be listed in the .Pa deny file in order to use this command. If neither of these files exists, then depending on site-dependent configuration parameters, only the super user will be allowed to use this command, or all users will be able to use this command. The format of these files is one username per line, with no leading or trailing whitespace. Lines of other formats will be ignored, and so can be used for comments. .Pp The first form of this command is used to install a new crontab from some named file or standard input if the pseudo-filename .Sq Fl is given. .Pp The following options are available: .Bl -tag -width indent .It Fl u Specify the name of the user whose crontab is to be tweaked. If this option is not given, .Nm examines .Dq your crontab, i.e., the crontab of the person executing the command. Note that .Xr su 1 can confuse .Nm and that if you are running inside of .Xr su 1 you should always use the .Fl u option for safety's sake. .It Fl l Display the current crontab on standard output. .It Fl r Remove the current crontab. By default the .Fl r option prompts for confirmation, adding the .Fl f option will attempt to remove the current crontab without confirmation. .It Fl e Edit the current crontab using the editor specified by the .Ev VISUAL or .Ev EDITOR environment variables. The specified editor .Em must edit the file in place; any editor that unlinks the file and recreates it cannot be used. After you exit from the editor, the modified crontab will be installed automatically. .El .Sh FILES .Bl -tag -width /var/cron/allow -compact .It Pa /var/cron/allow List of users allowed to use crontab .It Pa /var/cron/deny List of users prohibited from using crontab .It Pa /var/cron/tabs Directory for personal crontab files .El .Sh DIAGNOSTICS A fairly informative usage message appears if you run it with a bad command line. .Sh SEE ALSO .Xr crontab 5 , .Xr cron 8 .Sh STANDARDS The .Nm command conforms to .St -p1003.2 with the exception that the dangerous variant of calling .Nm without a file name in the first form of the command is not allowed by this implementation. The pseudo-filename .Sq Fl must be specified to read from standard input. The new command syntax differs from previous versions of Vixie Cron, as well as from the classic SVR3 syntax. .Sh AUTHORS .An Paul Vixie Aq Mt paul@vix.com diff --git a/usr.sbin/cron/crontab/crontab.5 b/usr.sbin/cron/crontab/crontab.5 index 0e48a0d804f4..12caa45cff46 100644 --- a/usr.sbin/cron/crontab/crontab.5 +++ b/usr.sbin/cron/crontab/crontab.5 @@ -1,381 +1,383 @@ .\"/* Copyright 1988,1990,1993,1994 by Paul Vixie .\" * All rights reserved -.\" * -.\" * Distribute freely, except: don't remove my name from the source or -.\" * documentation (don't take credit for my work), mark your changes (don't -.\" * get me blamed for your possible bugs), don't alter or remove this -.\" * notice. May be sold if buildable source is provided to buyer. No -.\" * warrantee of any kind, express or implied, is included with this -.\" * software; use at your own risk, responsibility for damages (if any) to -.\" * anyone resulting from the use of this software rests entirely with the -.\" * user. -.\" * -.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and -.\" * I'll try to keep a version up to date. I can be reached as follows: -.\" * Paul Vixie uunet!decwrl!vixie!paul .\" */ .\" -.\" $FreeBSD$ +.\"Copyright (c) 1997 by Internet Software Consortium +.\" +.\"Permission to use, copy, modify, and distribute this software for any +.\"purpose with or without fee is hereby granted, provided that the above +.\"copyright notice and this permission notice appear in all copies. +.\" +.\"THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS +.\"ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +.\"OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE +.\"CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +.\"DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +.\"PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +.\"ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +.\"SOFTWARE. +.\" +.\" $Id: crontab.5,v 1.2 1998/08/14 00:32:38 vixie Exp $ .\" .Dd March 29, 2020 .Dt CRONTAB 5 .Os .Sh NAME .Nm crontab .Nd tables for driving cron .Sh DESCRIPTION A .Nm file contains instructions to the .Xr cron 8 daemon of the general form: ``run this command at this time on this date''. Each user has their own crontab, and commands in any given crontab will be executed as the user who owns the crontab. Uucp and News will usually have their own crontabs, eliminating the need for explicitly running .Xr su 1 as part of a cron command. .Pp Blank lines and leading spaces and tabs are ignored. Lines whose first non-space character is a pound-sign (#) are comments, and are ignored. Note that comments are not allowed on the same line as cron commands, since they will be taken to be part of the command. Similarly, comments are not allowed on the same line as environment variable settings. .Pp An active line in a crontab will be either an environment setting or a cron command. An environment setting is of the form, .Bd -literal name = value .Ed .Pp where the spaces around the equal-sign (=) are optional, and any subsequent non-leading spaces in .Em value will be part of the value assigned to .Em name . The .Em value string may be placed in quotes (single or double, but matching) to preserve leading or trailing blanks. The .Em name string may also be placed in quote (single or double, but matching) to preserve leading, trailing or inner blanks. .Pp Several environment variables are set up automatically by the .Xr cron 8 daemon. .Ev SHELL is set to .Pa /bin/sh , and .Ev LOGNAME and .Ev HOME are set from the .Pa /etc/passwd line of the crontab's owner. In addition, the environment variables of the user's login class will be set from .Pa /etc/login.conf.db and .Pa ~/.login_conf . (A setting of .Ev HOME in the login class will override the value from .Pa /etc/passwd , but will not change the current directory when the command is invoked, which can only be overridden with an explicit setting of .Ev HOME within the crontab file itself.) If .Ev PATH is not set by any other means, it is defaulted to .Pa /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin . .Ev HOME , .Ev PATH and .Ev SHELL , and any variables set from the login class, may be overridden by settings in the crontab; .Ev LOGNAME may not. .Pp (Another note: the .Ev LOGNAME variable is sometimes called .Ev USER on .Bx systems... On these systems, .Ev USER will be set also). .Pp If .Xr cron 8 has any reason to send mail as a result of running commands in ``this'' crontab, it will respect the following settings which may be defined in the crontab (but which are not taken from the login class). If .Ev MAILTO is defined (and non-empty), mail is sent to the user so named. If .Ev MAILFROM is defined (and non-empty), its value will be used as the from address. .Ev MAILTO may also be used to direct mail to multiple recipients by separating recipient users with a comma. If .Ev MAILTO is defined but empty (MAILTO=""), no mail will be sent. Otherwise mail is sent to the owner of the crontab. This option is useful if you decide on .Pa /bin/mail instead of .Pa /usr/lib/sendmail as your mailer when you install cron -- .Pa /bin/mail does not do aliasing, and UUCP usually does not read its mail. .Pp The format of a cron command is very much the V7 standard, with a number of upward-compatible extensions. Each line has five time and date fields, followed by a user name (with optional ``:'' and ``/'' suffixes) if this is the system crontab file, followed by a command. Commands are executed by .Xr cron 8 when the minute, hour, and month of year fields match the current time, .Em and when at least one of the two day fields (day of month, or day of week) matches the current time (see ``Note'' below). .Xr cron 8 examines cron entries once every minute. The time and date fields are: .Bd -literal -offset indent field allowed values ----- -------------- minute 0-59 hour 0-23 day of month 1-31 month 1-12 (or names, see below) day of week 0-7 (0 or 7 is Sun, or use names) .Ed .Pp A field may be an asterisk (*), which always stands for ``first\-last''. .Pp Ranges of numbers are allowed. Ranges are two numbers separated with a hyphen. The specified range is inclusive. For example, 8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10 and 11. .Pp Lists are allowed. A list is a set of numbers (or ranges) separated by commas. Examples: ``1,2,5,9'', ``0-4,8-12''. .Pp Step values can be used in conjunction with ranges. Following a range with ``/'' specifies skips of the number's value through the range. For example, ``0-23/2'' can be used in the hours field to specify command execution every other hour (the alternative in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). Steps are also permitted after an asterisk, so if you want to say ``every two hours'', just use ``*/2''. .Pp Names can also be used for the ``month'' and ``day of week'' fields. Use the first three letters of the particular day or month (case does not matter). Ranges and lists are also allowed. .Pp The ``sixth'' field (the rest of the line) specifies the command to be run. One or more command options may precede the command to modify processing behavior. The entire command portion of the line, up to a newline or % character, will be executed by .Pa /bin/sh or by the shell specified in the .Ev SHELL variable of the cronfile. Percent-signs (%) in the command, unless escaped with backslash (\\), will be changed into newline characters, and all data after the first % will be sent to the command as standard input. .Pp The following command options can be supplied: .Bl -tag -width Ds .It Fl n No mail is sent after a successful run. The execution output will only be mailed if the command exits with a non-zero exit code. The .Fl n option is an attempt to cure potentially copious volumes of mail coming from .Xr cron 8 . .It Fl q Execution will not be logged. .El .sp Duplicate options are not allowed. .Pp Note: The day of a command's execution can be specified by two fields \(em day of month, and day of week. If both fields are restricted (ie, are not *), the command will be run when .Em either field matches the current time. For example, ``30 4 1,15 * 5'' would cause a command to be run at 4:30 am on the 1st and 15th of each month, plus every Friday. .Pp Instead of the first five fields, a line may start with .Sq @ symbol followed either by one of eight special strings or by a numeric value. The recognized special strings are: .Bd -literal -offset indent string meaning ------ ------- @reboot Run once, at startup of cron. @yearly Run once a year, "0 0 1 1 *". @annually (same as @yearly) @monthly Run once a month, "0 0 1 * *". @weekly Run once a week, "0 0 * * 0". @daily Run once a day, "0 0 * * *". @midnight (same as @daily) @hourly Run once an hour, "0 * * * *". @every_minute Run once a minute, "*/1 * * * *". @every_second Run once a second. .Ed .Pp The .Sq @ symbol followed by a numeric value has a special notion of running a job that many seconds after completion of the previous invocation of the job. Unlike regular syntax, it guarantees not to overlap two or more invocations of the same job during normal cron execution. Note, however, that overlap may occur if the job is running when the file containing the job is modified and subsequently reloaded. The first run is scheduled for the specified number of seconds after cron is started or the crontab entry is reloaded. .Sh EXAMPLE CRON FILE .Bd -literal # use /bin/sh to run commands, overriding the default set by cron SHELL=/bin/sh # mail any output to `paul', no matter whose crontab this is MAILTO=paul # # run five minutes after midnight, every day 5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1 # run at 2:15pm on the first of every month -- output mailed to paul 15 14 1 * * $HOME/bin/monthly # run at 10 pm on weekdays, annoy Joe 0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?% 23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday" 5 4 * * sun echo "run at 5 after 4 every sunday" # run at 5 minutes intervals, no matter how long it takes @300 svnlite up /usr/src # run every minute, suppress logging * * * * * -q date # run every minute, only send mail if ping fails * * * * * -n ping -c 1 freebsd.org .Ed .Sh SEE ALSO .Xr crontab 1 , .Xr cron 8 .Sh EXTENSIONS When specifying day of week, both day 0 and day 7 will be considered Sunday. .Bx and .Tn ATT seem to disagree about this. .Pp Lists and ranges are allowed to co-exist in the same field. "1-3,7-9" would be rejected by .Tn ATT or .Bx cron -- they want to see "1-3" or "7,8,9" ONLY. .Pp Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9". .Pp Names of months or days of the week can be specified by name. .Pp Environment variables can be set in the crontab. In .Bx or .Tn ATT , the environment handed to child processes is basically the one from .Pa /etc/rc . .Pp Command output is mailed to the crontab owner .No ( Bx cannot do this), can be mailed to a person other than the crontab owner (SysV cannot do this), or the feature can be turned off and no mail will be sent at all (SysV cannot do this either). .Pp All of the .Sq @ directives that can appear in place of the first five fields are extensions. .Pp Command processing can be modified using command options. The .Sq -q option suppresses logging. The .Sq -n option does not mail on successful run. .Sh AUTHORS .An Paul Vixie Aq Mt paul@vix.com .Sh BUGS If you are in one of the 70-odd countries that observe Daylight Savings Time, jobs scheduled during the rollback or advance may be affected if .Xr cron 8 is not started with the .Fl s flag. In general, it is not a good idea to schedule jobs during this period if .Xr cron 8 is not started with the .Fl s flag, which is enabled by default. See .Xr cron 8 for more details. .Pp For US timezones (except parts of AZ and HI) the time shift occurs at 2AM local time. For others, the output of the .Xr zdump 8 program's verbose .Fl ( v ) option can be used to determine the moment of time shift. diff --git a/usr.sbin/cron/crontab/crontab.c b/usr.sbin/cron/crontab/crontab.c index 13e8250a1b63..0975a2ae8c79 100644 --- a/usr.sbin/cron/crontab/crontab.c +++ b/usr.sbin/cron/crontab/crontab.c @@ -1,650 +1,632 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul - * From Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ #if !defined(lint) && !defined(LINT) static const char rcsid[] = - "$FreeBSD$"; + "$Id: crontab.c,v 1.3 1998/08/14 00:32:38 vixie Exp $"; #endif /* crontab - install and manage per-user crontab files * vix 02may87 [RCS has the rest of the log] * vix 26jan87 [original] */ #define MAIN_PROGRAM -#include #include "cron.h" -#include -#include #include -#include -#include -#include -#ifdef USE_UTIMES -# include -#else -# include -# include -#endif -#if defined(POSIX) -# include -#endif #define MD5_SIZE 33 #define NHEADER_LINES 3 - enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace }; #if DEBUGGING static char *Options[] = { "???", "list", "delete", "edit", "replace" }; #endif - static PID_T Pid; static char User[MAXLOGNAME], RealUser[MAXLOGNAME]; static char Filename[MAX_FNAME]; static FILE *NewCrontab; static int CheckErrorCount; static enum opt_t Option; static int fflag; static struct passwd *pw; static void list_cmd(void), delete_cmd(void), edit_cmd(void), poke_daemon(void), - check_error(char *), + check_error(const char *), parse_args(int c, char *v[]); static int replace_cmd(void); - static void -usage(char *msg) +usage(const char *msg) { fprintf(stderr, "crontab: usage error: %s\n", msg); fprintf(stderr, "%s\n%s\n", "usage: crontab [-u user] file", " crontab [-u user] { -l | -r [-f] | -e }"); exit(ERROR_EXIT); } - int main(int argc, char *argv[]) { int exitstatus; Pid = getpid(); ProgramName = argv[0]; -#if defined(POSIX) setlocale(LC_ALL, ""); -#endif #if defined(BSD) setlinebuf(stderr); #endif parse_args(argc, argv); /* sets many globals, opens a file */ set_cron_uid(); set_cron_cwd(); if (!allowed(User)) { warnx("you (%s) are not allowed to use this program", User); log_it(RealUser, Pid, "AUTH", "crontab command not allowed"); exit(ERROR_EXIT); } exitstatus = OK_EXIT; switch (Option) { - case opt_list: list_cmd(); - break; - case opt_delete: delete_cmd(); - break; - case opt_edit: edit_cmd(); - break; - case opt_replace: if (replace_cmd() < 0) - exitstatus = ERROR_EXIT; - break; + case opt_list: + list_cmd(); + break; + case opt_delete: + delete_cmd(); + break; + case opt_edit: + edit_cmd(); + break; + case opt_replace: + if (replace_cmd() < 0) + exitstatus = ERROR_EXIT; + break; case opt_unknown: - break; + default: + abort(); } exit(exitstatus); /*NOTREACHED*/ } - static void parse_args(int argc, char *argv[]) { - int argch; - char resolved_path[PATH_MAX]; + int argch; + char resolved_path[PATH_MAX]; if (!(pw = getpwuid(getuid()))) errx(ERROR_EXIT, "your UID isn't in the passwd file, bailing out"); bzero(pw->pw_passwd, strlen(pw->pw_passwd)); (void) strncpy(User, pw->pw_name, (sizeof User)-1); User[(sizeof User)-1] = '\0'; strcpy(RealUser, User); Filename[0] = '\0'; Option = opt_unknown; while ((argch = getopt(argc, argv, "u:lerx:f")) != -1) { switch (argch) { case 'x': if (!set_debug_flags(optarg)) usage("bad debug option"); break; case 'u': if (getuid() != ROOT_UID) errx(ERROR_EXIT, "must be privileged to use -u"); if (!(pw = getpwnam(optarg))) errx(ERROR_EXIT, "user `%s' unknown", optarg); bzero(pw->pw_passwd, strlen(pw->pw_passwd)); (void) strncpy(User, pw->pw_name, (sizeof User)-1); User[(sizeof User)-1] = '\0'; break; case 'l': if (Option != opt_unknown) usage("only one operation permitted"); Option = opt_list; break; case 'r': if (Option != opt_unknown) usage("only one operation permitted"); Option = opt_delete; break; case 'e': if (Option != opt_unknown) usage("only one operation permitted"); Option = opt_edit; break; case 'f': fflag = 1; break; default: usage("unrecognized option"); } } endpwent(); if (Option != opt_unknown) { if (argv[optind] != NULL) { usage("no arguments permitted after this option"); } } else { if (argv[optind] != NULL) { Option = opt_replace; (void) strncpy (Filename, argv[optind], (sizeof Filename)-1); Filename[(sizeof Filename)-1] = '\0'; } else { usage("file name must be specified for replace"); } } if (Option == opt_replace) { /* relinquish the setuid status of the binary during * the open, lest nonroot users read files they should * not be able to read. we can't use access() here * since there's a race condition. thanks go out to * Arnt Gulbrandsen for spotting * the race. */ if (swap_uids() < OK) err(ERROR_EXIT, "swapping uids"); /* we have to open the file here because we're going to * chdir(2) into /var/cron before we get around to * reading the file. */ if (!strcmp(Filename, "-")) { NewCrontab = stdin; } else if (realpath(Filename, resolved_path) != NULL && !strcmp(resolved_path, SYSCRONTAB)) { err(ERROR_EXIT, SYSCRONTAB " must be edited manually"); } else { if (!(NewCrontab = fopen(Filename, "r"))) err(ERROR_EXIT, "%s", Filename); } if (swap_uids_back() < OK) err(ERROR_EXIT, "swapping uids back"); } Debug(DMISC, ("user=%s, file=%s, option=%s\n", User, Filename, Options[(int)Option])) } static void -copy_file(FILE *in, FILE *out) { - int x, ch; +copy_file(FILE *in, FILE *out) +{ + int x, ch; Set_LineNum(1) /* ignore the top few comments since we probably put them there. */ - for (x = 0; x < NHEADER_LINES; x++) { + for (x = 0; x < NHEADER_LINES; x++) { ch = get_char(in); if (EOF == ch) break; if ('#' != ch) { putc(ch, out); break; } while (EOF != (ch = get_char(in))) if (ch == '\n') break; if (EOF == ch) break; } /* copy the rest of the crontab (if any) to the output file. */ if (EOF != ch) while (EOF != (ch = get_char(in))) putc(ch, out); } static void list_cmd(void) { - char n[MAX_FNAME]; - FILE *f; + char n[MAX_FNAME]; + FILE *f; log_it(RealUser, Pid, "LIST", User); (void) snprintf(n, sizeof(n), CRON_TAB(User)); if (!(f = fopen(n, "r"))) { if (errno == ENOENT) errx(ERROR_EXIT, "no crontab for %s", User); else err(ERROR_EXIT, "%s", n); } /* file is open. copy to stdout, close. */ copy_file(f, stdout); fclose(f); } - static void delete_cmd(void) { - char n[MAX_FNAME]; + char n[MAX_FNAME]; int ch, first; if (!fflag && isatty(STDIN_FILENO)) { (void)fprintf(stderr, "remove crontab for %s? ", User); first = ch = getchar(); while (ch != '\n' && ch != EOF) ch = getchar(); if (first != 'y' && first != 'Y') return; } log_it(RealUser, Pid, "DELETE", User); - (void) snprintf(n, sizeof(n), CRON_TAB(User)); - if (unlink(n)) { + if (snprintf(n, sizeof(n), CRON_TAB(User)) >= (int)sizeof(n)) + errx(ERROR_EXIT, "path too long"); + if (unlink(n) != 0) { if (errno == ENOENT) errx(ERROR_EXIT, "no crontab for %s", User); else err(ERROR_EXIT, "%s", n); } poke_daemon(); } - static void -check_error(char *msg) +check_error(const char *msg) { CheckErrorCount++; fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg); } - static void edit_cmd(void) { - char n[MAX_FNAME], q[MAX_TEMPSTR], *editor; - FILE *f; - int t; - struct stat statbuf, fsbuf; - WAIT_T waiter; - PID_T pid, xpid; - mode_t um; - int syntax_error = 0; - char orig_md5[MD5_SIZE]; - char new_md5[MD5_SIZE]; + char n[MAX_FNAME], q[MAX_TEMPSTR], *editor; + FILE *f; + int t; + struct stat statbuf, fsbuf; + WAIT_T waiter; + PID_T pid, xpid; + mode_t um; + int syntax_error = 0; + char orig_md5[MD5_SIZE]; + char new_md5[MD5_SIZE]; log_it(RealUser, Pid, "BEGIN EDIT", User); - (void) snprintf(n, sizeof(n), CRON_TAB(User)); + if (snprintf(n, sizeof(n), CRON_TAB(User)) >= (int)sizeof(n)) + errx(ERROR_EXIT, "path too long"); if (!(f = fopen(n, "r"))) { if (errno != ENOENT) err(ERROR_EXIT, "%s", n); warnx("no crontab for %s - using an empty one", User); if (!(f = fopen(_PATH_DEVNULL, "r"))) err(ERROR_EXIT, _PATH_DEVNULL); } um = umask(077); (void) snprintf(Filename, sizeof(Filename), "/tmp/crontab.XXXXXXXXXX"); if ((t = mkstemp(Filename)) == -1) { warn("%s", Filename); (void) umask(um); goto fatal; } (void) umask(um); #ifdef HAS_FCHOWN if (fchown(t, getuid(), getgid()) < 0) { #else if (chown(Filename, getuid(), getgid()) < 0) { #endif warn("fchown"); goto fatal; } if (!(NewCrontab = fdopen(t, "r+"))) { warn("fdopen"); goto fatal; } copy_file(f, NewCrontab); fclose(f); if (fflush(NewCrontab)) err(ERROR_EXIT, "%s", Filename); if (fstat(t, &fsbuf) < 0) { warn("unable to fstat temp file"); goto fatal; } again: if (swap_uids() < OK) err(ERROR_EXIT, "swapping uids"); if (stat(Filename, &statbuf) < 0) { warn("stat"); - fatal: unlink(Filename); + fatal: + unlink(Filename); exit(ERROR_EXIT); } if (swap_uids_back() < OK) err(ERROR_EXIT, "swapping uids back"); if (statbuf.st_dev != fsbuf.st_dev || statbuf.st_ino != fsbuf.st_ino) errx(ERROR_EXIT, "temp file must be edited in place"); if (MD5File(Filename, orig_md5) == NULL) { warn("MD5"); goto fatal; } - if ((!(editor = getenv("VISUAL"))) - && (!(editor = getenv("EDITOR"))) - ) { + if ((editor = getenv("VISUAL")) == NULL && + (editor = getenv("EDITOR")) == NULL) { editor = EDITOR; } /* we still have the file open. editors will generally rewrite the * original file rather than renaming/unlinking it and starting a * new one; even backup files are supposed to be made by copying * rather than by renaming. if some editor does not support this, * then don't use it. the security problems are more severe if we * close and reopen the file around the edit. */ switch (pid = fork()) { case -1: warn("fork"); goto fatal; case 0: /* child */ if (setuid(getuid()) < 0) err(ERROR_EXIT, "setuid(getuid())"); if (chdir("/tmp") < 0) err(ERROR_EXIT, "chdir(/tmp)"); if (strlen(editor) + strlen(Filename) + 2 >= MAX_TEMPSTR) errx(ERROR_EXIT, "editor or filename too long"); execlp(editor, editor, Filename, (char *)NULL); err(ERROR_EXIT, "%s", editor); /*NOTREACHED*/ default: /* parent */ break; } /* parent */ { void (*sig[3])(int signal); sig[0] = signal(SIGHUP, SIG_IGN); sig[1] = signal(SIGINT, SIG_IGN); sig[2] = signal(SIGTERM, SIG_IGN); xpid = wait(&waiter); signal(SIGHUP, sig[0]); signal(SIGINT, sig[1]); signal(SIGTERM, sig[2]); } if (xpid != pid) { warnx("wrong PID (%d != %d) from \"%s\"", xpid, pid, editor); goto fatal; } if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) { warnx("\"%s\" exited with status %d", editor, WEXITSTATUS(waiter)); goto fatal; } if (WIFSIGNALED(waiter)) { warnx("\"%s\" killed; signal %d (%score dumped)", editor, WTERMSIG(waiter), WCOREDUMP(waiter) ?"" :"no "); goto fatal; } if (swap_uids() < OK) err(ERROR_EXIT, "swapping uids"); if (stat(Filename, &statbuf) < 0) { warn("stat"); goto fatal; } if (statbuf.st_dev != fsbuf.st_dev || statbuf.st_ino != fsbuf.st_ino) errx(ERROR_EXIT, "temp file must be edited in place"); if (MD5File(Filename, new_md5) == NULL) { warn("MD5"); goto fatal; } if (swap_uids_back() < OK) err(ERROR_EXIT, "swapping uids back"); if (strcmp(orig_md5, new_md5) == 0 && !syntax_error) { warnx("no changes made to crontab"); goto remove; } warnx("installing new crontab"); switch (replace_cmd()) { case 0: /* Success */ break; case -1: /* Syntax error */ for (;;) { printf("Do you want to retry the same edit? "); fflush(stdout); q[0] = '\0'; (void) fgets(q, sizeof q, stdin); switch (islower(q[0]) ? q[0] : tolower(q[0])) { case 'y': syntax_error = 1; goto again; case 'n': goto abandon; default: fprintf(stderr, "Enter Y or N\n"); } } /*NOTREACHED*/ case -2: /* Install error */ abandon: warnx("edits left in %s", Filename); goto done; default: warnx("panic: bad switch() in replace_cmd()"); goto fatal; } remove: unlink(Filename); done: log_it(RealUser, Pid, "END EDIT", User); } /* returns 0 on success * -1 on syntax error * -2 on install error */ static int replace_cmd(void) { - char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME]; - FILE *tmp; - int ch, eof; - entry *e; - time_t now = time(NULL); - char **envp = env_init(); + char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME]; + FILE *tmp; + int ch, eof; + entry *e; + time_t now = time(NULL); + char **envp = env_init(); if (envp == NULL) { warnx("cannot allocate memory"); return (-2); } (void) snprintf(n, sizeof(n), "tmp.%d", Pid); - (void) snprintf(tn, sizeof(tn), CRON_TAB(n)); + if (snprintf(tn, sizeof(tn), CRON_TAB(n)) >= (int)sizeof(tn)) { + warnx("path too long"); + return (-2); + } if (!(tmp = fopen(tn, "w+"))) { warn("%s", tn); return (-2); } /* write a signature at the top of the file. * * VERY IMPORTANT: make sure NHEADER_LINES agrees with this code. */ fprintf(tmp, "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n"); fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now)); fprintf(tmp, "# (Cron version -- %s)\n", rcsid); /* copy the crontab to the tmp */ rewind(NewCrontab); Set_LineNum(1) while (EOF != (ch = get_char(NewCrontab))) putc(ch, tmp); ftruncate(fileno(tmp), ftello(tmp)); fflush(tmp); rewind(tmp); if (ferror(tmp)) { warnx("error while writing new crontab to %s", tn); fclose(tmp); unlink(tn); return (-2); } /* check the syntax of the file being installed. */ /* BUG: was reporting errors after the EOF if there were any errors * in the file proper -- kludged it by stopping after first error. * vix 31mar87 */ Set_LineNum(1 - NHEADER_LINES) CheckErrorCount = 0; eof = FALSE; while (!CheckErrorCount && !eof) { switch (load_env(envstr, tmp)) { case ERR: eof = TRUE; break; case FALSE: e = load_entry(tmp, check_error, pw, envp); if (e) free_entry(e); break; case TRUE: break; } } if (CheckErrorCount != 0) { warnx("errors in crontab file, can't install"); fclose(tmp); unlink(tn); return (-1); } #ifdef HAS_FCHOWN if (fchown(fileno(tmp), ROOT_UID, -1) < OK) #else if (chown(tn, ROOT_UID, -1) < OK) #endif { warn("chown"); fclose(tmp); unlink(tn); return (-2); } #ifdef HAS_FCHMOD if (fchmod(fileno(tmp), 0600) < OK) #else if (chmod(tn, 0600) < OK) #endif { warn("chown"); fclose(tmp); unlink(tn); return (-2); } if (fclose(tmp) == EOF) { warn("fclose"); unlink(tn); return (-2); } - (void) snprintf(n, sizeof(n), CRON_TAB(User)); + if (snprintf(n, sizeof(n), CRON_TAB(User)) >= (int)sizeof(n)) { + warnx("path too long"); + unlink(tn); + return (-2); + } + if (rename(tn, n)) { warn("error renaming %s to %s", tn, n); unlink(tn); return (-2); } log_it(RealUser, Pid, "REPLACE", User); /* * Creating the 'tn' temp file has already updated the * modification time of the spool directory. Sleep for a * second to ensure that poke_daemon() sets a later * modification time. Otherwise, this can race with the cron * daemon scanning for updated crontabs. */ sleep(1); poke_daemon(); return (0); } - static void poke_daemon(void) { -#ifdef USE_UTIMES - struct timeval tvs[2]; - - (void)gettimeofday(&tvs[0], NULL); - tvs[1] = tvs[0]; - if (utimes(SPOOL_DIR, tvs) < OK) { - warn("can't update mtime on spooldir %s", SPOOL_DIR); - return; - } -#else if (utime(SPOOL_DIR, NULL) < OK) { warn("can't update mtime on spooldir %s", SPOOL_DIR); return; } -#endif /*USE_UTIMES*/ } diff --git a/usr.sbin/cron/doc/INSTALL b/usr.sbin/cron/doc/INSTALL index 7f4c9974da64..326be482dd71 100644 --- a/usr.sbin/cron/doc/INSTALL +++ b/usr.sbin/cron/doc/INSTALL @@ -1,87 +1,91 @@ /* Copyright 1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ -$FreeBSD$ +$Id: INSTALL,v 1.2 1998/08/14 00:32:35 vixie Exp $ Read the comments at the top of the Makefile, then edit the area marked 'configurable stuff'. Edit config.h. The stuff I expect you to change is down a bit from the top of the file, but it's clearly marked. Also look at pathnames.h. You don't have to create the /var/cron or /var/cron/tabs directories, since both the daemon and the `crontab' program will do this the first time they run if they don't exist. You do need to have a /var, though -- just "mkdir /var" if you don't have one, or you can "mkdir /usr/var; ln -s /usr/var /var" if you expect your /var to have a lot of stuff in it. You will also need /usr/local/etc and /usr/local/bin directories unless you change the Makefile. These will have to be created by hand, but if you are a long-time Usenet user you probably have them already. /usr/local/man is where I keep my man pages, but I have the source for `man' and you probably do not. Therefore you may have to put the man pages into /usr/man/manl, which will be hard since there will be name collisions. (Note that the man command was originally written by Bill Joy before he left Berkeley, and it contains no AT&T code, so it is in UUNET's archive of freely-distributable BSD code.) LINUX note: /usr/include/paths.h on some linux systems shows _PATH_SENDMAIL to be /usr/bin/sendmail even though sendmail is installed in /usr/lib. you should check this out. say: make all su and say: make install Note that if I can get you to "su and say" something just by asking, you have a very serious security problem on your system and you should look into it. Edit your /usr/lib/crontab file into little pieces -- see the CONVERSION file for help on this. Use the `crontab' command to install all the little pieces you just created. Some examples (see below before trying any of these!) crontab -u uucp -r /usr/lib/uucp/crontab.src crontab -u news -r /usr/lib/news/crontab.src crontab -u root -r /usr/adm/crontab.src Notes on above examples: (1) the .src files are copied at the time the command is issued; changing the source files later will have no effect until they are reinstalled with another `crontab -r' command. (2) The crontab command will affect the crontab of the person using the command unless `-u USER' is given; `-u' only works for root. When using most `su' commands under most BSD's, `crontab' will still think of you as yourself even though you may think of yourself as root -- so use `-u' liberally. (3) the `-r' option stands for `replace'; check the man page for crontab(1) for other possibilities. Kill your existing cron daemon -- do `ps aux' and look for /etc/cron. Edit your /etc/rc or /etc/rc.local, looking for the line that starts up /etc/cron. Comment it out and add a line to start the new cron daemon -- usually /usr/local/etc/cron, unless you changed it in the Makefile. Start up this cron daemon yourself as root. Just type /usr/local/etc/cron (or whatever); no '&' is needed since the daemon forks itself and the process you executed returns immediately. ATT notes: for those people unfortunate enough to be stuck on a AT&T UNIX, you will need the public-domain "libndir", found in the B News source and in any comp.sources.unix archive. You will also need to hack the code some. diff --git a/usr.sbin/cron/doc/Makefile.vixie b/usr.sbin/cron/doc/Makefile.vixie index 8df1301c689d..53b8e9b9bcc5 100644 --- a/usr.sbin/cron/doc/Makefile.vixie +++ b/usr.sbin/cron/doc/Makefile.vixie @@ -1,128 +1,124 @@ #/* Copyright 1988,1990,1993,1994 by Paul Vixie # * All rights reserved -# * -# * Distribute freely, except: don't remove my name from the source or -# * documentation (don't take credit for my work), mark your changes (don't -# * get me blamed for your possible bugs), don't alter or remove this -# * notice. May be sold if buildable source is provided to buyer. No -# * warrantee of any kind, express or implied, is included with this -# * software; use at your own risk, responsibility for damages (if any) to -# * anyone resulting from the use of this software rests entirely with the -# * user. -# * -# * Send bug reports, bug fixes, enhancements, requests, flames, etc., and -# * I'll try to keep a version up to date. I can be reached as follows: -# * Paul Vixie uunet!decwrl!vixie!paul # */ +## Copyright (c) 1997 by Internet Software Consortium. +## +## Permission to use, copy, modify, and distribute this software for any +## purpose with or without fee is hereby granted, provided that the above +## copyright notice and this permission notice appear in all copies. +## +## THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS +## ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +## OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE +## CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +## DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +## PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +## ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +## SOFTWARE. + # Makefile for vixie's cron # -# $FreeBSD$ +# $Id: Makefile,v 1.2 1998/08/14 00:32:35 vixie Exp $ # # vix 03mar88 [moved to RCS, rest of log is in there] # vix 30mar87 [goodbye, time.c; hello, getopt] # vix 12feb87 [cleanup for distribution] # vix 30dec86 [written] # NOTES: # 'make' can be done by anyone # 'make install' must be done by root # # this package needs getopt(3), bitstring(3), and BSD install(8). # # the configurable stuff in this makefile consists of compilation # options (use -O, cron runs forever) and destination directories. # SHELL is for the 'augumented make' systems where 'make' imports # SHELL from the environment and then uses it to run its commands. # if your environment SHELL variable is /bin/csh, make goes real -# slow and sometimes does the wrong thing. +# slow and sometimes does the wrong thing. # # this package needs the 'bitstring macros' library, which is # available from me or from the comp.sources.unix archive. if you # put 'bitstring.h' in a non-standard place (i.e., not intuited by # cc(1)), you will have to define INCLUDE to set the include # directory for cc. INCLUDE should be `-Isomethingorother'. # # there's more configuration info in config.h; edit that first! #################################### begin configurable stuff #<> DESTROOT = $(DESTDIR)/usr DESTSBIN = $(DESTROOT)/sbin DESTBIN = $(DESTROOT)/bin DESTMAN = $(DESTROOT)/share/man #<> INCLUDE = -I. #INCLUDE = #<> LIBS = #<> -#OPTIM = -O -OPTIM = -g -#<> -# (ATT untested) -#COMPAT = -DATT -#(BSD is only needed if does not define it, as on ULTRIX) -#COMPAT = -DBSD -# (POSIX) -#COMPAT = -DPOSIX +#CDEBUG = -O +CDEBUG = -g #<> -LINTFLAGS = -hbxa $(INCLUDE) $(COMPAT) $(DEBUGGING) +LINTFLAGS = -hbxa $(INCLUDE) $(DEBUGGING) #<> -#CC = vcc +CC = gcc -Wall -Wno-unused -Wno-comment #<> DEFS = #(SGI IRIX systems need this) #DEFS = -D_BSD_SIGNALS -Dconst= #<> #INSTALL = installbsd INSTALL = install #<> LDFLAGS = #################################### end configurable stuff SHELL = /bin/sh -CFLAGS = $(OPTIM) $(INCLUDE) $(COMPAT) $(DEFS) +CFLAGS = $(CDEBUG) $(INCLUDE) $(DEFS) INFOS = README CHANGES FEATURES INSTALL CONVERSION THANKS MAIL MANPAGES = bitstring.3 crontab.5 crontab.1 cron.8 putman.sh -HEADERS = bitstring.h cron.h config.h pathnames.h \ - externs.h compat.h +HEADERS = bitstring.h cron.h config.h pathnames.h externs.h SOURCES = cron.c crontab.c database.c do_command.c entry.c \ - env.c job.c user.c popen.c misc.c compat.c + env.c job.c user.c popen.c misc.c SHAR_SOURCE = $(INFOS) $(MANPAGES) Makefile $(HEADERS) $(SOURCES) -LINT_CRON = cron.c database.c user.c entry.c compat.c \ +LINT_CRON = cron.c database.c user.c entry.c \ misc.c job.c do_command.c env.c popen.c -LINT_CRONTAB = crontab.c misc.c entry.c env.c compat.c +LINT_CRONTAB = crontab.c misc.c entry.c env.c CRON_OBJ = cron.o database.o user.o entry.o job.o do_command.o \ - misc.o env.o popen.o compat.o -CRONTAB_OBJ = crontab.o misc.o entry.o env.o compat.o + misc.o env.o popen.o +CRONTAB_OBJ = crontab.o misc.o entry.o env.o all : cron crontab lint : lint $(LINTFLAGS) $(LINT_CRON) $(LIBS) \ |grep -v "constant argument to NOT" 2>&1 lint $(LINTFLAGS) $(LINT_CRONTAB) $(LIBS) \ |grep -v "constant argument to NOT" 2>&1 cron : $(CRON_OBJ) $(CC) $(LDFLAGS) -o cron $(CRON_OBJ) $(LIBS) crontab : $(CRONTAB_OBJ) $(CC) $(LDFLAGS) -o crontab $(CRONTAB_OBJ) $(LIBS) install : all $(INSTALL) -c -m 111 -o root -s cron $(DESTSBIN)/ $(INSTALL) -c -m 4111 -o root -s crontab $(DESTBIN)/ sh putman.sh crontab.1 $(DESTMAN) sh putman.sh cron.8 $(DESTMAN) sh putman.sh crontab.5 $(DESTMAN) clean :; rm -f *.o cron crontab a.out core tags *~ #* +tags :; ctags ${SOURCES} + kit : $(SHAR_SOURCE) makekit -m -s99k $(SHAR_SOURCE) -$(CRON_OBJ) : cron.h compat.h config.h externs.h pathnames.h Makefile -$(CRONTAB_OBJ) : cron.h compat.h config.h externs.h pathnames.h Makefile +$(CRON_OBJ) : cron.h config.h externs.h pathnames.h Makefile +$(CRONTAB_OBJ) : cron.h config.h externs.h pathnames.h Makefile diff --git a/usr.sbin/cron/doc/README b/usr.sbin/cron/doc/README index eaced9e65d94..ebd8849a52a0 100644 --- a/usr.sbin/cron/doc/README +++ b/usr.sbin/cron/doc/README @@ -1,72 +1,68 @@ -#/* Copyright 1988,1990,1993 by Paul Vixie +#/* Copyright 1988,1990,1993 by Paul Vixie # * All rights reserved -# * -# * Distribute freely, except: don't remove my name from the source or -# * documentation (don't take credit for my work), mark your changes (don't -# * get me blamed for your possible bugs), don't alter or remove this -# * notice. May be sold if buildable source is provided to buyer. No -# * warrantee of any kind, express or implied, is included with this -# * software; use at your own risk, responsibility for damages (if any) to -# * anyone resulting from the use of this software rests entirely with the -# * user. -# * -# * Send bug reports, bug fixes, enhancements, requests, flames, etc., and -# * I'll try to keep a version up to date. I can be reached as follows: -# * Paul Vixie uunet!decwrl!vixie!paul # */ -Vixie Cron V3.0 -December 27, 1993 +## Copyright (c) 1997 by Internet Software Consortium. +## +## Permission to use, copy, modify, and distribute this software for any +## purpose with or without fee is hereby granted, provided that the above +## copyright notice and this permission notice appear in all copies. +## +## THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS +## ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +## OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE +## CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +## DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +## PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +## ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +## SOFTWARE. + +Vixie Cron V4.0 - September 7, 1997 +[V3.1 was some time after 1993] +[V3.0 was December 27, 1993] [V2.2 was some time in 1992] [V2.1 was May 29, 1991] [V2.0 was July 5, 1990] [V2.0-beta was December 9, 1988] [V1.0 was May 6, 1987] Paul Vixie -This is a version of 'cron' that is known to run on BSD 4.[23] systems. It +This is a version of 'cron' that is known to run on most systems. It is functionally based on the SysV cron, which means that each user can have their own crontab file (all crontab files are stored in a read-protected directory, usually /var/cron/tabs). No direct support is provided for 'at'; you can continue to run 'atrun' from the crontab as you have been doing. If you don't have atrun (i.e., System V) you are in trouble. A messages is logged each time a command is executed; also, the files "allow" and "deny" in /var/cron can be used to control access to the "crontab" command (which installs crontabs). It hasn't been tested on SysV, although some effort has gone into making the port an easy one. -This is more or less the copyright that USENET contributed software usually -has. Since ATT couldn't use this version if they had to freely distribute -source, and since I'd love to see them use it, I'll offer some rediculously -low license fee just to have them take it. In the unlikely event that they -do this, I will continue to support and distribute the pseudo-PD version, so -please, don't flame me for wanting my work to see a wider distribution. - To use this: Sorry, folks, there is no cutesy 'Configure' script. You'll have to go edit a couple of files... So, here's the checklist: Read all the FEATURES, INSTALL, and CONVERSION files Edit config.h Edit Makefile (both of these files have instructions inside; note that some things in config.h are definable in Makefile and are therefore surrounded by #ifndef...#endif) 'make' 'su' and 'make install' (you may have to install the man pages by hand) kill your existing cron process (actually you can run your existing cron if you want, but why?) build new crontabs using /usr/lib/{crontab,crontab.local} (either put them all in "root"'s crontab, or divide it up and rip out all the 'su' commands, collapse the lengthy lists into ranges with steps -- basically, this step is as much work as you want to make it) start up the new cron (must be done as root) watch it. test it with 'crontab -r' and watch the daemon track your changes. if you like it, change your /etc/{rc,rc.local} to use it instead of the old one. -$FreeBSD$ +$Id: README,v 1.2 1998/08/14 00:32:35 vixie Exp $ diff --git a/usr.sbin/cron/lib/compat.c b/usr.sbin/cron/lib/compat.c deleted file mode 100644 index 3cdbae920d47..000000000000 --- a/usr.sbin/cron/lib/compat.c +++ /dev/null @@ -1,225 +0,0 @@ -/* Copyright 1988,1990,1993,1994 by Paul Vixie - * All rights reserved - * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. - * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul - */ - -#if !defined(lint) && !defined(LINT) -static char rcsid[] = "$FreeBSD$"; -#endif - -/* vix 30dec93 [broke this out of misc.c - see RCS log for history] - * vix 15jan87 [added TIOCNOTTY, thanks csg@pyramid] - */ - - -#include "cron.h" -#ifdef NEED_GETDTABLESIZE -# include -#endif -#if defined(NEED_SETSID) && defined(BSD) -# include -#endif -#include -#include - - -#ifdef NEED_STRDUP -char * -strdup(str) - char *str; -{ - char *temp; - - if ((temp = malloc(strlen(str) + 1)) == NULL) { - errno = ENOMEM; - return NULL; - } - (void) strcpy(temp, str); - return temp; -} -#endif - - -#ifdef NEED_STRERROR -char * -strerror(error) - int error; -{ - extern char *sys_errlist[]; - extern int sys_nerr; - static char buf[32]; - - if ((error <= sys_nerr) && (error > 0)) { - return sys_errlist[error]; - } - - sprintf(buf, "Unknown error: %d", error); - return buf; -} -#endif - - -#ifdef NEED_STRCASECMP -int -strcasecmp(left, right) - char *left; - char *right; -{ - while (*left && (MkLower(*left) == MkLower(*right))) { - left++; - right++; - } - return MkLower(*left) - MkLower(*right); -} -#endif - - -#ifdef NEED_SETSID -int -setsid() -{ - int newpgrp; -# if defined(BSD) - int fd; -# if defined(POSIX) - newpgrp = setpgid((pid_t)0, getpid()); -# else - newpgrp = setpgrp(0, getpid()); -# endif - if ((fd = open(_PATH_TTY, 2)) >= 0) - { - (void) ioctl(fd, TIOCNOTTY, (char*)0); - (void) close(fd); - } -# else /*BSD*/ - newpgrp = setpgrp(); - - (void) close(STDIN); (void) open(_PATH_DEVNULL, 0); - (void) close(STDOUT); (void) open(_PATH_DEVNULL, 1); - (void) close(STDERR); (void) open(_PATH_DEVNULL, 2); -# endif /*BSD*/ - return newpgrp; -} -#endif /*NEED_SETSID*/ - - -#ifdef NEED_GETDTABLESIZE -int -getdtablesize() { -#ifdef _SC_OPEN_MAX - return sysconf(_SC_OPEN_MAX); -#else - return _POSIX_OPEN_MAX; -#endif -} -#endif - - -#ifdef NEED_FLOCK -/* The following flock() emulation snarfed intact *) from the HP-UX - * "BSD to HP-UX porting tricks" maintained by - * system@alchemy.chem.utoronto.ca (System Admin (Mike Peterson)) - * from the version "last updated: 11-Jan-1993" - * Snarfage done by Jarkko Hietaniemi - * *) well, almost, had to K&R the function entry, HPUX "cc" - * does not grok ANSI function prototypes */ - -/* - * flock (fd, operation) - * - * This routine performs some file locking like the BSD 'flock' - * on the object described by the int file descriptor 'fd', - * which must already be open. - * - * The operations that are available are: - * - * LOCK_SH - get a shared lock. - * LOCK_EX - get an exclusive lock. - * LOCK_NB - don't block (must be ORed with LOCK_SH or LOCK_EX). - * LOCK_UN - release a lock. - * - * Return value: 0 if lock successful, -1 if failed. - * - * Note that whether the locks are enforced or advisory is - * controlled by the presence or absence of the SETGID bit on - * the executable. - * - * Note that there is no difference between shared and exclusive - * locks, since the 'lockf' system call in SYSV doesn't make any - * distinction. - * - * The file "" should be modified to contain the definitions - * of the available operations, which must be added manually (see below - * for the values). - */ - -/* this code has been reformatted by vixie */ - -int -flock(fd, operation) - int fd; - int operation; -{ - int i; - - switch (operation) { - case LOCK_SH: /* get a shared lock */ - case LOCK_EX: /* get an exclusive lock */ - i = lockf (fd, F_LOCK, 0); - break; - - case LOCK_SH|LOCK_NB: /* get a non-blocking shared lock */ - case LOCK_EX|LOCK_NB: /* get a non-blocking exclusive lock */ - i = lockf (fd, F_TLOCK, 0); - if (i == -1) - if ((errno == EAGAIN) || (errno == EACCES)) - errno = EWOULDBLOCK; - break; - - case LOCK_UN: /* unlock */ - i = lockf (fd, F_ULOCK, 0); - break; - - default: /* can't decipher operation */ - i = -1; - errno = EINVAL; - break; - } - - return (i); -} -#endif /*NEED_FLOCK*/ - - -#ifdef NEED_SETENV -int -setenv(name, value, overwrite) - char *name, *value; - int overwrite; -{ - char *tmp; - - if (overwrite && getenv(name)) - return -1; - - if (!(tmp = malloc(strlen(name) + strlen(value) + 2))) { - errno = ENOMEM; - return -1; - } - - sprintf(tmp, "%s=%s", name, value); - return putenv(tmp); /* intentionally orphan 'tmp' storage */ -} -#endif diff --git a/usr.sbin/cron/lib/entry.c b/usr.sbin/cron/lib/entry.c index 6d8cff847604..b0446d9cdec5 100644 --- a/usr.sbin/cron/lib/entry.c +++ b/usr.sbin/cron/lib/entry.c @@ -1,701 +1,729 @@ -/* Copyright 1988,1990,1993,1994 by Paul Vixie +/* + * Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ #if !defined(lint) && !defined(LINT) static const char rcsid[] = - "$FreeBSD$"; + "$Id: entry.c,v 1.3 1998/08/14 00:32:39 vixie Exp $"; #endif /* vix 26jan87 [RCS'd; rest of log is in RCS file] * vix 01jan87 [added line-level error recovery] * vix 31dec86 [added /step to the from-to range, per bob@acornrc] * vix 30dec86 [written] */ #include "cron.h" #include #ifdef LOGIN_CAP #include #endif typedef enum ecode { e_none, e_minute, e_hour, e_dom, e_month, e_dow, e_cmd, e_timespec, e_username, e_group, e_option, e_mem #ifdef LOGIN_CAP , e_class #endif } ecode_e; -static char get_list(bitstr_t *, int, int, char *[], int, FILE *), - get_range(bitstr_t *, int, int, char *[], int, FILE *), - get_number(int *, int, char *[], int, FILE *); -static int set_element(bitstr_t *, int, int, int); - -static char *ecodes[] = +static const char *ecodes[] = { "no error", "bad minute", "bad hour", "bad day-of-month", "bad month", "bad day-of-week", "bad command", "bad time specifier", "bad username", "bad group name", "bad option", "out of memory", #ifdef LOGIN_CAP "bad class name", #endif }; +static char get_list(bitstr_t *, int, int, const char *[], int, FILE *), + get_range(bitstr_t *, int, int, const char *[], int, FILE *), + get_number(int *, int, const char *[], int, FILE *); +static int set_element(bitstr_t *, int, int, int); void free_entry(entry *e) { #ifdef LOGIN_CAP if (e->class != NULL) free(e->class); #endif if (e->cmd != NULL) free(e->cmd); if (e->envp != NULL) env_free(e->envp); free(e); } /* return NULL if eof or syntax error occurs; * otherwise return a pointer to a new entry. */ entry * -load_entry(FILE *file, void (*error_func)(char *), struct passwd *pw, +load_entry(FILE *file, void (*error_func)(const char *), struct passwd *pw, char **envp) { /* this function reads one crontab entry -- the next -- from a file. * it skips any leading blank lines, ignores comments, and returns * EOF if for any reason the entry can't be read and parsed. * * the entry is also parsed here. * * syntax: * user crontab: * minutes hours doms months dows cmd\n * system crontab (/etc/crontab): * minutes hours doms months dows USERNAME cmd\n */ ecode_e ecode = e_none; entry *e; int ch; + int len; char cmd[MAX_COMMAND]; char envstr[MAX_ENVSTR]; char **prev_env; Debug(DPARS, ("load_entry()...about to eat comments\n")) skip_comments(file); ch = get_char(file); if (ch == EOF) return NULL; /* ch is now the first useful character of a useful line. * it may be an @special or it may be the first character * of a list of minutes. */ e = (entry *) calloc(sizeof(entry), sizeof(char)); if (e == NULL) { warn("load_entry: calloc failed"); return NULL; } if (ch == '@') { long interval; char *endptr; /* all of these should be flagged and load-limited; i.e., * instead of @hourly meaning "0 * * * *" it should mean * "close to the front of every hour but not 'til the * system load is low". Problems are: how do you know * what "low" means? (save me from /etc/cron.conf!) and: * how to guarantee low variance (how low is low?), which * means how to we run roughly every hour -- seems like * we need to keep a history or let the first hour set * the schedule, which means we aren't load-limited * anymore. too much for my overloaded brain. (vix, jan90) * HINT */ Debug(DPARS, ("load_entry()...about to test shortcuts\n")) ch = get_string(cmd, MAX_COMMAND, file, " \t\n"); if (!strcmp("reboot", cmd)) { Debug(DPARS, ("load_entry()...reboot shortcut\n")) e->flags |= WHEN_REBOOT; } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){ Debug(DPARS, ("load_entry()...yearly shortcut\n")) bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_set(e->dom, 0); bit_set(e->month, 0); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); e->flags |= DOW_STAR; } else if (!strcmp("monthly", cmd)) { Debug(DPARS, ("load_entry()...monthly shortcut\n")) bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_set(e->dom, 0); bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); e->flags |= DOW_STAR; } else if (!strcmp("weekly", cmd)) { Debug(DPARS, ("load_entry()...weekly shortcut\n")) bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); e->flags |= DOM_STAR; bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_set(e->dow, 0); } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) { Debug(DPARS, ("load_entry()...daily shortcut\n")) bit_set(e->second, 0); bit_set(e->minute, 0); bit_set(e->hour, 0); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); } else if (!strcmp("hourly", cmd)) { Debug(DPARS, ("load_entry()...hourly shortcut\n")) bit_set(e->second, 0); bit_set(e->minute, 0); bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); } else if (!strcmp("every_minute", cmd)) { Debug(DPARS, ("load_entry()...every_minute shortcut\n")) bit_set(e->second, 0); bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1)); bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); } else if (!strcmp("every_second", cmd)) { Debug(DPARS, ("load_entry()...every_second shortcut\n")) e->flags |= SEC_RES; bit_nset(e->second, 0, (LAST_SECOND-FIRST_SECOND+1)); bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1)); bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); } else if (*cmd != '\0' && (interval = strtol(cmd, &endptr, 10)) > 0 && *endptr == '\0') { Debug(DPARS, ("load_entry()... %ld seconds " "since last run\n", interval)) e->interval = interval; e->flags = INTERVAL; } else { ecode = e_timespec; goto eof; } /* Advance past whitespace between shortcut and * username/command. */ Skip_Blanks(ch, file); if (ch == EOF) { ecode = e_cmd; goto eof; } } else { Debug(DPARS, ("load_entry()...about to parse numerics\n")) bit_set(e->second, 0); ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, PPC_NULL, ch, file); if (ch == EOF) { ecode = e_minute; goto eof; } /* hours */ ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR, PPC_NULL, ch, file); if (ch == EOF) { ecode = e_hour; goto eof; } /* DOM (days of month) */ if (ch == '*') e->flags |= DOM_STAR; ch = get_list(e->dom, FIRST_DOM, LAST_DOM, PPC_NULL, ch, file); if (ch == EOF) { ecode = e_dom; goto eof; } /* month */ ch = get_list(e->month, FIRST_MONTH, LAST_MONTH, MonthNames, ch, file); if (ch == EOF) { ecode = e_month; goto eof; } /* DOW (days of week) */ if (ch == '*') e->flags |= DOW_STAR; ch = get_list(e->dow, FIRST_DOW, LAST_DOW, DowNames, ch, file); if (ch == EOF) { ecode = e_dow; goto eof; } } /* make sundays equivalent */ if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) { bit_set(e->dow, 0); bit_set(e->dow, 7); } /* ch is the first character of a command, or a username */ unget_char(ch, file); if (!pw) { char *username = cmd; /* temp buffer */ char *s; struct group *grp; #ifdef LOGIN_CAP login_cap_t *lc; #endif Debug(DPARS, ("load_entry()...about to parse username\n")) ch = get_string(username, MAX_COMMAND, file, " \t"); Debug(DPARS, ("load_entry()...got %s\n",username)) if (ch == EOF) { ecode = e_cmd; goto eof; } /* need to have consumed blanks when checking options below */ Skip_Blanks(ch, file) unget_char(ch, file); #ifdef LOGIN_CAP if ((s = strrchr(username, '/')) != NULL) { *s = '\0'; e->class = strdup(s + 1); if (e->class == NULL) warn("strdup(\"%s\")", s + 1); } else { e->class = strdup(RESOURCE_RC); if (e->class == NULL) warn("strdup(\"%s\")", RESOURCE_RC); } if (e->class == NULL) { ecode = e_mem; goto eof; } if ((lc = login_getclass(e->class)) == NULL) { ecode = e_class; goto eof; } login_close(lc); #endif grp = NULL; if ((s = strrchr(username, ':')) != NULL) { *s = '\0'; if ((grp = getgrnam(s + 1)) == NULL) { ecode = e_group; goto eof; } } pw = getpwnam(username); if (pw == NULL) { ecode = e_username; goto eof; } if (grp != NULL) pw->pw_gid = grp->gr_gid; Debug(DPARS, ("load_entry()...uid %d, gid %d\n",pw->pw_uid,pw->pw_gid)) #ifdef LOGIN_CAP Debug(DPARS, ("load_entry()...class %s\n",e->class)) #endif } #ifndef PAM /* PAM takes care of account expiration by itself */ if (pw->pw_expire && time(NULL) >= pw->pw_expire) { ecode = e_username; goto eof; } #endif /* !PAM */ e->uid = pw->pw_uid; e->gid = pw->pw_gid; /* copy and fix up environment. some variables are just defaults and * others are overrides; we process only the overrides here, defaults * are handled in do_command after login.conf is processed. */ e->envp = env_copy(envp); if (e->envp == NULL) { warn("env_copy"); ecode = e_mem; goto eof; } if (!env_get("SHELL", e->envp)) { prev_env = e->envp; - sprintf(envstr, "SHELL=%s", _PATH_BSHELL); - e->envp = env_set(e->envp, envstr); + e->envp = env_set(e->envp, "SHELL=" _PATH_BSHELL); if (e->envp == NULL) { - warn("env_set(%s)", envstr); + warn("env_set(%s)", "SHELL=" _PATH_BSHELL); env_free(prev_env); ecode = e_mem; goto eof; } } /* If LOGIN_CAP, this is deferred to do_command where the login class * is processed. If !LOGIN_CAP, do it here. */ #ifndef LOGIN_CAP if (!env_get("HOME", e->envp)) { prev_env = e->envp; - sprintf(envstr, "HOME=%s", pw->pw_dir); - e->envp = env_set(e->envp, envstr); - if (e->envp == NULL) { + len = snprintf(envstr, sizeof(envstr), "HOME=%s", pw->pw_dir); + if (len < sizeof(envstr)) + e->envp = env_set(e->envp, envstr); + if (len >= sizeof(envstr) || e->envp == NULL) { warn("env_set(%s)", envstr); env_free(prev_env); ecode = e_mem; goto eof; } } #endif prev_env = e->envp; - sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name); - e->envp = env_set(e->envp, envstr); - if (e->envp == NULL) { + len = snprintf(envstr, sizeof(envstr), "LOGNAME=%s", pw->pw_name); + if (len < (int)sizeof(envstr)) + e->envp = env_set(e->envp, envstr); + if (len >= (int)sizeof(envstr) || e->envp == NULL) { warn("env_set(%s)", envstr); env_free(prev_env); ecode = e_mem; goto eof; } #if defined(BSD) prev_env = e->envp; - sprintf(envstr, "%s=%s", "USER", pw->pw_name); - e->envp = env_set(e->envp, envstr); - if (e->envp == NULL) { + len = snprintf(envstr, sizeof(envstr), "USER=%s", pw->pw_name); + if (len < (int)sizeof(envstr)) + e->envp = env_set(e->envp, envstr); + if (len >= (int)sizeof(envstr) || e->envp == NULL) { warn("env_set(%s)", envstr); env_free(prev_env); ecode = e_mem; goto eof; } #endif Debug(DPARS, ("load_entry()...checking for command options\n")) ch = get_char(file); while (ch == '-') { Debug(DPARS|DEXT, ("load_entry()...expecting option\n")) switch (ch = get_char(file)) { case 'n': Debug(DPARS|DEXT, ("load_entry()...got MAIL_WHEN_ERR ('n') option\n")) /* only allow the user to set the option once */ if ((e->flags & MAIL_WHEN_ERR) == MAIL_WHEN_ERR) { Debug(DPARS|DEXT, ("load_entry()...duplicate MAIL_WHEN_ERR ('n') option\n")) ecode = e_option; goto eof; } e->flags |= MAIL_WHEN_ERR; break; case 'q': Debug(DPARS|DEXT, ("load_entry()...got DONT_LOG ('q') option\n")) /* only allow the user to set the option once */ if ((e->flags & DONT_LOG) == DONT_LOG) { Debug(DPARS|DEXT, ("load_entry()...duplicate DONT_LOG ('q') option\n")) ecode = e_option; goto eof; } e->flags |= DONT_LOG; break; default: Debug(DPARS|DEXT, ("load_entry()...invalid option '%c'\n", ch)) ecode = e_option; goto eof; } ch = get_char(file); if (ch!='\t' && ch!=' ') { ecode = e_option; goto eof; } Skip_Blanks(ch, file) if (ch == EOF || ch == '\n') { ecode = e_cmd; goto eof; } } unget_char(ch, file); Debug(DPARS, ("load_entry()...about to parse command\n")) /* Everything up to the next \n or EOF is part of the command... * too bad we don't know in advance how long it will be, since we * need to malloc a string for it... so, we limit it to MAX_COMMAND. - * XXX - should use realloc(). */ ch = get_string(cmd, MAX_COMMAND, file, "\n"); /* a file without a \n before the EOF is rude, so we'll complain... */ if (ch == EOF) { ecode = e_cmd; goto eof; } /* got the command in the 'cmd' string; save it in *e. */ e->cmd = strdup(cmd); if (e->cmd == NULL) { warn("strdup(\"%s\")", cmd); ecode = e_mem; goto eof; } Debug(DPARS, ("load_entry()...returning successfully\n")) /* success, fini, return pointer to the entry we just created... */ return e; eof: free_entry(e); if (ecode != e_none && error_func) (*error_func)(ecodes[(int)ecode]); while (ch != EOF && ch != '\n') ch = get_char(file); return NULL; } +/* + * bits one bit per flag, default=FALSE + * low, high bounds, impl. offset for bitstr + * names NULL or names for these elements + * ch current character being processed + * file file being read + */ static char -get_list(bitstr_t *bits, int low, int high, char *names[], int ch, FILE *file) +get_list(bitstr_t *bits, int low, int high, const char *names[], int ch, + FILE *file) { - register int done; + int done; /* we know that we point to a non-blank character here; * must do a Skip_Blanks before we exit, so that the * next call (or the code that picks up the cmd) can * assume the same thing. */ Debug(DPARS|DEXT, ("get_list()...entered\n")) /* list = range {"," range} */ /* clear the bit string, since the default is 'off'. */ bit_nclear(bits, 0, (high-low+1)); /* process all ranges */ done = FALSE; while (!done) { ch = get_range(bits, low, high, names, ch, file); if (ch == ',') ch = get_char(file); else done = TRUE; } /* exiting. skip to some blanks, then skip over the blanks. */ Skip_Nonblanks(ch, file) Skip_Blanks(ch, file) Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch)) return ch; } +/* + * bits one bit per flag, default=FALSE + * low, high bounds, impl. offset for bitstr + * names NULL or names for these elements + * ch current character being processed + * file file being read + */ static char -get_range(bitstr_t *bits, int low, int high, char *names[], int ch, FILE *file) +get_range(bitstr_t *bits, int low, int high, const char *names[], int ch, + FILE *file) { /* range = number | number "-" number [ "/" number ] */ - register int i; - auto int num1, num2, num3; + int i, num1, num2, num3; Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n")) if (ch == '*') { /* '*' means "first-last" but can still be modified by /step */ num1 = low; num2 = high; ch = get_char(file); if (ch == EOF) return EOF; } else { if (EOF == (ch = get_number(&num1, low, names, ch, file))) return EOF; if (ch == '/') num2 = high; else if (ch != '-') { /* not a range, it's a single number. */ if (EOF == set_element(bits, low, high, num1)) return EOF; return ch; } else { /* eat the dash */ ch = get_char(file); if (ch == EOF) return EOF; /* get the number following the dash */ ch = get_number(&num2, low, names, ch, file); if (ch == EOF) return EOF; } } /* check for step size */ if (ch == '/') { /* eat the slash */ ch = get_char(file); if (ch == EOF) return EOF; /* get the step size -- note: we don't pass the * names here, because the number is not an * element id, it's a step size. 'low' is * sent as a 0 since there is no offset either. */ ch = get_number(&num3, 0, PPC_NULL, ch, file); if (ch == EOF || num3 == 0) return EOF; } else { /* no step. default==1. */ num3 = 1; } /* range. set all elements from num1 to num2, stepping * by num3. (the step is a downward-compatible extension * proposed conceptually by bob@acornrc, syntactically * designed then implmented by paul vixie). */ for (i = num1; i <= num2; i += num3) if (EOF == set_element(bits, low, high, i)) return EOF; return ch; } +/* + * numptr where does the result go? + * low offset applied to enum result + * names symbolic names, if any, for enums + * ch current character + * file source + */ static char -get_number(int *numptr, int low, char *names[], int ch, FILE *file) +get_number(int *numptr, int low, const char *names[], int ch, FILE *file) { char temp[MAX_TEMPSTR], *pc; int len, i, all_digits; /* collect alphanumerics into our fixed-size temp array */ pc = temp; len = 0; all_digits = TRUE; while (isalnum(ch)) { if (++len >= MAX_TEMPSTR) return EOF; *pc++ = ch; if (!isdigit(ch)) all_digits = FALSE; ch = get_char(file); } *pc = '\0'; if (len == 0) return (EOF); /* try to find the name in the name list */ if (names) { for (i = 0; names[i] != NULL; i++) { Debug(DPARS|DEXT, ("get_num, compare(%s,%s)\n", names[i], temp)) if (!strcasecmp(names[i], temp)) { *numptr = i+low; return ch; } } } /* no name list specified, or there is one and our string isn't * in it. either way: if it's all digits, use its magnitude. * otherwise, it's an error. */ if (all_digits) { *numptr = atoi(temp); return ch; } return EOF; } static int set_element(bitstr_t *bits, int low, int high, int number) { Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number)) if (number < low || number > high) return EOF; bit_set(bits, (number-low)); return OK; } diff --git a/usr.sbin/cron/lib/env.c b/usr.sbin/cron/lib/env.c index 77fbdf0c2627..36c5fca12117 100644 --- a/usr.sbin/cron/lib/env.c +++ b/usr.sbin/cron/lib/env.c @@ -1,261 +1,263 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ #if !defined(lint) && !defined(LINT) -static const char rcsid[] = - "$FreeBSD$"; +static const char rcsid[] = "$Id: env.c,v 1.3 1998/08/14 00:32:39 vixie Exp $"; #endif #include "cron.h" char ** env_init(void) { - register char **p = (char **) malloc(sizeof(char *)); + char **p = (char **) malloc(sizeof(char **)); if (p) p[0] = NULL; return (p); } void env_free(char **envp) { char **p; if ((p = envp)) for (; *p; p++) free(*p); free(envp); } char ** env_copy(char **envp) { - register int count, i; - register char **p; + int count, i; + char **p; for (count = 0; envp[count] != NULL; count++) ; p = (char **) malloc((count+1) * sizeof(char *)); /* 1 for the NULL */ if (p == NULL) { errno = ENOMEM; return NULL; } for (i = 0; i < count; i++) if ((p[i] = strdup(envp[i])) == NULL) { while (--i >= 0) (void) free(p[i]); free(p); errno = ENOMEM; return NULL; } p[count] = NULL; return (p); } char ** env_set(char **envp, char *envstr) { - register int count, found; - register char **p; - char *q; + int count, found; + char **p; + char *q; /* * count the number of elements, including the null pointer; * also set 'found' to -1 or index of entry if already in here. */ found = -1; for (count = 0; envp[count] != NULL; count++) { if (!strcmp_until(envp[count], envstr, '=')) found = count; } count++; /* for the NULL */ if (found != -1) { /* * it exists already, so just free the existing setting, * save our new one there, and return the existing array. */ q = envp[found]; if ((envp[found] = strdup(envstr)) == NULL) { envp[found] = q; /* XXX env_free(envp); */ errno = ENOMEM; return NULL; } free(q); return (envp); } /* * it doesn't exist yet, so resize the array, move null pointer over * one, save our string over the old null pointer, and return resized * array. */ p = (char **) realloc((void *) envp, (unsigned) ((count+1) * sizeof(char *))); if (p == NULL) { /* XXX env_free(envp); */ errno = ENOMEM; return NULL; } p[count] = p[count-1]; if ((p[count-1] = strdup(envstr)) == NULL) { env_free(p); errno = ENOMEM; return NULL; } return (p); } /* return ERR = end of file * FALSE = not an env setting (file was repositioned) * TRUE = was an env setting */ int load_env(char *envstr, FILE *f) { long filepos; int fileline; char name[MAX_ENVSTR], val[MAX_ENVSTR]; char quotechar, *c, *str; int state; /* The following states are traversed in order: */ #define NAMEI 0 /* First char of NAME, may be quote */ #define NAME 1 /* Subsequent chars of NAME */ #define EQ1 2 /* After end of name, looking for '=' sign */ #define EQ2 3 /* After '=', skipping whitespace */ #define VALUEI 4 /* First char of VALUE, may be quote */ #define VALUE 5 /* Subsequent chars of VALUE */ #define FINI 6 /* All done, skipping trailing whitespace */ #define ERROR 7 /* Error */ filepos = ftell(f); fileline = LineNumber; skip_comments(f); if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n")) return (ERR); Debug(DPARS, ("load_env, read <%s>\n", envstr)); bzero (name, sizeof name); bzero (val, sizeof val); str = name; state = NAMEI; quotechar = '\0'; c = envstr; while (state != ERROR && *c) { switch (state) { case NAMEI: case VALUEI: if (*c == '\'' || *c == '"') quotechar = *c++; ++state; /* FALLTHROUGH */ case NAME: case VALUE: if (quotechar) { if (*c == quotechar) { state++; c++; break; } if (state == NAME && *c == '=') { state = ERROR; break; } } else { if (state == NAME) { if (isspace (*c)) { c++; state++; break; } if (*c == '=') { state++; break; } } } *str++ = *c++; break; case EQ1: if (*c == '=') { state++; str = val; quotechar = '\0'; } else { if (!isspace (*c)) state = ERROR; } c++; break; case EQ2: case FINI: if (isspace (*c)) c++; else state++; break; } } if (state != FINI && !(state == VALUE && !quotechar)) { Debug(DPARS, ("load_env, parse error, state = %d\n", state)) fseek(f, filepos, 0); Set_LineNum(fileline); return (FALSE); } if (state == VALUE) { /* End of unquoted value: trim trailing whitespace */ c = val + strlen (val); while (c > val && isspace (*(c - 1))) *(--c) = '\0'; } /* 2 fields from parser; looks like an env setting */ - if (strlen(name) + 1 + strlen(val) >= MAX_ENVSTR-1) + if (snprintf(envstr, MAX_ENVSTR, "%s=%s", name, val) >= MAX_ENVSTR) return (FALSE); - (void) sprintf(envstr, "%s=%s", name, val); Debug(DPARS, ("load_env, <%s> <%s> -> <%s>\n", name, val, envstr)) return (TRUE); } char * env_get(char *name, char **envp) { - register int len = strlen(name); - register char *p, *q; + int len = strlen(name); + char *p, *q; - while ((p = *envp++)) { + while ((p = *envp++) != NULL) { if (!(q = strchr(p, '='))) continue; if ((q - p) == len && !strncmp(p, name, len)) return (q+1); } return (NULL); } diff --git a/usr.sbin/cron/lib/misc.c b/usr.sbin/cron/lib/misc.c index 5746bc33ca51..7f0257d7b469 100644 --- a/usr.sbin/cron/lib/misc.c +++ b/usr.sbin/cron/lib/misc.c @@ -1,578 +1,587 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 1997 by Internet Software Consortium * - * Distribute freely, except: don't remove my name from the source or - * documentation (don't take credit for my work), mark your changes (don't - * get me blamed for your possible bugs), don't alter or remove this - * notice. May be sold if buildable source is provided to buyer. No - * warrantee of any kind, express or implied, is included with this - * software; use at your own risk, responsibility for damages (if any) to - * anyone resulting from the use of this software rests entirely with the - * user. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Send bug reports, bug fixes, enhancements, requests, flames, etc., and - * I'll try to keep a version up to date. I can be reached as follows: - * Paul Vixie uunet!decwrl!vixie!paul + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ #if !defined(lint) && !defined(LINT) -static const char rcsid[] = - "$FreeBSD$"; +static const char rcsid[] = "$Id: misc.c,v 1.5 1998/08/14 00:32:40 vixie Exp $"; #endif /* vix 26jan87 [RCS has the rest of the log] * vix 30dec86 [written] */ #include "cron.h" #if SYS_TIME_H # include #else # include #endif #include #include -#include #include #include #include #if defined(SYSLOG) # include #endif +#if defined(LOG_CRON) && defined(LOG_FILE) +# undef LOG_FILE +#endif + #if defined(LOG_DAEMON) && !defined(LOG_CRON) -#define LOG_CRON LOG_DAEMON +# define LOG_CRON LOG_DAEMON #endif static int LogFD = ERR; int -strcmp_until(char *left, char *right, int until) +strcmp_until(const char *left, const char *right, int until) { - register int diff; - while (*left && *left != until && *left == *right) { left++; right++; } if ((*left=='\0' || *left == until) && (*right=='\0' || *right == until)) { - diff = 0; - } else { - diff = *left - *right; + return (0); } - - return diff; + return (*left - *right); } /* strdtb(s) - delete trailing blanks in string 's' and return new length */ int strdtb(char *s) { char *x = s; /* scan forward to the null */ while (*x) x++; /* scan backward to either the first character before the string, * or the last non-blank in the string, whichever comes first. */ do {x--;} while (x >= s && isspace(*x)); /* one character beyond where we stopped above is where the null * goes. */ *++x = '\0'; /* the difference between the position of the null character and * the position of the first character of the string is the length. */ return x - s; } int set_debug_flags(char *flags) { /* debug flags are of the form flag[,flag ...] * * if an error occurs, print a message to stdout and return FALSE. * otherwise return TRUE after setting ERROR_FLAGS. */ #if !DEBUGGING printf("this program was compiled without debugging enabled\n"); return FALSE; #else /* DEBUGGING */ char *pc = flags; DebugFlags = 0; while (*pc) { - char **test; - int mask; + const char **test; + int mask; /* try to find debug flag name in our list. */ for ( test = DebugFlagNames, mask = 1; - *test && strcmp_until(*test, pc, ','); + *test != NULL && strcmp_until(*test, pc, ','); test++, mask <<= 1 ) ; if (!*test) { fprintf(stderr, "unrecognized debug flag <%s> <%s>\n", flags, pc); return FALSE; } DebugFlags |= mask; /* skip to the next flag */ while (*pc && *pc != ',') pc++; if (*pc == ',') pc++; } if (DebugFlags) { int flag; fprintf(stderr, "debug flags enabled:"); for (flag = 0; DebugFlagNames[flag]; flag++) if (DebugFlags & (1 << flag)) fprintf(stderr, " %s", DebugFlagNames[flag]); fprintf(stderr, "\n"); } return TRUE; #endif /* DEBUGGING */ } void set_cron_uid(void) { #if defined(BSD) || defined(POSIX) if (seteuid(ROOT_UID) < OK) err(ERROR_EXIT, "seteuid"); #else if (setuid(ROOT_UID) < OK) err(ERROR_EXIT, "setuid"); #endif } void set_cron_cwd(void) { struct stat sb; /* first check for CRONDIR ("/var/cron" or some such) */ if (stat(CRONDIR, &sb) < OK && errno == ENOENT) { warn("%s", CRONDIR); if (OK == mkdir(CRONDIR, 0700)) { warnx("%s: created", CRONDIR); stat(CRONDIR, &sb); } else { err(ERROR_EXIT, "%s: mkdir", CRONDIR); } } if (!(sb.st_mode & S_IFDIR)) err(ERROR_EXIT, "'%s' is not a directory, bailing out", CRONDIR); if (chdir(CRONDIR) < OK) err(ERROR_EXIT, "cannot chdir(%s), bailing out", CRONDIR); /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such) */ if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) { warn("%s", SPOOL_DIR); if (OK == mkdir(SPOOL_DIR, 0700)) { warnx("%s: created", SPOOL_DIR); stat(SPOOL_DIR, &sb); } else { err(ERROR_EXIT, "%s: mkdir", SPOOL_DIR); } } if (!(sb.st_mode & S_IFDIR)) err(ERROR_EXIT, "'%s' is not a directory, bailing out", SPOOL_DIR); } /* get_char(file) : like getc() but increment LineNumber on newlines */ int get_char(FILE *file) { int ch; ch = getc(file); if (ch == '\n') Set_LineNum(LineNumber + 1) return ch; } /* unget_char(ch, file) : like ungetc but do LineNumber processing */ void unget_char(int ch, FILE *file) { ungetc(ch, file); if (ch == '\n') Set_LineNum(LineNumber - 1) } /* get_string(str, max, file, termstr) : like fgets() but * (1) has terminator string which should include \n * (2) will always leave room for the null * (3) uses get_char() so LineNumber will be accurate * (4) returns EOF or terminating character, whichever */ int get_string(char *string, int size, FILE *file, char *terms) { int ch; while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) { if (size > 1) { *string++ = (char) ch; size--; } } if (size > 0) *string = '\0'; return ch; } /* skip_comments(file) : read past comment (if any) */ void skip_comments(FILE *file) { int ch; while (EOF != (ch = get_char(file))) { /* ch is now the first character of a line. */ while (ch == ' ' || ch == '\t') ch = get_char(file); if (ch == EOF) break; /* ch is now the first non-blank character of a line. */ if (ch != '\n' && ch != '#') break; /* ch must be a newline or comment as first non-blank * character on a line. */ while (ch != '\n' && ch != EOF) ch = get_char(file); /* ch is now the newline of a line which we're going to * ignore. */ } if (ch != EOF) unget_char(ch, file); } /* int in_file(char *string, FILE *file) * return TRUE if one of the lines in file matches string exactly, * FALSE otherwise. */ static int in_file(char *string, FILE *file) { char line[MAX_TEMPSTR]; rewind(file); while (fgets(line, MAX_TEMPSTR, file)) { if (line[0] != '\0') if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = '\0'; if (0 == strcmp(line, string)) return TRUE; } return FALSE; } /* int allowed(char *username) * returns TRUE if (ALLOW_FILE exists and user is listed) * or (DENY_FILE exists and user is NOT listed) * or (neither file exists but user=="root" so it's okay) */ int allowed(char *username) { FILE *allow, *deny; int isallowed; isallowed = FALSE; deny = NULL; #if defined(ALLOW_FILE) && defined(DENY_FILE) if ((allow = fopen(ALLOW_FILE, "r")) == NULL && errno != ENOENT) goto out; if ((deny = fopen(DENY_FILE, "r")) == NULL && errno != ENOENT) goto out; Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny)) #else allow = NULL; #endif if (allow) isallowed = in_file(username, allow); else if (deny) isallowed = !in_file(username, deny); else { #if defined(ALLOW_ONLY_ROOT) isallowed = (strcmp(username, ROOT_USER) == 0); #else isallowed = TRUE; #endif } out: if (allow) fclose(allow); if (deny) fclose(deny); return (isallowed); } void -log_it(char *username, int xpid, char *event, const char *detail) +log_it(const char *username, int xpid, const char *event, const char *detail) { #if defined(LOG_FILE) || DEBUGGING - PID_T pid = xpid; + PID_T pid = xpid; #endif #if defined(LOG_FILE) - char *msg; - TIME_T now = time((TIME_T) 0); - register struct tm *t = localtime(&now); + char *msg; + TIME_T now = time((TIME_T) 0); + struct tm *t = localtime(&now); #endif /*LOG_FILE*/ #if defined(SYSLOG) - static int syslog_open = 0; + static int syslog_open = 0; #endif #if defined(LOG_FILE) /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation. */ msg = malloc(strlen(username) + strlen(event) + strlen(detail) + MAX_TEMPSTR); if (msg == NULL) warnx("failed to allocate memory for log message"); else { if (LogFD < OK) { LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600); if (LogFD < OK) { warn("can't open log file %s", LOG_FILE); } else { (void) fcntl(LogFD, F_SETFD, 1); } } /* we have to sprintf() it because fprintf() doesn't always * write everything out in one chunk and this has to be * atomically appended to the log file. */ sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n", username, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, pid, event, detail); /* we have to run strlen() because sprintf() returns (char*) * on old BSD. */ if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) { if (LogFD >= OK) warn("%s", LOG_FILE); warnx("can't write to log file"); write(STDERR, msg, strlen(msg)); } free(msg); } #endif /*LOG_FILE*/ #if defined(SYSLOG) if (!syslog_open) { /* we don't use LOG_PID since the pid passed to us by * our client may not be our own. therefore we want to * print the pid ourselves. */ # ifdef LOG_DAEMON openlog(ProgramName, LOG_PID, LOG_CRON); # else openlog(ProgramName, LOG_PID); # endif syslog_open = TRUE; /* assume openlog success */ } syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail); #endif /*SYSLOG*/ #if DEBUGGING if (DebugFlags) { fprintf(stderr, "log_it: (%s %d) %s (%s)\n", username, pid, event, detail); } #endif } void log_close(void) { if (LogFD != ERR) { close(LogFD); LogFD = ERR; } } /* two warnings: * (1) this routine is fairly slow * (2) it returns a pointer to static storage + * parameters: + * s: string we want the first word of + * t: terminators, implicitly including \0 */ char * first_word(char *s, char *t) { static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */ static int retsel = 0; - register char *rb, *rp; + char *rb, *rp; /* select a return buffer */ retsel = 1-retsel; rb = &retbuf[retsel][0]; rp = rb; /* skip any leading terminators */ while (*s && (NULL != strchr(t, *s))) { s++; } /* copy until next terminator or full buffer */ while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) { *rp++ = *s++; } /* finish the return-string and return it */ *rp = '\0'; return rb; } /* warning: * heavily ascii-dependent. */ static void -mkprint(register char *dst, register unsigned char *src, register int len) +mkprint(char *dst, unsigned char *src, int len) { + /* + * XXX + * We know this routine can't overflow the dst buffer because mkprints() + * allocated enough space for the worst case. + */ while (len-- > 0) { - register unsigned char ch = *src++; + unsigned char ch = *src++; if (ch < ' ') { /* control character */ *dst++ = '^'; *dst++ = ch + '@'; } else if (ch < 0177) { /* printable */ *dst++ = ch; } else if (ch == 0177) { /* delete/rubout */ *dst++ = '^'; *dst++ = '?'; } else { /* parity character */ sprintf(dst, "\\%03o", ch); dst += 4; } } *dst = '\0'; } /* warning: * returns a pointer to malloc'd storage, you must call free yourself. */ char * mkprints(unsigned char *src, unsigned int len) { - register char *dst = malloc(len*4 + 1); + char *dst = malloc(len*4 + 1); if (dst != NULL) mkprint(dst, src, len); return dst; } #ifdef MAIL_DATE /* Sat, 27 Feb 93 11:44:51 CST * 123456789012345678901234567 */ char * arpadate(time_t *clock) { time_t t = clock ?*clock :time(0L); struct tm *tm = localtime(&t); - static char ret[32]; /* zone name might be >3 chars */ + static char ret[60]; /* zone name might be >3 chars */ if (tm->tm_year >= 100) tm->tm_year += 1900; (void) snprintf(ret, sizeof(ret), "%s, %2d %s %d %02d:%02d:%02d %s", DowNames[tm->tm_wday], tm->tm_mday, MonthNames[tm->tm_mon], tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec, TZONE(*tm)); return ret; } #endif /*MAIL_DATE*/ #ifdef HAVE_SAVED_UIDS static int save_euid; int swap_uids(void) { save_euid = geteuid(); return seteuid(getuid()); } int swap_uids_back(void) { return seteuid(save_euid); } #else /*HAVE_SAVED_UIDS*/ int swap_uids(void) { return setreuid(geteuid(), getuid()); } int swap_uids_back(void) { return swap_uids(); } #endif /*HAVE_SAVED_UIDS*/