home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 11 Util
/
11-Util.zip
/
TIMEXSRC.ZIP
/
BA_EVCTL.C
< prev
next >
Wrap
Text File
|
1990-03-29
|
23KB
|
784 lines
/* ba_evctl.c -- Background Agent event control
February 1990 Mark E. Mallett, Personal Workstation Magazine
This file contains routines that control events.
Included are:
schedule_events The event scheduler thread
*/
#define INCL_BASE /* Get all DOS defs */
#include <stdio.h>
#include <io.h>
#include <sys/types.h>
#include <time.h>
#include <string.h>
#include <memory.h>
#include <os2.h>
#include <dos.h>
#include "timex.h"
#include "ba_timex.h"
/* Local Definitions */
/* External data referenced */
extern HSEM Eventsem; /* Event list interlock semaphore */
extern PEVENT *PendingL; /* List of pending events */
extern int Priclass; /* My priority class */
extern int Prival; /* My priority value */
/* External routines used */
extern void log_begin( void );
extern void log_end( void );
extern long tmclock( struct tm *tmP );
extern struct tm *xlocaltime( time_t * ); /* Fix for buggy MSC one. */
/* Local data publicly available */
/* Local routines and forward declarations */
void action( PEVENT *peventP );
void act_run( PEVENT *peventP );
void calcntime( PEVENT *peventP );
static void calc_dayof( EVENT *eventP, struct tm *tmP, int op );
static void calc_hour( EVENT *eventP, struct tm *tmP, int op );
static void calc_min( EVENT *eventP, struct tm *tmP, int op );
static void calc_month( EVENT *eventP, struct tm *tmP, int op );
static void calc_sec( EVENT *eventP, struct tm *tmP, int op );
static void calc_year( EVENT *eventP, struct tm *tmP, int op );
/* Private data */
/* Table of days per month */
static int Mdays[12] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
/*
*//* schedule_events()
Schedule the events
Accepts :
Returns :
Notes :
This is the routine that handles the scheduling of pending
events. Since the events on the pending list (PendingL) are in
due-time order, only the first event need be looked at to determine
the next schedule time. This routine waits for that event to come
due (in small increments, so that external changes can be caught
in reasonable time), and schedules it.
*/
void
schedule_events()
{
time_t curtime; /* Current time (in seconds) */
LONG sleeptime; /* Milliseconds to sleep */
PEVENT *peventP; /* Ptr to next pending event */
/* Loop forever, scheduling events. */
for( ; ; ) {
/* Figure out how long to sleep until the next event is ready. */
if ( ( peventP = PendingL ) == NULL )
/* No next event ... use default time */
sleeptime = SLEEPINCR;
else {
/* Get difference between due time and time now */
curtime = time( (time_t *)NULL );
sleeptime = peventP->pe_ntime - curtime;
/* Interesting compiler problem: MSC 5.1 doesn't do the
multiplication by 1000 without the (long) cast. It
compiles fine, but has no effect. */
if ( sleeptime < ( 0x7fffffff/1000 ) )
sleeptime = (long)sleeptime * 1000;
if ( sleeptime > SLEEPINCR )
sleeptime = SLEEPINCR;
}
/* If we must sleep (positive sleeptime), then sleep. */
if ( sleeptime > 0 )
DosSleep( sleeptime );
/* Check to see if first event is ready to go. */
if ( ( peventP = PendingL ) != NULL ) {
curtime = time( (time_t *)NULL );
if ( curtime >= peventP->pe_ntime ) {
/* First event appears ready. Grab access to the data
before proceeding. */
get_sem( "schedule_events", "event list", Eventsem );
/* Run the first event if it is still ready */
curtime = time( (time_t *)NULL );
if ( ( ( peventP = PendingL ) != NULL ) &&
( curtime >= peventP->pe_ntime ) ) {
/* Do the action for the event */
action( peventP );
/* Remove the event from the event list */
unschedule( peventP );
/* Schedule the event again. */
schedule( peventP );
}
/* Release access to the events */
rel_sem( "schedule_events", "event list", Eventsem );
}
}
}
}
/*
*//* action( peventP )
Perform the action for an event
Accepts :
peventP A pending event structure
Returns :
< nothing >
*/
void
action( peventP )
PEVENT *peventP; /* Event to make act */
{
/* If the event specifies a particular priority, setup our
priority before doing the action. */
if ( peventP->pe_eventP->ev_priclass != -1 )
set_priority( 0, peventP->pe_eventP->ev_priclass,
peventP->pe_eventP->ev_prival );
/* Dispatch according to the action type */
switch( peventP->pe_eventP->ev_acttype ) {
case ACTION_RUN: /* Run a program */
act_run( peventP );
break;
default: /* Error.. */
warning( EC_UNKACTION, "unknown action type in action()" );
break;
}
/* Reset program priority */
if ( peventP->pe_eventP->ev_priclass != -1 )
set_priority( 0, Priclass, Prival );
}
/*
*//* act_run( peventP )
Perform a "RUN" action for an event.
Accepts :
peventP The run event
Returns :
<nothing>
*/
void
act_run( peventP )
PEVENT *peventP; /* Ptr to event */
{
int status; /* Status code */
RESULTCODES result; /* Result from prog run */
char *cmdP; /* Ptr to command line */
char *argP; /* Ptr to arg string */
char progname[128]; /* Name of program */
char args[128]; /* Program arguments */
char failbuf[256]; /* Failure string buffer */
/* Extract the program name from the action string */
cmdP = peventP->pe_eventP->ev_actargP;
cmdP = gettoken( cmdP, &progname[0], 128, "", " \t" );
/* Get argument string and double-NUL-terminate it */
argP = &args[0];
strcpy( argP, &progname[0] ); /* Command name */
argP += strlen( argP );
*argP++ = NUL; /* Name must be nul-terminated */
strcpy( argP, cmdP ); /* Arg string */
argP += strlen( argP );
argP[1] = NUL; /* Must be double-nul'ed */
/* Log the attempt to start the program */
log_begin(); /* Redirect to log file */
log( "Starting RUN of \"%s\" with args \"%s\"", &progname[0],
&args[ strlen( &args[0] ) +1] );
/* Run the program */
status = DosExecPgm( &failbuf[0], /* Failure filename buffer */
256, /* Size of fail buff */
EXEC_SYNC, /* Flags */
&args[0], /* Command line args */
0, /* Environment string */
&result, /* Results */
&progname[0] /* Program */
);
if ( status != 0 )
warning( EC_NOTOK, "DosExecPgm failed: %d", status );
else
/* Success; log the program termination. */
log( "Program terminated with status %d", result.codeResult );
log_end(); /* End log redirect */
}
/*
*//* schedule( peventP )
Schedule an event
Accepts :
peventP Ptr to pending event structure
Returns :
< nothing >
Notes :
This routine adds the event to the event list, in proper
time-ordered place.
*/
void
schedule(
PEVENT *peventP /* Ptr to pending event struct */
) {
time_t ntime; /* Next event fire time */
PEVENT *nextP; /* Ptr to next one on list */
PEVENT *prevP; /* Previous one */
/* Calculate the next fire time */
calcntime( peventP );
ntime = peventP->pe_ntime;
/* Find the place to put the new event */
prevP = NULL;
for( nextP = PendingL; nextP != NULL;
prevP = nextP, nextP = nextP->pe_nextP ) {
if ( ntime < nextP->pe_ntime )
break;
}
/* Link in the new event */
peventP->pe_nextP = nextP;
peventP->pe_prevP = prevP;
if ( nextP != NULL )
nextP->pe_prevP = peventP;
if ( prevP != NULL )
prevP->pe_nextP = peventP;
else
PendingL = peventP;
}
/*
*//* unschedule( peventP )
Unschedule an event
Accepts :
peventP Ptr to pending event structure
Returns :
Notes :
This routine removes an event from the time-ordered list
No check is made to ensure that the event is actually on the
list; it's assumed that the program calls this routine without
error.
*/
void
unschedule(
PEVENT *peventP /* Ptr to pending event */
) {
PEVENT *prevP; /* Ptr to previous */
PEVENT *nextP; /* Ptr to next */
/* Unlink it */
prevP = peventP->pe_prevP;
nextP = peventP->pe_nextP;
if ( prevP != NULL )
prevP->pe_nextP = nextP;
else
PendingL = nextP;
if ( nextP != NULL )
nextP->pe_prevP = prevP;
}
/*
*//* event_find( nameP )
Find an event by name
Accepts :
nameP Name to find
Returns :
< value > ptr to PEVENT, or NULL if no event found.
*/
PEVENT *
event_find(
char *nameP /* Name of the event */
) {
PEVENT *peventP; /* Ptr to event... */
/* Look through the list */
for( peventP = PendingL; peventP != NULL; peventP = peventP->pe_nextP )
if ( stricmp( nameP, peventP->pe_eventP->ev_nameP ) == 0 )
break;
/* Return what we got */
return ( peventP );
}
/*
*//* calcntime( peventP )
Calculate next "fire" time for an event
Accepts :
peventP Ptr to pending event struct
Returns :
peventP modified to include next fire time
Notes :
Next fire time is calculated relative to the current time. For
events that fire rapidly, this could result in missed triggers.
I don't see that as a problem: events that fire that quickly
should not be allowed to get behind the clock, and most events
won't be in that category anyway. If there's a requirement for
the other mode, it could be added as a special run category.
But it also probably wouldn't work: if an event gets behind and
is not allowed to catch up, it will likely get further and
further behind anyway.
The calculation of the next fire time is done via a suite of
routines that either advance or minimize the particular
component, according to the constraints in the event
description. When a component is advanced beyond its
maximum, and wraps around, a carry is given to the next
higher compoent, and a minimize is done on the next lower
component. When a minimize operation is carried out,
a minimize is also performed on the next lower component.
Thus this is like a series of digit wheels, causing right-and-
left cascading.
Some rigamarole has to be done to maintain proper day of week.
This is reflected in the fiddling with day-of-week and
day-of-year in places where the day is adjusted (year, month,
and day).
I'm open to suggestions for a better way to do this...
*/
void
calcntime(
PEVENT *peventP /* Ptr to pending event */
) {
time_t curtime; /* Current time */
time_t ntime; /* New time */
struct tm curtm; /* Current time component */
struct tm ntm; /* New time components */
curtime = time( (time_t *)NULL );
memcpy( &curtm, xlocaltime( &curtime ), sizeof( struct tm ) );
memcpy( &ntm, &curtm, sizeof( struct tm ) );
/* Start the digit ticker by advancing the seconds */
calc_sec( peventP->pe_eventP, &ntm, 1 );
/* Result is in ntm. Year has been set to -1 if there's no
possible next fire time. */
if ( ntm.tm_year == -1 )
ntime = 0x7fffffff;
else {
ntime = tmclock( &ntm );
if ( ntime == -1 )
ntime = 0x7fffffff;
}
/* Store the new value */
peventP->pe_ntime = ntime;
}
/*
*//* calc_xxx( eventP, tmP, op )
Calculate-component routines follow. These routines perform
a minimize or maximize operation on a particular time component
as constrained by an event description, as described in the
notes for calcntime().
Accepts :
eventP Ptr to event struct
tmP Ptr to time value to manipulate
op Operation: -1 = validate, 0 = minimize, 1 = maximize
Returns :
Notes :
Note that both min and max operations cause a right-minimize.
*/
static void
calc_dayof( EVENT *eventP, struct tm *tmP, int op ) {
int dommax; /* Maximum day of month */
/* Get max days in this month */
dommax = Mdays[tmP->tm_mon];
if ( tmP->tm_mon == 1 ) /* Check Feb */
if ( tmP->tm_year % 4 == 0 )
++dommax;
/* Proceed according to day-of class */
if ( eventP->ev_dayof == DAYOF_MONTH ) {
if ( op == -1 ) /* Validate */
/* Decrement current value and go through the increment. */
--tmP->tm_mday;
else if ( op == 0 )
/* We'll set day of month to zero and then advance it to
a constrained value */
tmP->tm_mday = 0;
/* Advance day of month until it meets constraints */
for( ; ; ) {
if ( ++tmP->tm_mday > dommax ) {
/* Exceeded month; carry into next month. */
calc_month( eventP, tmP, 1 );
return;
}
/* Check constraints */
if ( ( eventP->ev_dayofs == 0 ) ||
( ( eventP->ev_dayofs & ( 1L << ( tmP->tm_mday -1 ) ) )
!= 0 )
)
/* OK. */
break;
op = 1; /* No longer just validating. */
}
}
else { /* Day-of week */
if ( op == -1 ) { /* Validate */
/* Back up one, and go through the increment process. */
--tmP->tm_wday;
--tmP->tm_yday;
--tmP->tm_mday;
}
else if ( op == 0 ) { /* Minimize */
/* As with dayofmonth, we'll reset to a point were we can
advance to a valid value. */
tmP->tm_wday -= (tmP->tm_mday % 7);
tmP->tm_yday -= tmP->tm_yday;
tmP->tm_mday = 0;
}
/* Advance until constraint is met */
for ( ; ; ) {
++tmP->tm_mday;
++tmP->tm_yday;
tmP->tm_wday = (tmP->tm_wday +1 ) % 7;
if ( tmP->tm_mday > dommax ) {
/* Went over month; carry into next one. */
calc_month( eventP, tmP, 1 );
return;
}
/* Check day-of-week constraints. Note that TIMEX operates
on a monday=0 basis, whereas the tm struct has sunday=0.
Adjustment has to be made for that.
*/
if ( ( eventP->ev_dayofs == 0 ) ||
( ( eventP->ev_dayofs &
( 1L << ( ( tmP->tm_wday + 6 ) % 7 ) ) ) != 0 )
)
break;
op = 1; /* Not just validating now. */
}
}
if ( op != 0 ) /* Validate left if not minimizing */
calc_month( eventP, tmP, -1 );
if ( op != -1 ) /* Minimize right if not validating */
calc_hour( eventP, tmP, 0 );
}
static void
calc_hour( EVENT *eventP, struct tm *tmP, int op ) {
if ( op == -1 ) /* Validate */
/* Back off an hour, and work forward. */
--tmP->tm_hour;
else if ( op == 0 ) /* Minimize */
tmP->tm_hour = -1;
/* Advance hour until it is OK. */
for( ; ; ) {
++tmP->tm_hour;
if ( tmP->tm_hour == 24 ) {
/* Went too far; carry into dayofs. */
calc_dayof( eventP, tmP, 1 );
return;
}
if ( ( eventP->ev_hours == 0 ) ||
( ( eventP->ev_hours & ( 1L << tmP->tm_hour ) ) != 0 )
)
break;
op = 1; /* Clear validation. */
}
if ( op != 0 ) /* Validate left if not minimizing */
calc_dayof( eventP, tmP, -1 );
if ( op != -1 ) /* Minimize right if not validating */
calc_min( eventP, tmP, 0 );
}
static void
calc_min( EVENT *eventP, struct tm *tmP, int op ) {
if ( op == -1 ) /* Validate */
--tmP->tm_min;
else if ( op == 0 ) /* Minimize */
tmP->tm_min = -1;
/* Advance minutes until OK */
for( ; ; ) {
++tmP->tm_min;
if ( tmP->tm_min == 60 ) {
/* Carry into the next hour. */
calc_hour( eventP, tmP, 1 );
return;
}
if ( ( eventP->ev_mins[0] == 0 ) &&
( eventP->ev_mins[1] == 0 )
)
break;
if ( ( eventP->ev_mins[tmP->tm_min/32] &
( 1L << ( tmP->tm_min % 32 ) ) ) != 0 )
break;
op = 1; /* Clear validation */
}
if ( op != 0 ) /* Validate left if not minimizing */
calc_hour( eventP, tmP, -1 );
if ( op != -1 ) /* Minimize right if not validating */
calc_sec( eventP, tmP, 0 );
}
static void
calc_month( EVENT *eventP, struct tm *tmP, int op ) {
int mdays; /* Days in month */
if ( op == -1 ) /* Validation */
/* Back up one and then find a valid month by incrementing. */
--tmP->tm_mon;
else if ( op == 0 ) { /* Minimize */
/* Set to minus month in preparation for advancing */
tmP->tm_mon = -1;
tmP->tm_wday =- ( tmP->tm_yday % 7 );
tmP->tm_yday = -1;
}
/* Advance until properly constrained */
for( ; ; ) {
if ( tmP->tm_mon == 11 ) {
/* Carry into the next year */
calc_year( eventP, tmP, 1 );
return;
}
if ( op == 1 ) {
mdays = Mdays[tmP->tm_mon];
if ( ( tmP->tm_mon == 1 ) &&
( ( tmP->tm_year % 4 ) == 0 )
)
++mdays;
tmP->tm_wday += (mdays % 7);
tmP->tm_yday += mdays;
}
++tmP->tm_mon;
if ( ( eventP->ev_months == 0 ) ||
( ( eventP->ev_months & ( 1 << tmP->tm_mon ) ) != 0 )
)
break;
/* No longer validating... in increment mode. */
op = 1;
}
if ( op != 0 ) /* Validate left if not minimizing */
calc_year( eventP, tmP, -1 );
if ( op != -1 ) /* Minimize right if not validating */
calc_dayof( eventP, tmP, 0 );
}
static void
calc_sec( EVENT *eventP, struct tm *tmP, int op ) {
/* TIMEX doesn't keep track of seconds, but this routine is
here just to keep them zero. */
tmP->tm_sec = 0;
/* If it's not a minimize, pass the operation leftwards. */
if ( op != 0 )
calc_min( eventP, tmP, op );
}
static void
calc_year( EVENT *eventP, struct tm *tmP, int op ) {
int year; /* Actual year. */
int yX; /* Year index */
int ydays; /* Days in the year */
if ( op == -1 ) /* Validate */
/* Decrement and then walk forward as if incrementing. */
--tmP->tm_year;
else if ( op == 0 ) /* Minimize */
/* There is no year minimize -- just return. */
return;
/* Must keep advancing by single years to keep the weekdays and
yeardays in line. */
for( ; ; ) {
if ( op == 1 ) {
/* Adjust day of week. Note that this (as well as the other
tm components such as month day) won't be exactly right
if we just left it like this -- it's only used by other
functions (month, day of month) when they do a minimize.
*/
ydays = 365;
if ( ( tmP->tm_year % 4 ) == 0 )
++ydays;
tmP->tm_wday += (ydays % 7);
}
/* Bump year and see if it matches constraints. */
++tmP->tm_year;
if ( eventP->ev_yearC == 0 )
break; /* No year constraints. */
/* See if we're in the table anywhere. */
year = tmP->tm_year + 1900;
for( yX = 0; yX < eventP->ev_yearC; ++yX )
if ( ( year >= eventP->ev_yearP[yX].y_first ) &&
( year <= eventP->ev_yearP[yX].y_last )
)
break;
if ( yX == eventP->ev_yearC ) {
/* No matching year -- can't schedule this event. We set
the year to -1 as a signal to the caller, and return.
*/
tmP->tm_year = -1;
return;
}
/* No longer validating.. */
op = 1;
}
if ( op != -1 ) /* Minimize right if not validating */
calc_month( eventP, tmP, 0 );
}