cron for unix-pc file 3 of 3
Donald Lashomb
donlash at uncle.UUCP
Fri Dec 29 14:17:31 AEST 1989
For all who requested my cron program:
here's part 3 of 3
-Don donlash at uncle.UUCP
#! /bin/sh"
# -------------------- CUT HERE ----------------------"
#! /bin/sh"
# This is a shar file. To unbundle it, remove everything"
# above including the 'CUT HERE' line using your editor."
# Then type 'sh thisfile'. Your shell will unbundle it."
echo "extracting daemon.c ( 13919 chars)"
sed 's/^X//' <<'SHAR_EOF' >daemon.c
X/* cron(1M) - cron facility daemon */
X
X/* ------------------------- NOTICE -----------------------------
X
X at(1) batch(1) cronjob(1) crtabj(1) crontab(1) cron(1M)
X (c) copyright April 10, 1989 by Donald Lashomb
X
X This program is free. Use it, modify it, copy it, give a copy
X to a friend; I simply request the following provisions be observed:
X
X 1. My name as original author and this notice remain intact.
X 2. This program (or any modification of it) is not to be sold
X for profit.
X 3. If this program is included in commercial products, there be
X no charge for it.
X 4. This program must be distributed with source code. Compiled-
X only or binary-only distribution of this program is not allowed.
X The administrator of any system that uses this program must have
X full access to the source code.
X 5. If you enhance this program, discover a bug, have any comments
X about it (or flames) please let me know.
X
X Donald Lashomb
X Main Street
X Cranberry Lake, NY 12927
X
X -------------------------------------------------------------- */
X
X
X/* hint- catching errors by comparing to 0 rather than -1,
X is (probably) more efficent on most processors */
X
X#include <stdio.h>
X#include <string.h>
X#include <errno.h>
X#include <fcntl.h>
X#include <pwd.h>
X#include <time.h>
X#include <sys/signal.h>
X#include <sys/dir.h>
X#include "cron.h"
X#include "job.h"
X
X#define BADSIG ((int (*)()) -1)
X
Xextern int errno;
Xextern int sys_nerr;
Xextern char *sys_errlist[];
Xextern char **environ;
Xchar *getenv();
Xvoid exit();
Xvoid perror();
Xint (*signal())();
Xunsigned alarm();
Xlong atol();
Xlong time();
Xlong ulimit();
Xchar *sbrk();
Xstruct passwd *getpwuid();
Xstruct passwd *getpwnam();
Xunsigned sleep(); /* user tries - not daemon */
X
X/* routines in fifo.c */
X
Xvoid mkfifo();
Xint openfifo();
Xint rdfifo();
Xint wrfifo();
X
X/* routines in memlist.c */
X
Xextern memJOB *jtodo;
Xvoid initlist();
Xvoid insert();
Xvoid intodo();
Xvoid infree();
XmemJOB *rmtodo();
XmemJOB *deltodo();
X/*int numfree();-----not used */
Xint taken();
X
X/* routines in log.c */
X
Xvoid openlog();
Xvoid cleanlog();
Xvoid logjob();
Xvoid logtime();
Xvoid logmsg2();
X
X/* routine in resched.c */
X
Xlong resched();
X
X
X/* global variables ------------------------------------------- */
X
Xstruct passwd *cronpw; /* cron's login stuff */
X
Xint jnum,fnam; /* fifo fildes */
Xint nul; /* /dev/null fildes */
X
XJOB jjj; /* default JOB buffer */
XmemJOB *mjp; /* current job ptr */
Xchar fname[DIRSIZ+2]; /* job file name */
Xint fd0; /* job fildes */
X
Xchar line[LINESIZ]; /* general line buff */
Xlong catchup,start,nap,now; /* time for main() */
Xint kids,maxkids; /* max proc.s to fork */
X
Xchar *tz; /* TZ env override */
X
Xstatic char copyright[] = "cron(1M) - (c)1989 D.Lashomb";
X
X
X/* misc routines ---------------------------------------------- */
X
Xvoid fatal(str)
X char *str;
X {
X int n;
X char *errmsg;
X if((errno > 0) && (errno < sys_nerr))
X errmsg = sys_errlist[errno];
X else
X errmsg = "(no sys errmsg)";
X sprintf(line,
X "FATAL cron daemon: can't %s errno= %d %s\n",
X str,errno,errmsg);
X n = strlen(line);
X write(2,line,(unsigned)n);
X exit(1);
X }
X
Xdummy()
X {
X /* no-op function */
X }
X
X/* inplementation dependent */
Xalign()
X {
X int x;
X if((x = (int)sbrk(0)) == -1)
X return(-1);
X if((x %= sizeof(char **)) != 0)
X if((int)sbrk((int)(sizeof(char **) - x)) == -1)
X return(-1);
X return(0);
X }
X
X#define alloc(n) ((char **)sbrk(n))
X
Xvoid wrfifofail(pfd)
X int pfd;
X {
X /* just record loss of jobnumber for now */
X logmsg2("fifo full, jobnumber lost",jjj.jobn);
X }
X
X
X/* -----------------------------------------------------------
X Remove a job:
X
X This part of the code is for the daemon. The job file is
X unlinked so its name is free to be used over. However, it
X is not actually removed until it is closed when the user's
X shell exits. The job slot is free to be used again, so it
X is put on the free list and sent into the JNUM fifo for any
X user that wants it.
X ----------------------------------------------------------- */
X
Xvoid rmvjob(msg)
X char *msg; /* reason */
X {
X
X unlink(fname);
X infree(mjp);
X if(wrfifo(jnum,&jjj) == 0)
X wrfifofail(jnum);
X if(msg != NULL)
X logjob(msg);
X }
X
X/* close job file --------------------------------------------- */
X
Xvoid closejob()
X {
X if(close(fd0) != 0)
X fatal("close job file");
X }
X
X/* open job file ---------------------------------------------- */
X/* setup fd0 and jjj */
X
Xint openjob()
X {
X if((fd0=open(fname,O_RDONLY)) == -1) {
X rmvjob("can't open job file");
X return(0);
X }
X if((
X read(fd0,(char *)&jjj,JOBSIZ) != JOBSIZ) || (
X mjp->jobn != jjj.jobn) || (
X mjp->msg != jjj.msg) || (
X mjp->uid != jjj.uid)
X ) {
X closejob();
X rmvjob("file not= memory, file removed");
X return(0);
X }
X return(1);
X }
X
X/* set up environment ----------------------------------------- */
X/* return working dir and crontab command */
X
Xint readenv(shell,wd,cmd)
X char **shell;
X char **wd;
X char **cmd;
X {
X int i,n,z;
X char **p,*q;
X
X i = jjj.envc * sizeof(char *);
X z = jjj.envz;
X if((environ = p = alloc(i+z)) == (char **) -1)
X return(0);
X q = (char *)p + i;
X if(read(fd0,q,(unsigned)z) != z) /* file ptr --> ascii */
X return(0);
X n = 0;
X do {
X *p++ = q;
X while(++n, (*q++ != '\0')) ;
X }
X while(n < z);
X *cmd = *(--p); /* --> crontab cmd */
X *wd = *(--p); /* --> working dir */
X *shell = *(--p); /* --> user shell */
X *p = NULL; /* mark end of env */
X return(1);
X }
X
X/* -----------------------------------------------------------
X Execute a job:
X
X This part of the code is for the child process. Now what we
X got to do is get the user's shell running with the proper uid,
X gid, nice, process group and connect stdin to the job file,
X stdout and stderr piped to mail. Alarm signal is already set
X back to the default. Death_of_child signal reset to default.
X The environment is read from the job file and set up in memory
X as well as the current working directory which is chdir'd to.
X Execl is used rather than execlp so only compiled shells and
X mail programs are usable; this is done for efficiency and
X security reasons. If the forking and exec-ing of the user's
X processes fail, an indication of what happened is recorded in
X the log. The exit(errno) is used in case someday I make use
X of it. There is no return from this function!
X ----------------------------------------------------------- */
X
Xvoid execjob(shopt)
X char *shopt; /* shell option */
X {
X char *shell,*wd,*cmd;
X char *p;
X char *login;
X int pfd[2];
X int tries;
X
X setpgrp();
X nice(jjj.nice);
X umask(jjj.umask);
X ulimit(2,jjj.ulimit);
X
X if((
X signal(SIGCLD,SIG_DFL) != BADSIG) && (
X setgid(jjj.gid) == 0) && (
X setuid(jjj.uid) == 0) && (
X readenv(&shell,&wd,&cmd) != 0) && (
X chdir(wd) == 0) && (
X (login = getenv("LOGNAME")) != NULL) && (
X pipe(pfd) == 0) && (
X close(0) == 0) && (
X close(1) == 0) && (
X close(2) == 0)
X ) {
X
X /* begin if#1 */
X
X#ifdef TZOVRIDE
X if((p=getenv("TZ")) != NULL)
X strncpy(p,tz,strlen(p));
X#endif
X
X tries = MAXTRIES;
X while(1) {
X
X switch(fork()) {
X case 0:
X if((
X dup(fd0) == 0) && (
X dup(pfd[1]) == 1) && (
X dup(pfd[1]) == 2) && (
X close(fd0) == 0) && (
X close(pfd[0]) == 0) && (
X close(pfd[1]) == 0)
X ) {
X if(*shell == '\0')
X shell = "/bin/sh";
X if(*cmd == '\0')
X cmd = NULL;
X else
X logjob(cmd); /* CR_TAB */
X
X execl(shell,strrchr(shell,'/')+1,shopt,cmd,NULL);
X }
X logjob("can't exec");
X kill(0,SIGTERM);
X exit(errno);
X case -1:
X if(--tries >= 0) { /* try MAXTRIES times to fork: */
X sleep(10); /* sleep might screw-up alarm */
X continue; /* signals, but it's easy to use */
X }
X logjob("can't fork2");
X kill(0,SIGTERM);
X exit(errno);
X default:
X if((
X dup(pfd[0]) == 0) && (
X dup(nul) == 1) && (
X dup(nul) == 2) && (
X close(fd0) == 0) && (
X close(pfd[0]) == 0) && (
X close(pfd[1]) == 0)
X )
X execl("/bin/mail","mail",login,NULL);
X
X logjob("can't exec /bin/mail");
X kill(0,SIGTERM);
X exit(errno);
X }
X
X }
X /* end while */
X
X }
X /* end if#1 */
X logjob("can't setup");
X exit(errno);
X }
X
X
X/* -----------------------------------------------------------
X run job:
X
X Open job file, fork off process to execute the job, close it.
X Return 0 if job removed because can't open it.
X ----------------------------------------------------------- */
X
Xint runjob(shopt)
X char *shopt; /* shell option */
X {
X if(++kids > maxkids) {
X mjp->time = now + GRAINULARITY;
X intodo(mjp);
X return(0);
X }
X if(!openjob()) return(0);
X
X/* -----------------------------------------------------------
X At this point, the job file is open. We are ready to fork
X off a new process and run the job. The important variables
X at this point are:
X fd0 - file decsriptor of open job file
X jjj - JOB structure
X a *copy* of these variables will exist after the fork.
X ----------------------------------------------------------- */
X
X logjob("run");
X switch(fork()) {
X
X case 0:
X execjob(shopt);
X case -1:
X logjob("can't fork1");
X if(errno == EAGAIN) {
X closejob();
X mjp->time = now + GRAINULARITY;
X intodo(mjp);
X return(0);
X }
X fatal("kernal fork trouble");
X default:
X closejob();
X }
X return(1);
X }
X
X
X/* =========================================================== */
X
Xmain(argc,argv)
X int argc;
X char **argv;
X {
X int n;
X int batch;
X char *q;
X
X
X/* get things ready -------------------------------------- */
X
X catchup = CATCHUP;
X if((q=getenv("CATCHUP")) != NULL) catchup = atol(q);
X if(argc >= 2) catchup = atol(argv[1]);
X maxkids = MAXKIDS;
X if((q=getenv("MAXKIDS")) != NULL) maxkids = atoi(q);
X if(argc == 3) maxkids = atoi(argv[2]);
X
X if((tz=getenv("TZ")) == NULL)
X fatal("get TZ env.var.");
X
X umask(UMASK);
X if((cronpw=getpwnam("cron")) == NULL)
X fatal("get cron passwd");
X if(chdir(ATSPOOL) != 0)
X fatal("cd to at spool dir");
X
X/* open the fifo.s --------------------------------------- */
X
X mkfifo(JNUM);
X mkfifo(FNAM);
X jnum = openfifo(JNUM);
X fnam = openfifo(FNAM);
X
X/* open a file descriptors to /dev/null ------------------ */
X/* user job's mail stdout and stderr connected there */
X/* daemon's stdin and stdout, so can setpgrp() !=window */
X/* also used by daemon for fildes 2 when clean log */
X
X if((nul=open("/dev/null",O_RDWR)) == -1)
X fatal("open /dev/null");
X if(fcntl(nul,F_SETFD,1) == -1)
X fatal("set close-on-exec for /dev/null");
X if((close(0) != 0) || (close(1) != 0) ||
X (dup(nul) != 0) || (dup(nul) != 1))
X fatal("connect stdin, stdout to /dev/null");
X
X/* open a file descriptor to the log file ---------------- */
X/* and connect stderr there too */
X
X openlog();
X
X/* build the in-memory job list -------------------------- */
X
X logtime(" : cron daemon start\n");
X start = time((long *)0);
X initlist();
X
X/* put available jobnumbers into JNUM fifo --------------- */
X
X/* ---------------------- alternate way
Xmust declare i and z if used
X
X i = n = 0;
X z = numfree();
X while(n < z) {
X if(!taken(i)) {
X jjj.jobn = i;
X if(wrfifo(jnum,&jjj) == 0)
X wrfifofail(jnum);
X ++n;
X }
X ++i;
X }
X---------------------------------- */
X
X for(n=0;n<NUMJOBS;++n)
X if(!taken(n)) {
X jjj.jobn = n;
X if(wrfifo(jnum,&jjj) == 0)
X wrfifofail(jnum);
X }
X
X/* no zombies -------------------------------------------- */
X
X if(signal(SIGCLD,SIG_IGN) == BADSIG)
X fatal("ignore death_of_child signal");
X
X/* align memory so can set up environments --------------- */
X
X if(align() != 0)
X fatal("memory overflow");
X
X/* fork off the real daemon ------------------------------ */
X/* makes daemon's parent init */
X
X switch(fork()) {
X case 0:
X setpgrp();
X break;
X case -1:
X fatal("fork daemon");
X default:
X exit(0);
X }
X
X/* main loop --------------------------------------------- */
X
Xlogmsg2("daemon running",getpid());
Xwhile(1) {
X
X/* go to sleep for awhile -------------------------------- */
X/* I keep track of when it's really supposed to wakeup */
X
Xif(signal(SIGALRM,dummy) == BADSIG)
X fatal("set alarm signal");
Xstart += GRAINULARITY;
Xnap = start - time((long *)0);
Xalarm((unsigned)(nap<=0L? 1: nap));
Xpause();
Xnow = time((long *)0);
Xif((now % 3600) < GRAINULARITY) logtime("");
Xbatch = kids = 0;
X
X/* read any messages sent by users ----------------------- */
X
Xwhile((rdfifo(fnam,&jjj)) != 0) {
X
X sprintf(fname,"%d",jjj.jobn);
X switch(jjj.msg) {
X
X case AT_JOB:
X case BATCHJ:
X case CR_JOB:
X case CR_TAB:
X
X insert(&jjj);
X logjob("add");
X break;
X
X case REMOVE:
X
X if((mjp=deltodo(jjj.jobn)) != NULL) {
X if((mjp->uid == jjj.uid) || (jjj.uid == 0)) {
X rmvjob("rmv");
X }
X else {
X intodo(mjp);
X logjob("unauthorized try rmv");
X }
X }
X else
X logjob("try rmv non-exist");
X break;
X
X case CL_LOG:
X
X if((jjj.uid == 0) || (jjj.uid == cronpw->pw_uid))
X cleanlog();
X else
X logmsg2("unauthorized try clean log",jjj.uid);
X break;
X }
X }
X
X/* check job list ---------------------------------------- */
X
Xwhile((jtodo != NULL) && (jtodo->time <= now)) {
X
X mjp=rmtodo();
X sprintf(fname,"%d",mjp->jobn);
X switch(mjp->msg) {
X
X case BATCHJ:
X
X if(batch) {
X mjp->time = now + GRAINULARITY;
X intodo(mjp);
X continue;
X }
X batch = 1;
X if(runjob("-s"))
X rmvjob(NULL);
X break;
X
X case AT_JOB:
X
X if(mjp->time < (now-catchup-GRAINULARITY-1)) {
X rmvjob("rmv outdated");
X continue;
X }
X if(runjob("-s"))
X rmvjob(NULL);
X break;
X
X case CR_JOB:
X case CR_TAB:
X
X if(mjp->time < (now-catchup-GRAINULARITY-1)) {
X if(openjob()) {
X closejob();
X if((mjp->time = resched(now)) < 0L)
X rmvjob("bad cron schedule");
X else
X intodo(mjp);
X }
X continue;
X }
X if(runjob((mjp->msg == CR_TAB)? "-c" : "-s")) {
X if((mjp->time = resched(now)) < 0L)
X rmvjob("bad cron schedule");
X else
X intodo(mjp);
X }
X break;
X
X } /* end switch */
X
X } /* end check job list */
X
X} /* end main loop */
X
X} /* end main */
X
SHAR_EOF
if test 13919 -ne `wc -c <daemon.c`
then
echo "daemon.c unpacked with wrong size"
fi
echo "extracting fifo.c ( 2570 chars)"
sed 's/^X//' <<'SHAR_EOF' >fifo.c
X/* ------------------------------------------------------------
X These routines handle the FIFOs
X
X Read and write only read a partital JOB struct from the FIFOs
X even though their callers pass a pointer to the whole thing.
X
X Reading and writing FIFOs (quoting from the manual):
X
X write(2)- "... pipe (or FIFO), no partial writes ..."
X read(2)- number of bytes read less than requested "if the file
X is associated with a communication line ..., or if the
X number of bytes left in the file is less than" req'd.
X
X So, as long as you read and write the same number of bytes for
X for each call, it's an all-or-nothing situation. The only time
X read will fail is if there is 0 bytes in the FIFO. The only
X time write will fail is if the FIFO reaches its "limit". The
X limit is implementation dependent. It is 10240 bytes in the
X UNIX-PC. There is only one undocumented thing that could mess
X this up: if the FIFO limit changes dynamically at runtime and
X after the FIFO has already been openned. If that's the case,
X then you can't depend on being able to write into a FIFO even
X though it *isn't* (wasn't) full. I don't think this can happen.
X I've tried to make this happen on my UNIX-PC and I always get
X a FIFO that can handle 10240 bytes. If it does happen, then
X my whole scheme is flawed.
X ------------------------------------------------------------ */
X
X#include <stdio.h>
X#include <fcntl.h>
X#include <sys/stat.h>
X#include <pwd.h>
X#include "cron.h"
X#include "job.h"
X
Xvoid fatal();
X
Xextern struct passwd *cronpw;
X
Xvoid mkfifo(name)
X char *name;
X {
X if(access(name,0) != 0) {
X if(mknod(name,S_IFIFO|FIFO_PERM,0) != 0)
X fatal("make fifo");
X#ifndef DEBUG
X if(chown(name,cronpw->pw_uid,cronpw->pw_gid) != 0)
X fatal("chown cron fifo");
X#endif
X }
X }
X
Xint openfifo(name)
X char *name;
X {
X int fd;
X
X if((fd=open(name,O_RDWR|O_NDELAY)) == -1)
X fatal("open fifo");
X if(fcntl(fd,F_SETFD,1) == -1)
X fatal("set close-on-exec for fifo");
X return(fd);
X }
X
Xint rdfifo(fd,jbuf)
X int fd;
X JOB *jbuf;
X {
X int bytes;
X fifoJOB j;
X
X if((bytes=read(fd,(char *)&j,fifoJOBSIZ)) != 0)
X if(bytes != fifoJOBSIZ)
X fatal("read job struct");
X jbuf->jobn = j.jobn;
X jbuf->msg = j.msg;
X jbuf->time = j.time;
X jbuf->uid = j.uid;
X return(bytes);
X }
X
Xint wrfifo(fd,jbuf)
X int fd;
X JOB *jbuf;
X {
X int bytes;
X fifoJOB j;
X
X j.jobn = jbuf->jobn;
X j.msg = jbuf->msg;
X j.time = jbuf->time;
X j.uid = jbuf->uid;
X if((bytes=write(fd,(char *)&j,fifoJOBSIZ)) != 0)
X if(bytes != fifoJOBSIZ)
X fatal("write job struct");
X return(bytes);
X }
SHAR_EOF
if test 2570 -ne `wc -c <fifo.c`
then
echo "fifo.c unpacked with wrong size"
fi
echo "extracting job.c ( 164 chars)"
sed 's/^X//' <<'SHAR_EOF' >job.c
Xchar jdone[] = "# job done\n"; /* end of job marker */
Xchar *jtypes[] = {
X "AT_JOB",
X "BATCHJ",
X "CR_JOB",
X "CR_TAB",
X "REMOVE",
X "CL_LOG",
X "unknown"
X };
SHAR_EOF
if test 164 -ne `wc -c <job.c`
then
echo "job.c unpacked with wrong size"
fi
echo "extracting job.h ( 4130 chars)"
sed 's/^X//' <<'SHAR_EOF' >job.h
X/* --------------------------------------------------------------
X The following job definition is used to pass information between
X the daemon and users. This information, in binary form, is in
X the job file. Only a small part of it is actually passed thru
X the FIFOs. This is because the number of jobs is limitted by
X the size of FIFOs, which is implementation dependent. Also, the
X daemon only keeps some of this in memory. The way I've declared
X the structures here is not really kosher, but it avoids the
X syntax mess that would happen if I did it *right*.
X NOTE: in the unix-pc, FIFOs are 10k bytes. There seems to
X be no limit on how many FIFOs you can have. I always run out of
X processes before I run out of FIFOs. And if you can make a FIFO
X you can fill it up to 10240 bytes. There must be a limit in the
X kernal somewhere, but I think my scheme is safe.
X -------------------------------------------------------------- */
X
Xstruct job {
X struct job *link; /* link or magic number |d |j */
X short jobn; /* jobnumber & filename |f |e |o */
X short msg; /* message type |i |m |b */
X long time; /* sec.s from Jan1 1970 |f |o | */
X int uid; /* user id |o |n |f */
X int gid; /* group id |i */
X int nice; /* nice increment |l */
X int umask; /* user's umask |e */
X long ulimit; /* user's ulimit | */
X int envc; /* num env string ptrs | */
X int envz; /* bytes of env strings | */
X char min[60]; /* cron sched info | */
X char hour[24]; /* char is \0 or not | */
X char mday[32]; /* for time to do it | */
X char mon[12]; /* indexed by corre- | */
X char wday[7]; /* sponding value | */
X };
X
Xstruct fifojob {
X short jobn; /* jobnumber & filename */
X short msg; /* message type */
X long time; /* sec.s from Jan1 1970 */
X int uid; /* user id */
X };
X
Xstruct memjob {
X struct memjob *link; /* link or magic number */
X short jobn; /* jobnumber & filename */
X short msg; /* message type */
X long time; /* sec.s from Jan1 1970 */
X int uid; /* user id */
X };
X
Xstruct schedule {
X char min[60]; /* cron sched info | */
X char hour[24]; /* char is \0 or not | */
X char mday[32]; /* for time to do it | */
X char mon[12]; /* indexed by corre- | */
X char wday[7]; /* sponding value | */
X };
X
Xtypedef struct job JOB;
Xtypedef struct fifojob fifoJOB;
Xtypedef struct memjob memJOB;
Xtypedef struct schedule SCHED;
X
X#define JOBSIZ (sizeof(JOB))
X#define fifoJOBSIZ (sizeof(fifoJOB))
X#define memJOBSIZ (sizeof(memJOB))
X#define SCHEDSIZ (sizeof(SCHED))
X
X/* avoid magic #'s */
X#define MAGIC ((JOB *)0)
X
X/* msg values */
X#define AT_JOB 0
X#define BATCHJ 1
X#define CR_JOB 2
X#define CR_TAB 3
X#define REMOVE 4
X#define CL_LOG 5
X
X/* implementation dependent limits */
X#define MINMSG 0
X#define MAXMSG 5
X#define MINNICE (-39)
X#define MAXNICE 39
X#define MAXULIM 2147483647
X
X
X/* --------------------------------------------------------------
X Structure of a job file: jobn = atoi( filename )
X
X
X__________________________________________
Xstruct job *link=MAGIC; |
Xshort jobn; V
Xshort msg;
Xlong time; JOB FILE HEADER -
Xint uid; This part of the file is
Xint gid; implementation dependent
Xint nice; non-ascii stream of bytes.
Xint umask; Environment strings are
Xlong ulimit; marked with null bytes at
Xint envc; their ends, not newlines.
Xint envz;
Xchar min[60],
X hour[24],
X mday[32],
X mon[12],
X wday[7];
X
X<<environment>> ^
X<<shell>> |
X<<working directory>> |
X<<crontab command>> -or- '\0' |
X__________________________________________|
X
X<<user supplied>>
X
X'\n' \ mark end of job. note extra newline.
X"# job done\n" / not used with crontab, crtabj
X -------------------------------------------------------------- */
X
SHAR_EOF
if test 4130 -ne `wc -c <job.h`
then
echo "job.h unpacked with wrong size"
fi
echo "extracting log.c ( 2215 chars)"
sed 's/^X//' <<'SHAR_EOF' >log.c
X/* handle daemon's log file */
X
X#include <fcntl.h>
X#include <stdio.h>
X#include <pwd.h>
X#include <time.h>
X#include "cron.h"
X#include "job.h"
X
Xextern int errno;
Xchar *ctime();
Xlong time();
X
Xvoid fatal();
Xextern struct passwd *cronpw;
Xextern char line[];
Xextern int nul;
Xextern JOB jjj;
Xextern char *jtypes[];
X
Xstatic int log;
X
X/* log job ---------------------------------------------------- */
X
Xvoid logjob(str)
X char *str;
X {
X int m;
X
X m=jjj.msg;
X if((m < MINMSG) || (m > MAXMSG)) m=MAXMSG+1;
X sprintf(line,"%s %s job= %d uid= %d\n",
X str,jtypes[m],jjj.jobn,jjj.uid);
X write(log,line,(unsigned)strlen(line));
X }
X
X/* log time --------------------------------------------------- */
X
Xvoid logtime(str)
X char *str;
X {
X long t;
X
X t = time((long *)0);
X sprintf(line,"\n%s%s",ctime(&t),str);
X write(log,line,(unsigned)strlen(line));
X }
X
X/* log message string ----------------------------------------- */
X
Xvoid logmsg1(str)
X char *str;
X {
X sprintf(line,"%s\n",str);
X write(log,line,(unsigned)strlen(line));
X }
X
X/* log message string and number ------------------------------ */
X
Xvoid logmsg2(str,n)
X char *str;
X int n;
X {
X sprintf(line,"%s : %d\n",str,n);
X write(log,line,(unsigned)strlen(line));
X }
X
X/* open a file descriptor to the log file --------------------- */
X/* and connect stderr there too */
X
Xvoid openlog()
X {
X
X if((log=open(LOG,O_WRONLY|O_CREAT|O_APPEND,LOG_PERM)) == -1)
X fatal("open log file");
X#ifndef DEBUG
X if(chown(LOG,cronpw->pw_uid,cronpw->pw_gid) != 0)
X fatal("chown cron log file");
X#endif
X if(fcntl(log,F_SETFD,1) == -1)
X fatal("set close-on-exec for log file");
X if((close(2) != 0) || (dup(log) != 2)) {
X logmsg2("FATAL: can't connect stderr to log",errno);
X fatal("connect stderr to log");
X }
X }
X
X/* clean log file --------------------------------------------- */
X
Xvoid cleanlog()
X {
X if((
X close(2) != 0) || (
X dup(nul) != 2) || (
X close(log) != 0) || (
X unlink(LOG) != 0) || (
X (log=open(LOG,
X O_WRONLY|O_CREAT|O_APPEND,LOG_PERM)) == -1) || (
X close(2) != 0) || (
X dup(log) != 2) || (
X#ifndef DEBUG
X chown(LOG,cronpw->pw_uid,cronpw->pw_gid) != 0) || (
X#endif
X fcntl(log,F_SETFD,1) == -1)
X )
X fatal("clean log");
X
X logtime("");
X }
SHAR_EOF
if test 2215 -ne `wc -c <log.c`
then
echo "log.c unpacked with wrong size"
fi
echo "extracting makefile ( 1560 chars)"
sed 's/^X//' <<'SHAR_EOF' >makefile
X# makefile for cron facility
X
Xall: at cron crontab
X
Xat: at.o allow.o fifo.o job.o parsetime.o parsesched.o resched.o
X ld -s /lib/crt0s.o /lib/shlib.ifile -o at at.o \
X allow.o fifo.o job.o parsetime.o parsesched.o resched.o
X -ln at batch
X -ln at cronjob
X -ln at crtabj
X
Xcron: daemon.o fifo.o job.o log.o memlist.o resched.o
X ld -s /lib/crt0s.o /lib/shlib.ifile -o cron daemon.o \
X fifo.o job.o log.o memlist.o resched.o
X
Xcrontab: crontab.o allow.o
X ld -s /lib/crt0s.o /lib/shlib.ifile -o crontab crontab.o allow.o
X
X# ======================
X
Xallow.o: cron.h
X cc -O -c allow.c
X
Xat.o: cron.h job.h at.c
X cc -O -c at.c
X
Xcrontab.o: cron.h crontab.c
X cc -O -c crontab.c
X
Xdaemon.o: cron.h job.h daemon.c
X cc -O -c daemon.c
X
Xfifo.o: cron.h job.h fifo.c
X cc -O -c fifo.c
X
Xjob.o: job.c
X cc -O -c job.c
X
Xlog.o: cron.h job.h log.c
X cc -O -c log.c
X
Xmemlist.o: cron.h job.h memlist.c
X cc -O -c memlist.c
X
Xparsesched.o: cron.h job.h parsesched.c
X cc -O -c parsesched.c
X
Xparsetime.o: cron.h parsetime.c
X cc -O -c parsetime.c
X
Xresched.o: job.h resched.c
X cc -O -c resched.c
X
X# ======================
X
Xlint: atlint cronlint crontablint
X
Xatlint:
X lint at.c allow.c fifo.c job.c parsetime.c parsesched.c resched.c
X
Xcronlint:
X lint daemon.c fifo.c job.c log.c memlist.c resched.c
X
Xcrontablint:
X lint crontab.c allow.c
X
X# ======================
X
Xclean:
X rm -f *.o
X
Xclobber:
X rm -f *.o at batch cronjob crtabj cron crontab
X rm -rf lib spool
X
Xpublic:
X rm -f *.o at batch cronjob crtabj cron crontab
X shar * >cron.shar
X compress cron.shar
X mv cron.shar.Z /usr/src/public/cron.shar.Z
SHAR_EOF
if test 1560 -ne `wc -c <makefile`
then
echo "makefile unpacked with wrong size"
fi
echo "extracting memlist.c ( 4705 chars)"
sed 's/^X//' <<'SHAR_EOF' >memlist.c
X/* -----------------------------------------------------------
X The following code handles the in-memory storage of jobs.
X Two linked lists are maintained. The first is a list of
X free blocks of memory for jobs. The free list is basically
X handled like a stack because any free block is useful. The
X second is a list of pending jobs. It is in order of time
X that the job is scheduled to be run. Inserting jobs in the
X todo list requires searching for the right spot, but removing
X from this list is easy because the earliest job is always at
X the front of the list.
X ----------------------------------------------------------- */
X
X#include <stdio.h>
X#include <string.h>
X#include <sys/dir.h>
X#include "cron.h"
X#include "job.h"
X
Xvoid logjob();
Xvoid logmsg1();
Xvoid fatal();
Xextern char fname[];
Xextern char line[];
Xextern char jdone[];
Xextern JOB jjj;
X
XmemJOB *jtodo; /* head of pending jobs list */
X
Xstatic memJOB *jfree; /* head of free jobs list */
Xstatic memJOB jobmem[NUMJOBS]; /* in-memory storage for jobs */
X
X/* intilz:
X * link everything to free list, nothing on todo list
X */
Xstatic void initfree()
X {
X memJOB *j,*jend;
X jfree=jobmem;
X jtodo = NULL;
X
X /* link everything to free list */
X j = jfree;
X jend = &(jobmem[NUMJOBS-1]);
X while(j < jend) {
X j->link = j+1;
X ++j;
X }
X j->link = NULL;
X }
X
X/* remove first one on the free list or return NULL
X */
Xstatic memJOB *rmfree()
X {
X memJOB *j;
X j = jfree;
X if(j != NULL) jfree = j->link;
X return(j);
X }
X
X/* externally accessed routines ------------------------------- */
X
X/* insert at head of free list
X */
Xvoid infree(j)
X memJOB *j;
X {
X j->link = jfree;
X jfree = j;
X }
X
X/* remove first one on todo list or return NULL
X */
XmemJOB *rmtodo()
X {
X memJOB *j;
X j = jtodo;
X if(j != NULL) jtodo = j->link;
X return(j);
X }
X
X/* delete from todo list by jobnumber or return NULL
X */
XmemJOB *deltodo(n)
X int n;
X {
X memJOB *j,*k;
X j = jtodo;
X if((j == NULL) || (j->jobn == n)) {
X jtodo = j->link;
X return(j);
X }
X k = jtodo;
X j = jtodo->link;
X while(j != NULL) {
X if(j->jobn == n) {
X k->link = j->link;
X return(j);
X }
X k = j;
X j = k->link;
X }
X return(NULL);
X }
X
X/* intodo:
X * insert in todo list by time
X */
Xvoid intodo(j)
X memJOB *j;
X {
X memJOB *k,*l;
X long t;
X t = j->time;
X if((jtodo == NULL) || (t < jtodo->time)) {
X j->link = jtodo;
X jtodo = j;
X return;
X }
X k = jtodo;
X l = jtodo->link;
X while(l != NULL) {
X if(t < l->time) {
X j->link = l;
X k->link = j;
X return;
X }
X k = l;
X l = k->link;
X }
X j->link = NULL;
X k->link = j;
X }
X
X/* count the number of free jobs
X */
X/* ----------------------- not used
Xint numfree()
X {
X memJOB *j;
X int n;
X j = jfree;
X n = 0;
X while(j != NULL) {
X ++n;
X j = j->link;
X }
X return(n);
X }
X------------------------------------- */
X
X/* see if a jobnumber is already in use
X */
Xint taken(n)
X int n;
X {
X memJOB *j;
X j = jtodo;
X while(j != NULL) {
X if(j->jobn == n) return(1);
X j = j->link;
X }
X return(0);
X }
X
X/* insert:
X * put job on todo list
X */
Xvoid insert(jbuf)
X JOB *jbuf;
X {
X memJOB *j;
X
X if((j=rmfree()) == NULL)
X fatal("get empty job slot");
X j->jobn = jbuf->jobn;
X j->msg = jbuf->msg;
X j->time = jbuf->time;
X j->uid = jbuf->uid;
X intodo(j);
X }
X
X/* initlist:
X * build the in-memory job list
X */
Xvoid initlist()
X {
X FILE *pfp;
X FILE *fp;
X int goodflag;
X
X logmsg1("build in-mem job list");
X initfree();
X if((pfp=popen("/bin/ls","r")) == NULL)
X fatal("open pipe to ls cmd");
X while(fgets(fname,DIRSIZ+2,pfp) != NULL) {
X fname[strcspn(fname,"\n")] = '\0'; /* strip \n */
X if((fp=fopen(fname,"r")) == NULL)
X fatal("open a job file");
X
X goodflag = 0;
X if(fread(&jjj,JOBSIZ,1,fp) == 1) {
X if(jjj.msg == CR_TAB) {
X sprintf(line,"%s/%d",CRSPOOL,jjj.uid);
X if(access(line,0) == 0)
X goodflag = 1;
X }
X else {
X while(fgets(line,LINESIZ,fp) != NULL) {
X goodflag = 0;
X if(strcmp(line,jdone) == 0)
X goodflag = 1;
X }
X if(!feof(fp)) goodflag=0;
X }
X if((
X jjj.link == MAGIC) && (
X jjj.jobn == atoi(fname)) && (
X jjj.msg >= MINMSG) && (
X jjj.msg <= MAXMSG) && (
X jjj.time >= 0L) && (
X jjj.uid >= 0) && (
X jjj.gid >= 0) && (
X jjj.nice >= MINNICE) && (
X jjj.nice <= MAXNICE) && (
X jjj.umask >= 0) && (
X jjj.umask <= 0777) && (
X jjj.ulimit >= 0L) && (
X jjj.ulimit <= MAXULIM) && (
X jjj.envc >= 4) && (
X jjj.envz >= 3) && (
X goodflag )
X ) {
X insert(&jjj);
X logjob("queued");
X }
X else goodflag=0;
X }
X if(!goodflag) {
X jjj.jobn = atoi(fname);
X logjob("rmv corrupt file");
X if(unlink(fname) != 0)
X fatal("unlink job file");
X }
X if(fclose(fp) != 0)
X fatal("close job file");
X
X } /* end while */
X
X if(pclose(pfp) == -1)
X fatal("close pipe to ls cmd");
X }
SHAR_EOF
if test 4705 -ne `wc -c <memlist.c`
then
echo "memlist.c unpacked with wrong size"
fi
echo "extracting parsesched.c ( 4418 chars)"
sed 's/^X//' <<'SHAR_EOF' >parsesched.c
X/* parse: cronjob <schedule>
X *
X * <schedule> = <mm> <hh> <DD> <MM> <ww>
X *
X * <mm> = <intlist>
X *
X * <hh> = <intlist>
X *
X * <DD> = <intlist>
X *
X * <MM> = <intlist>
X * |<monthlist>
X *
X * <ww> = <intlist>
X * |<wdaylist>
X *
X * <intlist> = <number>
X * |<number>,<intlist>
X *
X * <number> = <digits>
X * |<digits>-<digits>
X *
X */
X
X#include <stdio.h>
X#include <string.h>
X#include <ctype.h>
X#include <memory.h>
X#include <setjmp.h>
X#include "cron.h"
X#include "job.h"
X
X
X/* =============== hooks to the caller of parsesched() ================ */
X
X/* parsesched() returns pointer to static SCHED struct or NULL if bad */
X/* sets caller's char *str --> rest of the line in static area */
X
Xextern int optind;
Xstatic SCHED sched;
X
X/* ==================================================================== */
X
X
Xvoid longjmp();
Xstatic jmp_buf parsebad;
X#define badsched() longjmp(parsebad,-1)
X
Xstatic char line[LINESIZ];
Xstatic char *lin;
X
Xstatic void markstring();
Xstatic void list();
Xstatic int nocvt();
Xstatic int mocvt();
Xstatic int wkcvt();
Xstatic int compare();
Xstatic void eatword();
X/* ==================================================================== */
X
XSCHED *parsesched(argc,argv,strp)
X int argc;
X char **argv;
X char **strp;
X {
X char *mm,*hh,*DD,*MM,*ww;
X
X if(setjmp(parsebad)) return((SCHED *)NULL);
X
X /* put command line back together, always put " " at end */
X *line = '\0';
X while(optind < argc) {
X strncat(line,argv[optind],
X LINESIZ-1-strlen(line)-strlen(argv[optind]));
X strncat(line," ",
X LINESIZ-1-strlen(line)-strlen(argv[optind]));
X ++optind;
X }
X lin = line;
X
X /* now break command line into 5 strings */
X markstring(&mm);
X markstring(&hh);
X markstring(&DD);
X markstring(&MM);
X markstring(&ww);
X while(isspace(*lin)) ++lin;
X *strp = lin; /* pass back pointer to rest of line */
X
X list(mm,sched.min,0,60,0,nocvt);
X list(hh,sched.hour,0,24,0,nocvt);
X list(DD,sched.mday,1,32,0,nocvt);
X list(MM,sched.mon,0,12,1,mocvt);
X list(ww,sched.wday,0,7,0,wkcvt);
X
X/* if specify '*' for only one "days" field
X * then only the other one counts
X */
X if((*DD == '*') && (*ww != '*'))
X memset(sched.mday,'\0',32);
X if((*ww == '*') && (*DD != '*'))
X memset(sched.wday,'\0',7);
X
X/* note: things like Feb 31st are not checked for
X * the resched() routine takes care of these
X */
X return(&sched);
X }
X
X
X/* mark separate strings -------------------------------------- */
X
Xstatic void markstring(str)
X char **str;
X {
X while(isspace(*lin)) ++lin;
X if(*lin == '\0') badsched();
X *str = lin;
X while(!isspace(*lin)) ++lin;
X *lin++ = '\0';
X }
X
X/* set arrays from ascii "lists" ------------------------------ */
X
Xstatic void list(asc,ary,beg,end,off,cvt)
X char *asc;
X char ary[];
X int beg;
X int end;
X int off;
X int (*cvt)();
X {
X int i,b,e;
X
X if(strcmp(asc,"*") == 0) {
X memset(ary,'\001',end);
X return;
X }
X memset(ary,'\0',end);
X while(1) {
X if(isdigit(*asc)) {
X b = atoi(asc)-off;
X while(isdigit(*asc)) ++asc;
X }
X else
X b = (*cvt)(&asc);
X
X if(*asc == '-') {
X ++asc;
X if(isdigit(*asc)) {
X e = atoi(asc)-off;
X while(isdigit(*asc)) ++asc;
X }
X else
X e = (*cvt)(&asc);
X }
X else
X e = b;
X
X for(i=b;i<=e;++i) {
X if((i < beg) || (i > end))
X badsched();
X ary[i] = '\001';
X }
X switch(*asc) {
X case '\0':
X return;
X case ',':
X ++asc;
X continue;
X }
X badsched();
X }
X }
X
Xstatic int nocvt(ascp)
X char **ascp;
X {
X badsched();
X /*NOTREACHED*/
X }
X
X
Xstatic char *months[] = {
X "January","February","March","April",
X "May","June","July","August",
X "September","October","November","December"
X };
Xstatic int mocvt(ascp)
X char **ascp;
X {
X int i;
X char *p;
X
X /* check month names */
X for(i=0;i<12;++i) {
X p = months[i];
X if(compare(*ascp,p,3) == 0) {
X eatword(ascp,p);
X break;
X }
X }
X if(i > 11) badsched();
X return(i);
X }
X
X
Xstatic char *wdays[] = {
X "Sunday","Monday","Tuesday","Wednesday",
X "Thursday","Friday","Saturday"
X };
Xstatic int wkcvt(ascp)
X char **ascp;
X {
X int i;
X char *p;
X
X /* check weekday names */
X for(i=0;i<7;++i) {
X p = wdays[i];
X if(compare(*ascp,p,3) == 0) {
X eatword(ascp,p);
X break;
X }
X }
X if(i > 6) badsched();
X return(i);
X }
X
Xstatic int compare(p,q,n)
X char *p,*q;
X int n;
X {
X while(n--)
X if(tolower(*p++) != tolower(*q++)) return(-1);
X return(0);
X }
X
Xstatic void eatword(ascp,p)
X char **ascp;
X char *p;
X {
X while(tolower(**ascp) == tolower(*p)) { ++(*ascp); ++p; }
X }
SHAR_EOF
if test 4418 -ne `wc -c <parsesched.c`
then
echo "parsesched.c unpacked with wrong size"
fi
echo "extracting parsetime.c ( 10399 chars)"
sed 's/^X//' <<'SHAR_EOF' >parsetime.c
X/* parse: at <time>[ <date>][ <increment>]
X *
X * <sched> = <time>
X * |<time> <date>
X * |<time> +<increment>
X * |<time> + <increment>
X * |<time> <date> +<increment>
X * |<time> <date> + <increment>
X *
X * <time> = <clock>|noon|midnight|now|next|nxt|this this=next=now
X *
X * <clock> = <clk>|<clk><csufx>|<clk> <csufx>
X * <clk> = <hour>|<hour><min>|<hour>:<min>
X * <csufx> = am|pm|zulu
X * <hour> = <digit>|<digit><digit>
X * <min> = <digit><digit>
X *
X * <date> = <month> <day>
X * |<month> <day> <year>
X * |<month> <day>,<year>
X * |<month> <day>, <year>
X * |<week>
X * |next <month> <day>
X * |next <week>
X * |nxt <month> <day>
X * |nxt <week>
X * |this <month> <day>
X * |this <week>
X * |today|tomorrow
X *
X * <month> = January|Feburary ....
X * <day> = <digit>|<digit><digit> 1-31
X * <year> = <digit><digit><digit><digit> 1970-2037
X * <week> = Monday|Tuesday ....
X *
X * <increment> = +<number><incr>
X * |+<number> <incr>
X * |+ <number><incr>
X * |+ <number> <incr>
X *
X * <number> = <digit>|<digit><number>
X * <incr> = minutes|hours|days|weeks|months|years
X * |hrs|wks|mos|yrs
X *
X * notes: words only have to match first three characters
X * words can be upper or lower case
X * next|nxt|this must have <date>
X * can't use <year>|today|tomorrow with next|nxt|this
X */
X
X/* ------------------------- NOTICE -----------------------------
X
X parsetime
X (c) copyright March 29, 1989 by Donald Lashomb
X
X This program is free. Use it, modify it, copy it, give a copy
X to a friend; I simply request the following provisions be observed:
X
X 1. My name as original author and this notice remain intact.
X 2. This program (or any modification of it) is not to be sold
X for profit.
X 3. If this program is included in commercial products, there be
X no charge for it.
X 4. This program must be distributed with source code. Compiled-
X only or binary-only distribution of this program is not allowed.
X The administrator of any system that uses this program must have
X full access to the source code.
X 5. If you enhance this program, discover a bug, have any comments
X about it (or flames) please let me know.
X
X Donald Lashomb
X Main Street
X Cranberry Lake, NY 12927
X
X -------------------------------------------------------------- */
X
X
X/* =============== hooks to the caller of parsetime() ================= */
X
X/* parsetime() returns sec.s since Jan 1, 1970 gmt or -1L if bad */
X
Xextern int optind;
X
X/* ==================================================================== */
X
X#include <stdio.h>
X#include <time.h>
X#include <memory.h>
X#include <string.h>
X#include <ctype.h>
X#include <setjmp.h>
X#include "cron.h"
X
X
Xlong time();
Xstruct tm *localtime();
Xstruct tm *gmtime();
Xvoid tzset();
Xextern long timezone;
Xextern int daylight;
Xvoid longjmp();
X
Xstatic jmp_buf parsebad;
X#define badsched() longjmp(parsebad,-1)
X
Xstatic char line[LINESIZ];
Xstatic char *lin;
Xstatic long now;
Xstatic struct tm nowtm;
X
X /* timcvt() line[] struct tm */
Xstatic int YY, /* 70-137 1970-2037 70-137 */
X MM, /* 0-11 Jan-Dec 0-11 */
X DD, /* 1-31 1-31 1-31 */
X /* weekday --- Sun-Sat 0-6 */
X hh, /* 0-23 1-12ampm 0-23 */
X mm, /* 0-59 0-59 0-59 */
X ss; /* 0-59 --- 0-59 */
Xstatic int gmtflag = 0;
Xstatic int nxtflag = 0;
Xstatic int thiflag = 0;
X
X#define chr (*lin)
X#define gchr (*lin++)
X
Xstatic char copyright[] = "parsetime - (c)1989 D.Lashomb";
X
Xstatic void hour();
Xstatic void year();
Xstatic int increment();
Xstatic void eatword();
Xstatic int compare();
Xstatic long timcvt();
X/* ==================================================================== */
X
Xlong parsetime(argc,argv)
X int argc;
X char **argv;
X {
X char *p;
X int dateflag;
X
X /* put command line back together, always put " " at end */
X *line = '\0';
X while(optind < argc) {
X strncat(line,argv[optind],
X LINESIZ-1-strlen(line)-strlen(argv[optind]));
X strncat(line," ",
X LINESIZ-1-strlen(line)-strlen(argv[optind]));
X ++optind;
X }
X lin = line;
X now = time((long *)0);
X memcpy(&nowtm,localtime(&now),sizeof(struct tm));
X tzset();
X YY = nowtm.tm_year;
X MM = nowtm.tm_mon;
X DD = nowtm.tm_mday;
X hh = nowtm.tm_hour;
X mm = nowtm.tm_min;
X#ifdef ZEROSECS
X ss = 0;
X#else
X ss = nowtm.tm_sec;
X#endif
X
X if(setjmp(parsebad)) return(-1L);
X
X if(isdigit(chr)) hour();
X
X else if(compare(lin,(p="noon"),3) == 0) {
X eatword(p);
X hh = 12;
X mm = 0;
X }
X else if(compare(lin,(p="midnight"),3) == 0) {
X eatword(p);
X hh = 0;
X mm = 0;
X }
X else if(compare(lin,(p="now"),3) == 0) {
X eatword(p);
X ss = nowtm.tm_sec;
X /* default now */
X }
X else if((compare(lin,(p="next"),3) == 0) ||
X (compare(lin,(p="nxt"),3) == 0)) {
X eatword(p);
X nxtflag = 1;
X }
X else if(compare(lin,(p="this"),3) == 0) {
X eatword(p);
X thiflag = 1;
X }
X else badsched();
X
X dateflag = date();
X if(!dateflag) {
X if((hh < nowtm.tm_hour) ||
X ((hh == nowtm.tm_hour) && (mm < nowtm.tm_min))) {
X /* tommorrow */
X ++DD;
X }
X }
X increment();
X if(chr != '\0') badsched();
X if((nxtflag || thiflag) && (!dateflag)) badsched();
X return(timcvt());
X }
X
X/* ==================================================================== */
X
Xstatic void hour()
X {
X char digits[5];
X int i;
X char *p;
X
X for(i=0;i<5; ) {
X if(isdigit(chr)) digits[i++] = gchr;
X else if(chr == ':') ++lin;
X else break;
X }
X digits[i] = '\0';
X if(i > 2) {
X if((mm = atoi(&digits[i-2])) > 59) badsched();
X digits[i-2] = '\0';
X }
X else
X mm = 0;
X
X hh = atoi(digits);
X
X if(chr == ' ') ++lin;
X if(compare(lin,(p="am"),2) == 0) {
X eatword(p);
X if(hh > 12) badsched();
X if(hh == 12) hh = 0;
X }
X else if(compare(lin,(p="pm"),2) == 0) {
X eatword(p);
X if(hh != 12) hh += 12;
X }
X else if(compare(lin,(p="zulu"),3) == 0) {
X eatword(p);
X gmtflag = 1;
X memcpy(&nowtm,gmtime(&now),sizeof(struct tm));
X YY = nowtm.tm_year;
X MM = nowtm.tm_mon;
X DD = nowtm.tm_mday;
X }
X else if(*(lin-1) != ' ') badsched();
X
X if(hh > 23) badsched();
X }
X
X/* ==================================================================== */
X
Xstatic char *months[] = {
X "January","February","March","April",
X "May","June","July","August",
X "September","October","November","December"
X };
X
Xstatic char *wdays[] = {
X "Sunday","Monday","Tuesday","Wednesday",
X "Thursday","Friday","Saturday"
X };
X
Xstatic int date() /* strips trailing space */
X {
X int i;
X char *p;
X
X /* check for "next" and "this" */
X if((compare(lin,(p="next"),3) == 0) ||
X (compare(lin,(p="nxt"),3) == 0)) {
X eatword(p);
X nxtflag = 1;
X }
X else if(compare(lin,(p="this"),3) == 0) {
X eatword(p);
X thiflag = 1;
X }
X
X /* check month names */
X for(i=0;i<12;++i) {
X p = months[i];
X if(compare(lin,p,3) == 0) {
X eatword(p);
X break;
X }
X }
X if(i <= 11) {
X MM = i;
X if(!isdigit(chr)) badsched();
X DD = atoi(lin);
X if(DD > 31) badsched();
X while(isdigit(chr)) ++lin;
X if(chr == ',') {
X ++lin;
X if(chr == ' ') ++lin;
X if(!isdigit(chr)) badsched();
X year();
X return(1);
X }
X if(gchr != ' ') badsched();
X if(isdigit(chr)) year();
X if((nxtflag) ||
X ((!thiflag) && (MM < nowtm.tm_mon)))
X ++YY;
X return(1);
X }
X
X /* check weekday names */
X for(i=0;i<7;++i) {
X p = wdays[i];
X if(compare(lin,p,3) == 0) {
X eatword(p);
X break;
X }
X }
X if(i <= 6) {
X if(nxtflag) DD += (i - nowtm.tm_wday) + 7;
X else if(thiflag) DD += (i - nowtm.tm_wday);
X else DD += (7 + (i - nowtm.tm_wday)) % 7;
X return(1);
X }
X
X /* check "today" or "tomorrow" */
X if(nxtflag || thiflag) badsched();
X p = "today";
X if(compare(lin,p,3) == 0) {
X eatword(p);
X return(1);
X }
X p = "tomorrow";
X if(compare(lin,p,3) == 0) {
X eatword(p);
X ++DD;
X return(1);
X }
X
X return(0);
X }
X
X/* ==================================================================== */
X
Xstatic void year()
X {
X if(nxtflag || thiflag) badsched();
X if(((YY = atoi(lin)) < 1970) || (YY > 2037)) badsched();
X lin += 4;
X if(gchr != ' ') badsched();
X YY -= 1900;
X }
X
X/* ==================================================================== */
X
Xstatic int increment()
X {
X int i;
X char *p;
X
X if(chr != '+') return(0);
X ++lin; /* gobble up '+' */
X if(chr == ' ') ++lin;
X if(!isdigit(chr)) badsched();
X i = atoi(lin);
X while(isdigit(chr)) ++lin;
X if(chr == ' ') ++lin;
X
X if(compare(lin,(p="minutes"),3) == 0) { eatword(p); mm += i; }
X else if(compare(lin,(p="hours"),3) == 0) { eatword(p); hh += i; }
X else if(compare(lin,(p="hrs"),3) == 0) { eatword(p); hh += i; }
X else if(compare(lin,(p="days"),3) == 0) { eatword(p); DD += i; }
X else if(compare(lin,(p="weeks"),3) == 0) { eatword(p); DD += 7*i; }
X else if(compare(lin,(p="wks"),3) == 0) { eatword(p); DD += 7*i; }
X else if(compare(lin,(p="months"),3) == 0) { eatword(p); MM += i; }
X else if(compare(lin,(p="mos"),3) == 0) { eatword(p); MM += i; }
X else if(compare(lin,(p="years"),3) == 0) { eatword(p); YY += i; }
X else if(compare(lin,(p="yrs"),3) == 0) { eatword(p); YY += i; }
X else badsched();
X return(1);
X }
X
X/* ==================================================================== */
X
Xstatic void eatword(p)
X char *p;
X {
X while(tolower(chr) == tolower(*p)) { ++lin; ++p; }
X if(gchr != ' ') badsched();
X }
X
X/* ==================================================================== */
X
Xstatic int compare(p,q,n)
X char *p,*q;
X int n;
X {
X while(n--)
X if(tolower(*p++) != tolower(*q++)) return(-1);
X return(0);
X }
X
X/* ==================================================================== */
X
Xstatic int thirty[2][12] = {31,28,31,30,31,30,31,31,30,31,30,31,
X 31,29,31,30,31,30,31,31,30,31,30,31};
X
Xstatic int modays[2][12] = {0,31,59,90,120,151,181,212,243,273,304,334,
X 0,31,60,91,121,152,182,213,244,274,305,335};
X
Xstatic long timcvt()
X {
X long secs,days;
X int isleap;
X struct tm *when;
X
X /* roll forward the hands of time */
X hh += mm/60; mm %= 60;
X DD += hh/24; hh %= 24;
X while(1) {
X YY += MM/12; MM %= 12;
X /* leapyear = (div4 except(100 except(400))); 2000 is! */
X isleap = ((YY%4)==0)? 1: 0;
X if(DD <= thirty[isleap][MM])
X break;
X DD -= thirty[isleap][MM];
X ++MM;
X }
X
X days = (((YY-70)*365)+((YY-69)/4) + modays[isleap][MM]);
X secs = (((days+DD-1)*24L*3600L) + (hh*3600L) + (mm*60) + ss);
X
X if(!gmtflag) {
X secs += timezone;
X when = localtime(&secs);
X if(when->tm_isdst) secs -= 3600;
X }
X return(secs);
X }
SHAR_EOF
if test 10399 -ne `wc -c <parsetime.c`
then
echo "parsetime.c unpacked with wrong size"
fi
echo "extracting resched.c ( 3406 chars)"
sed 's/^X//' <<'SHAR_EOF' >resched.c
X/* ------------------------------------------------------------
X reschedule a cron job:
X
X Resched scans the scheduling information in the global JOB
X structure, jjj, comparing it to jtime, and determines the
X next time that a job is supposed to run. It returns the
X answer or -1L if the scheduling info in invalid.
X
X This is a weird algorithm. You'd think there would be a more
X elegant way, maybe some matrix calculations. But, anyway, it
X is fairly fast. It's an O-sum rather than O-product method
X because, eg., minutes are reset if hours need searching.
X ------------------------------------------------------------ */
X
X#include <time.h>
X#include <setjmp.h>
X#include "job.h"
X
Xstruct tm *localtime();
Xvoid tzset();
Xextern long timezone;
Xvoid longjmp();
X
Xextern JOB jjj; /* global JOB struct */
X
X
Xstatic int thirty[2][12] = {31,28,31,30,31,30,31,31,30,31,30,31,
X 31,29,31,30,31,30,31,31,30,31,30,31};
X
Xstatic int modays[2][12] = {0,31,59,90,120,151,181,212,243,273,304,334,
X 0,31,60,91,121,152,182,213,244,274,305,335};
X
Xstatic int ss;
Xstatic int mm;
Xstatic int hh;
Xstatic int DD;
Xstatic int MM;
Xstatic int YY;
Xstatic int ww;
Xstatic int chk;
Xstatic struct tm *sched;
Xstatic int isleap;
Xstatic long days;
Xstatic long jtime;
Xstatic jmp_buf bad;
X#define badsched() longjmp(bad,-1)
X
Xstatic void incmon()
X {
X MM = (++MM)%12;
X if(MM == 0) {
X ++YY;
X isleap = ((YY%4)==0)? 1: 0;
X }
X }
X
Xstatic void chkmon()
X {
X if(jjj.mon[MM] == '\0') {
X ss = mm = hh = 0;
X DD = 1;
X do {
X if(--chk < 0) badsched();
X incmon();
X } while(jjj.mon[MM] == '\0');
X
X /* find day of the week: 00:00 Jan 1 1970 GMT was Thursday */
X days = (((YY-70)*365)+((YY-69)/4) + modays[isleap][MM]);
X ww = (int)((days+4)%7);
X }
X }
X
Xstatic void incday()
X {
X ww = (++ww)%7;
X ++DD;
X if(DD > thirty[isleap][MM]) {
X DD = 1;
X incmon();
X chkmon();
X }
X }
X
Xstatic void chkday()
X {
X if((jjj.wday[ww] == '\0') && (jjj.mday[DD] == '\0')) {
X ss = mm = hh = 0;
X do {
X if(--chk < 0) badsched();
X incday();
X } while((jjj.wday[ww]=='\0') && (jjj.mday[DD]=='\0'));
X }
X }
X
Xstatic void inchour()
X {
X hh = (++hh)%24;
X if(hh == 0) {
X incday();
X chkday();
X }
X }
X
Xstatic void chkhour()
X {
X if(jjj.hour[hh] == '\0') {
X ss = mm = 0;
X do {
X if(--chk < 0) badsched();
X inchour();
X } while(jjj.hour[hh] == '\0');
X }
X }
X
Xstatic void incmin()
X {
X mm = (++mm)%60;
X if(mm == 0) {
X inchour();
X chkhour();
X }
X }
X
Xstatic void chkmin()
X {
X if(jjj.min[mm] == '\0') {
X ss = 0;
X do {
X if(--chk < 0) badsched();
X incmin();
X } while(jjj.min[mm] == '\0');
X }
X }
X
Xlong resched(tim)
X long tim;
X {
X jtime = tim + 60; /* kick ahead 1 min */
X
X/* check that sched arrays are valid
X * if takes more than 60+24+31+12 loops
X * then schedule is invalid
X */
X chk = (60+24+31+12)*2;
X if(setjmp(bad)) return(-1L);
X
X tzset();
X sched = localtime(&jtime);
X ss = sched->tm_sec;
X mm = sched->tm_min;
X hh = sched->tm_hour;
X DD = sched->tm_mday;
X MM = sched->tm_mon;
X YY = sched->tm_year;
X ww = sched->tm_wday;
X isleap = ((YY%4)==0)? 1: 0; /* works for year 2000 */
X
X chkmon();
X chkday();
X chkhour();
X chkmin();
X
X days = (((YY-70)*365)+((YY-69)/4) + modays[isleap][MM]);
X jtime = (((days+DD-1)*24L*3600L)+(hh*3600L)+(mm*60)+ss+timezone);
X sched = localtime(&jtime);
X if(sched->tm_isdst) jtime -= 3600;
X if(jtime <= tim) jtime += 3600; /* kludge around 2:00am dst */
X return(jtime);
X }
X
SHAR_EOF
if test 3406 -ne `wc -c <resched.c`
then
echo "resched.c unpacked with wrong size"
fi
echo "extracting version ( 2008 chars)"
sed 's/^X//' <<'SHAR_EOF' >version
X****************************************************
X************ cron facility by D.Lashomb ************
X****************************************************
X
XThis is an informal revision history for the cron facility:
X
X1.0 March 89 minimal daemon. at(1) and batch(1) working
X
X2.0 April 89 source code is broken into multiple files for
X easier dev and maint. added cronjob(1).
X
X2.5 April 89 added crontab(1). incr number of jobs facility
X can handle by decr bytes passed thru fifo and
X stored in mem. multiple forms of JOB struct.
X added MAXKIDS param. daemon's sleep/wake cycle
X now adjusts for time used while awake. many
X other tweeks - first *real* version
X
X2.6 April 89 fixed minor bug in cleanlog with uid=cron.
X released to jbm at uncle
X
X2.7 May 17, 89 faster day of week calc in resched.c and kludge
X around resched problem at 2:00am d.s.t. changes
X
X2.8 June 19, 89 fixed align() in daemon.c
X
X2.8.1 July 3, 89 crontab must run as SUID=root because of bug in
X setuid() with uid=0. changed README and Install
X
X2.9 July 16, 89 fixed problem in at.c concerning getpwnam() - I
X forgot that it uses static area of memory. This
X bug was introduced in ver 2.6 when I put in code
X for getpwnam("cron"). Cosmetic changes to cron.h
X and README file.
X
X3.0 July 26, 89 Runtime speedup: user's shell is now put in job file
X by at(1) ... crontab(1) instead of daemon having to
X search passwd file to get this info. At(1) ... have
X to check the passwd file in order validate LOGNAME
X anyway, so this is much more effecient.
X
X Added SET_LOGNAME code to putenv("LOGNAME=root")
X for uid=0 when normal LOGNAME check fails for at(1)
X ... crontab(1) commands. Thanks to John Milton for
X this suggestion. Pulled allow/deny checking out of
X at.c and crontab.c and put it in a new file allow.c
X
X Cleanup and reorganized use of some globals. Minor
X additions to makefile. Fixed man pages re: LOGNAME
X
X3.0.1 Dec 21, 89 Added caveats to README file, released to usenet.
SHAR_EOF
if test 2008 -ne `wc -c <version`
then
echo "version unpacked with wrong size"
fi
More information about the Comp.sys.att
mailing list