diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/Makefile cron/Makefile --- /home/tmw/head/head/usr.sbin/cron/Makefile 2014-06-23 13:52:17.000000000 +0200 +++ cron/Makefile 2014-06-06 12:22:40.000000000 +0200 @@ -1,4 +1,4 @@ -# $FreeBSD: head/usr.sbin/cron/Makefile 80029 2001-07-20 06:20:32Z obrien $ +# $FreeBSD: releng/10.0/usr.sbin/cron/Makefile 80029 2001-07-20 06:20:32Z obrien $ SUBDIR= lib cron crontab diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/Makefile.inc cron/Makefile.inc --- /home/tmw/head/head/usr.sbin/cron/Makefile.inc 2014-06-23 13:52:17.000000000 +0200 +++ cron/Makefile.inc 2014-06-06 12:22:40.000000000 +0200 @@ -1,4 +1,4 @@ -# $FreeBSD: head/usr.sbin/cron/Makefile.inc 147225 2005-06-10 06:12:53Z des $ +# $FreeBSD: releng/10.0/usr.sbin/cron/Makefile.inc 147225 2005-06-10 06:12:53Z des $ LIBCRON= ${.OBJDIR}/../lib/libcron.a diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/Makefile cron/cron/Makefile --- /home/tmw/head/head/usr.sbin/cron/cron/Makefile 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/Makefile 2014-06-06 12:22:40.000000000 +0200 @@ -1,4 +1,4 @@ -# $FreeBSD: head/usr.sbin/cron/cron/Makefile 267233 2014-06-08 17:29:31Z bdrewery $ +# $FreeBSD: releng/10.0/usr.sbin/cron/cron/Makefile 201390 2010-01-02 11:07:44Z ed $ PROG= cron MAN= cron.8 @@ -11,6 +11,4 @@ WARNS?= 2 -NO_PIE= yes - .include diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/compat.h cron/cron/compat.h --- /home/tmw/head/head/usr.sbin/cron/cron/compat.h 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/compat.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,140 +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: head/usr.sbin/cron/cron/compat.h 50479 1999-08-28 01:35:59Z peter $ - */ - -#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) && !defined(HPUX) && !defined(CONVEX) && !defined(__linux) -# define NEED_VFORK -#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 -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/config.h cron/cron/config.h --- /home/tmw/head/head/usr.sbin/cron/cron/config.h 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/config.h 2014-06-06 12:26:53.000000000 +0200 @@ -1,29 +1,29 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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 +/* config.h - configurables for ISC Cron * - * $FreeBSD: head/usr.sbin/cron/cron/config.h 50479 1999-08-28 01:35:59Z peter $ + * $Id: config.h,v 1.10 2004/01/23 18:56:42 vixie Exp $ */ -#if !defined(_PATH_SENDMAIL) -# define _PATH_SENDMAIL "/usr/lib/sendmail" -#endif /*SENDMAIL*/ - /* * these are site-dependent */ @@ -33,44 +33,38 @@ #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" /*-*/ - /* -Fx = set full-name of sender +#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 + * -oi = Ignore "." alone on a line + * -t = Get recipient from headers */ +#define MAILARG _PATH_SENDMAIL /*-*/ -/* #define MAILCMD "/bin/mail" */ /*-*/ -/* #define MAILARGS "%s -d %s" */ /*-*/ - /* -d = undocumented but common flag: deliver locally? +/* #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 */ /*-*/ +/* #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 */ /*-*/ - /* 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 @@ -85,3 +79,27 @@ * places. */ #define SYSLOG /*-*/ + + /* if you want cron to capitalize its name in ps + * when running a job. Does not work on SYSV. + */ +/*#define CAPITALIZE_FOR_PS */ + + /* if you have a tm_gmtoff member in struct tm. + * If not, we will have to compute the value ourselves. + */ +/*#define HAVE_TM_GMTOFF */ + + /* if your OS supports a BSD-style login.conf file */ +/*#define LOGIN_CAP */ + + /* if your OS supports BSD authentication */ +/*#define BSD_AUTH */ + + /* Define this to run crontab setgid instead of + * setuid root. Group access will be used to read + * the tabs/atjobs dirs and the allow/deny files. + * If this is not defined then crontab and at + * must be setuid root. + */ +/*#define CRON_GROUP "crontab" */ diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/cron.8 cron/cron/cron.8 --- /home/tmw/head/head/usr.sbin/cron/cron/cron.8 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/cron.8 2014-06-06 12:22:40.000000000 +0200 @@ -15,7 +15,7 @@ .\" * Paul Vixie uunet!decwrl!vixie!paul .\" */ .\" -.\" $FreeBSD: head/usr.sbin/cron/cron/cron.8 267668 2014-06-20 09:57:27Z bapt $ +.\" $FreeBSD: releng/10.0/usr.sbin/cron/cron/cron.8 180096 2008-06-29 16:56:18Z marck $ .\" .Dd June 29, 2008 .Dt CRON 8 @@ -218,4 +218,4 @@ .Xr crontab 5 , .Xr pam.conf 5 .Sh AUTHORS -.An Paul Vixie Aq Mt paul@vix.com +.An Paul Vixie Aq paul@vix.com diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/cron.c cron/cron/cron.c --- /home/tmw/head/head/usr.sbin/cron/cron/cron.c 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/cron.c 2014-06-11 14:31:15.000000000 +0200 @@ -1,142 +1,137 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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: head/usr.sbin/cron/cron/cron.c 261146 2014-01-25 02:16:09Z ache $"; -#endif +//#if !defined(lint) && !defined(LINT) +//static char rcsid[] = "$Id: cron.c,v 1.12 2004/01/23 18:56:42 vixie Exp $"; +//#endif #define MAIN_PROGRAM - +#include +#include #include "cron.h" -#include -#include -#if SYS_TIME_H -# include -#else -# include -#endif +enum timejump { negative, small, medium, large }; 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 + find_jobs(int, cron_db *, int, int), + set_time(int), + cron_sleep(int), sigchld_handler(int), -#endif sighup_handler(int), + sigchld_reaper(void), + quit(int), parse_args(int c, char *v[]); -static int run_at_secres(cron_db *); - -static time_t last_time = 0; -static int dst_enabled = 0; -struct pidfh *pfh; - -static void -usage() { -#if DEBUGGING - char **dflags; -#endif - - fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] " - "[-m mailto] [-s] [-o] [-x debugflag[,...]]\n"); -#if DEBUGGING - fprintf(stderr, "\ndebugflags: "); - - for(dflags = DebugFlagNames; *dflags; dflags++) { - fprintf(stderr, "%s ", *dflags); - } - fprintf(stderr, "\n"); -#endif +static volatile sig_atomic_t got_sighup, got_sigchld; +static int timeRunning, virtualTime, clockTime; +static int cronSock; +static long GMToff; +static cron_db database; + +struct pidfh *pfh; +static void +usage(void) { + const char **dflags; + + fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] "); + fprintf(stderr, "%s [-n] [-x [", ProgramName); + for (dflags = DebugFlagNames; *dflags; dflags++) + fprintf(stderr, "%s%s", *dflags, dflags[1] ? "," : "]"); + fprintf(stderr, "]\n"); exit(ERROR_EXIT); } static void open_pidfile(void) { - char pidfile[MAX_FNAME]; - 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); - } + char pidfile[MAX_FNAME]; + 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(argc, argv) - int argc; - char *argv[]; -{ - cron_db database; - int runnum; - int secres1, secres2; - struct tm *tm; +main(int argc, char *argv[]) { + struct sigaction sact; ProgramName = argv[0]; + setlocale(LC_ALL, ""); + #if defined(BSD) setlinebuf(stdout); setlinebuf(stderr); #endif + NoFork = 0; parse_args(argc, argv); -#ifdef USE_SIGCHLD - (void) signal(SIGCHLD, sigchld_handler); -#else - (void) signal(SIGCLD, SIG_IGN); + bzero((char *)&sact, sizeof sact); + sigemptyset(&sact.sa_mask); + sact.sa_flags = 0; +#ifdef SA_RESTART + sact.sa_flags |= SA_RESTART; #endif - (void) signal(SIGHUP, sighup_handler); + sact.sa_handler = sigchld_handler; + (void) sigaction(SIGCHLD, &sact, NULL); + sact.sa_handler = sighup_handler; + (void) sigaction(SIGHUP, &sact, NULL); + sact.sa_handler = quit; + (void) sigaction(SIGINT, &sact, NULL); + (void) sigaction(SIGTERM, &sact, NULL); open_pidfile(); + cronSock = open_socket(); set_cron_uid(); set_cron_cwd(); -#if defined(POSIX) - setenv("PATH", _PATH_DEFPATH, 1); -#endif + if (putenv("PATH="_PATH_DEFPATH) < 0) { + log_it("CRON", getpid(), "DEATH", "can't malloc"); + exit(1); + } /* 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 DEBUGGING + (void) fprintf(stderr, "[%ld] cron started\n", (long)getpid()); +#endif + } else if (NoFork == 0) { if (daemon(1, 0) == -1) { pidfile_remove(pfh); log_it("CRON",getpid(),"DEATH","can't become daemon"); @@ -145,160 +140,180 @@ } if (madvise(NULL, 0, MADV_PROTECT) != 0) - log_it("CRON", getpid(), "WARNING", "madvise() failed"); + log_it("CRON", getpid(), "WARNING", "madvise() filed"); pidfile_write(pfh); database.head = NULL; database.tail = NULL; database.mtime = (time_t) 0; load_database(&database); - secres1 = secres2 = run_at_secres(&database); + set_time(TRUE); run_reboot_jobs(&database); - cron_sync(secres1); - runnum = 0; + timeRunning = virtualTime = clockTime; + + /* + * Too many clocks, not enough time (Al. Einstein) + * These clocks are in minutes since the epoch, adjusted for timezone. + * virtualTime: is the time it *would* be if we woke up + * promptly and nobody ever changed the clock. It is + * monotonically increasing... unless a timejump happens. + * At the top of the loop, all jobs for 'virtualTime' have run. + * timeRunning: is the time we last awakened. + * clockTime: is the time when set_time was last called. + */ 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; - } - } + int timeDiff; + enum timejump wakeupKind; + + /* ... wait for the time (in minutes) to change ... */ + do { + cron_sleep(timeRunning + 1); + set_time(FALSE); + } while (clockTime == timeRunning); + timeRunning = clockTime; + + /* + * Calculate how the current time differs from our virtual + * clock. Classify the change into one of 4 cases. + */ + timeDiff = timeRunning - virtualTime; + + /* shortcut for the most common case */ + if (timeDiff == 1) { + virtualTime = timeRunning; + find_jobs(virtualTime, &database, TRUE, TRUE); + } else { + if (timeDiff > (3*MINUTE_COUNT) || + timeDiff < -(3*MINUTE_COUNT)) + wakeupKind = large; + else if (timeDiff > 5) + wakeupKind = medium; + else if (timeDiff > 0) + wakeupKind = small; + else + wakeupKind = negative; + + switch (wakeupKind) { + case small: + /* + * case 1: timeDiff is a small positive number + * (wokeup late) run jobs for each virtual + * minute until caught up. + */ + Debug(DSCH, ("[%ld], normal case %d minutes to go\n", + (long)getpid(), timeDiff)) + do { + if (job_runqueue()) + sleep(10); + virtualTime++; + find_jobs(virtualTime, &database, + TRUE, TRUE); + } while (virtualTime < timeRunning); + break; + + case medium: + /* + * case 2: timeDiff is a medium-sized positive + * number, for example because we went to DST + * run wildcard jobs once, then run any + * fixed-time jobs that would otherwise be + * skipped if we use up our minute (possible, + * if there are a lot of jobs to run) go + * around the loop again so that wildcard jobs + * have a chance to run, and we do our + * housekeeping. + */ + Debug(DSCH, ("[%ld], DST begins %d minutes to go\n", + (long)getpid(), timeDiff)) + /* run wildcard jobs for current minute */ + find_jobs(timeRunning, &database, TRUE, FALSE); + + /* run fixed-time jobs for each minute missed */ + do { + if (job_runqueue()) + sleep(10); + virtualTime++; + find_jobs(virtualTime, &database, + FALSE, TRUE); + set_time(FALSE); + } while (virtualTime< timeRunning && + clockTime == timeRunning); + break; + + case negative: + /* + * case 3: timeDiff is a small or medium-sized + * negative num, eg. because of DST ending. + * Just run the wildcard jobs. The fixed-time + * jobs probably have already run, and should + * not be repeated. Virtual time does not + * change until we are caught up. + */ + Debug(DSCH, ("[%ld], DST ends %d minutes to go\n", + (long)getpid(), timeDiff)) + find_jobs(timeRunning, &database, TRUE, FALSE); + break; + default: + /* + * other: time has changed a *lot*, + * jump virtual time, and run everything + */ + Debug(DSCH, ("[%ld], clock jumped\n", + (long)getpid())) + virtualTime = timeRunning; + find_jobs(timeRunning, &database, TRUE, TRUE); } } - /* do this iteration - */ - cron_tick(&database, secres1); + /* Jobs to be run (if any) are loaded; clear the queue. */ + job_runqueue(); - /* sleep 1 or 60 seconds - */ - TargetTime += (secres1 != 0) ? 1 : 60; - runnum += 1; + /* Check to see if we received a signal while running jobs. */ + if (got_sighup) { + got_sighup = 0; + log_close(); + } + if (got_sigchld) { + got_sigchld = 0; + sigchld_reaper(); + } + load_database(&database); } } - static void -run_reboot_jobs(db) - cron_db *db; -{ - register user *u; - register entry *e; +run_reboot_jobs(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 & WHEN_REBOOT) { + 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); - } } } (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; +find_jobs(int vtime, cron_db *db, int doWild, int doNonWild) { + time_t virtualSecond = vtime * SECONDS_PER_MINUTE; + struct tm *tm = gmtime(&virtualSecond); + int minute, hour, dom, month, dow; + user *u; + entry *e; /* make 0-based values out of these so we can use them as indicies */ - 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) { - 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) { - 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) { - 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 indicies - */ - 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; - } - } + Debug(DSCH, ("[%ld] tick(%d,%d,%d,%d,%d) %s %s\n", + (long)getpid(), minute, hour, dom, month, dow, + doWild?" ":"No wildcard",doNonWild?" ":"Wildcard only")) /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the * first and fifteenth AND every Sunday; '* * * * Sun' will run *only* @@ -306,256 +321,195 @@ * 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 ( 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)) - ? (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)) + env_get("LOGNAME", e->envp), + e->uid, e->gid, e->cmd)) + if (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); + if ((doNonWild && + !(e->flags & (MIN_STAR|HR_STAR))) || + (doWild && (e->flags & (MIN_STAR|HR_STAR)))) + 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.. +/* + * Set StartTime and clockTime to the current time. + * These are used for computing what time it really is right now. + * Note that clockTime is a unix wallclock time converted to minutes. */ static void -cron_sync(int secres) { - struct tm *tm; +set_time(int initialize) { + struct tm tm; + static int isdst; - TargetTime = time((time_t*)0); - if (secres != 0) { - TargetTime += 1; - } else { - tm = localtime(&TargetTime); - TargetTime += (60 - tm->tm_sec); - } -} + StartTime = time(NULL); -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; + /* We adjust the time to GMT so we can catch DST changes. */ + tm = *localtime(&StartTime); + if (initialize || tm.tm_isdst != isdst) { + isdst = tm.tm_isdst; + GMToff = get_gmtoff(&StartTime, &tm); + Debug(DSCH, ("[%ld] GMToff=%ld\n", + (long)getpid(), (long)GMToff)) } + clockTime = (StartTime + GMToff) / (time_t)SECONDS_PER_MINUTE; } +/* + * Try to just hit the next minute. + */ 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); - +cron_sleep(int target) { + time_t t1, t2; + unsigned char poke; + int nfds, fd; + socklen_t sunlen; + static fd_set *fdsr; + struct sockaddr_un s_un; + + struct timeval seconds_to_wait; + seconds_to_wait.tv_usec = 0; + + t1 = time(NULL) + GMToff; + seconds_to_wait.tv_sec = (int)(target * SECONDS_PER_MINUTE - t1) + 1; + Debug(DSCH, ("[%ld] Target time=%ld, sec-to-wait=%ld\n", + (long)getpid(), (long)target*SECONDS_PER_MINUTE, seconds_to_wait.tv_sec)) + + fdsr = (fd_set *)calloc(howmany(cronSock + 1, NFDBITS), + sizeof(fd_mask)); + + while (seconds_to_wait.tv_sec > 0 && seconds_to_wait.tv_sec < 65) { + poke = RELOAD_CRON; + if (fdsr) + FD_SET(cronSock, fdsr); /* - * If the seconds_to_wait value is insane, jump the cron + * Check to see if we were interrupted by a signal. + * If so, service the signal(s) then continue sleeping + * where we left off. */ - - if (stime.tv_sec < -600 || stime.tv_sec > 600) { - cron_clean(db); - cron_sync(secres); - continue; + nfds = select(cronSock + 1, fdsr, NULL, NULL, &seconds_to_wait); + if (nfds == 0) + break; /* timer expired */ + if (nfds == -1 && errno != EINTR) + break; /* an error occurred */ + if (nfds > 0) { + Debug(DSCH, ("[%ld] Got a poke on the socket\n", + (long)getpid())) + sunlen = sizeof(s_un); + fd = accept(cronSock, (struct sockaddr *)&s_un, &sunlen); + if (fd >= 0 && fcntl(fd, F_SETFL, O_NONBLOCK) == 0) { + (void) read(fd, &poke, 1); + close(fd); + if (poke & RELOAD_CRON) { + database.mtime = (time_t)0; + load_database(&database); + } + } } - - 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; + else { + if (got_sighup) { + got_sighup = 0; + log_close(); + } + if (got_sigchld) { + got_sigchld = 0; + sigchld_reaper(); } } + t2 = time(NULL) + GMToff; + seconds_to_wait.tv_sec -= (int)(t2 - t1); + t1 = t2; } } - -/* if the time was changed abruptly, clear the flags related - * to the daylight time switch handling to avoid strange effects - */ - static void -cron_clean(db) - cron_db *db; -{ - user *u; - entry *e; +sighup_handler(int x) { + got_sighup = 1; +} - last_time = 0; +static void +sigchld_handler(int x) { + got_sigchld = 1; +} - for (u = db->head; u != NULL; u = u->next) { - for (e = u->crontab; e != NULL; e = e->next) { - e->flags &= ~(RUN_AT|NOT_UNTIL); - } - } +static void +quit(int x) { + pidfile_remove(pfh); + _exit(0); } -#ifdef USE_SIGCHLD static void -sigchld_handler(int x) -{ - WAIT_T waiter; - PID_T pid; +sigchld_reaper(void) { + WAIT_T waiter; + PID_T pid; - for (;;) { -#ifdef POSIX + do { pid = waitpid(-1, &waiter, WNOHANG); -#else - pid = wait3(&waiter, WNOHANG, (struct rusage *)0); -#endif switch (pid) { case -1: + if (errno == EINTR) + continue; Debug(DPROC, - ("[%d] sigchld...no children\n", getpid())) - return; + ("[%ld] sigchld...no children\n", + (long)getpid())) + break; case 0: Debug(DPROC, - ("[%d] sigchld...no dead kids\n", getpid())) - return; + ("[%ld] sigchld...no dead kids\n", + (long)getpid())) + break; default: Debug(DPROC, - ("[%d] sigchld...pid #%d died, stat=%d\n", - getpid(), pid, WEXITSTATUS(waiter))) + ("[%ld] sigchld...pid #%ld died, stat=%d\n", + (long)getpid(), (long)pid, WEXITSTATUS(waiter))) + break; } - } + } while (pid > 0); } -#endif /*USE_SIGCHLD*/ - static void -sighup_handler(int x) -{ - log_close(); -} +parse_args(int argc, char *argv[]) { + int argch; + char *endp; -static void -parse_args(argc, argv) - int argc; - char *argv[]; -{ - int argch; - char *endp; - - while ((argch = getopt(argc, argv, "j:J:m:osx:")) != -1) { + while (-1 != (argch = getopt(argc, argv, "j:J:m:nx:"))) { switch (argch) { + default: + usage(); case 'j': Jitter = strtoul(optarg, &endp, 10); if (*optarg == '\0' || *endp != '\0' || Jitter > 60) errx(ERROR_EXIT, - "bad value for jitter: %s", optarg); + "bad value for jitter: %s", optarg); break; case 'J': RootJitter = strtoul(optarg, &endp, 10); - if (*optarg == '\0' || *endp != '\0' || RootJitter > 60) + if( *optarg == '\0' || *endp != '\0' || RootJitter > 60) errx(ERROR_EXIT, - "bad value for root jitter: %s", optarg); + "bad value for root jitter: %s", optarg); break; case 'm': defmailto = optarg; 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) != 0) - return 1; + case 'n': + NoFork = 1; + break; } } - return 0; } diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/cron.h cron/cron/cron.h --- /home/tmw/head/head/usr.sbin/cron/cron/cron.h 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/cron.h 2014-06-09 09:45:58.000000000 +0200 @@ -1,307 +1,46 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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: head/usr.sbin/cron/cron/cron.h 242101 2012-10-25 22:54:29Z sobomax $ + * $Id: cron.h,v 1.6 2004/01/23 18:56:42 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" +#define CRON_VERSION "V5.0" -#include -#include +unsigned Jitter, RootJitter; +extern unsigned Jitter, RootJitter; +extern struct pidfh *pfh; #include -#include #include -#include -#include -#include -#include -#include -#include "pathnames.h" +#define SYS_NAME "*system*" #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 MAX_UNAME 20 /* max length of username, should be overkill */ -#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; - 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); - 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 - 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 *, 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 *); - - - /* 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 -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/database.c cron/cron/database.c --- /home/tmw/head/head/usr.sbin/cron/cron/database.c 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/database.c 2014-06-09 11:31:10.000000000 +0200 @@ -1,55 +1,48 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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: head/usr.sbin/cron/cron/database.c 173412 2007-11-07 10:53:41Z kevlo $"; -#endif +//#if !defined(lint) && !defined(LINT) +//static char rcsid[] = "$Id: database.c,v 1.7 2004/01/23 18:56:42 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(old_db) - cron_db *old_db; -{ - DIR *dir; - struct stat statbuf; - struct stat syscron_stat; - DIR_T *dp; - cron_db new_db; - user *u, *nu; +load_database(cron_db *old_db) { + struct stat statbuf, syscron_stat; + cron_db new_db; + DIR_T *dp; + DIR *dir; + user *u, *nu; - Debug(DLOAD, ("[%d] load_database()\n", getpid())) + Debug(DLOAD, ("[%ld] load_database()\n", (long)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 @@ -73,8 +66,8 @@ * time this function is called. */ if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime)) { - Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n", - getpid())) + Debug(DLOAD, ("[%ld] spool dir mtime unch, no load needed.\n", + (long)getpid())) return; } @@ -86,11 +79,9 @@ new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime); new_db.head = new_db.tail = NULL; - if (syscron_stat.st_mtime) { - process_crontab("root", SYS_NAME, - SYSCRONTAB, &syscron_stat, + if (syscron_stat.st_mtime) + process_crontab("root", NULL, SYSCRONTAB, &syscron_stat, &new_db, old_db); - } /* 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 @@ -102,8 +93,7 @@ } 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 @@ -113,9 +103,13 @@ 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 (strlen(dp->d_name) >= sizeof fname) + continue; /* XXX log? */ + (void) strlcpy(fname, dp->d_name, sizeof(fname)); + + if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, + fname, '/')) + continue; /* XXX log? */ process_crontab(fname, fname, tabname, &statbuf, &new_db, old_db); @@ -144,12 +138,8 @@ Debug(DLOAD, ("load_database is done\n")) } - void -link_user(db, u) - cron_db *db; - user *u; -{ +link_user(cron_db *db, user *u) { if (db->head == NULL) db->head = u; if (db->tail) @@ -159,12 +149,8 @@ db->tail = u; } - void -unlink_user(db, u) - cron_db *db; - user *u; -{ +unlink_user(cron_db *db, user *u) { if (u->prev == NULL) db->head = u->next; else @@ -176,43 +162,36 @@ u->next->prev = u->prev; } - user * -find_user(db, name) - cron_db *db; - char *name; -{ - char *env_get(); - user *u; +find_user(cron_db *db, const char *name) { + 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(uname, fname, tabname, statbuf, new_db, old_db) - 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; + struct passwd *pw = NULL; + int crontab_fd = OK - 1; + user *u; - if (strcmp(fname, SYS_NAME) && !(pw = getpwnam(uname))) { + if (fname == NULL) { + /* must be set to something for logging purposes. + */ + fname = "*system*"; + } else if ((pw = getpwnam(uname)) == NULL) { /* 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) { + if ((crontab_fd = open(tabname, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0)) < OK) { /* crontab not accessible? */ log_it(fname, getpid(), "CAN'T OPEN", tabname); @@ -223,6 +202,23 @@ log_it(fname, getpid(), "FSTAT FAILED", tabname); goto next_crontab; } + if (!S_ISREG(statbuf->st_mode)) { + log_it(fname, getpid(), "NOT REGULAR", tabname); + goto next_crontab; + } + if ((statbuf->st_mode & 07777) != 0600) { + log_it(fname, getpid(), "BAD FILE MODE", tabname); + goto next_crontab; + } + if (statbuf->st_uid != ROOT_UID && (pw == NULL || + statbuf->st_uid != pw->pw_uid || strcmp(uname, pw->pw_name) != 0)) { + log_it(fname, getpid(), "WRONG FILE OWNER", tabname); + goto next_crontab; + } + if (statbuf->st_nlink != 1) { + log_it(fname, getpid(), "BAD LINK COUNT", tabname); + goto next_crontab; + } Debug(DLOAD, ("\t%s:", fname)) u = find_user(old_db, fname); @@ -255,7 +251,7 @@ link_user(new_db, u); } -next_crontab: + next_crontab: if (crontab_fd >= OK) { Debug(DLOAD, (" [done]\n")) close(crontab_fd); diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/do_command.c cron/cron/do_command.c --- /home/tmw/head/head/usr.sbin/cron/cron/do_command.c 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/do_command.c 2014-06-23 13:00:58.000000000 +0200 @@ -1,54 +1,49 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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: head/usr.sbin/cron/cron/do_command.c 228990 2011-12-30 10:58:14Z uqs $"; -#endif - +//#if !defined(lint) && !defined(LINT) +//static char rcsid[] = "$Id: do_command.c,v 1.9 2004/01/23 18:56:42 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 *), - do_univ(user *); - +static void child_process(entry *, user *); +static int safe_p(const char *, const char *); void -do_command(e, u) - entry *e; - user *u; -{ +do_command(entry *e, user *u) { Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n", - getpid(), e->cmd, u->name, e->uid, e->gid)) + 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 @@ -59,41 +54,39 @@ */ switch (fork()) { case -1: - log_it("CRON",getpid(),"error","can't fork"); + log_it("CRON", getpid(), "error", "can't fork"); break; case 0: /* child process */ pidfile_close(pfh); child_process(e, u); - Debug(DPROC, ("[%d] child process done, exiting\n", getpid())) + Debug(DPROC, ("[%ld] child process done, exiting\n", + (long)getpid())) _exit(OK_EXIT); break; default: /* parent process */ break; } - Debug(DPROC, ("[%d] main process returning to work\n", getpid())) + Debug(DPROC, ("[%ld] main process returning to work\n",(long)getpid())) } - static void -child_process(e, u) - entry *e; - user *u; -{ - int stdin_pipe[2], stdout_pipe[2]; - register char *input_data; - char *usernm, *mailto; - int children = 0; +child_process(entry *e, user *u) { + int stdin_pipe[2], stdout_pipe[2]; + char *input_data, *usernm, *mailto; + int children = 0; + # 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)) + Debug(DPROC, ("[%ld] child_process('%s')\n", (long)getpid(), e->cmd)) + - /* mark ourselves as different to PS command watchers by upshifting - * our program name. This has no effect on some kernels. + /* mark ourselves as different to PS command watchers by upshiting + * our program name. This has no effect on some kernels. */ setproctitle("running job"); @@ -103,88 +96,80 @@ mailto = env_get("MAILTO", 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); - } + /* 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_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); - } + 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 + * use wait() explicitly. so we have to reset 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 */ pipe(stdin_pipe); /* child's stdin */ pipe(stdout_pipe); /* child's stdout */ - + /* 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, + * the command. An escaped % will have the escape character stripped + * from it. 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 == '\\') + if (ch == '%') *--p = ch; escaped = FALSE; continue; @@ -205,26 +190,24 @@ */ switch (vfork()) { case -1: - log_it("CRON",getpid(),"error","can't vfork"); + log_it("CRON", getpid(), "error", "can't vfork"); exit(ERROR_EXIT); /*NOTREACHED*/ case 0: - Debug(DPROC, ("[%d] grandchild process Vfork()'ed\n", - getpid())) - + Debug(DPROC, ("[%ld] grandchild process vfork()'ed\n", + (long)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. */ - /*local*/{ + if ((e->flags & DONT_LOG) == 0) { char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); log_it(usernm, getpid(), "CMD", x); @@ -233,9 +216,7 @@ /* that's the last thing we'll log. close the log files. */ -#ifdef SYSLOG - closelog(); -#endif + log_close(); /* get new pgrp, void tty, etc. */ @@ -253,75 +234,75 @@ /* 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]); - - /* set our login universe. Do this in the grandchild - * so that the child can invoke /usr/lib/sendmail - * without surprises. - */ - do_univ(u); - -# if defined(LOGIN_CAP) - /* Set user's entire context, but skip the environment - * as cron provides a separate interface for this - */ - 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) { - log_it(usernm, getpid(), - "error", "setgid failed"); - exit(ERROR_EXIT); - } + if (stdin_pipe[READ_PIPE] != STDIN) { + dup2(stdin_pipe[READ_PIPE], STDIN); + close(stdin_pipe[READ_PIPE]); + } + if (stdout_pipe[WRITE_PIPE] != STDOUT) { + dup2(stdout_pipe[WRITE_PIPE], STDOUT); + close(stdout_pipe[WRITE_PIPE]); + } + dup2(STDOUT, STDERR); + + +#ifdef LOGIN_CAP + /* Set user's entire context, but skip the environment + * as cron provides a separate interface for this + */ + 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 /* LOGIN_CAP */ + /* 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); - } + 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 (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); + } + if (lc != NULL) + login_close(lc); #endif + chdir(env_get("HOME", e->envp)); - /* exec the command. + /* + * Exec the command. */ { char *shell = env_get("SHELL", e->envp); @@ -336,7 +317,8 @@ } # endif /*DEBUGGING*/ execle(shell, shell, "-c", e->cmd, (char *)0, e->envp); - warn("execl: couldn't exec `%s'", shell); + fprintf(stderr, "execl: couldn't exec `%s'\n", shell); + perror("execl"); _exit(ERROR_EXIT); } break; @@ -351,7 +333,7 @@ * the user's command. */ - Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid())) + Debug(DPROC, ("[%ld] child continues, closing pipes\n",(long)getpid())) /* close the ends of the pipe that will only be referenced in the * grandchild process... @@ -371,17 +353,18 @@ */ if (*input_data && 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"); + warn("fdopen in child2"); _exit(ERROR_EXIT); } - Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid())) + Debug(DPROC, ("[%ld] child2 sending data to grandchild\n", + (long)getpid())) /* close the pipe we don't use, since we inherited it and * are part of its reference count now. @@ -393,7 +376,7 @@ * % -> \n * \x -> \x for all x != % */ - while ((ch = *input_data++)) { + while ((ch = *input_data++) != '\0') { if (escaped) { if (ch != '%') putc('\\', out); @@ -417,7 +400,8 @@ */ fclose(out); - Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid())) + Debug(DPROC, ("[%ld] child2 done sending to grandchild\n", + (long)getpid())) exit(0); } @@ -435,57 +419,62 @@ * when the grandchild exits, we'll get EOF. */ - Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid())) + Debug(DPROC, ("[%ld] child reading output from grandchild\n", + (long)getpid())) /*local*/{ - register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); - register int ch; + FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); + int ch = getc(in); if (in == NULL) { warn("fdopen failed in child"); _exit(ERROR_EXIT); } - ch = getc(in); if (ch != EOF) { - register FILE *mail; - register int bytes = 1; - int status = 0; + FILE *mail; + int bytes = 1; + int status = 0; Debug(DPROC|DEXT, - ("[%d] got data (%x:%c) from grandchild\n", - getpid(), ch, ch)) + ("[%ld] got data (%x:%c) from grandchild\n", + (long)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 overriden. - */ - if (defmailto) - mailto = defmailto; - else - mailto = usernm; - } - if (mailto && *mailto == '\0') - mailto = NULL; + /* MAILTO not present, set to USER, + * unless globally overriden. + */ + 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]; - - (void) gethostname(hostname, MAXHOSTNAMELEN); - (void) snprintf(mailcmd, sizeof(mailcmd), - MAILARGS, MAILCMD); + if (mailto && safe_p(usernm, mailto)) { + char **env; + char mailcmd[MAX_COMMAND]; + char hostname[MAXHOSTNAMELEN]; + + gethostname(hostname, MAXHOSTNAMELEN); + if (strlens(MAILFMT, MAILARG, NULL) + 1 + >= sizeof mailcmd) { + fprintf(stderr, "mailcmd too long\n"); + (void) _exit(ERROR_EXIT); + } + (void)snprintf(mailcmd, sizeof(mailcmd), MAILFMT, MAILARG); if (!(mail = cron_popen(mailcmd, "w", e))) { - warn("%s", MAILCMD); + warn("%s", mailcmd); (void) _exit(ERROR_EXIT); } fprintf(mail, "From: %s (Cron Daemon)\n", usernm); @@ -493,10 +482,10 @@ 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 */ + arpadate(&StartTime)); +#endif /*MAIL_DATE*/ for (env = e->envp; *env; env++) fprintf(mail, "X-Cron-Env: <%s>\n", *env); @@ -523,8 +512,8 @@ */ if (mailto) { - Debug(DPROC, ("[%d] closing pipe to mail\n", - getpid())) + Debug(DPROC, ("[%ld] closing pipe to mail\n", + (long)getpid())) /* Note: the pclose will probably see * the termination of the grandchild * in addition to the mail process, since @@ -550,68 +539,48 @@ } /*if data from grandchild*/ - Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid())) + Debug(DPROC, ("[%ld] got EOF from grandchild\n", + (long)getpid())) fclose(in); /* also closes stdout_pipe[READ_PIPE] */ } /* wait for children to die. */ - for (; children > 0; children--) - { - WAIT_T waiter; - PID_T pid; - - Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n", - getpid(), children)) - pid = wait(&waiter); + for (; children > 0; children--) { + WAIT_T waiter; + PID_T pid; + + Debug(DPROC, ("[%ld] waiting for grandchild #%d to finish\n", + (long)getpid(), children)) + while ((pid = wait(&waiter)) < OK && errno == EINTR) + ; if (pid < OK) { - Debug(DPROC, ("[%d] no more grandchildren--mail written?\n", - getpid())) + Debug(DPROC, + ("[%ld] no more grandchildren--mail written?\n", + (long)getpid())) break; } - Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x", - getpid(), pid, WEXITSTATUS(waiter))) + Debug(DPROC, ("[%ld] grandchild #%ld finished, status=%04x", + (long)getpid(), (long)pid, WEXITSTATUS(waiter))) if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) Debug(DPROC, (", dumped core")) Debug(DPROC, ("\n")) } } - -static void -do_univ(u) - user *u; -{ -#if defined(sequent) -/* Dynix (Sequent) hack to put the user associated with - * the passed user structure into the ATT universe if - * necessary. We have to dig the gecos info out of - * the user's password entry to see if the magic - * "universe(att)" string is present. - */ - - struct passwd *p; - char *s; - int i; - - p = getpwuid(u->uid); - (void) endpwent(); - - if (p == NULL) - return; - - s = p->pw_gecos; - - for (i = 0; i < 4; i++) - { - if ((s = strchr(s, ',')) == NULL) - return; - s++; +static int +safe_p(const char *usernm, const char *s) { + static const char safe_delim[] = "@!:%-.,"; /* conservative! */ + const char *t; + int ch, first; + + for (t = s, first = 1; (ch = *t++) != '\0'; first = 0) { + if (isascii(ch) && isprint(ch) && + (isalnum(ch) || (!first && strchr(safe_delim, ch)))) + continue; + log_it(usernm, getpid(), "UNSAFE", s); + return (FALSE); } - if (strcmp(s, "universe(att)")) - return; - - (void) universe(U_ATT); -#endif + return (TRUE); } diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/externs.h cron/cron/externs.h --- /home/tmw/head/head/usr.sbin/cron/cron/externs.h 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/externs.h 2014-06-11 11:32:01.000000000 +0200 @@ -1,78 +1,104 @@ -/* $FreeBSD: head/usr.sbin/cron/cron/externs.h 173412 2007-11-07 10:53:41Z kevlo $ */ - /* Copyright 1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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 +#ifndef isascii +#define isascii(c) ((unsigned)(c)<=0177) +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#if defined(SYSLOG) +# include +#endif + +#if defined(LOGIN_CAP) +# include +#endif /*LOGIN_CAP*/ + +#if defined(BSD_AUTH) +# include +#endif /*BSD_AUTH*/ + +#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 -#endif - -#if defined(POSIX) -# define SIG_T sig_t -# define TIME_T time_t -# define PID_T pid_t -#endif - -#if defined(ATT) -# define SIG_T void -# define TIME_T long -# define PID_T int -#endif - -#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(BSD)) && (BSD >= 198606) || defined(__linux) +# define HAVE_FCHOWN +# define HAVE_FCHMOD #endif +#ifdef POSIX +#include +#ifdef _POSIX_SAVED_IDS +# define HAVE_SAVED_UIDS +#endif +#endif + +#define MY_UID(pw) getuid() +#define MY_GID(pw) getgid() + /* 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 @@ -81,67 +107,25 @@ 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 provide 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 -#ifdef NEED_VFORK -extern PID_T vfork(void); +#ifndef WCOREDUMP +# define WCOREDUMP(st) (((st) & 0200) != 0) #endif diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/funcs.h cron/cron/funcs.h --- /home/tmw/head/head/usr.sbin/cron/cron/funcs.h 1970-01-01 01:00:00.000000000 +0100 +++ cron/cron/funcs.h 2014-06-11 11:32:01.000000000 +0200 @@ -0,0 +1,81 @@ +/* + * $Id: funcs.h,v 1.9 2004/01/23 18:56:42 vixie Exp $ + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. + * + * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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 *), + acquire_daemonlock(int), + skip_comments(FILE *), + log_it(const char *, int, const char *, const char *), + log_close(void), + mkprint(char *, unsigned char *, int); + +int job_runqueue(void), + set_debug_flags(const char *), + get_char(FILE *), + get_string(char *, int, FILE *, char *), + swap_uids(void), + swap_uids_back(void), + load_env(char *, FILE *), + cron_pclose(FILE *), + glue_strings(char *, size_t, const char *, const char *, char), + strcmp_until(const char *, const char *, char), + allowed(char *), + strdtb(char *), + open_socket(void); + +size_t strlens(const 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 (*)(), struct passwd *, char **); + +FILE *cron_popen(char *, char *, entry *); + +struct passwd *pw_dup(const struct passwd *); + +#ifndef HAVE_TM_GMTOFF +long get_gmtoff(time_t *, struct tm *); +#endif diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/globals.h cron/cron/globals.h --- /home/tmw/head/head/usr.sbin/cron/cron/globals.h 1970-01-01 01:00:00.000000000 +0100 +++ cron/cron/globals.h 2014-06-09 13:21:06.000000000 +0200 @@ -0,0 +1,80 @@ +/* + * $Id: globals.h,v 1.10 2004/01/23 19:03:33 vixie Exp $ + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. + * + * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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 + = { + "@(#) ISC Cron V4.1", + "@(#) Copyright 1988,1989,1990,1993,1994 by Paul Vixie", + "@(#) Copyright 1997,2000 by Internet Software Consortium, Inc.", + "@(#) Copyright 2004 by Internet Systems Consortium, Inc.", + "@(#) 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 char *ProgramName INIT("amnesia"); +XTRN char *defmailto; +XTRN int LineNumber INIT(0); +XTRN time_t StartTime INIT(0); +XTRN int NoFork INIT(0); + +#if DEBUGGING +XTRN int DebugFlags INIT(0); +XTRN const char *DebugFlagNames[] +#ifdef MAIN_PROGRAM + = { + "ext", "sch", "proc", "pars", "load", "misc", "test", "bit", + NULL + } +#endif + ; +#else +#define DebugFlags 0 +#endif /* DEBUGGING */ diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/job.c cron/cron/job.c --- /home/tmw/head/head/usr.sbin/cron/cron/job.c 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/job.c 2014-06-06 12:30:50.000000000 +0200 @@ -1,76 +1,73 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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: head/usr.sbin/cron/cron/job.c 50479 1999-08-28 01:35:59Z peter $"; -#endif - +//#if !defined(lint) && !defined(LINT) +//static char rcsid[] = "$Id: job.c,v 1.6 2004/01/23 18:56:43 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(e, u) - register entry *e; - register user *u; -{ - register job *j; +job_add(entry *e, user *u) { + 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) + if ((j = (job *)malloc(sizeof(job))) == NULL) return; - j->next = (job*) NULL; + j->next = 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() -{ - register job *j, *jn; - register int run = 0; +job_runqueue(void) { + 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 -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/macros.h cron/cron/macros.h --- /home/tmw/head/head/usr.sbin/cron/cron/macros.h 1970-01-01 01:00:00.000000000 +0100 +++ cron/cron/macros.h 2014-06-11 14:31:15.000000000 +0200 @@ -0,0 +1,136 @@ +/* + * $Id: macros.h,v 1.9 2004/01/23 18:56:43 vixie Exp $ + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. + * + * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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 INIT_PID 1 /* parent of orphans */ +#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 MAX_UNAME 33 /* max length of username, should be overkill */ +#define ROOT_UID 0 /* don't change this, it really must be root */ +#define ROOT_USER "root" /* ditto */ + + /* 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 PPC_NULL ((const char **)NULL) + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#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); + +#if DEBUGGING +# define Debug(mask, message) \ + if ((DebugFlags & (mask)) != 0) \ + printf message; +#else /* !DEBUGGING */ +# define Debug(mask, message) \ + ; +#endif /* DEBUGGING */ + +#define MkUpper(ch) (islower(ch) ? toupper(ch) : ch) +#define Set_LineNum(ln) {Debug(DPARS|DEXT,("linenum=%d\n",ln)); \ + LineNumber = ln; \ + } + +#ifdef HAVE_TM_GMTOFF +#define get_gmtoff(c, t) ((t)->tm_gmtoff) +#endif + +#define SECONDS_PER_MINUTE 60 +#define SECONDS_PER_HOUR 3600 + +#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) + +/* + * Because crontab/at files may be owned by their respective users we + * take extreme care in opening them. If the OS lacks the O_NOFOLLOW + * we will just have to live without it. In order for this to be an + * issue an attacker would have to subvert group CRON_GROUP. + */ +#ifndef O_NOFOLLOW +#define O_NOFOLLOW 0 +#endif + +#define RELOAD_CRON 0x2 diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/pathnames.h cron/cron/pathnames.h --- /home/tmw/head/head/usr.sbin/cron/cron/pathnames.h 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/pathnames.h 2014-06-11 11:32:01.000000000 +0200 @@ -1,31 +1,38 @@ /* Copyright 1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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: head/usr.sbin/cron/cron/pathnames.h 50479 1999-08-28 01:35:59Z peter $ + * $Id: pathnames.h,v 1.9 2004/01/23 18:56:43 vixie Exp $ */ +#ifndef _PATHNAMES_H_ +#define _PATHNAMES_H_ + #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 + /* CRONDIR is where cron(8) and crontab(1) both chdir + * to; SPOOL_DIR, CRON_ALLOW, CRON_DENY, and LOG_FILE * are all relative to this directory. */ #define CRONDIR "/var/cron" @@ -34,24 +41,29 @@ /* 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 + * the signal for cron(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. - */ -#define ALLOW_FILE "allow" /*-*/ -#define DENY_FILE "deny" /*-*/ -/*#define LOG_FILE "log"*/ /*-*/ + /* cron allow/deny file. At least cron.deny must + * exist for ordinary users to run crontab. + */ +#define ALLOW_FILE "allow" +#define DENY_FILE "deny" + + /* undefining this turns off logging to a file. If + * neither LOG_FILE or SYSLOG is defined, we don't log. + * If both are defined, we log both ways. Note that if + * LOG_CRON is defined by , LOG_FILE will not + * be used. + */ +#define LOG_FILE "log" /* where should the daemon stick its PID? + * PIDDIR must end in '/'. */ #ifdef _PATH_VARRUN # define PIDDIR _PATH_VARRUN @@ -59,6 +71,8 @@ # define PIDDIR "/etc/" #endif #define PIDFILE "%scron.pid" +#define SOCKFILE ".sock" +#define _PATH_CRON_PID PIDDIR PIDFILE /* 4.3BSD-style crontab */ #define SYSCRONTAB "/etc/crontab" @@ -72,6 +86,10 @@ # define EDITOR "/usr/ucb/vi" #endif +#ifndef _PATH_SENDMAIL +# define _PATH_SENDMAIL "/usr/lib/sendmail" +#endif + #ifndef _PATH_BSHELL # define _PATH_BSHELL "/bin/sh" #endif @@ -79,3 +97,13 @@ #ifndef _PATH_DEFPATH # define _PATH_DEFPATH "/usr/bin:/bin" #endif + +#ifndef _PATH_TMP +# define _PATH_TMP "/tmp" +#endif + +#ifndef _PATH_DEVNULL +# define _PATH_DEVNULL "/dev/null" +#endif + +#endif /* _PATHNAMES_H_ */ diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/popen.c cron/cron/popen.c --- /home/tmw/head/head/usr.sbin/cron/cron/popen.c 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/popen.c 2014-06-07 18:24:55.000000000 +0200 @@ -28,7 +28,7 @@ static char sccsid[] = "@(#)popen.c 5.7 (Berkeley) 2/14/89"; #endif static const char rcsid[] = - "$FreeBSD: head/usr.sbin/cron/cron/popen.c 159527 2006-06-11 21:13:49Z maxim $"; + "$FreeBSD: releng/10.0/usr.sbin/cron/cron/popen.c 159527 2006-06-11 21:13:49Z maxim $"; #endif /* not lint */ #include "cron.h" diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/structs.h cron/cron/structs.h --- /home/tmw/head/head/usr.sbin/cron/cron/structs.h 1970-01-01 01:00:00.000000000 +0100 +++ cron/cron/structs.h 2014-06-07 18:44:26.000000000 +0200 @@ -0,0 +1,68 @@ +/* + * $Id: structs.h,v 1.7 2004/01/23 18:56:43 vixie Exp $ + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. + * + * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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; + 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); + int flags; +#define MIN_STAR 0x01 +#define HR_STAR 0x02 +#define DOM_STAR 0x04 +#define DOW_STAR 0x08 +#define WHEN_REBOOT 0x10 +#define DONT_LOG 0x20 + 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 -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/cron/user.c cron/cron/user.c --- /home/tmw/head/head/usr.sbin/cron/cron/user.c 2014-06-23 13:52:17.000000000 +0200 +++ cron/cron/user.c 2014-06-08 00:51:14.000000000 +0200 @@ -1,38 +1,37 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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: head/usr.sbin/cron/cron/user.c 50479 1999-08-28 01:35:59Z peter $"; -#endif +//#if !defined(lint) && !defined(LINT) +//static char rcsid[] = "$Id: user.c,v 1.5 2004/01/23 18:56:43 vixie Exp $"; +//#endif /* vix 26jan87 [log is in RCS file] */ - #include "cron.h" static char *User_name; - void -free_user(u) - user *u; -{ - entry *e, *ne; +free_user(user *u) { + entry *e, *ne; free(u->name); for (e = u->crontab; e != NULL; e = ne) { @@ -43,56 +42,49 @@ } static void -log_error(msg) - char *msg; -{ +log_error(char *msg) { log_it(User_name, getpid(), "PARSE", msg); } user * -load_user(crontab_fd, pw, name) - int crontab_fd; - struct passwd *pw; /* NULL implies syscrontab */ - char *name; -{ - char envstr[MAX_ENVSTR]; - FILE *file; - user *u; - entry *e; - int status; - char **envp, **tenvp; +load_user(int crontab_fd, struct passwd *pw, const char *name) { + char envstr[MAX_ENVSTR]; + FILE *file; + user *u; + entry *e; + int status, save_errno; + char **envp, **tenvp; if (!(file = fdopen(crontab_fd, "r"))) { - warn("fdopen on crontab_fd in load_user"); - return NULL; + perror("fdopen on crontab_fd in load_user"); + 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; - } + if ((u = (user *) malloc(sizeof(user))) == NULL) + return (NULL); if ((u->name = strdup(name)) == NULL) { + save_errno = errno; free(u); - errno = ENOMEM; - return NULL; + errno = save_errno; + return (NULL); } u->crontab = NULL; - /* - * init environment. this will be copied/augmented for each entry. + /* init environment. this will be copied/augmented for each entry. */ if ((envp = env_init()) == NULL) { + save_errno = errno; free(u->name); free(u); - return NULL; + errno = save_errno; + return (NULL); } - /* - * load the crontab + /* load the crontab */ while ((status = load_env(envstr, file)) >= OK) { switch (status) { @@ -101,7 +93,7 @@ u = NULL; goto done; case FALSE: - User_name = u->name; /* for log_error */ + User_name = u->name; e = load_entry(file, log_error, pw, envp); if (e) { e->next = u->crontab; @@ -109,13 +101,14 @@ } break; case TRUE: - if ((tenvp = env_set(envp, envstr))) { - envp = tenvp; - } else { + if ((tenvp = env_set(envp, envstr)) == NULL) { + save_errno = errno; free_user(u); u = NULL; + errno = save_errno; goto done; } + envp = tenvp; break; } } @@ -124,5 +117,5 @@ env_free(envp); fclose(file); Debug(DPARS, ("...load_user() done\n")) - return u; + return (u); } diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/crontab/Makefile cron/crontab/Makefile --- /home/tmw/head/head/usr.sbin/cron/crontab/Makefile 2014-06-23 13:52:17.000000000 +0200 +++ cron/crontab/Makefile 2014-06-11 11:32:01.000000000 +0200 @@ -1,11 +1,12 @@ -# $FreeBSD: head/usr.sbin/cron/crontab/Makefile 267233 2014-06-08 17:29:31Z bdrewery $ +# $FreeBSD: releng/10.0/usr.sbin/cron/crontab/Makefile 185040 2008-11-18 00:12:15Z matteo $ BINDIR= /usr/bin PROG= crontab MAN= crontab.1 crontab.5 BINOWN= root -BINMODE=4555 +BINGRP= crontab +BINMODE=2555 PRECIOUSPROG= WARNS?= 3 @@ -15,6 +16,4 @@ DPADD= ${LIBCRON} ${LIBMD} ${LIBUTIL} LDADD= ${LIBCRON} -lmd -lutil -NO_PIE= yes - .include diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/crontab/crontab.1 cron/crontab/crontab.1 --- /home/tmw/head/head/usr.sbin/cron/crontab/crontab.1 2014-06-23 13:52:17.000000000 +0200 +++ cron/crontab/crontab.1 2014-06-06 12:22:40.000000000 +0200 @@ -15,7 +15,7 @@ .\" * Paul Vixie uunet!decwrl!vixie!paul .\" */ .\" -.\" $FreeBSD: head/usr.sbin/cron/crontab/crontab.1 267668 2014-06-20 09:57:27Z bapt $ +.\" $FreeBSD: releng/10.0/usr.sbin/cron/crontab/crontab.1 208054 2010-05-14 01:25:30Z brueffer $ .\" .Dd May 13, 2010 .Dt CRONTAB 1 @@ -142,4 +142,4 @@ 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 +.An Paul Vixie Aq paul@vix.com diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/crontab/crontab.5 cron/crontab/crontab.5 --- /home/tmw/head/head/usr.sbin/cron/crontab/crontab.5 2014-06-23 13:52:17.000000000 +0200 +++ cron/crontab/crontab.5 2014-06-06 12:22:40.000000000 +0200 @@ -15,7 +15,7 @@ .\" * Paul Vixie uunet!decwrl!vixie!paul .\" */ .\" -.\" $FreeBSD: head/usr.sbin/cron/crontab/crontab.5 267668 2014-06-20 09:57:27Z bapt $ +.\" $FreeBSD: releng/10.0/usr.sbin/cron/crontab/crontab.5 242101 2012-10-25 22:54:29Z sobomax $ .\" .Dd April 28, 2012 .Dt CRONTAB 5 @@ -295,7 +295,7 @@ commands that can appear in place of the first five fields are extensions. .Sh AUTHORS -.An Paul Vixie Aq Mt paul@vix.com +.An Paul Vixie Aq 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 diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/crontab/crontab.c cron/crontab/crontab.c --- /home/tmw/head/head/usr.sbin/cron/crontab/crontab.c 2014-06-23 13:52:17.000000000 +0200 +++ cron/crontab/crontab.c 2014-06-11 11:32:01.000000000 +0200 @@ -1,24 +1,26 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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: head/usr.sbin/cron/crontab/crontab.c 239991 2012-09-01 14:45:15Z ed $"; +static char rcsid[] = "$Id: crontab.c,v 1.12 2004/01/23 18:56:42 vixie Exp $"; #endif /* crontab - install and manage per-user crontab files @@ -29,36 +31,20 @@ #define MAIN_PROGRAM #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" }; +static char *getoptargs = "u:lerx:"; +#else +static char *getoptargs = "u:ler"; #endif - static PID_T Pid; static char User[MAX_UNAME], RealUser[MAX_UNAME]; -static char Filename[MAX_FNAME]; +static char Filename[MAX_FNAME], TempFilename[MAX_FNAME]; static FILE *NewCrontab; static int CheckErrorCount; static enum opt_t Option; @@ -66,95 +52,103 @@ static void list_cmd(void), delete_cmd(void), edit_cmd(void), - poke_daemon(void), - check_error(char *), - parse_args(int c, char *v[]); + poke_daemon(unsigned char), + check_error(const char *), + parse_args(int c, char *v[]), + die(int); static int replace_cmd(void); - static void -usage(char *msg) -{ - fprintf(stderr, "crontab: usage error: %s\n", msg); - fprintf(stderr, "%s\n%s\n", - "usage: crontab [-u user] file", - " crontab [-u user] { -e | -l | -r }"); +usage(const char *msg) { + fprintf(stderr, "%s: usage error: %s\n", ProgramName, msg); + fprintf(stderr, "usage:\t%s [-u user] file\n", ProgramName); + fprintf(stderr, "\t%s [-u user] [ -e | -l | -r ]\n", ProgramName); + fprintf(stderr, "\t\t(default operation is replace, per 1003.2)\n"); + fprintf(stderr, "\t-e\t(edit user's crontab)\n"); + fprintf(stderr, "\t-l\t(list user's crontab)\n"); + fprintf(stderr, "\t-r\t(delete user's crontab)\n"); exit(ERROR_EXIT); } - int -main(int argc, char *argv[]) -{ - int exitstatus; +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); + if (!allowed(RealUser)) { + fprintf(stderr, + "You (%s) are not allowed to use this program (%s)\n", + User, ProgramName); + fprintf(stderr, "See crontab(1) for more information\n"); 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_unknown: - break; + 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; + default: + abort(); } exit(exitstatus); /*NOTREACHED*/ } - static void -parse_args(argc, argv) - int argc; - char *argv[]; -{ - 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); +parse_args(int argc, char *argv[]) { + int argch; + + if (!(pw = getpwuid(getuid()))) + errx(ERROR_EXIT, "%s: your UID isn't in the password file.", + ProgramName); + if (strlen(pw->pw_name) >= sizeof User) + errx(ERROR_EXIT, "username too long\n"); + + strlcpy(User, pw->pw_name, sizeof(User)); + strlcpy(RealUser, User, sizeof(RealUser)); Filename[0] = '\0'; Option = opt_unknown; - while ((argch = getopt(argc, argv, "u:lerx:")) != -1) { + while (-1 != (argch = getopt(argc, argv, getoptargs))) { switch (argch) { +#if DEBUGGING case 'x': if (!set_debug_flags(optarg)) usage("bad debug option"); break; +#endif case 'u': - if (getuid() != ROOT_UID) + if (MY_UID(pw) != 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'; + + if (!(pw = getpwnam(optarg))) + errx(ERROR_EXIT, "%s: user %s unknown", ProgramName, optarg); + + if (strlen(optarg) >= sizeof User) + usage("username too long"); + (void) strlcpy(User, optarg, sizeof(User)); break; case 'l': if (Option != opt_unknown) @@ -179,210 +173,206 @@ endpwent(); if (Option != opt_unknown) { - if (argv[optind] != NULL) { + 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 { + if (strlen(argv[optind]) >= sizeof Filename) + usage("filename too long"); + (void) strlcpy (Filename, argv[optind], sizeof(Filename)); + } 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, "-")) { + 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"); - } + else { + /* 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. + */ - Debug(DMISC, ("user=%s, file=%s, option=%s\n", - User, Filename, Options[(int)Option])) -} + if (swap_uids() < OK) + errx(ERROR_EXIT, "swapping uids"); -static void -copy_file(FILE *in, FILE *out) { - int x, ch; + if (!(NewCrontab = fopen(Filename, "r"))) + errx(ERROR_EXIT, "%s", Filename); - Set_LineNum(1) - /* ignore the top few comments since we probably put them there. - */ - for (x = 0; x < NHEADER_LINES; x++) { - ch = get_char(in); - if (EOF == ch) - break; - if ('#' != ch) { - putc(ch, out); - break; + if (swap_uids_back() < OK) + errx(ERROR_EXIT, "swapping uids back"); } - 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); + Debug(DMISC, ("user=%s, file=%s, option=%s\n", + User, Filename, Options[(int)Option])) } static void -list_cmd() { - char n[MAX_FNAME]; - FILE *f; +list_cmd(void) { + char n[MAX_FNAME]; + FILE *f; + int ch; log_it(RealUser, Pid, "LIST", User); - (void) snprintf(n, sizeof(n), CRON_TAB(User)); + if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) + errx(ERROR_EXIT, "path too long"); + if (!(f = fopen(n, "r"))) { if (errno == ENOENT) - errx(ERROR_EXIT, "no crontab for %s", User); + fprintf(stderr, "no crontab for %s\n", User); else - err(ERROR_EXIT, "%s", n); + perror(n); + exit(ERROR_EXIT); } /* file is open. copy to stdout, close. */ - copy_file(f, stdout); + Set_LineNum(1) + while (EOF != (ch = get_char(f))) + putchar(ch); fclose(f); } - static void -delete_cmd() { - char n[MAX_FNAME]; - int ch, first; - - if (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; - } +delete_cmd(void) { + char n[MAX_FNAME]; log_it(RealUser, Pid, "DELETE", User); - (void) snprintf(n, sizeof(n), CRON_TAB(User)); - if (unlink(n)) { + if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) + errx(ERROR_EXIT, "path to long"); + + if (unlink(n) != 0) { if (errno == ENOENT) - errx(ERROR_EXIT, "no crontab for %s", User); + fprintf(stderr, "no crontab for %s\n", User); else - err(ERROR_EXIT, "%s", n); + perror(n); + exit(ERROR_EXIT); } - poke_daemon(); + poke_daemon(RELOAD_CRON); } - static void -check_error(msg) - char *msg; -{ +check_error(const char *msg) { CheckErrorCount++; fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg); } - static void -edit_cmd() { - 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]; +edit_cmd(void) { + char n[MAX_FNAME], q[MAX_TEMPSTR], *editor; + FILE *f; + int ch, t, x; + struct stat statbuf, fsbuf; + struct utimbuf utimebuf; + WAIT_T waiter; + PID_T pid, xpid; log_it(RealUser, Pid, "BEGIN EDIT", User); - (void) snprintf(n, sizeof(n), CRON_TAB(User)); + if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) + 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); + if (errno != ENOENT) { + perror(n); + exit(ERROR_EXIT); + } + fprintf(stderr, "no crontab for %s - using an empty one\n", + User); + if (!(f = fopen(_PATH_DEVNULL, "r"))) { + perror(_PATH_DEVNULL); + exit(ERROR_EXIT); + } } - um = umask(077); - (void) snprintf(Filename, sizeof(Filename), "/tmp/crontab.XXXXXXXXXX"); - if ((t = mkstemp(Filename)) == -1) { - warn("%s", Filename); - (void) umask(um); + if (fstat(fileno(f), &statbuf) < 0) { + perror("fstat"); + goto fatal; + } + utimebuf.actime = statbuf.st_atime; + utimebuf.modtime = statbuf.st_mtime; + + /* Turn off signals. */ + (void)signal(SIGHUP, SIG_IGN); + (void)signal(SIGINT, SIG_IGN); + (void)signal(SIGQUIT, SIG_IGN); + + if (!glue_strings(Filename, sizeof Filename, _PATH_TMP, + "crontab.XXXXXXXXXX", '/')) { + fprintf(stderr, "path too long\n"); + goto fatal; + } + if (-1 == (t = mkstemp(Filename))) { + perror(Filename); goto fatal; } - (void) umask(um); #ifdef HAS_FCHOWN - if (fchown(t, getuid(), getgid()) < 0) { + if (fchown(t, MY_UID(pw), MY_GID(pw)) < 0) { + perror("fchown"); + goto fatal; + } #else - if (chown(Filename, getuid(), getgid()) < 0) { -#endif - warn("fchown"); + if (chown(Filename, MY_UID(pw), -1) < 0) { + perror("chown"); goto fatal; } +#endif if (!(NewCrontab = fdopen(t, "r+"))) { warn("fdopen"); goto fatal; } - copy_file(f, NewCrontab); + Set_LineNum(1) + + /* ignore the top few comments since we probably put them there. + */ + x = 0; + while (EOF != (ch = get_char(f))) { + if ('#' != ch) { + putc(ch, NewCrontab); + break; + } + while (EOF != (ch = get_char(f))) + if (ch == '\n') + break; + if (++x >= NHEADER_LINES) + break; + } + + /* copy the rest of the crontab (if any) to the temp file. + */ + if (EOF != ch) + while (EOF != (ch = get_char(f))) + putc(ch, NewCrontab); fclose(f); - if (fflush(NewCrontab)) - err(ERROR_EXIT, "%s", Filename); + if (fflush(NewCrontab) < OK) + errx(ERROR_EXIT, "%s", Filename); if (fstat(t, &fsbuf) < 0) { warn("unable to fstat temp file"); goto fatal; } + + utime(Filename, &utimebuf); again: - if (swap_uids() < OK) - err(ERROR_EXIT, "swapping uids"); - if (stat(Filename, &statbuf) < 0) { - warn("stat"); - fatal: unlink(Filename); + rewind(NewCrontab); + if (ferror(NewCrontab)) { + fprintf(stderr, "%s: error while writing new crontab to %s\n", + ProgramName, 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 == '\0') && + ((editor = getenv("EDITOR")) == NULL || *editor == '\0')) { editor = EDITOR; } @@ -396,18 +386,26 @@ switch (pid = fork()) { case -1: - warn("fork"); + perror("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); + if (setgid(MY_GID(pw)) < 0) + errx(ERROR_EXIT, "setgid(getgid())"); + + if (setuid(MY_UID(pw)) < 0) + errx(ERROR_EXIT, "setuid(getuid())"); + + if (chdir(_PATH_TMP) < 0) + errx(ERROR_EXIT, _PATH_TMP); + + if (!glue_strings(q, sizeof q, editor, Filename, ' ')) + errx(ERROR_EXIT, "%s: editor command line too long", + ProgramName); + + execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", q, (char *)0); + perror(editor); + exit(ERROR_EXIT); /*NOTREACHED*/ default: /* parent */ @@ -415,74 +413,73 @@ } /* 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"); + for (;;) { + xpid = waitpid(pid, &waiter, WUNTRACED); + if (xpid == -1) { + if (errno != EINTR) + fprintf(stderr, "%s: waitpid() failed waiting for PID %ld from \"%s\": %s\n", + ProgramName, (long)pid, editor, strerror(errno)); + } else if (xpid != pid) { + fprintf(stderr, "%s: wrong PID (%ld != %ld) from \"%s\"\n", + ProgramName, (long)xpid, (long)pid, editor); + goto fatal; + } else if (WIFSTOPPED(waiter)) { + kill(getpid(), WSTOPSIG(waiter)); + } else if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) { + fprintf(stderr, "%s: \"%s\" exited with status %d\n", + ProgramName, editor, WEXITSTATUS(waiter)); + goto fatal; + } else if (WIFSIGNALED(waiter)) { + fprintf(stderr, + "%s: \"%s\" killed; signal %d (%score dumped)\n", + ProgramName, editor, WTERMSIG(waiter), + WCOREDUMP(waiter) ?"" :"no "); + goto fatal; + } else + break; + } + (void)signal(SIGHUP, SIG_DFL); + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + if (fstat(t, &statbuf) < 0) { + perror("fstat"); + goto fatal; + } + if (utimebuf.modtime == statbuf.st_mtime) { + fprintf(stderr, "%s: no changes made to crontab\n", + ProgramName); goto remove; } - warnx("installing new crontab"); + fprintf(stderr, "%s: installing new crontab\n", ProgramName); switch (replace_cmd()) { - case 0: /* Success */ + case 0: break; - case -1: /* Syntax error */ + case -1: 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])) { + switch (q[0]) { case 'y': - syntax_error = 1; + case 'Y': goto again; case 'n': + case 'N': goto abandon; default: fprintf(stderr, "Enter Y or N\n"); } } /*NOTREACHED*/ - case -2: /* Install error */ + case -2: abandon: - warnx("edits left in %s", Filename); + fprintf(stderr, "%s: edits left in %s\n", + ProgramName, Filename); goto done; default: - warnx("panic: bad switch() in replace_cmd()"); + fprintf(stderr, "%s: panic: bad switch() in replace_cmd()\n", + ProgramName); goto fatal; } remove: @@ -491,40 +488,53 @@ log_it(RealUser, Pid, "END EDIT", User); } - /* returns 0 on success * -1 on syntax error * -2 on install error */ static int -replace_cmd() { - 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(); +replace_cmd(void) { + char n[MAX_FNAME], envstr[MAX_ENVSTR]; + FILE *tmp; + int ch, eof, fd; + int error = 0; + entry *e; + uid_t file_owner; + time_t now = time(NULL); + char **envp = env_init(); if (envp == NULL) { - warnx("cannot allocate memory"); + fprintf(stderr, "%s: Cannot allocate memory.\n", ProgramName); return (-2); } - (void) snprintf(n, sizeof(n), "tmp.%d", Pid); - (void) snprintf(tn, sizeof(tn), CRON_TAB(n)); - - if (!(tmp = fopen(tn, "w+"))) { - warn("%s", tn); + if (!glue_strings(TempFilename, sizeof TempFilename, SPOOL_DIR, + "tmp.XXXXXXXXXX", '/')) { + TempFilename[0] = '\0'; + fprintf(stderr, "path too long\n"); + return (-2); + } + if ((fd = mkstemp(TempFilename)) == -1 || !(tmp = fdopen(fd, "w+"))) { + perror(TempFilename); + if (fd != -1) { + close(fd); + unlink(TempFilename); + } + TempFilename[0] = '\0'; return (-2); } + (void) signal(SIGHUP, die); + (void) signal(SIGINT, die); + (void) signal(SIGQUIT, die); + /* 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); + fprintf(tmp, "# (Cron version %s -- %s)\n", CRON_VERSION, rcsid); /* copy the crontab to the tmp */ @@ -532,13 +542,15 @@ Set_LineNum(1) while (EOF != (ch = get_char(NewCrontab))) putc(ch, tmp); - ftruncate(fileno(tmp), ftell(tmp)); + ftruncate(fileno(tmp), ftell(tmp)); /* XXX redundant with "w+"? */ fflush(tmp); rewind(tmp); if (ferror(tmp)) { - warnx("error while writing new crontab to %s", tn); - fclose(tmp); unlink(tn); - return (-2); + fprintf(stderr, "%s: error while writing new crontab to %s\n", + ProgramName, TempFilename); + fclose(tmp); + error = -2; + goto done; } /* check the syntax of the file being installed. @@ -553,6 +565,11 @@ while (!CheckErrorCount && !eof) { switch (load_env(envstr, tmp)) { case ERR: + /* check for data before the EOF */ + if (envstr[0] != '\0') { + Set_LineNum(LineNumber + 1); + check_error("premature EOF"); + } eof = TRUE; break; case FALSE: @@ -566,78 +583,110 @@ } if (CheckErrorCount != 0) { - warnx("errors in crontab file, can't install"); - fclose(tmp); unlink(tn); - return (-1); + fprintf(stderr, "errors in crontab file, can't install.\n"); + fclose(tmp); + error = -1; + goto done; } + file_owner = (getgid() == getegid()) ? ROOT_UID : pw->pw_uid; + #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); + if (fchown(fileno(tmp), file_owner, -1) < OK) { + perror("fchown"); + fclose(tmp); + error = -2; + goto done; } - -#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 (chown(TempFilename, file_owner, -1) < OK) { + perror("chown"); + fclose(tmp); + error = -2; + goto done; } +#endif if (fclose(tmp) == EOF) { - warn("fclose"); - unlink(tn); - return (-2); + perror("fclose"); + error = -2; + goto done; } - (void) snprintf(n, sizeof(n), CRON_TAB(User)); - if (rename(tn, n)) { - warn("error renaming %s to %s", tn, n); - unlink(tn); - return (-2); + if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) { + fprintf(stderr, "path too long\n"); + error = -2; + goto done; } - + if (rename(TempFilename, n)) { + fprintf(stderr, "%s: error renaming %s to %s\n", + ProgramName, TempFilename, n); + perror("rename"); + error = -2; + goto done; + } + TempFilename[0] = '\0'; 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(); + poke_daemon(RELOAD_CRON); - return (0); +done: + (void) signal(SIGHUP, SIG_DFL); + (void) signal(SIGINT, SIG_DFL); + (void) signal(SIGQUIT, SIG_DFL); + if (TempFilename[0]) { + (void) unlink(TempFilename); + TempFilename[0] = '\0'; + } + return (error); } - static void -poke_daemon() { +poke_daemon(unsigned char cookie) { + int sock = -1; + struct sockaddr_un s_un; #ifdef USE_UTIMES - struct timeval tvs[2]; - - (void)gettimeofday(&tvs[0], NULL); - tvs[1] = tvs[0]; - if (utimes(SPOOL_DIR, tvs) < OK) { + struct timeval tvs[1]; + + (void)gettimeofday(&tvs, NULL); + if (utime(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); + warn("cant't update mtime on spooldir %s", SPOOL_DIR); return; } #endif /*USE_UTIMES*/ + + memset(&s_un, '\0', sizeof(s_un)); + if ((unsigned long)snprintf(s_un.sun_path, sizeof(s_un.sun_path), "%s/%s/%s", + CRONDIR, SPOOL_DIR, SOCKFILE) >= sizeof(s_un.sun_path)) { + fprintf(stderr, "%s: %s/%s: path too long\n", + ProgramName, CRONDIR, SOCKFILE); + return; + } + + s_un.sun_family = PF_LOCAL; +#ifdef SUN_LEN + s_un.sun_len = SUN_LEN(&s_un); +#endif + (void) signal(SIGPIPE, SIG_IGN); + if ((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) >= 0 && + connect(sock, (struct sockaddr *)&s_un, sizeof(s_un)) == 0) + write(sock, &cookie, 1); + else + fprintf(stderr, "%s: warning, cron does not appear to be " + "running.\n", ProgramName); + if (sock >= 0) + close(sock); + (void) signal(SIGPIPE, SIG_DFL); +} + +static void +die(int x) { + if (TempFilename[0]) + (void) unlink(TempFilename); + _exit(ERROR_EXIT); } diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/doc/CHANGES cron/doc/CHANGES --- /home/tmw/head/head/usr.sbin/cron/doc/CHANGES 2014-06-23 13:52:17.000000000 +0200 +++ cron/doc/CHANGES 2014-06-06 12:22:40.000000000 +0200 @@ -1,4 +1,4 @@ -$FreeBSD: head/usr.sbin/cron/doc/CHANGES 228990 2011-12-30 10:58:14Z uqs $ +$FreeBSD: releng/10.0/usr.sbin/cron/doc/CHANGES 228990 2011-12-30 10:58:14Z uqs $ -------- Vixie Cron Changes from V2 to V3 diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/doc/CONVERSION cron/doc/CONVERSION --- /home/tmw/head/head/usr.sbin/cron/doc/CONVERSION 2014-06-23 13:52:17.000000000 +0200 +++ cron/doc/CONVERSION 2014-06-06 12:22:40.000000000 +0200 @@ -1,4 +1,4 @@ -$FreeBSD: head/usr.sbin/cron/doc/CONVERSION 72091 2001-02-06 11:21:58Z asmodai $ +$FreeBSD: releng/10.0/usr.sbin/cron/doc/CONVERSION 72091 2001-02-06 11:21:58Z asmodai $ Conversion of BSD 4.[23] crontab files: diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/doc/FEATURES cron/doc/FEATURES --- /home/tmw/head/head/usr.sbin/cron/doc/FEATURES 2014-06-23 13:52:17.000000000 +0200 +++ cron/doc/FEATURES 2014-06-06 12:22:40.000000000 +0200 @@ -1,4 +1,4 @@ -$FreeBSD: head/usr.sbin/cron/doc/FEATURES 50479 1999-08-28 01:35:59Z peter $ +$FreeBSD: releng/10.0/usr.sbin/cron/doc/FEATURES 50479 1999-08-28 01:35:59Z peter $ Features of Vixie's cron relative to BSD 4.[23] and SysV crons: diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/doc/INSTALL cron/doc/INSTALL --- /home/tmw/head/head/usr.sbin/cron/doc/INSTALL 2014-06-23 13:52:17.000000000 +0200 +++ cron/doc/INSTALL 2014-06-06 12:22:40.000000000 +0200 @@ -15,7 +15,7 @@ * Paul Vixie uunet!decwrl!vixie!paul */ -$FreeBSD: head/usr.sbin/cron/doc/INSTALL 50479 1999-08-28 01:35:59Z peter $ +$FreeBSD: releng/10.0/usr.sbin/cron/doc/INSTALL 50479 1999-08-28 01:35:59Z peter $ Read the comments at the top of the Makefile, then edit the area marked 'configurable stuff'. diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/doc/MAIL cron/doc/MAIL --- /home/tmw/head/head/usr.sbin/cron/doc/MAIL 2014-06-23 13:52:17.000000000 +0200 +++ cron/doc/MAIL 2014-06-06 12:22:40.000000000 +0200 @@ -3,7 +3,7 @@ version of cron. it is presented here for its entertainment value. --vix ] -$FreeBSD: head/usr.sbin/cron/doc/MAIL 228990 2011-12-30 10:58:14Z uqs $ +$FreeBSD: releng/10.0/usr.sbin/cron/doc/MAIL 228990 2011-12-30 10:58:14Z uqs $ From ptsfa!lll-crg!ames!acornrc!bob Wed Dec 31 10:07:08 1986 Date: Wed, 31 Dec 86 08:59:31 pst diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/doc/Makefile.vixie cron/doc/Makefile.vixie --- /home/tmw/head/head/usr.sbin/cron/doc/Makefile.vixie 2014-06-23 13:52:17.000000000 +0200 +++ cron/doc/Makefile.vixie 2014-06-06 12:22:40.000000000 +0200 @@ -17,7 +17,7 @@ # Makefile for vixie's cron # -# $FreeBSD: head/usr.sbin/cron/doc/Makefile.vixie 50479 1999-08-28 01:35:59Z peter $ +# $FreeBSD: releng/10.0/usr.sbin/cron/doc/Makefile.vixie 50479 1999-08-28 01:35:59Z peter $ # # vix 03mar88 [moved to RCS, rest of log is in there] # vix 30mar87 [goodbye, time.c; hello, getopt] diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/doc/README cron/doc/README --- /home/tmw/head/head/usr.sbin/cron/doc/README 2014-06-23 13:52:17.000000000 +0200 +++ cron/doc/README 2014-06-06 12:22:40.000000000 +0200 @@ -69,4 +69,4 @@ if you like it, change your /etc/{rc,rc.local} to use it instead of the old one. -$FreeBSD: head/usr.sbin/cron/doc/README 50479 1999-08-28 01:35:59Z peter $ +$FreeBSD: releng/10.0/usr.sbin/cron/doc/README 50479 1999-08-28 01:35:59Z peter $ diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/lib/Makefile cron/lib/Makefile --- /home/tmw/head/head/usr.sbin/cron/lib/Makefile 2014-06-23 13:52:17.000000000 +0200 +++ cron/lib/Makefile 2014-06-06 12:22:40.000000000 +0200 @@ -1,4 +1,4 @@ -# $FreeBSD: head/usr.sbin/cron/lib/Makefile 185042 2008-11-18 00:59:26Z matteo $ +# $FreeBSD: releng/10.0/usr.sbin/cron/lib/Makefile 185042 2008-11-18 00:59:26Z matteo $ LIB= cron INTERNALLIB= diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/lib/compat.c cron/lib/compat.c --- /home/tmw/head/head/usr.sbin/cron/lib/compat.c 2014-06-23 13:52:17.000000000 +0200 +++ cron/lib/compat.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,237 +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: head/usr.sbin/cron/lib/compat.c 69793 2000-12-09 09:35:55Z obrien $"; -#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 - - -/* the code does not depend on any of vfork's - * side-effects; it just uses it as a quick - * fork-and-exec. - */ -#ifdef NEED_VFORK -PID_T -vfork() { - return (fork()); -} -#endif - - -#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 -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/lib/entry.c cron/lib/entry.c --- /home/tmw/head/head/usr.sbin/cron/lib/entry.c 2014-06-23 13:52:17.000000000 +0200 +++ cron/lib/entry.c 2014-06-23 12:22:10.000000000 +0200 @@ -1,24 +1,28 @@ -/* Copyright 1988,1990,1993,1994 by Paul Vixie +/* + * Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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: head/usr.sbin/cron/lib/entry.c 242101 2012-10-25 22:54:29Z sobomax $"; -#endif +//#if !defined(lint) && !defined(LINT) +//static char rcsid[] = "$Id: entry.c,v 1.17 2004/01/23 18:56:42 vixie Exp $"; +//#endif /* vix 26jan87 [RCS'd; rest of log is in RCS file] * vix 01jan87 [added line-level error recovery] @@ -26,27 +30,14 @@ * 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_mem -#ifdef LOGIN_CAP - , e_class -#endif + e_cmd, e_timespec, e_username, e_option, e_memory } 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", @@ -57,19 +48,18 @@ "bad command", "bad time specifier", "bad username", - "bad group name", - "out of memory", -#ifdef LOGIN_CAP - "bad class name", -#endif + "bad option", + "out of memory" }; +static int 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 *, const char *), + set_element(bitstr_t *, int, int, int); void -free_entry(e) - entry *e; -{ -#ifdef LOGIN_CAP +free_entry(entry *e) { +# ifdef LOGIN_CAP if (e->class != NULL) free(e->class); #endif @@ -80,20 +70,14 @@ free(e); } - /* return NULL if eof or syntax error occurs; * otherwise return a pointer to a new entry. */ entry * -load_entry(file, error_func, pw, envp) - FILE *file; - void (*error_func)(char *); - struct passwd *pw; - char **envp; -{ +load_entry(FILE *file, void (*error_func)(), 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. + * NULL if for any reason the entry can't be read and parsed. * * the entry is also parsed here. * @@ -105,11 +89,11 @@ */ ecode_e ecode = e_none; - entry *e; - int ch; - char cmd[MAX_COMMAND]; - char envstr[MAX_ENVSTR]; - char **prev_env; + entry *e; + int ch; + char cmd[MAX_COMMAND]; + char envstr[MAX_ENVSTR]; + char **tenvp; Debug(DPARS, ("load_entry()...about to eat comments\n")) @@ -117,7 +101,7 @@ ch = get_char(file); if (ch == EOF) - return NULL; + 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 @@ -126,11 +110,6 @@ e = (entry *) calloc(sizeof(entry), sizeof(char)); - if (e == NULL) { - warn("load_entry: calloc failed"); - return NULL; - } - if (ch == '@') { /* all of these should be flagged and load-limited; i.e., * instead of @hourly meaning "0 * * * *" it should mean @@ -144,14 +123,10 @@ * 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); @@ -159,8 +134,6 @@ 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); @@ -168,47 +141,33 @@ 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); + e->flags |= DOW_STAR; } 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)); + e->flags |= HR_STAR; } 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)); + 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 { ecode = e_timespec; goto eof; @@ -217,14 +176,15 @@ * username/command. */ Skip_Blanks(ch, file); - if (ch == EOF) { + if (ch == EOF || ch == '\n') { ecode = e_cmd; goto eof; } } else { Debug(DPARS, ("load_entry()...about to parse numerics\n")) - bit_set(e->second, 0); + if (ch == '*') + e->flags |= MIN_STAR; ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, PPC_NULL, ch, file); if (ch == EOF) { @@ -235,6 +195,8 @@ /* hours */ + if (ch == '*') + e->flags |= HR_STAR; ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR, PPC_NULL, ch, file); if (ch == EOF) { @@ -283,149 +245,138 @@ bit_set(e->dow, 7); } + /* check for permature EOL and catch a common typo */ + if (ch == '\n' || ch == '*') { + ecode = e_cmd; + goto eof; + } + /* 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"); + ch = get_string(username, MAX_COMMAND, file, " \t\n"); Debug(DPARS, ("load_entry()...got %s\n",username)) - if (ch == EOF) { + if (ch == EOF || ch == '\n' || ch == '*') { ecode = e_cmd; goto eof; } -#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)) + Debug(DPARS, ("load_entry()...uid %ld, gid %ld\n", + (long)pw->pw_uid, (long)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) { +#ifndef PAM + if (pm->pw_expire && time(NULL) >= pw->pw_expire) { ecode = e_username; goto eof; } -#endif /* !PAM */ - +#endif e->uid = pw->pw_uid; e->gid = pw->pw_gid; /* copy and fix up environment. some variables are just defaults and * others are overrides. */ - e->envp = env_copy(envp); - if (e->envp == NULL) { - warn("env_copy"); - ecode = e_mem; + if ((e->envp = env_copy(envp)) == NULL) { + ecode = e_memory; 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); - if (e->envp == NULL) { - warn("env_set(%s)", envstr); - env_free(prev_env); - ecode = e_mem; - goto eof; - } + if (glue_strings(envstr, sizeof envstr, "SHELL", + _PATH_BSHELL, '=')) { + if ((tenvp = env_set(e->envp, envstr)) == NULL) { + ecode = e_memory; + goto eof; + } + e->envp = tenvp; + } else + log_it("CRON", getpid(), "error", "can't set SHELL"); } 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) { - warn("env_set(%s)", envstr); - env_free(prev_env); - ecode = e_mem; - goto eof; - } + if (glue_strings(envstr, sizeof envstr, "HOME", + pw->pw_dir, '=')) { + if ((tenvp = env_set(e->envp, envstr)) == NULL) { + ecode = e_memory; + goto eof; + } + e->envp = tenvp; + } else + log_it("CRON", getpid(), "error", "can't set HOME"); } +#ifndef LOGIN_CAP + /* If login.conf is in used we will get the default PATH later. */ if (!env_get("PATH", e->envp)) { - prev_env = e->envp; - sprintf(envstr, "PATH=%s", _PATH_DEFPATH); - e->envp = env_set(e->envp, envstr); - if (e->envp == NULL) { - warn("env_set(%s)", envstr); - env_free(prev_env); - ecode = e_mem; - goto eof; - } - } - prev_env = e->envp; - sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name); - e->envp = env_set(e->envp, envstr); - if (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) { - warn("env_set(%s)", envstr); - env_free(prev_env); - ecode = e_mem; - goto eof; - } + if (glue_strings(envstr, sizeof envstr, "PATH", + _PATH_DEFPATH, '=')) { + if ((tenvp = env_set(e->envp, envstr)) == NULL) { + ecode = e_memory; + goto eof; + } + e->envp = tenvp; + } else + log_it("CRON", getpid(), "error", "can't set PATH"); + } +#endif /* LOGIN_CAP */ + if (glue_strings(envstr, sizeof envstr, "LOGNAME", + pw->pw_name, '=')) { + if ((tenvp = env_set(e->envp, envstr)) == NULL) { + ecode = e_memory; + goto eof; + } + e->envp = tenvp; + } else + log_it("CRON", getpid(), "error", "can't set LOGNAME"); +#if defined(BSD) || defined(__linux) + if (glue_strings(envstr, sizeof envstr, "USER", + pw->pw_name, '=')) { + if ((tenvp = env_set(e->envp, envstr)) == NULL) { + ecode = e_memory; + goto eof; + } + e->envp = tenvp; + } else + log_it("CRON", getpid(), "error", "can't set USER"); #endif Debug(DPARS, ("load_entry()...about to parse command\n")) + /* If the first character of the command is '-' it is a cron option. + */ + while ((ch = get_char(file)) == '-') { + switch (ch = get_char(file)) { + case 'q': + e->flags |= DONT_LOG; + Skip_Nonblanks(ch, file) + break; + default: + ecode = e_option; + goto eof; + } + Skip_Blanks(ch, file) + if (ch == EOF || ch == '\n') { + ecode = e_cmd; + goto eof; + } + } + unget_char(ch, file); + /* 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... @@ -437,37 +388,35 @@ /* 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; + if ((e->cmd = strdup(cmd)) == NULL) { + ecode = e_memory; goto eof; } + Debug(DPARS, ("load_entry()...returning successfully\n")) /* success, fini, return pointer to the entry we just created... */ - return e; + return (e); eof: - free_entry(e); + if (e->envp) + env_free(e->envp); + if (e->cmd) + free(e->cmd); + free(e); + while (ch != '\n' && !feof(file)) + ch = get_char(file); if (ecode != e_none && error_func) (*error_func)(ecodes[(int)ecode]); - while (ch != EOF && ch != '\n') - ch = get_char(file); - return NULL; + return (NULL); } - -static char -get_list(bits, low, high, names, ch, file) - bitstr_t *bits; /* one bit per flag, default=FALSE */ - int low, high; /* bounds, impl. offset for bitstr */ - char *names[]; /* NULL or *[] of names for these elements */ - int ch; /* current character being processed */ - FILE *file; /* file being read */ +static int +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 @@ -479,7 +428,7 @@ /* list = range {"," range} */ - + /* clear the bit string, since the default is 'off'. */ bit_nclear(bits, 0, (high-low+1)); @@ -488,7 +437,8 @@ */ done = FALSE; while (!done) { - ch = get_range(bits, low, high, names, ch, file); + if (EOF == (ch = get_range(bits, low, high, names, ch, file))) + return (EOF); if (ch == ',') ch = get_char(file); else @@ -502,23 +452,18 @@ Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch)) - return ch; + return (ch); } -static char -get_range(bits, low, high, names, ch, file) - bitstr_t *bits; /* one bit per flag, default=FALSE */ - int low, high; /* bounds, impl. offset for bitstr */ - char *names[]; /* NULL or names of elements */ - int ch; /* current character being processed */ - FILE *file; /* file being read */ +static int +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")) @@ -529,31 +474,32 @@ num2 = high; ch = get_char(file); if (ch == EOF) - return EOF; + return (EOF); } else { - if (EOF == (ch = get_number(&num1, low, names, ch, file))) - return EOF; + ch = get_number(&num1, low, names, ch, file, ",- \t\n"); + if (ch == EOF) + return (EOF); - if (ch == '/') - num2 = high; - else if (ch != '-') { + if (ch != '-') { /* not a range, it's a single number. */ - if (EOF == set_element(bits, low, high, num1)) - return EOF; - return ch; + if (EOF == set_element(bits, low, high, num1)) { + unget_char(ch, file); + return (EOF); + } + return (ch); } else { /* eat the dash */ ch = get_char(file); if (ch == EOF) - return EOF; + return (EOF); /* get the number following the dash */ - ch = get_number(&num2, low, names, ch, file); - if (ch == EOF) - return EOF; + ch = get_number(&num2, low, names, ch, file, "/, \t\n"); + if (ch == EOF || num1 > num2) + return (EOF); } } @@ -564,16 +510,16 @@ */ ch = get_char(file); if (ch == EOF) - return 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); + ch = get_number(&num3, 0, PPC_NULL, ch, file, ", \t\n"); if (ch == EOF || num3 == 0) - return EOF; + return (EOF); } else { /* no step. default==1. */ @@ -583,85 +529,76 @@ /* 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). + * designed then implemented by paul vixie). */ for (i = num1; i <= num2; i += num3) - if (EOF == set_element(bits, low, high, i)) - return EOF; + if (EOF == set_element(bits, low, high, i)) { + unget_char(ch, file); + return (EOF); + } - return ch; + return (ch); } +static int +get_number(int *numptr, int low, const char *names[], int ch, FILE *file, + const char *terms) { + char temp[MAX_TEMPSTR], *pc; + int len, i; -static char -get_number(numptr, low, names, ch, file) - int *numptr; /* where does the result go? */ - int low; /* offset applied to result if symbolic enum used */ - char *names[]; /* symbolic names, if any, for enums */ - int ch; /* current character */ - FILE *file; /* source */ -{ - 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; + /* first look for a number */ + while (isdigit((unsigned char)ch)) { + if (++len >= MAX_TEMPSTR) + goto bad; *pc++ = ch; - - if (!isdigit(ch)) - all_digits = FALSE; - ch = get_char(file); } *pc = '\0'; - if (len == 0) - return (EOF); + if (len != 0) { + /* got a number, check for valid terminator */ + if (!strchr(terms, ch)) + goto bad; + *numptr = atoi(temp); + return (ch); + } - /* try to find the name in the name list - */ + /* no numbers, look for a string if we have any */ 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; + while (isalpha((unsigned char)ch)) { + if (++len >= MAX_TEMPSTR) + goto bad; + *pc++ = ch; + ch = get_char(file); + } + *pc = '\0'; + if (len != 0 && strchr(terms, ch)) { + 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; +bad: + unget_char(ch, file); + return (EOF); } - static int -set_element(bits, low, high, number) - bitstr_t *bits; /* one bit per flag, default=FALSE */ - int low; - int high; - int number; -{ +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; + return (EOF); bit_set(bits, (number-low)); - return OK; + return (OK); } diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/lib/env.c cron/lib/env.c --- /home/tmw/head/head/usr.sbin/cron/lib/env.c 2014-06-23 13:52:17.000000000 +0200 +++ cron/lib/env.c 2014-06-06 12:30:09.000000000 +0200 @@ -1,95 +1,82 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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: head/usr.sbin/cron/lib/env.c 110638 2003-02-10 11:20:58Z thomas $"; -#endif - +//#if !defined(lint) && !defined(LINT) +//static char rcsid[] = "$Id: env.c,v 1.10 2004/01/23 18:56:42 vixie Exp $"; +//#endif #include "cron.h" - char ** -env_init() -{ - register char **p = (char **) malloc(sizeof(char *)); +env_init(void) { + char **p = (char **) malloc(sizeof(char **)); - if (p) + if (p != NULL) p[0] = NULL; return (p); } - void -env_free(envp) - char **envp; -{ - char **p; +env_free(char **envp) { + char **p; - if ((p = envp)) - for (; *p; p++) + for (p = envp; *p != NULL; p++) free(*p); free(envp); } - char ** -env_copy(envp) - register char **envp; -{ - register int count, i; - register 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; +env_copy(char **envp) { + int count, i, save_errno; + char **p; + + for (count = 0; envp[count] != NULL; count++) + NULL; + p = (char **) malloc((count+1) * sizeof(char *)); /* 1 for the NULL */ + if (p != NULL) { + for (i = 0; i < count; i++) + if ((p[i] = strdup(envp[i])) == NULL) { + save_errno = errno; + while (--i >= 0) + free(p[i]); + free(p); + errno = save_errno; + return (NULL); + } + p[count] = 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(envp, envstr) - char **envp; - char *envstr; -{ - register int count, found; - register char **p; - char *q; +env_set(char **envp, char *envstr) { + int count, found; + char **p, *envtmp; /* * 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++) { + for (count = 0; envp[count] != NULL; count++) { if (!strcmp_until(envp[count], envstr, '=')) found = count; } @@ -100,14 +87,10 @@ * 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); + if ((envtmp = strdup(envstr)) == NULL) + return (NULL); + free(envp[found]); + envp[found] = envtmp; return (envp); } @@ -116,47 +99,42 @@ * one, save our string over the old null pointer, and return resized * array. */ + if ((envtmp = strdup(envstr)) == NULL) + return (NULL); p = (char **) realloc((void *) envp, - (unsigned) ((count+1) * sizeof(char *))); - if (p == NULL) { - /* XXX env_free(envp); */ - errno = ENOMEM; - return NULL; + (size_t) ((count+1) * sizeof(char **))); + if (p == NULL) { + free(envtmp); + return (NULL); } p[count] = p[count-1]; - if ((p[count-1] = strdup(envstr)) == NULL) { - env_free(p); - errno = ENOMEM; - return NULL; - } + p[count-1] = envtmp; return (p); } +/* The following states are used by load_env(), traversed in order: */ +enum env_state { + NAMEI, /* First char of NAME, may be quote */ + NAME, /* Subsequent chars of NAME */ + EQ1, /* After end of name, looking for '=' sign */ + EQ2, /* After '=', skipping whitespace */ + VALUEI, /* First char of VALUE, may be quote */ + VALUE, /* Subsequent chars of VALUE */ + FINI, /* All done, skipping trailing whitespace */ + ERROR, /* Error */ +}; /* return ERR = end of file * FALSE = not an env setting (file was repositioned) * TRUE = was an env setting */ int -load_env(envstr, f) - 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 */ +load_env(char *envstr, FILE *f) { + long filepos; + int fileline; + enum env_state state; + char name[MAX_ENVSTR], val[MAX_ENVSTR]; + char quotechar, *c, *str; filepos = ftell(f); fileline = LineNumber; @@ -164,10 +142,10 @@ if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n")) return (ERR); - Debug(DPARS, ("load_env, read <%s>\n", envstr)); + Debug(DPARS, ("load_env, read <%s>\n", envstr)) - bzero (name, sizeof name); - bzero (val, sizeof val); + bzero(name, sizeof name); + bzero(val, sizeof val); str = name; state = NAMEI; quotechar = '\0'; @@ -178,7 +156,7 @@ case VALUEI: if (*c == '\'' || *c == '"') quotechar = *c++; - ++state; + state++; /* FALLTHROUGH */ case NAME: case VALUE: @@ -194,7 +172,7 @@ } } else { if (state == NAME) { - if (isspace (*c)) { + if (isspace((unsigned char)*c)) { c++; state++; break; @@ -214,52 +192,55 @@ str = val; quotechar = '\0'; } else { - if (!isspace (*c)) + if (!isspace((unsigned char)*c)) state = ERROR; } c++; break; + case EQ2: case FINI: - if (isspace (*c)) + if (isspace((unsigned char)*c)) c++; else state++; break; + + default: + abort(); } } if (state != FINI && !(state == VALUE && !quotechar)) { - Debug(DPARS, ("load_env, parse error, state = %d\n", state)) + Debug(DPARS, ("load_env, not an env var, 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 = val + strlen(val); + while (c > val && isspace((unsigned char)c[-1])) *(--c) = '\0'; } /* 2 fields from parser; looks like an env setting */ - if (strlen(name) + 1 + strlen(val) >= MAX_ENVSTR-1) + /* + * This can't overflow because get_string() limited the size of the + * name and val fields. Still, it doesn't hurt to be careful... + */ + if (!glue_strings(envstr, MAX_ENVSTR, name, val, '=')) 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(name, envp) - register char *name; - register char **envp; -{ - register int len = strlen(name); - register char *p, *q; +env_get(char *name, char **envp) { + 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)) diff -ruN --exclude=.git /home/tmw/head/head/usr.sbin/cron/lib/misc.c cron/lib/misc.c --- /home/tmw/head/head/usr.sbin/cron/lib/misc.c 2014-06-23 13:52:17.000000000 +0200 +++ cron/lib/misc.c 2014-06-23 13:01:52.000000000 +0200 @@ -1,63 +1,90 @@ /* Copyright 1988,1990,1993,1994 by Paul Vixie * All rights reserved + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. * - * 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 ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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: head/usr.sbin/cron/lib/misc.c 241125 2012-10-02 09:23:16Z pluknet $"; -#endif +//#if !defined(lint) && !defined(LINT) +//static char rcsid[] = "$Id: misc.c,v 1.16 2004/01/23 18:56:43 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 +#include +#if defined(SYSLOG) && 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 +#ifndef FACILITY +#define FACILITY LOG_CRON +#endif -static int LogFD = ERR; +static int LogFD = ERR; +#if defined(SYSLOG) +static int syslog_open = FALSE; +#endif +/* + * glue_strings is the overflow-safe equivalent of + * sprintf(buffer, "%s%c%s", a, separator, b); + * + * returns 1 on success, 0 on failure. 'buffer' MUST NOT be used if + * glue_strings fails. + */ int -strcmp_until(left, right, until) - char *left; - char *right; - int until; +glue_strings(char *buffer, size_t buffer_size, const char *a, const char *b, + char separator) { - register int diff; + char *buf; + char *buf_end; + + if (buffer_size <= 0) + return (0); + buf_end = buffer + buffer_size; + buf = buffer; + + for ( /* nothing */; buf < buf_end && *a != '\0'; buf++, a++ ) + *buf = *a; + if (buf == buf_end) + return (0); + if (separator != '/' || buf == buffer || buf[-1] != '/') + *buf++ = separator; + if (buf == buf_end) + return (0); + for ( /* nothing */; buf < buf_end && *b != '\0'; buf++, b++ ) + *buf = *b; + if (buf == buf_end) + return (0); + *buf = '\0'; + return (1); +} +int +strcmp_until(const char *left, const char *right, char until) { while (*left && *left != until && *left == *right) { left++; right++; @@ -65,21 +92,15 @@ 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(s) - char *s; -{ +strdtb(char *s) { char *x = s; /* scan forward to the null @@ -91,7 +112,7 @@ * or the last non-blank in the string, whichever comes first. */ do {x--;} - while (x >= s && isspace(*x)); + while (x >= s && isspace((unsigned char)*x)); /* one character beyond where we stopped above is where the null * goes. @@ -101,14 +122,11 @@ /* 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; + return (x - s); } - int -set_debug_flags(flags) - char *flags; -{ +set_debug_flags(const char *flags) { /* debug flags are of the form flag[,flag ...] * * if an error occurs, print a message to stdout and return FALSE. @@ -118,31 +136,30 @@ #if !DEBUGGING printf("this program was compiled without debugging enabled\n"); - return FALSE; + return (FALSE); #else /* DEBUGGING */ - char *pc = flags; + const 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++, mask <<= 1 - ) - ; + for (test = DebugFlagNames, mask = 1; + *test != NULL && strcmp_until(*test, pc, ','); + test++, mask <<= 1) + NULL; if (!*test) { fprintf(stderr, "unrecognized debug flag <%s> <%s>\n", flags, pc); - return FALSE; + return (FALSE); } DebugFlags |= mask; @@ -156,7 +173,7 @@ } if (DebugFlags) { - int flag; + int flag; fprintf(stderr, "debug flags enabled:"); @@ -166,90 +183,105 @@ fprintf(stderr, "\n"); } - return TRUE; + return (TRUE); #endif /* DEBUGGING */ } - void -set_cron_uid() -{ +set_cron_uid(void) { #if defined(BSD) || defined(POSIX) - if (seteuid(ROOT_UID) < OK) - err(ERROR_EXIT, "seteuid"); + if (seteuid(ROOT_UID) < OK) { + perror("seteuid"); + exit(ERROR_EXIT); + } #else - if (setuid(ROOT_UID) < OK) - err(ERROR_EXIT, "setuid"); + if (setuid(ROOT_UID) < OK) { + perror("setuid"); + exit(ERROR_EXIT); + } #endif } - void -set_cron_cwd() -{ - struct stat sb; +set_cron_cwd(void) { + struct stat sb; + struct group *grp = NULL; +#ifdef CRON_GROUP + grp = getgrnam(CRON_GROUP); +#endif /* 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); + perror(CRONDIR); + if (OK == mkdir(CRONDIR, 0710)) { + fprintf(stderr, "%s: created\n", CRONDIR); stat(CRONDIR, &sb); } else { - err(ERROR_EXIT, "%s: mkdir", CRONDIR); + fprintf(stderr, "%s: ", CRONDIR); + perror("mkdir"); + exit(ERROR_EXIT); } } - 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); + if (!S_ISDIR(sb.st_mode)) { + fprintf(stderr, "'%s' is not a directory, bailing out.\n", + CRONDIR); + exit(ERROR_EXIT); + } + if (chdir(CRONDIR) < OK) { + fprintf(stderr, "cannot chdir(%s), bailing out.\n", CRONDIR); + perror(CRONDIR); + exit(ERROR_EXIT); + } /* 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); + perror(SPOOL_DIR); if (OK == mkdir(SPOOL_DIR, 0700)) { - warnx("%s: created", SPOOL_DIR); + fprintf(stderr, "%s: created\n", SPOOL_DIR); stat(SPOOL_DIR, &sb); } else { - err(ERROR_EXIT, "%s: mkdir", SPOOL_DIR); + fprintf(stderr, "%s: ", SPOOL_DIR); + perror("mkdir"); + exit(ERROR_EXIT); } } - if (!(sb.st_mode & S_IFDIR)) - err(ERROR_EXIT, "'%s' is not a directory, bailing out", SPOOL_DIR); + if (!S_ISDIR(sb.st_mode)) { + fprintf(stderr, "'%s' is not a directory, bailing out.\n", + SPOOL_DIR); + exit(ERROR_EXIT); + } + if (grp != NULL) { + if (sb.st_gid != grp->gr_gid) + chown(SPOOL_DIR, -1, grp->gr_gid); + if (sb.st_mode != 01730) + chmod(SPOOL_DIR, 01730); + } } - /* get_char(file) : like getc() but increment LineNumber on newlines */ int -get_char(file) - FILE *file; -{ - int ch; +get_char(FILE *file) { + int ch; ch = getc(file); if (ch == '\n') Set_LineNum(LineNumber + 1) - return ch; + return (ch); } - /* unget_char(ch, file) : like ungetc but do LineNumber processing */ void -unget_char(ch, file) - int ch; - FILE *file; -{ +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 @@ -257,13 +289,8 @@ * (4) returns EOF or terminating character, whichever */ int -get_string(string, size, file, terms) - char *string; - int size; - FILE *file; - char *terms; -{ - int ch; +get_string(char *string, int size, FILE *file, char *terms) { + int ch; while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) { if (size > 1) { @@ -275,17 +302,14 @@ if (size > 0) *string = '\0'; - return ch; + return (ch); } - /* skip_comments(file) : read past comment (if any) */ void -skip_comments(file) - FILE *file; -{ - int ch; +skip_comments(FILE *file) { + int ch; while (EOF != (ch = get_char(file))) { /* ch is now the first character of a line. @@ -318,92 +342,87 @@ unget_char(ch, file); } - -/* int in_file(char *string, FILE *file) +/* int in_file(const char *string, FILE *file, int error) * return TRUE if one of the lines in file matches string exactly, - * FALSE otherwise. + * FALSE if no lines match, and error on error. */ static int -in_file(char *string, FILE *file) +in_file(const char *string, FILE *file, int error) { char line[MAX_TEMPSTR]; + char *endp; - rewind(file); + if (fseek(file, 0L, SEEK_SET)) + return (error); 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; + if (line[0] != '\0') { + endp = &line[strlen(line) - 1]; + if (*endp != '\n') + return (error); + *endp = '\0'; + if (0 == strcmp(line, string)) + return (TRUE); + } } - return FALSE; + if (ferror(file)) + return (error); + 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) + * 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(username) - char *username; +allowed(char *username) { - FILE *allow, *deny; - int isallowed; + FILE *allow, *deny; + int isallowed; - isallowed = FALSE; + isallowed = FALSE; - deny = NULL; + 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)) + 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; + allow = NULL; #endif - if (allow) - isallowed = in_file(username, allow); - else if (deny) - isallowed = !in_file(username, deny); - else { + if (allow) + isallowed = in_file(username, allow, FALSE); + else if (deny) + isallowed = !in_file(username, deny, FALSE); + else { #if defined(ALLOW_ONLY_ROOT) - isallowed = (strcmp(username, ROOT_USER) == 0); + isallowed = (strcmp(username, ROOT_USER) == 0); #else - isallowed = TRUE; + isallowed = TRUE; #endif - } -out: if (allow) - fclose(allow); - if (deny) - fclose(deny); - return (isallowed); + } +out: if (allow) + fclose(allow); + if (deny) + fclose(deny); + return (isallowed); } - void -log_it(username, xpid, event, detail) - char *username; - int xpid; - char *event; - char *detail; -{ +log_it(const char *username, PID_T 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; -#endif - #if defined(LOG_FILE) /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation. */ @@ -411,90 +430,89 @@ + strlen(event) + strlen(detail) + MAX_TEMPSTR); - if (msg == NULL) - warnx("failed to allocate memory for log message"); - else { + return; + + if (LogFD < OK) { + LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600); 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); - } + fprintf(stderr, "%s: can't open log file\n", + ProgramName); + perror(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)); - } + /* 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. + */ + snprintf(msg, sizeof(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); - free(msg); + /* we have to run strlen() because sprintf() returns (char*) on old BSD + */ + if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) { + if (LogFD >= OK) + perror(LOG_FILE); + fprintf(stderr, "%s: can't write to log file\n", ProgramName); + 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); + openlog(ProgramName, LOG_PID, FACILITY); # else openlog(ProgramName, LOG_PID); # endif syslog_open = TRUE; /* assume openlog success */ } - syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail); + syslog(LOG_INFO, "(%s) %s (%s)", username, event, detail); #endif /*SYSLOG*/ #if DEBUGGING if (DebugFlags) { - fprintf(stderr, "log_it: (%s %d) %s (%s)\n", - username, pid, event, detail); + fprintf(stderr, "log_it: (%s %ld) %s (%s)\n", + username, (long)pid, event, detail); } #endif } - void -log_close() { +log_close(void) { if (LogFD != ERR) { close(LogFD); LogFD = ERR; } +#if defined(SYSLOG) + closelog(); + syslog_open = FALSE; +#endif /*SYSLOG*/ } - -/* two warnings: +/* char *first_word(char *s, char *t) + * return pointer to first word + * parameters: + * s - string we want the first word of + * t - terminators, implicitly including \0 + * warnings: * (1) this routine is fairly slow * (2) it returns a pointer to static storage */ char * -first_word(s, t) - register char *s; /* string we want the first word of */ - register char *t; /* terminators, implicitly including \0 */ -{ +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; @@ -513,19 +531,23 @@ /* finish the return-string and return it */ *rp = '\0'; - return rb; + return (rb); } - /* warning: * heavily ascii-dependent. */ -static void -mkprint(register char *dst, register unsigned char *src, register int len) +void +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++ = '^'; @@ -536,65 +558,182 @@ *dst++ = '^'; *dst++ = '?'; } else { /* parity character */ - sprintf(dst, "\\%03o", ch); + snprintf(dst, sizeof(dst), "\\%03o", ch); dst += 4; } } *dst = '\0'; } - /* warning: * returns a pointer to malloc'd storage, you must call free yourself. */ char * mkprints(src, len) - register unsigned char *src; - register unsigned int len; + unsigned char *src; + unsigned int len; { - register char *dst = malloc(len*4 + 1); + char *dst = malloc(len*4 + 1); - if (dst != NULL) + if (dst) mkprint(dst, src, len); - return dst; + return (dst); } - #ifdef MAIL_DATE -/* Sat, 27 Feb 93 11:44:51 CST - * 123456789012345678901234567 +/* Sat, 27 Feb 1993 11:44:51 -0800 (CST) + * 1234567890123456789012345678901234567 */ char * arpadate(clock) 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 */ - - 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, + time_t t = clock ? *clock : time((TIME_T) 0); + struct tm tm = *localtime(&t); + long gmtoff = get_gmtoff(&t, &tm); + int hours = gmtoff / SECONDS_PER_HOUR; + int minutes = (gmtoff - (hours * SECONDS_PER_HOUR)) / SECONDS_PER_MINUTE; + static char ret[64]; /* zone name might be >3 chars */ + + (void) snprintf(ret, sizeof(ret), "%s, %2d %s %2d %02d:%02d:%02d %.2d%.2d (%s)", + DowNames[tm.tm_wday], + tm.tm_mday, + MonthNames[tm.tm_mon], + tm.tm_year + 1900, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + hours, + minutes, TZONE(*tm)); - return ret; + return (ret); } #endif /*MAIL_DATE*/ - #ifdef HAVE_SAVED_UIDS -static int save_euid; -int swap_uids() { save_euid = geteuid(); return seteuid(getuid()); } -int swap_uids_back() { return seteuid(save_euid); } +static uid_t save_euid; +static gid_t save_egid; + +int swap_uids(void) { + save_egid = getegid(); + save_euid = geteuid(); + return ((setegid(getgid()) || seteuid(getuid())) ? -1 : 0); +} + +int swap_uids_back(void) { + return ((setegid(getgid()) || seteuid(getuid())) ? -1 : 0); +} + #else /*HAVE_SAVED_UIDS*/ -int swap_uids() { return setreuid(geteuid(), getuid()); } -int swap_uids_back() { return swap_uids(); } + +int swap_uids(void) { + return ((setregid(getegid(), getgid()) || setreuid(geteuid(), getuid())) + ? -1 : 0); +} + +int swap_uids_back(void) { + return (swap_uids()); +} #endif /*HAVE_SAVED_UIDS*/ + +size_t +strlens(const char *last, ...) { + va_list ap; + size_t ret = 0; + const char *str; + + va_start(ap, last); + for (str = last; str != NULL; str = va_arg(ap, const char *)) + ret += strlen(str); + va_end(ap); + return (ret); +} + +/* Return the offset from GMT in seconds (algorithm taken from sendmail). + * + * warning: + * clobbers the static storage space used by localtime() and gmtime(). + * If the local pointer is non-NULL it *must* point to a local copy. + */ +#ifndef HAVE_TM_GMTOFF +long get_gmtoff(time_t *clock, struct tm *local) +{ + struct tm gmt; + long offset; + + gmt = *gmtime(clock); + if (local == NULL) + local = localtime(clock); + + offset = (local->tm_sec - gmt.tm_sec) + + ((local->tm_min - gmt.tm_min) * 60) + + ((local->tm_hour - gmt.tm_hour) * 3600); + + /* Timezone may cause year rollover to happen on a different day. */ + if (local->tm_year < gmt.tm_year) + offset -= 24 * 3600; + else if (local->tm_year > gmt.tm_year) + offset -= 24 * 3600; + else if (local->tm_yday < gmt.tm_yday) + offset -= 24 * 3600; + else if (local->tm_yday > gmt.tm_yday) + offset += 24 * 3600; + + return (offset); +} +#endif /* HAVE_TM_GMTOFF */ + +int +open_socket(void) +{ + int sock; + mode_t omask; + struct sockaddr_un s_un; + + sock = socket(PF_LOCAL, SOCK_STREAM, 0); + if (sock == -1) { + fprintf(stderr, "%s: can't create socket: %s\n", + ProgramName, strerror(errno)); + exit(EXIT_FAILURE); + } + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { + fprintf(stderr, "%s: can't make socket close on exec: %s\n", + ProgramName, strerror(errno)); + exit(EXIT_FAILURE); + } + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { + fprintf(stderr, "%s: can't make socket non-blocking: %s\n", + ProgramName, strerror(errno)); + exit(EXIT_FAILURE); + } + memset(&s_un, '\0', sizeof(s_un)); + if ((unsigned long)snprintf(s_un.sun_path, sizeof(s_un.sun_path), "%s/%s/%s", + CRONDIR, SPOOL_DIR, SOCKFILE) >= sizeof(s_un.sun_path)) { + fprintf(stderr, "%s/%s: path too long\n", CRONDIR, SOCKFILE); + exit(EXIT_FAILURE); + } + unlink(s_un.sun_path); + s_un.sun_family = PF_LOCAL; +#ifdef SUN_LEN + s_un.sun_len = SUN_LEN(&s_un); +#endif + + omask = umask(007); + if (bind(sock, (struct sockaddr *)&s_un, sizeof(s_un))) { + fprintf(stderr, "%s: can't bind socket: %s\n", + ProgramName, strerror(errno)); + umask(omask); + exit(EXIT_FAILURE); + } + umask(omask); + if (listen(sock, SOMAXCONN)) { + fprintf(stderr, "%s: can't listen on socket: %s\n", + ProgramName, strerror(errno)); + exit(EXIT_FAILURE); + } + chmod(s_un.sun_path, 0660); + + return(sock); +} +