Listing 1. Schedule

<Begin schedule.h>

#define NUMEVENTS 100
#define MAXFDS 20
char * host = "crabapple";
char * port = "5432";
char * tty = "";
char * option = "";
char * dbname = "jim";
char* LOGFILE = "/usr/schedule/schedlog";
void update(char*,char*);
<Begin schedule.c>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <bits/signum.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <bits/waitflags.h>
#include <libpq-fe.h>
#include <sqlca.h>
#include "schedule.h"

struct {
    char days[8];
    char time[6];
    char program[101];
    char checkprog[101];
    char didrun[2];
} schedule[NUMEVENTS];

PGconn *conn;
FILE *LOG;

void
openlog()
{
    LOG = fopen(LOGFILE,"w");
    if(!LOG){
        exit(EXIT_FAILURE);
    }
}

void logit(char * message)
{
    fprintf(LOG,"%s\n",message);
    return;
}

int read_config()
{
    int i;
    PGresult *res;
    char buf[100];

    conn = PQsetdb(host,port,tty,option,dbname);
    if(PQstatus(conn) == CONNECTION_BAD){
        logit("Connection to database failed.");
	sprintf(buf, "%s", PQerrorMessage(conn));
	logit(buf);
	exit(EXIT_FAILURE);
    }
    res = PQexec(conn,"BEGIN");
    if(PQresultStatus(res) != PGRES_COMMAND_OK){
        logit("Begin command failed.");
        exit(EXIT_FAILURE);
    }
    PQclear(res);
    res = PQexec(conn,"declare mycursor cursor for select * from schedule where didrun = 'N'");
    if(PQresultStatus(res) != PGRES_COMMAND_OK){
        logit("Cursor command failed.");
        exit(EXIT_FAILURE);
    }
    PQclear(res);
    res = PQexec(conn,"FETCH ALL IN mycursor");
    if(PQresultStatus(res) != PGRES_TUPLES_OK){
        logit("Fetch failed.");
        exit(EXIT_FAILURE);
    }
    for(i = 0; i < NUMEVENTS; i++){
        strcpy(schedule[i].didrun,"Y");
    }
    for(i = 0;i < PQntuples(res);i++){
        strncpy(schedule[i].days,PQgetvalue(res,i,0),7);
        schedule[i].days[PQgetlength(res,i,0) + 4] = '\0';
        strncpy(schedule[i].time,PQgetvalue(res,i,1),5);
        schedule[i].time[PQgetlength(res,i,1) + 4] = '\0';
        strncpy(schedule[i].program,PQgetvalue(res,i,2),100);
        schedule[i].program[PQgetlength(res,i,2) + 4] = '\0';
        strncpy(schedule[i].checkprog,PQgetvalue(res,i,3),100);
        schedule[i].checkprog[PQgetlength(res,i,3) + 4] = '\0';
        strncpy(schedule[i].didrun,PQgetvalue(res,i,4),1);
        schedule[i].didrun[1] = '\0';
    } 
    schedule[i].days[0] = '\0';
    PQclear(res);
    res = PQexec(conn,"CLOSE mycursor");
    PQclear(res);
    res = PQexec(conn,"END");
    PQclear(res);
    PQfinish(conn);
    return 0;
}

void
update(char *prog,char *state)
{
    char buf[200];
    PGresult *res;
    conn = PQsetdb(host,port,tty,option,dbname);
    if(PQstatus(conn) == CONNECTION_BAD){
        logit("Connection to database failed.");
        exit(EXIT_FAILURE);
    }
    
    sprintf(buf,"update schedule set didrun = \'%s\' \
              where program = \'%s\'",state,prog);
    res = PQexec(conn,"BEGIN");
    if(PQresultStatus(res) != PGRES_COMMAND_OK){
        exit(EXIT_FAILURE);
    }
    PQclear(res);
    res = PQexec(conn,buf);
    if(PQresultStatus(res) != PGRES_COMMAND_OK){
        exit(EXIT_FAILURE);
    }
    PQclear(res);
    PQexec(conn,"COMMIT");
    PQfinish(conn);
    return;
	
}

void 
dofork(int j)
{
    int k,hour,min;
    char msg[120];
    pid_t pid;
    time_t t;
    struct tm *tmp;

    time(&t);
    tmp = localtime(&t);
    hour = tmp->tm_hour;
    min = tmp->tm_min;
    pid = fork();
    if(pid == 0) { /* This is the child. */
        update(schedule[j].program,"P");
        k = system(schedule[j].checkprog);
        if(k == 0){
            ;
        } else {
            update(schedule[j].program,"N");
            sprintf(msg,"%s failed at %02d:%02d",schedule[j].checkprog,hour,min);
            logit(msg);
            exit(EXIT_FAILURE);
        }
        k = system(schedule[j].program);
        time(&t);
        tmp = localtime(&t);
        hour = tmp->tm_hour;
        min = tmp->tm_min;

        if(k == 0){
            update(schedule[j].program,"Y");
            sprintf(msg,"%s succeeded at %02d:%02d",schedule[j].program,hour,min);
            logit(msg);
            exit(EXIT_SUCCESS);
        } else {
            update(schedule[j].program,"N");
            sprintf(msg,"%s FAILED at %02d:%02d",schedule[j].program,hour,min);
            logit(msg);
            exit(EXIT_FAILURE);
        }
    } else { /* This is the parent */
        sleep(30); /* Give the child some time */
        read_config();
        return;
    }
}

void 
scheduler(void)
{
    int j = 0,day,hour,min,k,l;
    time_t t;
    struct tm *tmp;

    while(1){
        time(&t);
        tmp = localtime(&t);
        hour = tmp->tm_hour;
        min = tmp->tm_min;
        day = tmp->tm_wday;

        if(schedule[j].days[day] == 'N'){
            j++;
            if(schedule[j].days[0] == '\0'){
                j = 0;
		sleep(60);
                read_config();
                continue;
            }
            continue;
        } 
            
        sscanf(schedule[j].time,"%02d:%02d",&k,&l);
        if((hour > k) || ((k == hour) && (min >= l))){
           if(schedule[j].didrun[0] == 'N'){
               dofork(j);
               j++;
               if(schedule[j].days[0] == '\0'){
                   j = 0;
		   sleep(60);
                   read_config();
                   continue;
               }
               continue;
           } else {
               j++;
               if(schedule[j].days[0] == '\0'){
                   j = 0;
                   sleep(60);
                   read_config();
                   continue;
               }
               continue;
           }
        }
        j++;   
        if(schedule[j].days[0] == '\0'){
            j = 0;
            sleep(60);    
            read_config();
        }         
    }
}

void
do_exit(int signo)
{
    char buf[100];
    time_t t;
    int hour,min;
    struct tm *tmp;

    if(signo != SIGUSR1){
        ;
    }
    time(&t);
    tmp = localtime(&t);
    hour = tmp->tm_hour;
    min = tmp->tm_min;
    sprintf(buf,"Killed at %02d:%02d.",hour,min);
    logit(buf);
    exit(EXIT_SUCCESS);
}

void
sig_child(int signo)
{
    pid_t pid;
    int stat;

    if(signo != SIGCHLD){
	;
    }
    signal(SIGCHLD,sig_child);
    while((pid = waitpid(-1, &stat, WNOHANG)) > 0);
    return;
}
    

void setup()
{
    signal(SIGHUP,SIG_IGN);
    signal(SIGINT,SIG_IGN);
    signal(SIGQUIT,SIG_IGN);        
    signal(SIGILL,SIG_IGN);        
    signal(SIGTRAP,SIG_IGN);      
    signal(SIGABRT,SIG_IGN);     
    signal(SIGIOT,SIG_IGN);     
    signal(SIGBUS,SIG_IGN);    
    signal(SIGFPE,SIG_IGN);         
    signal(SIGKILL,SIG_IGN);       
    signal(SIGUSR1,do_exit);      
    signal(SIGSEGV,SIG_IGN);     
    signal(SIGUSR2,SIG_IGN);    
    signal(SIGPIPE,SIG_IGN);   
    signal(SIGALRM,SIG_IGN);  
    signal(SIGTERM,SIG_IGN); 
    signal(SIGCHLD,sig_child);
}

void daemonize()
{
	int i;
	pid_t pid; 	

	if((pid = fork()) != 0){
		exit(EXIT_SUCCESS);
	}

	setsid();
	if((pid = fork()) != 0){
		exit(EXIT_SUCCESS);
	}
        chdir("/");
	umask(0);

	for(i = 0; i < MAXFDS; i++){
		close(i);
	}
        
}

int
main(void)
{
    int j;

    system("at -f /usr/schedule/reset.sh midnight");
    daemonize();
    setup();
    openlog();
    j = read_config();
    if(j != 0){
	exit(EXIT_FAILURE);
    }
    scheduler();
    exit(EXIT_SUCCESS);
}

<Begin Makefile>

schedule:	schedule.c
		gcc -g -W -Wall -ansi  -pedantic schedule.c -o schedule -lpq