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