SysVR4/cmd/cron/cron.c

Compare this file to the similar file:
Show the results in this format:

/*	Copyright (c) 1990 UNIX System Laboratories, Inc.	*/
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF     	*/
/*	UNIX System Laboratories, Inc.                     	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/


/*	Portions Copyright(c) 1988, Sun Microsystems, Inc.	*/
/*	All Rights Reserved.					*/

/*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
/*	  All Rights Reserved	*/

/*	This Module contains Proprietary Information of Microsoft  */
/*	Corporation and should be treated as Confidential.	   */

#ident	"@(#)cron:cron.c	1.17.6.3"
#include <sys/types.h>
#include <sys/param.h>
#include <dirent.h>
#include <sys/stat.h>
#include <pwd.h>
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#include <deflt.h>
#include <unistd.h>
#include <locale.h>
#include "cron.h"

#define MAIL		"/usr/bin/mail"	/* mail program to use */
#define CONSOLE		"/dev/console"	/* where to write error messages when cron dies	*/

#define TMPINFILE	"/tmp/crinXXXXXX"  /* file to put stdin in for cmd  */
#define	TMPDIR		"/tmp"
#define	PFX		"crout"
#define TMPOUTFILE	"/tmp/croutXXXXXX" /* file to place stdout, stderr */

#define INMODE		00400		/* mode for stdin file	*/
#define OUTMODE		00600		/* mode for stdout file */
#define ISUID		06000		/* mode for verifing at jobs */

#define INFINITY	2147483647L	/* upper bound on time	*/
#define CUSHION		120L
#define	MAXRUN		25		/* max total jobs allowed in system */
#define ZOMB		100		/* proc slot used for mailing output */

#define	JOBF		'j'
#define	NICEF		'n'
#define	USERF		'u'
#define WAITF		'w'

#define BCHAR		'>'
#define	ECHAR		'<'

#define	DEFAULT		0
#define	LOAD		1

/* Defined actions for crabort() routine */
#define	NO_ACTION	000
#define	REMOVE_FIFO	001
#define	CONSOLE_MSG	002

#define BADCD		"can't change directory to the crontab directory."
#define NOREADDIR	"can't read the crontab directory."

#define BADJOBOPEN	"unable to read your at job."
#define BADSHELL	"because your login shell isn't /usr/bin/sh, you can't use cron."
#define BADSTAT		"can't access your crontab file.  Resubmit it."
#define CANTCDHOME	"can't change directory to your home directory.\nYour commands will not be executed."
#define CANTEXECSH	"unable to exec the shell for one of your commands."
#define EOLN		"unexpected end of line"
#define NOREAD		"can't read your crontab file.  Resubmit it."
#define NOSTDIN		"unable to create a standard input file for one of your crontab commands.\nThat command was not executed."
#define OUTOFBOUND	"number too large or too small for field"
#define STDERRMSG	"\n\n*************************************************\nCron: The previous message is the standard output\n      and standard error of one of your cron commands.\n"
#define STDOUTERR	"one of your commands generated output or errors, but cron was unable to mail you this output.\nRemember to redirect standard output and standard error for each of your commands."
#define UNEXPECT	"unexpected symbol found"
#define DIDFORK didfork
#define NOFORK !didfork

#define	ERR_CRONTABENT	0	/* error in crontab file entry */
#define	ERR_UNIXERR	1	/* error in some system call */
#define	ERR_CANTEXECCRON 2	/* error in setting up "cron" job environment*/
#define	ERR_CANTEXECAT	3	/* error in setting up "at" job environment */


#define	FORMAT	"%a %b %e %H:%M:%S %Y"
char	timebuf[80];

struct event {	
	time_t time;	/* time of the event	*/
	short etype;	/* what type of event; 0=cron, 1=at	*/
	char *cmd;	/* command for cron, job name for at	*/
	struct usr *u;	/* ptr to the owner (usr) of this event	*/
	struct event *link; 	/* ptr to another event for this user */
	union { 
		struct { /* for crontab events */
			char *minute;	/*  (these	*/
			char *hour;	/*   fields	*/
			char *daymon;	/*   are	*/
			char *month;	/*   from	*/
			char *dayweek;	/*   crontab)	*/
			char *input;	/* ptr to stdin	*/
		} ct;
		struct { /* for at events */
			short exists;	/* for revising at events	*/
			int eventid;	/* for el_remove-ing at events	*/
		} at;
	} of; 
};

struct usr {	
	char *name;	/* name of user (e.g. "root")	*/
	char *home;	/* home directory for user	*/
	uid_t uid;	/* user id	*/
	gid_t gid;	/* group id	*/
#ifdef ATLIMIT
	int aruncnt;	/* counter for running jobs per uid */
#endif
#ifdef CRONLIMIT
	int cruncnt;	/* counter for running cron jobs per uid */
#endif
	int ctid;	/* for el_remove-ing crontab events */
	short ctexists;	/* for revising crontab events	*/
	struct event *ctevents;	/* list of this usr's crontab events */
	struct event *atevents;	/* list of this usr's at events */
	struct usr *nextusr; 
};	/* ptr to next user	*/

struct	queue
{
	int njob;	/* limit */
	int nice;	/* nice for execution */
	int nwait;	/* wait time to next execution attempt */
	int nrun;	/* number running */
}	
	qd = {100, 2, 60},		/* default values for queue defs */
	qt[NQUEUE];
struct	queue	qq;
int	wait_time = 60;

struct	runinfo
{
	pid_t	pid;
	short	que;
	struct  usr *rusr;	/* pointer to usr struct */
	char 	*outfile;	/* file where stdout & stderr are trapped */
	short	jobtype;	/* what type of event: 0=cron, 1=at */
	char	*jobname;	/* command for "cron", jobname for "at" */
	int	mailwhendone;	/* 1 = send mail even if no ouptut */
}	rt[MAXRUN];

short didfork = 0;	/* flag to see if I'm process group leader */
int msgfd;		/* file descriptor for fifo queue */
int ecid=1;		/* for giving event classes distinguishable id names 
			   for el_remove'ing them.  MUST be initialized to 1 */
short jobtype;		/* at or batch job */
int delayed;		/* is job being rescheduled or did it run first time */
int notexpired;		/* time for next job has not come */
int cwd;		/* current working directory */
int running;		/* zero when no jobs are executing */
struct event *next_event;	/* the next event to execute	*/
struct usr *uhead;	/* ptr to the list of users	*/
struct usr *ulast;	/* ptr to last usr table entry */
time_t init_time,num(),time();
char *strcpy(),*strncpy(),*strcat();
extern char *xmalloc();

/* user's default environment for the shell */
char homedir[100]="HOME=";
char logname[50]="LOGNAME=";
char tzone[100]="TZ=";
char *envinit[]={
	homedir,
	logname,
	"PATH=/sbin:/usr/bin:/usr/sbin:/usr/lbin:",
	"SHELL=/usr/bin/sh",
	tzone,
	0};
extern char **environ;

/* added for xenix */
#define DEFTZ		"ESTEDT"
int 	log = 0;
char 	hzname[10];
/* end of xenix */

void cronend();
void timeout();

main(argc,argv)
int argc;
char **argv;
{
	time_t t,t_old;
	time_t last_time;
	time_t ne_time;		/* amt of time until next event execution */
	time_t next_time();
	time_t lastmtime = 0L;
	struct usr *u,*u2;
	struct event *e,*e2,*eprev;
	struct stat buf;
	long seconds;
	pid_t rfork;

begin:
	(void)setlocale(LC_ALL, "");
	/* fork unless 'nofork' is specified */
	if((argc <= 1) || (strcmp(argv[1],"nofork"))) {
		if (rfork = fork()) {
			if (rfork == (pid_t)-1) {
				sleep(30);
				goto begin; 
			}
			exit(0); 
		}
		didfork++;
		setpgrp();	/* detach cron from console */
	}

	umask(022);
	signal(SIGHUP, SIG_IGN);
	signal(SIGINT, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
	signal(SIGTERM, cronend);

	defaults();
	initialize(1);
	quedefs(DEFAULT);	/* load default queue definitions */
	msg("*** cron started ***   pid = %d",getpid());
	timeout();	/* set up alarm clock trap */
	t_old = time((long *) 0);
	last_time = t_old;
	while (TRUE) {			/* MAIN LOOP	*/
		t = time((long *) 0);
		if((t_old > t) || (t-last_time > CUSHION)) {
			/* the time was set backwards or forward */
			el_delete();
			u = uhead;
			while (u!=NULL) {
				rm_ctevents(u);
				e = u->atevents;
				while (e!=NULL) {
					free(e->cmd);
					e2 = e->link;
					free(e);
					e = e2; 
				}
				u2 = u->nextusr;
				u = u2; 
			}
			close(msgfd);
			initialize(0);
			t = time((long *) 0); 
		}
		t_old = t;
		if (next_event == NULL) {
			if (el_empty()) ne_time = INFINITY;
			else {	
				next_event = (struct event *) el_first();
				ne_time = next_event->time - t; 
			}
		} else {
			ne_time = next_event->time - t;
#ifdef DEBUG
			cftime(timebuf, FORMAT, &next_event->time);
			fprintf(stderr, "next_time=%ld %s\n",
				next_event->time, timebuf);
#endif
		}
		seconds = (ne_time < (long) 0) ? (long) 0 : ne_time;
		if(ne_time > (long) 0)
			idle(seconds);
		if(notexpired) {
			notexpired = 0;
			last_time = INFINITY;
			continue;
		}
		if(stat(QUEDEFS,&buf))
			msg("cannot stat QUEDEFS file");
		else
			if(lastmtime != buf.st_mtime) {
				quedefs(LOAD);
				lastmtime = buf.st_mtime;
			}
		last_time = next_event->time;	/* save execution time */
		ex(next_event);
		switch(next_event->etype) {
		/* add cronevent back into the main event list */
		case CRONEVENT:
			if(delayed) {
				delayed = 0;
				break;
			}
			next_event->time = next_time(next_event);
			el_add( next_event,next_event->time,
			    (next_event->u)->ctid ); 
			break;
		/* remove at or batch job from system */
		default:
			eprev=NULL;
			e=(next_event->u)->atevents;
			while (e != NULL)
				if (e == next_event) {
					if (eprev == NULL)
						(e->u)->atevents = e->link;
					else	eprev->link = e->link;
					free(e->cmd);
					free(e);
					break;	
				}
				else {	
					eprev = e;
					e = e->link; 
				}
			break;
		}
		next_event = NULL; 
	}
}

initialize(firstpass)
{
	char *getenv();
	static int flag = 0;

#ifdef DEBUG
	fprintf(stderr,"in initialize\n");
#endif
	init_time = time((long *) 0);
	el_init(8,init_time,(long)(60*60*24),10);
	if(firstpass) {
		/* for mail(1), make sure messages come from root */
		(void)putenv("LOGNAME=root");
		if(access(FIFO,R_OK) == -1) {
			if(errno == ENOENT) {
				if(mknod(FIFO,S_IFIFO|0600,0)!=0)
					crabort("cannot create fifo queue",
						REMOVE_FIFO|CONSOLE_MSG);
			} 
			else  {
				if(NOFORK) {
					/* didn't fork... init(1M) is waiting */
					sleep(60);
				}
				perror("FIFO");
				crabort("cannot access fifo queue",
					REMOVE_FIFO|CONSOLE_MSG);
			}
		}
		else {
			if(NOFORK) {
				/* didn't fork... init(1M) is waiting */
				sleep(60); /* the wait is painful, but we don't
					   want init respawning this quickly */
			}
			crabort("cannot start cron; FIFO exists", CONSOLE_MSG);
		}
	}
	if((msgfd = open(FIFO, O_RDWR)) < 0) {
		perror("! open");
		crabort("cannot open fifo queue", REMOVE_FIFO|CONSOLE_MSG);
	}
	sprintf(tzone,"TZ=%s",getenv("TZ"));

	/* read directories, create users list,
	   and add events to the main event list	*/
	uhead = NULL;
	read_dirs();
	next_event = NULL;
	if(flag)
		return;
	if(freopen(ACCTFILE,"a",stdout) == NULL)
		fprintf(stderr,"cannot open %s\n",ACCTFILE);
	close(fileno(stderr));
	dup(1);
	/* this must be done to make popen work....i dont know why */
	freopen("/dev/null","r",stdin);
	flag = 1;
}


read_dirs()
{
	DIR	*dir, *opendir();
	int mod_ctab(), mod_atjob();

	if (chdir(CRONDIR) == -1)
		crabort(BADCD, REMOVE_FIFO|CONSOLE_MSG);
	cwd = CRON;
	if ((dir=opendir("."))==NULL)
		crabort(NOREADDIR, REMOVE_FIFO|CONSOLE_MSG);
	dscan(dir,mod_ctab);
	closedir(dir);
	if(chdir(ATDIR) == -1) {
		msg("cannot chdir to at directory");
		return;
	}
	cwd = AT;
	if ((dir=opendir("."))==NULL) {
		msg("cannot read at at directory");
		return; 
	}
	dscan(dir,mod_atjob);
	closedir(dir);
}

dscan(df,fp)
DIR	*df;
int	(*fp)();
{

	register	i, dn;
	register	struct	dirent	*dp;

	while((dp=readdir(df)) != NULL) {
		(*fp) (dp->d_name);
	}
}

mod_ctab(name)
char	*name;
{

	struct	passwd	*pw;
	struct	stat	buf;
	struct	usr	*u,*find_usr();
	char	namebuf[132];
	char	*pname;

	if((pw=getpwnam(name)) == NULL)
		return;
	if(cwd != CRON) {
		strcat(strcat(strcpy(namebuf,CRONDIR),"/"),name);
		pname = namebuf;
	} else
		pname = name;
	/* a warning message is given by the crontab command so there is
	   no need to give one here......use this code if you only want users
	   with a login shell of /usr/bin/sh to use cron
	if((strcmp(pw->pw_shell,"")!=0) && (strcmp(pw->pw_shell,SHELL)!=0)){
			mail(name,BADSHELL,ERR_CANTEXECCRON);
			unlink(pname);
			return;
	}
	*/
	if(stat(pname,&buf)) {
		mail(name,BADSTAT,ERR_UNIXERR);
		unlink(pname);
		return;
	}
	if((u=find_usr(name)) == NULL) {
#ifdef DEBUG
		fprintf(stderr,"new user (%s) with a crontab\n",name);
#endif
		u = (struct usr *) xmalloc(sizeof(struct usr));
		u->name = xmalloc(strlen(name)+1);
		strcpy(u->name,name);
		u->home = xmalloc(strlen(pw->pw_dir)+1);
		strcpy(u->home,pw->pw_dir);
		u->uid = pw->pw_uid;
		u->gid = pw->pw_gid;
		u->ctexists = TRUE;
		u->ctid = ecid++;
		u->ctevents = NULL;
		u->atevents = NULL;
#ifdef ATLIMIT
		u->aruncnt = 0;
#endif
#ifdef CRONLIMIT
		u->cruncnt = 0;
#endif
		u->nextusr = uhead;
		uhead = u;
		readcron(u);
	} else {
		u->uid = pw->pw_uid;
		u->gid = pw->pw_gid;
		if(strcmp(u->home,pw->pw_dir) != 0) {
			free(u->home);
			u->home = xmalloc(strlen(pw->pw_dir)+1);
			strcpy(u->home,pw->pw_dir);
		}
		u->ctexists = TRUE;
		if(u->ctid == 0) {
#ifdef DEBUG
			fprintf(stderr,"%s now has a crontab\n",u->name);
#endif
			/* user didnt have a crontab last time */
			u->ctid = ecid++;
			u->ctevents = NULL;
			readcron(u);
			return;
		}
#ifdef DEBUG
		fprintf(stderr,"%s has revised his crontab\n",u->name);
#endif
		rm_ctevents(u);
		el_remove(u->ctid,0);
		readcron(u);
	}
}


mod_atjob(name)
char	*name;
{

	char	*ptr;
	time_t	tim;
	struct	passwd	*pw;
	struct	stat	buf;
	struct	usr	*u,*find_usr();
	struct	event	*e;
	char	namebuf[132];
	char	*pname;

	ptr = name;
	if(((tim=num(&ptr)) == 0) || (*ptr != '.'))
		return;
	ptr++;
	if(!isalpha(*ptr))
		return;
	jobtype = *ptr - 'a';
	if(cwd != AT) {
		strcat(strcat(strcpy(namebuf,ATDIR),"/"),name);
		pname = namebuf;
	} else
		pname = name;
	if(stat(pname,&buf) || jobtype >= NQUEUE) {
		unlink(pname);
		return;
	}
	if(!(buf.st_mode & ISUID)) {
		unlink(pname);
		return;
	}
	if((pw=getpwuid(buf.st_uid)) == NULL)
		return;
	/* a warning message is given by the at command so there is no
	   need to give one here......use this code if you only want users
	   with a login shell of /usr/bin/sh to use cron
	if((strcmp(pw->pw_shell,"")!=0) && (strcmp(pw->pw_shell,SHELL)!=0)){
			mail(pw->pw_name,BADSHELL,ERR_CANTEXECAT);
			unlink(pname);
			return;
	}
	*/
	if((u=find_usr(pw->pw_name)) == NULL) {
#ifdef DEBUG
		fprintf(stderr,"new user (%s) with an at job = %s\n",pw->pw_name,name);
#endif
		u = (struct usr *) xmalloc(sizeof(struct usr));
		u->name = xmalloc(strlen(pw->pw_name)+1);
		strcpy(u->name,pw->pw_name);
		u->home = xmalloc(strlen(pw->pw_dir)+1);
		strcpy(u->home,pw->pw_dir);
		u->uid = pw->pw_uid;
		u->gid = pw->pw_gid;
		u->ctexists = FALSE;
		u->ctid = 0;
		u->ctevents = NULL;
		u->atevents = NULL;
#ifdef ATLIMIT
		u->aruncnt = 0;
#endif
#ifdef CRONLIMIT
		u->cruncnt = 0;
#endif
		u->nextusr = uhead;
		uhead = u;
		add_atevent(u,name,tim);
	} else {
		u->uid = pw->pw_uid;
		u->gid = pw->pw_gid;
		if(strcmp(u->home,pw->pw_dir) != 0) {
			free(u->home);
			u->home = xmalloc(strlen(pw->pw_dir)+1);
			strcpy(u->home,pw->pw_dir);
		}
		e = u->atevents;
		while(e != NULL)
			if(strcmp(e->cmd,name) == 0) {
				e->of.at.exists = TRUE;
				break;
			} else
				e = e->link;
		if (e == NULL) {
#ifdef DEBUG
			fprintf(stderr,"%s has a new at job = %s\n",u->name,name);
#endif
			add_atevent(u,name,tim);
		}
	}
}



add_atevent(u,job,tim)
struct usr *u;
char *job;
time_t tim;
{
	struct event *e;

	e=(struct event *) xmalloc(sizeof(struct event));
	e->etype = jobtype;
	e->cmd = xmalloc(strlen(job)+1);
	strcpy(e->cmd,job);
	e->u = u;
#ifdef DEBUG
	fprintf(stderr,"add_atevent: user=%s, job=%s, time=%ld\n",
		u->name,e->cmd, e->time);
#endif
	e->link = u->atevents;
	u->atevents = e;
	e->of.at.exists = TRUE;
	e->of.at.eventid = ecid++;
	if(tim < init_time)		/* old job */
		e->time = init_time;
	else
		e->time = tim;
	el_add(e, e->time, e->of.at.eventid); 
}


char line[CTLINESIZE];		/* holds a line from a crontab file	*/
int cursor;			/* cursor for the above line	*/

readcron(u)
struct usr *u;
{
	/* readcron reads in a crontab file for a user (u).
	   The list of events for user u is built, and 
	   u->events is made to point to this list.
	   Each event is also entered into the main event list. */

	FILE *fopen(),*cf;		/* cf will be a user's crontab file */
	time_t next_time();
	struct event *e;
	int start,i;
	char *next_field();
	char namebuf[132];
	char *pname;

	/* read the crontab file */
	if(cwd != CRON) {
		strcat(strcat(strcpy(namebuf,CRONDIR),"/"),u->name);
		pname = namebuf;
	} else
		pname = u->name;
	if ((cf=fopen(pname,"r")) == NULL) {
		mail(u->name,NOREAD,ERR_UNIXERR);
		return; 
	}
	while (fgets(line,CTLINESIZE,cf) != NULL) {
		/* process a line of a crontab file */
		cursor = 0;
		while(line[cursor] == ' ' || line[cursor] == '\t')
			cursor++;
		if(line[cursor] == '#')
			continue;
		e = (struct event *) xmalloc(sizeof(struct event));
		e->etype = CRONEVENT;
		if ((e->of.ct.minute=next_field(0,59,u)) == NULL) goto badline;
		if ((e->of.ct.hour=next_field(0,23,u)) == NULL) goto badline;
		if ((e->of.ct.daymon=next_field(1,31,u)) == NULL) goto badline;
		if ((e->of.ct.month=next_field(1,12,u)) == NULL) goto badline;
		if ((e->of.ct.dayweek=next_field(0,6,u)) == NULL) goto badline;
		if (line[++cursor] == '\0') {
			mail(u->name,EOLN,ERR_CRONTABENT);
			goto badline; 
		}
		/* get the command to execute	*/
		start = cursor;
again:
		while ((line[cursor]!='%')&&(line[cursor]!='\n')
		    &&(line[cursor]!='\0') && (line[cursor]!='\\')) cursor++;
		if(line[cursor] == '\\') {
			cursor += 2;
			goto again;
		}
		e->cmd = xmalloc(cursor-start+1);
		strncpy(e->cmd,line+start,cursor-start);
		e->cmd[cursor-start] = '\0';
		/* see if there is any standard input	*/
		if (line[cursor] == '%') {
			e->of.ct.input = xmalloc(strlen(line)-cursor+1);
			strcpy(e->of.ct.input,line+cursor+1);
			for (i=0; i<strlen(e->of.ct.input); i++)
				if (e->of.ct.input[i] == '%') e->of.ct.input[i] = '\n'; 
		}
		else e->of.ct.input = NULL;
		/* have the event point to it's owner	*/
		e->u = u;
		/* insert this event at the front of this user's event list   */
		e->link = u->ctevents;
		u->ctevents = e;
		/* set the time for the first occurance of this event	*/
		e->time = next_time(e);
		/* finally, add this event to the main event list	*/
		el_add(e,e->time,u->ctid);
#ifdef DEBUG
		cftime(timebuf, FORMAT, &e->time);
		fprintf(stderr,"inserting cron event %s at %ld (%s)\n",
			e->cmd,e->time,timebuf);
#endif
		continue;

badline: 
		free(e); 
	}

	fclose(cf);
}



mail(usrname,msg,format)
char *usrname,*msg;
int format;
{
	/* mail mails a user a message.	*/

	FILE *pipe,*popen();
	char *temp,*i,*strrchr();
	struct passwd	*ruser_ids;
	pid_t fork_val;
	int saveerrno = errno;

#ifdef TESTING
	return;
#endif
	
	if ((fork_val = fork()) == (pid_t)-1) {
		running++;
		return;
	}
	if (fork_val == 0) {
		if ((ruser_ids = getpwnam(usrname)) == (struct passwd *)NULL)
				exit(0);
		setuid(ruser_ids->pw_uid);
		temp = xmalloc(strlen(MAIL)+strlen(usrname)+2);
		pipe = popen(strcat(strcat(strcpy(temp,MAIL)," "),usrname),"w");
		if (pipe!=NULL) {
		    fprintf(pipe,"To: %s\n", usrname);
		    switch (format) {
			case ERR_CRONTABENT:
			    fprintf(pipe,"Subject: Your crontab file has an error in it\n\n");
			    i = strrchr(line,'\n');
			    if (i != NULL) *i = ' ';
  			    fprintf(pipe, "\t%s\n\t%s\n",line,msg);
			    fprintf(pipe, "This entry has been ignored.\n"); 
				    break;	
			case ERR_UNIXERR:
		  	    fprintf(pipe, "Subject: %s\n\n", msg);
			    fprintf(pipe, "The error was \"%s\"\n", errmsg(saveerrno));
			    break;

			case ERR_CANTEXECCRON:
			    fprintf(pipe, "Subject: Couldn't run your \"cron\" job\n\n");
			    fprintf(pipe, "%s\n", msg);
			    fprintf(pipe, "The error was \"%s\"\n", errmsg(saveerrno));

			case ERR_CANTEXECAT:
	       	            fprintf(pipe, "Subject: Couldn't run your \"at\" job\n\n");
			    fprintf(pipe, "%s\n", msg);
			    fprintf(pipe, "The error was \"%s\"\n", errmsg(saveerrno));
		    }
		    pclose(pipe); 
		}
		free(temp);
		exit(0);
	}
	/* decremented in idle() */
	running++;
}



char
*next_field(lower,upper,u)
int lower,upper;
struct usr *u;
{
	/* next_field returns a pointer to a string which holds 
	   the next field of a line of a crontab file.
	   if (numbers in this field are out of range (lower..upper),
	       or there is a syntax error) then
			NULL is returned, and a mail message is sent to
			the user telling him which line the error was in.     */

	char *s;
	int num,num2,start;

	while ((line[cursor]==' ') || (line[cursor]=='\t')) cursor++;
	start = cursor;
	if (line[cursor] == '\0') {
		mail(u->name,EOLN,ERR_CRONTABENT);
		return(NULL); 
	}
	if (line[cursor] == '*') {
		cursor++;
		if ((line[cursor]!=' ') && (line[cursor]!='\t')) {
			mail(u->name,UNEXPECT,ERR_CRONTABENT);
			return(NULL); 
		}
		s = xmalloc(2);
		strcpy(s,"*");
		return(s); 
	}
	while (TRUE) {
		if (!isdigit(line[cursor])) {
			mail(u->name,UNEXPECT,ERR_CRONTABENT);
			return(NULL); 
		}
		num = 0;
		do { 
			num = num*10 + (line[cursor]-'0'); 
		}			while (isdigit(line[++cursor]));
		if ((num<lower) || (num>upper)) {
			mail(u->name,OUTOFBOUND,ERR_CRONTABENT);
			return(NULL); 
		}
		if (line[cursor]=='-') {
			if (!isdigit(line[++cursor])) {
				mail(u->name,UNEXPECT,ERR_CRONTABENT);
				return(NULL); 
			}
			num2 = 0;
			do { 
				num2 = num2*10 + (line[cursor]-'0'); 
			}				while (isdigit(line[++cursor]));
			if ((num2<lower) || (num2>upper)) {
				mail(u->name,OUTOFBOUND,ERR_CRONTABENT);
				return(NULL); 
			}
		}
		if ((line[cursor]==' ') || (line[cursor]=='\t')) break;
		if (line[cursor]=='\0') {
			mail(u->name,EOLN,ERR_CRONTABENT);
			return(NULL); 
		}
		if (line[cursor++]!=',') {
			mail(u->name,UNEXPECT,ERR_CRONTABENT);
			return(NULL); 
		}
	}
	s = xmalloc(cursor-start+1);
	strncpy(s,line+start,cursor-start);
	s[cursor-start] = '\0';
	return(s);
}


time_t
next_time(e)
struct event *e;
{
	/* returns the integer time for the next occurance of event e.
	   the following fields have ranges as indicated:
	PRGM  | min	hour	day of month	mon	day of week
	------|-------------------------------------------------------
	cron  | 0-59	0-23	    1-31	1-12	0-6 (0=sunday)
	time  | 0-59	0-23	    1-31	0-11	0-6 (0=sunday)
	   NOTE: this routine is hard to understand. */

	struct tm *tm,*localtime();
	int tm_mon,tm_mday,tm_wday,wday,m,min,h,hr,carry,day,days,
	d1,day1,carry1,d2,day2,carry2,daysahead,mon,yr,db,wd,today;
	time_t t;
	static int firstpass = 1, dst;

	t = time((long *) 0);
	tm = localtime(&t);

	tm_mon = next_ge(tm->tm_mon+1,e->of.ct.month) - 1;	/* 0-11 */
	tm_mday = next_ge(tm->tm_mday,e->of.ct.daymon);		/* 1-31 */
	tm_wday = next_ge(tm->tm_wday,e->of.ct.dayweek);	/* 0-6  */
	today = TRUE;
	if ( (strcmp(e->of.ct.daymon,"*")==0 && tm->tm_wday!=tm_wday)
	    || (strcmp(e->of.ct.dayweek,"*")==0 && tm->tm_mday!=tm_mday)
	    || (tm->tm_mday!=tm_mday && tm->tm_wday!=tm_wday)
	    || (tm->tm_mon!=tm_mon)) today = FALSE;

	m = tm->tm_min+1;
	if ((tm->tm_hour + 1) <= next_ge(tm->tm_hour%24, e->of.ct.hour)) {
		m = 0;
	}
	min = next_ge(m%60,e->of.ct.minute);
	carry = (min < m) ? 1:0;
	h = tm->tm_hour+carry;
	hr = next_ge(h%24,e->of.ct.hour);
	carry = (hr < h) ? 1:0;
	if ((!carry) && today) {
		/* this event must occur today	*/
		if (tm->tm_min>min)
			t +=(time_t)(hr-tm->tm_hour-1)*HOUR + 
			    (time_t)(60-tm->tm_min+min)*MINUTE;
		else t += (time_t)(hr-tm->tm_hour)*HOUR +
			(time_t)(min-tm->tm_min)*MINUTE;
		return(t-(long)tm->tm_sec); 
	}

	min = next_ge(0,e->of.ct.minute);
	hr = next_ge(0,e->of.ct.hour);

	/* calculate the date of the next occurance of this event,
	   which will be on a different day than the current day.	*/

	/* check monthly day specification	*/
	d1 = tm->tm_mday+1;
	day1 = next_ge((d1-1)%days_in_mon(tm->tm_mon,tm->tm_year)+1,e->of.ct.daymon);
	carry1 = (day1 < d1) ? 1:0;

	/* check weekly day specification	*/
	d2 = tm->tm_wday+1;
	wday = next_ge(d2%7,e->of.ct.dayweek);
	if (wday < d2) daysahead = 7 - d2 + wday;
	else daysahead = wday - d2;
	day2 = (d1+daysahead-1)%days_in_mon(tm->tm_mon,tm->tm_year)+1;
	carry2 = (day2 < d1) ? 1:0;

	/* based on their respective specifications,
	   day1, and day2 give the day of the month
	   for the next occurance of this event.	*/

	if ((strcmp(e->of.ct.daymon,"*")==0) && (strcmp(e->of.ct.dayweek,"*")!=0)) {
		day1 = day2;
		carry1 = carry2; 
	}
	if ((strcmp(e->of.ct.daymon,"*")!=0) && (strcmp(e->of.ct.dayweek,"*")==0)) {
		day2 = day1;
		carry2 = carry1; 
	}

	yr = tm->tm_year;
	if ((carry1 && carry2) || (tm->tm_mon != tm_mon)) {
		/* event does not occur in this month	*/
		m = tm->tm_mon+1;
		mon = next_ge(m%12+1,e->of.ct.month)-1;		/* 0..11 */
		carry = (mon < m) ? 1:0;
		yr += carry;
		/* recompute day1 and day2	*/
		day1 = next_ge(1,e->of.ct.daymon);
		db = days_btwn(tm->tm_mon,tm->tm_mday,tm->tm_year,mon,1,yr) + 1;
		wd = (tm->tm_wday+db)%7;
		/* wd is the day of the week of the first of month mon	*/
		wday = next_ge(wd,e->of.ct.dayweek);
		if (wday < wd) day2 = 1 + 7 - wd + wday;
		else day2 = 1 + wday - wd;
		if ((strcmp(e->of.ct.daymon,"*")!=0) && (strcmp(e->of.ct.dayweek,"*")==0))
			day2 = day1;
		if ((strcmp(e->of.ct.daymon,"*")==0) && (strcmp(e->of.ct.dayweek,"*")!=0))
			day1 = day2;
		day = (day1 < day2) ? day1:day2; 
	}
	else { /* event occurs in this month	*/
		mon = tm->tm_mon;
		if (!carry1 && !carry2) day = (day1 < day2) ? day1 : day2;
		else if (!carry1) day = day1;
		else day = day2;
	}

	/* now that we have the min,hr,day,mon,yr of the next
	   event, figure out what time that turns out to be.	*/

	days = days_btwn(tm->tm_mon,tm->tm_mday,tm->tm_year,mon,day,yr);
	t += (time_t)(23-tm->tm_hour)*HOUR + (time_t)(60-tm->tm_min)*MINUTE
	    + (time_t)hr*HOUR + (time_t)min*MINUTE + (time_t)days*DAY;
	return(t-(long)tm->tm_sec);
}



#define	DUMMY	100
next_ge(current,list)
int current;
char *list;
{
	/* list is a character field as in a crontab file;
	   	for example: "40,20,50-10"
	   next_ge returns the next number in the list that is
	   greater than or equal to current.
	   if no numbers of list are >= current, the smallest
	   element of list is returned.
	   NOTE: current must be in the appropriate range.	*/

	char *ptr;
	int n,n2,min,min_gt;

	if (strcmp(list,"*") == 0) return(current);
	ptr = list;
	min = DUMMY; 
	min_gt = DUMMY;
	while (TRUE) {
		if ((n=(int)num(&ptr))==current) return(current);
		if (n<min) min=n;
		if ((n>current)&&(n<min_gt)) min_gt=n;
		if (*ptr=='-') {
			ptr++;
			if ((n2=(int)num(&ptr))>n) {
				if ((current>n)&&(current<=n2))
					return(current); 
			}
			else {	/* range that wraps around */
				if (current>n) return(current);
				if (current<=n2) return(current); 
			}
		}
		if (*ptr=='\0') break;
		ptr += 1; 
	}
	if (min_gt!=DUMMY) return(min_gt);
	else return(min);
}

del_atjob(name,usrname)
char	*name;
char	*usrname;
{

	struct	event	*e, *eprev;
	struct	usr	*u, *find_usr();

	if((u = find_usr(usrname)) == NULL)
		return;
	e = u->atevents;
	eprev = NULL;
	while(e != NULL)
		if(strcmp(name,e->cmd) == 0) {
			if(next_event == e)
				next_event = NULL;
			if(eprev == NULL)
				u->atevents = e->link;
			else
				eprev->link = e->link;
			el_remove(e->of.at.eventid, 1);
			free(e->cmd);
			free(e);
			break;
		} else {
			eprev = e;
			e = e->link;
		}
	if(!u->ctexists && u->atevents == NULL) {
#ifdef DEBUG
		fprintf(stderr,"%s removed from usr list\n",usrname);
#endif
		if(ulast == NULL)
			uhead = u->nextusr;
		else
			ulast->nextusr = u->nextusr;
		free(u->name);
		free(u->home);
		free(u);
	}
}

del_ctab(name)
char	*name;
{

	struct	usr	*u, *find_usr();

	if((u = find_usr(name)) == NULL)
		return;
	rm_ctevents(u);
	el_remove(u->ctid, 0);
	u->ctid = 0;
	u->ctexists = 0;
	if(u->atevents == NULL) {
#ifdef DEBUG
		fprintf(stderr,"%s removed from usr list\n",name);
#endif
		if(ulast == NULL)
			uhead = u->nextusr;
		else
			ulast->nextusr = u->nextusr;
		free(u->name);
		free(u->home);
		free(u);
	}
}


rm_ctevents(u)
struct usr *u;
{
	struct event *e2,*e3;

	/* see if the next event (to be run by cron)
	   is a cronevent owned by this user.		*/
	if ( (next_event!=NULL) && 
	    (next_event->etype==CRONEVENT) &&
	    (next_event->u==u) )
		next_event = NULL;
	e2 = u->ctevents;
	while (e2 != NULL) {
		free(e2->cmd);
		free(e2->of.ct.minute);
		free(e2->of.ct.hour);
		free(e2->of.ct.daymon);
		free(e2->of.ct.month);
		free(e2->of.ct.dayweek);
		if (e2->of.ct.input != NULL) free(e2->of.ct.input);
		e3 = e2->link;
		free(e2);
		e2 = e3; 
	}
	u->ctevents = NULL;
}


struct usr
*find_usr(uname)
char *uname;
{
	struct usr *u;

	u = uhead;
	ulast = NULL;
	while (u != NULL) {
		if (strcmp(u->name,uname) == 0) return(u);
		ulast = u;
		u = u->nextusr; 
	}
	return(NULL);
}


ex(e)
struct event *e;
{

	register i,j;
	short sp_flag;
	int fd;
	pid_t rfork;
	FILE *atcmdfp;
	char mailvar[4];
	char *at_cmdfile, *cron_infile;
	char *mktemp();
	char *tempnam();
	struct stat buf;
	struct queue *qp;
	struct runinfo *rp;

	qp = &qt[e->etype];	/* set pointer to queue defs */
	if(qp->nrun >= qp->njob) {
		msg("%c queue max run limit reached",e->etype+'a');
		resched(qp->nwait);
		return;
	}
	for(rp=rt; rp < rt+MAXRUN; rp++) {
		if(rp->pid == 0)
			break;
	}
	if(rp >= rt+MAXRUN) {
		msg("MAXRUN (%d) procs reached",MAXRUN);
		resched(qp->nwait);
		return;
	}
#ifdef ATLIMIT
	if((e->u)->uid != 0 && (e->u)->aruncnt >= ATLIMIT) {
		msg("ATLIMIT (%d) reached for uid %d",ATLIMIT,(e->u)->uid);
		resched(qp->nwait);
		return;
	}
#endif
#ifdef CRONLIMIT
	if((e->u)->uid != 0 && (e->u)->cruncnt >= CRONLIMIT) {
		msg("CRONLIMIT (%d) reached for uid %d",CRONLIMIT,(e->u)->uid);
		resched(qp->nwait);
		return;
	}
#endif

	rp->outfile = tempnam(TMPDIR,PFX);
	rp->jobtype = e->etype;
	if (e->etype == CRONEVENT) {
		if ((rp->jobname = (char *)malloc(strlen(e->cmd)+1)) != NULL)
			(void) strcpy(rp->jobname, e->cmd);
		rp->mailwhendone = 0;	/* "cron" jobs only produce mail if there's output */

	} else {
		at_cmdfile = xmalloc(strlen(ATDIR)+strlen(e->cmd)+2);
		strcat(strcat(strcpy(at_cmdfile,ATDIR),"/"),e->cmd);
		if ((atcmdfp = fopen(at_cmdfile,"r")) == NULL) {
			mail((e->u)->name,BADJOBOPEN,ERR_CANTEXECAT);
			unlink(e->cmd);
			return;
		}
		if ((rp->jobname = (char *)malloc(strlen(at_cmdfile)+1)) != NULL)
			(void) strcpy(rp->jobname, at_cmdfile);
		
		/*
		 * Skip over the first two lines.
		 */
		fscanf(atcmdfp,"%*[^\n]\n");
		fscanf(atcmdfp,"%*[^\n]\n");
		if (fscanf(atcmdfp,": notify by mail: %3s%*[^\n]\n",mailvar) == 1) {
			/*
			 * Check to see if we should always send mail
			 * to the owner.
			 */
			rp->mailwhendone = (strcmp(mailvar, "yes") == 0);
		} else
			rp->mailwhendone = 0;
		(void)fclose(atcmdfp);
	}
	if((rfork = fork()) == (pid_t)-1) {
		msg("cannot fork");
		resched(wait_time);
		sleep(30);
		return;
	}
	if(rfork) {	/* parent process */
		++qp->nrun;
		++running;
		rp->pid = rfork;
		rp->que = e->etype;
#ifdef ATLIMIT
		if(e->etype != CRONEVENT)
			(e->u)->aruncnt++;
#endif
#if ATLIMIT && CRONLIMIT
		else
			(e->u)->cruncnt++;
#else
#ifdef CRONLIMIT
		if(e->etype == CRONEVENT)
			(e->u)->cruncnt++;
#endif
#endif
		rp->rusr = (e->u);
		logit((char)BCHAR,rp,0);
		return;
	}

	for (i=0; i<20; i++) close(i);

	if (e->etype != CRONEVENT ) {
		/* open jobfile as stdin to shell */
		if (stat(at_cmdfile,&buf)) exit(1);
		if (!(buf.st_mode&ISUID)) {
			/* if setuid bit off, original owner has 
			   given this file to someone else	*/
			unlink(at_cmdfile);
			exit(1); 
		}
		if (open(at_cmdfile,O_RDONLY) == -1) {
			mail((e->u)->name,BADJOBOPEN,ERR_CANTEXECCRON);
			unlink(at_cmdfile);
			exit(1); 
		}
		unlink(at_cmdfile); 
	}

	/*
	 * set correct user and group identification and initialize
	 * the supplementary group access list
	 */

	if (setgid(e->u->gid) == -1
		|| initgroups(e->u->name, e->u->gid) == -1
		|| setuid(e->u->uid) == -1)
		exit(1);
	sp_flag = FALSE;
	if (e->etype == CRONEVENT)
		/* check for standard input to command	*/
		if (e->of.ct.input != NULL) {
			cron_infile = mktemp(TMPINFILE);
			if ((fd=creat(cron_infile,INMODE)) == -1) {
				mail((e->u)->name,NOSTDIN,ERR_CANTEXECCRON);
				exit(1); 
			}
			if (write(fd,e->of.ct.input,strlen(e->of.ct.input))
			    != strlen(e->of.ct.input)) {
				mail((e->u)->name,NOSTDIN,ERR_CANTEXECCRON);
				unlink(cron_infile);
				exit(1); 
			}
			close(fd);
			/* open tmp file as stdin input to sh	*/
			if (open(cron_infile,O_RDONLY)==-1) {
				mail((e->u)->name,NOSTDIN,ERR_CANTEXECCRON);
				unlink(cron_infile);
				exit(1); 
			}
			unlink(cron_infile); 
		}
		else if (open("/dev/null",O_RDONLY)==-1) {
			open("/",O_RDONLY);
			sp_flag = TRUE; 
		}

	/* redirect stdout and stderr for the shell	*/
	if (creat(rp->outfile,OUTMODE)!=-1) dup(1);
	else if (open("/dev/null",O_WRONLY)!=-1) dup(1);
	if (sp_flag) close(0);
	strcat(homedir,(e->u)->home);
	strcat(logname,(e->u)->name);
	environ = envinit;
	if (chdir((e->u)->home)==-1) {
		mail((e->u)->name,CANTCDHOME,
		  e->etype == CRONEVENT ? ERR_CANTEXECCRON : ERR_CANTEXECAT);
		exit(1); 
	}
#ifdef TESTING
	exit(1);
#endif
	if((e->u)->uid != 0)
		nice(qp->nice);
	if (e->etype == CRONEVENT)
		execl(SHELL,"sh","-c",e->cmd,0);
	else /* type == ATEVENT */
		execl(SHELL,"sh",0);
	mail((e->u)->name,CANTEXECSH,
	    e->etype == CRONEVENT ? ERR_CANTEXECCRON : ERR_CANTEXECAT);
	exit(1);
}


idle(tyme)
long tyme;
{

	long t;
	time_t	now;
	pid_t	pid;
	int	prc;
	long	alm;
	struct	runinfo	*rp;

	t = tyme;
	while(t > 0L) {
		if(running) {
			if(t > wait_time)
				alm = wait_time;
			else
				alm = t;
#ifdef DEBUG
			fprintf(stderr,"in idle - setting alarm for %ld sec\n",alm);
#endif
			alarm((unsigned) alm);
			pid = wait(&prc);
			alarm(0);
#ifdef DEBUG
			fprintf(stderr,"wait returned %x\n",prc);
#endif
			if(pid == (pid_t)-1) {
				if(msg_wait())
					return;
			} else {
				for(rp=rt;rp < rt+MAXRUN; rp++)
					if(rp->pid == pid)
						break;
				if(rp >= rt+MAXRUN) {
					msg("unexpected pid returned %d (ignored)",pid);
					/* incremented in mail() */
					running--;
				}
				else
					if(rp->que == ZOMB) {
						running--;
						rp->pid = 0;
						free(rp->outfile);
						unlink(rp->outfile);
					}
					else
						cleanup(rp,prc);
			}
		} else {
			msg_wait();
			return;
		}
		now = time((long *) 0);
		t = (long)next_event->time - now;
	}
}


cleanup(pr,rc)
struct	runinfo	*pr;
{

	int	fd;
	char	line[5+UNAMESIZE+CTLINESIZE];
	struct	usr	*p;
	struct	stat	buf;
	struct	passwd	*ruser_ids;
	FILE	*mailpipe;
	FILE	*st;
	int	nbytes;
	char	iobuf[BUFSIZ];

	logit((char)ECHAR,pr,rc);
	--qt[pr->que].nrun;
	pr->pid = 0;
	--running;
	p = pr->rusr;
#ifdef ATLIMIT
	if(pr->que != CRONEVENT)
		--p->aruncnt;
#endif
#if ATLIMIT && CRONLIMIT
	else
		--p->cruncnt;
#else
#ifdef CRONLIMIT
	if(pr->que == CRONEVENT)
		--p->cruncnt;
#endif
#endif
	if(!stat(pr->outfile,&buf)) {
		if(buf.st_size > 0 || pr->mailwhendone) {
			/* mail user stdout and stderr */
			if((pr->pid = fork()) == 0) {

				/*
				 * Get uid for real user and become that person.
				 * We do this so that mail won't come from root since
				 * this could be a security hole.
				 * If failure, quit - don't send mail as root.
				 */
				if ((ruser_ids = getpwnam(p->name)) == 
					(struct passwd *)NULL)
					exit(0);
				setuid(ruser_ids->pw_uid);

				(void) strcpy(line, MAIL);
				(void) strcat(line, " ");
				(void) strcat(line, p->name);
				mailpipe = popen(line, "w");
				if (mailpipe == NULL)
					exit(127);
				(void) fprintf(mailpipe, "To: %s\n", p->name);
				if (pr->jobtype == CRONEVENT) {
					(void) fprintf(mailpipe,
					    "Subject: Output from \"cron\" command\n\n");
					if (pr->jobname != NULL) {
						(void) fprintf(mailpipe,
						    "Your \"cron\" job\n\n");
						(void) fprintf(mailpipe,
						    "%s\n\n", pr->jobname);
					} else
						(void) fprintf(mailpipe,
					    "Your \"cron\" job ");
				} else {
					(void) fprintf(mailpipe,
					    "Subject: Output from \"at\" job\n\n");
					(void) fprintf(mailpipe,
					    "Your \"at\" job");
					if (pr->jobname != NULL)
						(void) fprintf(mailpipe,
						    " \"%s\"", pr->jobname);
					(void) fprintf(mailpipe, " ");
				}
				if (buf.st_size > 0
				    && (st = fopen(pr->outfile, "r")) != NULL) {
					(void) fprintf(mailpipe, "produced the following output:\n\n");
					while ((nbytes = fread(iobuf,
					    sizeof (char), BUFSIZ, st)) != 0)
						(void) fwrite(iobuf,
						    sizeof (char), nbytes,
						    mailpipe);
					(void) fclose(st);
				} else
					(void) fprintf(mailpipe,
					    "completed.\n");
				(void) pclose(mailpipe);
				exit(0);
			}
			pr->que = ZOMB;
			running++;
		} else {
			unlink(pr->outfile);
			free(pr->outfile);
		}
	}
}

#define	MSGSIZE	sizeof(struct message)

msg_wait()
{

	long	t;
	time_t	now;
	struct	stat msgstat;
	struct	message	*pmsg;
	int	cnt;

	if(fstat(msgfd,&msgstat) != 0)
		crabort("cannot stat fifo queue", REMOVE_FIFO|CONSOLE_MSG);
	if(msgstat.st_size == 0 && running)
		return(0);
	if(next_event == NULL)
		t = INFINITY;
	else {
		now = time((long *) 0);
		t = next_event->time - now;
		if(t <= 0L)
			t = 1L;
	}
#ifdef DEBUG
	fprintf(stderr,"in msg_wait - setting alarm for %ld sec\n", t);
#endif
	alarm((unsigned) t);
	pmsg = &msgbuf;
	errno = 0;
	if((cnt=read(msgfd,pmsg,MSGSIZE)) != MSGSIZE) {
		if(errno != EINTR) {
			perror("! read");
			notexpired = 1;
		}
		if(next_event == NULL)
			notexpired = 1;
		return(1);
	}
	alarm(0);
	if(pmsg->etype != NULL) {
		switch(pmsg->etype) {
		case AT:
			if(pmsg->action == DELETE)
				del_atjob(pmsg->fname,pmsg->logname);
			else
				mod_atjob(pmsg->fname);
			break;
		case CRON:
			if(pmsg->action == DELETE)
				del_ctab(pmsg->fname);
			else
				mod_ctab(pmsg->fname);
			break;
		default:
			msg("message received - bad format");
			break;
		}
		if (next_event != NULL) {
			if (next_event->etype == CRONEVENT)
				el_add(next_event,next_event->time,(next_event->u)->ctid);
			else /* etype == ATEVENT */
				el_add(next_event,next_event->time,next_event->of.at.eventid);
			next_event = NULL;
		}
		fflush(stdout);
		pmsg->etype = NULL;
		notexpired = 1;
		return(1);
	}
}


void
timeout()
{
	signal(SIGALRM, timeout);
}

void
cronend()
{
	crabort("SIGTERM", REMOVE_FIFO);
}


/*
 * crabort() - handle exits out of cron 
 */
crabort(mssg, action)
	char	*mssg;
	int	action;
{
	int	c;

	if (action & REMOVE_FIFO) {
		/* FIFO vanishes when cron finishes */
		if(unlink(FIFO) < 0)
			perror("cron could not unlink FIFO");
	}

	if (action & CONSOLE_MSG) {
		/* write error msg to console */
		if ((c=open(CONSOLE,O_WRONLY))>=0) {
			write(c,"cron aborted: ",14);
			write(c,mssg,strlen(mssg));
			write(c,"\n",1);
			close(c); 
		}
	}

	/* always log the message */
	msg(mssg);
	msg("******* CRON ABORTED ********");
	exit(1);
}

msg(fmt,a,b)
char *fmt;
{

	time_t	t;

	t = time((long *) 0);
	fprintf(stderr,"! ");
	fprintf(stderr,fmt,a,b);
	cftime(timebuf, FORMAT, &t);
	fprintf(stderr, " %s\n", timebuf);
	fflush(stdout);
}


logit(cc,rp,rc)
char	cc;
struct	runinfo	*rp;
{
	time_t t;
	int    ret;

	if (!log)
                return;

	t = time((long *) 0);
	if(cc == BCHAR)
		fprintf(stderr,"%c  CMD: %s\n",cc, next_event->cmd);
	cftime(timebuf, FORMAT, &t);
	fprintf(stderr,"%c  %.8s %u %c %s",
		cc,(rp->rusr)->name, rp->pid, QUE(rp->que),timebuf);
	if((ret=TSTAT(rc)) != 0)
		fprintf(stderr," ts=%d",ret);
	if((ret=RCODE(rc)) != 0)
		fprintf(stderr," rc=%d",ret);
	putchar('\n');
	fflush(stdout);
}

resched(delay)
int	delay;
{
	time_t	nt;

	/* run job at a later time */
	nt = next_event->time + delay;
	if(next_event->etype == CRONEVENT) {
		next_event->time = next_time(next_event);
		if(nt < next_event->time)
			next_event->time = nt;
		el_add(next_event,next_event->time,(next_event->u)->ctid);
		delayed = 1;
		msg("rescheduling a cron job");
		return;
	}
	add_atevent(next_event->u, next_event->cmd, nt);
	msg("rescheduling at job");
}

#define	QBUFSIZ		80

quedefs(action)
int	action;
{
	register i;
	int	j;
	char	name[MAXNAMELEN];
	char	qbuf[QBUFSIZ];
	FILE	*fd;

	/* set up default queue definitions */
	for(i=0;i<NQUEUE;i++) {
		qt[i].njob = qd.njob;
		qt[i].nice = qd.nice;
		qt[i].nwait = qd.nwait;
	}
	if(action == DEFAULT)
		return;
	if((fd = fopen(QUEDEFS,"r")) == NULL) {
		msg("cannot open quedefs file");
		msg("using default queue definitions");
		return;
	}
	while(fgets(qbuf, QBUFSIZ, fd) != NULL) {
		if((j=qbuf[0]-'a') < 0 || j >= NQUEUE || qbuf[1] != '.')
			continue;
		i = 0;
		while(qbuf[i] != NULL)
			name[i] = qbuf[i++];
		name[i] = NULL;
		parsqdef(&name[2]);
		qt[j].njob = qq.njob;
		qt[j].nice = qq.nice;
		qt[j].nwait = qq.nwait;
	}
	fclose(fd);
}

parsqdef(name)
char *name;
{
	register i;

	qq = qd;
	while(*name) {
		i = 0;
		while(isdigit(*name)) {
			i *= 10;
			i += *name++ - '0';
		}
		switch(*name++) {
		case JOBF:
			qq.njob = i;
			break;
		case NICEF:
			qq.nice = i;
			break;
		case WAITF:
			qq.nwait = i;
			break;
		}
	}
}

/***	defaults -- read defaults	- M000 -
 *     		    from /etc/default/cron
 */

defaults()
{
	extern char *defread();
	extern int defopen();
	char *getenv();
	register int  flags;
	register char *deflog;
	char *hz, *tz;

	/*
	 * get HZ value for environment
	 */
	if ((hz = getenv("HZ")) == (char *)NULL )
		sprintf(hzname, "HZ=%d", HZ);
	else
		sprintf(hzname, "HZ=%s", hz);
	/*
	 * get TZ value for environment
	 */
	sprintf(tzone, "TZ=%s", ((tz = getenv("TZ")) != NULL) ? tz : DEFTZ);

	if (defopen(DEFFILE) == 0) {
		/* ignore case */
		flags = defcntl(DC_GETFLAGS, 0);
		TURNOFF(flags, DC_CASE);
		defcntl(DC_SETFLAGS, flags);

		if (((deflog = defread("CRONLOG=")) == NULL) ||
		     (*deflog == 'N') || (*deflog == 'n'))
			log = 0;
		else
			log = 1;
		defopen((char *) NULL);
	}
	return;
}