1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-01-04 12:52:15 +00:00

Paul Vixie's cron, version 3.0. Munged into bmake format. If this goes

well, expect our two seperate directories for cron and crontab to go away
shortly.
Submitted by:	jkh
This commit is contained in:
Jordan K. Hubbard 1994-08-27 13:43:04 +00:00
parent e84e5d655f
commit 84f33dea62
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/cvs2svn/branches/cron/; revision=2311
35 changed files with 6147 additions and 0 deletions

3
usr.sbin/cron/Makefile Normal file
View File

@ -0,0 +1,3 @@
SUBDIR= lib cron crontab
.include <bsd.subdir.mk>

View File

@ -0,0 +1,8 @@
PROG= cron
SRCS= cron.c database.c do_command.c job.c user.c popen.c
MAN3= bitstring.3
MAN8= cron.8
LDADD+= -L${.CURDIR}/../lib -lcron
.include <bsd.prog.mk>

View File

@ -0,0 +1,168 @@
.\" Copyright (c) 1989 The Regents of the University of California.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to Berkeley by
.\" Paul Vixie.
.\"
.\" Redistribution and use in source and binary forms are permitted
.\" provided that the above copyright notice and this paragraph are
.\" duplicated in all such forms and that any documentation,
.\" advertising materials, and other materials related to such
.\" distribution and use acknowledge that the software was developed
.\" by the University of California, Berkeley. The name of the
.\" University may not be used to endorse or promote products derived
.\" from this software without specific prior written permission.
.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
.\"
.\" @(#)bitstring.3 5.1 (Berkeley) 12/13/89
.\"
.TH BITSTRING 3 "December 13, 1989"
.UC 4
.SH NAME
bit_alloc, bit_clear, bit_decl, bit_ffs, bit_nclear, bit_nset,
bit_set, bitstr_size, bit_test \- bit-string manipulation macros
.SH SYNOPSIS
.ft B
.nf
#include <bitstring.h>
name = bit_alloc(nbits)
bitstr_t *name;
int nbits;
bit_decl(name, nbits)
bitstr_t name;
int nbits;
bit_clear(name, bit)
bitstr_t name;
int bit;
bit_ffc(name, nbits, value)
bitstr_t name;
int nbits, *value;
bit_ffs(name, nbits, value)
bitstr_t name;
int nbits, *value;
bit_nclear(name, start, stop)
bitstr_t name;
int start, stop;
bit_nset(name, start, stop)
bitstr_t name;
int start, stop;
bit_set(name, bit)
bitstr_t name;
int bit;
bitstr_size(nbits)
int nbits;
bit_test(name, bit)
bitstr_t name;
int bit;
.fi
.ft R
.SH DESCRIPTION
These macros operate on strings of bits.
.PP
.I Bit_alloc
returns a pointer of type
.I bitstr_t\ *
to sufficient space to store
.I nbits
bits, or NULL if no space is available.
.PP
.I Bit_decl
is a macro for allocating sufficient space to store
.I nbits
bits on the stack.
.PP
.I Bitstr_size
returns the number of elements of type
.I bitstr_t
necessary to store
.I nbits
bits.
This is useful for copying bit strings.
.PP
.I Bit_clear
and
.I bit_set
clear or set the zero-based numbered bit
.IR bit ,
in the bit string
.IR name .
.PP
.I Bit_nset
and
.I bit_nclear
set or clear the zero-based numbered bits from
.I start
to
.I stop
in the bit string
.IR name .
.PP
.I Bit_test
evaluates to zero if the zero-based numbered bit
.I bit
of bit string
.I name
is set, and non-zero otherwise.
.PP
.I Bit_ffs
sets
.I *value
to the zero-based number of the first bit set in the array of
.I nbits
bits referenced by
.IR name .
If no bits are set,
.I *value
is set to -1.
.PP
.I Bit_ffc
sets
.I *value
to the zero-based number of the first bit not set in the array of
.I nbits
bits referenced by
.IR name .
If all bits are set,
.I value
is set to -1.
.SH EXAMPLE
.nf
.in +5
#include <limits.h>
#include <bitstring.h>
...
#define LPR_BUSY_BIT 0
#define LPR_FORMAT_BIT 1
#define LPR_DOWNLOAD_BIT 2
...
#define LPR_AVAILABLE_BIT 9
#define LPR_MAX_BITS 10
make_lpr_available()
{
bitstr_t bit_decl(bitlist, LPR_MAX_BITS);
...
bit_nclear(bitlist, 0, LPR_MAX_BITS - 1);
...
if (!bit_test(bitlist, LPR_BUSY_BIT)) {
bit_clear(bitlist, LPR_FORMAT_BIT);
bit_clear(bitlist, LPR_DOWNLOAD_BIT);
bit_set(bitlist, LPR_AVAILABLE_BIT);
}
}
.fi
.SH "SEE ALSO"
malloc(3)

View File

@ -0,0 +1,122 @@
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Paul Vixie.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* @(#)bitstring.h 5.2 (Berkeley) 4/4/90
*/
typedef unsigned char bitstr_t;
/* internal macros */
/* byte of the bitstring bit is in */
#define _bit_byte(bit) \
((bit) >> 3)
/* mask for the bit within its byte */
#define _bit_mask(bit) \
(1 << ((bit)&0x7))
/* external macros */
/* bytes in a bitstring of nbits bits */
#define bitstr_size(nbits) \
((((nbits) - 1) >> 3) + 1)
/* allocate a bitstring */
#define bit_alloc(nbits) \
(bitstr_t *)malloc(1, \
(unsigned int)bitstr_size(nbits) * sizeof(bitstr_t))
/* allocate a bitstring on the stack */
#define bit_decl(name, nbits) \
(name)[bitstr_size(nbits)]
/* is bit N of bitstring name set? */
#define bit_test(name, bit) \
((name)[_bit_byte(bit)] & _bit_mask(bit))
/* set bit N of bitstring name */
#define bit_set(name, bit) \
(name)[_bit_byte(bit)] |= _bit_mask(bit)
/* clear bit N of bitstring name */
#define bit_clear(name, bit) \
(name)[_bit_byte(bit)] &= ~_bit_mask(bit)
/* clear bits start ... stop in bitstring */
#define bit_nclear(name, start, stop) { \
register bitstr_t *_name = name; \
register int _start = start, _stop = stop; \
register int _startbyte = _bit_byte(_start); \
register int _stopbyte = _bit_byte(_stop); \
if (_startbyte == _stopbyte) { \
_name[_startbyte] &= ((0xff >> (8 - (_start&0x7))) | \
(0xff << ((_stop&0x7) + 1))); \
} else { \
_name[_startbyte] &= 0xff >> (8 - (_start&0x7)); \
while (++_startbyte < _stopbyte) \
_name[_startbyte] = 0; \
_name[_stopbyte] &= 0xff << ((_stop&0x7) + 1); \
} \
}
/* set bits start ... stop in bitstring */
#define bit_nset(name, start, stop) { \
register bitstr_t *_name = name; \
register int _start = start, _stop = stop; \
register int _startbyte = _bit_byte(_start); \
register int _stopbyte = _bit_byte(_stop); \
if (_startbyte == _stopbyte) { \
_name[_startbyte] |= ((0xff << (_start&0x7)) & \
(0xff >> (7 - (_stop&0x7)))); \
} else { \
_name[_startbyte] |= 0xff << ((_start)&0x7); \
while (++_startbyte < _stopbyte) \
_name[_startbyte] = 0xff; \
_name[_stopbyte] |= 0xff >> (7 - (_stop&0x7)); \
} \
}
/* find first bit clear in name */
#define bit_ffc(name, nbits, value) { \
register bitstr_t *_name = name; \
register int _byte, _nbits = nbits; \
register int _stopbyte = _bit_byte(_nbits), _value = -1; \
for (_byte = 0; _byte <= _stopbyte; ++_byte) \
if (_name[_byte] != 0xff) { \
_value = _byte << 3; \
for (_stopbyte = _name[_byte]; (_stopbyte&0x1); \
++_value, _stopbyte >>= 1); \
break; \
} \
*(value) = _value; \
}
/* find first bit set in name */
#define bit_ffs(name, nbits, value) { \
register bitstr_t *_name = name; \
register int _byte, _nbits = nbits; \
register int _stopbyte = _bit_byte(_nbits), _value = -1; \
for (_byte = 0; _byte <= _stopbyte; ++_byte) \
if (_name[_byte]) { \
_value = _byte << 3; \
for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \
++_value, _stopbyte >>= 1); \
break; \
} \
*(value) = _value; \
}

137
usr.sbin/cron/cron/compat.h Normal file
View File

@ -0,0 +1,137 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
/*
* $Id: compat.h,v 1.8 1994/01/15 20:43:43 vixie Exp $
*/
#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 <stdio.h>
* and <stdlib.h> 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
#if (BSD >= 199103)
# define HAVE_SAVED_UIDS
#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

View File

@ -0,0 +1,86 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
/* config.h - configurables for Vixie Cron
*
* $Id: config.h,v 2.6 1994/01/15 20:43:43 vixie Exp $
*/
#if !defined(_PATH_SENDMAIL)
# define _PATH_SENDMAIL "/usr/lib/sendmail"
#endif /*SENDMAIL*/
/*
* these are site-dependent
*/
#ifndef DEBUGGING
#define DEBUGGING 1 /* 1 or 0 -- do you want debugging code built in? */
#endif
/*
* choose one of these MAILCMD commands. I use
* /bin/mail for speed; it makes biff bark but doesn't
* do aliasing. /usr/lib/sendmail does 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 -or0s %s" /*-*/
/* -Fx = set full-name of sender
* -odi = Option Deliverymode Interactive
* -oem = Option Errors Mailedtosender
* -or0s = Option Readtimeout -- don't time out
*/
/* #define MAILCMD "/bin/mail" /*-*/
/* #define MAILARGS "%s -d %s" /*-*/
/* -d = undocumented but common flag: deliver locally?
*/
/* #define MAILCMD "/usr/mmdf/bin/submit" /*-*/
/* #define MAILARGS "%s -mlrxto %s" /*-*/
/* #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
* 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
* info is written, and that you probably don't want
* to use this on 4.2bsd since everything goes in
* /usr/spool/mqueue/syslog. On 4.[34]bsd you can
* tell /etc/syslog.conf to send cron's logging to
* a separate file.
*
* Note that if this and LOG_FILE in "pathnames.h"
* are both defined, then logging will go to both
* places.
*/
#define SYSLOG /*-*/

61
usr.sbin/cron/cron/cron.8 Normal file
View File

@ -0,0 +1,61 @@
.\"/* Copyright 1988,1990,1993 by Paul Vixie
.\" * All rights reserved
.\" *
.\" * Distribute freely, except: don't remove my name from the source or
.\" * documentation (don't take credit for my work), mark your changes (don't
.\" * get me blamed for your possible bugs), don't alter or remove this
.\" * notice. May be sold if buildable source is provided to buyer. No
.\" * warrantee of any kind, express or implied, is included with this
.\" * software; use at your own risk, responsibility for damages (if any) to
.\" * anyone resulting from the use of this software rests entirely with the
.\" * user.
.\" *
.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
.\" * I'll try to keep a version up to date. I can be reached as follows:
.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
.\" */
.\"
.\" $Id: cron.8,v 2.2 1993/12/28 08:34:43 vixie Exp $
.\"
.TH CRON 8 "20 December 1993"
.UC 4
.SH NAME
cron \- daemon to execute scheduled commands (Vixie Cron)
.SH SYNOPSIS
cron
.SH DESCRIPTION
.I Cron
should be started from /etc/rc or /etc/rc.local. It will return immediately,
so you don't need to start it with '&'.
.PP
.I Cron
searches /var/cron/tabs for crontab files which are named after accounts in
/etc/passwd; crontabs found are loaded into memory.
.I Cron
also searches for /etc/crontab which is in a different format (see
.IR crontab(5)).
.I Cron
then wakes up every minute, examining all stored crontabs, checking each
command to see if it should be run in the current minute. When executing
commands, any output is mailed to the owner of the crontab (or to the user
named in the MAILTO environment variable in the crontab, if such exists).
.PP
Additionally,
.I cron
checks each minute to see if its spool directory's modtime (or the modtime
on
.IR /etc/crontab)
has changed, and if it has,
.I cron
will then examine the modtime on all crontabs and reload those which have
changed. Thus
.I cron
need not be restarted whenever a crontab file is modified. Note that the
.IR Crontab (1)
command updates the modtime of the spool directory whenever it changes a
crontab.
.SH "SEE ALSO"
crontab(1), crontab(5)
.SH AUTHOR
.nf
Paul Vixie <paul@vix.com>

301
usr.sbin/cron/cron/cron.c Normal file
View File

@ -0,0 +1,301 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
#if !defined(lint) && !defined(LINT)
static char rcsid[] = "$Id: cron.c,v 2.11 1994/01/15 20:43:43 vixie Exp $";
#endif
#define MAIN_PROGRAM
#include "cron.h"
#include <sys/signal.h>
#if SYS_TIME_H
# include <sys/time.h>
#else
# include <time.h>
#endif
static void usage __P((void)),
run_reboot_jobs __P((cron_db *)),
cron_tick __P((cron_db *)),
cron_sync __P((void)),
cron_sleep __P((void)),
#ifdef USE_SIGCHLD
sigchld_handler __P((int)),
#endif
sighup_handler __P((int)),
parse_args __P((int c, char *v[]));
static void
usage() {
fprintf(stderr, "usage: %s [-x debugflag[,...]]\n", ProgramName);
exit(ERROR_EXIT);
}
int
main(argc, argv)
int argc;
char *argv[];
{
cron_db database;
ProgramName = argv[0];
#if defined(BSD)
setlinebuf(stdout);
setlinebuf(stderr);
#endif
parse_args(argc, argv);
#ifdef USE_SIGCHLD
(void) signal(SIGCHLD, sigchld_handler);
#else
(void) signal(SIGCLD, SIG_IGN);
#endif
(void) signal(SIGHUP, sighup_handler);
acquire_daemonlock(0);
set_cron_uid();
set_cron_cwd();
#if defined(POSIX)
setenv("PATH", _PATH_DEFPATH, 1);
#endif
/* 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 {
switch (fork()) {
case -1:
log_it("CRON",getpid(),"DEATH","can't fork");
exit(0);
break;
case 0:
/* child process */
log_it("CRON",getpid(),"STARTUP","fork ok");
(void) setsid();
break;
default:
/* parent process should just die */
_exit(0);
}
}
acquire_daemonlock(0);
database.head = NULL;
database.tail = NULL;
database.mtime = (time_t) 0;
load_database(&database);
run_reboot_jobs(&database);
cron_sync();
while (TRUE) {
# if DEBUGGING
if (!(DebugFlags & DTEST))
# endif /*DEBUGGING*/
cron_sleep();
load_database(&database);
/* do this iteration
*/
cron_tick(&database);
/* sleep 1 minute
*/
TargetTime += 60;
}
}
static void
run_reboot_jobs(db)
cron_db *db;
{
register user *u;
register 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) {
job_add(e, u);
}
}
}
(void) job_runqueue();
}
static void
cron_tick(db)
cron_db *db;
{
register struct tm *tm = localtime(&TargetTime);
register int minute, hour, dom, month, dow;
register user *u;
register entry *e;
/* make 0-based values out of these so we can use them as indicies
*/
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)\n",
getpid(), minute, hour, dom, month, dow))
/* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
* first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
* on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
* is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre.
* like many bizarre things, it's the standard.
*/
for (u = db->head; u != NULL; u = u->next) {
for (e = u->crontab; e != NULL; e = e->next) {
Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n",
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))
)
) {
job_add(e, u);
}
}
}
}
/* the task here is to figure out how long it's going to be until :00 of the
* following minute and initialize TargetTime to this value. TargetTime
* will subsequently slide 60 seconds at a time, with correction applied
* implicitly in cron_sleep(). it would be nice to let cron execute in
* the "current minute" before going to sleep, but by restarting cron you
* could then get it to execute a given minute's jobs more than once.
* instead we have the chance of missing a minute's jobs completely, but
* that's something sysadmin's know to expect what with crashing computers..
*/
static void
cron_sync() {
register struct tm *tm;
TargetTime = time((time_t*)0);
tm = localtime(&TargetTime);
TargetTime += (60 - tm->tm_sec);
}
static void
cron_sleep() {
register int seconds_to_wait;
do {
seconds_to_wait = (int) (TargetTime - time((time_t*)0));
Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n",
getpid(), TargetTime, seconds_to_wait))
/* if we intend to sleep, this means that it's finally
* time to empty the job queue (execute it).
*
* if we run any jobs, we'll probably screw up our timing,
* so go recompute.
*
* note that we depend here on the left-to-right nature
* of &&, and the short-circuiting.
*/
} while (seconds_to_wait > 0 && job_runqueue());
while (seconds_to_wait > 0) {
Debug(DSCH, ("[%d] sleeping for %d seconds\n",
getpid(), seconds_to_wait))
seconds_to_wait = (int) sleep((unsigned int) seconds_to_wait);
}
}
#ifdef USE_SIGCHLD
static void
sigchld_handler(x) {
WAIT_T waiter;
PID_T pid;
for (;;) {
#ifdef POSIX
pid = waitpid(-1, &waiter, WNOHANG);
#else
pid = wait3(&waiter, WNOHANG, (struct rusage *)0);
#endif
switch (pid) {
case -1:
Debug(DPROC,
("[%d] sigchld...no children\n", getpid()))
return;
case 0:
Debug(DPROC,
("[%d] sigchld...no dead kids\n", getpid()))
return;
default:
Debug(DPROC,
("[%d] sigchld...pid #%d died, stat=%d\n",
getpid(), pid, WEXITSTATUS(waiter)))
}
}
}
#endif /*USE_SIGCHLD*/
static void
sighup_handler(x) {
log_close();
}
static void
parse_args(argc, argv)
int argc;
char *argv[];
{
int argch;
while (EOF != (argch = getopt(argc, argv, "x:"))) {
switch (argch) {
default:
usage();
case 'x':
if (!set_debug_flags(optarg))
usage();
break;
}
}
}

277
usr.sbin/cron/cron/cron.h Normal file
View File

@ -0,0 +1,277 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
/* cron.h - header for vixie's cron
*
* $Id: cron.h,v 2.10 1994/01/15 20:43:43 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 <sys/types.h>
#include <sys/param.h>
#include "compat.h"
#include <stdio.h>
#include <ctype.h>
#include <bitstring.h>
#include <pwd.h>
#include <sys/wait.h>
#include "pathnames.h"
#include "config.h"
#include "externs.h"
/* these are really immutable, and are
* defined for symbolic convenience only
* TRUE, FALSE, and ERR must be distinct
* ERR must be < OK.
*/
#define TRUE 1
#define FALSE 0
/* system calls return this on success */
#define OK 0
/* or this on error */
#define ERR (-1)
/* turn this on to get '-x' code */
#ifndef DEBUGGING
#define DEBUGGING FALSE
#endif
#define READ_PIPE 0 /* which end of a pipe pair do you read? */
#define WRITE_PIPE 1 /* or write to? */
#define STDIN 0 /* what is stdin's file descriptor? */
#define STDOUT 1 /* stdout's? */
#define STDERR 2 /* stderr's? */
#define ERROR_EXIT 1 /* exit() with this will scare the shell */
#define OK_EXIT 0 /* exit() with this is considered 'normal' */
#define MAX_FNAME 100 /* max length of internally generated fn */
#define MAX_COMMAND 1000 /* max length of internally generated cmd */
#define MAX_ENVSTR 1000 /* max length of envvar=value\0 strings */
#define MAX_TEMPSTR 100 /* obvious */
#define 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 */
/* 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 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);
#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_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)
/* 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;
char **envp;
char *cmd;
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
} 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 __P((void)),
set_cron_cwd __P((void)),
load_database __P((cron_db *)),
open_logfile __P((void)),
sigpipe_func __P((void)),
job_add __P((entry *, user *)),
do_command __P((entry *, user *)),
link_user __P((cron_db *, user *)),
unlink_user __P((cron_db *, user *)),
free_user __P((user *)),
env_free __P((char **)),
unget_char __P((int, FILE *)),
free_entry __P((entry *)),
acquire_daemonlock __P((int)),
skip_comments __P((FILE *)),
log_it __P((char *, int, char *, char *)),
log_close __P((void));
int job_runqueue __P((void)),
set_debug_flags __P((char *)),
get_char __P((FILE *)),
get_string __P((char *, int, FILE *, char *)),
swap_uids __P((void)),
load_env __P((char *, FILE *)),
cron_pclose __P((FILE *)),
strcmp_until __P((char *, char *, int)),
allowed __P((char *)),
strdtb __P((char *));
char *env_get __P((char *, char **)),
*arpadate __P((time_t *)),
*mkprints __P((unsigned char *, unsigned int)),
*first_word __P((char *, char *)),
**env_init __P((void)),
**env_copy __P((char **)),
**env_set __P((char **, char *));
user *load_user __P((int, struct passwd *, char *)),
*find_user __P((cron_db *, char *));
entry *load_entry __P((FILE *, void (*)(),
struct passwd *, char **));
FILE *cron_popen __P((char *, char *));
/* 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;
int LineNumber;
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;
extern int LineNumber;
extern time_t TargetTime;
# if DEBUGGING
extern int DebugFlags;
extern char *DebugFlagNames[];
# endif /* DEBUGGING */
#endif /*MAIN_PROGRAM*/

View File

@ -0,0 +1,261 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
#if !defined(lint) && !defined(LINT)
static char rcsid[] = "$Id: database.c,v 2.8 1994/01/15 20:43:43 vixie Exp $";
#endif
/* vix 26jan87 [RCS has the log]
*/
#include "cron.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/file.h>
#define TMAX(a,b) ((a)>(b)?(a):(b))
static void process_crontab __P((char *, char *, 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;
Debug(DLOAD, ("[%d] load_database()\n", getpid()))
/* before we start loading any data, do a stat on SPOOL_DIR
* so that if anything changes as of this moment (i.e., before we've
* cached any of the database), we'll see the changes next time.
*/
if (stat(SPOOL_DIR, &statbuf) < OK) {
log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR);
(void) exit(ERROR_EXIT);
}
/* track system crontab file
*/
if (stat(SYSCRONTAB, &syscron_stat) < OK)
syscron_stat.st_mtime = 0;
/* if spooldir's mtime has not changed, we don't need to fiddle with
* the database.
*
* Note that old_db->mtime is initialized to 0 in main(), and
* so is guaranteed to be different than the stat() mtime the first
* time this function is called.
*/
if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime)) {
Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n",
getpid()))
return;
}
/* something's different. make a new database, moving unchanged
* elements from the old database, reloading elements that have
* actually changed. Whatever is left in the old database when
* we're done is chaff -- crontabs that disappeared.
*/
new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime);
new_db.head = new_db.tail = NULL;
if (syscron_stat.st_mtime) {
process_crontab("root", "*system*",
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
* we fork a lot more often than the mtime of the dir changes.
*/
if (!(dir = opendir(SPOOL_DIR))) {
log_it("CRON", getpid(), "OPENDIR FAILED", SPOOL_DIR);
(void) exit(ERROR_EXIT);
}
while (NULL != (dp = readdir(dir))) {
char fname[MAXNAMLEN+1],
tabname[MAXNAMLEN+1];
/* avoid file names beginning with ".". this is good
* because we would otherwise waste two guaranteed calls
* to getpwnam() for . and .., and also because user names
* starting with a period are just too nasty to consider.
*/
if (dp->d_name[0] == '.')
continue;
(void) strcpy(fname, dp->d_name);
sprintf(tabname, CRON_TAB(fname));
process_crontab(fname, fname, tabname,
&statbuf, &new_db, old_db);
}
closedir(dir);
/* if we don't do this, then when our children eventually call
* getpwnam() in do_command.c's child_process to verify MAILTO=,
* they will screw us up (and v-v).
*/
endpwent();
/* whatever's left in the old database is now junk.
*/
Debug(DLOAD, ("unlinking old database:\n"))
for (u = old_db->head; u != NULL; u = nu) {
Debug(DLOAD, ("\t%s\n", u->name))
nu = u->next;
unlink_user(old_db, u);
free_user(u);
}
/* overwrite the database control block with the new one.
*/
*old_db = new_db;
Debug(DLOAD, ("load_database is done\n"))
}
void
link_user(db, u)
cron_db *db;
user *u;
{
if (db->head == NULL)
db->head = u;
if (db->tail)
db->tail->next = u;
u->prev = db->tail;
u->next = NULL;
db->tail = u;
}
void
unlink_user(db, u)
cron_db *db;
user *u;
{
if (u->prev == NULL)
db->head = u->next;
else
u->prev->next = u->next;
if (u->next == NULL)
db->tail = u->prev;
else
u->next->prev = u->prev;
}
user *
find_user(db, name)
cron_db *db;
char *name;
{
char *env_get();
user *u;
for (u = db->head; u != NULL; u = u->next)
if (!strcmp(u->name, name))
break;
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;
{
struct passwd *pw = NULL;
int crontab_fd = OK - 1;
user *u;
if (strcmp(fname, "*system*") && !(pw = getpwnam(uname))) {
/* file doesn't have a user in passwd file.
*/
log_it(fname, getpid(), "ORPHAN", "no passwd entry");
goto next_crontab;
}
if ((crontab_fd = open(tabname, O_RDONLY, 0)) < OK) {
/* crontab not accessible?
*/
log_it(fname, getpid(), "CAN'T OPEN", tabname);
goto next_crontab;
}
if (fstat(crontab_fd, statbuf) < OK) {
log_it(fname, getpid(), "FSTAT FAILED", tabname);
goto next_crontab;
}
Debug(DLOAD, ("\t%s:", fname))
u = find_user(old_db, fname);
if (u != NULL) {
/* if crontab has not changed since we last read it
* in, then we can just use our existing entry.
*/
if (u->mtime == statbuf->st_mtime) {
Debug(DLOAD, (" [no change, using old data]"))
unlink_user(old_db, u);
link_user(new_db, u);
goto next_crontab;
}
/* before we fall through to the code that will reload
* the user, let's deallocate and unlink the user in
* the old database. This is more a point of memory
* efficiency than anything else, since all leftover
* users will be deleted from the old database when
* we finish with the crontab...
*/
Debug(DLOAD, (" [delete old data]"))
unlink_user(old_db, u);
free_user(u);
log_it(fname, getpid(), "RELOAD", tabname);
}
u = load_user(crontab_fd, pw, fname);
if (u != NULL) {
u->mtime = statbuf->st_mtime;
link_user(new_db, u);
}
next_crontab:
if (crontab_fd >= OK) {
Debug(DLOAD, (" [done]\n"))
close(crontab_fd);
}
}

View File

@ -0,0 +1,501 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
#if !defined(lint) && !defined(LINT)
static char rcsid[] = "$Id: do_command.c,v 2.12 1994/01/15 20:43:43 vixie Exp $";
#endif
#include "cron.h"
#include <sys/signal.h>
#if defined(sequent)
# include <sys/universe.h>
#endif
#if defined(SYSLOG)
# include <syslog.h>
#endif
static void child_process __P((entry *, user *)),
do_univ __P((user *));
void
do_command(e, u)
entry *e;
user *u;
{
Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n",
getpid(), e->cmd, u->name, e->uid, e->gid))
/* fork to become asynchronous -- parent process is done immediately,
* and continues to run the normal cron code, which means return to
* tick(). the child and grandchild don't leave this function, alive.
*
* vfork() is unsuitable, since we have much to do, and the parent
* needs to be able to run off and fork other processes.
*/
switch (fork()) {
case -1:
log_it("CRON",getpid(),"error","can't fork");
break;
case 0:
/* child process */
acquire_daemonlock(1);
child_process(e, u);
Debug(DPROC, ("[%d] child process done, exiting\n", getpid()))
_exit(OK_EXIT);
break;
default:
/* parent process */
break;
}
Debug(DPROC, ("[%d] main process returning to work\n", 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;
Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd))
/* mark ourselves as different to PS command watchers by upshifting
* our program name. This has no effect on some kernels.
*/
/*local*/{
register char *pch;
for (pch = ProgramName; *pch; pch++)
*pch = MkUpper(*pch);
}
/* discover some useful and important environment settings
*/
usernm = env_get("LOGNAME", e->envp);
mailto = env_get("MAILTO", e->envp);
#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() explictly. so we have to disable the signal (which
* was inherited from the parent).
*/
(void) signal(SIGCHLD, SIG_IGN);
#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,
* but that happens later.
*/
/*local*/{
register int escaped = FALSE;
register int ch;
for (input_data = e->cmd; ch = *input_data; input_data++) {
if (escaped) {
escaped = FALSE;
continue;
}
if (ch == '\\') {
escaped = TRUE;
continue;
}
if (ch == '%') {
*input_data++ = '\0';
break;
}
}
}
/* fork again, this time so we can exec the user's command.
*/
switch (vfork()) {
case -1:
log_it("CRON",getpid(),"error","can't vfork");
exit(ERROR_EXIT);
/*NOTREACHED*/
case 0:
Debug(DPROC, ("[%d] grandchild process Vfork()'ed\n",
getpid()))
/* 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*/{
char *x = mkprints((u_char *)e->cmd, strlen(e->cmd));
log_it(usernm, getpid(), "CMD", x);
free(x);
}
/* that's the last thing we'll log. close the log files.
*/
#ifdef SYSLOG
closelog();
#endif
/* get new pgrp, void tty, etc.
*/
(void) setsid();
/* close the pipe ends that we won't use. this doesn't affect
* the parent, who has to read and write them; it keeps the
* kernel from recording us as a potential client TWICE --
* which would keep it from sending SIGPIPE in otherwise
* appropriate circumstances.
*/
close(stdin_pipe[WRITE_PIPE]);
close(stdout_pipe[READ_PIPE]);
/* grandchild process. make std{in,out} be the ends of
* pipes opened by our daddy; make stderr go to stdout.
*/
close(STDIN); dup2(stdin_pipe[READ_PIPE], STDIN);
close(STDOUT); dup2(stdout_pipe[WRITE_PIPE], STDOUT);
close(STDERR); dup2(STDOUT, STDERR);
/* close the pipes we just dup'ed. The resources will remain.
*/
close(stdin_pipe[READ_PIPE]);
close(stdout_pipe[WRITE_PIPE]);
/* set our login universe. Do this in the grandchild
* so that the child can invoke /usr/lib/sendmail
* without surprises.
*/
do_univ(u);
/* set our directory, uid and gid. Set gid first, since once
* we set uid, we've lost root privledges.
*/
setgid(e->gid);
# if defined(BSD)
initgroups(env_get("LOGNAME", e->envp), e->gid);
# endif
setuid(e->uid); /* we aren't root after this... */
chdir(env_get("HOME", e->envp));
/* exec the command.
*/
{
char *shell = env_get("SHELL", e->envp);
# if DEBUGGING
if (DebugFlags & DTEST) {
fprintf(stderr,
"debug DTEST is on, not exec'ing command.\n");
fprintf(stderr,
"\tcmd='%s' shell='%s'\n", e->cmd, shell);
_exit(OK_EXIT);
}
# endif /*DEBUGGING*/
execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);
fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
perror("execl");
_exit(ERROR_EXIT);
}
break;
default:
/* parent process */
break;
}
children++;
/* middle process, child of original cron, parent of process running
* the user's command.
*/
Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid()))
/* close the ends of the pipe that will only be referenced in the
* grandchild process...
*/
close(stdin_pipe[READ_PIPE]);
close(stdout_pipe[WRITE_PIPE]);
/*
* write, to the pipe connected to child's stdin, any input specified
* after a % in the crontab entry. while we copy, convert any
* additional %'s to newlines. when done, if some characters were
* written and the last one wasn't a newline, write a newline.
*
* Note that if the input data won't fit into one pipe buffer (2K
* or 4K on most BSD systems), and the child doesn't read its stdin,
* we would block here. thus we must fork again.
*/
if (*input_data && fork() == 0) {
register FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
register int need_newline = FALSE;
register int escaped = FALSE;
register int ch;
Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid()))
/* close the pipe we don't use, since we inherited it and
* are part of its reference count now.
*/
close(stdout_pipe[READ_PIPE]);
/* translation:
* \% -> %
* % -> \n
* \x -> \x for all x != %
*/
while (ch = *input_data++) {
if (escaped) {
if (ch != '%')
putc('\\', out);
} else {
if (ch == '%')
ch = '\n';
}
if (!(escaped = (ch == '\\'))) {
putc(ch, out);
need_newline = (ch != '\n');
}
}
if (escaped)
putc('\\', out);
if (need_newline)
putc('\n', out);
/* close the pipe, causing an EOF condition. fclose causes
* stdin_pipe[WRITE_PIPE] to be closed, too.
*/
fclose(out);
Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid()))
exit(0);
}
/* close the pipe to the grandkiddie's stdin, since its wicked uncle
* ernie back there has it open and will close it when he's done.
*/
close(stdin_pipe[WRITE_PIPE]);
children++;
/*
* read output from the grandchild. it's stderr has been redirected to
* it's stdout, which has been redirected to our pipe. if there is any
* output, we'll be mailing it to the user whose crontab this is...
* when the grandchild exits, we'll get EOF.
*/
Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid()))
/*local*/{
register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r");
register int ch = getc(in);
if (ch != EOF) {
register FILE *mail;
register int bytes = 1;
int status = 0;
Debug(DPROC|DEXT,
("[%d] got data (%x:%c) from grandchild\n",
getpid(), ch, ch))
/* get name of recipient. this is MAILTO if set to a
* valid local username; USER otherwise.
*/
if (mailto) {
/* MAILTO was present in the environment
*/
if (!*mailto) {
/* ... but it's empty. set to NULL
*/
mailto = NULL;
}
} else {
/* MAILTO not present, set to USER.
*/
mailto = usernm;
}
/* 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) sprintf(mailcmd, MAILARGS,
MAILCMD, mailto);
if (!(mail = cron_popen(mailcmd, "w"))) {
perror(MAILCMD);
(void) _exit(ERROR_EXIT);
}
fprintf(mail, "From: root (Cron Daemon)\n");
fprintf(mail, "To: %s\n", mailto);
fprintf(mail, "Subject: Cron <%s@%s> %s\n",
usernm, first_word(hostname, "."),
e->cmd);
# if defined(MAIL_DATE)
fprintf(mail, "Date: %s\n",
arpadate(&TargetTime));
# endif /* MAIL_DATE */
for (env = e->envp; *env; env++)
fprintf(mail, "X-Cron-Env: <%s>\n",
*env);
fprintf(mail, "\n");
/* this was the first char from the pipe
*/
putc(ch, mail);
}
/* we have to read the input pipe no matter whether
* we mail or not, but obviously we only write to
* mail pipe if we ARE mailing.
*/
while (EOF != (ch = getc(in))) {
bytes++;
if (mailto)
putc(ch, mail);
}
/* only close pipe if we opened it -- i.e., we're
* mailing...
*/
if (mailto) {
Debug(DPROC, ("[%d] closing pipe to mail\n",
getpid()))
/* Note: the pclose will probably see
* the termination of the grandchild
* in addition to the mail process, since
* it (the grandchild) is likely to exit
* after closing its stdout.
*/
status = cron_pclose(mail);
}
/* if there was output and we could not mail it,
* log the facts so the poor user can figure out
* what's going on.
*/
if (mailto && status) {
char buf[MAX_TEMPSTR];
sprintf(buf,
"mailed %d byte%s of output but got status 0x%04x\n",
bytes, (bytes==1)?"":"s",
status);
log_it(usernm, getpid(), "MAIL", buf);
}
} /*if data from grandchild*/
Debug(DPROC, ("[%d] got EOF from grandchild\n", 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);
if (pid < OK) {
Debug(DPROC, ("[%d] no more grandchildren--mail written?\n",
getpid()))
break;
}
Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x",
getpid(), 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++;
}
if (strcmp(s, "universe(att)"))
return;
(void) universe(U_ATT);
#endif
}

View File

@ -0,0 +1,145 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
#if defined(POSIX) || defined(ATT)
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <dirent.h>
# define DIR_T struct dirent
# define WAIT_T int
# define WAIT_IS_INT 1
extern char *tzname[2];
# define TZONE(tm) tzname[(tm).tm_isdst]
#endif
#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 <sys/dir.h>
# define TZONE(tm) (tm).tm_zone
#endif
/* getopt() isn't part of POSIX. some systems define it in <stdlib.h> 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)
int getopt __P((int, char * const *, const char *));
#endif
#if (!defined(BSD) || (BSD < 199103))
extern char *optarg;
extern int optind, opterr, optopt;
#endif
#if WAIT_IS_INT
# ifndef WEXITSTATUS
# define WEXITSTATUS(x) (((x) >> 8) & 0xff)
# endif
# ifndef WTERMSIG
# define WTERMSIG(x) ((x) & 0x7f)
# endif
# ifndef WCOREDUMP
# define WCOREDUMP(x) ((x) & 0x80)
# endif
#else /*WAIT_IS_INT*/
# ifndef WEXITSTATUS
# define WEXITSTATUS(x) ((x).w_retcode)
# endif
# ifndef WTERMSIG
# define WTERMSIG(x) ((x).w_termsig)
# endif
# ifndef WCOREDUMP
# define WCOREDUMP(x) ((x).w_coredump)
# endif
#endif /*WAIT_IS_INT*/
#ifndef WIFSIGNALED
#define WIFSIGNALED(x) (WTERMSIG(x) != 0)
#endif
#ifndef WIFEXITED
#define WIFEXITED(x) (WTERMSIG(x) == 0)
#endif
#ifdef NEED_STRCASECMP
extern int strcasecmp __P((char *, char *));
#endif
#ifdef NEED_STRDUP
extern char *strdup __P((char *));
#endif
#ifdef NEED_STRERROR
extern char *strerror __P((int));
#endif
#ifdef NEED_FLOCK
extern int flock __P((int, int));
# define LOCK_SH 1
# define LOCK_EX 2
# define LOCK_NB 4
# define LOCK_UN 8
#endif
#ifdef NEED_SETSID
extern int setsid __P((void));
#endif
#ifdef NEED_GETDTABLESIZE
extern int getdtablesize __P((void));
#endif
#ifdef NEED_SETENV
extern int setenv __P((char *, char *, int));
#endif
#ifdef NEED_VFORK
extern PID_T vfork __P((void));
#endif

74
usr.sbin/cron/cron/job.c Normal file
View File

@ -0,0 +1,74 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
#if !defined(lint) && !defined(LINT)
static char rcsid[] = "$Id: job.c,v 1.6 1994/01/15 20:43: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;
/* if already on queue, keep going */
for (j=jhead; j; j=j->next)
if (j->e == e && j->u == u) { return; }
/* build a job queue element */
j = (job*)malloc(sizeof(job));
j->next = (job*) NULL;
j->e = e;
j->u = u;
/* add it to the tail */
if (!jhead) { jhead=j; }
else { jtail->next=j; }
jtail = j;
}
int
job_runqueue()
{
register job *j, *jn;
register int run = 0;
for (j=jhead; j; j=jn) {
do_command(j->e, j->u);
jn = j->next;
free(j);
run++;
}
jhead = jtail = NULL;
return run;
}

View File

@ -0,0 +1,81 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
/*
* $Id: pathnames.h,v 1.3 1994/01/15 20:43:43 vixie Exp $
*/
#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
* to; SPOOL_DIR, ALLOW_FILE, DENY_FILE, and LOG_FILE
* are all relative to this directory.
*/
#define CRONDIR "/var/cron"
#endif
/* SPOOLDIR is where the crontabs live.
* This directory will have its modtime updated
* whenever crontab(1) changes a crontab; this is
* the signal for crond(8) to look at each individual
* crontab file and reload those whose modtimes are
* newer than they were last time around (or which
* didn't exist last time around...)
*/
#define SPOOL_DIR "tabs"
/* undefining these turns off their features. note
* that ALLOW_FILE and DENY_FILE must both be defined
* in order to enable the allow/deny code. If neither
* LOG_FILE or SYSLOG is defined, we don't log. If
* both are defined, we log both ways.
*/
#define ALLOW_FILE "allow" /*-*/
#define DENY_FILE "deny" /*-*/
#define LOG_FILE "log" /*-*/
/* where should the daemon stick its PID?
*/
#ifdef _PATH_VARRUN
# define PIDDIR _PATH_VARRUN
#else
# define PIDDIR "/etc/"
#endif
#define PIDFILE "%scron.pid"
/* 4.3BSD-style crontab */
#define SYSCRONTAB "/etc/crontab"
/* what editor to use if no EDITOR or VISUAL
* environment variable specified.
*/
#if defined(_PATH_VI)
# define EDITOR _PATH_VI
#else
# define EDITOR "/usr/ucb/vi"
#endif
#ifndef _PATH_BSHELL
# define _PATH_BSHELL "/bin/sh"
#endif
#ifndef _PATH_DEFPATH
# define _PATH_DEFPATH "/usr/bin:/bin"
#endif

166
usr.sbin/cron/cron/popen.c Normal file
View File

@ -0,0 +1,166 @@
/*
* Copyright (c) 1988 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software written by Ken Arnold and
* published in UNIX Review, Vol. 6, No. 8.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
/* this came out of the ftpd sources; it's been modified to avoid the
* globbing stuff since we don't need it. also execvp instead of execv.
*/
#ifndef lint
static char rcsid[] = "$Id: popen.c,v 1.5 1994/01/15 20:43:43 vixie Exp $";
static char sccsid[] = "@(#)popen.c 5.7 (Berkeley) 2/14/89";
#endif /* not lint */
#include "cron.h"
#include <sys/signal.h>
#define WANT_GLOBBING 0
/*
* Special version of popen which avoids call to shell. This insures noone
* may create a pipe to a hidden program as a side effect of a list or dir
* command.
*/
static PID_T *pids;
static int fds;
FILE *
cron_popen(program, type)
char *program, *type;
{
register char *cp;
FILE *iop;
int argc, pdes[2];
PID_T pid;
char *argv[100];
#if WANT_GLOBBING
char **pop, *vv[2];
int gargc;
char *gargv[1000];
extern char **glob(), **copyblk();
#endif
if (*type != 'r' && *type != 'w' || type[1])
return(NULL);
if (!pids) {
if ((fds = getdtablesize()) <= 0)
return(NULL);
if (!(pids = (PID_T *)malloc((u_int)(fds * sizeof(PID_T)))))
return(NULL);
bzero((char *)pids, fds * sizeof(PID_T));
}
if (pipe(pdes) < 0)
return(NULL);
/* break up string into pieces */
for (argc = 0, cp = program;; cp = NULL)
if (!(argv[argc++] = strtok(cp, " \t\n")))
break;
#if WANT_GLOBBING
/* glob each piece */
gargv[0] = argv[0];
for (gargc = argc = 1; argv[argc]; argc++) {
if (!(pop = glob(argv[argc]))) { /* globbing failed */
vv[0] = argv[argc];
vv[1] = NULL;
pop = copyblk(vv);
}
argv[argc] = (char *)pop; /* save to free later */
while (*pop && gargc < 1000)
gargv[gargc++] = *pop++;
}
gargv[gargc] = NULL;
#endif
iop = NULL;
switch(pid = vfork()) {
case -1: /* error */
(void)close(pdes[0]);
(void)close(pdes[1]);
goto pfree;
/* NOTREACHED */
case 0: /* child */
if (*type == 'r') {
if (pdes[1] != 1) {
dup2(pdes[1], 1);
dup2(pdes[1], 2); /* stderr, too! */
(void)close(pdes[1]);
}
(void)close(pdes[0]);
} else {
if (pdes[0] != 0) {
dup2(pdes[0], 0);
(void)close(pdes[0]);
}
(void)close(pdes[1]);
}
#if WANT_GLOBBING
execvp(gargv[0], gargv);
#else
execvp(argv[0], argv);
#endif
_exit(1);
}
/* parent; assume fdopen can't fail... */
if (*type == 'r') {
iop = fdopen(pdes[0], type);
(void)close(pdes[1]);
} else {
iop = fdopen(pdes[1], type);
(void)close(pdes[0]);
}
pids[fileno(iop)] = pid;
pfree:
#if WANT_GLOBBING
for (argc = 1; argv[argc] != NULL; argc++) {
/* blkfree((char **)argv[argc]); */
free((char *)argv[argc]);
}
#endif
return(iop);
}
int
cron_pclose(iop)
FILE *iop;
{
register int fdes;
int omask;
WAIT_T stat_loc;
PID_T pid;
/*
* pclose returns -1 if stream is not associated with a
* `popened' command, or, if already `pclosed'.
*/
if (pids == 0 || pids[fdes = fileno(iop)] == 0)
return(-1);
(void)fclose(iop);
omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1)
;
(void)sigsetmask(omask);
pids[fdes] = 0;
return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
}

View File

@ -0,0 +1,23 @@
#!/bin/sh
# putman.sh - install a man page according to local custom
# vixie 27dec93 [original]
#
# $Id:$
PAGE=$1
DIR=$2
SECT=`expr $PAGE : '[a-z]*.\([0-9]\)'`
[ -d $DIR/man$SECT ] && {
set -x
cp $PAGE $DIR/man$SECT/$PAGE
set +x
} || {
set -x
nroff -man $PAGE >$DIR/cat$SECT/`basename $PAGE .$SECT`.0
set +x
}
exit 0

102
usr.sbin/cron/cron/user.c Normal file
View File

@ -0,0 +1,102 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
#if !defined(lint) && !defined(LINT)
static char rcsid[] = "$Id: user.c,v 2.8 1994/01/15 20:43:43 vixie Exp $";
#endif
/* vix 26jan87 [log is in RCS file]
*/
#include "cron.h"
void
free_user(u)
user *u;
{
entry *e, *ne;
free(u->name);
for (e = u->crontab; e != NULL; e = ne) {
ne = e->next;
free_entry(e);
}
free(u);
}
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;
if (!(file = fdopen(crontab_fd, "r"))) {
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.
*/
u = (user *) malloc(sizeof(user));
u->name = strdup(name);
u->crontab = NULL;
/*
* init environment. this will be copied/augmented for each entry.
*/
envp = env_init();
/*
* load the crontab
*/
while ((status = load_env(envstr, file)) >= OK) {
switch (status) {
case ERR:
free_user(u);
u = NULL;
goto done;
case FALSE:
e = load_entry(file, NULL, pw, envp);
if (e) {
e->next = u->crontab;
u->crontab = e;
}
break;
case TRUE:
envp = env_set(envp, envstr);
break;
}
}
done:
env_free(envp);
fclose(file);
Debug(DPARS, ("...load_user() done\n"))
return u;
}

View File

@ -0,0 +1,10 @@
BINDIR?= /usr/bin
PROG= crontab
SRCS= crontab.c
CFLAGS+= -I${.CURDIR}/../cron
LDADD+= -L${.CURDIR}/../lib -lcron
MAN1= crontab.1
MAN5= crontab.5
.include <bsd.prog.mk>

View File

@ -0,0 +1,100 @@
.\"/* Copyright 1988,1990,1993 by Paul Vixie
.\" * All rights reserved
.\" *
.\" * Distribute freely, except: don't remove my name from the source or
.\" * documentation (don't take credit for my work), mark your changes (don't
.\" * get me blamed for your possible bugs), don't alter or remove this
.\" * notice. May be sold if buildable source is provided to buyer. No
.\" * warrantee of any kind, express or implied, is included with this
.\" * software; use at your own risk, responsibility for damages (if any) to
.\" * anyone resulting from the use of this software rests entirely with the
.\" * user.
.\" *
.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
.\" * I'll try to keep a version up to date. I can be reached as follows:
.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
.\" */
.\"
.\" $Id: crontab.1,v 2.4 1993/12/31 10:47:33 vixie Exp $
.\"
.TH CRONTAB 1 "29 December 1993"
.UC 4
.SH NAME
crontab \- maintain crontab files for individual users (V3)
.SH SYNOPSIS
crontab [ -u user ] file
.br
crontab [ -u user ] { -l | -r | -e }
.SH DESCRIPTION
.I Crontab
is the program used to install, deinstall or list the tables
used to drive the
.IR cron (8)
daemon in Vixie Cron. Each user can have their own crontab, and though
these are files in /var, they are not intended to be edited directly.
.PP
If the
.I allow
file exists, then you must be listed therein in order to be allowed to use
this command. If the
.I allow
file does not exist but the
.I deny
file does exist, then you must \fBnot\fR be listed in the
.I deny
file in order to use this command. If neither of these files exists, then
depending on site-dependent configuration parameters, only the super user
will be allowed to use this command, or all users will be able to use this
command.
.PP
If the
.I -u
option is given, it specifies the name of the user whose crontab is to be
tweaked. If this option is not given,
.I crontab
examines "your" crontab, i.e., the crontab of the person executing the
command. Note that
.IR su (8)
can confuse
.I crontab
and that if you are running inside of
.IR su (8)
you should always use the
.I -u
option for safety's sake.
.PP
The first form of this command is used to install a new crontab from some
named file or standard input if the pseudo-filename ``-'' is given.
.PP
The
.I -l
option causes the current crontab to be displayed on standard output.
.PP
The
.I -r
option causes the current crontab to be removed.
.PP
The
.I -e
option is used to edit the current crontab using the editor specified by
the \s-1VISUAL\s+1 or \s-1EDITOR\s+1 environment variables. After you exit
from the editor, the modified crontab will be installed automatically.
.SH "SEE ALSO"
crontab(5), cron(8)
.SH FILES
.nf
/var/cron/allow
/var/cron/deny
.fi
.SH STANDARDS
The
.I crontab
command conforms to IEEE Std1003.2-1992 (``POSIX''). This new command syntax
differs from previous versions of Vixie Cron, as well as from the classic
SVR3 syntax.
.SH DIAGNOSTICS
A fairly informative usage message appears if you run it with a bad command
line.
.SH AUTHOR
.nf
Paul Vixie <paul@vix.com>

View File

@ -0,0 +1,188 @@
.\"/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
.\" */
.\"
.\" $Id: crontab.5,v 2.4 1994/01/15 20:43:43 vixie Exp $
.\"
.TH CRONTAB 5 "24 January 1994"
.UC 4
.SH NAME
crontab \- tables for driving cron
.SH DESCRIPTION
A
.I crontab
file contains instructions to the
.IR cron (8)
daemon of the general form: ``run this command at this time on this date''.
Each user has their own crontab, and commands in any given crontab will be
executed as the user who owns the crontab. Uucp and News will usually have
their own crontabs, eliminating the need for explicitly running
.IR su (1)
as part of a cron command.
.PP
Blank lines and leading spaces and tabs are ignored. Lines whose first
non-space character is a pound-sign (#) are comments, and are ignored.
Note that comments are not allowed on the same line as cron commands, since
they will be taken to be part of the command. Similarly, comments are not
allowed on the same line as environment variable settings.
.PP
An active line in a crontab will be either an environment setting or a cron
command. An environment setting is of the form,
.PP
name = value
.PP
where the spaces around the equal-sign (=) are optional, and any subsequent
non-leading spaces in
.I value
will be part of the value assigned to
.IR name .
The
.I value
string may be placed in quotes (single or double, but matching) to preserve
leading or trailing blanks.
.PP
Several environment variables are set up
automatically by the
.IR cron (8)
daemon.
SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd
line of the crontab's owner.
HOME and SHELL may be overridden by settings in the crontab; LOGNAME may not.
.PP
(Another note: the LOGNAME variable is sometimes called USER on BSD systems...
on these systems, USER will be set also.)
.PP
In addition to LOGNAME, HOME, and SHELL,
.IR cron (8)
will look at MAILTO if it has any reason to send mail as a result of running
commands in ``this'' crontab. If MAILTO is defined (and non-empty), mail is
sent to the user so named. If MAILTO is defined but empty (MAILTO=""), no
mail will be sent. Otherwise mail is sent to the owner of the crontab. This
option is useful if you decide on /bin/mail instead of /usr/lib/sendmail as
your mailer when you install cron -- /bin/mail doesn't do aliasing, and UUCP
usually doesn't read its mail.
.PP
The format of a cron command is very much the V7 standard, with a number of
upward-compatible extensions. Each line has five time and date fields,
followed by a user name if this is the system crontab file,
followed by a command. Commands are executed by
.IR cron (8)
when the minute, hour, and month of year fields match the current time,
.I and
when at least one of the two day fields (day of month, or day of week)
match the current time (see ``Note'' below).
.IR cron (8)
examines cron entries once every minute.
The time and date fields are:
.IP
.ta 1.5i
field allowed values
.br
----- --------------
.br
minute 0-59
.br
hour 0-23
.br
day of month 0-31
.br
month 0-12 (or names, see below)
.br
day of week 0-7 (0 or 7 is Sun, or use names)
.br
.PP
A field may be an asterisk (*), which always stands for ``first\-last''.
.PP
Ranges of numbers are allowed. Ranges are two numbers separated
with a hyphen. The specified range is inclusive. For example,
8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10
and 11.
.PP
Lists are allowed. A list is a set of numbers (or ranges)
separated by commas. Examples: ``1,2,5,9'', ``0-4,8-12''.
.PP
Step values can be used in conjunction with ranges. Following
a range with ``/<number>'' specifies skips of the number's value
through the range. For example, ``0-23/2'' can be used in the hours
field to specify command execution every other hour (the alternative
in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). Steps are
also permitted after an asterisk, so if you want to say ``every two
hours'', just use ``*/2''.
.PP
Names can also be used for the ``month'' and ``day of week''
fields. Use the first three letters of the particular
day or month (case doesn't matter). Ranges or
lists of names are not allowed.
.PP
The ``sixth'' field (the rest of the line) specifies the command to be
run.
The entire command portion of the line, up to a newline or %
character, will be executed by /bin/sh or by the shell
specified in the SHELL variable of the cronfile.
Percent-signs (%) in the command, unless escaped with backslash
(\\), will be changed into newline characters, and all data
after the first % will be sent to the command as standard
input.
.PP
Note: The day of a command's execution can be specified by two
fields \(em day of month, and day of week. If both fields are
restricted (ie, aren't *), the command will be run when
.I either
field matches the current time. For example,
.br
``30 4 1,15 * 5''
would cause a command to be run at 4:30 am on the 1st and 15th of each
month, plus every Friday.
.SH EXAMPLE CRON FILE
.nf
# use /bin/sh to run commands, no matter what /etc/passwd says
SHELL=/bin/sh
# mail any output to `paul', no matter whose crontab this is
MAILTO=paul
#
# run five minutes after midnight, every day
5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
# run at 2:15pm on the first of every month -- output mailed to paul
15 14 1 * * $HOME/bin/monthly
# run at 10 pm on weekdays, annoy Joe
0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?%
23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday"
5 4 * * sun echo "run at 5 after 4 every sunday"
.fi
.SH SEE ALSO
cron(8), crontab(1)
.SH EXTENSIONS
When specifying day of week, both day 0 and day 7 will be considered Sunday.
BSD and ATT seem to disagree about this.
.PP
Lists and ranges are allowed to co-exist in the same field. "1-3,7-9" would
be rejected by ATT or BSD cron -- they want to see "1-3" or "7,8,9" ONLY.
.PP
Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9".
.PP
Names of months or days of the week can be specified by name.
.PP
Environment variables can be set in the crontab. In BSD or ATT, the
environment handed to child processes is basically the one from /etc/rc.
.PP
Command output is mailed to the crontab owner (BSD can't do this), can be
mailed to a person other than the crontab owner (SysV can't do this), or the
feature can be turned off and no mail will be sent at all (SysV can't do this
either).
.SH AUTHOR
.nf
Paul Vixie <paul@vix.com>

View File

@ -0,0 +1,624 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
#if !defined(lint) && !defined(LINT)
static char rcsid[] = "$Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $";
#endif
/* crontab - install and manage per-user crontab files
* vix 02may87 [RCS has the rest of the log]
* vix 26jan87 [original]
*/
#define MAIN_PROGRAM
#include "cron.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#ifdef USE_UTIMES
# include <sys/time.h>
#else
# include <time.h>
# include <utime.h>
#endif
#if defined(POSIX)
# include <locale.h>
#endif
#define NHEADER_LINES 3
enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace };
#if DEBUGGING
static char *Options[] = { "???", "list", "delete", "edit", "replace" };
#endif
static PID_T Pid;
static char User[MAX_UNAME], RealUser[MAX_UNAME];
static char Filename[MAX_FNAME];
static FILE *NewCrontab;
static int CheckErrorCount;
static enum opt_t Option;
static struct passwd *pw;
static void list_cmd __P((void)),
delete_cmd __P((void)),
edit_cmd __P((void)),
poke_daemon __P((void)),
check_error __P((char *)),
parse_args __P((int c, char *v[]));
static int replace_cmd __P((void));
static void
usage(msg)
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(argc, argv)
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)) {
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;
}
exit(0);
/*NOTREACHED*/
}
static void
parse_args(argc, argv)
int argc;
char *argv[];
{
int argch;
if (!(pw = getpwuid(getuid()))) {
fprintf(stderr, "%s: your UID isn't in the passwd file.\n",
ProgramName);
fprintf(stderr, "bailing out.\n");
exit(ERROR_EXIT);
}
strcpy(User, pw->pw_name);
strcpy(RealUser, User);
Filename[0] = '\0';
Option = opt_unknown;
while (EOF != (argch = getopt(argc, argv, "u:lerx:"))) {
switch (argch) {
case 'x':
if (!set_debug_flags(optarg))
usage("bad debug option");
break;
case 'u':
if (getuid() != ROOT_UID)
{
fprintf(stderr,
"must be privileged to use -u\n");
exit(ERROR_EXIT);
}
if (!(pw = getpwnam(optarg)))
{
fprintf(stderr, "%s: user `%s' unknown\n",
ProgramName, optarg);
exit(ERROR_EXIT);
}
(void) strcpy(User, optarg);
break;
case 'l':
if (Option != opt_unknown)
usage("only one operation permitted");
Option = opt_list;
break;
case 'r':
if (Option != opt_unknown)
usage("only one operation permitted");
Option = opt_delete;
break;
case 'e':
if (Option != opt_unknown)
usage("only one operation permitted");
Option = opt_edit;
break;
default:
usage("unrecognized option");
}
}
endpwent();
if (Option != opt_unknown) {
if (argv[optind] != NULL) {
usage("no arguments permitted after this option");
}
} else {
if (argv[optind] != NULL) {
Option = opt_replace;
(void) strcpy (Filename, argv[optind]);
} else {
usage("file name must be specified for replace");
}
}
if (Option == opt_replace) {
/* we have to open the file here because we're going to
* chdir(2) into /var/cron before we get around to
* reading the file.
*/
if (!strcmp(Filename, "-")) {
NewCrontab = stdin;
} else {
/* 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 <agulbra@pvv.unit.no> for spotting
* the race.
*/
if (swap_uids() < OK) {
perror("swapping uids");
exit(ERROR_EXIT);
}
if (!(NewCrontab = fopen(Filename, "r"))) {
perror(Filename);
exit(ERROR_EXIT);
}
if (swap_uids() < OK) {
perror("swapping uids back");
exit(ERROR_EXIT);
}
}
}
Debug(DMISC, ("user=%s, file=%s, option=%s\n",
User, Filename, Options[(int)Option]))
}
static void
list_cmd() {
char n[MAX_FNAME];
FILE *f;
int ch;
log_it(RealUser, Pid, "LIST", User);
(void) sprintf(n, CRON_TAB(User));
if (!(f = fopen(n, "r"))) {
if (errno == ENOENT)
fprintf(stderr, "no crontab for %s\n", User);
else
perror(n);
exit(ERROR_EXIT);
}
/* file is open. copy to stdout, close.
*/
Set_LineNum(1)
while (EOF != (ch = get_char(f)))
putchar(ch);
fclose(f);
}
static void
delete_cmd() {
char n[MAX_FNAME];
log_it(RealUser, Pid, "DELETE", User);
(void) sprintf(n, CRON_TAB(User));
if (unlink(n)) {
if (errno == ENOENT)
fprintf(stderr, "no crontab for %s\n", User);
else
perror(n);
exit(ERROR_EXIT);
}
poke_daemon();
}
static void
check_error(msg)
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 ch, t, x;
struct stat statbuf;
time_t mtime;
WAIT_T waiter;
PID_T pid, xpid;
log_it(RealUser, Pid, "BEGIN EDIT", User);
(void) sprintf(n, CRON_TAB(User));
if (!(f = fopen(n, "r"))) {
if (errno != ENOENT) {
perror(n);
exit(ERROR_EXIT);
}
fprintf(stderr, "no crontab for %s - using an empty one\n",
User);
if (!(f = fopen("/dev/null", "r"))) {
perror("/dev/null");
exit(ERROR_EXIT);
}
}
(void) sprintf(Filename, "/tmp/crontab.%d", Pid);
if (-1 == (t = open(Filename, O_CREAT|O_EXCL|O_RDWR, 0600))) {
perror(Filename);
goto fatal;
}
#ifdef HAS_FCHOWN
if (fchown(t, getuid(), getgid()) < 0) {
#else
if (chown(Filename, getuid(), getgid()) < 0) {
#endif
perror("fchown");
goto fatal;
}
if (!(NewCrontab = fdopen(t, "r+"))) {
perror("fdopen");
goto fatal;
}
Set_LineNum(1)
/* ignore the top few comments since we probably put them there.
*/
for (x = 0; x < NHEADER_LINES; x++) {
ch = get_char(f);
if (EOF == ch)
break;
if ('#' != ch) {
putc(ch, NewCrontab);
break;
}
while (EOF != (ch = get_char(f)))
if (ch == '\n')
break;
if (EOF == ch)
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) < OK) {
perror(Filename);
exit(ERROR_EXIT);
}
again:
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 (fstat(t, &statbuf) < 0) {
perror("fstat");
goto fatal;
}
mtime = statbuf.st_mtime;
if ((!(editor = getenv("VISUAL")))
&& (!(editor = getenv("EDITOR")))
) {
editor = EDITOR;
}
/* we still have the file open. editors will generally rewrite the
* original file rather than renaming/unlinking it and starting a
* new one; even backup files are supposed to be made by copying
* rather than by renaming. if some editor does not support this,
* then don't use it. the security problems are more severe if we
* close and reopen the file around the edit.
*/
switch (pid = fork()) {
case -1:
perror("fork");
goto fatal;
case 0:
/* child */
if (setuid(getuid()) < 0) {
perror("setuid(getuid())");
exit(ERROR_EXIT);
}
if (chdir("/tmp") < 0) {
perror("chdir(/tmp)");
exit(ERROR_EXIT);
}
if (strlen(editor) + strlen(Filename) + 2 >= MAX_TEMPSTR) {
fprintf(stderr, "%s: editor or filename too long\n",
ProgramName);
exit(ERROR_EXIT);
}
sprintf(q, "%s %s", editor, Filename);
execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", q, NULL);
perror(editor);
exit(ERROR_EXIT);
/*NOTREACHED*/
default:
/* parent */
break;
}
/* parent */
xpid = wait(&waiter);
if (xpid != pid) {
fprintf(stderr, "%s: wrong PID (%d != %d) from \"%s\"\n",
ProgramName, xpid, pid, editor);
goto fatal;
}
if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) {
fprintf(stderr, "%s: \"%s\" exited with status %d\n",
ProgramName, editor, WEXITSTATUS(waiter));
goto fatal;
}
if (WIFSIGNALED(waiter)) {
fprintf(stderr,
"%s: \"%s\" killed; signal %d (%score dumped)\n",
ProgramName, editor, WTERMSIG(waiter),
WCOREDUMP(waiter) ?"" :"no ");
goto fatal;
}
if (fstat(t, &statbuf) < 0) {
perror("fstat");
goto fatal;
}
if (mtime == statbuf.st_mtime) {
fprintf(stderr, "%s: no changes made to crontab\n",
ProgramName);
goto remove;
}
fprintf(stderr, "%s: installing new crontab\n", ProgramName);
switch (replace_cmd()) {
case 0:
break;
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])) {
case 'y':
goto again;
case 'n':
goto abandon;
default:
fprintf(stderr, "Enter Y or N\n");
}
}
/*NOTREACHED*/
case -2:
abandon:
fprintf(stderr, "%s: edits left in %s\n",
ProgramName, Filename);
goto done;
default:
fprintf(stderr, "%s: panic: bad switch() in replace_cmd()\n");
goto fatal;
}
remove:
unlink(Filename);
done:
log_it(RealUser, Pid, "END EDIT", User);
}
/* returns 0 on success
* -1 on syntax error
* -2 on install error
*/
static int
replace_cmd() {
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();
(void) sprintf(n, "tmp.%d", Pid);
(void) sprintf(tn, CRON_TAB(n));
if (!(tmp = fopen(tn, "w+"))) {
perror(tn);
return (-2);
}
/* write a signature at the top of the file.
*
* VERY IMPORTANT: make sure NHEADER_LINES agrees with this code.
*/
fprintf(tmp, "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n");
fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now));
fprintf(tmp, "# (Cron version -- %s)\n", rcsid);
/* copy the crontab to the tmp
*/
rewind(NewCrontab);
Set_LineNum(1)
while (EOF != (ch = get_char(NewCrontab)))
putc(ch, tmp);
ftruncate(fileno(tmp), ftell(tmp));
fflush(tmp); rewind(tmp);
if (ferror(tmp)) {
fprintf(stderr, "%s: error while writing new crontab to %s\n",
ProgramName, tn);
fclose(tmp); unlink(tn);
return (-2);
}
/* check the syntax of the file being installed.
*/
/* BUG: was reporting errors after the EOF if there were any errors
* in the file proper -- kludged it by stopping after first error.
* vix 31mar87
*/
Set_LineNum(1 - NHEADER_LINES)
CheckErrorCount = 0; eof = FALSE;
while (!CheckErrorCount && !eof) {
switch (load_env(envstr, tmp)) {
case ERR:
eof = TRUE;
break;
case FALSE:
e = load_entry(tmp, check_error, pw, envp);
if (e)
free(e);
break;
case TRUE:
break;
}
}
if (CheckErrorCount != 0) {
fprintf(stderr, "errors in crontab file, can't install.\n");
fclose(tmp); unlink(tn);
return (-1);
}
#ifdef HAS_FCHOWN
if (fchown(fileno(tmp), ROOT_UID, -1) < OK)
#else
if (chown(tn, ROOT_UID, -1) < OK)
#endif
{
perror("chown");
fclose(tmp); unlink(tn);
return (-2);
}
#ifdef HAS_FCHMOD
if (fchmod(fileno(tmp), 0600) < OK)
#else
if (chmod(tn, 0600) < OK)
#endif
{
perror("chown");
fclose(tmp); unlink(tn);
return (-2);
}
if (fclose(tmp) == EOF) {
perror("fclose");
unlink(tn);
return (-2);
}
(void) sprintf(n, CRON_TAB(User));
if (rename(tn, n)) {
fprintf(stderr, "%s: error renaming %s to %s\n",
ProgramName, tn, n);
perror("rename");
unlink(tn);
return (-2);
}
log_it(RealUser, Pid, "REPLACE", User);
poke_daemon();
return (0);
}
static void
poke_daemon() {
#ifdef USE_UTIMES
struct timeval tvs[2];
struct timezone tz;
(void) gettimeofday(&tvs[0], &tz);
tvs[1] = tvs[0];
if (utimes(SPOOL_DIR, tvs) < OK) {
fprintf(stderr, "crontab: can't update mtime on spooldir\n");
perror(SPOOL_DIR);
return;
}
#else
if (utime(SPOOL_DIR, NULL) < OK) {
fprintf(stderr, "crontab: can't update mtime on spooldir\n");
perror(SPOOL_DIR);
return;
}
#endif /*USE_UTIMES*/
}

155
usr.sbin/cron/doc/CHANGES Normal file
View File

@ -0,0 +1,155 @@
Vixie Cron Changes from V2 to V3
Paul Vixie
29-Dec-1993
The crontab command now conforms to POSIX 1003.2. This means that when you
install it, if you have any "crontab" command lines floating around in shell
scripts (such as /etc/rc or /etc/rc.local), you will need to change them.
I have integrated several changes made by BSDi for their BSD/386 operating
system; these were offerred to me before I started consulting for them, so
it is safe to say that they were intended for publication. Most notably,
the name of the cron daemon has changed from "crond" to "cron". This was
done for compatibility with 4.3BSD. Another change made for the same reason
is the ability to read in an /etc/crontab file which has an extra field in
each entry, between the time fields and the command. This field is a user
name, and it permits the /etc/crontab command to contain commands which are
to be run by any user on the system. /etc/crontab is not "installed" via
the crontab(1) command; it is automatically read at startup time and it will
be reread whenever it changes.
I also added a "-e" option to crontab(1). Nine people also sent me diffs
to add this option, but I had already implemented it on my own. I actually
released an interrim version (V2.2, I think) for limited testing, and got a
chance to fix a bad security bug in the "-e" option thanks to XXX.
The daemon used to be extraordinarily sloppy in its use of file descriptors.
A heck of a lot of them were left open in spawned jobs, which caused problems
for the daemon and also caused problems with the spawned jobs if they were
shell scripts since "sh" and "csh" have traditionally used hidden file
descriptors to pass information to subshells, and cron was causing them to
think they were subshells. If you had trouble with "sh" or "csh" scripts in
V2, chances are good that V3 will fix your problems.
About a dozen people have reminded me that I forgot to initialize
"crontab_fd" in database.c. Keith Cantrell was the first, so he gets the
point.
Steve Simmons reminded me that once an account has been deleted from the
system, "crontab -u USER -d" will not work. My solution is to suggest to
all of you that before you delete a user's account, you first delete that
user's crontab file if any. From cron's point of view, usernames can never
be treated as arbitrary strings. Either they are valid user names, or they
are not. I will not make an exception for the "-d" case, for security
reasons that I consider reasonable. It is trivial for a root user to delete
the entry by hand if necessary.
Dan O'Neil reminded me that I forgot to reset "log_fd" in misc.c. A lot of
others also reminded me of this, but Dan gets the point. I didn't fix it
there, since the real bug was that it should have been open in the parent.
Peter Kabal reminded me that I forgot to "#ifdef DEBUGGING" some code in
misc.c. Hans Trompert actually told me first, but Peter sent the patch so
he gets the point.
Russell Nelson told me that I'd forgotten to "#include <syslog.h>" in misc.c,
which explains why a lot of other people complained that it wasn't using
syslog even when they configured it that way :-). Steve Simmons told me
first, though, so he gets the point.
An interrim version of the daemon tried to "stat" every file before
executing it; this turned out to be a horribly bad idea since finding the
name of a file from a shell command is a hard job (that's why we have
shells, right?) I removed this bogus code. Dave Burgess gets the point.
Dennis R. Conley sent a suggestion for MMDF systems, which I've added to the
comments in cron.h.
Mike Heisler noted that I use comments in the CONVERSION file which are
documented as illegal in the man pages. Thanks, Mike.
Irving Wolfe sent me some very cheerful changes for a NeXT system, but I
consider the system itself broken and I can't bring myself to #ifdef for
something as screwed up as this system seems to be. However, various others
did send me smaller patches which appear to have cause cron to build and run
correctly on (the latest) NeXT machines, with or without the "-posix" CFLAG.
Irving also asked for a per-job MAILTO, and this was finally added later when
I integrated the BSD/386 changes contributed by BSDi, and generalized some of
the parsing.
Lots of folks complained that the autogenerated "Date:" header wasn't in
ARPA format. I didn't understand this -- either folks will use Sendmail and
not generate a Date: at all (since Sendmail will do it), or folks will use
something other than Sendmail which won't care about Date: formats. But
I've "fixed" it anyway...
Several people suggested that "*" should be able to take a "/step". One person
suggested that "N/step" ought to mean "N-last/step", but that's stretching things
a bit far. "*/step" seems quite intuitive to me, so I've added it. Colin Plumb
sent in the first and most polite request for this feature.
As with every release of Cron, BIND, and seemingly everything else I do, one
user stands out with the most critical but also the most useful analysis.
Cron V3's high score belongs to Peter Holzer, who sent in the nicest looking
patch for the "%" interpretation problem and also helped me understand a
tricky bit of badness in the "log_fd" problem.
agulbra@flode.nvg.unit.no wins the honors for being the first to point out the
nasty security hole in "crontab -r". 'Nuff said.
Several folks pointed out that log_it() needed to exist even if logging was
disabled. Some day I will create a tool that will compile a subsystem with
every possible combination and permutation of #ifdef options, but meanwhile
thanks to everybody.
job_runqueue() was using storage after freeing it, since Jordan told me back
in 1983 that C let you do that, and I believed him in 1986 when I wrote all
this junk. Linux was the first to die from this error, and the Linux people
sent me the most amazing, um, collection of patches for this problem. Thanks
for all the fish.
Jeremy Bettis reminded me that popen() isn't safe. I grabbed Ken Arnold's
version of popen/pclose from the ftpd and hacked it to taste. We're safe now,
from this at least.
Branko Lankester sent me a very timely and helpful fix for a looming security
problem in my "crontab -e" implementation.
--------
Vixie Cron Changes from V1 to V2
Paul Vixie
8-Feb-1988
Many changes were made in a rash of activity about six months ago, the exact
list of which is no longer clear in my memory. I know that V1 used a file
called POKECRON in /usr/spool/cron to tell it that it was time to re-read
all the crontab files; V2 uses the modtime the crontab directory as a flag to
check out the crontab files; those whose modtime has changed will be re-read,
and the others left alone. Note that the crontab(1) command will do a utimes
call to make sure the mtime of the dir changes, since the filename/inode will
often remain the same after a replacement and the mtime wouldn't change in
that case.
8-Feb-88: made it possible to use much larger environment variable strings.
V1 allowed 100 characters; V2 allows 1000. This was needed for PATH
variables on some systems. Thanks to Toerless Eckert for this idea.
E-mail: UUCP: ...pyramid!fauern!faui10!eckert
16-Feb-88: added allow/deny, moved /usr/spool/cron/crontabs to
/usr/lib/cron/tabs. allow and deny are /usr/lib/cron/{allow,deny},
since the sysv naming for this depends on 'at' using the same
dir, which would be stupid (hint: use /usr/{lib,spool}/at).
22-Feb-88: made it read the spool directory for crontabs and look each one
up using getpwnam() rather than reading all passwds with getpwent()
and trying to open each crontab.
9-Dec-88: made it sync to :00 after the minute, makes cron predictable.
added logging to /var/cron/log.
14-Apr-90: (actually, changes since December 1989)
fixed a number of bugs reported from the net and from John Gilmore.
added syslog per Keith Bostic. security features including not
being willing to run a command owned or writable by other than
the owner of the crontab 9not working well yet)

View File

@ -0,0 +1,85 @@
$Id: CONVERSION,v 2.2 1993/12/28 08:34:43 vixie Exp $
Conversion of BSD 4.[23] crontab files:
Edit your current crontab (/usr/lib/crontab) into little pieces, with each
users' commands in a different file. This is different on 4.2 and 4.3,
but I'll get to that below. The biggest feature of this cron is that you
can move 'news' and 'uucp' cron commands into files owned and maintainable
by those two users. You also get to rip all the fancy 'su' footwork out
of the cron commands. On 4.3, there's no need for the 'su' stuff since the
user name appears on each command -- but I'd still rather have separate
crontabs with seperate environments and so on.
Leave the original /usr/lib/crontab! This cron doesn't use it, so you may
as well keep it around for a while in case something goes wakko with this
fancy version.
Most commands in most crontabs are run by root, have to run by root, and
should continue to be run by root. They still have to be in their own file;
I recommend /etc/crontab.src or /usr/adm/crontab.src.
'uucp's commands need their own file; how about /usr/lib/uucp/crontab.src?
'news' also, perhaps in /usr/lib/news/crontab.src...
I say `how about' and `perhaps' because it really doesn't matter to anyone
(except you) where you put the crontab source files. The `crontab' command
COPIES them into a protected directory (CRONDIR/SPOOL_DIR in cron.h), named
after the user whose crontab it is. If you want to examine, replace, or
delete a crontab, the `crontab' command does all of those things. The
various `crontab.src' (my suggested name for them) files are just source
files---they have to be copied to SPOOLDIR using `crontab' before they'll be
executed.
On 4.2, your crontab might have a few lines like this:
5 * * * * su uucp < /usr/lib/uucp/uudemon.hr
10 4 * * * su uucp < /usr/lib/uucp/uudemon.day
15 5 * * 0 su uucp < /usr/lib/uucp/uudemon.wk
...or like this:
5 * * * * echo /usr/lib/uucp/uudemon.hr | su uucp
10 4 * * * echo /usr/lib/uucp/uudemon.day | su uucp
15 5 * * 0 echo /usr/lib/uucp/uudemon.wk | su uucp
On 4.3, they'd look a little bit better, but not much:
5 * * * * uucp /usr/lib/uucp/uudemon.hr
10 4 * * * uucp /usr/lib/uucp/uudemon.day
15 5 * * 0 uucp /usr/lib/uucp/uudemon.wk
For this cron, you'd create /usr/lib/uucp/crontab.src (or wherever you want
to keep uucp's commands) which would look like this:
# /usr/lib/uucp/crontab.src - uucp's crontab
#
PATH=/usr/lib/uucp:/bin:/usr/bin
SHELL=/bin/sh
HOME=/usr/lib/uucp
#
5 * * * * uudemon.hr
10 4 * * * uudemon.day
15 5 * * 0 uudemon.wk
The application to the `news' cron commands (if any) is left for you to
figure out. Likewise if there are any other cruddy-looking 'su' commands in
your crontab commands, you don't need them anymore: just find a good place
to put the `crontab.src' (or whatever you want to call it) file for that
user, put the cron commands into it, and install it using the `crontab'
command (probably with "-u USERNAME", but see the man page).
If you run a 4.2-derived cron, you could of course just install your current
crontab in toto as root's crontab. It would work exactly the way your
current one does, barring the extra steps in installing or changing it.
There would still be advantages to this cron, mostly that you get mail if
there is any output from your cron commands.
One note about getting mail from cron: you will probably find, after you
install this version of cron, that your cron commands are generating a lot
of irritating output. The work-around for this is to redirect all EXPECTED
output to a per-execution log file, which you can examine if you want to
see the output from the "last time" a command was executed; if you get any
UNEXPECTED output, it will be mailed to you. This takes a while to get
right, but it's amazingly convenient. Trust me.

View File

@ -0,0 +1,84 @@
$Id: FEATURES,v 2.1 1993/12/28 08:34:43 vixie Exp $
Features of Vixie's cron relative to BSD 4.[23] and SysV crons:
-- Environment variables can be set in each crontab. SHELL, USER,
LOGNAME, and HOME are set from the user's passwd entry; all except
USER can be changed in the crontab. PATH is especially useful to
set there. TZ can be set, but cron ignores it other than passing
it on through to the commands it runs. Format is
variable=value
Blanks surrounding the '=' will be eaten; other blanks in value are
okay. Leading or trailing blanks can be preserved by quoting, single
or double quotes are okay, just so they match.
PATH=.:/bin:/usr/bin
SHELL=/bin/sh
FOOBAR = this is a long blanky example
Above, FOOBAR would get "this is a long blanky example" as its value.
SHELL and HOME will be used when it's time to run a command; if
you don't set them, HOME defaults to your /etc/passwd entry
and SHELL defaults to /bin/sh.
MAILTO, if set to the login name of a user on your system, will be the
person that cron mails the output of commands in that crontab. This is
useful if you decide on BINMAIL when configuring cron.h, since binmail
doesn't know anything about aliasing.
-- Weekdays can be specified by name. Case is not significant, but only
the first three letters should be specified.
-- Months can likewise be specified by name. Three letters only.
-- Ranges and lists can be mixed. Standard crons won't allow '1,3-5'.
-- Ranges can specify 'step' values. '10-16/2' is like '10,12,14,16'.
-- Sunday is both day 0 and day 7 -- apparently BSD and ATT disagree
about this.
-- Each user gets their own crontab file. This is a win over BSD 4.2,
where only root has one, and over BSD 4.3, where they made the crontab
format incompatible and although the commands can be run by non-root
uid's, root is still the only one who can edit the crontab file. This
feature mimics the SysV cron.
-- The 'crontab' command is loosely compatible with SysV, but has more
options which just generally make more sense. Running crontab with
no arguments will print a cute little summary of the command syntax.
-- Comments and blank lines are allowed in the crontab file. Comments
must be on a line by themselves; leading whitespace is ignored, and
a '#' introduces the comment.
-- (big win) If the `crontab' command changes anything in any crontab,
the 'cron' daemon will reload all the tables before running the
next iteration. In some crons, you have to kill and restart the
daemon whenever you change a crontab. In other crons, the crontab
file is reread and reparsed every minute even if it didn't change.
-- In order to support the automatic reload, the crontab files are not
readable or writable except by 'crontab' or 'cron'. This is not a
problem, since 'crontab' will let you do pretty much whatever you
want to your own crontab, or if you are root, to anybody's crontab.
-- If any output is generated by a command (on stdout OR stderr), it will
be mailed to the owner of the crontab that contained the command (or
MAILTO, see discussion of environment variables, above). The headers
of the mail message will include the command that was run, and a
complete list of the environment that was passed to it, which will
contain (at least) the USER (LOGNAME on SysV), HOME, and SHELL.
-- the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
is why we keep 'e->dow_star' and 'e->dom_star'. I didn't think up
this behaviour; it's how cron has always worked but the documentation
hasn't been very clear. I have been told that some AT&T crons do not
act this way and do the more reasonable thing, which is (IMHO) to "or"
the various field-matches together. In that sense this cron may not
be completely similar to some AT&T crons.

87
usr.sbin/cron/doc/INSTALL Normal file
View File

@ -0,0 +1,87 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
$Id: INSTALL,v 2.5 1994/01/15 20:43:43 vixie Exp $
Read the comments at the top of the Makefile, then edit the area marked
'configurable stuff'.
Edit config.h. The stuff I expect you to change is down a bit from the
top of the file, but it's clearly marked. Also look at pathnames.h.
You don't have to create the /var/cron or /var/cron/tabs directories, since
both the daemon and the `crontab' program will do this the first time they
run if they don't exist. You do need to have a /var, though -- just "mkdir
/var" if you don't have one, or you can "mkdir /usr/var; ln -s /usr/var /var"
if you expect your /var to have a lot of stuff in it.
You will also need /usr/local/etc and /usr/local/bin directories unless you
change the Makefile. These will have to be created by hand, but if you are
a long-time Usenet user you probably have them already. /usr/local/man is
where I keep my man pages, but I have the source for `man' and you probably
do not. Therefore you may have to put the man pages into /usr/man/manl,
which will be hard since there will be name collisions. (Note that the man
command was originally written by Bill Joy before he left Berkeley, and it
contains no AT&T code, so it is in UUNET's archive of freely-distributable
BSD code.)
LINUX note: /usr/include/paths.h on some linux systems shows _PATH_SENDMAIL
to be /usr/bin/sendmail even though sendmail is installed in /usr/lib.
you should check this out.
say:
make all
su and say:
make install
Note that if I can get you to "su and say" something just by asking, you have
a very serious security problem on your system and you should look into it.
Edit your /usr/lib/crontab file into little pieces -- see the CONVERSION file
for help on this.
Use the `crontab' command to install all the little pieces you just created.
Some examples (see below before trying any of these!)
crontab -u uucp -r /usr/lib/uucp/crontab.src
crontab -u news -r /usr/lib/news/crontab.src
crontab -u root -r /usr/adm/crontab.src
Notes on above examples: (1) the .src files are copied at the time the
command is issued; changing the source files later will have no effect until
they are reinstalled with another `crontab -r' command. (2) The crontab
command will affect the crontab of the person using the command unless `-u
USER' is given; `-u' only works for root. When using most `su' commands
under most BSD's, `crontab' will still think of you as yourself even though
you may think of yourself as root -- so use `-u' liberally. (3) the `-r'
option stands for `replace'; check the man page for crontab(1) for other
possibilities.
Kill your existing cron daemon -- do `ps aux' and look for /etc/cron.
Edit your /etc/rc or /etc/rc.local, looking for the line that starts up
/etc/cron. Comment it out and add a line to start the new cron daemon
-- usually /usr/local/etc/cron, unless you changed it in the Makefile.
Start up this cron daemon yourself as root. Just type /usr/local/etc/cron
(or whatever); no '&' is needed since the daemon forks itself and the
process you executed returns immediately.
ATT notes: for those people unfortunate enough to be stuck on a AT&T UNIX,
you will need the public-domain "libndir", found in the B News source and in
any comp.sources.unix archive. You will also need to hack the code some.

475
usr.sbin/cron/doc/MAIL Normal file
View File

@ -0,0 +1,475 @@
[ this is really old mail that came to me in response to my 1986 posting
to usenet asking for feature suggestions before releasing the first
version of cron. it is presented here for its entertainment value.
--vix ]
$Id: MAIL,v 1.1 1993/12/28 08:30:36 vixie Exp $
From ptsfa!lll-crg!ames!acornrc!bob Wed Dec 31 10:07:08 1986
Date: Wed, 31 Dec 86 08:59:31 pst
From: lll-crg!ames!acornrc!bob (Bob Weissman)
To: ptsfa!vixie!paul
Status: RO
Sure, here's a suggestion: I'd like to be able to run a program, say,
every two hours. Current cron requires me to write
0,2,4,6,8,10,12,14,16,18,20,22 in the hours field. How about a notation
to handle this more elegantly?
<< Okay, I've allowed 0-22/2 as a means of handling this.
The time specification for my cron is as follows:
specification = range {"," range}
range = (start "-" finish ["/" step]) | single-unit
This allows "1,3,5-7", which the current cron doesn't (it won't
do a range inside a list), and handles your specific need. >>
From drw@mit-eddie Wed Dec 31 18:25:27 1986
Date: Wed, 31 Dec 86 14:28:19 est
From: drw@mit-eddie (Dale Worley)
To: mit-eddie!vixie!paul
Status: RO
We have a lot of lines in our crontab of the form
00 12 * * * su user < /usr/users/user/script.file
This barfs (silently!) on our system (Dec Ultrix 1.2 == 4.2bsd) if
user's shell is csh. This, I am told, is because csh requires that
the environment be set up in certain ways, which cron doesn't do.
(Actually, I believe, it is because /etc/rc, which runs cron, doesn't
set up the environment enough for csh to run, and cron just inherits
the situation.) Anyway, the point is that if you find out what csh
really needs in its environment, you might want to set up cron to
provide some reasonable defaults (if it isn't supplied by cron's
parent). Also, could you tell me what csh needs, if you find out, so
we can hack our /etc/rc?
<< well, the environment IS a problem. processes that cron forks
will inherit the environment of the person who ran the cron
daemon... I plan to edit out such useless things as TERMCAP,
TERM, and the like; supply correct values for HOME, USER, CWD,
and whatever else comes to mind. I'll make sure csh works... >>
From ptsfa!ames!seismo!dgis!generous Thu Jan 1 07:33:17 1987
Date: Thu Jan 1 10:29:20 1987
From: ames!seismo!dgis!generous (Curtis Generous)
To: nike!ptsfa!vixie!paul
Status: RO
Paul:
One of the limitations of the present versions of cron is the lack
of the capability of specifying a way to execute a command every
n units of time.
Here is a good example:
# Present method to start up uucico
02,12,22,32,42,52 * * * * exec /usr/lib/uucp/uucico -r1
# New method ?? (the ':' here is just one possibility for syntax)
02:10 * * * * exec /usr/lib/uucp/uucico -r1
This method would prove very helpful for those programs that get started
every few minutes, making the entry long and not easily readable. The first
number would specify the base time, and the second number the repetition
interval.
<< Good idea, but bob@acornrc beat you to it. I used '/' instead of
':'. This is my personal preference, and seems intuitive when you
think of the divide operator in C... Does anyone have a preference? >>
From ptsfa!lll-lcc!seismo!decuac!c3pe!c3engr!charles Thu Jan 1 17:04:24 1987
From: lll-lcc!seismo!c3pe!c3engr!charles (Charles Green)
To: c3pe!decuac!dolqci!vrdxhq!seismo!lll-lcc!ptsfa!vixie!paul
Date: Thu Jan 1 19:22:47 1987
Status: RO
Well, this isn't a compatible extension, but I have in times past wondered
about a facility to let you start a process at intervals of, say, 17 minutes,
instead of particular minutes out of each hour.
<< This was a popular request! >>
From seismo!uwvax!astroatc!nicmad!norvax!mann Sun Jan 4 13:04:01 1987
Date: Fri, 2 Jan 87 09:23:53 cst
From: lll-lcc!seismo!uwvax!astroatc!nicmad!norvax!mann (Tom Mann)
To: ptsfa!vixie!paul
Status: RO
I'm not sure if it is in cron (either SysV or BSD ... if it is, I haven't
figured it out ) but a comment feature would SURE BE NICE!.
There are times when I want to comment out an entry
for a period of time; it might also make it a lot more legible.
<< My cron allows blank lines and standard #-type comments. I know
that one BSD4.2 cron I've used had it. I don't know about SysV. >>
From ptsfa!hoptoad!hugh Mon Jan 5 10:26:46 1987
Date: Mon, 5 Jan 87 01:22:17 PST
From: hoptoad!hugh (Hugh Daniel)
To: ptsfa!vixie!paul
Status: RO
Hi, I do have a BIG one that I would like. I want to log ALL output
from command lines into a file for each line. Thus I might have a chance
of finding out why my crontab entry did not work.
This would seem to work best if done by cron, as it is now I have a google
of shell scripts laying about just to put the error output where I can see
it.
<< My cron (and the SysV cron) will send mail to the owner of the
particular crontab file if a command generates any output on stdout
or stderr. This can be irritating, but if you write a script such
that any output means a problem occurred, you can avoid most logfile
needs, and not generate mail except in unforeseen circumstances. >>
From ptsfa!dual!ucbvax!ihnp4!anvil!es!Robert_Toxen Mon Jan 5 13:08:46 1987
From: dual!ucbvax!ihnp4!anvil!es!Robert_Toxen
Date: Fri, 2 Jan 87 14:25:29 EST
To: anvil!ihnp4!ucbvax!dual!ptsfa!vixie!paul
Status: RO
Here are some suggestions:
1. Run it through the C preprocessor via "/lib/<whatever>".
<< hmmm. this seems of limited utility, and if you really wanted
to do it that way, you could do it yourself (since users can
write to their own crontab files). I'll add '-' (read stdin)
to the crontab installer program to facilitate this. >>
2. Allow specifying every Nth day of week, i.e., every second Wednesday.
I did this to calendar by separating the day of week (Wed=4, which one
to start on and N with slashes). I took modulo the day of year as a
starting point so that someone with a desk calendar documenting such
things can easily determine the offset (second number). I did this
while at SGI; alas I don't have a copy of the code.
<< I can see how this could be useful, but I'm not sure how I'd
implement it. Cron currently doesn't keep track of the last time
a given command was run; whether the current Wednesday is the first
or second since the command was last run would be pretty hard to
figure out. I'd have to keep a database of commands and their
execution around, and purge it when the crontab was overwritten.
This is too much work for me, but if someone adds it, let me know. >>
From ptsfa!ames!seismo!cbmvax!devon!paul Tue Jan 6 05:50:17 1987
From: ames!seismo!cbmvax!devon!paul
To: cbmvax!seismo!nike!ptsfa!vixie!paul
Date: Mon Jan 5 09:29:57 1987
Status: RO
One problem that has always plagued me with cron is the assumed ORing.
I'd like to see some type of ANDing implemented. I guess I can best
describe this by example. Say I have the following line in my crontab
file:
* * 4-31 * 1-6 /usr/bin/command
What this does is run 'command' on the 4th thru 31st days of the
month, AND on Monday thru Saturday; which probably means running it
every day of the month (unless Sunday falls on days 1-3). This
happens because cron runs the command if the day-of-month OR the
day-of-week is true.
What I'd like to happen with the above line is to run the command ONLY
on Monday thru Saturday any time after the 3rd of the month, e.g. if
the day-of-month AND the day-of-week are true.
My proposal to you is to implement some special chars for the first
five fields. Examples:
* * !1-3 * 1-6 /usr/bin/command
(run command Mon-Sat, but NOT [!] on the first 3 days of the month)
* * &4-31 * &1-6 /usr/bin/command
(run command if day-of-month AND day-of-week are true)
Get the picture? This would be compatable with existing versions of
cron (which wouldn't currently be using any special characters, so
that old crontabs would be handled correctly).
<< This message made me aware of the actual boolean expression involved
in a crontab entry. I'd assumed that it was
(minute && hour && DoM && month && DoW)
But it's really
(minute && hour && month && (DoM || DoW))
I can see some value in changing this, but with a fixed order of
fields, operators get to be kindof unary, which && and || really
aren't. If someone has an idea on a syntax that allows useful
variations to the standard (&& && && (||)) default, please suggest. >>
From bobkat!pedz Tue Jan 6 20:02:10 1987
From: pedz@bobkat.UUCP (Pedz Thing)
Date: 2 Jan 87 17:34:44 GMT
Status: RO
Log files! It would be nice to be able to specify a log for cron
itself and also a log for each program's stdout and stderr to go to.
The latter can of course be done with > and 2> but it would be nice if
there could be a single line with some sort of pattern like
`> /usr/spool/log/%' and the command would be substituted for the %.
Another thing which would be nice is to be able to specify which shell
to call to give the command to.
<< Log files are done with mail. The '%' idea could be useful if
a different character were used (% is special to cron, see man
page); a different directory would have to be chosen, since each
user has their own crontab file; and something intelligent would
have to be done in the file naming, since the first word of the
command might be ambiguous (with other commands). In short, it's
too much work. Sorry. >>
From guy%gorodish@sun Tue Jan 6 20:03:13 1987
From: guy%gorodish@sun (Guy Harris)
Message-ID: <10944@sun.uucp>
Date: 5 Jan 87 12:09:09 GMT
References: <429@vixie.UUCP> <359@bobkat.UUCP>
Sender: news@sun.uucp
Status: RO
> Another thing which would be nice is to be able to specify which shell
> to call to give the command to.
Well, the obvious choice would be the user's shell, but this wouldn't work
for accounts like "uucico".
<< I use the owning user's shell, and to handle "uucico" I check a
list of "acceptable shells" (currently compiled in, does anybody
mind?), substituting a default (compiled in) shell if the user's
shell isn't on the list.
BTW, "compiled in" means that it's in a .h file, easily changed
during installation, but requiring recompilation to modify. You
don't have to go digging through the code to find it... >>
From qantel!hplabs!ucbvax!mwm@violet.berkeley.edu Tue Jan 6 21:24:48 1987
To: hplabs!qantel!vixie!paul (Paul Vixie Esq)
Date: 04 Jan 87 00:42:35 PST (Sun)
From: Mike Meyer <mwm@violet.berkeley.edu>
Status: RO
<<[Discussion of RMS/FSF, and mwm's GNU Cron deleted]>>
Oh, yeah - here are the extensions on my cron:
1) Sunday is both day 0 and day 7, so it complies with both SysV and
BSD cron.
<< Good idea. I did it too, thanks for informing me. >>
2) At is integrated into the cron. Instead of atrun to scan the
/usr/spool/at directory, at files are put into the /usr/lib/cron
directory along with users cron files, and cron fabricates a line from
a crontab file to run them. This is considered a major win by all who
use it.
<< I don't use 'at', and my cron doesn't do anything with it. To run
'at', I use 'atrun' the same way the current BSD cron does. My
crontab files are in /usr/spool/cron/crontabs, in the SysV
tradition -- not in /usr/lib/cron. This is a configuration
parameter, of course. >>
There are two known restrictions:
1) I don't support any of the SysV security hooks. I don't have a use
for them, and RMS didn't like the idea at all :-).
<< This means cron.allow and cron.deny. I plan to support them, as
they've been quite helpful at various HPUX sites I've administered. >>
2) Cron expects to be able to create files with names longer than 14
characters, which makes it hard to run on SysV. At least one person
was working on a port, but I don't know how it's going. That might
make for a good reason for releasing yours, right there.
<< If someone has SysV (with the 14-character limit), they probably
won't want my cron, since it doesn't add much to the standard
version (which they may have support for). My cron is not currently
portable to non-BSD systems, since it relies on interval timers (I
needed to sleep for intervals more granular than seconds alone would
allow). The port would be trivial, and I will do it if a lot of
people ask for it... >>
Oh, yeah - I'm going to see about getting this cron integrated into
the next 4BSD release.
<< How does one go about this? I have a few nifty gadgets I'd like
to contribute, this cron being one of them... >>
<<[more FSF/GNU discussion deleted]>>
From qantel!hplabs!ames!ut-sally!ut-ngp!melpad!bigtex!james Tue Jan 6 21:24:57 1987
Posted-Date: Fri, 2 Jan 87 19:26:16 est
Date: Fri, 2 Jan 87 19:26:16 est
From: hplabs!ames!ut-sally!ut-ngp!bigtex!james
To: vixie!paul
Status: RO
Yes!!! There are several critical failures in System V cron...
1. Pass all variables in cron's environment into the environment of things
cron starts up, or at least into the crontab entries started up (at jobs
will inherit the environment of the user). If nothing else it is critically
important that the TZ variable be passed on. PATH should be passed on too.
Basically, passing environment values allows one to design a standard
environment with TZ and PATH and have that run by everything. If anyone
tells you this is no big deal, consider what happens when uucico is
started by cron in CA to make a long distance phone link... Unless the
administrator is really on his/her toes, calls scheduled at 5pm will really
go at two in the afternoon, needlessly incurring huge phone bills, all
because System V refuses to pass the TZ from its environment down. There
are work arounds, but only putting it in cron will really work. This is
not a security hole.
<< delete TERM and TERMCAP; modify HOME, USER, and CWD; pass TZ and
PATH through undisturbed. any other requests out there?
BSD doesn't have this problem -- TZ is passed right on through if
you define it in the shell before starting my cron daemon. However,
the BSD I'm running this on doesn't need TZ to be defined anyway...
The default in the kernel has been just fine so far... But just the
same, if/when I port to SysV (I guess I really should), I'll make
sure this works right.
I guess I've been spoiled. HPUX is SysV-based, and I never had a
problem with cron and TZ when I used it. >>
2. A way to avoid logging stuff in /usr/lib/cron/log. I have a cron entry
run uudemon.hr every 10 minutes. This is 144 times/day. Each run generates
three lines of text, for a total of 432 lines of text I don't want to see.
Obviously this should be optional, but it would be nice if there were a
way to flag an entry so that it wasn't logged at all unless there was an
error.
<< I don't know nothin' 'bout no /usr/lib/cron/log. What is this file?
I don't see any reason to create log entries, given the mail-the-
output behaviour. Opinions, anyone? >>
I will come up with other ideas no doubt, but I can always implement them
myself.
<< That's what I like about PD software. Please send me the diffs! >>
The other problem you have is making sure you can run standard
crontabs. I would suggest something like this: if the command part of the
entry starts with an unescaped -, then flags and options follow immediately
thereafter. As in:
2,12,22,32,42,52 * * * * -q /usr/lib/uucp/uudemon.hr
This could mean do not log the uudemon.hr run unless there is a problem of
some kind. This is probably safe as not many filenames start with "-", and
those that do are already a problem for people.
<< Since I don't plan on supporting /usr/lib/cron/log in ANY form unless
many people request it, I won't be needing -q as you've defined it.
I could use something like this to avoid sending mail on errors, for
the occasional script that you don't want to bullet-proof.
The compatibility issue is CRITICAL. The 4.3BSD crontab format is
a crime against the whole philosophy of Unix(TM), in my opinion. >>
One other minor thing to consider is the ulimit: can different users get
different ulimits for their crontab entries?
<< Boy I'm ignorant today. What's a ulimit, and what should I do with
it in a crontab? Suggestions, enlightenment, etc ?? >>
From qantel!lll-crg!ames!uw-beaver!uw-nsr!john Tue Jan 6 23:32:44 1987
Date: Thu, 1 Jan 87 10:53:05 pst
From: lll-crg!ames!uw-beaver!uw-nsr!john (John Sambrook 5-7433)
To: vixie!paul
Status: RO
How about not hardwiring the default environment that cron builds for its
children in the cron program itself? Our cron does this and it's the pits
because we are TZ=PST8PDT not TZ=EST5EDT !
<< yeachk. I assure you, I will not hardwire the TZ! >>
From ptsfa!well!dv Fri Jan 9 04:01:50 1987
Date: Thu, 8 Jan 87 23:50:40 pst
From: well!dv (David W. Vezie)
To: ptsfa!vixie!paul
Status: RO
6, have a special notation called 'H' which would expand to weekends
and holidays (you'd have to keep a database somewhere of real
holidays), and also 'W' for workdays (neither weekend or holiday).
<< Too much work. There should be a standard way to define and
detect holidays under Unix(TM); if there were, I'd use it. As
it is, I'll leave this for someone else to add.
I can see the usefulness; it just doesn't quite seem worth it. >>
From qantel!gatech!akgua!blnt1!jat Wed Jan 14 20:00:40 1987
Date: Tue, 13 Jan 87 16:39:38 EST
From: gatech!akgua!blnt1!jat
Status: RO
1) Add some way to tell cron to reread the files, say kill -1 <pid>
<< whenever the 'crontab' program is run and updates a crontab file,
a file /usr/spool/cron/POKECRON is created; next time the cron
daemon wakes up, it sees it, and re-reads the crontab files.
I thought of handling the signal; even implemented it. Then this
clever idea hit me and I ripped it all out and added a single
IF-statement to handle the POKECRON file. >>
2) Have some kind of retry time so that if a command fails, cron will try to
execute it again after a certain period. This is useful if you have some
type of cleanup program that can run at the scheduled time for some reason
(such as locked device, unmounted filesystem, etc).
<< Hmmm, sounds useful. I could do this by submitting an 'at' job...
I'll think about it. >>
From ptsfa!dual!ucbvax!ihnp4!mtuxo!ender Sat Jan 3 16:54:00 1987
From: dual!ucbvax!ihnp4!mtuxo!ender
Date: Sat, 3 Jan 87 14:05:13 PST
To: ucbvax!dual!ptsfa!vixie!paul
Status: RO
It would be nice if nonprivileged users can setup personal crontab files
(~/.cronrc, say) and be able to run personal jobs at regular intervals.
<< this is done, but in the SysV style: the 'crontab' program installs
a new crontab file for the executing user (can be overridden by root
for setup of uucp and news). the advantage of this is that (1) when
a crontab is changed, the daemon can be informed automatically; and
(2) the file can be syntax-checked before installation. >>
From ptsfa!ames!seismo!ihnp4!lcc!richard Fri Jan 16 04:47:33 1987
Date: Fri, 16 Jan 87 07:44:57 EST
To: nike!ptsfa!vixie!paul
Status: RO
The System V cron is nice, but it has a few annoying features. One is that
its mail files will say that the previous message is the output of "one of your
cron commands." I wish it would say WHICH cron commmand.
<< Done. Also which shell, which user (useful when the mail gets
forwarded), which home directory, and other useful crud. >>
Another problem is with timezones. It is necessary to specify TZ=PST8PDT (or
whatever) when you invoke cron (from inittab, or /etc/rc) and it is also
necessary to add TZ=PST8PDT to each crontab line which might need it. Cron
should automatically export its idea of the "TZ" to each invoked command, and
it should be possible to put a line in the crontab file which overrides that
for every command in the file (e.g., most users are on EST, so cron is run
with TZ=EST5EDT; but one user is usually on PST and wants all of his cron
commands to run with TZ=PST8PDT). This might be extended to allow any
environment variable to be specified once for the whole crontab file (e.g.,
PATH).
<< Well, since I run the user's shell, you could put this into .cshrc.
generic environment-variable setting could be useful, though. Since
I have to modify the environment anyway, I'll consider this. >>
A log file might be a nice idea, but the System V cron log is too verbose.
I seem to remember that cron keeps it open, too; so you can't even have
something go and periodically clean it out.
<< I don't do /usr/lib/cron/log. I wasn't aware of this file until I
got all these suggestions. Do people want this file? Tell me! >>

View File

@ -0,0 +1,128 @@
#/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
# */
# Makefile for vixie's cron
#
# $Id: Makefile,v 2.9 1994/01/15 20:43:43 vixie Exp $
#
# vix 03mar88 [moved to RCS, rest of log is in there]
# vix 30mar87 [goodbye, time.c; hello, getopt]
# vix 12feb87 [cleanup for distribution]
# vix 30dec86 [written]
# NOTES:
# 'make' can be done by anyone
# 'make install' must be done by root
#
# this package needs getopt(3), bitstring(3), and BSD install(8).
#
# the configurable stuff in this makefile consists of compilation
# options (use -O, cron runs forever) and destination directories.
# SHELL is for the 'augumented make' systems where 'make' imports
# SHELL from the environment and then uses it to run its commands.
# if your environment SHELL variable is /bin/csh, make goes real
# slow and sometimes does the wrong thing.
#
# this package needs the 'bitstring macros' library, which is
# available from me or from the comp.sources.unix archive. if you
# put 'bitstring.h' in a non-standard place (i.e., not intuited by
# cc(1)), you will have to define INCLUDE to set the include
# directory for cc. INCLUDE should be `-Isomethingorother'.
#
# there's more configuration info in config.h; edit that first!
#################################### begin configurable stuff
#<<DESTROOT is assumed to have ./etc, ./bin, and ./man subdirectories>>
DESTROOT = $(DESTDIR)/usr
DESTSBIN = $(DESTROOT)/sbin
DESTBIN = $(DESTROOT)/bin
DESTMAN = $(DESTROOT)/share/man
#<<need bitstring.h>>
INCLUDE = -I.
#INCLUDE =
#<<need getopt()>>
LIBS =
#<<optimize or debug?>>
#OPTIM = -O
OPTIM = -g
#<<ATT or BSD or POSIX?>>
# (ATT untested)
#COMPAT = -DATT
#(BSD is only needed if <sys/params.h> does not define it, as on ULTRIX)
#COMPAT = -DBSD
# (POSIX)
#COMPAT = -DPOSIX
#<<lint flags of choice?>>
LINTFLAGS = -hbxa $(INCLUDE) $(COMPAT) $(DEBUGGING)
#<<want to use a nonstandard CC?>>
#CC = vcc
#<<manifest defines>>
DEFS =
#(SGI IRIX systems need this)
#DEFS = -D_BSD_SIGNALS -Dconst=
#<<the name of the BSD-like install program>>
#INSTALL = installbsd
INSTALL = install
#<<any special load flags>>
LDFLAGS =
#################################### end configurable stuff
SHELL = /bin/sh
CFLAGS = $(OPTIM) $(INCLUDE) $(COMPAT) $(DEFS)
INFOS = README CHANGES FEATURES INSTALL CONVERSION THANKS MAIL
MANPAGES = bitstring.3 crontab.5 crontab.1 cron.8 putman.sh
HEADERS = bitstring.h cron.h config.h pathnames.h \
externs.h compat.h
SOURCES = cron.c crontab.c database.c do_command.c entry.c \
env.c job.c user.c popen.c misc.c compat.c
SHAR_SOURCE = $(INFOS) $(MANPAGES) Makefile $(HEADERS) $(SOURCES)
LINT_CRON = cron.c database.c user.c entry.c compat.c \
misc.c job.c do_command.c env.c popen.c
LINT_CRONTAB = crontab.c misc.c entry.c env.c compat.c
CRON_OBJ = cron.o database.o user.o entry.o job.o do_command.o \
misc.o env.o popen.o compat.o
CRONTAB_OBJ = crontab.o misc.o entry.o env.o compat.o
all : cron crontab
lint :
lint $(LINTFLAGS) $(LINT_CRON) $(LIBS) \
|grep -v "constant argument to NOT" 2>&1
lint $(LINTFLAGS) $(LINT_CRONTAB) $(LIBS) \
|grep -v "constant argument to NOT" 2>&1
cron : $(CRON_OBJ)
$(CC) $(LDFLAGS) -o cron $(CRON_OBJ) $(LIBS)
crontab : $(CRONTAB_OBJ)
$(CC) $(LDFLAGS) -o crontab $(CRONTAB_OBJ) $(LIBS)
install : all
$(INSTALL) -c -m 111 -o root -s cron $(DESTSBIN)/
$(INSTALL) -c -m 4111 -o root -s crontab $(DESTBIN)/
sh putman.sh crontab.1 $(DESTMAN)
sh putman.sh cron.8 $(DESTMAN)
sh putman.sh crontab.5 $(DESTMAN)
clean :; rm -f *.o cron crontab a.out core tags *~ #*
kit : $(SHAR_SOURCE)
makekit -m -s99k $(SHAR_SOURCE)
$(CRON_OBJ) : cron.h compat.h config.h externs.h pathnames.h Makefile
$(CRONTAB_OBJ) : cron.h compat.h config.h externs.h pathnames.h Makefile

72
usr.sbin/cron/doc/README Normal file
View File

@ -0,0 +1,72 @@
#/* Copyright 1988,1990,1993 by Paul Vixie
# * All rights reserved
# *
# * Distribute freely, except: don't remove my name from the source or
# * documentation (don't take credit for my work), mark your changes (don't
# * get me blamed for your possible bugs), don't alter or remove this
# * notice. May be sold if buildable source is provided to buyer. No
# * warrantee of any kind, express or implied, is included with this
# * software; use at your own risk, responsibility for damages (if any) to
# * anyone resulting from the use of this software rests entirely with the
# * user.
# *
# * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
# * I'll try to keep a version up to date. I can be reached as follows:
# * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
# */
Vixie Cron V3.0
December 27, 1993
[V2.2 was some time in 1992]
[V2.1 was May 29, 1991]
[V2.0 was July 5, 1990]
[V2.0-beta was December 9, 1988]
[V1.0 was May 6, 1987]
Paul Vixie
This is a version of 'cron' that is known to run on BSD 4.[23] systems. It
is functionally based on the SysV cron, which means that each user can have
their own crontab file (all crontab files are stored in a read-protected
directory, usually /var/cron/tabs). No direct support is provided for
'at'; you can continue to run 'atrun' from the crontab as you have been
doing. If you don't have atrun (i.e., System V) you are in trouble.
A messages is logged each time a command is executed; also, the files
"allow" and "deny" in /var/cron can be used to control access to the
"crontab" command (which installs crontabs). It hasn't been tested on
SysV, although some effort has gone into making the port an easy one.
This is more or less the copyright that USENET contributed software usually
has. Since ATT couldn't use this version if they had to freely distribute
source, and since I'd love to see them use it, I'll offer some rediculously
low license fee just to have them take it. In the unlikely event that they
do this, I will continue to support and distribute the pseudo-PD version, so
please, don't flame me for wanting my work to see a wider distribution.
To use this: Sorry, folks, there is no cutesy 'Configure' script. You'll
have to go edit a couple of files... So, here's the checklist:
Read all the FEATURES, INSTALL, and CONVERSION files
Edit config.h
Edit Makefile
(both of these files have instructions inside; note that
some things in config.h are definable in Makefile and are
therefore surrounded by #ifndef...#endif)
'make'
'su' and 'make install'
(you may have to install the man pages by hand)
kill your existing cron process
(actually you can run your existing cron if you want, but why?)
build new crontabs using /usr/lib/{crontab,crontab.local}
(either put them all in "root"'s crontab, or divide it up
and rip out all the 'su' commands, collapse the lengthy
lists into ranges with steps -- basically, this step is
as much work as you want to make it)
start up the new cron
(must be done as root)
watch it. test it with 'crontab -r' and watch the daemon track your
changes.
if you like it, change your /etc/{rc,rc.local} to use it instead of
the old one.
$Id: README,v 2.3 1993/12/28 08:34:43 vixie Exp $

View File

@ -0,0 +1,4 @@
Sources substantially rearranged 26 Aug 94, Jordan Hubbard. Content
remains faithful to the original 3.0 cron source from Paul Vixie, but
any bugs introduced by this reorganization or the port to FreeBSD remain
purely my own. - Jordan

29
usr.sbin/cron/doc/THANKS Normal file
View File

@ -0,0 +1,29 @@
15 January 1990
Paul Vixie
Many people have contributed to cron. Many more than I can remember, in fact.
Rich Salz and Carl Gutekunst were each of enormous help to me in V1; Carl for
helping me understand UNIX well enough to write it, and Rich for helping me
get the features right.
John Gilmore wrote me a wonderful review of V2, which took me a whole year to
answer even though it made me clean up some really awful things in the code.
(According to John the most awful things are still in here, of course.)
Paul Close made a suggestion which led to /etc/crond.pid and the mutex locking
on it. Kevin Braunsdorf of Purdue made a suggestion that led to @reboot and
its brothers and sisters; he also sent some diffs that lead cron toward compil-
ability with System V, though without at(1) capabilities, this cron isn't going
to be that useful on System V. Bob Alverson fixed a silly bug in the line
number counting. Brian Reid made suggestions which led to the run queue and
the source-file labelling in installed crontabs.
Scott Narveson ported V2 to a Sequent, and sent in the most useful single batch
of diffs I got from anybody. Changes attributable to Scott are:
-> sendmail won't time out if the command is slow to generate output
-> day-of-week names aren't off by one anymore
-> crontab says the right thing if you do something you shouldn't do
-> crontab(5) man page is longer and more informative
-> misc changes related to the side effects of fclose()
-> Sequent "universe" support added (may also help on Pyramids)
-> null pw_shell is dealt with now; default is /bin/sh

View File

@ -0,0 +1,8 @@
LIB= cron
SRCS= entry.c env.c misc.c
CFLAGS+= -I${.CURDIR}/../cron
NOSHARED= yes
NOPROFILE= yes
.include <bsd.lib.mk>

233
usr.sbin/cron/lib/compat.c Normal file
View File

@ -0,0 +1,233 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
#if !defined(lint) && !defined(LINT)
static char rcsid[] = "$Id: compat.c,v 1.6 1994/01/15 20:43:43 vixie Exp $";
#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 <limits.h>
#endif
#if defined(NEED_SETSID) && defined(BSD)
# include <sys/ioctl.h>
#endif
#include <errno.h>
/* 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;
temp = malloc(strlen(str) + 1);
(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("/dev/tty", 2)) >= 0)
{
(void) ioctl(fd, TIOCNOTTY, (char*)0);
(void) close(fd);
}
# else /*BSD*/
newpgrp = setpgrp();
(void) close(STDIN); (void) open("/dev/null", 0);
(void) close(STDOUT); (void) open("/dev/null", 1);
(void) close(STDERR); (void) open("/dev/null", 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 <Jarkko.Hietaniemi@hut.fi>
* *) 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 "<sys/file.h>" 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("%s=%s", name, value);
return putenv(tmp); /* intentionally orphan 'tmp' storage */
}
#endif

507
usr.sbin/cron/lib/entry.c Normal file
View File

@ -0,0 +1,507 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
#if !defined(lint) && !defined(LINT)
static char rcsid[] = "$Id: entry.c,v 2.12 1994/01/17 03:20:37 vixie Exp $";
#endif
/* vix 26jan87 [RCS'd; rest of log is in RCS file]
* vix 01jan87 [added line-level error recovery]
* vix 31dec86 [added /step to the from-to range, per bob@acornrc]
* vix 30dec86 [written]
*/
#include "cron.h"
typedef enum ecode {
e_none, e_minute, e_hour, e_dom, e_month, e_dow,
e_cmd, e_timespec, e_username
} ecode_e;
static char get_list __P((bitstr_t *, int, int, char *[], int, FILE *)),
get_range __P((bitstr_t *, int, int, char *[], int, FILE *)),
get_number __P((int *, int, char *[], int, FILE *));
static int set_element __P((bitstr_t *, int, int, int));
static char *ecodes[] =
{
"no error",
"bad minute",
"bad hour",
"bad day-of-month",
"bad month",
"bad day-of-week",
"bad command",
"bad time specifier",
"bad username",
};
void
free_entry(e)
entry *e;
{
free(e->cmd);
env_free(e->envp);
free(e);
}
/* return NULL if eof or syntax error occurs;
* otherwise return a pointer to a new entry.
*/
entry *
load_entry(file, error_func, pw, envp)
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.
*
* the entry is also parsed here.
*
* syntax:
* user crontab:
* minutes hours doms months dows cmd\n
* system crontab (/etc/crontab):
* minutes hours doms months dows USERNAME cmd\n
*/
ecode_e ecode = e_none;
entry *e;
int ch;
char cmd[MAX_COMMAND];
char envstr[MAX_ENVSTR];
Debug(DPARS, ("load_entry()...about to eat comments\n"))
skip_comments(file);
ch = get_char(file);
if (ch == EOF)
return NULL;
/* ch is now the first useful character of a useful line.
* it may be an @special or it may be the first character
* of a list of minutes.
*/
e = (entry *) calloc(sizeof(entry), sizeof(char));
if (ch == '@') {
/* all of these should be flagged and load-limited; i.e.,
* instead of @hourly meaning "0 * * * *" it should mean
* "close to the front of every hour but not 'til the
* system load is low". Problems are: how do you know
* what "low" means? (save me from /etc/cron.conf!) and:
* how to guarantee low variance (how low is low?), which
* means how to we run roughly every hour -- seems like
* we need to keep a history or let the first hour set
* the schedule, which means we aren't load-limited
* anymore. too much for my overloaded brain. (vix, jan90)
* HINT
*/
ch = get_string(cmd, MAX_COMMAND, file, " \t\n");
if (!strcmp("reboot", cmd)) {
e->flags |= WHEN_REBOOT;
} else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
bit_set(e->minute, 0);
bit_set(e->hour, 0);
bit_set(e->dom, 0);
bit_set(e->month, 0);
bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
} else if (!strcmp("monthly", cmd)) {
bit_set(e->minute, 0);
bit_set(e->hour, 0);
bit_set(e->dom, 0);
bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
} else if (!strcmp("weekly", cmd)) {
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_set(e->dow, 0);
} else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
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)) {
bit_set(e->minute, 0);
bit_set(e->hour, (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;
}
} else {
Debug(DPARS, ("load_entry()...about to parse numerics\n"))
ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
PPC_NULL, ch, file);
if (ch == EOF) {
ecode = e_minute;
goto eof;
}
/* hours
*/
ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR,
PPC_NULL, ch, file);
if (ch == EOF) {
ecode = e_hour;
goto eof;
}
/* DOM (days of month)
*/
if (ch == '*')
e->flags |= DOM_STAR;
ch = get_list(e->dom, FIRST_DOM, LAST_DOM,
PPC_NULL, ch, file);
if (ch == EOF) {
ecode = e_dom;
goto eof;
}
/* month
*/
ch = get_list(e->month, FIRST_MONTH, LAST_MONTH,
MonthNames, ch, file);
if (ch == EOF) {
ecode = e_month;
goto eof;
}
/* DOW (days of week)
*/
if (ch == '*')
e->flags |= DOW_STAR;
ch = get_list(e->dow, FIRST_DOW, LAST_DOW,
DowNames, ch, file);
if (ch == EOF) {
ecode = e_dow;
goto eof;
}
}
/* make sundays equivilent */
if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) {
bit_set(e->dow, 0);
bit_set(e->dow, 7);
}
/* ch is the first character of a command, or a username */
unget_char(ch, file);
if (!pw) {
char *username = cmd; /* temp buffer */
Debug(DPARS, ("load_entry()...about to parse username\n"))
ch = get_string(username, MAX_COMMAND, file, " \t");
Debug(DPARS, ("load_entry()...got %s\n",username))
if (ch == EOF) {
ecode = e_cmd;
goto eof;
}
pw = getpwnam(username);
if (pw == NULL) {
ecode = e_username;
goto eof;
}
Debug(DPARS, ("load_entry()...uid %d, gid %d\n",e->uid,e->gid))
}
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 (!env_get("SHELL", e->envp)) {
sprintf(envstr, "SHELL=%s", _PATH_BSHELL);
e->envp = env_set(e->envp, envstr);
}
if (!env_get("HOME", e->envp)) {
sprintf(envstr, "HOME=%s", pw->pw_dir);
e->envp = env_set(e->envp, envstr);
}
if (!env_get("PATH", e->envp)) {
sprintf(envstr, "PATH=%s", _PATH_DEFPATH);
e->envp = env_set(e->envp, envstr);
}
sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name);
e->envp = env_set(e->envp, envstr);
#if defined(BSD)
sprintf(envstr, "%s=%s", "USER", pw->pw_name);
e->envp = env_set(e->envp, envstr);
#endif
Debug(DPARS, ("load_entry()...about to parse command\n"))
/* Everything up to the next \n or EOF is part of the command...
* too bad we don't know in advance how long it will be, since we
* need to malloc a string for it... so, we limit it to MAX_COMMAND.
* XXX - should use realloc().
*/
ch = get_string(cmd, MAX_COMMAND, file, "\n");
/* a file without a \n before the EOF is rude, so we'll complain...
*/
if (ch == EOF) {
ecode = e_cmd;
goto eof;
}
/* got the command in the 'cmd' string; save it in *e.
*/
e->cmd = strdup(cmd);
Debug(DPARS, ("load_entry()...returning successfully\n"))
/* success, fini, return pointer to the entry we just created...
*/
return e;
eof:
free(e);
if (ecode != e_none && error_func)
(*error_func)(ecodes[(int)ecode]);
while (ch != EOF && ch != '\n')
ch = get_char(file);
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 */
{
register int done;
/* we know that we point to a non-blank character here;
* must do a Skip_Blanks before we exit, so that the
* next call (or the code that picks up the cmd) can
* assume the same thing.
*/
Debug(DPARS|DEXT, ("get_list()...entered\n"))
/* list = range {"," range}
*/
/* clear the bit string, since the default is 'off'.
*/
bit_nclear(bits, 0, (high-low+1));
/* process all ranges
*/
done = FALSE;
while (!done) {
ch = get_range(bits, low, high, names, ch, file);
if (ch == ',')
ch = get_char(file);
else
done = TRUE;
}
/* exiting. skip to some blanks, then skip over the blanks.
*/
Skip_Nonblanks(ch, file)
Skip_Blanks(ch, file)
Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch))
return ch;
}
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 */
{
/* range = number | number "-" number [ "/" number ]
*/
register int i;
auto int num1, num2, num3;
Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n"))
if (ch == '*') {
/* '*' means "first-last" but can still be modified by /step
*/
num1 = low;
num2 = high;
ch = get_char(file);
if (ch == EOF)
return EOF;
} else {
if (EOF == (ch = get_number(&num1, low, names, ch, file)))
return EOF;
if (ch != '-') {
/* not a range, it's a single number.
*/
if (EOF == set_element(bits, low, high, num1))
return EOF;
return ch;
} else {
/* eat the dash
*/
ch = get_char(file);
if (ch == EOF)
return EOF;
/* get the number following the dash
*/
ch = get_number(&num2, low, names, ch, file);
if (ch == EOF)
return EOF;
}
}
/* check for step size
*/
if (ch == '/') {
/* eat the slash
*/
ch = get_char(file);
if (ch == EOF)
return EOF;
/* get the step size -- note: we don't pass the
* names here, because the number is not an
* element id, it's a step size. 'low' is
* sent as a 0 since there is no offset either.
*/
ch = get_number(&num3, 0, PPC_NULL, ch, file);
if (ch == EOF)
return EOF;
} else {
/* no step. default==1.
*/
num3 = 1;
}
/* range. set all elements from num1 to num2, stepping
* by num3. (the step is a downward-compatible extension
* proposed conceptually by bob@acornrc, syntactically
* designed then implmented by paul vixie).
*/
for (i = num1; i <= num2; i += num3)
if (EOF == set_element(bits, low, high, i))
return EOF;
return ch;
}
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;
*pc++ = ch;
if (!isdigit(ch))
all_digits = FALSE;
ch = get_char(file);
}
*pc = '\0';
/* try to find the name in the name list
*/
if (names) {
for (i = 0; names[i] != NULL; i++) {
Debug(DPARS|DEXT,
("get_num, compare(%s,%s)\n", names[i], temp))
if (!strcasecmp(names[i], temp)) {
*numptr = i+low;
return ch;
}
}
}
/* no name list specified, or there is one and our string isn't
* in it. either way: if it's all digits, use its magnitude.
* otherwise, it's an error.
*/
if (all_digits) {
*numptr = atoi(temp);
return ch;
}
return EOF;
}
static int
set_element(bits, low, high, number)
bitstr_t *bits; /* one bit per flag, default=FALSE */
int low;
int high;
int number;
{
Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number))
if (number < low || number > high)
return EOF;
bit_set(bits, (number-low));
return OK;
}

178
usr.sbin/cron/lib/env.c Normal file
View File

@ -0,0 +1,178 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
#if !defined(lint) && !defined(LINT)
static char rcsid[] = "$Id: env.c,v 2.6 1994/01/15 20:43:43 vixie Exp $";
#endif
#include "cron.h"
char **
env_init()
{
register char **p = (char **) malloc(sizeof(char **));
p[0] = NULL;
return (p);
}
void
env_free(envp)
char **envp;
{
char **p;
for (p = envp; *p; 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 */
for (i = 0; i < count; i++)
p[i] = strdup(envp[i]);
p[count] = NULL;
return (p);
}
char **
env_set(envp, envstr)
char **envp;
char *envstr;
{
register int count, found;
register char **p;
/*
* count the number of elements, including the null pointer;
* also set 'found' to -1 or index of entry if already in here.
*/
found = -1;
for (count = 0; envp[count] != NULL; count++) {
if (!strcmp_until(envp[count], envstr, '='))
found = count;
}
count++; /* for the NULL */
if (found != -1) {
/*
* it exists already, so just free the existing setting,
* save our new one there, and return the existing array.
*/
free(envp[found]);
envp[found] = strdup(envstr);
return (envp);
}
/*
* it doesn't exist yet, so resize the array, move null pointer over
* one, save our string over the old null pointer, and return resized
* array.
*/
p = (char **) realloc((void *) envp,
(unsigned) ((count+1) * sizeof(char **)));
p[count] = p[count-1];
p[count-1] = strdup(envstr);
return (p);
}
/* 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_TEMPSTR], val[MAX_ENVSTR];
int fields;
filepos = ftell(f);
fileline = LineNumber;
skip_comments(f);
if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n"))
return (ERR);
Debug(DPARS, ("load_env, read <%s>\n", envstr))
name[0] = val[0] = '\0';
fields = sscanf(envstr, "%[^ =] = %[^\n#]", name, val);
if (fields != 2) {
Debug(DPARS, ("load_env, not 2 fields (%d)\n", fields))
fseek(f, filepos, 0);
Set_LineNum(fileline);
return (FALSE);
}
/* 2 fields from scanf; looks like an env setting
*/
/*
* process value string
*/
/*local*/{
int len = strdtb(val);
if (len >= 2) {
if (val[0] == '\'' || val[0] == '"') {
if (val[len-1] == val[0]) {
val[len-1] = '\0';
(void) strcpy(val, val+1);
}
}
}
}
(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;
while (p = *envp++) {
if (!(q = strchr(p, '=')))
continue;
if ((q - p) == len && !strncmp(p, name, len))
return (q+1);
}
return (NULL);
}

664
usr.sbin/cron/lib/misc.c Normal file
View File

@ -0,0 +1,664 @@
/* 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 <paul@vix.com> uunet!decwrl!vixie!paul
*/
#if !defined(lint) && !defined(LINT)
static char rcsid[] = "$Id: misc.c,v 2.9 1994/01/15 20:43:43 vixie Exp $";
#endif
/* vix 26jan87 [RCS has the rest of the log]
* vix 30dec86 [written]
*/
#include "cron.h"
#if SYS_TIME_H
# include <sys/time.h>
#else
# include <time.h>
#endif
#include <sys/file.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#if defined(SYSLOG)
# include <syslog.h>
#endif
#if defined(LOG_DAEMON) && !defined(LOG_CRON)
#define LOG_CRON LOG_DAEMON
#endif
static int LogFD = ERR;
int
strcmp_until(left, right, until)
char *left;
char *right;
int until;
{
register int diff;
while (*left && *left != until && *left == *right) {
left++;
right++;
}
if ((*left=='\0' || *left == until) &&
(*right=='\0' || *right == until)) {
diff = 0;
} else {
diff = *left - *right;
}
return diff;
}
/* strdtb(s) - delete trailing blanks in string 's' and return new length
*/
int
strdtb(s)
char *s;
{
char *x = s;
/* scan forward to the null
*/
while (*x)
x++;
/* scan backward to either the first character before the string,
* or the last non-blank in the string, whichever comes first.
*/
do {x--;}
while (x >= s && isspace(*x));
/* one character beyond where we stopped above is where the null
* goes.
*/
*++x = '\0';
/* the difference between the position of the null character and
* the position of the first character of the string is the length.
*/
return x - s;
}
int
set_debug_flags(flags)
char *flags;
{
/* debug flags are of the form flag[,flag ...]
*
* if an error occurs, print a message to stdout and return FALSE.
* otherwise return TRUE after setting ERROR_FLAGS.
*/
#if !DEBUGGING
printf("this program was compiled without debugging enabled\n");
return FALSE;
#else /* DEBUGGING */
char *pc = flags;
DebugFlags = 0;
while (*pc) {
char **test;
int mask;
/* try to find debug flag name in our list.
*/
for ( test = DebugFlagNames, mask = 1;
*test && strcmp_until(*test, pc, ',');
test++, mask <<= 1
)
;
if (!*test) {
fprintf(stderr,
"unrecognized debug flag <%s> <%s>\n",
flags, pc);
return FALSE;
}
DebugFlags |= mask;
/* skip to the next flag
*/
while (*pc && *pc != ',')
pc++;
if (*pc == ',')
pc++;
}
if (DebugFlags) {
int flag;
fprintf(stderr, "debug flags enabled:");
for (flag = 0; DebugFlagNames[flag]; flag++)
if (DebugFlags & (1 << flag))
fprintf(stderr, " %s", DebugFlagNames[flag]);
fprintf(stderr, "\n");
}
return TRUE;
#endif /* DEBUGGING */
}
void
set_cron_uid()
{
#if defined(BSD) || defined(POSIX)
if (seteuid(ROOT_UID) < OK) {
perror("seteuid");
exit(ERROR_EXIT);
}
#else
if (setuid(ROOT_UID) < OK) {
perror("setuid");
exit(ERROR_EXIT);
}
#endif
}
void
set_cron_cwd()
{
struct stat sb;
/* first check for CRONDIR ("/var/cron" or some such)
*/
if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
perror(CRONDIR);
if (OK == mkdir(CRONDIR, 0700)) {
fprintf(stderr, "%s: created\n", CRONDIR);
stat(CRONDIR, &sb);
} else {
fprintf(stderr, "%s: ", CRONDIR);
perror("mkdir");
exit(ERROR_EXIT);
}
}
if (!(sb.st_mode & S_IFDIR)) {
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) {
perror(SPOOL_DIR);
if (OK == mkdir(SPOOL_DIR, 0700)) {
fprintf(stderr, "%s: created\n", SPOOL_DIR);
stat(SPOOL_DIR, &sb);
} else {
fprintf(stderr, "%s: ", SPOOL_DIR);
perror("mkdir");
exit(ERROR_EXIT);
}
}
if (!(sb.st_mode & S_IFDIR)) {
fprintf(stderr, "'%s' is not a directory, bailing out.\n",
SPOOL_DIR);
exit(ERROR_EXIT);
}
}
/* acquire_daemonlock() - write our PID into /etc/cron.pid, unless
* another daemon is already running, which we detect here.
*
* note: main() calls us twice; once before forking, once after.
* we maintain static storage of the file pointer so that we
* can rewrite our PID into the PIDFILE after the fork.
*
* it would be great if fflush() disassociated the file buffer.
*/
void
acquire_daemonlock(closeflag)
int closeflag;
{
static FILE *fp = NULL;
if (closeflag && fp) {
fclose(fp);
fp = NULL;
return;
}
if (!fp) {
char pidfile[MAX_FNAME];
char buf[MAX_TEMPSTR];
int fd, otherpid;
(void) sprintf(pidfile, PIDFILE, PIDDIR);
if ((-1 == (fd = open(pidfile, O_RDWR|O_CREAT, 0644)))
|| (NULL == (fp = fdopen(fd, "r+")))
) {
sprintf(buf, "can't open or create %s: %s",
pidfile, strerror(errno));
fprintf(stderr, "%s: %s\n", ProgramName, buf);
log_it("CRON", getpid(), "DEATH", buf);
exit(ERROR_EXIT);
}
if (flock(fd, LOCK_EX|LOCK_NB) < OK) {
int save_errno = errno;
fscanf(fp, "%d", &otherpid);
sprintf(buf, "can't lock %s, otherpid may be %d: %s",
pidfile, otherpid, strerror(save_errno));
fprintf(stderr, "%s: %s\n", ProgramName, buf);
log_it("CRON", getpid(), "DEATH", buf);
exit(ERROR_EXIT);
}
(void) fcntl(fd, F_SETFD, 1);
}
rewind(fp);
fprintf(fp, "%d\n", getpid());
fflush(fp);
(void) ftruncate(fileno(fp), ftell(fp));
/* abandon fd and fp even though the file is open. we need to
* keep it open and locked, but we don't need the handles elsewhere.
*/
}
/* get_char(file) : like getc() but increment LineNumber on newlines
*/
int
get_char(file)
FILE *file;
{
int ch;
ch = getc(file);
if (ch == '\n')
Set_LineNum(LineNumber + 1)
return ch;
}
/* unget_char(ch, file) : like ungetc but do LineNumber processing
*/
void
unget_char(ch, file)
int ch;
FILE *file;
{
ungetc(ch, file);
if (ch == '\n')
Set_LineNum(LineNumber - 1)
}
/* get_string(str, max, file, termstr) : like fgets() but
* (1) has terminator string which should include \n
* (2) will always leave room for the null
* (3) uses get_char() so LineNumber will be accurate
* (4) returns EOF or terminating character, whichever
*/
int
get_string(string, size, file, terms)
char *string;
int size;
FILE *file;
char *terms;
{
int ch;
while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) {
if (size > 1) {
*string++ = (char) ch;
size--;
}
}
if (size > 0)
*string = '\0';
return ch;
}
/* skip_comments(file) : read past comment (if any)
*/
void
skip_comments(file)
FILE *file;
{
int ch;
while (EOF != (ch = get_char(file))) {
/* ch is now the first character of a line.
*/
while (ch == ' ' || ch == '\t')
ch = get_char(file);
if (ch == EOF)
break;
/* ch is now the first non-blank character of a line.
*/
if (ch != '\n' && ch != '#')
break;
/* ch must be a newline or comment as first non-blank
* character on a line.
*/
while (ch != '\n' && ch != EOF)
ch = get_char(file);
/* ch is now the newline of a line which we're going to
* ignore.
*/
}
if (ch != EOF)
unget_char(ch, file);
}
/* int in_file(char *string, FILE *file)
* return TRUE if one of the lines in file matches string exactly,
* FALSE otherwise.
*/
static int
in_file(string, file)
char *string;
FILE *file;
{
char line[MAX_TEMPSTR];
rewind(file);
while (fgets(line, MAX_TEMPSTR, file)) {
if (line[0] != '\0')
line[strlen(line)-1] = '\0';
if (0 == strcmp(line, string))
return TRUE;
}
return FALSE;
}
/* int allowed(char *username)
* returns TRUE if (ALLOW_FILE exists and user is listed)
* or (DENY_FILE exists and user is NOT listed)
* or (neither file exists but user=="root" so it's okay)
*/
int
allowed(username)
char *username;
{
static int init = FALSE;
static FILE *allow, *deny;
if (!init) {
init = TRUE;
#if defined(ALLOW_FILE) && defined(DENY_FILE)
allow = fopen(ALLOW_FILE, "r");
deny = fopen(DENY_FILE, "r");
Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny))
#else
allow = NULL;
deny = NULL;
#endif
}
if (allow)
return (in_file(username, allow));
if (deny)
return (!in_file(username, deny));
#if defined(ALLOW_ONLY_ROOT)
return (strcmp(username, ROOT_USER) == 0);
#else
return TRUE;
#endif
}
void
log_it(username, xpid, event, detail)
char *username;
int xpid;
char *event;
char *detail;
{
PID_T pid = xpid;
#if defined(LOG_FILE)
char *msg;
TIME_T now = time((TIME_T) 0);
register 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.
*/
msg = malloc(strlen(username)
+ strlen(event)
+ strlen(detail)
+ MAX_TEMPSTR);
if (LogFD < OK) {
LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
if (LogFD < OK) {
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)
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);
# else
openlog(ProgramName, LOG_PID);
# endif
syslog_open = TRUE; /* assume openlog success */
}
syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail);
#endif /*SYSLOG*/
#if DEBUGGING
if (DebugFlags) {
fprintf(stderr, "log_it: (%s %d) %s (%s)\n",
username, pid, event, detail);
}
#endif
}
void
log_close() {
if (LogFD != ERR) {
close(LogFD);
LogFD = ERR;
}
}
/* two 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 */
{
static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */
static int retsel = 0;
register char *rb, *rp;
/* select a return buffer */
retsel = 1-retsel;
rb = &retbuf[retsel][0];
rp = rb;
/* skip any leading terminators */
while (*s && (NULL != strchr(t, *s))) {
s++;
}
/* copy until next terminator or full buffer */
while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
*rp++ = *s++;
}
/* finish the return-string and return it */
*rp = '\0';
return rb;
}
/* warning:
* heavily ascii-dependent.
*/
void
mkprint(dst, src, len)
register char *dst;
register unsigned char *src;
register int len;
{
while (len-- > 0)
{
register unsigned char ch = *src++;
if (ch < ' ') { /* control character */
*dst++ = '^';
*dst++ = ch + '@';
} else if (ch < 0177) { /* printable */
*dst++ = ch;
} else if (ch == 0177) { /* delete/rubout */
*dst++ = '^';
*dst++ = '?';
} else { /* parity character */
sprintf(dst, "\\%03o", ch);
dst += 4;
}
}
*dst = '\0';
}
/* warning:
* returns a pointer to malloc'd storage, you must call free yourself.
*/
char *
mkprints(src, len)
register unsigned char *src;
register unsigned int len;
{
register char *dst = malloc(len*4 + 1);
mkprint(dst, src, len);
return dst;
}
#ifdef MAIL_DATE
/* Sat, 27 Feb 93 11:44:51 CST
* 123456789012345678901234567
*/
char *
arpadate(clock)
time_t *clock;
{
time_t t = clock ?*clock :time(0L);
struct tm *tm = localtime(&t);
static char ret[30]; /* zone name might be >3 chars */
(void) sprintf(ret, "%s, %2d %s %2d %02d:%02d:%02d %s",
DowNames[tm->tm_wday],
tm->tm_mday,
MonthNames[tm->tm_mon],
tm->tm_year,
tm->tm_hour,
tm->tm_min,
tm->tm_sec,
TZONE(*tm));
return ret;
}
#endif /*MAIL_DATE*/
#ifdef HAVE_SAVED_SUIDS
static int save_euid;
int swap_uids() { save_euid = geteuid(); return seteuid(getuid()); }
int swap_uids_back() { return seteuid(save_euid); }
#else /*HAVE_SAVED_UIDS*/
int swap_uids() { return setreuid(geteuid(), getuid()); }
int swap_uids_back() { return swap_uids(); }
#endif /*HAVE_SAVED_UIDS*/