msm@nucleus.UUCP (Michael S. Mattone) (03/24/88)
Here is the source to an "at" and "atrun" I wrote a while ago and recently modified for use with MINIX. They seem to work O.K. although "atrun" could probably use some work, especially with error handling. Put something like this in /usr/lib/crontab: 0,15,30,45 * * * * /usr/lib/atrun ---------------------------cut-here---------------------------------------- echo x - Makefile gres '^X' '' > Makefile << '/' X# X# Makefile for at and atrun X# X X# add -DDEBUG for debugging printfs XCFLAGS=-F -DUNIX X Xall: at atrun X Xat: at.s X cc -o at at.s X chmem =4096 at X Xatrun: atrun.s X cc -o atrun atrun.s X chmem =4096 atrun X Xinstall: X cp at /usr/bin X chown root /usr/bin X chmod 4755 /usr/bin/at X cp atrun /usr/lib X chown root /usr/lib/atrun X chmod 700 /usr/lib/atrun X / echo x - at.c gres '^X' '' > at.c << '/' X/* X * at.c - execute commands at a later time. X * X * Copyright (c) 1988 MSM Software X * All rights reserved. X * X * last edit: X * 25-Feb-1988 MSM MINIX version X * 21-Dec-1985 MSM created X */ X#include <stdio.h> X#include <ctype.h> X#include <sys/types.h> X#include <time.h> X X#define MAXL 256 X X#define SECS 86400L /* # seconds in a day */ X#define WEEK 604800L /* # seconds in a week */ X#define BASEYEAR 70 /* seconds start from 1970 */ X X#define NOTFOUND -1 X#define MANYFOUND -2 X X#ifdef AZTEC X#define CASE /* convert args to lower case */ X#define SPOOLDIR "15" /* spool directory */ X#define getpid() 23 /* fake getpid */ X#endif X X#ifdef MSDOS X#define SPOOLDIR "/usr/spool/at" /* spool directory */ X Xextern char **environ; /* environment strings */ Xchar *pwd(); X#endif X X#ifdef UNIX X#define SPOOLDIR "/usr/spool/at" /* spool directory */ X#define PWDPROG "pwd" /* pwd program */ X Xextern char **environ; /* environment strings */ Xchar *pwd(); X#endif X Xstatic char *daystr[] = { X "sunday", "monday", "tuesday", "wednesday", "thursday", X "friday", "saturday", X 0 X}; X Xstatic char *monstr[] = { X "january", "february", "march", "april", "may", "june", X "july", "august", "september", "october", "november", "december", X 0 X}; X Xchar Atfile[MAXL]; /* filename to open */ Xchar line[MAXL]; X X/* forward function declarations */ Xextern char *ctime(); Xextern long time(); Xextern struct tm *localtime(); X Xlong _dtoxtime(); XFILE *atopen(); X Xmain(argc,argv) Xint argc; Xchar **argv; X{ X int athour, atmin, atmon, atmday, atyear; X int tmp, todc; X register char *tp, **ep; X long tim, now, offset = 0; X FILE *ifp = stdin, *ofp; X struct tm ltm; X X if (argc < 2) X error("usage: at time [day] [file]\n"); X X#ifdef CASE X /* make everything lcase */ X for (tmp = 1; tmp < argc; ++tmp) { X for (tp = argv[tmp]; *tp; ++tp) X if (isupper(*tp)) X *tp = tolower(*tp); X } X#endif X X --argc; X ++argv; X X for (tmp = 0, tp = *argv; *tp; ++tp) { X if (isdigit(*tp)) X ++tmp; /* count # of digits */ X else { X todc = *tp; X if (isupper(todc)) X todc = tolower(todc); X if (todc!='a' && todc!='p' && todc!='n' && todc!='m') X error("at: bad time format"); X break; X } X } X X athour = atoi(*argv); X if (tmp < 3) X atmin = 0; X else { X atmin = athour % 100; X athour /= 100; X } X X if (todc == 'p') X athour += 12; /* add 12 hrs for PM */ X else if (todc == 'a' && athour > 11) X athour -= 12; /* subtract 12 hrs (e.g. 1201am) */ X else if (todc == 'm') X athour = 0; /* "midnight" = 0000 hrs */ X else if (todc == 'n') X athour = 12; /* "noon" = 1200 hrs */ X X if (athour > 24 || (athour == 24 && atmin > 0)) X error("at: time out of range"); X if (atmin > 59) X error("at: illegal minute field"); X X time(&now); /* time at this second */ X X#ifdef DEBUG X fprintf(stderr,"athour=%d, atmin=%d\n",athour,atmin); X fprintf(stderr,"now=%ld\n",now); X#endif X X#ifdef NOSTRUCTPASS X memcpy(<m,localtime(&now),sizeof(struct tm)); /* copy a STRUCTURE */ X#else X ltm = *localtime(&now); /* copy a STRUCTURE */ X#endif X X atmday = ltm.tm_mday; X atmon = ltm.tm_mon; X atyear = ltm.tm_year; X X#ifdef DEBUG X fprintf(stderr,"atmday=%d, atmon=%d, atyear=%d\n",atmday,atmon,atyear); X#endif X X if (argc > 1) { X if ((tmp = ckstr(*++argv,monstr)) == MANYFOUND) X error("at: ambiguous month"); X else if (tmp != NOTFOUND) { X --argc; X ++argv; X if (argc > 1) { X atmday = atoi(*argv++); X --argc; X } X if ((atmon = tmp) < ltm.tm_mon X || (atmon == ltm.tm_mon && atmday < ltm.tm_mday) X || (atmon == ltm.tm_mon && atmday == ltm.tm_mday X && athour < ltm.tm_hour*100+ltm.tm_min)) X ++atyear; X if (!ckmday(atmon,atmday,atyear+1900)) X error("at: illegal day"); X } X else if ((tmp = ckstr(*argv,daystr)) == MANYFOUND) X error("at: ambiguous day of week"); X else if (tmp != NOTFOUND) { X --argc; X ++argv; X if (tmp < ltm.tm_wday) X tmp += (7 - ltm.tm_wday); X else X tmp -= ltm.tm_wday; X offset = (long)tmp * SECS; X if (argc > 1) { X if (strcmp("week",*argv) == 0) { X --argc; X ++argv; X offset += WEEK; X } X } X } X } X X if (argc > 1 && (ifp = fopen(*argv,"r")) == NULL) X error("at: cannot open memo file"); X X tim = offset + _dtoxtime(atyear-BASEYEAR,atmon+1,atmday,athour,atmin,0); X X if (tim < now) X tim += SECS; /* time has past, so add 24 hrs */ X X#ifdef DEBUG X printf("at: current time: (%ld) %s",now,ctime(&now)); X printf("at: scheduled time: (%ld) %s",tim,ctime(&tim)); X#endif X X if ((ofp = atopen(tim)) == NULL) X error("at: cannot open temporary"); X X#ifdef MSDOS X tp = pwd(); X fprintf(ofp,"%.2s\n",tp); /* move to the right disk */ X fprintf(ofp,"cd /%s\n",&tp[3]); /* change directory */ X#endif X X#ifdef UNIX X fprintf(ofp,"cd %s",pwd()); X fprintf(ofp,"umask %o\n",umask(0)); X for (ep = environ; *ep; ++ep) { X for (tp = *ep; *tp && *tp != '='; ++tp) X putc(*tp,ofp); X fprintf(ofp,"='%s'\n",tp+1); X fputs("export ",ofp); X for (tp = *ep; *tp && *tp != '='; ++tp) X putc(*tp,ofp); X fputs("\n",ofp); X } X X fprintf(ofp,"%s %s\n",getenv("SHELL"),"<< 'xxFUNNYxx'"); X#endif X X while (fgets(line,MAXL,ifp) != NULL) { X fputs(line,ofp); X } X X#ifdef UNIX X fprintf(ofp,"%s\n","xxFUNNYxx"); X#endif X X if (ifp != stdin) X fclose(ifp); X atclose(ofp); X X#ifdef DEBUG X printf("at: Job will run in %ld seconds.\n",tim-now); X#endif X} X X/* atopen -- open a file adjusting unique # if necessary */ XFILE *atopen(tim) Xlong tim; X{ X int hour, uniq; X struct tm ltm; X register FILE *fp = NULL; X X#ifdef DEBUG X fprintf(stderr,"atopen(%ld)\n",tim); X#endif X X#ifdef NOSTRUCTPASS X memcpy(<m,localtime(&tim),sizeof(struct tm)); X#else X ltm = *localtime(&tim); X#endif X X hour = ltm.tm_hour*100 + ltm.tm_min; X uniq = getpid() % 100; X X for (;;) { X#ifdef MSDOS /* and AZTEC */ X sprintf(Atfile,"%s/%02d%04d%02d.%03d", X SPOOLDIR,ltm.tm_year,hour,uniq,ltm.tm_yday); X#endif X#ifdef UNIX X sprintf(Atfile,"%s/%02d.%03d.%04d.%02d", X SPOOLDIR,ltm.tm_year,ltm.tm_yday,hour,uniq); X#endif X X if (access(Atfile,0) == 0) { /* file exists ? */ X ++uniq; X if ((uniq %= 100) == getpid() % 100) /* complete circle */ X break; X } X else { X fp = fopen(Atfile,"w"); X break; X } X } X return fp; X} X X/* atclose -- close the file opened by atopen() */ Xatclose(fp) XFILE *fp; X{ X fclose(fp); X chown(Atfile, getuid(), getgid()); X} X X/* ckstr -- check valid string */ Xstatic ckstr(s,sp) Xregister char *s, **sp; X{ X register int len, num; X register int sawone = NOTFOUND; X X len = strlen(s); X for (num = 0; *sp; ++sp, ++num) { X if (strncmp(s,*sp,len) == 0) X if (sawone == NOTFOUND) X sawone = num; /* found a match */ X else { X sawone = MANYFOUND; /* more than 1 matches */ X break; X } X } X return sawone; X} X X/* ckmday -- check if valid day in month */ Xstatic ckmday(mon,mday,year) Xregister int mon, mday, year; X{ X static int mdays[] = {31,28,31,30,31,30,31,31,30,31,30,31}; X X /* special case for Feb and leap years */ X if (mon == 1 && isleap(year) && mday == 29) X return 1; X X return (mday >= 1 && mday <= mdays[mon]); X} X X/* error -- print error and die */ Xstatic error(s) Xregister char *s; X{ X fputs(s,stderr); X fputs("\n",stderr); X exit(1); X} X X/* isleap -- check if year is a leap year */ Xstatic isleap(year) Xregister int year; X{ X return ((year % 4 == 0 && year % 100 != 0) || (year % 400) == 0); X} X X#ifndef MSDOS X/* _dtoxtime -- convert a mm/dd/yy hh:mm:ss to a long int seconds */ Xstatic long _dtoxtime(year,mon,mday,hour,min,sec) Xint year, mon, mday, hour, min, sec; X{ X static int days[]={0,31,59,90,120,151,181,212,243,273,304,334}; X int yday; X long absdate; X X#ifdef DEBUG X fprintf(stderr,"_dtoxtime(%d,%d,%d,%d,%d,%d)\n",year,mon,mday,hour,min,sec); X#endif X X /* days due to leap years */ X#ifdef UNIX X yday = (year + 1) / 4; /* assume BASEYEAR of 1970 for {MI|U}NIX */ X#else X yday = (year + 3) / 4; /* assume BASEYEAR of 1980 for MS-DOS */ X#endif X X yday += days[mon-1] + mday - 1; X X if ((year % 4) && mon > 2) /* more leap year adjustments */ X ++yday; X X absdate = (((long)year * 365L) + (long)yday) * SECS; X return absdate + (long)hour * 3600L + (long)min * 60L + (long)sec; X} X#endif X X#ifdef MSDOS X/* pwd -- get the current directory */ Xstatic char *pwd() X{ X extern char *getcwd(); X X return getcwd(NULL,65); /* returns malloc()'d memory ptr */ X} X#endif X X#ifdef UNIX X/* pwd -- return the resulting string from a pwd command */ Xstatic char *pwd() X{ X char *retv = "/tmp"; X FILE *popen(), *pfp; X X if ((pfp = popen(PWDPROG,"r")) != NULL) { X if (fgets(line,MAXL,pfp) != NULL) /* uses the global line[] */ X retv = line; X pclose(pfp); X } X else X error("at: can't execute pwd"); X X return retv; X} X#endif / echo x - atrun.c gres '^X' '' > atrun.c << '/' X/* X * atrun.c - program to execute at(1) commands on time X * X * Copyright (c) 1988 MSM Software X * All rights reserved. X * X * last edit: X * 25-Feb-1988 MSM MINIX version X */ X#include <stdio.h> X#include <ctype.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/dir.h> X#include <time.h> X X#define MAXL 256 X#define MAXP 65 X X#define SECS 86400L /* # seconds in a day */ X#define WEEK 604800L /* # seconds in a week */ X#define BASEYEAR 1970 X X#define SHELL "/bin/sh" X#define SPOOLDIR "/usr/spool/at" /* spool directory */ X#define LASTTIME "/usr/spool/at/lasttimedone" X#define PASTDIR "/usr/spool/at/past" X#define DEVNULL "/dev/null" X X/* forward function declarations */ Xextern char *ctime(); Xextern long time(); Xextern struct tm *localtime(); X Xmain(argc,argv) Xint argc; Xchar **argv; X{ X int fd; X char buf[16], hhhh[5]; X long tim; X struct tm *tbuf; X X if (chdir(SPOOLDIR) < 0) X exit(1); X X time(&tim); X tbuf = localtime(&tim); X X if ((fd = creat(LASTTIME, 0644)) >= 0) { X sprintf(hhhh,"%02.2d%02.2d", tbuf->tm_hour, tbuf->tm_min); X write(fd, hhhh, 4); X close(fd); X } X#ifdef DEBUG X fprintf(stderr, X "DEBUG: main: tim = %ld, yday = %d, mon = %d, day = %d, year = %d\n", X tim, tbuf->tm_yday, tbuf->tm_mon, tbuf->tm_mday, tbuf->tm_year); X#endif X X while (getfile(buf, tbuf) > 0) { X#ifdef DEBUG X fprintf(stderr, "DEBUG: main: getfile() > 0\n"); X#endif X run(buf); X } X} X X/* getfile -- look up any files ready to run, buf returns filename */ Xgetfile(buf, now) Xregister char *buf; Xstruct tm *now; X{ X static int fd = -1; X int nread, year, yday, hour, uniq; X char fname[DIRSIZ+1]; X struct direct dent; X X if (fd == -1) { X if ((fd = open(".", 0)) < 0) X return -1; X } X X while ((nread = read(fd, &dent, sizeof(dent))) == sizeof(dent)) { X#ifdef DEBUG X fprintf(stderr, "DEBUG: getfile: d_ino = %d, d_name = %.14s\n", X dent.d_ino, dent.d_name); X#endif X if (dent.d_ino == 0 || dent.d_name[0] == '.') X continue; X strncpy(fname, dent.d_name, DIRSIZ); X fname[DIRSIZ] = '\0'; X if (sscanf(fname, X "%02d.%03d.%04d.%02d",&year,&yday,&hour,&uniq) != 4) X continue; X if (now->tm_year > year X || (now->tm_year == year && now->tm_yday > yday) X || (now->tm_year == year && now->tm_yday == yday X && (now->tm_hour*100 + now->tm_min) >= hour)) { X sprintf(buf, X "%02d.%03d.%04d.%02d",year,yday,hour,uniq); X return 1; X } X } X X if (nread != sizeof(dent)) { X close(fd); X fd = -1; X } X return 0; X} X X/* run -- process the commands in file */ Xrun(file) Xregister char *file; X{ X register int i; X int pid, status; X char pastfile[MAXP]; X X#ifdef DEBUG X fprintf(stderr, "DEBUG: run: running %s\n", file); X#endif X X /* X * First link the file to be run into the past directory. This X * way if the script takes a real long time to run, and "atrun" X * gets executed before it completes, the file won't be executed X * twice. X */ X sprintf(pastfile, "%s/%s", PASTDIR, file); X if (link(file, pastfile) < 0) X return -1; X unlink(file); X X if ((pid = fork()) < 0) X return -1; X else if (pid != 0) { X while (pid != wait(&status)) X ; X } X else { /* the child process */ X struct stat sb; X if (stat(pastfile, &sb) != -1) { /* set to the owners uid, gid */ X setgid(sb.st_gid); X setuid(sb.st_uid); X } X for (i = 0; i < NFILES; ++i) X close(i); X open(DEVNULL, 0); /* open for read */ X open(DEVNULL, 1); /* open for write */ X dup(1); /* dup stderr */ X execl(SHELL, SHELL, pastfile, 0); X exit(1); X } X unlink(pastfile); /* remove the processed file */ X return status; X} X / exit 0