/* 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 * * Version 1.6 SrT 90/04/08 * Added casting fixes by Ralf Wenk, and integrated net * changes that release current directory, and use the * 1.5.5 include files. Altered assign, so no temporary * buffer is needed. * * Version 1.5 SrT 89/04/10 * Changed sleep code, to type SRS sent me. * * 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 clock 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 */ #include <sys/types.h> #include <sys/stat.h> #include <signal.h> #include <time.h> #include <string.h> #include <fcntl.h> #include <stdio.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, *entry_ptr; char crontab[CRONSIZE]; FILE *cronlog; int wakeup(), ret(); time_t previous_time = 0L; main() { int status; time_t 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); chdir("/"); open(NULLDEV, O_RDONLY); if ((cronlog = fopen(LOGFILE, "a")) == (FILE *) NULL) { open(NULLDEV, O_WRONLY); open(NULLDEV, O_WRONLY); } else { setbuf(cronlog, (char *) NULL); dup(fileno(cronlog)); } entry_ptr = (CRONSTRUCT *) malloc(sizeof(CRONSTRUCT)); entry_ptr->next = (CRONSTRUCT *) NULL; head = entry_ptr; while (TRUE) { signal(SIGALRM, wakeup); time(&clock); alarm((unsigned) (60 - clock % 60)); pause(); signal(SIGALRM, ret); alarm(1); while (wait((int *) NULL) != (-1)); } } int ret() {} wakeup() { register struct tm *tm; time_t cur_time; 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\n", 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, (char *) 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\n"); previous_time = 0L; return; } #ifdef DEBUG printf("Crontab Time:%ld In_Core:%ld\n", buf.st_mtime, previous_time); #endif if (buf.st_mtime <= previous_time) return; if ((cfp = fopen(CRONTAB, "r")) == (FILE *) NULL) { if (previous_time == 0L) printf("Can't open crontab\n"); previous_time = 0L; return; } previous_time = buf.st_mtime; entry_ptr = head; while (fgets(&crontab[pos], CRONSIZE - pos, cfp) != (char *) NULL) { int len; if (crontab[pos] == '#') continue; len = strlen(&crontab[pos]); if (crontab[pos + len - 1] == '\n') { len--; crontab[pos + len] = '\0'; } assign(entry_ptr, &crontab[pos]); if (entry_ptr->next == (CRONSTRUCT *) NULL) { entry_ptr->next = (CRONSTRUCT *) malloc(sizeof(CRONSTRUCT)); entry_ptr->next->next = (CRONSTRUCT *) NULL; } entry_ptr = entry_ptr->next; pos += ++len; if (pos >= CRONSIZE) break; } fclose(cfp); while (entry_ptr) { entry_ptr->mn = (char *) NULL; entry_ptr = entry_ptr->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; { 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 = strchr(entry->wkd,'\0') + 1; } #ifdef DEBUG dumptable(table) CRONSTRUCT *table; { time_t 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\n", table->mn, table->hr, table->day, table->mon, table->wkd, table->cmd); table = table->next; } } #endif