1
/***************************************************************/
5
/* Queue up reminders for subsequent execution. */
7
/* This file is part of REMIND. */
8
/* Copyright (C) 1992-1998 by David F. Skoll */
9
/* Copyright (C) 1999-2000 by Roaring Penguin Software Inc. */
11
/***************************************************************/
14
static char const RCSID[] = "$Id: queue.c,v 1.16 2000/06/26 14:44:07 dfs Exp $";
16
/* Solaris needs this to get select() prototype */
18
#define __EXTENSIONS__ 1
21
/* We only want object code generated if we have queued reminders */
26
#include <sys/types.h>
29
#ifdef HAVE_SYS_TIME_H
33
#ifdef HAVE_SYS_SELECT_H
34
#include <sys/select.h>
49
#if defined(__OS2__) || defined(__MSDOS__)
51
#if defined(__BORLANDC__)
63
/* List structure for holding queued reminders */
64
typedef struct queuedrem {
65
struct queuedrem *next;
70
char sched[VAR_NAME_LEN+1];
75
/* Global variables */
77
static QueuedRem *QueueHead;
78
static time_t FileModTime;
79
static struct stat StatBuf;
81
PRIVATE void CheckInitialFile ARGS ((void));
82
PRIVATE int CalculateNextTime ARGS ((QueuedRem *q));
83
PRIVATE QueuedRem *FindNextReminder ARGS ((void));
84
PRIVATE int CalculateNextTimeUsingSched ARGS ((QueuedRem *q));
85
PRIVATE void DaemonWait ARGS ((unsigned int sleeptime));
86
PRIVATE void reread ARGS((void));
88
/***************************************************************/
92
/* Put the reminder on a queue for later, if queueing is */
95
/***************************************************************/
97
PUBLIC int QueueReminder(ParsePtr p, Trigger *trig,
98
TimeTrig *tim, const char *sched)
100
int QueueReminder(p, trig, tim, sched)
110
tim->ttime == NO_TIME ||
111
trig->typ == CAL_TYPE ||
112
tim->ttime < SystemTime(0) / 60 ||
113
((trig->typ == RUN_TYPE) && RunDisabled)) return OK;
115
qelem = NEW(QueuedRem);
119
qelem->text = StrDup(p->pos); /* Guaranteed that parser is not nested. */
125
qelem->typ = trig->typ;
127
qelem->next = QueueHead;
128
qelem->RunDisabled = RunDisabled;
130
strcpy(qelem->sched, sched);
131
strcpy(qelem->tag, trig->tag);
136
/***************************************************************/
138
/* HandleQueuedReminders */
140
/* Handle the issuing of queued reminders in the background */
142
/***************************************************************/
144
PUBLIC void HandleQueuedReminders(void)
146
void HandleQueuedReminders()
149
QueuedRem *q = QueueHead;
155
/* Suppress the BANNER from being issued */
158
/* Turn off sorting -- otherwise, TriggerReminder has no effect! */
161
/* If we are not connected to a tty, then we must close the
162
* standard file descriptors. This is to prevent someone
164
* remind file | <filter> | >log
165
* and have <filter> hung because the child (us) is still
166
* connected to it. This means the only commands that will be
167
* processed correctly are RUN commands, provided they mail
168
* the result back or use their own resource (as a window).
170
if (!DontFork && (!isatty(1) || !isatty(2))) {
175
/* If we're a daemon, get the mod time of initial file */
177
if (stat(InitialFile, &StatBuf)) {
178
fprintf(ErrFp, "Cannot stat %s - not running as daemon!\n",
181
} else FileModTime = StatBuf.st_mtime;
184
/* Initialize the queue - initialize all the entries time of issue */
187
q->tt.nexttime = (int) (SystemTime(0)/60 - 1);
188
q->tt.nexttime = CalculateNextTime(q);
193
signal(SIGINT, SigIntHandler);
195
if (!DontFork || Daemon) signal(SIGINT, SigIntHandler);
198
/* Sit in a loop, issuing reminders when necessary */
200
q = FindNextReminder();
202
/* If no more reminders to issue, we're done unless we're a daemon. */
203
if (!q && !Daemon) break;
207
/* Sleep until midnight */
208
TimeToSleep = (long) 1440*60L - SystemTime(0);
210
TimeToSleep = (long) 60*Daemon;
213
TimeToSleep = (long) q->tt.nexttime * 60L - SystemTime(0);
216
while (TimeToSleep > 0L) {
217
SleepTime = (unsigned) ((TimeToSleep > 30000L) ? 30000 : TimeToSleep);
219
if (Daemon > 0 && SleepTime > 60*Daemon) SleepTime = 60*Daemon;
224
DaemonWait(SleepTime);
227
if (Daemon> 0 && SleepTime) CheckInitialFile();
231
/* Sleep until midnight */
232
TimeToSleep = (long) 1440*60L - SystemTime(0);
234
TimeToSleep = (long) 60*Daemon;
237
TimeToSleep = (long) q->tt.nexttime * 60L - SystemTime(0);
242
/* Trigger the reminder */
243
CreateParser(q->text, &p);
245
RunDisabled = q->RunDisabled;
247
printf("NOTE reminder %s ",
248
SimpleTime(q->tt.ttime));
249
printf("%s ", SimpleTime(SystemTime(0)/60));
253
printf("%s", q->tag);
258
/* Set up global variables so some functions like trigdate()
259
and trigtime() work correctly */
260
LastTriggerDate = JulianToday;
261
LastTriggerTime = q->tt.ttime;
264
(void) TriggerReminder(&p, &trig, &q->tt, JulianToday, 1);
266
(void) TriggerReminder(&p, &trig, &q->tt, JulianToday);
269
printf("NOTE endreminder\n");
273
/* Calculate the next trigger time */
274
q->tt.nexttime = CalculateNextTime(q);
277
signal(SIGINT, SIG_DFL);
283
/***************************************************************/
285
/* CalculateNextTime */
287
/* Calculate the next time when a reminder should be issued. */
288
/* Return NO_TIME if reminder expired. */
289
/* Strategy is: If a sched() function is defined, call it. */
290
/* Otherwise, use AT time with delta and rep. If sched() */
291
/* fails, revert to AT with delta and rep. */
293
/***************************************************************/
295
PRIVATE int CalculateNextTime(QueuedRem *q)
297
static int CalculateNextTime(q)
301
int tim = q->tt.ttime;
303
int delta = q->tt.delta;
304
int curtime = q->tt.nexttime+1;
307
/* Increment number of times this one has been triggered */
310
r = CalculateNextTimeUsingSched(q);
311
if (r != NO_TIME) return r;
313
if (delta == NO_DELTA) {
322
if (rep == NO_REP) rep = delta;
323
if (tim < curtime) tim += ((curtime - tim) / rep) * rep;
324
if (tim < curtime) tim += rep;
325
if (tim > q->tt.ttime) tim = q->tt.ttime;
326
if (tim < curtime) return NO_TIME; else return tim;
329
/***************************************************************/
331
/* FindNextReminder */
333
/* Find the next reminder to trigger */
335
/***************************************************************/
337
PRIVATE QueuedRem *FindNextReminder(void)
339
static QueuedRem *FindNextReminder()
342
QueuedRem *q = QueueHead;
343
QueuedRem *ans = NULL;
346
if (q->tt.nexttime != NO_TIME) {
348
else if (q->tt.nexttime < ans->tt.nexttime) ans = q;
357
/***************************************************************/
361
/* Split out what's done on a SIGINT from the SIGINT Handler. */
362
/* This will be necessary for OS/2 multithreaded. */
364
/***************************************************************/
371
QueuedRem *q = QueueHead;
373
printf("Contents of AT queue:%s", NL);
376
if (q->tt.nexttime != NO_TIME) {
377
printf("Trigger: %02d%c%02d Activate: %02d%c%02d Rep: %d Delta: %d Sched: %s",
378
q->tt.ttime / 60, TIMESEP, q->tt.ttime % 60,
379
q->tt.nexttime / 60, TIMESEP, q->tt.nexttime % 60,
380
q->tt.rep, q->tt.delta, q->sched);
381
if (*q->sched) printf("(%d)", q->ntrig+1);
383
printf("Text: %s %s%s%s", ((q->typ == MSG_TYPE) ? "MSG" :
384
((q->typ == MSF_TYPE) ? "MSF" :"RUN")),
393
/***************************************************************/
395
/* CheckInitialFile */
397
/* If the initial file has been modified, then restart the */
400
/***************************************************************/
402
PRIVATE void CheckInitialFile(void)
404
static void CheckInitialFile()
407
/* If date has rolled around, or file has changed, spawn a new version. */
408
time_t tim = FileModTime;
411
if (stat(InitialFile, &StatBuf) == 0) tim = StatBuf.st_mtime;
412
if (tim != FileModTime ||
413
RealToday != SystemDate(&y, &m, &d)) {
418
/***************************************************************/
420
/* CalculateNextTimeUsingSched */
422
/* Call the scheduling function. */
424
/***************************************************************/
426
PRIVATE int CalculateNextTimeUsingSched(QueuedRem *q)
428
static int CalculateNextTimeUsingSched(q)
432
/* Use LineBuffer for temp. string storage. */
439
if (UserFuncExists(q->sched) != 1) {
444
RunDisabled = q->RunDisabled; /* Don't want weird scheduling functions
445
to be a security hole! */
447
char exprBuf[VAR_NAME_LEN+32];
448
sprintf(exprBuf, "%s(%d)", q->sched, q->ntrig);
450
r = EvalExpr(&s, &v);
455
if (v.type == TIM_TYPE) {
457
} else if (v.type == INT_TYPE) {
459
ThisTime = q->tt.nexttime + v.v.val;
461
ThisTime = q->tt.ttime + v.v.val;
468
if (ThisTime < 0) ThisTime = 0; /* Can't be less than 00:00 */
469
if (ThisTime > 1439) ThisTime = 1439; /* or greater than 11:59 */
470
if (ThisTime > q->tt.nexttime) return ThisTime;
471
if (ThisTime <= LastTime) {
480
/***************************************************************/
484
/* Sleep or read command from stdin in "daemon -1" mode */
486
/***************************************************************/
488
PRIVATE void DaemonWait(unsigned int sleeptime)
490
static DaemonWait(sleeptime)
491
unsigned int sleeptime;
495
struct timeval timeout;
502
timeout.tv_sec = sleeptime;
504
retval = select(1, &readSet, NULL, NULL, &timeout);
506
/* If date has rolled around, restart */
507
if (RealToday != SystemDate(&y, &m, &d)) {
508
printf("NOTE newdate\nNOTE reread\n");
513
/* If nothing readable or interrupted system call, return */
514
if (retval <= 0) return;
516
/* If stdin not readable, return */
517
if (!FD_ISSET(0, &readSet)) return;
519
/* If EOF on stdin, exit */
524
/* Read a line from stdin and interpret it */
525
if (!fgets(cmdLine, sizeof(cmdLine), stdin)) {
529
if (!strcmp(cmdLine, "EXIT\n")) {
531
} else if (!strcmp(cmdLine, "STATUS\n")) {
533
QueuedRem *q = QueueHead;
535
if (q->tt.nexttime != NO_TIME) {
540
printf("NOTE queued %d\n", nqueued);
542
} else if (!strcmp(cmdLine, "REREAD\n")) {
543
printf("NOTE reread\n");
547
printf("ERR Invalid daemon command: %s", cmdLine);
552
/***************************************************************/
556
/* Restarts Remind if date rolls over or REREAD cmd received */
558
/***************************************************************/
560
PRIVATE void reread(void)
565
execvp(ArgV[0], ArgV);
568
#endif /* HAVE_QUEUED from way at the top */