(x-invocation-args): Add defvar.
[emacs.git] / lib-src / timer.c
blob7a4ee3c571b247bc67528382760819e7abaeacc9
1 /* timer.c --- daemon to provide a tagged interval timer service
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.
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. */
15 #include <stdio.h>
16 #include <signal.h>
17 #include <fcntl.h> /* FASYNC */
18 #include <sys/types.h> /* time_t */
20 #include "../src/config.h"
21 #ifdef USG
22 #undef SIGIO
23 #define SIGIO SIGUSR1
24 #endif
26 extern int errno;
27 extern char *sys_errlist[], *malloc ();
28 extern time_t time ();
31 * The field separator for input. This character shouldn't be legal in a date,
32 * and should be printable so event strings are readable by people. Was
33 * originally ';', then got changed to bogus `\001'.
35 #define FS '@'
37 struct event
39 char *token;
40 time_t reply_at;
42 int events_size; /* How many slots have we allocated? */
43 int num_events; /* How many are actually scheduled? */
44 struct event *events; /* events[0 .. num_events-1] are the
45 valid events. */
47 char *pname; /* programme name for error messages */
49 /* Accepts a string of two fields seperated by FS.
50 First field is string for get_date, saying when to wake-up.
51 Second field is a token to identify the request. */
52 void
53 schedule (str)
54 char *str;
56 extern time_t get_date ();
57 extern char *strcpy ();
58 time_t now;
59 register char *p;
60 static struct event *ep;
62 /* check entry format */
63 for (p = str; *p && *p != FS; p++)
64 continue;
65 if (!*p)
67 fprintf (stderr, "%s: bad input format: %s\n", pname, str);
68 return;
70 *p++ = 0;
72 /* allocate an event slot */
73 ep = events + num_events;
75 /* If the event array is full, stretch it. After stretching, we know
76 that ep will be pointing to an available event spot. */
77 if (ep == events + events_size)
79 int old_size = events_size;
81 events_size *= 2;
82 events = ((struct event *)
83 realloc (events, events_size * sizeof (struct event)));
84 if (! events)
86 fprintf (stderr, "%s: virtual memory exhausted.\n", pname);
88 /* Should timer exit now? Well, we've still got other
89 events in the queue, and more memory might become
90 available in the future, so we'll just toss this event.
91 This will screw up whoever scheduled the event, but
92 maybe someone else will survive. */
93 return;
96 while (old_size < events_size)
97 events[old_size++].token = NULL;
100 /* Don't allow users to schedule events in past time. */
101 ep->reply_at = get_date (str, NULL);
102 if (ep->reply_at - time (&now) < 0)
104 fprintf (stderr, "%s: bad time spec: %s%c%s\n", pname, str, FS, p);
105 return;
108 /* save the event description */
109 ep->token = (char *) malloc ((unsigned) strlen (p) + 1);
110 if (! ep->token)
112 fprintf (stderr, "%s: malloc %s: %s%c%s\n",
113 pname, sys_errlist[errno], str, FS, p);
114 return;
117 strcpy (ep->token, p);
118 num_events++;
121 void
122 notify ()
124 time_t now, tdiff, waitfor = -1;
125 register struct event *ep;
127 /* If an alarm timer runs out while this function is executing,
128 it could get called recursively. This would be bad, because
129 it's not re-entrant. So we must try to suspend the signal. */
130 #if 0 /* This function isn't right for BSD. Fix it later. */
131 sighold(SIGIO);
132 #endif
134 now = time ((time_t *) NULL);
136 for (ep = events; ep < events + num_events; ep++)
137 /* Are any events ready to fire? */
138 if (ep->reply_at <= now)
140 fputs (ep->token, stdout);
141 putc ('\n', stdout);
142 fflush (stdout);
143 free (ep->token);
145 /* We now have a hole in the event array; fill it with the last
146 event. */
147 ep->token = events[num_events - 1].token;
148 ep->reply_at = events[num_events - 1].reply_at;
149 num_events--;
151 /* We ought to scan this event again. */
152 ep--;
154 else
156 /* next timeout should be the soonest of any remaining */
157 if ((tdiff = ep->reply_at - now) < waitfor || waitfor < 0)
158 waitfor = (long)tdiff;
161 /* If there are no more events, we needn't bother setting an alarm. */
162 if (num_events > 0)
163 alarm (waitfor);
165 #if 0 /* This function isn't right for BSD. */
166 sigrelse(SIGIO);
167 #endif
170 void
171 getevent ()
173 int i;
174 char *buf;
175 int buf_size;
177 /* In principle the itimer should be disabled on entry to this
178 function, but it really doesn't make any important difference
179 if it isn't. */
181 buf_size = 80;
182 buf = (char *) malloc (buf_size);
184 /* Read a line from standard input, expanding buf if it is too short
185 to hold the line. */
186 for (i = 0; ; i++)
188 int c;
190 if (i >= buf_size)
192 buf_size *= 2;
193 buf = (char *) realloc (buf, buf_size);
195 /* If we're out of memory, toss this event. */
198 c = getchar ();
200 while (c != '\n' && c != EOF);
202 return;
205 c = getchar ();
207 if (c == EOF)
208 exit (0);
210 if (c == '\n')
212 buf[i] = '\0';
213 break;
216 buf[i] = c;
219 /* Register the event. */
220 schedule (buf);
221 free (buf);
223 /* Who knows what this interrupted, or if it said "now"? */
224 notify ();
227 SIGTYPE
228 sigcatch (sig)
229 int sig;
230 /* dispatch on incoming signal, then restore it */
232 struct event *ep;
234 switch (sig)
236 case SIGALRM:
237 notify ();
238 break;
239 case SIGIO:
240 getevent ();
241 break;
242 case SIGTERM:
243 fprintf (stderr, "Events still queued:\n");
244 for (ep = events; ep < events + num_events; ep++)
245 fprintf (stderr, "%d = %ld @ %s\n",
246 ep - events, ep->reply_at, ep->token);
247 exit (0);
248 break;
251 /* required on older UNIXes; harmless on newer ones */
252 signal (sig, sigcatch);
255 /*ARGSUSED*/
257 main (argc, argv)
258 int argc;
259 char **argv;
261 for (pname = argv[0] + strlen (argv[0]);
262 *pname != '/' && pname != argv[0];
263 pname--);
264 if (*pname == '/')
265 pname++;
267 events_size = 16;
268 events = ((struct event *) malloc (events_size * sizeof (*events)));
269 num_events = 0;
271 signal (SIGIO, sigcatch);
272 signal (SIGALRM, sigcatch);
273 signal (SIGTERM, sigcatch);
275 #ifndef USG
276 if (fcntl (0, F_SETOWN, getpid ()) == -1)
278 fprintf (stderr, "%s: can't set ownership of stdin\n", pname);
279 fprintf (stderr, "%s\n", sys_errlist[errno]);
280 exit (1);
282 if (fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) | FASYNC) == -1)
284 fprintf (stderr, "%s: can't request asynchronous I/O on stdin\n", pname);
285 fprintf (stderr, "%s\n", sys_errlist[errno]);
286 exit (1);
288 #endif /* USG */
290 for (;;)
291 pause ();
294 /* timer.c ends here */