|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - downloadIndex: ┃ T n ┃
Length: 29947 (0x74fb) Types: TextFile Names: »nag.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki └─ ⟦this⟧ »EUUGD11/euug-87hel/sec1/nag/nag.c«
/*---------------------------------------------------------------- * * Nag.c -- annoying reminder service daemon * * Sun Aug 24 14:18:08 PDT 1986 * * by Dave Brower, {amdahl, cbosgd, mtxinu, sun}!rtech!gonzo!daveb * * Copyright 1986, David C Brower. All rights reserved. * * This is a preliminary version. The final release will be offered * with fewer restrictions. * * Nag should be launched out of your .login or .profile. It * periodically reads your ~/.nag file and executes commands * that can be used as reminders of upcoming events. The environment * variable NAGFILE can be used to get input from something other than * the ~/.nag file. * * NAGFILE FORMAT: * --------------- * * The ~/.nag file should contain lines of the form: * * status day time interval command * * where: * * status is one of * 1. '#' indicating a commented out reminder * 2. ':' indicating a silenced reminder * 3. ' ' for an activate reminder. * Other values produce unpredicatable results. * * day is one of: * 1. A date, "8/8/88", "8-Aug-88", etc., but no blanks. * 2. '*' for any day. * 3. A day, "Sun", "Mon", ... * The last is presently unimplemented (sorry). * * time is a time spec, "8AM", "23:00", etc., but no blanks * * interval is a colon separated list of minutes after time at which * to execute the command, e.g., * * -30:-15:0:5:10 * * produces execution 30 and 15 minutes before the event, * at the time, and 5 and 10 minutes later. * * command is a command to execute with /bin/sh. Some shell variables * are set for use in messages: * * $pretime -interval * $posttime interval * $now hh:mm of the current time * $then hh:mm of the parent event * * Blank lines are ignored. * * Example: * * # don't forget to eat. * * 12:30PM 0 writebig "Lunch Time" * * # Weekly warning that has been silenced. * :Mon 3:00PM -30:-20:-10:-5:0 echo "^GStatus report in $time" * * # Active Weekly warning. * Fri 1:30PM -20:-10:-5:0:5:10 echo "^GCommittee meeting in $time" * * # One shot warning to call the east coast. * 8/25/86 1:30PM -180:-120:-60:0:10:20 echo "^GCall DEC Marlblerow" * * NAG * --- * * Nag puts itself in the background, and exits when you logout. * Standard output and standard error go to your terminal. * * Each time it wakes up, it sees if the ~/.nag file has changed. * If so, it builds an event queue for lines without '#' comment symbols. * * Events that were not silenced with 'X' and were due before "now" * are executed. If the event was the last for an entry in ~/.nag, * the file is edited to re-enable it from a gagged state. * * The program then sleeps for at most MINSLEEP minutes. * * OKOK * ---- * * The "okok" program just edits ~/.nag and prepends an 'X' to lines * that need to be shut up. * * BUILD INSTRUCTIONS: * ------------------- * * cc -o nag [ -DSYS5 ] nag.c gdate.c * ln nag okok * * The code compiles for a BSD system by default. * * CAVEATS: * -------- * * Sorry Christopher, it probably won't work if stty nostop is set. * */ # include <stdio.h> # include <sys/types.h> # include <sys/stat.h> # include <signal.h> # include <pwd.h> # include <ctype.h> # ifdef SYS5 # include <string.h> # include <time.h> # define index strchr # define rindex strrchr # else # include <strings.h> # include <sys/time.h> # endif /*---------------- * * defines * */ # define DPRINTF if(Debug) (void)fprintf # define COMCHAR '#' # define SILCHAR ':' # define HRSECS 3600L # define CTIMELEN 32 /* length of a date/time string */ # define MINSLEEP (5*60) # define MAXARGS 5120 /* max arg/env size on System V */ # define TRUE (1) # define FALSE (0) # define min(a,b) ((a) < (b) ? (a) : (b)) /*---------------- * * typedefs and structure definitions * */ /* * A NAGLINE is a parsed entry from the .nag file. We keep * a list of them representing the current file, so we can * write it back out easily. */ typedef struct nagline NAGLINE; struct nagline { NAGLINE * next; /* Next in the chain */ int type; /* COMMENT, SILENT, PENDING, BAD */ # define UNKNOWN 0 # define COMMENT 1 # define SILENT 2 # define PENDING 3 # define BAD 4 int errtype; /* if type is BAD, cause of error */ # define NOERR 0 # define EMPTY 1 # define DATEBAD 2 # define NOTIME 3 # define TIMEBAD 4 # define NOINTERVALS 5 # define NOCMD 6 time_t atime; /* absolute time of event */ char *err; /* string that caused the error */ char *line; /* the raw line, allocated */ char *datestr; /* the date string, allocated */ char *timestr; /* the time string, allocated */ char *intstr; /* extracted string of intervals, allocated */ char *cmd; /* extracted command to execute, allocated */ }; static char *linetypes[] = { "Unknown", "Comment", "Silent", "Pending", "Bad" }; static char *parserrs[] = { "No error", "Empty line", "Bad date", "No time", "Bad time", "No intervals", "No command" }; /* * An EVENT is an entry in the event queue. */ typedef struct event EVENT; struct event { EVENT * next; /* next event in chain */ NAGLINE *lp; /* the parent nagline */ time_t etime; /* absolute time of the event */ int offset; /* minutes difference with parent time */ }; /*---------------- * * File local variables * */ static char *Myname=""; /* name from argv[0] */ static time_t Now = 0; /* absolute time of "now" */ static time_t Last = 0; /* time last time we were awake */ static NAGLINE *Flist = NULL; /* lines from the file */ static NAGLINE *Flast = NULL; /* last line from the file */ static EVENT *Evq = NULL; /* the global event queue */ static char Origlogin[20] = ""; /* login name when program started */ static char Nagfile[ 256 ] = ""; /* full path of the nag file */ static int Debug = FALSE; /* debugging? */ static char Laststr[ CTIMELEN ]; /* ctime output for last time through */ static char Nowstr[ CTIMELEN ]; /* ctime output for this time through */ /*---------------- * * Forward and external function definitions * */ /* library defined */ extern char *getlogin(); /* login name in /etc/utmp */ extern char *getenv(); /* get an environment variable */ extern struct passwd *getpwuid(); /* passwd entry for this user */ extern time_t time(); extern struct tm *localtime(); extern char *fgets(); extern char *index(); extern char *rindex(); extern char *sprintf(); /* kim@enea: changed from 'int' for BSD */ extern int perror(); /* kim@enea: changed from 'void' for BSD? */ extern int qsort(); /* kim@enea: changed from 'void' for BSD? */ extern unsigned sleep(); extern void free(); extern void exit(); extern char *ctime(); /* gdate.c defined */ extern char *gdate(); /* date string to time buf struct */ extern char *gtime(); /* time string to time buf struct */ extern time_t tm_to_time(); /* time buf to secs past epoch */ extern char *dow[]; /* days of the week names */ extern int find(); /* unambiguous search of string tables */ /* forward function references */ # define forward extern forward void nagfile(); forward void setup(); forward int readf(); forward int editf(); forward int writef(); forward int parseline(); forward void zaplines(); forward void buildq(); forward void zapq(); forward void insq(); forward void addevents(); forward int timecmp(); forward void sortq(); forward void runq(); forward void showlines(); forward void dumpline(); forward void showevents(); forward void dumpevent(); forward char *emalloc(); forward char *ecalloc(); forward FILE *efopen(); forward char *nctime(); forward char *nhour(); forward void delay(); forward void lowcase(); /*---------------- * * main() -- Main program. * * Do one time setup, then go into a loop rebuilding the event queue, * executing events in order. Sleep is done after running the queue. * */ /*ARGSUSED*/ main(argc, argv) int argc; char **argv; { char *cp; if(argc > 1) Debug = TRUE; Myname = (cp = rindex(argv[0], '/')) ? cp + 1 : argv[0] ; nagfile(); if( !strcmp(Myname, "nag") ) { setup(); # ifndef FOREGROUND DPRINTF(stderr, "forking to self-backgrounnd"); if(fork()) exit(0); # endif /* pretend we started at the epoch */ Now = 0; (void) strcpy( Nowstr, nctime( &Now )); /* * This loop never exits. * * The program terminates in delay() when the user logs * off this terminal. */ for(;;) { (void) strcpy( Laststr, Nowstr ); Last = Now; Now = time(NULL); (void) strcpy( Nowstr, nctime( &Now ) ); DPRINTF(stderr, "\nLoop:\tLast %s\tNow %s\n", Laststr, Nowstr); if ( readf() ) buildq(); runq(); } } else if ( !strcmp(Myname, "okok")) { Now = time( NULL ); (void) strcpy( Nowstr, nctime( &Now )); if ( readf() ) { buildq(); if ( editf( PENDING ) ) exit( writef() ); } else { (void) fprintf(stderr, "%s: Can't read %s\n", Myname, Nagfile ); exit(1); } } else { (void) fprintf(stderr, "Identity crisis: \"%s\" bad program name\n", argv[0]); exit(1); } exit(0); /*NOTREACHED*/ } /*---------------- * * nagfile -- get the full .nag file path * */ void nagfile() { register char *home; register char *cp; /* remember who you are to check for logout later */ (void) strcpy(Origlogin, getlogin()); /* expand the Nagfile name */ if( cp = getenv("NAGFILE") ) (void)strcpy( Nagfile, cp ); else if( home = getenv("HOME") ) (void) sprintf( Nagfile, "%s/.nag", home ); else { (void) fprintf(stderr, "%s: HOME is not set\n", Myname ); exit(1); } DPRINTF(stderr, "Origlogin %s, Nagfile %s\n", Origlogin, Nagfile); } /*---------------- * * setup() -- one time initialization. * * Setup signals so we don't go away. * accidentally. * */ void setup() { if(!Debug) { (void) signal( SIGQUIT, SIG_IGN ); (void) signal( SIGTERM, SIG_IGN ); # ifdef SIGTTOU (void) signal( SIGTTOU, SIG_IGN ); # endif } } /*---------------- * * readf() -- read the nagfile and build in memory copy. * * Returns TRUE if the file was read. */ int readf() { register NAGLINE *lp; register FILE *fp; char line[ MAXARGS ]; struct stat newstat; static struct stat laststat = { 0 }; static time_t readtime = 0; /* check to see if Nagfile has changed, and reread file. */ if(stat(Nagfile, &newstat)) { /* set it the epoch, but don't complain */ newstat.st_mtime = 0; } /* if file changed, or we read it more than 12 hours ago */ if ( newstat.st_mtime <= laststat.st_mtime || (readtime && Now > 0 && readtime < (Now - (HRSECS * 12)))) { DPRINTF(stderr, "already read %s\n", Nagfile ); return FALSE; } /* rebuild the internal copy of the file */ DPRINTF(stderr, "reading Nagfile\n"); laststat = newstat; readtime = Now; zaplines(); /* warn, but don't fatal if file can't be opened this time through */ if ( NULL==(fp = efopen(Nagfile, "r"))) return FALSE; /* build the new incore copy */ while( NULL != fgets( line, sizeof(line), fp ) ) { /* Lose trailing newline */ line[ strlen(line) - 1 ] = '\0'; /*ALIGNOK*/ lp = (NAGLINE *) ecalloc( sizeof(*lp), 1 ); if( parseline( line, lp ) ) { if( lp->type == BAD ) DPRINTF(stderr, "Parsed OK: %s\n", lp->line ); else DPRINTF(stderr, "Parsed OK: %s %s %s %s\n", lp->datestr, lp->timestr, lp->intstr, lp->cmd ); } else { (void) fprintf(stderr, "%s: Can't parse line:\n%s\n%s %s\n", Myname, lp->line, parserrs[ lp->errtype ], lp->err ); } if( !Flist ) Flist = lp; if( Flast ) Flast->next = lp; Flast = lp; } (void) fclose(fp); if(Debug) { (void) fprintf(stderr, "Read file OK\n"); showlines( "\nLines after file read in:\n" ); } return TRUE; } /*---------------- * * editf() -- interactively edit the nag file in memory, then write it out. * * Used by 'okok' to make PENDING events SILENT; can also be used to * make SILENT events PENDING. * * Goes WAY out of it's way to force i/o to be on the terminal. * * Returns TRUE if lines were changed. */ int editf( what ) register int what; { register FILE *ifp; register FILE *ofp; register NAGLINE *lp; register EVENT *ep; register int changed = FALSE; char buf[ 80 ]; if( ( ifp = efopen( "/dev/tty", "r" ) ) == NULL ) return( changed ); if( ( ofp = efopen( "/dev/tty", "w" ) ) == NULL ) return( changed ); setbuf( ofp, NULL ); /* force output to be unbuffered */ for( lp = Flist; lp ; lp = lp->next ) { if( lp->type == what ) { /* only display events on the queue within 12 hours */ for( ep = Evq; ep && ep->lp != lp; ep = ep->next ) continue; if( !ep || ep->etime > Now + (HRSECS * 12) ) continue; (void) fprintf( ofp, "Silence %s: %s (y/n/q)? ", lp->timestr, lp->cmd ) ; if( fgets( buf, sizeof(buf), ifp ) == NULL ) break; if( buf[ 0 ] == 'y' || buf[ 0 ] == 'Y' ) { lp->type = ( what == PENDING ) ? SILENT : PENDING; changed = TRUE; } /* stop querying if a 'q' is entered */ if( buf[ 0 ] == 'q' || buf[ 0 ] == 'Q' ) break; } } (void) fclose( ifp ); (void) fclose( ofp ); return ( changed ); } /*---------------- * * writef() -- Write the file back out after a change. * * Returns TRUE if file wrote OK. */ int writef() { char buf[ 80 ]; register int err; register FILE *fp; register NAGLINE *lp; DPRINTF(stderr, "Writing %s\n", Nagfile ); if( ( fp = efopen( Nagfile, "w" ) ) == NULL ) return (FALSE); err = 0; for( lp = Flist; lp && err >= 0 ; lp = lp->next ) { switch( lp->type ) { case BAD: case COMMENT: err = fprintf( fp, "%s\n", lp->line ); break; default: err = fprintf( fp, "%c%s %s %s %s\n", lp->type == SILENT ? SILCHAR : ' ', lp->datestr, lp->timestr, lp->intstr, lp->cmd ); break; } } if( err < 0 ) { DPRINTF( stderr, "err %d\n", err ); (void) sprintf( buf, "%s: error writing %s", Myname, Nagfile ); perror( buf ); } else if( (err = fclose( fp ) ) < 0 ) { (void) sprintf( buf, "%s: error closing %s", Myname, Nagfile ); perror( buf ); return( FALSE ); } return ( err >= 0 ); } /*---------------- * * parseline() -- Split text into a NAGLINE more amenable to processing. * * Returns TRUE with the NAGLINE all set up if parsed OK. * Returns FALSE with the line->type set to BAD, * and line->errtype set if undecipherable. * * * in the code, buf points to the first character not processed, * cp points to the last character examined. * * cp places nulls in likely places. * * This is a very ugly function and should be rewritten. */ int parseline( buf, lp ) register char *buf; register NAGLINE *lp; { register char *cp; register int today; register int i; time_t d; time_t t; int anyday; struct tm ntm; /* now tm struct */ struct tm dtm; /* date tm struct */ struct tm ttm; /* time tm struct */ anyday = FALSE; lp->line = strcpy( emalloc( strlen( buf ) + 1 ), buf ); /* * determine line type, and advance buf to first non-blank after * the status field */ switch (*buf) { case COMCHAR: lp->type = COMMENT; return TRUE; /*NOTREACHED*/ case SILCHAR: lp->type = SILENT; buf++; break; default: lp->type = PENDING; break; } /* skip to non-whitespace */ while( *buf && isspace(*buf)) buf++; /* empty line isn't fatal (it's a comment) */ if (!*buf) { lp->type = BAD; lp->errtype = EMPTY; lp->err = buf; return TRUE; } /* bracket the day/date, and null terminate it */ for( cp = buf; *cp && !isspace( *cp ); cp++ ) continue; if( *cp ) *cp++ = '\0'; else *cp = '\0'; /* cp now positioned at char past null, or on null at the end */ /* * buf points at the day field; figure out the * absolute time of "Midnight" of the right day for the event. */ lp->datestr = strcpy( emalloc( strlen( buf ) + 1 ), buf ); /* figure when midnight of today was */ ntm = *localtime( &Now ); ntm.tm_sec = 0; ntm.tm_min = 0; ntm.tm_hour = 0; if (*buf == '*') { anyday = TRUE; dtm = ntm; } else { /* parse date */ if( NULL != gdate( buf, &dtm ) ) { DPRINTF(stderr, "not a date, maybe a day\n"); /* maybe it's a day name... */ lowcase( buf ); if( (i = find( buf, dow )) >= 0 ) { i--; today = ntm.tm_wday; DPRINTF(stderr, "today %s, event %s\n", dow[ today ], dow[ i ] ); if( i < today ) i += 7; /* it's next week */ d = Now + (( i - today ) * HRSECS * 24 ); dtm = *localtime( &d ); dtm.tm_sec = 0; dtm.tm_min = 0; dtm.tm_hour = 0; } else { DPRINTF(stderr, "find of %s in dow returned %d\n", buf, i ); lp->type = BAD; lp->errtype = DATEBAD; lp->err = buf; return FALSE; } } } d = tm_to_time( &dtm ); DPRINTF(stderr, "parseline: date %s\n", nctime(&d) ); /* advance to time */ for( buf = cp ; *buf && isspace(*buf); buf++) /* skip blanks */ continue; if (!*buf) { lp->type = BAD; lp->errtype = NOTIME; lp->err = buf; return FALSE; } /* bracket the time */ for( cp = buf; *cp && !isspace( *cp ); cp++ ) continue; if( *cp ) *cp++ = '\0'; else *cp = '\0'; /* * buf now at time field, figure offset until event, * then fill in absolute time. * * gtime can't fail -- it will say it's 00:00 if it * doesn't understand. */ DPRINTF(stderr, "parseline: time buf %s\n", buf ); lp->timestr = strcpy( emalloc( strlen( buf ) + 1 ), buf ); (void) gtime( buf, &ttm ); t = (ttm.tm_hour * HRSECS) + (ttm.tm_min * 60); lp->atime = d + t; /* ** If past the event, and it's for any day, do it tomorrow. ** BUG: This breaks if there is an interval after the event ** This is a rare case, and I haven't yet thought of a clean fix. */ if( anyday && lp->atime < Now ) lp->atime += HRSECS * 24; DPRINTF(stderr, "parseline: time offset %s is %d seconds, %02d:%02d\n", buf, t, t / HRSECS, t % HRSECS ); DPRINTF(stderr, "parseline: etime %s\n", nctime(&lp->atime)); /* advance to intervals */ for( buf = cp; *buf && isspace(*buf); buf++) continue; if (!*buf) { lp->type = BAD; lp->errtype = NOINTERVALS; lp->err = buf; return FALSE; } /* bracket the intervals */ for( cp = buf; *cp && !isspace( *cp ); cp++ ) continue; if( *cp ) *cp++ = '\0'; else *cp = '\0'; /* save the interval string. */ lp->intstr = strcpy( emalloc( strlen( buf ) + 1 ), buf ); /* take rest of the line as the command */ if (!*cp) { lp->type = BAD; lp->errtype = NOCMD; lp->err = strcpy( emalloc ( strlen( cp ) + 1 ), cp ); return FALSE; } lp->cmd = strcpy( emalloc ( strlen( cp ) + 1 ), cp ); return TRUE; } /*---------------- * * zaplines() -- delete all NAGLINEs and free their space * */ void zaplines() { register NAGLINE *lp; register NAGLINE *nlp; for( lp = Flist; lp ; lp = nlp ) { nlp = lp->next; if( lp->line ) free(lp->line); if( lp->datestr ) free(lp->datestr); if( lp->timestr ) free(lp->timestr); if( lp->intstr ) free(lp->intstr); if( lp->cmd ) free(lp->cmd); free( lp ); } Flast = Flist = NULL; } /*---------------- * * buildq() -- Rebuild the event queue if the .nag file has changed. * */ void buildq() { register NAGLINE *lp; DPRINTF(stderr, "buildq: rebuilding the event queue\n"); zapq(); for( lp = Flist; lp; lp = lp->next ) { /* add events for silenced lines too. */ if( lp->type != COMMENT ) addevents( lp ); } sortq(); if(Debug) showevents( "Event queue after rebuild and sort\n" ); } /*---------------- * * zapq() -- Destroy an event queue, setting the head back to NULL. * * Only the actual element is freed. */ void zapq() { register EVENT *this; register EVENT *next; for ( this = Evq; this ; this = next ) { next = this->next; free( this ); } Evq = NULL; } /*---------------- * * insq() -- Add a new EVENT to the head of a queue. * */ void insq( etime, offset, lp ) time_t etime; register int offset; NAGLINE *lp; { register EVENT *ep; etime += (offset * 60); /* add events after last time we ran, but no more than 24 hours in the future */ if( ( etime >= Now || ( Last && etime > Last ) ) && etime < ( Now + ( HRSECS * 24 ) ) ) { DPRINTF(stderr, "insq: Adding %s at %s\n", lp->cmd, nctime(&etime) ); } else /* too late */ { DPRINTF(stderr, "insq: Dropping %s at %s\n", lp->cmd, nctime(&etime) ); return; } /*ALIGNOK*/ ep = (EVENT *) emalloc( sizeof(*ep) ); ep->etime = etime; ep->offset = offset; ep->lp = lp; /* splice into the head of the queue */ ep->next = Evq; /* NULL, if last event */ Evq = ep; } /*---------------- * * addevents() -- Add pending events for the NAGLINE to the queue. * * Events in the past are not considered. * If the command has been silenced, don't do the command. * */ void addevents( lp ) register NAGLINE *lp; { register char *cp; /* ptr into the interval string */ int offset; /* offset in minutes */ /* for every numeric value in the interval string... */ for( cp = lp->intstr; cp && *cp ; cp = index( cp, ':' ) ) { if (*cp == ':') /* skip past optional ':' */ cp++; if (!*cp) /* ignore trailing ':' */ return; /* read (possibly) signed interval value */ if( 1 != sscanf( cp, "%d", &offset ) ) { (void) fprintf(stderr, "%s: bad intervals '%s'\n", Myname, lp->intstr ); return; } insq( lp->atime, offset, lp ); } } /*---------------- * * timecmp() -- Compare time of two events. * * Made slightly tricky since it must return an int, not a time_t. * */ int timecmp( a, b ) register EVENT **a; register EVENT **b; { time_t val = (*a)->etime - (*b)->etime; return( val < 0 ? -1 : val > 0 ); } /*---------------- * * sortq() -- Sort the event queue into chronological order. * * 1. Create an array of pointers to the events in the queue. * 2. Sort the array by time of the pointed-to events. * 3. Rebuild the queue in the order of the array. * */ void sortq() { register unsigned int n; /* number of events in the queue */ register unsigned int i; /* handy counter */ register EVENT **events; /* allocated array of EVENT ptrs */ register EVENT **ap; /* ptr into allocated events */ register EVENT *ep; /* pointer in event chain */ forward int timecmp(); n = 0; for( ep = Evq; ep; ep = ep->next ) n++; DPRINTF(stderr, "sortq: %d events\n", n ); if ( n < 2 ) return; /* build array of ptrs to events */ /*ALIGNOK*/ ap = events = (EVENT **) ecalloc( (unsigned)sizeof(**ap), n ); /* build array of ptrs to events */ for( ep = Evq; ep; ep = ep->next ) *ap++ = ep; /* sort by ascending time */ (void) qsort( events, (unsigned)n, sizeof(*events), timecmp ); /* rechain the event queue from the sorted array */ Evq = ep = events[0]; for ( i = 0 ; i < n ; ) { ep->next = events[i++]; ep = ep->next; } ep->next = NULL; free( events ); } /*---------------- * * runq() -- Execute all events that are due. * * Sleep until the next scheduled event. If there are none, or * next is far away, sleep for MINSLEEP and try again. * */ void runq() { char cmd[ 5120 ]; char now[ CTIMELEN ]; register EVENT *evq; /* standin for global Evq in loop */ register EVENT *ep; /* next event */ register NAGLINE *lp; int dsecs; DPRINTF(stderr, "runq start at %s\n", Nowstr ); evq = Evq; /* fast access, be sure to save back */ /* * Execute commands that are due. * * Keeps head of the queue current by cutting out events as * they are processed. * * The loop breaks out when the queue is gobbled up, * or we get to an event that is not due now. */ while( evq && evq->etime <= Now ) { lp = evq->lp; DPRINTF(stderr, "due at %s:\n", nctime( &evq->etime ) ); /* Run a PENDING event */ if( lp->type == PENDING && lp->cmd ) { (void)strcpy( now, &Nowstr[ 11 ] ); now[ 5 ] = '\0'; (void)sprintf( cmd, "pretime=%d;posttime=%d;now=%s;then=%s;%s\n", -evq->offset, evq->offset, now, nhour( &lp->atime ), lp->cmd ); DPRINTF(stderr, "executing:\n%s\n", cmd ); if( system( cmd ) ) (void) fprintf( stderr, "%s: Trouble running\n'%s'\n", Myname, cmd ); } /* if it's a SILENT event, is it time to make it PENDING? */ if( lp->type == SILENT ) { /* find the queue end or the next event for the line */ for( ep = evq->next ; ep && ep->lp != lp ; ep = ep->next ) continue; /* if match, or it was the last in the queue, turn it on */ if ( ep ) { DPRINTF(stderr, "SILENT event\n"); } else { DPRINTF(stderr, "Last SILENT event, making PENDING again.\n"); lp->type = PENDING; /* * if the write fails, keep going and hope the user fixes * the nag file. If we exit, the daemon would need * to be restarted by hand. Since it won't do anything * but sleep and exit when the user logs off, no harm * is done by sticking around. */ (void) writef(); } } ep = evq->next; free( evq ); evq = ep; } /* for events on the queue */ dsecs = evq ? min( evq->etime - Now, MINSLEEP) : MINSLEEP; DPRINTF(stderr, "sleeping for %d seconds, next %s\n", dsecs, evq ? nctime( &evq->etime ) : "never" ); Evq = evq; /* back to global var */ delay( dsecs ); } /*---------------- * * emalloc() -- malloc with error msg. * */ char * emalloc( size ) register int size; { register char *ptr; extern char *malloc(); if ( ( ptr = malloc( (unsigned) size ) ) == NULL ) { (void) fprintf(stderr, "%s: Can't malloc %d bytes\n", Myname, size ); exit(1); } return( ptr ); } /*---------------- * * ecalloc() -- calloc with error message. * */ char * ecalloc( n, size ) register unsigned int n; register unsigned int size; { register char *ptr; extern char *calloc(); if ( ( ptr = calloc( (unsigned) size, n ) ) == NULL ) { (void) fprintf(stderr, "%s: Can't calloc %d bytes\n", Myname, size * n); exit(1); } return( ptr ); } /* * efopen() -- fopen with error message on failure (no fatal error) */ FILE * efopen( file, mode ) char *file; char *mode; { char buf [ 80 ]; register FILE * fp; if( (fp = fopen( file, mode )) == NULL ) { (void)sprintf( buf, "%s: can't open file %s with mode \"%s\"", Myname, file, mode ); perror( buf ); } return( fp ); } /* * showline() -- Dump the line list. */ void showlines( msg ) char *msg; { register NAGLINE *lp; (void) fprintf(stderr, "%s", msg ); for( lp = Flist; lp ; lp = lp->next ) dumpline( lp ); } /* * dumpline() -- dump a NAGLINE for debugging. */ void dumpline( lp ) register NAGLINE *lp; { if( lp == NULL ) { (void) fprintf(stderr, "dumpline: NULL lp\n"); return; } (void) fprintf(stderr, "\nline (%s):\n%s\n", linetypes[ lp->type ], lp->line ); switch( lp->type ) { case BAD: (void) fprintf(stderr, "%s %s\n", parserrs[ lp->errtype ], lp->err ); break; case PENDING: case SILENT: (void) fprintf(stderr, "The event is at %s\n", nctime( &lp->atime )); } } /* * showevents() -- dump the event list, for debugging. */ void showevents( msg ) char *msg; { register EVENT *ep; (void) fprintf(stderr, "%s", msg ); for( ep = Evq; ep; ep = ep->next ) dumpevent( ep ); } /* * dumpevent() -- print an event, for debugging. */ void dumpevent( ep ) register EVENT *ep; { if( ep == NULL ) (void) fprintf(stderr, "dumpevent: NULL ep\n"); else (void) fprintf(stderr, "event 0x%x, next 0x%x offset %d time %s\n", ep, ep->next, ep->offset, nctime(&ep->etime) ); } /* * nctime() -- ctime with trailing '\n' whacked off. */ char * nctime( t ) time_t *t; { register char *cp; cp = ctime( t ); cp[ strlen( cp ) - 1 ] = '\0'; return ( cp ); } /* * nhour() -- return an hh:mm string given a pointer to a time_t. */ char * nhour( t ) time_t *t; { register char *buf = ctime( t ); /* * 012345678901234567890123 * Wed Dec 31 16:00:00 1969 */ buf[ 16 ] = '\0'; return ( &buf[ 11 ] ); } /*---------------- * * delay() -- like sleep but knows what 0 means. * * If user logs out, notices and exit with OK status. * */ void delay( secs ) int secs; { char thislogin[20]; if( secs > 0) { (void) sleep( (unsigned) secs ); (void) strcpy(thislogin, getlogin()); if ( strcmp(Origlogin, thislogin) ) exit(0); } } /* * lowcase() -- make a string all lower case. */ void lowcase( s ) char *s; { while ( *s ) { if( isupper( *s ) ) *s = tolower( *s ); s++; } } # if 0 /* * dumptm() -- show contents of a tm structure. */ dumptm( tm ) struct tm *tm; { (void) fprintf(stderr, "year : %d month: %d day: %d\n", tm->tm_year,tm->tm_mon,tm->tm_mday); (void) fprintf(stderr, "day of month: %d hour: %d minute: %d second: %d\n", tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec) ; (void) fprintf(stderr, "day of year: %d day of week: %d dst: %d\n", tm->tm_yday, tm->tm_wday, tm->tm_isdst) ; } # endif /* end of nag.c */