Path: utzoo!utgpu!jarvis.csri.toronto.edu!clyde.concordia.ca!uunet!tut.cis.ohio-state.edu!n8emr!uncle!donlash From: donlash@uncle.UUCP (Donald Lashomb) Newsgroups: comp.sys.att Subject: cron for unix-pc file 3 of 3 Keywords: cron crontab Message-ID: <650@uncle.UUCP> Date: 29 Dec 89 03:17:31 GMT Reply-To: donlash@uncle.UUCP (Donald Lashomb) Organization: U.N.C.L.E. Lines: 2296 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 X#include X#include X#include X#include X#include X#include X#include 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;nuid == 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 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 X#include X#include X#include 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 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.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<> ^ X<> | X<> | X<> -or- '\0' | X__________________________________________| X X<> 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 log.c X/* handle daemon's log file */ X X#include X#include X#include X#include 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 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 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 X#include X#include 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 parsesched.c X/* parse: cronjob X * X * =
X * X * = X * X * = X * X *
= X * X * = X * | X * X * = X * | X * X * = X * |, X * X * = X * |- X * X */ X X#include X#include X#include X#include X#include 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 parsetime.c X/* parse: at