Commit | Line | Data |
---|---|---|
fbfed6f0 JB |
1 | /* timer.c --- daemon to provide a tagged interval timer service |
2 | ||
f8998fa9 RS |
3 | This little daemon runs forever waiting for commands to schedule events. |
4 | SIGALRM causes | |
fbfed6f0 JB |
5 | it to check its queue for events attached to the current second; if |
6 | one is found, its label is written to stdout. SIGTERM causes it to | |
7 | terminate, printing a list of pending events. | |
8 | ||
9 | This program is intended to be used with the lisp package called | |
f8998fa9 RS |
10 | timer.el. The first such program was written anonymously in 1990. |
11 | This version was documented and rewritten for portability by | |
12 | esr@snark.thyrsus.com, Aug 7 1992. */ | |
fbfed6f0 | 13 | |
5cc564a6 | 14 | #include <stdio.h> |
15 | #include <signal.h> | |
f8998fa9 | 16 | #include <errno.h> |
5cc564a6 | 17 | #include <sys/types.h> /* time_t */ |
18 | ||
18160b98 | 19 | #include <../src/config.h> |
f8998fa9 | 20 | #undef read |
9e2b097b | 21 | |
2c029353 RS |
22 | #ifdef LINUX |
23 | /* Perhaps this is correct unconditionally. */ | |
24 | #undef signal | |
25 | #endif | |
f8998fa9 RS |
26 | #ifdef _CX_UX |
27 | /* I agree with the comment above, this probably should be unconditional (it | |
28 | * is already unconditional in a couple of other files in this directory), | |
29 | * but in the spirit of minimizing the effects of my port, I am making it | |
30 | * conditional on _CX_UX. | |
31 | */ | |
32 | #undef signal | |
33 | #endif | |
2c029353 RS |
34 | |
35 | ||
5cc564a6 | 36 | extern int errno; |
dbc52c10 | 37 | extern char *strerror (), *malloc (); |
fbfed6f0 | 38 | extern time_t time (); |
5cc564a6 | 39 | |
9e2b097b | 40 | /* |
f8998fa9 RS |
41 | * The field separator for input. This character shouldn't occur in dates, |
42 | * and should be printable so event strings are readable by people. | |
9e2b097b JB |
43 | */ |
44 | #define FS '@' | |
45 | ||
46 | struct event | |
fbfed6f0 | 47 | { |
9e2b097b JB |
48 | char *token; |
49 | time_t reply_at; | |
fbfed6f0 JB |
50 | }; |
51 | int events_size; /* How many slots have we allocated? */ | |
52 | int num_events; /* How many are actually scheduled? */ | |
53 | struct event *events; /* events[0 .. num_events-1] are the | |
54 | valid events. */ | |
5cc564a6 | 55 | |
f8998fa9 RS |
56 | char *pname; /* program name for error messages */ |
57 | ||
58 | /* This buffer is used for reading commands. | |
59 | We make it longer when necessary, but we never free it. */ | |
60 | char *buf; | |
61 | /* This is the allocated size of buf. */ | |
62 | int buf_size; | |
63 | ||
64 | /* Non-zero means don't handle an alarm now; | |
65 | instead, just set alarm_deferred if an alarm happens. | |
66 | We set this around parts of the program that call malloc and free. */ | |
67 | int defer_alarms; | |
5cc564a6 | 68 | |
f8998fa9 RS |
69 | /* Non-zero if an alarm came in during the reading of a command. */ |
70 | int alarm_deferred; | |
71 | \f | |
72 | /* Schedule one event, and arrange an alarm for it. | |
73 | STR is a string of two fields separated by FS. | |
ad2e78fb | 74 | First field is string for get_date, saying when to wake-up. |
fbfed6f0 | 75 | Second field is a token to identify the request. */ |
f8998fa9 | 76 | |
fbfed6f0 JB |
77 | void |
78 | schedule (str) | |
79 | char *str; | |
5cc564a6 | 80 | { |
ad2e78fb | 81 | extern time_t get_date (); |
fbfed6f0 JB |
82 | extern char *strcpy (); |
83 | time_t now; | |
84 | register char *p; | |
85 | static struct event *ep; | |
f8998fa9 | 86 | |
fbfed6f0 JB |
87 | /* check entry format */ |
88 | for (p = str; *p && *p != FS; p++) | |
89 | continue; | |
90 | if (!*p) | |
9e2b097b | 91 | { |
c193197b | 92 | fprintf (stderr, "%s: bad input format: %s\n", pname, str); |
fbfed6f0 | 93 | return; |
9e2b097b | 94 | } |
fbfed6f0 | 95 | *p++ = 0; |
5cc564a6 | 96 | |
fbfed6f0 JB |
97 | /* allocate an event slot */ |
98 | ep = events + num_events; | |
99 | ||
100 | /* If the event array is full, stretch it. After stretching, we know | |
101 | that ep will be pointing to an available event spot. */ | |
102 | if (ep == events + events_size) | |
9e2b097b | 103 | { |
fbfed6f0 | 104 | int old_size = events_size; |
9e2b097b | 105 | |
fbfed6f0 JB |
106 | events_size *= 2; |
107 | events = ((struct event *) | |
108 | realloc (events, events_size * sizeof (struct event))); | |
109 | if (! events) | |
110 | { | |
111 | fprintf (stderr, "%s: virtual memory exhausted.\n", pname); | |
f8998fa9 RS |
112 | /* Since there is so much virtual memory, and running out |
113 | almost surely means something is very very wrong, | |
114 | it is best to exit rather than continue. */ | |
115 | exit (1); | |
fbfed6f0 JB |
116 | } |
117 | ||
118 | while (old_size < events_size) | |
119 | events[old_size++].token = NULL; | |
120 | } | |
121 | ||
122 | /* Don't allow users to schedule events in past time. */ | |
123 | ep->reply_at = get_date (str, NULL); | |
124 | if (ep->reply_at - time (&now) < 0) | |
125 | { | |
c193197b | 126 | fprintf (stderr, "%s: bad time spec: %s%c%s\n", pname, str, FS, p); |
fbfed6f0 JB |
127 | return; |
128 | } | |
129 | ||
130 | /* save the event description */ | |
131 | ep->token = (char *) malloc ((unsigned) strlen (p) + 1); | |
132 | if (! ep->token) | |
133 | { | |
c193197b | 134 | fprintf (stderr, "%s: malloc %s: %s%c%s\n", |
dbc52c10 | 135 | pname, strerror (errno), str, FS, p); |
fbfed6f0 | 136 | return; |
9e2b097b | 137 | } |
fbfed6f0 JB |
138 | |
139 | strcpy (ep->token, p); | |
140 | num_events++; | |
5cc564a6 | 141 | } |
f8998fa9 RS |
142 | \f |
143 | /* Print the notification for the alarmed event just arrived if any, | |
144 | and schedule an alarm for the next event if any. */ | |
5cc564a6 | 145 | |
146 | void | |
fbfed6f0 | 147 | notify () |
5cc564a6 | 148 | { |
ca5b61ea | 149 | time_t now, tdiff, waitfor = -1; |
fbfed6f0 JB |
150 | register struct event *ep; |
151 | ||
f8998fa9 RS |
152 | /* Inhibit interference with alarms while changing global vars. */ |
153 | defer_alarms = 1; | |
154 | alarm_deferred = 0; | |
ee6b9d19 | 155 | |
fbfed6f0 | 156 | now = time ((time_t *) NULL); |
9e2b097b | 157 | |
fbfed6f0 JB |
158 | for (ep = events; ep < events + num_events; ep++) |
159 | /* Are any events ready to fire? */ | |
160 | if (ep->reply_at <= now) | |
161 | { | |
162 | fputs (ep->token, stdout); | |
07941899 | 163 | putc ('\n', stdout); |
507876f3 | 164 | fflush (stdout); |
fbfed6f0 | 165 | free (ep->token); |
9e2b097b | 166 | |
fbfed6f0 JB |
167 | /* We now have a hole in the event array; fill it with the last |
168 | event. */ | |
ee6b9d19 ER |
169 | ep->token = events[num_events - 1].token; |
170 | ep->reply_at = events[num_events - 1].reply_at; | |
fbfed6f0 JB |
171 | num_events--; |
172 | ||
173 | /* We ought to scan this event again. */ | |
174 | ep--; | |
175 | } | |
176 | else | |
177 | { | |
178 | /* next timeout should be the soonest of any remaining */ | |
179 | if ((tdiff = ep->reply_at - now) < waitfor || waitfor < 0) | |
180 | waitfor = (long)tdiff; | |
181 | } | |
182 | ||
183 | /* If there are no more events, we needn't bother setting an alarm. */ | |
184 | if (num_events > 0) | |
185 | alarm (waitfor); | |
ee6b9d19 | 186 | |
f8998fa9 RS |
187 | /* Now check if there was another alarm |
188 | while we were handling an explicit request. */ | |
189 | defer_alarms = 0; | |
190 | if (alarm_deferred) | |
191 | notify (); | |
192 | alarm_deferred = 0; | |
fbfed6f0 | 193 | } |
f8998fa9 RS |
194 | \f |
195 | /* Read one command from command from standard input | |
196 | and schedule the event for it. */ | |
fbfed6f0 JB |
197 | |
198 | void | |
199 | getevent () | |
200 | { | |
201 | int i; | |
f8998fa9 | 202 | int n_events; |
fbfed6f0 JB |
203 | |
204 | /* In principle the itimer should be disabled on entry to this | |
205 | function, but it really doesn't make any important difference | |
206 | if it isn't. */ | |
207 | ||
f8998fa9 RS |
208 | if (buf == 0) |
209 | { | |
210 | buf_size = 80; | |
211 | buf = (char *) malloc (buf_size); | |
212 | } | |
fbfed6f0 JB |
213 | |
214 | /* Read a line from standard input, expanding buf if it is too short | |
215 | to hold the line. */ | |
216 | for (i = 0; ; i++) | |
217 | { | |
f8998fa9 RS |
218 | char c; |
219 | int nread; | |
fbfed6f0 JB |
220 | |
221 | if (i >= buf_size) | |
9e2b097b | 222 | { |
fbfed6f0 | 223 | buf_size *= 2; |
f8998fa9 RS |
224 | alarm_deferred = 0; |
225 | defer_alarms = 1; | |
fbfed6f0 | 226 | buf = (char *) realloc (buf, buf_size); |
f8998fa9 RS |
227 | defer_alarms = 0; |
228 | if (alarm_deferred) | |
229 | notify (); | |
230 | alarm_deferred = 0; | |
231 | } | |
fbfed6f0 | 232 | |
f8998fa9 RS |
233 | /* Read one character into c. */ |
234 | while (1) | |
235 | { | |
236 | nread = read (fileno (stdin), &c, 1); | |
237 | ||
238 | /* Retry after transient error. */ | |
239 | if (nread < 0 | |
240 | && (1 | |
241 | #ifdef EINTR | |
242 | || errno == EINTR | |
243 | #endif | |
244 | #ifdef EAGAIN | |
245 | || errno == EAGAIN | |
246 | #endif | |
247 | )) | |
248 | continue; | |
249 | ||
250 | /* Report serious errors. */ | |
251 | if (nread < 0) | |
9e2b097b | 252 | { |
f8998fa9 RS |
253 | perror ("read"); |
254 | exit (1); | |
9e2b097b | 255 | } |
9e2b097b | 256 | |
f8998fa9 RS |
257 | /* On eof, exit. */ |
258 | if (nread == 0) | |
259 | exit (0); | |
5cc564a6 | 260 | |
f8998fa9 RS |
261 | break; |
262 | } | |
9e2b097b | 263 | |
fbfed6f0 JB |
264 | if (c == '\n') |
265 | { | |
266 | buf[i] = '\0'; | |
267 | break; | |
268 | } | |
9e2b097b | 269 | |
fbfed6f0 JB |
270 | buf[i] = c; |
271 | } | |
9e2b097b | 272 | |
fbfed6f0 | 273 | /* Register the event. */ |
f8998fa9 RS |
274 | alarm_deferred = 0; |
275 | defer_alarms = 1; | |
fbfed6f0 | 276 | schedule (buf); |
f8998fa9 | 277 | defer_alarms = 0; |
fbfed6f0 | 278 | notify (); |
f8998fa9 | 279 | alarm_deferred = 0; |
9e2b097b JB |
280 | } |
281 | ||
f8998fa9 RS |
282 | /* Handle incoming signal SIG. */ |
283 | ||
b1839491 | 284 | SIGTYPE |
fbfed6f0 JB |
285 | sigcatch (sig) |
286 | int sig; | |
5cc564a6 | 287 | { |
fbfed6f0 | 288 | struct event *ep; |
9e2b097b | 289 | |
f8998fa9 RS |
290 | /* required on older UNIXes; harmless on newer ones */ |
291 | signal (sig, sigcatch); | |
292 | ||
fbfed6f0 | 293 | switch (sig) |
9e2b097b JB |
294 | { |
295 | case SIGALRM: | |
f8998fa9 RS |
296 | if (defer_alarms) |
297 | alarm_deferred = 1; | |
298 | else | |
299 | notify (); | |
fbfed6f0 | 300 | break; |
9e2b097b | 301 | case SIGTERM: |
fbfed6f0 JB |
302 | fprintf (stderr, "Events still queued:\n"); |
303 | for (ep = events; ep < events + num_events; ep++) | |
c193197b | 304 | fprintf (stderr, "%d = %ld @ %s\n", |
fbfed6f0 JB |
305 | ep - events, ep->reply_at, ep->token); |
306 | exit (0); | |
307 | break; | |
9e2b097b | 308 | } |
5cc564a6 | 309 | } |
9e2b097b | 310 | |
5cc564a6 | 311 | /*ARGSUSED*/ |
312 | int | |
fbfed6f0 | 313 | main (argc, argv) |
5cc564a6 | 314 | int argc; |
315 | char **argv; | |
5cc564a6 | 316 | { |
fbfed6f0 JB |
317 | for (pname = argv[0] + strlen (argv[0]); |
318 | *pname != '/' && pname != argv[0]; | |
5cc564a6 | 319 | pname--); |
fbfed6f0 JB |
320 | if (*pname == '/') |
321 | pname++; | |
322 | ||
323 | events_size = 16; | |
324 | events = ((struct event *) malloc (events_size * sizeof (*events))); | |
325 | num_events = 0; | |
5cc564a6 | 326 | |
fbfed6f0 JB |
327 | signal (SIGALRM, sigcatch); |
328 | signal (SIGTERM, sigcatch); | |
9e2b097b | 329 | |
f8998fa9 RS |
330 | /* Loop reading commands from standard input |
331 | and scheduling alarms accordingly. | |
332 | The alarms are handled asynchronously, while we wait for commands. */ | |
333 | while (1) | |
334 | getevent (); | |
5cc564a6 | 335 | } |
27711600 RM |
336 | \f |
337 | #ifndef HAVE_STRERROR | |
338 | char * | |
339 | strerror (errnum) | |
340 | int errnum; | |
341 | { | |
342 | extern char *sys_errlist[]; | |
343 | extern int sys_nerr; | |
344 | ||
345 | if (errnum >= 0 && errnum < sys_nerr) | |
346 | return sys_errlist[errnum]; | |
347 | return (char *) "Unknown error"; | |
348 | } | |
349 | ||
350 | #endif /* ! HAVE_STRERROR */ | |
9e2b097b JB |
351 | |
352 | /* timer.c ends here */ |