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