home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
201.lha
/
DCron_v1.05
/
dcron.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-12-27
|
15KB
|
625 lines
/*
* DCRON.C V1.05
*
* Limitations: The only RUN commands allowed are of the form:
*
* RUN >nil: <nil: <command>
*
* -Wakeup once every minute & check the modify date for S:CRONTAB
* and to check if the DateStamp() has been re-set.
*
* -Attempt to be smart about checking s:crontab (scan the file
* as little as possible).
*
* -Handles massive Date changes and crontab modifications
*
* DCRON [-d] logfile
*/
#include <stdio.h>
#include <local/typedefs.h>
#define CRONTAB "s:crontab"
#define SIGS (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D|SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F)
#define ACTION_READWRITE 1004
#define ACTION_FIND_INPUT 1005 /* various ACTION's supported */
#define ACTION_FIND_OUTPUT 1006
#define ACTION_END 1007
#define ACTION_EXAMINE 23
#define ACTION_EXAMINENEXT 24
#define ACTION_LOCATE 8
#define ACTION_FREELOCK 15
#define ACTION_COPYDIR 19
#define DOS_FALSE 0
#define DOS_TRUE -1
#define BTOC(bptr) ((void *)((long)(bptr) << 2))
typedef struct FileHandle FH;
typedef struct {
short min;
short hour;
short day;
short month;
short dow;
} MHDMW;
typedef struct {
short entry[5];
} MHDMWARY;
typedef struct {
MHDMW Beg;
MHDMW End;
} CDATE;
typedef struct {
char min[60];
char hour[24];
char day[32];
char month[13];
char dow[7];
} CARRY;
extern int Enable_Abort;
extern APTR DeviceProc();
DATESTAMP BegDate; /* Start and end range for */
DATESTAMP EndDate; /* file compare */
DATESTAMP ModDate; /* Check if date modified */
DATESTAMP TabDate; /* Check if crontab modified */
IOT Iot; /* Best guess at next timeout */
IOT Iotmin; /* 1 minute T.O. (date/file modified test */
PORT *TPort; /* Collector plate for IO requests */
short FatalError; /* Uh oh, can't recover */
short NextTimeout; /* # minutes till next timeout */
char *LogFile;
char XDebug;
long NilFH;
short CronFileExists = 1; /* Does the crontab file exist? */
void logmessage();
void
main(ac, av)
short ac;
char *av[];
{
PROC *proc = FindTask(NULL);
Enable_Abort = 0;
{
register short i;
short j = 0;
for (i = 1; i < ac; ++i) {
register char *ptr = av[i];
if (*ptr != '-') {
switch(j++) {
case 0:
LogFile = ptr;
break;
}
continue;
}
while (*++ptr) {
switch(*ptr) {
case 'd':
++XDebug;
break;
default:
WriteStr(Output(), "bad option\n");
goto fail;
}
}
}
}
if (!LogFile) {
fail:
WriteStr(Output(), "DCron [-d] Logfile\n");
WriteStr(Output(), "DCron, V1.05\n");
exit(1);
}
if (OpenDevice("timer.device", 0, &Iot, UNIT_VBLANK)) {
logmessage("Unable to open timer.device\n");
exit(1);
}
if (!DeviceProc("NULL:")) {
logmessage("NULL: device required for dcron to run\n");
WriteStr(Output(), "NULL: device required to run\n");
exit(1);
}
proc->pr_ConsoleTask = DeviceProc("NULL:");
fclose(stderr);
logmessage("Startup: V1.05 (dist: 22 December 1988)\n");
NilFH = Open("null:", 1006);
DateStamp(&EndDate);
DateStamp(&ModDate);
TPort = CreatePort(NULL,0);
Iot.tr_time.tv_secs = 1;
Iot.tr_time.tv_micro= 0;
Iot.tr_node.io_Message.mn_ReplyPort = TPort;
Iot.tr_node.io_Command = TR_ADDREQUEST;
Iotmin = Iot;
SendIO(&Iot);
SendIO(&Iotmin);
for (;;) {
long mask;
mask = Wait(SIGS | (1 << TPort->mp_SigBit));
if (mask & (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D)) {
logmessage("DCRON: Break\n");
break;
}
if (mask & (SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F)) {
logmessage("^E/F force scan\n");
AbortIO(&Iot); /* force execution */
}
if (FatalError)
break;
if (CheckIO(&Iotmin)) { /* if file/date modified, force exec. */
WaitIO(&Iotmin);
if (datemod() + filemod())
AbortIO(&Iot);
Iotmin.tr_time.tv_secs = 60;
Iotmin.tr_time.tv_micro= 0;
SendIO(&Iotmin);
}
if (CheckIO(&Iot)) {
DATESTAMP Ds;
short secmin;
WaitIO(&Iot);
dostuff();
if (NextTimeout <= 0)
NextTimeout = 1;
if (XDebug) {
logmessage(" Next Timeout in %02ld:%02ld\n",
NextTimeout / 60,
NextTimeout % 60
);
}
DateStamp(&Ds);
secmin = 61 - (Ds.ds_Tick / 50); /* 1+Secs till next min. */
Iot.tr_time.tv_secs = secmin + (NextTimeout - 1) * 60;
Iot.tr_time.tv_micro= 0;
SendIO(&Iot);
}
}
AbortIO(&Iot);
AbortIO(&Iotmin);
WaitIO(&Iot);
WaitIO(&Iotmin);
CloseDevice(&Iot);
DeletePort(TPort);
Close(NilFH);
}
/*
* If the current date is less than the previous date
* If the current date is more than +5 minutes the previous date
*/
datemod()
{
DATESTAMP Date;
long xold, xnew;
DateStamp(&Date);
xold = ModDate.ds_Days * 1440 + ModDate.ds_Minute;
xnew = Date.ds_Days * 1440 + Date.ds_Minute;
ModDate = Date;
if (xnew < xold || xnew - 5 > xold) {
DateStamp(&EndDate);
logmessage("Date change noted\n");
return(1);
}
return(0);
}
/*
* If the file modification time is different than when
* we last checked, we want to recalculate our timeout
* and rescan it.
*/
filemod()
{
char buf[sizeof(FIB)+4];
register FIB *fib = (FIB *)(((long)buf+3)&~3);
long lock;
long result = 0;
if (lock = Lock(CRONTAB, SHARED_LOCK)) {
if (Examine(lock, fib)) {
if (fib->fib_Date.ds_Tick != TabDate.ds_Tick ||
fib->fib_Date.ds_Minute != TabDate.ds_Minute ||
fib->fib_Date.ds_Days != TabDate.ds_Days) {
if (TabDate.ds_Days) {
logmessage("crontab modification noted\n");
result = 1;
}
TabDate = fib->fib_Date;
}
}
UnLock(lock);
}
return(result);
}
/*
* DOSTUFF()
*
* Scan the CRONTAB and execute any entries that fall between
* BegDate and EndDate. Specifically, >= BegDate and < EndDate.
*
* This is done as little as possible. While scanning, we calculate the
* timeout period till the next entry and will attempt to sleep for that
* period of time before comming back to this routine. Checks are still
* done every 60 seconds for radical date changes and crontab modification
* (routines above).
*
* The calculation of the timeout period is not entirely accurate.
* Specifically, in cases similar to:
*
* "30 16 * * *"
*
* If the current time is 16:xx, the timeout period calculated for the hours
* will 0 instead of 24 (if the minutes had been '*', this would be
* correct. But if it is 16:31 the system will wait 29 minutes instead of
* 23 hours and 30 minutes. Of course, it will re-scan at +29 minutes and
* not find anything to do, but you get the point.
*/
static char dim[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static CARRY CArry;
static short CRanStart[] = { 0, 0, 1 , 1, 0 };
static short CRanEnd[] = { 59, 23, 31 , 12, 6 };
static long CWeight[] = { 1, 60, 1440, 28*1440, 1440 };
static char *CBase[] = { CArry.min, CArry.hour, CArry.day, CArry.month, CArry.dow };
dostuff()
{
CDATE cdate;
long fh;
char buf[256];
int ie;
NextTimeout = -1;
BegDate = EndDate;
DateStamp(&EndDate);
breakup(&EndDate, &cdate.End); /* Breakup the time */
breakup(&BegDate, &cdate.Beg); /* note: dim[FEB] is modified */
bzero(&CArry, sizeof(CArry)); /* range array */
/*
* Generate the range for the delta time that has occured since the
* last reading. (A) This is used to ensure we don't miss anything
* (B) Ensure we don't repeat anything
*/
ie = makerange(CArry.min , 0, 59, cdate.Beg.min , cdate.End.min , 1);
ie = makerange(CArry.hour , 0, 23, cdate.Beg.hour , cdate.End.hour , ie);
ie = makerange(CArry.day , 1, dim[cdate.Beg.month-1],
cdate.Beg.day , cdate.End.day , ie);
ie = makerange(CArry.month, 1, 12, cdate.Beg.month, cdate.End.month, ie);
ie = makerange(CArry.dow , 0, 6, cdate.Beg.dow , cdate.End.dow , ie);
/*
* Note: if ie is set after this then dostuff() was called
* within the same minute segment and would yield a
* repeation of commands.
*/
if (ie == 0) {
ReadLn(NULL, NULL, 0);
if (fh = Open(CRONTAB, 1005)) {
long whennext; /* in minutes, estimate */
if (CronFileExists == 0) {
CronFileExists = 1;
logmessage("DCron File %s exists\n", CRONTAB);
}
while (ReadLn(fh, buf, 256)) {
if (buf[0] == 0 || buf[0] == '#')
continue;
whennext = handleline(buf, &cdate.End); /* take smallest */
if (XDebug > 1)
logmessage("whennext == %ld\n", whennext);
if (NextTimeout < 0 || whennext < NextTimeout)
NextTimeout = whennext;
}
Close(fh);
} else {
if (CronFileExists == 1) {
CronFileExists = 0;
logmessage("Unable to open %s\n", CRONTAB);
}
}
}
/*
* You can't trust the Amiga's clock vs the timer.device ... this is to
* ensure that they don't get too far off... no more than an hour timeout
* is allowed. Frankly, anything above 30 minutes will yield unnoticeable
* results.
*/
if (--NextTimeout > 60)
NextTimeout = 60;
}
breakup(date, mhd)
DATESTAMP *date;
MHDMW *mhd;
{
long days;
long years;
char leap;
short month;
days = date->ds_Days + 731; /* 1976 */
years = days / (365*3+366); /* #quad yrs */
days -= years * (365*3+366);
leap = (days < 366); /* is a leap yr*/
years = 1976 + 4 * years;
dim[1] = 29;
if (!leap) {
dim[1] = 28;
days -= 366;
++years;
}
years += days / 365;
days -= (days / 365) * 365;
for (month = 0; (month==1) ? (days >= 28 + leap) : (days >= dim[month]); ++month)
days -= (month==1) ? (28 + leap) : dim[month];
mhd->min = date->ds_Minute % 60;
mhd->hour = date->ds_Minute / 60;
mhd->day = days + 1;
mhd->month = month + 1;
mhd->dow = date->ds_Days % 7; /* 0 = sunday */
}
/*
* S < RANGE <= E (if previous entries were equal)
* note: if S == E, E is still included
* S <= RANGE <= E (if previous entries were not equal)
*/
makerange(ptr, cs, ce, s, e, isequal)
register char *ptr;
{
long error = 5000;
short oldisequal = isequal;
isequal = (isequal && s == e);
if (oldisequal && s != e) {
if (++s > ce)
s = cs;
}
while (--error && s != e) {
ptr[s++] = 1;
if (s > ce)
s = cs;
}
ptr[e] = 1;
if (error == 0) {
logmessage("DCRON,makerange(): software error\n");
FatalError = 1;
}
return(isequal);
}
/*
* This is the core of the routine. The fields are interpreted, times
* estimated, ready entries run, etc..
*
* order: minutes hours days months dayofweek
*
* Each field may be a * indicating all-times, and a combination of a
* range and comma delimited numbers: 0-3,45,46-49 (MUST BE SORTED)
*
* Each field is separated by one or spaces+tabs
*/
handleline(buf, endt)
char *buf;
MHDMWARY *endt;
{
register char *ptr = buf;
register short i;
char *gnum();
short sumok = 0;
long howsoon = 0; /* how soon until next entry */
for (i = 0; i < 5; ++i) { /* 5 arguments */
char ok = 0;
short nextunit = -1; /* count, in units */
while (*ptr == ' ' || *ptr == 9)
++ptr;
while (*ptr && *ptr != 9 && *ptr != ' ') {
short start;
short finish;
if (*ptr == '*') {
++ptr;
ok = 1;
break;
}
ptr = gnum(ptr, &start);
finish = start;
if (*ptr == '-')
ptr = gnum(ptr+1, &finish);
if (*ptr == ',')
++ptr;
/*
* Determine if current date is within time range. nextunit
* is the number of time units for this index till the next
* entry will be executed.
*/
if (endt->entry[i] < start) {
register short newnextunit = start - endt->entry[i];
if (nextunit < 0 || newnextunit < nextunit)
nextunit = newnextunit;
} else
if (endt->entry[i] > finish) {
register short newnextunit = 1 + CRanEnd[i] - endt->entry[i] + start - CRanStart[i];
if (nextunit < 0 || newnextunit < nextunit)
nextunit = newnextunit;
} else
nextunit = 0; /* Inside time range */
if (start < CRanStart[i] || finish < CRanStart[i] || start > CRanEnd[i] || finish > CRanEnd[i]) {
logmessage("Illegal Bounds in: %s\n", buf);
logmessage(" Value %ld & %ld bounds %ld-%ld ", start, finish, CRanStart[i], CRanEnd[i]);
logmessage(" AT: '%s'\n", ptr);
continue;
} else {
do {
if (start > CRanEnd[i])
start = CRanStart[i];
if (CBase[i][start]) {
ok = 1;
break;
}
++start;
} while (start != finish + 1);
}
}
if (ok)
++sumok;
if (nextunit > 0)
howsoon += nextunit * CWeight[i];
}
/*
* This is tricky. Since this program also handles the dummy-null
* device, it CANNOT block on the execute. The only way to do that
* is to run <nil: >nil: so run itself does not attempt to access
* "*". It is ok if the program run runs accesses "*" as we will not
* be frozen then.
*/
if (sumok == i) {
register char *p2 = malloc(strlen(ptr)+32);
logmessage("%s\n", buf);
while (*ptr == ' ' || *ptr == 9)
++ptr;
/* strcpy(p2, "run <nil: >nil: "); */
strcpy(p2, "run ");
strcat(p2, ptr);
Execute(p2, NilFH, NilFH);
}
if (XDebug > 2) {
logmessage("FOR %-20s In %ld %02ld:%02ld\n",
buf, howsoon / 1440, howsoon / 60 % 24, howsoon % 60
);
}
return(howsoon);
}
/*
* Poor man's log. Note that the log file is not left open ... this allows
* one to read or tail it at any time.
*/
void
logmessage(ptr, a, b, c, d, e)
char *ptr;
{
char *buf = malloc(512);
DATESTAMP date;
MHDMW cb;
long fh;
static char *Dow[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static char *Miy[] = { "---", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
if (!buf)
return;
DateStamp(&date);
breakup(&date, &cb);
sprintf(buf, "dcron: %s %2ld %s %02ld:%02ld ",
Dow[cb.dow], cb.day, Miy[cb.month], cb.hour, cb.min
);
sprintf(buf+strlen(buf), ptr, a, b, c, d, e);
if ((fh = Open(LogFile, 1005)) == NULL)
fh = Open(LogFile, 1006);
if (fh) {
Seek(fh, 0L, 1);
WriteStr(fh, buf);
Close(fh);
}
free(buf);
}
/*
* Scan a string for a value, return the new pointer position and set the
* specified short pointer to the value.
*/
char *
gnum(ptr, pval)
register char *ptr;
register short *pval;
{
short val = 0;
if (*ptr < '0' || *ptr > '9')
logmessage("Not a number: %s\n", ptr);
while (*ptr >= '0' && *ptr <= '9') {
val = val * 10 + *ptr - '0';
++ptr;
}
*pval = val;
return(ptr);
}
WriteStr(fh, buf)
long fh;
char *buf;
{
Write(fh, buf, strlen(buf));
}
ReadLn(fh, buf, max)
long fh;
char *buf;
short max;
{
static char Data[1024];
static short RIdx, RLen;
register short i;
if (fh == NULL) {
RIdx = RLen = 0;
return(0);
}
for (--max, i = 0; i < max; ++i) {
if (RIdx == RLen) {
RLen = Read(fh, Data, 1024);
RIdx = 0;
if (RLen <= 0) {
buf[i] = 0;
return(0);
}
}
if ((buf[i] = Data[RIdx++]) == '\n')
break;
}
buf[i] = 0;
return(1);
}