donlash@uncle.UUCP (Donald Lashomb) (12/29/89)
For all who requested my cron program: here's part 3 of 3 -Don donlash@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@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