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:
parent
e84e5d655f
commit
84f33dea62
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/cvs2svn/branches/cron/; revision=2311
3
usr.sbin/cron/Makefile
Normal file
3
usr.sbin/cron/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
SUBDIR= lib cron crontab
|
||||
|
||||
.include <bsd.subdir.mk>
|
8
usr.sbin/cron/cron/Makefile
Normal file
8
usr.sbin/cron/cron/Makefile
Normal 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>
|
168
usr.sbin/cron/cron/bitstring.3
Normal file
168
usr.sbin/cron/cron/bitstring.3
Normal 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)
|
122
usr.sbin/cron/cron/bitstring.h
Normal file
122
usr.sbin/cron/cron/bitstring.h
Normal 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
137
usr.sbin/cron/cron/compat.h
Normal 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
|
86
usr.sbin/cron/cron/config.h
Normal file
86
usr.sbin/cron/cron/config.h
Normal 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
61
usr.sbin/cron/cron/cron.8
Normal 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
301
usr.sbin/cron/cron/cron.c
Normal 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
277
usr.sbin/cron/cron/cron.h
Normal 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*/
|
261
usr.sbin/cron/cron/database.c
Normal file
261
usr.sbin/cron/cron/database.c
Normal 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);
|
||||
}
|
||||
}
|
501
usr.sbin/cron/cron/do_command.c
Normal file
501
usr.sbin/cron/cron/do_command.c
Normal 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
|
||||
}
|
145
usr.sbin/cron/cron/externs.h
Normal file
145
usr.sbin/cron/cron/externs.h
Normal 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
74
usr.sbin/cron/cron/job.c
Normal 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;
|
||||
}
|
81
usr.sbin/cron/cron/pathnames.h
Normal file
81
usr.sbin/cron/cron/pathnames.h
Normal 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
166
usr.sbin/cron/cron/popen.c
Normal 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));
|
||||
}
|
23
usr.sbin/cron/cron/putman.sh
Normal file
23
usr.sbin/cron/cron/putman.sh
Normal 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
102
usr.sbin/cron/cron/user.c
Normal 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;
|
||||
}
|
10
usr.sbin/cron/crontab/Makefile
Normal file
10
usr.sbin/cron/crontab/Makefile
Normal 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>
|
100
usr.sbin/cron/crontab/crontab.1
Normal file
100
usr.sbin/cron/crontab/crontab.1
Normal 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>
|
188
usr.sbin/cron/crontab/crontab.5
Normal file
188
usr.sbin/cron/crontab/crontab.5
Normal 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>
|
624
usr.sbin/cron/crontab/crontab.c
Normal file
624
usr.sbin/cron/crontab/crontab.c
Normal 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
155
usr.sbin/cron/doc/CHANGES
Normal 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)
|
85
usr.sbin/cron/doc/CONVERSION
Normal file
85
usr.sbin/cron/doc/CONVERSION
Normal 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.
|
||||
|
84
usr.sbin/cron/doc/FEATURES
Normal file
84
usr.sbin/cron/doc/FEATURES
Normal 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
87
usr.sbin/cron/doc/INSTALL
Normal 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
475
usr.sbin/cron/doc/MAIL
Normal 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! >>
|
128
usr.sbin/cron/doc/Makefile.vixie
Normal file
128
usr.sbin/cron/doc/Makefile.vixie
Normal 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
72
usr.sbin/cron/doc/README
Normal 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 $
|
4
usr.sbin/cron/doc/README.1ST
Normal file
4
usr.sbin/cron/doc/README.1ST
Normal 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
29
usr.sbin/cron/doc/THANKS
Normal 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
|
8
usr.sbin/cron/lib/Makefile
Normal file
8
usr.sbin/cron/lib/Makefile
Normal 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
233
usr.sbin/cron/lib/compat.c
Normal 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
507
usr.sbin/cron/lib/entry.c
Normal 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
178
usr.sbin/cron/lib/env.c
Normal 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
664
usr.sbin/cron/lib/misc.c
Normal 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*/
|
Loading…
Reference in New Issue
Block a user