[comp.os.minix] AT/ATRUN source for MINIX

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(&ltm,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(&ltm,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