| 1 | /* timer.c --- daemon to provide a tagged interval timer service |
| 2 | |
| 3 | This little daemon runs forever waiting for signals. SIGIO (or |
| 4 | SIGUSR1) causes it to read an event spec from stdin; that is, a |
| 5 | date followed by colon followed by an event label. SIGALRM causes |
| 6 | it to check its queue for events attached to the current second; if |
| 7 | one is found, its label is written to stdout. SIGTERM causes it to |
| 8 | terminate, printing a list of pending events. |
| 9 | |
| 10 | This program is intended to be used with the lisp package called |
| 11 | timer.el. It was written anonymously in 1990. This version was |
| 12 | documented and rewritten for portability by esr@snark.thyrsus.com, |
| 13 | Aug 7 1992. */ |
| 14 | |
| 15 | #include <stdio.h> |
| 16 | #include <signal.h> |
| 17 | #include <fcntl.h> /* FASYNC */ |
| 18 | #include <sys/types.h> /* time_t */ |
| 19 | |
| 20 | #include <../src/config.h> |
| 21 | #ifdef USG |
| 22 | #undef SIGIO |
| 23 | #define SIGIO SIGUSR1 |
| 24 | #endif |
| 25 | |
| 26 | #ifdef LINUX |
| 27 | /* Perhaps this is correct unconditionally. */ |
| 28 | #undef signal |
| 29 | #endif |
| 30 | |
| 31 | |
| 32 | extern int errno; |
| 33 | extern char *sys_errlist[], *malloc (); |
| 34 | extern time_t time (); |
| 35 | |
| 36 | /* |
| 37 | * The field separator for input. This character shouldn't be legal in a date, |
| 38 | * and should be printable so event strings are readable by people. Was |
| 39 | * originally ';', then got changed to bogus `\001'. |
| 40 | */ |
| 41 | #define FS '@' |
| 42 | |
| 43 | struct event |
| 44 | { |
| 45 | char *token; |
| 46 | time_t reply_at; |
| 47 | }; |
| 48 | int events_size; /* How many slots have we allocated? */ |
| 49 | int num_events; /* How many are actually scheduled? */ |
| 50 | struct event *events; /* events[0 .. num_events-1] are the |
| 51 | valid events. */ |
| 52 | |
| 53 | char *pname; /* programme name for error messages */ |
| 54 | |
| 55 | /* Accepts a string of two fields separated by FS. |
| 56 | First field is string for get_date, saying when to wake-up. |
| 57 | Second field is a token to identify the request. */ |
| 58 | void |
| 59 | schedule (str) |
| 60 | char *str; |
| 61 | { |
| 62 | extern time_t get_date (); |
| 63 | extern char *strcpy (); |
| 64 | time_t now; |
| 65 | register char *p; |
| 66 | static struct event *ep; |
| 67 | |
| 68 | /* check entry format */ |
| 69 | for (p = str; *p && *p != FS; p++) |
| 70 | continue; |
| 71 | if (!*p) |
| 72 | { |
| 73 | fprintf (stderr, "%s: bad input format: %s\n", pname, str); |
| 74 | return; |
| 75 | } |
| 76 | *p++ = 0; |
| 77 | |
| 78 | /* allocate an event slot */ |
| 79 | ep = events + num_events; |
| 80 | |
| 81 | /* If the event array is full, stretch it. After stretching, we know |
| 82 | that ep will be pointing to an available event spot. */ |
| 83 | if (ep == events + events_size) |
| 84 | { |
| 85 | int old_size = events_size; |
| 86 | |
| 87 | events_size *= 2; |
| 88 | events = ((struct event *) |
| 89 | realloc (events, events_size * sizeof (struct event))); |
| 90 | if (! events) |
| 91 | { |
| 92 | fprintf (stderr, "%s: virtual memory exhausted.\n", pname); |
| 93 | |
| 94 | /* Should timer exit now? Well, we've still got other |
| 95 | events in the queue, and more memory might become |
| 96 | available in the future, so we'll just toss this event. |
| 97 | This will screw up whoever scheduled the event, but |
| 98 | maybe someone else will survive. */ |
| 99 | return; |
| 100 | } |
| 101 | |
| 102 | while (old_size < events_size) |
| 103 | events[old_size++].token = NULL; |
| 104 | } |
| 105 | |
| 106 | /* Don't allow users to schedule events in past time. */ |
| 107 | ep->reply_at = get_date (str, NULL); |
| 108 | if (ep->reply_at - time (&now) < 0) |
| 109 | { |
| 110 | fprintf (stderr, "%s: bad time spec: %s%c%s\n", pname, str, FS, p); |
| 111 | return; |
| 112 | } |
| 113 | |
| 114 | /* save the event description */ |
| 115 | ep->token = (char *) malloc ((unsigned) strlen (p) + 1); |
| 116 | if (! ep->token) |
| 117 | { |
| 118 | fprintf (stderr, "%s: malloc %s: %s%c%s\n", |
| 119 | pname, sys_errlist[errno], str, FS, p); |
| 120 | return; |
| 121 | } |
| 122 | |
| 123 | strcpy (ep->token, p); |
| 124 | num_events++; |
| 125 | } |
| 126 | |
| 127 | void |
| 128 | notify () |
| 129 | { |
| 130 | time_t now, tdiff, waitfor = -1; |
| 131 | register struct event *ep; |
| 132 | |
| 133 | /* If an alarm timer runs out while this function is executing, |
| 134 | it could get called recursively. This would be bad, because |
| 135 | it's not re-entrant. So we must try to suspend the signal. */ |
| 136 | #if 0 /* This function isn't right for BSD. Fix it later. */ |
| 137 | sighold(SIGIO); |
| 138 | #endif |
| 139 | |
| 140 | now = time ((time_t *) NULL); |
| 141 | |
| 142 | for (ep = events; ep < events + num_events; ep++) |
| 143 | /* Are any events ready to fire? */ |
| 144 | if (ep->reply_at <= now) |
| 145 | { |
| 146 | fputs (ep->token, stdout); |
| 147 | putc ('\n', stdout); |
| 148 | fflush (stdout); |
| 149 | free (ep->token); |
| 150 | |
| 151 | /* We now have a hole in the event array; fill it with the last |
| 152 | event. */ |
| 153 | ep->token = events[num_events - 1].token; |
| 154 | ep->reply_at = events[num_events - 1].reply_at; |
| 155 | num_events--; |
| 156 | |
| 157 | /* We ought to scan this event again. */ |
| 158 | ep--; |
| 159 | } |
| 160 | else |
| 161 | { |
| 162 | /* next timeout should be the soonest of any remaining */ |
| 163 | if ((tdiff = ep->reply_at - now) < waitfor || waitfor < 0) |
| 164 | waitfor = (long)tdiff; |
| 165 | } |
| 166 | |
| 167 | /* If there are no more events, we needn't bother setting an alarm. */ |
| 168 | if (num_events > 0) |
| 169 | alarm (waitfor); |
| 170 | |
| 171 | #if 0 /* This function isn't right for BSD. */ |
| 172 | sigrelse(SIGIO); |
| 173 | #endif |
| 174 | } |
| 175 | |
| 176 | void |
| 177 | getevent () |
| 178 | { |
| 179 | int i; |
| 180 | char *buf; |
| 181 | int buf_size; |
| 182 | |
| 183 | /* In principle the itimer should be disabled on entry to this |
| 184 | function, but it really doesn't make any important difference |
| 185 | if it isn't. */ |
| 186 | |
| 187 | buf_size = 80; |
| 188 | buf = (char *) malloc (buf_size); |
| 189 | |
| 190 | /* Read a line from standard input, expanding buf if it is too short |
| 191 | to hold the line. */ |
| 192 | for (i = 0; ; i++) |
| 193 | { |
| 194 | int c; |
| 195 | |
| 196 | if (i >= buf_size) |
| 197 | { |
| 198 | buf_size *= 2; |
| 199 | buf = (char *) realloc (buf, buf_size); |
| 200 | |
| 201 | /* If we're out of memory, toss this event. */ |
| 202 | do |
| 203 | { |
| 204 | c = getchar (); |
| 205 | } |
| 206 | while (c != '\n' && c != EOF); |
| 207 | |
| 208 | return; |
| 209 | } |
| 210 | |
| 211 | c = getchar (); |
| 212 | |
| 213 | if (c == EOF) |
| 214 | exit (0); |
| 215 | |
| 216 | if (c == '\n') |
| 217 | { |
| 218 | buf[i] = '\0'; |
| 219 | break; |
| 220 | } |
| 221 | |
| 222 | buf[i] = c; |
| 223 | } |
| 224 | |
| 225 | /* Register the event. */ |
| 226 | schedule (buf); |
| 227 | free (buf); |
| 228 | |
| 229 | /* Who knows what this interrupted, or if it said "now"? */ |
| 230 | notify (); |
| 231 | } |
| 232 | |
| 233 | SIGTYPE |
| 234 | sigcatch (sig) |
| 235 | int sig; |
| 236 | /* dispatch on incoming signal, then restore it */ |
| 237 | { |
| 238 | struct event *ep; |
| 239 | |
| 240 | switch (sig) |
| 241 | { |
| 242 | case SIGALRM: |
| 243 | notify (); |
| 244 | break; |
| 245 | case SIGIO: |
| 246 | getevent (); |
| 247 | break; |
| 248 | case SIGTERM: |
| 249 | fprintf (stderr, "Events still queued:\n"); |
| 250 | for (ep = events; ep < events + num_events; ep++) |
| 251 | fprintf (stderr, "%d = %ld @ %s\n", |
| 252 | ep - events, ep->reply_at, ep->token); |
| 253 | exit (0); |
| 254 | break; |
| 255 | } |
| 256 | |
| 257 | /* required on older UNIXes; harmless on newer ones */ |
| 258 | signal (sig, sigcatch); |
| 259 | } |
| 260 | |
| 261 | /*ARGSUSED*/ |
| 262 | int |
| 263 | main (argc, argv) |
| 264 | int argc; |
| 265 | char **argv; |
| 266 | { |
| 267 | for (pname = argv[0] + strlen (argv[0]); |
| 268 | *pname != '/' && pname != argv[0]; |
| 269 | pname--); |
| 270 | if (*pname == '/') |
| 271 | pname++; |
| 272 | |
| 273 | events_size = 16; |
| 274 | events = ((struct event *) malloc (events_size * sizeof (*events))); |
| 275 | num_events = 0; |
| 276 | |
| 277 | signal (SIGIO, sigcatch); |
| 278 | signal (SIGALRM, sigcatch); |
| 279 | signal (SIGTERM, sigcatch); |
| 280 | |
| 281 | #ifndef USG |
| 282 | if (fcntl (0, F_SETOWN, getpid ()) == -1) |
| 283 | { |
| 284 | fprintf (stderr, "%s: can't set ownership of stdin\n", pname); |
| 285 | fprintf (stderr, "%s\n", sys_errlist[errno]); |
| 286 | exit (1); |
| 287 | } |
| 288 | if (fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) | FASYNC) == -1) |
| 289 | { |
| 290 | fprintf (stderr, "%s: can't request asynchronous I/O on stdin\n", pname); |
| 291 | fprintf (stderr, "%s\n", sys_errlist[errno]); |
| 292 | exit (1); |
| 293 | } |
| 294 | #endif /* USG */ |
| 295 | |
| 296 | /* In case Emacs sent some input before we set up |
| 297 | the handling of SIGIO, read it now. */ |
| 298 | kill (0, SIGIO); |
| 299 | |
| 300 | for (;;) |
| 301 | pause (); |
| 302 | } |
| 303 | |
| 304 | /* timer.c ends here */ |