diff lib-src/=timer.c @ 958:cc82116a8f1c

*** empty log message ***
author Jim Blandy <jimb@redhat.com>
date Wed, 12 Aug 1992 12:57:12 +0000
parents eca8812e61cd
children 61c6983219ff
line wrap: on
line diff
--- a/lib-src/=timer.c	Wed Aug 12 12:50:10 1992 +0000
+++ b/lib-src/=timer.c	Wed Aug 12 12:57:12 1992 +0000
@@ -1,155 +1,221 @@
+/*
+ * timer.c --- daemon to provide a tagged interval timer service
+ *
+ * This little daemon runs forever waiting for signals.  SIGIO (or SIGUSR1)
+ * causes it to read an event spec from stdin; that is, a date followed by
+ * colon followed by an event label.  SIGALRM causes it to check its queue
+ * for events attached to the current second; if one is found, its label
+ * is written to stdout.  SIGTERM causes it to terminate, printing a list
+ * of pending events.
+ *
+ * This program is intended to be used with the lisp package called timer.el.
+ * It was written anonymously in 1990.  This version was documented and
+ * rewritten for portability by esr@snark,thyrsus.com, Aug 7 1992. 
+ */
 #include <stdio.h>
 #include <signal.h>
 #include <fcntl.h>      /* FASYNC */
-#ifdef USG              /* FASYNC for SysV */
-#include <sys/file.h>
+#include <sys/types.h>  /* time_t */
+
+#include "../src/config.h"
+#ifdef USG
+#undef SIGIO
+#define SIGIO	SIGUSR1
 #endif
-#include <sys/time.h>   /* itimer */
-#include <sys/types.h>  /* time_t */
 
 extern int errno;
 extern char *sys_errlist[], *malloc();
 extern time_t time();
 
 #define MAXEVENTS 256
-#define FS 1           /* field seperator for input */
+
+/*
+ * The field separator for input.  This character shouldn't be legal in a date,
+ * and should be printable so event strings are readable by people.  Was
+ * originally ';', then got changed to bogus `\001'.
+ */
+#define FS '@'
 
-struct event {
-  char *token;
-  time_t reply_at;
-} *events[MAXEVENTS];
+struct event
+{
+    char *token;
+    time_t reply_at;
+}
+events[MAXEVENTS];
 
-int slot;         /* The next open place in the events array */
-int mevent = 0;   /* 1+ the highest event number */
 char *pname;      /* programme name for error messages */
 
-/* Accepts a string of two fields seperated by a ';'
+/* Accepts a string of two fields seperated by FS.
  * First field is string for getdate, saying when to wake-up.
  * Second field is a token to identify the request.
  */
-struct event *
-schedule(str)
-     char *str;
-
+void schedule(str)
+	char *str;
 {
-  extern time_t getdate();
-  extern char *strcpy();
-  time_t now;
-  register char *p;
-  static struct event e;
+    extern time_t getdate();
+    extern char *strcpy();
+    time_t now;
+    register char *p;
+    static struct event *ep;
+
+#ifdef DEBUG
+    (void) fprintf(stderr, "Timer sees: %s", str);
+#endif /* DEBUG */
 
-  for(p = str; *p && *p != FS; p++);
-  if (!*p) {
-    (void)fprintf(stderr, "%s: bad input format: %s", pname, str);
-    return((struct event *)NULL);
-  }
-  *p++ = 0;
+    /* check entry format */
+    for(p = str; *p && *p != FS; p++)
+	continue;
+    if (!*p)
+    {
+	(void)fprintf(stderr, "%s: bad input format: %s", pname, str);
+	return;
+    }
+    *p++ = 0;
   
-  if ((e.reply_at = get_date(str, NULL)) - time(&now) < 0) {
-    (void)fprintf(stderr, "%s: bad time spec: %s%c%s", pname, str, FS, p);
-    return((struct event *)NULL);
-  }
+    /* allocate an event slot */
+    for(ep = events; ep < events + MAXEVENTS; ep++)
+	if (ep->token == (char *)NULL)
+	    break;
+    if (ep == events + MAXEVENTS)
+	(void) fprintf(stderr, "%s: too many events: %s", pname, str);
+
+    /* don't allow users to schedule events in past time */
+    else if ((ep->reply_at = get_date(str, NULL)) - time(&now) < 0)
+	(void)fprintf(stderr, "%s: bad time spec: %s%c%s", pname, str, FS, p);
 
-  if ((e.token = malloc((unsigned)strlen(p) + 1)) == NULL) {
-    (void)fprintf(stderr, "%s: malloc %s: %s%c%s",
-                  pname, sys_errlist[errno], str, FS, p);
-    return((struct event *)NULL);
-  }
-  (void)strcpy(e.token,p);
+    /* save the event description */
+    else if ((ep->token = malloc((unsigned)strlen(p) + 1)) == NULL)
+	(void)fprintf(stderr, "%s: malloc %s: %s%c%s",
+		      pname, sys_errlist[errno], str, FS, p);
+    else
+    {
+	(void)strcpy(ep->token, p);
 
-  return(&e);
+#ifdef DEBUG
+	(void) fprintf(stderr,
+		       "New event: %ld: %s", ep->reply_at, ep->token);
+#endif /* DEBUG */
+    }
 }
 
 void
 notify()
+{
+    time_t now, tdiff, waitfor = -1;
+    register struct event *ep;
 
-{
-  time_t now, tdiff;
-  register int i, newmax = 0;
-  /* I prefer using the interval timer rather than alarm(); the latter
-     could be substituted if portability requires it. */
-  struct itimerval itimer;
-
-  now = time((time_t *)NULL);
-  slot = mevent;
-  itimer.it_interval.tv_sec = itimer.it_interval.tv_usec = 0;
-  itimer.it_value.tv_usec = 0;
-  itimer.it_value.tv_sec = -1;
+    now = time((time_t *)NULL);
 
-  for(i=0; i < mevent; i++) {
-    while (events[i] && events[i]->reply_at <= now) {
-      (void)fputs(events[i]->token, stdout);
-      free(events[i]->token);
-      free((char *)events[i]);
-      events[i] = 0;
-    }
+    for(ep = events; ep < events + MAXEVENTS; ep++)
+	if (ep->token)
+	{
+	    /* any events ready to fire? */
+	    if (ep->reply_at <= now)
+	    {
+#ifdef DEBUG
+		(void) fprintf(stderr,
+			       "Event %d firing: %ld @ %s",
+			       (ep - events), ep->reply_at, ep->token);
+#endif /* DEBUG */
+		(void)fputs(ep->token, stdout);
+		free(ep->token);
+		ep->token = (char *)NULL;
+	    }
+	    else
+	    {
+#ifdef DEBUG
+		(void) fprintf(stderr,
+			   "Event %d still waiting: %ld @ %s",
+			   (ep - events), ep->reply_at, ep->token);
+#endif /* DEBUG */
 
-    if (events[i]) {
-      newmax = i+1;
-      if ((tdiff = events[i]->reply_at - now) < (time_t)itimer.it_value.tv_sec
-          || itimer.it_value.tv_sec < 0)
-        /* next timeout */
-        itimer.it_value.tv_sec = (long)tdiff;
-    } else {
-      /* Keep slot as the lowest unused events element */
-      if (i < slot) slot = i;
+		/* next timeout should be the soonest of any remaining */
+		if ((tdiff = ep->reply_at - now) < waitfor || waitfor < 0)
+		    waitfor = (long)tdiff;
+	    }
+	}
+
+    /* If there's no more events, SIGIO should be next wake-up */
+    if (waitfor != -1)
+    {
+#ifdef DEBUG
+	(void) fprintf(stderr,
+		       "Setting %d-second alarm\n", waitfor);
+#endif /* DEBUG */
+	(void)alarm(waitfor);
     }
-  }
-  /* if the array is full to mevent, slot should be the next available spot */
-  if (slot > (mevent = newmax)) slot = mevent;
-  /* If there's no more events, SIGIO should be next wake-up */
-  if (mevent) (void)setitimer(ITIMER_REAL, &itimer, (struct itimerval *)NULL);
 }
 
 void
 getevent()
+{
+    extern char *fgets();
+    struct event *ep;
+    char buf[BUFSIZ];
 
-{
-  extern char *fgets();
-  struct event *ep;
-  char buf[256];
+    /* in principle the itimer should be disabled on entry to this function,
+       but it really doesn't make any important difference if it isn't */
+
+    if (fgets(buf, sizeof(buf), stdin) == NULL)
+	exit(0);
 
-  /* in principle the itimer should be disabled on entry to this function,
-     but it really doesn't make any important difference if it isn't */
+    /* register the event */
+    schedule(buf);
 
-  if (fgets(buf, sizeof(buf), stdin) == NULL) exit(0);
+    /* Who knows what this interrupted, or if it said "now"? */
+    notify();
+}
 
-  if (slot == MAXEVENTS)
-    (void)fprintf(stderr, "%s: too many events: %s", pname, buf);
+void
+sigcatch(sig)
+/* dispatch on incoming signal, then restore it */
+{
+    struct event *ep;
 
-  else {
-    if ((events[slot] = (struct event *)malloc((sizeof(struct event))))
-        == NULL)
-      (void)fprintf(stderr,"%s: malloc %s: %s", pname, sys_errlist[errno],buf);
-
-    else {
-      if ((ep = schedule(buf)) == NULL)
-        free((char *)events[slot]), events[slot] = 0;
+    switch(sig)
+    {
+    case SIGALRM:
+#ifdef DEBUG
+	(void) fprintf(stderr, "Alarm signal received\n");
+#endif /* DEBUG */
+	notify();
+	break;
+    case SIGIO:
+	getevent();
+	break;
+    case SIGTERM:
+	(void) fprintf(stderr, "Events still queued:\n");
+	for (ep = events; ep < events + MAXEVENTS; ep++)
+	    if (ep->token)
+		(void) fprintf(stderr, "%d = %ld @ %s",
+			       ep - events, ep->reply_at, ep->token);
+	exit(0);
+	break;
+    }
 
-      else {
-        memcpy((char *)events[slot],(char *)ep,sizeof(struct event));
-        if (slot == mevent) mevent++;
-      } /* schedule */
-    } /* malloc */
-  } /* limit events */
-  /* timing, timing.  Who knows what this interrupted, or if it said "now"? */
-  notify();
+    /* required on older UNIXes; harmless on newer ones */
+    (void) signal(sig, sigcatch);
 }
- 
+
 /*ARGSUSED*/
 int
 main(argc, argv)
      int argc;
      char **argv;
-
 {
   for (pname = argv[0] + strlen(argv[0]); *pname != '/' && pname != argv[0];
        pname--);
   if (*pname == '/') pname++;
 
-  (void)signal(SIGIO, getevent);
-  (void)signal(SIGALRM, notify);
+  (void)signal(SIGIO, sigcatch);
+  (void)signal(SIGALRM, sigcatch);
+  (void)signal(SIGTERM, sigcatch);
+
+#ifndef USG
   (void)fcntl(0, F_SETFL, FASYNC);
+#endif /* USG */
 
   while (1) pause();
 }
+
+/* timer.c ends here */