summaryrefslogtreecommitdiff
path: root/usr.sbin/cron
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/cron')
-rw-r--r--usr.sbin/cron/Makefile6
-rw-r--r--usr.sbin/cron/atrun.c595
-rw-r--r--usr.sbin/cron/config.h7
-rw-r--r--usr.sbin/cron/cron.8214
-rw-r--r--usr.sbin/cron/cron.c108
-rw-r--r--usr.sbin/cron/crontab.14
-rw-r--r--usr.sbin/cron/crontab.c14
-rw-r--r--usr.sbin/cron/do_command.c66
-rw-r--r--usr.sbin/cron/entry.c15
-rw-r--r--usr.sbin/cron/externs.h12
-rw-r--r--usr.sbin/cron/funcs.h11
-rw-r--r--usr.sbin/cron/globals.h5
-rw-r--r--usr.sbin/cron/macros.h8
-rw-r--r--usr.sbin/cron/misc.c15
-rw-r--r--usr.sbin/cron/pathnames.h24
-rw-r--r--usr.sbin/cron/popen.c31
-rw-r--r--usr.sbin/cron/structs.h18
17 files changed, 940 insertions, 213 deletions
diff --git a/usr.sbin/cron/Makefile b/usr.sbin/cron/Makefile
index e4dc98b4608..00830bd05a6 100644
--- a/usr.sbin/cron/Makefile
+++ b/usr.sbin/cron/Makefile
@@ -1,9 +1,9 @@
-# $OpenBSD: Makefile,v 1.2 1997/09/21 11:43:33 deraadt Exp $
+# $OpenBSD: Makefile,v 1.3 2002/07/15 19:13:29 millert Exp $
PROG= cron
SRCS= cron.c database.c user.c entry.c job.c do_command.c \
- misc.c env.c popen.c
-CFLAGS+=-I${.CURDIR}
+ misc.c env.c popen.c atrun.c
+CFLAGS+=-I${.CURDIR} -Wall
MAN= cron.8
.include <bsd.prog.mk>
diff --git a/usr.sbin/cron/atrun.c b/usr.sbin/cron/atrun.c
new file mode 100644
index 00000000000..c404b2ba977
--- /dev/null
+++ b/usr.sbin/cron/atrun.c
@@ -0,0 +1,595 @@
+/* $OpenBSD: atrun.c,v 1.1 2002/07/15 19:13:29 millert Exp $ */
+
+/*
+ * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] = "$OpenBSD: atrun.c,v 1.1 2002/07/15 19:13:29 millert Exp $";
+#endif
+
+#include "cron.h"
+#include <sys/resource.h>
+
+static void unlink_job(at_db *, atjob *);
+static void run_job(atjob *, char *);
+
+/*
+ * Scan the at jobs dir and build up a list of jobs found.
+ */
+int
+scan_atjobs(at_db *old_db, struct timeval *tv)
+{
+ DIR *atdir = NULL;
+ int cwd, queue, pending;
+ long l;
+ TIME_T run_time;
+ char *ep;
+ at_db new_db;
+ atjob *job, *tjob;
+ struct dirent *file;
+ struct stat statbuf;
+
+ Debug(DLOAD, ("[%ld] scan_atjobs()\n", (long)getpid()))
+
+ if (stat(_PATH_ATJOBS, &statbuf) != 0) {
+ log_it("CRON", getpid(), "CAN'T STAT", _PATH_ATJOBS);
+ return (0);
+ }
+
+ if (old_db->mtime == statbuf.st_mtime) {
+ Debug(DLOAD, ("[%ld] at jobs dir mtime unch, no load needed.\n",
+ (long)getpid()))
+ return (0);
+ }
+
+ /* XXX - would be nice to stash the crontab cwd */
+ if ((cwd = open(".", O_RDONLY, 0)) < 0) {
+ log_it("CRON", getpid(), "CAN'T OPEN", ".");
+ return (0);
+ }
+
+ if (chdir(_PATH_ATJOBS) != 0 || (atdir = opendir(".")) == NULL) {
+ if (atdir == NULL)
+ log_it("CRON", getpid(), "OPENDIR FAILED",
+ _PATH_ATJOBS);
+ else
+ log_it("CRON", getpid(), "CHDIR FAILED",
+ _PATH_ATJOBS);
+ fchdir(cwd);
+ close(cwd);
+ return (0);
+ }
+
+ new_db.mtime = statbuf.st_mtime; /* stash at dir mtime */
+ new_db.head = new_db.tail = NULL;
+
+ pending = 0;
+ while ((file = readdir(atdir))) {
+ if (stat(file->d_name, &statbuf) != 0 ||
+ !S_ISREG(statbuf.st_mode))
+ continue;
+
+ /*
+ * at jobs are named as RUNTIME.QUEUE
+ * RUNTIME is the time to run in seconds since the epoch
+ * QUEUE is a letter that designates the job's queue
+ */
+ l = strtol(file->d_name, &ep, 10);
+ if (*ep != '.' || !isalpha(*(ep + 1)) || l < 0 || l >= INT_MAX)
+ continue;
+ run_time = (TIME_T)l;
+ queue = *(ep + 1);
+ if (!isalpha(queue))
+ continue;
+
+ job = (atjob *)malloc(sizeof(*job));
+ if (job == NULL) {
+ for (job = new_db.head; job != NULL; ) {
+ tjob = job;
+ job = job->next;
+ free(tjob);
+ }
+ return (0);
+ }
+ job->uid = statbuf.st_uid;
+ job->gid = statbuf.st_gid;
+ job->queue = queue;
+ job->run_time = run_time;
+ job->prev = new_db.tail;
+ job->next = NULL;
+ if (new_db.head == NULL)
+ new_db.head = job;
+ if (new_db.tail != NULL)
+ new_db.tail->next = job;
+ new_db.tail = job;
+ if (tv != NULL && run_time <= tv->tv_sec)
+ pending = 1;
+ }
+ closedir(atdir);
+
+ /* Free up old at db */
+ Debug(DLOAD, ("unlinking old at database:\n"))
+ for (job = old_db->head; job != NULL; ) {
+ Debug(DLOAD, ("\t%ld.%c\n", (long)job->run_time, job->queue))
+ tjob = job;
+ job = job->next;
+ free(tjob);
+ }
+
+ /* Change back to the normal cron dir. */
+ fchdir(cwd);
+ close(cwd);
+
+ /* Install the new database */
+ *old_db = new_db;
+ Debug(DLOAD, ("scan_atjobs is done\n"))
+
+ return (pending);
+}
+
+/*
+ * Loop through the at job database and run jobs whose time have come.
+ */
+void
+atrun(at_db *db, double batch_maxload, TIME_T now)
+{
+ char atfile[PATH_MAX];
+ struct stat statbuf;
+ double la;
+ atjob *job, *batch;
+
+ Debug(DPROC, ("[%ld] atrun()\n", (long)getpid()))
+
+ for (batch = NULL, job = db->head; job; job = job->next) {
+ /* Skip jobs in the future */
+ if (job->run_time > now)
+ continue;
+
+ snprintf(atfile, sizeof(atfile), "%s/%ld.%c", _PATH_ATJOBS,
+ (long)job->run_time, job->queue);
+
+ if (stat(atfile, &statbuf) != 0)
+ unlink_job(db, job); /* disapeared */
+
+ if (!S_ISREG(statbuf.st_mode))
+ continue; /* should not happen */
+
+ /*
+ * Pending jobs have the user execute bit set.
+ */
+ if (statbuf.st_mode & S_IXUSR) {
+ /* new job to run */
+ if (isupper(job->queue)) {
+ /* we run one batch job per atrun() call */
+ if (batch == NULL ||
+ job->run_time < batch->run_time)
+ batch = job;
+ } else {
+ /* normal at job */
+ run_job(job, atfile);
+ unlink_job(db, job);
+ }
+ }
+ }
+
+ /* Run a single batch job if there is one pending. */
+ if (batch != NULL && (batch_maxload == 0.0 ||
+ ((getloadavg(&la, 1) == 1) && la <= batch_maxload))) {
+ snprintf(atfile, sizeof(atfile), "%s/%ld.%c", _PATH_ATJOBS,
+ (long)batch->run_time, batch->queue);
+ run_job(batch, atfile);
+ unlink_job(db, batch);
+ }
+}
+
+/*
+ * Remove the specified at job from the database.
+ */
+static void
+unlink_job(at_db *db, atjob *job)
+{
+ if (job->prev == NULL)
+ db->head = job->next;
+ else
+ job->prev->next = job->next;
+
+ if (job->next == NULL)
+ db->tail = job->prev;
+ else
+ job->next->prev = job->prev;
+}
+
+/*
+ * Run the specified job contained in atfile.
+ */
+static void
+run_job(atjob *job, char *atfile)
+{
+ struct stat statbuf;
+ struct passwd *pw;
+ pid_t pid;
+ long nuid, ngid;
+ FILE *fp;
+ WAIT_T waiter;
+ char *cp, *ep, mailto[MAX_UNAME], buf[BUFSIZ];
+ int fd, always_mail;
+ int output_pipe[2];
+ char *nargv[2], *nenvp[1];
+
+ Debug(DPROC, ("[%ld] run_job('%s')\n", (long)getpid(), atfile))
+
+ /* Open the file and unlink it so we don't try running it again. */
+ if ((fd = open(atfile, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0)) < OK) {
+ log_it("CRON", getpid(), "CAN'T OPEN", atfile);
+ return;
+ }
+ unlink(atfile);
+
+ /* Fork so other pending jobs don't have to wait for us to finish. */
+ switch (fork()) {
+ case 0:
+ /* child */
+ break;
+ case -1:
+ /* error */
+ log_it("CRON", getpid(), "error", "can't fork");
+ /* FALLTHROUGH */
+ default:
+ /* parent */
+ close(fd);
+ return;
+ }
+
+ acquire_daemonlock(1); /* close lock fd */
+
+ /*
+ * We don't want the main cron daemon to wait for our children--
+ * we will do it ourselves via waitpid().
+ */
+ (void) signal(SIGCHLD, SIG_DFL);
+
+ /*
+ * Verify the user still exists and their account has not expired.
+ */
+ pw = getpwuid(job->uid);
+ if (pw == NULL) {
+ log_it("CRON", getpid(), "ORPHANED JOB", atfile);
+ _exit(ERROR_EXIT);
+ }
+ /* XXX - is this needed now that we do auth_approval? */
+ if (pw->pw_expire && time(NULL) >= pw->pw_expire) {
+ log_it(pw->pw_name, getpid(), "ACCOUNT EXPIRED, JOB ABORTED",
+ atfile);
+ _exit(ERROR_EXIT);
+ }
+
+ /* Sanity checks */
+ if (fstat(fd, &statbuf) < OK) {
+ log_it(pw->pw_name, getpid(), "FSTAT FAILED", atfile);
+ _exit(ERROR_EXIT);
+ }
+ if (!S_ISREG(statbuf.st_mode)) {
+ log_it(pw->pw_name, getpid(), "NOT REGULAR", atfile);
+ _exit(ERROR_EXIT);
+ }
+ if ((statbuf.st_mode & ALLPERMS) != (S_IRUSR | S_IWUSR | S_IXUSR)) {
+ log_it(pw->pw_name, getpid(), "BAD FILE MODE", atfile);
+ _exit(ERROR_EXIT);
+ }
+ if (statbuf.st_uid != 0 && statbuf.st_uid != job->uid) {
+ log_it(pw->pw_name, getpid(), "WRONG FILE OWNER", atfile);
+ _exit(ERROR_EXIT);
+ }
+ if (statbuf.st_nlink > 1) {
+ log_it(pw->pw_name, getpid(), "BAD LINK COUNT", atfile);
+ _exit(ERROR_EXIT);
+ }
+
+ if ((fp = fdopen(dup(fd), "r")) == NULL) {
+ log_it("CRON", getpid(), "error", "dup(2) failed");
+ _exit(ERROR_EXIT);
+ }
+
+ /*
+ * Check the at job header for sanity and extract the
+ * uid, gid, mailto user and always_mail flag.
+ *
+ * The header should look like this:
+ * #!/bin/sh
+ * # atrun uid=123 gid=123
+ * # mail joeuser 0
+ */
+ if (fgets(buf, sizeof(buf), fp) == NULL ||
+ strcmp(buf, "#!/bin/sh\n") != 0 ||
+ fgets(buf, sizeof(buf), fp) == NULL ||
+ strncmp(buf, "# atrun uid=", 12) != 0)
+ goto bad_file;
+
+ /* Pull out uid */
+ cp = buf + 12;
+ errno = 0;
+ nuid = strtol(cp, &ep, 10);
+ if (errno == ERANGE || (uid_t)nuid > UID_MAX || cp == ep ||
+ strncmp(ep, " gid=", 5) != 0)
+ goto bad_file;
+
+ /* Pull out gid */
+ cp = ep + 5;
+ errno = 0;
+ ngid = strtol(cp, &ep, 10);
+ if (errno == ERANGE || (uid_t)ngid > GID_MAX || cp == ep || *ep != '\n')
+ goto bad_file;
+
+ /* Pull out mailto user (and always_mail flag) */
+ if (fgets(buf, sizeof(buf), fp) == NULL ||
+ strncmp(buf, "# mail ", 7) != 0)
+ goto bad_file;
+ cp = buf + 7;
+ while (isspace(*cp))
+ cp++;
+ ep = cp;
+ while (!isspace(*ep) && *ep != '\0')
+ ep++;
+ if (*ep == '\0' || *ep != ' ' || ep - cp >= sizeof(mailto))
+ goto bad_file;
+ memcpy(mailto, cp, ep - cp);
+ mailto[ep - cp] = '\0';
+ always_mail = *(ep + 1) == '1';
+
+ (void)fclose(fp);
+ if (!safe_p(pw->pw_name, mailto))
+ _exit(ERROR_EXIT);
+ if ((uid_t)nuid != job->uid) {
+ log_it(pw->pw_name, getpid(), "UID MISMATCH", atfile);
+ _exit(ERROR_EXIT);
+ }
+ if ((gid_t)ngid != job->gid) {
+ log_it(pw->pw_name, getpid(), "GID MISMATCH", atfile);
+ _exit(ERROR_EXIT);
+ }
+
+#ifdef CAPITALIZE_FOR_PS
+ /* mark ourselves as different to PS command watchers by upshifting
+ * our program name. This has no effect on some kernels.
+ * XXX - really want to set proc title to at job name instead
+ */
+ /*local*/{
+ char *pch;
+
+ for (pch = ProgramName; *pch; pch++)
+ *pch = MkUpper(*pch);
+ }
+#endif /* CAPITALIZE_FOR_PS */
+
+ pipe(output_pipe); /* child's stdout/stderr */
+
+ /* Fork again, child will run the job, parent will catch output. */
+ switch ((pid = fork())) {
+ case -1:
+ log_it("CRON", getpid(), "error", "can't fork");
+ _exit(ERROR_EXIT);
+ /*NOTREACHED*/
+ case 0:
+ Debug(DPROC, ("[%ld] grandchild process fork()'ed\n",
+ (long)getpid()))
+
+ /* Write log message now that we have our real pid. */
+ log_it(pw->pw_name, getpid(), "ATJOB", atfile);
+
+#ifdef SYSLOG
+ closelog();
+#endif
+
+ /* Connect grandchild's stdin to the at job file. */
+ if (lseek(fd, (off_t) 0, SEEK_SET) < 0) {
+ perror("lseek");
+ _exit(ERROR_EXIT);
+ }
+ if (fd != STDIN) {
+ dup2(fd, STDIN);
+ close(fd);
+ }
+
+ /* Connect stdout/stderr to the pipe from our parent. */
+ if (output_pipe[WRITE_PIPE] != STDOUT) {
+ dup2(output_pipe[WRITE_PIPE], STDOUT);
+ close(output_pipe[WRITE_PIPE]);
+ }
+ dup2(STDOUT, STDERR);
+ close(output_pipe[READ_PIPE]);
+
+ (void) setsid();
+
+#ifdef LOGIN_CAP
+ {
+ login_cap_t *lc;
+# ifdef BSD_AUTH
+ auth_session_t *as;
+# endif
+ if ((lc = login_getclass(pw->pw_class)) == NULL) {
+ fprintf(stderr,
+ "Cannot get login class for %s\n",
+ pw->pw_name);
+ _exit(ERROR_EXIT);
+
+ }
+
+ if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETALL)) {
+ fprintf(stderr,
+ "setusercontext failed for %s\n",
+ pw->pw_name);
+ _exit(ERROR_EXIT);
+ }
+# ifdef BSD_AUTH
+ as = auth_open();
+ if (as == NULL || auth_setpwd(as, pw) != 0) {
+ fprintf(stderr, "can't malloc\n");
+ _exit(ERROR_EXIT);
+ }
+ if (auth_approval(as, lc, pw->pw_name, "cron") <= 0) {
+ fprintf(stderr, "approval failed for %s\n",
+ pw->pw_name);
+ _exit(ERROR_EXIT);
+ }
+ auth_close(as);
+ login_close(lc);
+# endif /* BSD_AUTH */
+ }
+#else
+ setgid(pw->pw_gid);
+ initgroups(pw->pw_name, pw->pw_gid);
+ setlogin(pw->pw_name);
+ setuid(pw->pw_uid);
+
+#endif /* LOGIN_CAP */
+
+ chdir("/"); /* at job will chdir to correct place */
+
+ /* If this is a low priority job, nice ourself. */
+ if (job->queue > 'b')
+ (void)setpriority(PRIO_PROCESS, 0, job->queue - 'b');
+
+#if DEBUGGING
+ if (DebugFlags & DTEST) {
+ fprintf(stderr,
+ "debug DTEST is on, not exec'ing at job %s\n",
+ atfile);
+ _exit(OK_EXIT);
+ }
+#endif /*DEBUGGING*/
+
+ /*
+ * Exec /bin/sh with stdin connected to the at job file
+ * and stdout/stderr hooked up to our parent.
+ * The at file will set the environment up for us.
+ */
+ nargv[0] = "sh";
+ nargv[1] = NULL;
+ nenvp[0] = NULL;
+ if (execve(_PATH_BSHELL, nargv, nenvp) != 0) {
+ perror("execve: " _PATH_BSHELL);
+ _exit(ERROR_EXIT);
+ }
+ break;
+ default:
+ /* parent */
+ break;
+ }
+
+ Debug(DPROC, ("[%ld] child continues, closing output pipe\n",
+ (long)getpid()))
+
+ /* Close the atfile's fd and the end of the pipe we don't use. */
+ close(fd);
+ close(output_pipe[WRITE_PIPE]);
+
+ /* Read piped output (if any) from the at job. */
+ Debug(DPROC, ("[%ld] child reading output from grandchild\n",
+ (long)getpid()))
+
+ fp = fdopen(output_pipe[READ_PIPE], "r");
+ if (always_mail || !feof(fp)) {
+ FILE *mail;
+ int bytes = 0;
+ int status = 0;
+ char mailcmd[MAX_COMMAND];
+ char hostname[MAXHOSTNAMELEN];
+ size_t nread;
+
+ Debug(DPROC|DEXT, ("[%ld] got data from grandchild\n",
+ (long)getpid()))
+
+ if (gethostname(hostname, sizeof(hostname)) != 0)
+ strcpy(hostname, "unknown");
+ if (snprintf(mailcmd, sizeof mailcmd, MAILFMT,
+ MAILARG) >= sizeof mailcmd) {
+ fprintf(stderr, "mailcmd too long\n");
+ (void) _exit(ERROR_EXIT);
+ }
+ if (!(mail = cron_popen(mailcmd, "w", pw))) {
+ perror(mailcmd);
+ (void) _exit(ERROR_EXIT);
+ }
+ fprintf(mail, "From: %s (Atrun Service)\n", pw->pw_name);
+ fprintf(mail, "To: %s\n", mailto);
+ fprintf(mail, "Subject: Output from \"at\" job\n");
+#ifdef MAIL_DATE
+ fprintf(mail, "Date: %s\n", arpadate(&StartTime));
+#endif /*MAIL_DATE*/
+ fprintf(mail, "\nYour \"at\" job on %s\n\"%s\"\n",
+ hostname, atfile);
+ fprintf(mail, "\nproduced the following output:\n\n");
+
+ /* Pipe the job's output to sendmail. */
+ while ((nread = fread(buf, 1, sizeof(buf), fp)) > 0) {
+ bytes += nread;
+ fwrite(buf, nread, 1, mail);
+ }
+
+ /*
+ * If the mailer exits with non-zero exit status, log
+ * this fact so the problem can (hopefully) be debugged.
+ */
+ Debug(DPROC, ("[%ld] closing pipe to mail\n",
+ (long)getpid()))
+ if ((status = cron_pclose(mail)) != 0) {
+ snprintf(buf, sizeof(buf), "mailed %d byte%s of output"
+ " but got status 0x%04x\n",
+ bytes, (bytes == 1) ? "" : "s", status);
+ log_it(pw->pw_name, getpid(), "MAIL", buf);
+ }
+ }
+ Debug(DPROC, ("[%ld] got EOF from grandchild\n", (long)getpid()))
+
+ fclose(fp); /* also closes output_pipe[READ_PIPE] */
+
+ /* Wait for grandchild to die. */
+ Debug(DPROC, ("[%ld] waiting for grandchild (%ld) to finish\n",
+ (long)getpid(), (long)pid))
+ for (;;) {
+ if (waitpid(pid, &waiter, 0) == -1) {
+ if (errno == EINTR)
+ continue;
+ Debug(DPROC,
+ ("[%ld] no grandchild process--mail written?\n",
+ (long)getpid()))
+ break;
+ } else {
+ 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"))
+ break;
+ }
+ }
+ _exit(OK_EXIT);
+
+bad_file:
+ log_it(pw->pw_name, getpid(), "BAD FILE FORMAT", atfile);
+ _exit(ERROR_EXIT);
+}
diff --git a/usr.sbin/cron/config.h b/usr.sbin/cron/config.h
index 61c573bc3b0..1a1639c553e 100644
--- a/usr.sbin/cron/config.h
+++ b/usr.sbin/cron/config.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.h,v 1.12 2002/07/08 23:42:17 millert Exp $ */
+/* $OpenBSD: config.h,v 1.13 2002/07/15 19:13:29 millert Exp $ */
/* Copyright 1988,1990,1993,1994 by Paul Vixie
* All rights reserved
@@ -21,7 +21,7 @@
* SOFTWARE.
*/
-/* config.h - configurables for Vixie Cron
+/* config.h - configurables for ISC cron
*/
/*
@@ -95,3 +95,6 @@
/* if your OS supports BSD authentication */
#define BSD_AUTH /*-*/
+
+ /* maximum load at which batch jobs will still run */
+#define BATCH_MAXLOAD 1.5
diff --git a/usr.sbin/cron/cron.8 b/usr.sbin/cron/cron.8
index 70b1c7a2dec..09cb6baf4f2 100644
--- a/usr.sbin/cron/cron.8
+++ b/usr.sbin/cron/cron.8
@@ -1,101 +1,166 @@
-.\"/* Copyright 1988,1990,1993,1996 by Paul Vixie
-.\" * All rights reserved
-.\" */
.\"
-.\" Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
+.\" Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+.\" All rights reserved.
.\"
-.\" 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.
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
.\"
-.\" THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
-.\" ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
-.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
-.\" CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
-.\" DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
-.\" PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
-.\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
-.\" SOFTWARE.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+.\" EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.\" $OpenBSD: cron.8,v 1.15 2002/07/08 18:11:02 millert Exp $
+.\" $OpenBSD: cron.8,v 1.16 2002/07/15 19:13:29 millert Exp $
.\"
-.Dd June 6, 1999
+.Dd July 6, 2002
.Dt CRON 8
.Os
.Sh NAME
.Nm cron
-.Nd daemon to execute scheduled commands (Vixie Cron)
+.Nd clock daemon
.Sh SYNOPSIS
.Nm cron
+.Op Fl l Ar load_avg
+.Op Fl x Ar [ext,sch,proc,pars,load,misc,test,bit]
.Sh DESCRIPTION
+The
.Nm
-should be started from
+daemon schedules commands to be run at specified dates and times.
+Commands that are to be run periodically are specified within
+.Xr crontab 5
+files.
+Commands that are only to be run once are scheduled via the
+.Xr at 1
+and
+.Xr batch 1
+commands.
+Normally, the
+.Nm
+daemon is started from the
.Pa /etc/rc
-or
-.Pa /etc/rc.local .
-It will return immediately, so you don't need to start it with
-.Ql \&& .
+command script.
Because it can execute commands on a user's behalf,
.Nm
should be run late in the startup sequence,
as close to the time when logins are accepted as possible.
.Pp
.Nm
-searches its spool directory
-.Pf ( Pa /var/cron/tabs Ns )
-for
+loads
.Xr crontab 5
-files which are named after accounts in
-.Pa /etc/passwd ;
-crontabs found are loaded into memory.
-.Nm
-also searches for
-.Pa /etc/crontab
-which is in a different format (see
-.Xr crontab 5 ) .
+and
+.Xr at 1
+files when it starts up and also when changes are made via the
+.Xr crontab 1
+and
+.Xr at 1
+commands.
+Additionally,
.Nm
-then wakes up every minute, examining all loaded crontabs, checking each
-command to see if it should be run in the current minute.
-When executing commands, any output is mailed to the user named in the
+checks the modification time on the system crontab file
+.Pq Pa /etc/crontab ,
+the crontab spool
+.Pq Pa /var/cron/tabs ,
+and the at spool
+.Pq Pa /var/at/jobs
+once a minute.
+If the modification time has changed, the affected files are reloaded.
+.Pp
+Any output produced by a command is sent to the used specified in the
.Ev MAILTO
-environment variable in the crontab, or to the owner of the crontab if
+environment variable as set in the
+.Xr crontab 5
+file or, if no
.Ev MAILTO
-is not present.
+variable is set (or if this is an
+.Xr at 1
+or
+.Xr batch 1
+job), to the job's owner.
+If a command produces no output, no mail will be sent.
+The exception to this are
+.Xr at 1
+or
+.Xr batch 1
+jobs submitted with the
+.Fl m
+flag.
+In this case, mail will be sent even if the job produces no output.
+.Ss Daylight Saving Time and other time changes
+Local time changes of less than three hours, such as those caused
+by the start or end of Daylight Saving Time, are handled specially.
+This only applies to jobs that run at a specific time and jobs that
+are run with a granularity greater than one hour. Jobs that run
+more frequently are scheduled normally.
.Pp
-Additionally,
-.Nm
-checks each minute to see if its spool directory's modtime (or the modtime on
-.Pa /etc/crontab )
-has changed, and if it has,
-.Nm
-examines the modtime on all crontabs and reloads those which have
-changed.
-Thus
-.Nm
-need not be restarted whenever a crontab file is modified.
-Note that the
-.Xr crontab 1
-command updates the modtime of the spool directory whenever it changes a
-crontab.
+If time has moved forward, those jobs that would have run in the
+interval that has been skipped will be run immediately.
+Conversely, if time has moved backward, care is taken to avoid running
+jobs twice.
.Pp
-Special considerations exist when the clock is changed by less than 3
-hours; for example, at the beginning and end of Daylight Saving
-Time.
-If the time has moved forward, those jobs which would have
-run in the time that was skipped will be run soon after the change.
-Conversely, if the time has moved backward by less than 3 hours,
-those jobs that fall into the repeated time will not be run.
+Time changes of more than 3 hours are considered to be corrections to
+the clock or timezone, and the new time is used immediately.
.Pp
-Only jobs that run at a particular time (not specified as @hourly, nor with
-.Ql *
-in the hour or minute specifier)
-are
-affected.
-Jobs which are specified with wildcards are run based on the
-new time immediately.
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl l Ar load_avg
+If the current load average is greater than
+.Ar load_avg ,
+.Xr batch 1
+jobs will not be run.
+The default value is 1.5.
+To allow
+.Xr batch 1
+jobs to run regardless of the load, a value of 0.0 may be used.
+.It Fl x Ar debug_flags
+If
+.Nm
+was compiled with debugging support, a number of debugging flags
+are available to show what
+.Nm
+is doing.
+The following flags may be specified:
+.Bl -tag -width Ds
+.It ext
+show extended information; used in conjunction with other debug flags
+to provide even more information
+.It sch
+print information related to scheduling jobs
+.It proc
+print information related to running processes
+.It pars
+print information related to parsing
+.Xr crontab 5
+files
+.It load
+print when loading the databases
+.It misc
+show misc other debugging information
+.It test
+test mode; don't actually execute commands
+.El
.Pp
-Clock changes of more than 3 hours are considered to be corrections to
-the clock, and the new time is used immediately.
+Multiple flags may be specified, sepatated by a comma
+.Pq So , Sc
+Regardless of which flags were specified, the
+.Fl x
+flag will cause
+.Nm
+to stay in the foreground and not become a daemon.
+.El
.Sh SIGNALS
.Bl -tag -width Ds
.It Dv SIGHUP
@@ -112,10 +177,16 @@ logs via
.El
.Sh FILES
.Bl -tag -width "/var/cron/tabs/.sock" -compact
+.It Pa /etc/crontab
+system crontab file
+.It Pa /var/at/jobs
+directory containing
+.Xr at 1
+jobs
.It Pa /var/cron/log
cron's log file
.It Pa /var/cron/tabs
-directory of individual crontabs
+directory containing individual crontab files
.It Pa /var/cron/tabs/.sock
used by
.Xr crontab 1
@@ -124,6 +195,7 @@ to tell
to check for crontab changes immediately
.El
.Sh SEE ALSO
+.Xr at 1 ,
.Xr crontab 1 ,
.Xr syslog 3 ,
.Xr crontab 5
diff --git a/usr.sbin/cron/cron.c b/usr.sbin/cron/cron.c
index 7afcdcdab6d..c3605ed50f3 100644
--- a/usr.sbin/cron/cron.c
+++ b/usr.sbin/cron/cron.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cron.c,v 1.26 2002/07/09 18:59:12 millert Exp $ */
+/* $OpenBSD: cron.c,v 1.27 2002/07/15 19:13:29 millert Exp $ */
/* Copyright 1988,1990,1993,1994 by Paul Vixie
* All rights reserved
*/
@@ -21,7 +21,7 @@
*/
#if !defined(lint) && !defined(LINT)
-static const char rcsid[] = "$OpenBSD: cron.c,v 1.26 2002/07/09 18:59:12 millert Exp $";
+static const char rcsid[] = "$OpenBSD: cron.c,v 1.27 2002/07/15 19:13:29 millert Exp $";
#endif
#define MAIN_PROGRAM
@@ -38,7 +38,6 @@ static void usage(void),
sigchld_handler(int),
sighup_handler(int),
sigchld_reaper(void),
- check_sigs(int),
quit(int),
parse_args(int c, char *v[]);
@@ -46,12 +45,14 @@ static volatile sig_atomic_t got_sighup, got_sigchld;
static int timeRunning, virtualTime, clockTime, cronSock;
static long GMToff;
static cron_db database;
+static at_db at_database;
+static double batch_maxload = BATCH_MAXLOAD;
static void
usage(void) {
const char **dflags;
- fprintf(stderr, "usage: %s [-x [", ProgramName);
+ fprintf(stderr, "usage: %s [-l load_avg] [-x [", ProgramName);
for (dflags = DebugFlagNames; *dflags; dflags++)
fprintf(stderr, "%s%s", *dflags, dflags[1] ? "," : "]");
fprintf(stderr, "]\n");
@@ -136,6 +137,10 @@ main(int argc, char *argv[]) {
database.tail = NULL;
database.mtime = (time_t) 0;
load_database(&database);
+ at_database.head = NULL;
+ at_database.tail = NULL;
+ at_database.mtime = (time_t) 0;
+ scan_atjobs(&at_database, NULL);
set_time(1);
run_reboot_jobs(&database);
timeRunning = virtualTime = clockTime;
@@ -154,7 +159,6 @@ main(int argc, char *argv[]) {
int timeDiff;
int wakeupKind;
- check_sigs(TRUE);
/* ... wait for the time (in minutes) to change ... */
do {
cron_sleep(timeRunning + 1);
@@ -257,6 +261,22 @@ main(int argc, char *argv[]) {
/* Jobs to be run (if any) are loaded; clear the queue. */
job_runqueue();
+
+ /* Run any jobs in the at queue. */
+ atrun(&at_database, batch_maxload,
+ timeRunning * SECONDS_PER_MINUTE - GMToff);
+
+ /* 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);
+ scan_atjobs(&at_database, NULL);
}
}
@@ -303,8 +323,8 @@ find_jobs(int vtime, cron_db *db, int doWild, int doNonWild) {
for (u = db->head; u != NULL; u = u->next) {
for (e = u->crontab; e != NULL; e = e->next) {
Debug(DSCH|DEXT, ("user [%s:%ld:%ld:...] cmd=\"%s\"\n",
- env_get("LOGNAME", e->envp),
- (long)e->uid, (long)e->gid, e->cmd))
+ e->pwd->pw_name, (long)e->pwd->pw_uid,
+ (long)e->pwd->pw_gid, e->cmd))
if (bit_test(e->minute, minute) &&
bit_test(e->hour, hour) &&
bit_test(e->month, month) &&
@@ -348,8 +368,8 @@ set_time(int initialize) {
*/
static void
cron_sleep(int target) {
- char c;
int fd, nfds;
+ unsigned char poke;
struct timeval t1, t2, tv;
struct sockaddr_un sun;
socklen_t sunlen;
@@ -359,8 +379,6 @@ cron_sleep(int target) {
t1.tv_sec += GMToff;
tv.tv_sec = (target * SECONDS_PER_MINUTE - t1.tv_sec) + 1;
tv.tv_usec = 0;
- Debug(DSCH, ("[%ld] Target time=%ld, sec-to-wait=%ld\n",
- (long)getpid(), (long)target*SECONDS_PER_MINUTE, tv.tv_sec))
if (fdsr == NULL) {
fdsr = (fd_set *)calloc(howmany(cronSock + 1, NFDBITS),
@@ -368,9 +386,13 @@ cron_sleep(int target) {
}
while (timerisset(&tv) && tv.tv_sec < 65) {
+ Debug(DSCH, ("[%ld] Target time=%ld, sec-to-wait=%ld\n",
+ (long)getpid(), (long)target*SECONDS_PER_MINUTE, tv.tv_sec))
+
+ poke = 0;
if (fdsr)
FD_SET(cronSock, fdsr);
- /* Sleep until we time out, get a crontab poke, or signal. */
+ /* Sleep until we time out, get a poke, or get a signal. */
nfds = select(cronSock + 1, fdsr, NULL, NULL, &tv);
if (nfds == 0)
break; /* timer expired */
@@ -381,18 +403,35 @@ cron_sleep(int target) {
(long)getpid()))
fd = accept(cronSock, (struct sockaddr *)&sun, &sunlen);
if (fd >= 0) {
- while (read(fd, &c, sizeof c) > 0)
- ; /* suck up anything in the socket */
+ (void) read(fd, &poke, 1);
close(fd);
+ if (poke & RELOAD_CRON)
+ load_database(&database);
+ if (poke & RELOAD_AT) {
+ /*
+ * We run any pending at jobs right
+ * away so that "at now" really runs
+ * jobs immediately.
+ */
+ gettimeofday(&t2, NULL);
+ if (scan_atjobs(&at_database, &t2))
+ atrun(&at_database,
+ batch_maxload, t2.tv_sec);
+ }
+ }
+ } else {
+ /* Interrupted by a signal. */
+ if (got_sighup) {
+ got_sighup = 0;
+ log_close();
+ }
+ if (got_sigchld) {
+ got_sigchld = 0;
+ sigchld_reaper();
}
}
- /*
- * Check to see if we were interrupted by a signal or a poke
- * on the socket. If so, service the signal/poke, adjust tv
- * and continue the select() where we left off.
- */
- check_sigs(nfds > 0);
+ /* Adjust tv and continue where we left off. */
gettimeofday(&t2, NULL);
t2.tv_sec += GMToff;
timersub(&t2, &t1, &t1);
@@ -451,31 +490,28 @@ sigchld_reaper() {
}
static void
-check_sigs(int force_dbload) {
- if (got_sighup) {
- got_sighup = 0;
- log_close();
- }
- if (got_sigchld) {
- got_sigchld = 0;
- sigchld_reaper();
- }
- if (force_dbload)
- load_database(&database);
-}
-
-static void
parse_args(int argc, char *argv[]) {
int argch;
+ char *ep;
- while (-1 != (argch = getopt(argc, argv, "x:"))) {
+ while (-1 != (argch = getopt(argc, argv, "l:x:"))) {
switch (argch) {
- default:
- usage();
+ case 'l':
+ errno = 0;
+ batch_maxload = strtod(optarg, &ep);
+ if (*ep != '\0' || ep == optarg || errno == ERANGE ||
+ batch_maxload < 0) {
+ fprintf(stderr, "Illegal load average: %s\n",
+ optarg);
+ usage();
+ }
+ break;
case 'x':
if (!set_debug_flags(optarg))
usage();
break;
+ default:
+ usage();
}
}
}
diff --git a/usr.sbin/cron/crontab.1 b/usr.sbin/cron/crontab.1
index d0d7de14d4c..31150510cdf 100644
--- a/usr.sbin/cron/crontab.1
+++ b/usr.sbin/cron/crontab.1
@@ -15,7 +15,7 @@
.\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
.\" SOFTWARE.
.\"
-.\" $OpenBSD: crontab.1,v 1.14 2002/07/08 18:11:02 millert Exp $
+.\" $OpenBSD: crontab.1,v 1.15 2002/07/15 19:13:29 millert Exp $
.\"
.Dd June 8, 1999
.Dt CRONTAB 1
@@ -39,7 +39,7 @@
is the program used to install, deinstall, or list the tables
used to drive the
.Xr cron 8
-daemon in Vixie Cron.
+daemon.
Each user can have their own
.Xr crontab 5 ,
and though these are files in
diff --git a/usr.sbin/cron/crontab.c b/usr.sbin/cron/crontab.c
index 346f249f0e5..48bcf3c4afc 100644
--- a/usr.sbin/cron/crontab.c
+++ b/usr.sbin/cron/crontab.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: crontab.c,v 1.33 2002/07/11 20:17:04 millert Exp $ */
+/* $OpenBSD: crontab.c,v 1.34 2002/07/15 19:13:29 millert Exp $ */
/* Copyright 1988,1990,1993,1994 by Paul Vixie
* All rights reserved
*/
@@ -21,7 +21,7 @@
*/
#if !defined(lint) && !defined(LINT)
-static char const rcsid[] = "$OpenBSD: crontab.c,v 1.33 2002/07/11 20:17:04 millert Exp $";
+static char const rcsid[] = "$OpenBSD: crontab.c,v 1.34 2002/07/15 19:13:29 millert Exp $";
#endif
/* crontab - install and manage per-user crontab files
@@ -639,6 +639,7 @@ done:
static void
poke_daemon() {
int sock, flags;
+ unsigned char poke;
struct sockaddr_un sun;
if (utime(SPOOL_DIR, NULL) < OK) {
@@ -654,12 +655,13 @@ poke_daemon() {
sun.sun_family = AF_UNIX;
sun.sun_len = strlen(sun.sun_path);
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 &&
- (flags = fcntl(sock, F_GETFL)) >= 0 &&
- fcntl(sock, F_SETFL, flags | O_NONBLOCK) >= 0 &&
connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
- write(sock, "!", 1);
+ poke = RELOAD_CRON;
+ write(sock, &poke, 1);
close(sock);
- }
+ } else
+ fprintf(stderr, "Warning, cron does not appear to be running.\n");
+
}
(void) signal(SIGPIPE, SIG_DFL);
}
diff --git a/usr.sbin/cron/do_command.c b/usr.sbin/cron/do_command.c
index e1d278e85fc..17e3943df05 100644
--- a/usr.sbin/cron/do_command.c
+++ b/usr.sbin/cron/do_command.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: do_command.c,v 1.18 2002/07/12 18:35:24 millert Exp $ */
+/* $OpenBSD: do_command.c,v 1.19 2002/07/15 19:13:29 millert Exp $ */
/* Copyright 1988,1990,1993,1994 by Paul Vixie
* All rights reserved
*/
@@ -21,19 +21,18 @@
*/
#if !defined(lint) && !defined(LINT)
-static char const rcsid[] = "$OpenBSD: do_command.c,v 1.18 2002/07/12 18:35:24 millert Exp $";
+static char const rcsid[] = "$OpenBSD: do_command.c,v 1.19 2002/07/15 19:13:29 millert Exp $";
#endif
#include "cron.h"
static void child_process(entry *, user *);
-static int safe_p(const char *, const char *);
void
do_command(entry *e, user *u) {
Debug(DPROC, ("[%ld] do_command(%s, (%s,%lu,%lu))\n",
(long)getpid(), e->cmd, u->name,
- (u_long)e->uid, (u_long)e->gid))
+ (u_long)e->pwd->pw_uid, (u_long)e->pwd->pw_gid))
/* fork to become asynchronous -- parent process is done immediately,
* and continues to run the normal cron code, which means return to
@@ -84,7 +83,7 @@ child_process(entry *e, user *u) {
/* discover some useful and important environment settings
*/
- usernm = env_get("LOGNAME", e->envp);
+ usernm = e->pwd->pw_name;
mailto = env_get("MAILTO", e->envp);
/* our parent is watching for our death by catching SIGCHLD. we
@@ -104,10 +103,9 @@ child_process(entry *e, user *u) {
*
* 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*/{
int escaped = FALSE;
@@ -141,11 +139,11 @@ child_process(entry *e, user *u) {
*/
switch (fork()) {
case -1:
- log_it("CRON", getpid(), "error", "can't vfork");
+ log_it("CRON", getpid(), "error", "can't fork");
exit(ERROR_EXIT);
/*NOTREACHED*/
case 0:
- Debug(DPROC, ("[%ld] grandchild process Vfork()'ed\n",
+ Debug(DPROC, ("[%ld] grandchild process fork()'ed\n",
(long)getpid()))
/* write a log message. we've waited this long to do it
@@ -197,45 +195,49 @@ child_process(entry *e, user *u) {
*/
#ifdef LOGIN_CAP
{
- struct passwd *pwd;
- char *ep, *np;
+ login_cap_t *lc;
+ char **p;
+ extern char **environ;
/* XXX - should just pass in a login_cap_t * */
- pwd = getpwuid(e->uid);
- if (pwd == NULL) {
- fprintf(stderr, "getpwuid: couldn't get entry for %u\n", e->uid);
+ if ((lc = login_getclass(e->pwd->pw_class)) == NULL) {
+ fprintf(stderr,
+ "unable to get login class for %s\n",
+ e->pwd->pw_name);
_exit(ERROR_EXIT);
}
- if (setusercontext(0, pwd, e->uid, LOGIN_SETALL) < 0) {
- fprintf(stderr, "setusercontext failed for %u\n", e->uid);
+ if (setusercontext(lc, e->pwd, e->pwd->pw_uid, LOGIN_SETALL) < 0) {
+ fprintf(stderr, "setusercontext failed for %s\n", e->pwd->pw_name);
_exit(ERROR_EXIT);
}
#ifdef BSD_AUTH
- if (auth_approval(0, 0, pwd->pw_name, "cron") <= 0) {
- fprintf(stderr, "approval failed for %u\n", e->uid);
+ /* XXX - stash pwd with auth_setpwd to avoid lookup */
+ if (auth_approval(0, lc, usernm, "cron") <= 0) {
+ fprintf(stderr, "approval failed for %s\n",
+ e->pwd->pw_name);
_exit(ERROR_EXIT);
}
#endif /* BSD_AUTH */
+ login_close(lc);
+
/* If no PATH specified in crontab file but
* we just added one via login.conf, add it to
* the crontab environment.
*/
- if (env_get("PATH", e->envp) == NULL &&
- (ep = getenv("PATH"))) {
- np = malloc(strlen(ep) + 6);
- if (np) {
- strcpy(np, "PATH=");
- strcat(np, ep);
- e->envp = env_set(e->envp, np);
+ if (env_get("PATH", e->envp) == NULL && environ != NULL) {
+ for (p = environ; *p; p++) {
+ if (strncmp(*p, "PATH=", 5) == 0) {
+ e->envp = env_set(e->envp, *p);
+ break;
+ }
}
}
-
}
#else
- setgid(e->gid);
- initgroups(env_get("LOGNAME", e->envp), e->gid);
+ setgid(e->pwd->pw_gid);
+ initgroups(usernm, e->pwd->pw_gid);
setlogin(usernm);
- setuid(e->uid); /* we aren't root after this... */
+ setuid(e->pw->pw_uid); /* we aren't root after this... */
#endif /* LOGIN_CAP */
chdir(env_get("HOME", e->envp));
@@ -402,7 +404,7 @@ child_process(entry *e, user *u) {
fprintf(stderr, "mailcmd too long\n");
(void) _exit(ERROR_EXIT);
}
- if (!(mail = cron_popen(mailcmd, "w", e))) {
+ if (!(mail = cron_popen(mailcmd, "w", e->pwd))) {
perror(mailcmd);
(void) _exit(ERROR_EXIT);
}
@@ -499,7 +501,7 @@ child_process(entry *e, user *u) {
}
}
-static int
+int
safe_p(const char *usernm, const char *s) {
static const char safe_delim[] = "@!:%-.,"; /* conservative! */
const char *t;
diff --git a/usr.sbin/cron/entry.c b/usr.sbin/cron/entry.c
index b672babcc9d..603f94538af 100644
--- a/usr.sbin/cron/entry.c
+++ b/usr.sbin/cron/entry.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: entry.c,v 1.12 2002/07/11 20:15:40 millert Exp $ */
+/* $OpenBSD: entry.c,v 1.13 2002/07/15 19:13:29 millert Exp $ */
/*
* Copyright 1988,1990,1993,1994 by Paul Vixie
* All rights reserved
@@ -22,7 +22,7 @@
*/
#if !defined(lint) && !defined(LINT)
-static char const rcsid[] = "$OpenBSD: entry.c,v 1.12 2002/07/11 20:15:40 millert Exp $";
+static char const rcsid[] = "$OpenBSD: entry.c,v 1.13 2002/07/15 19:13:29 millert Exp $";
#endif
/* vix 26jan87 [RCS'd; rest of log is in RCS file]
@@ -61,6 +61,7 @@ static int set_element(bitstr_t *, int, int, int);
void
free_entry(entry *e) {
free(e->cmd);
+ free(e->pwd);
env_free(e->envp);
free(e);
}
@@ -253,14 +254,16 @@ load_entry(FILE *file, void (*error_func)(), struct passwd *pw, char **envp) {
goto eof;
}
Debug(DPARS, ("load_entry()...uid %ld, gid %ld\n",
- (long)e->uid, (long)e->gid))
+ (long)e->pwd->pw_uid, (long)e->pwd->pw_gid))
} else if (ch == '*') {
ecode = e_cmd;
goto eof;
}
- e->uid = pw->pw_uid;
- e->gid = pw->pw_gid;
+ if ((e->pwd = pw_dup(pw)) == NULL) {
+ ecode = e_memory;
+ goto eof;
+ }
/* copy and fix up environment. some variables are just defaults and
* others are overrides.
@@ -372,6 +375,8 @@ load_entry(FILE *file, void (*error_func)(), struct passwd *pw, char **envp) {
eof:
if (e->envp)
env_free(e->envp);
+ if (e->pwd)
+ free(e->pwd);
if (e->cmd)
free(e->cmd);
free(e);
diff --git a/usr.sbin/cron/externs.h b/usr.sbin/cron/externs.h
index 2b4555dfa86..a39066461f1 100644
--- a/usr.sbin/cron/externs.h
+++ b/usr.sbin/cron/externs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: externs.h,v 1.6 2002/07/08 23:42:17 millert Exp $ */
+/* $OpenBSD: externs.h,v 1.7 2002/07/15 19:13:29 millert Exp $ */
/* Copyright 1993,1994 by Paul Vixie
* All rights reserved
@@ -57,14 +57,6 @@
# include <syslog.h>
#endif
-#if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX)
-# include <paths.h>
-#endif /*BSD*/
-
-#if !defined(_PATH_SENDMAIL)
-# define _PATH_SENDMAIL "/usr/lib/sendmail"
-#endif /*SENDMAIL*/
-
#if defined(LOGIN_CAP)
#include <login_cap.h>
#endif /*LOGIN_CAP*/
@@ -115,7 +107,7 @@ extern int optind, opterr, optopt;
*/
extern int flock(int, int);
-/* not all systems who provice flock() provide these definitions.
+/* not all systems who provide flock() provide these definitions.
*/
#ifndef LOCK_SH
# define LOCK_SH 1
diff --git a/usr.sbin/cron/funcs.h b/usr.sbin/cron/funcs.h
index 7d11afd97ac..84a2bf67260 100644
--- a/usr.sbin/cron/funcs.h
+++ b/usr.sbin/cron/funcs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: funcs.h,v 1.7 2002/07/08 18:11:02 millert Exp $ */
+/* $OpenBSD: funcs.h,v 1.8 2002/07/15 19:13:29 millert Exp $ */
/*
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
@@ -38,7 +38,8 @@ void set_cron_uid(void),
acquire_daemonlock(int),
skip_comments(FILE *),
log_it(const char *, int, const char *, const char *),
- log_close(void);
+ log_close(void),
+ atrun(at_db *, double, time_t);
int job_runqueue(void),
set_debug_flags(const char *),
@@ -52,7 +53,9 @@ int job_runqueue(void),
strcmp_until(const char *, const char *, char),
allowed(const char *),
strdtb(char *),
- open_socket(void);
+ open_socket(void),
+ safe_p(const char *, const char *),
+ scan_atjobs(at_db *, struct timeval *);
char *env_get(char *, char **),
*arpadate(time_t *),
@@ -67,7 +70,7 @@ user *load_user(int, struct passwd *, const char *),
entry *load_entry(FILE *, void (*)(), struct passwd *, char **);
-FILE *cron_popen(char *, char *, entry *);
+FILE *cron_popen(char *, char *, struct passwd *);
#ifndef HAVE_TM_GMTOFF
long get_gmtoff(time_t *, struct tm *);
diff --git a/usr.sbin/cron/globals.h b/usr.sbin/cron/globals.h
index dfd1874cc3c..53ae2d79d78 100644
--- a/usr.sbin/cron/globals.h
+++ b/usr.sbin/cron/globals.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: globals.h,v 1.3 2001/02/19 14:33:33 millert Exp $ */
+/* $OpenBSD: globals.h,v 1.4 2002/07/15 19:13:29 millert Exp $ */
/*
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
@@ -64,8 +64,7 @@ XTRN int DebugFlags INIT(0);
XTRN const char *DebugFlagNames[]
#ifdef MAIN_PROGRAM
= {
- "ext", "sch", "proc", "pars", "load", "misc", "test", "bit",\
- NULL
+ "ext", "sch", "proc", "pars", "load", "misc", "test", NULL
}
#endif
;
diff --git a/usr.sbin/cron/macros.h b/usr.sbin/cron/macros.h
index a325d2eed6b..452334c0ec2 100644
--- a/usr.sbin/cron/macros.h
+++ b/usr.sbin/cron/macros.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: macros.h,v 1.2 2001/02/20 02:03:19 millert Exp $ */
+/* $OpenBSD: macros.h,v 1.3 2002/07/15 19:13:29 millert Exp $ */
/*
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
@@ -60,9 +60,7 @@
#define DLOAD 0x0010 /* database loading debug mask */
#define DMISC 0x0020 /* misc debug mask */
#define DTEST 0x0040 /* test mode: don't execute any commands */
-#define DBIT 0x0080 /* bit twiddling shown (long) */
-#define CRON_TAB(u) "%s/%s", SPOOL_DIR, u
#define PPC_NULL ((const char **)NULL)
#ifndef MAXHOSTNAMELEN
@@ -95,6 +93,10 @@
LineNumber = ln; \
}
+/* Data values used on cron socket */
+#define RELOAD_CRON 0x2
+#define RELOAD_AT 0x4
+
#ifdef HAVE_TM_GMTOFF
#define get_gmtoff(c, t) (t->tm_gmtoff)
#endif
diff --git a/usr.sbin/cron/misc.c b/usr.sbin/cron/misc.c
index 6f2f3fefbf2..4c2a38eada0 100644
--- a/usr.sbin/cron/misc.c
+++ b/usr.sbin/cron/misc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.c,v 1.21 2002/07/09 18:59:12 millert Exp $ */
+/* $OpenBSD: misc.c,v 1.22 2002/07/15 19:13:29 millert Exp $ */
/* Copyright 1988,1990,1993,1994 by Paul Vixie
* All rights reserved
*/
@@ -21,7 +21,7 @@
*/
#if !defined(lint) && !defined(LINT)
-static char const rcsid[] = "$OpenBSD: misc.c,v 1.21 2002/07/09 18:59:12 millert Exp $";
+static char const rcsid[] = "$OpenBSD: misc.c,v 1.22 2002/07/15 19:13:29 millert Exp $";
#endif
/* vix 26jan87 [RCS has the rest of the log]
@@ -725,7 +725,7 @@ long get_gmtoff(time_t *clock, struct tm *local)
int
open_socket()
{
- int sock, flags;
+ int sock;
mode_t omask;
struct sockaddr_un sun;
@@ -736,15 +736,6 @@ open_socket()
log_it("CRON", getpid(), "DEATH", "can't create socket");
exit(ERROR_EXIT);
}
- if ((flags = fcntl(sock, F_GETFL)) == -1 ||
- fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
- fprintf(stderr, "%s: can't set non-block: %s\n",
- ProgramName, strerror(errno));
- log_it("CRON", getpid(), "DEATH", "can't set non-block");
- exit(ERROR_EXIT);
- }
- (void) fcntl(sock, F_SETFD, 1);
-
if (!glue_strings(sun.sun_path, sizeof sun.sun_path, SPOOL_DIR,
CRONSOCK, '/')) {
fprintf(stderr, "%s/%s: path too long\n", SPOOL_DIR, CRONSOCK);
diff --git a/usr.sbin/cron/pathnames.h b/usr.sbin/cron/pathnames.h
index dad71a081dd..52a4f6255cd 100644
--- a/usr.sbin/cron/pathnames.h
+++ b/usr.sbin/cron/pathnames.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pathnames.h,v 1.7 2002/07/09 18:59:12 millert Exp $ */
+/* $OpenBSD: pathnames.h,v 1.8 2002/07/15 19:13:29 millert Exp $ */
/* Copyright 1993,1994 by Paul Vixie
* All rights reserved
@@ -21,8 +21,15 @@
* SOFTWARE.
*/
+#ifndef _PATHNAMES_H_
+#define _PATHNAMES_H_
+
+#if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX)
+# include <paths.h>
+#endif /*BSD*/
+
#ifndef CRONDIR
- /* CRONDIR is where crond(8) and crontab(1) both chdir
+ /* CRONDIR is where cron(8) and crontab(1) both chdir
* to; SPOOL_DIR, ALLOW_FILE, DENY_FILE, and LOG_FILE
* are all relative to this directory.
*/
@@ -32,7 +39,7 @@
/* 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...)
@@ -95,3 +102,14 @@
#ifndef _PATH_DEVNULL
# define _PATH_DEVNULL "/dev/null"
#endif
+
+#if !defined(_PATH_SENDMAIL)
+# define _PATH_SENDMAIL "/usr/lib/sendmail"
+#endif /*SENDMAIL*/
+
+/* XXX */
+#define _PATH_ATJOBS "/var/at/jobs"
+#define _PATH_AT_ALLOW "/var/at/at.allow"
+#define _PATH_AT_DENY "/var/at/at.deny"
+
+#endif /* _PATHNAMES_H_ */
diff --git a/usr.sbin/cron/popen.c b/usr.sbin/cron/popen.c
index e2963ac747e..e8f5cfdd3c1 100644
--- a/usr.sbin/cron/popen.c
+++ b/usr.sbin/cron/popen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: popen.c,v 1.16 2002/07/09 00:24:50 millert Exp $ */
+/* $OpenBSD: popen.c,v 1.17 2002/07/15 19:13:29 millert Exp $ */
/*
* Copyright (c) 1988, 1993, 1994
@@ -42,7 +42,7 @@
#if 0
static const sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94";
#else
-static const char rcsid[] = "$OpenBSD: popen.c,v 1.16 2002/07/09 00:24:50 millert Exp $";
+static const char rcsid[] = "$OpenBSD: popen.c,v 1.17 2002/07/15 19:13:29 millert Exp $";
#endif
#endif /* not lint */
@@ -60,7 +60,7 @@ static PID_T *pids;
static int fds;
FILE *
-cron_popen(char *program, char *type, entry *e) {
+cron_popen(char *program, char *type, struct passwd *pw) {
char *cp;
FILE *iop;
int argc, pdes[2];
@@ -93,28 +93,23 @@ cron_popen(char *program, char *type, entry *e) {
return (NULL);
/* NOTREACHED */
case 0: /* child */
- if (e) {
+ if (pw) {
#if defined(LOGIN_CAP)
- struct passwd *pwd;
-
- pwd = getpwuid(e->uid);
- if (pwd == NULL) {
- fprintf(stderr, "getpwuid: couldn't get entry for %u\n", e->uid);
- _exit(ERROR_EXIT);
- }
- if (setusercontext(0, pwd, e->uid, LOGIN_SETALL) < 0) {
- fprintf(stderr, "setusercontext failed for %u\n", e->uid);
+ if (setusercontext(0, pw, pw->pw_uid, LOGIN_SETALL) < 0) {
+ fprintf(stderr,
+ "setusercontext failed for %s\n",
+ pw->pw_name);
_exit(ERROR_EXIT);
}
#else
- if (setgid(e->gid) ||
+ if (setgid(pw->pw_gid) ||
setgroups(0, NULL) ||
- initgroups(env_get("LOGNAME", e->envp), e->gid))
+ initgroups(pw->pw_name, pw->pw_gid))
_exit(1);
- setlogin(env_get("LOGNAME", e->envp));
- if (setuid(e->uid))
+ setlogin(pw->pw_name);
+ if (setuid(pw->pw_uid))
_exit(1);
- chdir(env_get("HOME", e->envp));
+ chdir(pw->pw_dir);
#endif /* LOGIN_CAP */
}
if (*type == 'r') {
diff --git a/usr.sbin/cron/structs.h b/usr.sbin/cron/structs.h
index a0920f7249e..e3602ab4830 100644
--- a/usr.sbin/cron/structs.h
+++ b/usr.sbin/cron/structs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: structs.h,v 1.3 2002/07/08 18:11:02 millert Exp $ */
+/* $OpenBSD: structs.h,v 1.4 2002/07/15 19:13:29 millert Exp $ */
/*
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
@@ -19,8 +19,7 @@
typedef struct _entry {
struct _entry *next;
- uid_t uid;
- gid_t gid;
+ struct passwd *pwd;
char **envp;
char *cmd;
bitstr_t bit_decl(minute, MINUTE_COUNT);
@@ -55,6 +54,19 @@ typedef struct _cron_db {
user *head, *tail; /* links */
time_t mtime; /* last modtime on spooldir */
} cron_db;
+
+typedef struct _atjob {
+ struct _atjob *next, *prev; /* links */
+ uid_t uid; /* uid of the job */
+ gid_t gid; /* gid of the job */
+ int queue; /* name of the at queue */
+ time_t run_time; /* time to run at job */
+} atjob;
+
+typedef struct _at_db {
+ atjob *head, *tail; /* links */
+ time_t mtime; /* last modtime on spooldir */
+} at_db;
/* in the C tradition, we only create
* variables for the main program, just
* extern them elsewhere.