[comp.os.minix] Cron.c Version 1.0

sampson@killer.UUCP (Steve Sampson) (02/15/88)

Here's a cron that's been tested with minix version 1.1.  It uses some
code that was posted recently.  Make sure you deal with permissions on 
the files.

/*
 *	cron.c
 *
 *	CReate On Utility, Minix Version 1.0
 *	Public Domain (p) February 1988 by S. R. Sampson
 *
 *	This program operates as a daemon, waking up every minute
 *	to execute the CRONTAB table.  Logging errors to CRONERR.
 *
 *	Put in Minix rc as: /etc/cron &
 *
 *	Some notes are included below concerning the cron table
 *	file format.  In this version the cron table file is left
 *	on disk and not moved to core, and  the command character
 *	to signify a newline '%' is not used.
 *
 *	Some cron table entry examples:
 *
 *	Print the date on console every minute:
 *		* * * * * /usr/bin/date >/dev/tty0
 *
 *	Print the date on console on the hour, every hour:
 *		0 * * * * /usr/bin/date >/dev/tty0
 *
 *	Backup the files at 4:30 am every day except Sat and Sun:
 *		30 4 * * 1-5 /etc/backup /dev/fd1
 *
 *	Backup the files every other day at 7:30 pm:
 *		30 19 * * 1,3,5 /etc/backup /dev/fd1
 */

#include <stdio.h>
#include <signal.h>
#include <errno.h>

#define CRONTAB "/act/crontab"
#define CRONERR "/act/cronerr"

#define MAXLINE	132
#define SIZE	64
#define	TRUE	1
#define	FALSE	0

#define void	int

extern	int	errno;

struct	tm	{
	int	tm_sec;
	int	tm_min;
	int	tm_hour;
	int	tm_mday;
	int	tm_mon;
	int	tm_year;
	int	tm_wday;
	int	tm_yday;
	int	tm_isdst;
};

int	eof;
char	min[SIZE], hour[SIZE], day[SIZE],
	month[SIZE], wday[SIZE], command[SIZE];
char	*tokv[] = { min, hour, day, month, wday };
FILE	*fd, *err;

/*
 *	This is the basics, ready for bells and whistles
 */

main()
{
	void wakeup(), killer();

	/*
	 *	fopen() will create the file if it does not exist.
	 *	Any other errors will be output to stderr at startup.
	 */
        
	if ((err = fopen(CRONERR, "a")) == (FILE *)NULL)  {
		perror("cron");
		exit(1);
	}

	setbuf(err, (char *)NULL);	/* error I/O is now unbuffered */

	close(0); close(1); close(2);	/* close all I/O paths */

	for (;;)  {
		signal(SIGALRM, wakeup);
		alarm(59);
		pause();		/* check for work every minute */

		signal(SIGALRM, killer);
		alarm(1);
		wait((int *)NULL);	/* clean up zombie children */
	}
}


/*
 *	Cron does not wait() for child to die.  Therefore children
 *	become zombies when they complete.  In order to clear out the
 *	"floating dead babies" the parent must execute wait() every
 *	now and then.  (Thanks to Fred Buck for enlightenment
 *	See Doctor Dobbs #126 April 87, p. 152)
 */

void killer()
{
	/* edit the cron.s file and remove the garbage! */
}


void wakeup()
{
	register struct tm *tm;
	long cur_time;
	extern struct tm *localtime();
	extern char *ctime();

	time(&cur_time);		/* get the current time */
	tm = localtime(&cur_time);	/* break it down */

	if (access(CRONTAB, 1) == -1)  {
	  fprintf(err,"%s has no read permission %s",CRONTAB,ctime(&cur_time));
	  return;
	}
	else
	  fd = fopen(CRONTAB, "r");

	eof = FALSE;

	while (!eof)  {
		if (getline() && match(min,tm->tm_min) &&
		   match(hour,tm->tm_hour) && match(day,tm->tm_mday) &&
		   match(month,tm->tm_mon) && match(wday,tm->tm_wday))  {

		   /*
		    *  Execute command in the shell
		    */

		   if (fork() == 0)  {
			execl("/bin/sh", "/bin/sh", "-c", command, 0);
			fprintf(err,"Can't access shell %s", ctime(&cur_time));

			fclose(err);
			fclose(fd);

			exit(1);
		   }

		}
	}

	fclose(fd);
}


/*
 *	A line consists of six fields.  The first five are:
 *
 *		minute:         0-59
 *		hour:           0-23
 *		day:            1-31
 *		month:          1-12
 *		weekday:        0-6 (Sunday = 0)
 *
 *	The fields are seperated by spaces or tabs, with the
 *	first field left justified (no leading spaces or tabs).
 *	See below for optional field syntax.
 *
 *	The last field is the command field.  This command will
 *	be executed by the shell just as if typed from a console.
 */

getline()
{
	register char *p;
	register int   i;
	char     buffer[MAXLINE];
	extern	 char *scanner();

	if (fgets(buffer, sizeof buffer, fd) == (char *)NULL)  {
		eof = TRUE;
		return(FALSE);
	}

	for (p = buffer, i = 0; i < 5; i++)  {
		if ((p = scanner(tokv[i], p)) == (char *)NULL)
			return(FALSE);
	}

	strcpy(command, p);     /* scoop the command */
	return(TRUE);
}


char *scanner(token, offset)
register char   *token;		/* target buffer to receive scanned token */
register char   *offset;	/* place holder into source buffer */
{
	while ((*offset != ' ') && (*offset != '\t') && *offset)
		*token++ = *offset++;

	/*
	 *      Check for possible error condition
	 */
         
	if (!*offset)
		return ((char *)NULL);

	*token = '\0';
        
	while ((*offset == ' ') || (*offset == '\t'))
		offset++;

	return (offset);
}


/*
 *	This routine will match the left string with the right number.
 *
 *	The string can contain the following syntax:
 *
 *	*		This will return TRUE for any number
 *	x,y [,z, ...]	This will return TRUE for any number given.
 *	x-y		This will return TRUE for any number within
 *			the range of x thru y.
 */

match(left, right)
register char   *left;
register int    right;
{
	register int	n;
	register char	c;

	n = 0;
	if (!strcmp(left, "*"))
		return(TRUE);

	while ((c = *left++) && (c >= '0') && (c <= '9'))
		n  =  (n * 10) + c - '0';

	switch (c)  {
		case '\0':
			return (right == n);

		case ',':
			if (right == n)
				return(TRUE);
			do {
			      n = 0;
			      while ((c = *left++) && (c >= '0') && (c <= '9'))
					n = (n * 10) + c - '0';

			      if (right == n)
					return(TRUE);
			} while (c == ',');
			return(FALSE);

		case '-':
			if (right < n)
				return(FALSE);

			n = 0;
			while ((c = *left++) && (c >= '0') && (c <= '9'))
				n = (n * 10) + c - '0';

			return(right <= n);
	}
}

/*
 *	Time Routines
 *
 *	ctime() and localtime()
 */

int	days_per_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
char	*months[] =   { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
		     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
char	*days[] = { "Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed" };

struct	tm   stm;
long	s_p_min;
long	s_p_hour;
long	s_p_day;
long	s_p_wday;
long	s_p_year;
char	t_buf[26];

char *ctime(pclock)
long	*pclock;
{
	long	tt;

	s_p_min  = 60L;
	s_p_hour = 60L * 60L;
	s_p_day  = 60L * 60L * 24L;
	s_p_year = 60L * 60L * 24L * 365L;

	tt = *pclock;
	cv_time(tt);

	sprintf(t_buf, "%s %s %02d %02d:%02d:%02d %04d\n",
	   days[(tt / s_p_day) % 7], months[stm.tm_mon], stm.tm_mday,
	   stm.tm_hour, stm.tm_min, stm.tm_sec, stm.tm_year); 

	return(t_buf);
}


struct tm *localtime(pclock)
long	*pclock;
{
	cv_time(time(*pclock));

	return &stm;
}


cv_time(t)
long	t;
{
	stm.tm_sec  = 0;
	stm.tm_min  = 0;
	stm.tm_hour = 0;
	stm.tm_mday = 1;
	stm.tm_mon  = 0;
	stm.tm_year = 0;
	stm.tm_wday = 0;
	stm.tm_yday = 0;
	stm.tm_isdst= 0;

	while (t >= s_p_year)  {
		if (((stm.tm_year + 2) % 4) == 0)
			t -= s_p_day;

		stm.tm_year += 1;
		t -= s_p_year;
	}

	if (((stm.tm_year + 2) % 4) == 0)
		days_per_month[1]++;

	stm.tm_year += 1970;

	while ( t >= (days_per_month[stm.tm_mon] * s_p_day))
		t -= days_per_month[stm.tm_mon++] * s_p_day;

	while (t >= s_p_day) {
		t -= s_p_day;
		stm.tm_mday++;
	}

	while (t >= s_p_hour) {
		t -= s_p_hour;
		stm.tm_hour++;
	}

	while (t >= s_p_min) {
		t -= s_p_min;
		stm.tm_min++;
	}

	stm.tm_sec = (int) t;
}

/* EOF */