[comp.os.minix] Different version of cron.c

simmy@ocean.ocean.fsu.edu (Simmule Turner) (07/31/89)

Here is my first posting!  This is a version of the cron daemon, that I fixed
up.  I'm planning on adding user crontab's in the near future.  Please send
bug fixes to me(see the address in the .signature).  I use 4k of stack
space.

char scid[]="$Header: cron.c,v 1.5 89/05/08 17:55:40  SrT$";
/* #define DEBUG /* Only define when testing */

/*
 * Version 1.5  SrT  89/05/08
 *      Changed sleep code.
 *
 * Version 1.4  SrT  89/03/17
 *	Fixed a pointer problem, when reloading crontab.
 *
 * Version 1.3  SrT  89/03/16
 *	Loads crontab, into memory and only rereads the disk
 *	version if it changes.  (Free up those CPU cycles!)
 *
 * Fixed 03/10/89, by Simmule Turner, simmy@nu.cs.fsu.edu
 *	Now correctly cleans up zombie processes
 *	Logs actions to /usr/adm/cronlog
 *	Syncs with clock after each minute
 *	Comments allowed in crontab
 *	Fixed bug that prevented month, from matching
 */

/* cron - clock daemon			Author: S.R. Sampson */
/*	Cron is the clock daemon.  It is typically started up from the
 *	/etc/rc file by the line:
 *		/usr/bin/cron
 *	Cron automatically puts itself in the background, so no & is needed.
 *	If cron is used, it runs all day, spending most of its time asleep.
 *	Once a minute it wakes up and examines /usr/lib/crontab to see if there
 *	are any commands to be executed.  The format of this table is the same
 *	as in UNIX, except that % is not allowed to indicate 'new line.'
 *	
 *	Each crontab entry has six fields:
 *	   minute    hour  day-of-the-month  month  day-of-the-week  command
 *	Each entry is checked in turn, and any entry matching the current time
 *	is executed.  The entry * matches anything.  Some examples:
 *
 *   min hr dat mo day   command
 *    *  *   *  *   *    /usr/bin/date >/dev/tty0   #print date every minute
 *    0  *   *  *   *    /usr/bin/date >/dev/tty0   #print date on the hour
 *   30  4   *  *  1-5   /bin/backup /dev/fd1       #do backup Mon-Fri at 0430
 *   30 19   *  *  1,3,5 /etc/backup /dev/fd1       #Mon, Wed, Fri at 1930
 *    0  9  25 12   *    /usr/bin/sing >/dev/tty0   #Xmas morning at 0900 only
 */

#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <setjmp.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifndef DEBUG
#define CRONTAB "/usr/lib/crontab"
#define LOGFILE "/usr/adm/cronlog"
#else
#define LOGFILE "/usr/adm/cronlog.dbg"
#define CRONTAB "/usr/adm/crontab.dbg"
#endif

#define NULLDEV "/dev/null"
#define SEPARATOR " \t"
#define CRONSIZE  2048
#define CRONSTRUCT struct cron_entry

#define	TRUE	1
#define	FALSE	0

struct cron_entry
    {
	char *mn;
	char *hr;
	char *day;
	char *mon;
	char *wkd;
	char *cmd;
        struct cron_entry *next;
    } *head, *p;

char crontab[CRONSIZE], *ctime();
FILE *cronlog;

int wakeup(), nothing();

long previous_time = 0L;
extern int errno;

main()
{
	int status, pid;
	long clock;

	status=fork();
	if ( status == -1 )
	  {
		fprintf(stderr,"Can't fork cron\n");
		exit(1); 
	  }

	if (status > 0)
          exit(0);

	signal(SIGINT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);

	close(0);
        close(1);
        close(2);

	open(NULLDEV,0);
        if ((cronlog = fopen(LOGFILE,"a")) == (FILE *) NULL)
          {
            open(NULLDEV,1);
            open(NULLDEV,1);
          }
        else
          {
            setbuf(cronlog,(char *)NULL);
            dup(fileno(cronlog));
          }

	p = (CRONSTRUCT *) malloc (sizeof(CRONSTRUCT));
	p->next = (CRONSTRUCT *) NULL;
	head = p;

	while (TRUE)
	  {
		signal(SIGALRM, wakeup);
		time(&clock);
		alarm(60 - clock % 60);
		pause();

                signal(SIGALRM, nothing);
                alarm(1);
                while (wait((int *) NULL) != -1)
                   ;
	  }
}

nothing(){}

wakeup()
{
	register struct tm *tm;
	long cur_time;
	extern struct tm *localtime();
	CRONSTRUCT *this_entry = head;

	load_crontab();

	time(&cur_time);
	tm = localtime(&cur_time);

    while (this_entry->next && this_entry->mn)
	{
		if (match(this_entry->mn,  tm->tm_min) &&
		    match(this_entry->hr,  tm->tm_hour) &&
                    match(this_entry->day, tm->tm_mday) &&
		    match(this_entry->mon, tm->tm_mon+1) &&
                    match(this_entry->wkd, tm->tm_wday))  {

                   fprintf(cronlog,"%02d/%02d-%02d:%02d  %s",
			tm->tm_mon+1, tm->tm_mday, tm->tm_hour,
                        tm->tm_min, this_entry->cmd);

		   if (fork() == 0)  {
			execl("/bin/sh", "/bin/sh", "-c", this_entry->cmd, 0);
			exit(1);
		   }
		}
		this_entry = this_entry->next;
	}
}

/*
 *	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);
	}
}

load_crontab()
{
   int pos = 0;
   FILE *cfp;
   struct stat buf;

   if (stat(CRONTAB,&buf))
     {
	if (previous_time == 0L)
	  printf("Can't stat crontab");
	previous_time = 0L;
	return;
     }

    if (buf.st_mtime <= previous_time)
	return;

#ifdef DEBUG
	printf("Crontab Time:%D In_Core:%D\n",buf.st_mtime,previous_time);
#endif

    if ((cfp = fopen(CRONTAB,"r")) == (FILE *) NULL)
     {
	if (previous_time == 0L)
	  printf("Can't open crontab");
	previous_time = 0L;
	return;
     }

    previous_time = buf.st_mtime;

    p = head;
    while (fgets(&crontab[pos],CRONSIZE-pos,cfp) != (char *) NULL)
	{
	     int len;

	     if (crontab[pos] == '#')
		continue;
             len = strlen(&crontab[pos]);
	     assign(p,&crontab[pos]);
	     if (p->next == (CRONSTRUCT *)NULL)
		{
		    p->next = (CRONSTRUCT *) malloc(sizeof(CRONSTRUCT));
		    p->next->next = (CRONSTRUCT *) NULL;
		}
	     p = p->next;
	     pos += ++len;
	     if (pos >= CRONSIZE)
		break;
	}
	fclose(cfp);

	while (p)
	  {
	     p->mn = (char *) NULL;
	     p = p->next;
	  }

#ifdef DEBUG
    printf("Crontab uses %d/%d bytes\n",pos,CRONSIZE);
    {
	CRONSTRUCT *start = head;
	dumptable(start);
    }
#endif
}

assign(entry,line)
CRONSTRUCT *entry;
char *line;
{
    static char buf[256];
    int where;

    strcpy(buf,line);

    entry->mn  = strtok(line,SEPARATOR);
    entry->hr  = strtok((char *) NULL,SEPARATOR);
    entry->day = strtok((char *) NULL,SEPARATOR);
    entry->mon = strtok((char *) NULL,SEPARATOR);
    entry->wkd = strtok((char *) NULL,SEPARATOR);
    entry->cmd = strtok((char *) NULL,SEPARATOR);

    where = entry->cmd - line;
    strcpy(&line[where],&buf[where]);
}

#ifdef DEBUG
dumptable(table)
CRONSTRUCT *table;
{
    long clock;
    time(&clock);

    printf("\nContents of crontab at: %s",ctime(&clock));
    printf("Minute\tHour\tDay\tMonth\tWeekday\tCommand\n");
    while (table->next && table->mn)
	{
	    printf("%s\t%s\t%s\t%s\t%s\t%s",
	           table->mn,table->hr,table->day,table->mon,
                   table->wkd,table->cmd);
	    table = table->next;
	}
}
#endif
--
Simmule Turner        |Arpa: simmy@nu.cs.fsu.edu        | "Wait until it is 
Florida State Univ    |Uucp: gatech!nu.cs.fsu.edu!simmy |  finished, it will
444 OSB               | Cis: 70651,67       Genie:simmy |  be great"        
Tallahassee, FL 32306 | Tel: +1 904 644 1573            |             simmy