1 /* $Id$ */
2
3 /*
4 *
5 * Copyright (C) 1998 David Mazieres (dm@uun.org)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2, or (at
10 * your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
22 */
23
24 #include "async.h"
25 #include "fdlim.h"
26 #include "ihash.h"
27 #include "itree.h"
28 #include "list.h"
29
30 #include <typeinfo>
31
32 #define FD_SETSIZE_ROUND (sizeof (long))/* # Bytes to which to round fd_sets */
33 int fd_set_bytes; // Size in bytes of a [wide] fd_set
34 int maxfd;
35 bool amain_panic;
36 static int nselfd;
37
38 timespec tsnow;
39 const time_t &timenow = tsnow.tv_sec;
40
41 static timeval selwait;
42
43 #ifdef WRAP_DEBUG
44 #define CBTR_FD 0x0001
45 #define CBTR_TIME 0x0002
46 #define CBTR_SIG 0x0004
47 #define CBTR_CHLD 0x0008
48 #define CBTR_LAZY 0x0010
49 static int callback_trace;
50 static bool callback_time;
51
52 static inline const char *
timestring()53 timestring ()
54 {
55 if (!callback_time)
56 return "";
57
58 clock_gettime (CLOCK_REALTIME, &tsnow);
59 static str buf;
60 buf = strbuf ("%d.%06d ", int (tsnow.tv_sec), int (tsnow.tv_nsec/1000));
61 return buf;
62 }
63 #endif /* WRAP_DEBUG */
64
65 struct child {
66 pid_t pid;
67 cbi cb;
68 ihash_entry<child> link;
childchild69 child (pid_t p, cbi c) : pid (p), cb (c) {}
70 };
71 static ihash<pid_t, child, &child::pid, &child::link> chldcbs;
72 static time_t chldcb_check_last;
73
74 struct timecb_t {
75 timespec ts;
76 const cbv cb;
77 itree_entry<timecb_t> link;
timecb_ttimecb_t78 timecb_t (const timespec &t, const cbv &c) : ts (t), cb (c) {}
79 };
80 static itree<timespec, timecb_t, &timecb_t::ts, &timecb_t::link> timecbs;
81 static bool timecbs_altered;
82
83 struct lazycb_t {
84 const time_t interval;
85 time_t next;
86 const cbv cb;
87 list_entry<lazycb_t> link;
88
89 lazycb_t (time_t interval, cbv cb);
90 ~lazycb_t ();
91 };
92 static bool lazycb_removed;
93 static list<lazycb_t, &lazycb_t::link> *lazylist;
94
95 const int fdsn = 2;
96 static cbv::ptr *fdcbs[fdsn];
97 static fd_set *fdsp[fdsn];
98 static fd_set *fdspt[fdsn];
99
100 static int sigpipes[2] = { -1, -1 };
101 #ifdef NSIG
102 const int nsig = NSIG;
103 #else /* !NSIG */
104 const int nsig = 32;
105 #endif /* !NSIG */
106 /* Note: sigdocheck and sigcaught intentionally ints rather than
107 * bools. The hope is that an int can safely be written without
108 * affecting surrounding memory. (This is certainly not the case on
109 * some architectures if bool is a char. Consider getting signal 2
110 * right after signal 3 on an alpha, for instance. You might end up
111 * clearing sigcaught[2] when you finish setting sigcaught[3].) */
112 static volatile int sigdocheck;
113 static volatile int sigcaught[nsig];
114 static bssptr<cbv::type> sighandler[nsig];
115
116 static void sigcb_check ();
117
118 void
chldcb(pid_t pid,cbi::ptr cb)119 chldcb (pid_t pid, cbi::ptr cb)
120 {
121 if (child *c = chldcbs[pid]) {
122 chldcbs.remove (c);
123 delete c;
124 }
125 if (cb)
126 chldcbs.insert (New child (pid, cb));
127 }
128
129 void
chldcb_check()130 chldcb_check ()
131 {
132 for (;;) {
133 int status;
134 pid_t pid = waitpid (-1, &status, WNOHANG);
135 if (pid == 0 || pid == -1)
136 break;
137 if (child *c = chldcbs[pid]) {
138 chldcbs.remove (c);
139 #ifdef WRAP_DEBUG
140 if (callback_trace & CBTR_CHLD)
141 warn ("CALLBACK_TRACE: %schild pid %d (status %d) %s <- %s\n",
142 timestring (), pid, status, c->cb->dest, c->cb->line);
143 #endif /* WRAP_DEBUG */
144 (*c->cb) (status);
145 delete c;
146 }
147 }
148 chldcb_check_last = timenow;
149 }
150
151 timecb_t *
timecb(const timespec & ts,cbv cb)152 timecb (const timespec &ts, cbv cb)
153 {
154 timecb_t *to = New timecb_t (ts, cb);
155 timecbs.insert (to);
156 // timecbs_altered = true;
157 return to;
158 }
159
160 timecb_t *
delaycb(time_t sec,u_int32_t nsec,cbv cb)161 delaycb (time_t sec, u_int32_t nsec, cbv cb)
162 {
163 timespec ts;
164 clock_gettime (CLOCK_REALTIME, &ts);
165 ts.tv_sec += sec;
166 ts.tv_nsec += nsec;
167 if (ts.tv_nsec >= 1000000000) {
168 ts.tv_nsec -= 1000000000;
169 ts.tv_sec++;
170 }
171 return timecb (ts, cb);
172 }
173
174 void
timecb_remove(timecb_t * to)175 timecb_remove (timecb_t *to)
176 {
177 if (!to)
178 return;
179
180 for (timecb_t *tp = timecbs[to->ts]; tp != to; tp = timecbs.next (tp))
181 if (!tp || tp->ts != to->ts)
182 panic ("timecb_remove: invalid timecb_t\n");
183 timecbs_altered = true;
184 timecbs.remove (to);
185 delete to;
186 }
187
188 void
timecb_check()189 timecb_check ()
190 {
191 clock_gettime (CLOCK_REALTIME, &tsnow);
192 timecb_t *tp, *ntp;
193
194 for (tp = timecbs.first (); tp && tp->ts <= tsnow;
195 tp = timecbs_altered ? timecbs.first () : ntp) {
196 ntp = timecbs.next (tp);
197 timecbs.remove (tp);
198 timecbs_altered = false;
199 #ifdef WRAP_DEBUG
200 if (callback_trace & CBTR_TIME)
201 warn ("CALLBACK_TRACE: %stimecb %s <- %s\n", timestring (),
202 tp->cb->dest, tp->cb->line);
203 #endif /* WRAP_DEBUG */
204 (*tp->cb) ();
205 delete tp;
206 }
207 selwait.tv_usec = 0;
208 if (!(tp = timecbs.first ()))
209 selwait.tv_sec = 86400;
210 else {
211 clock_gettime (CLOCK_REALTIME, &tsnow);
212 if (tp->ts < tsnow)
213 selwait.tv_sec = 0;
214 else if (tp->ts.tv_nsec >= tsnow.tv_nsec) {
215 selwait.tv_sec = tp->ts.tv_sec - tsnow.tv_sec;
216 selwait.tv_usec = (tp->ts.tv_nsec - tsnow.tv_nsec) / 1000;
217 }
218 else {
219 selwait.tv_sec = tp->ts.tv_sec - tsnow.tv_sec - 1;
220 selwait.tv_usec = (1000000000 + tp->ts.tv_nsec - tsnow.tv_nsec) / 1000;
221 }
222 }
223 if (sigdocheck)
224 selwait.tv_sec = selwait.tv_usec = 0;
225 }
226
227 void
fdcb(int fd,selop op,cbv::ptr cb)228 fdcb (int fd, selop op, cbv::ptr cb)
229 {
230 assert (fd >= 0);
231 assert (fd < maxfd);
232 fdcbs[op][fd] = cb;
233 if (cb) {
234 if (fd >= nselfd)
235 nselfd = fd + 1;
236 FD_SET (fd, fdsp[op]);
237 }
238 else
239 FD_CLR (fd, fdsp[op]);
240 }
241
242 static void
fdcb_check(void)243 fdcb_check (void)
244 {
245 for (int i = 0; i < fdsn; i++)
246 memcpy (fdspt[i], fdsp[i], fd_set_bytes);
247 int n = select (nselfd, fdspt[0], fdspt[1], NULL, &selwait);
248 if (n < 0 && errno != EINTR)
249 panic ("select: %m\n");
250 clock_gettime (CLOCK_REALTIME, &tsnow);
251 if (sigdocheck)
252 sigcb_check ();
253 for (int fd = 0; fd < maxfd && n > 0; fd++)
254 for (int i = 0; i < fdsn; i++)
255 if (FD_ISSET (fd, fdspt[i])) {
256 n--;
257 if (FD_ISSET (fd, fdsp[i])) {
258 #ifdef WRAP_DEBUG
259 if (fd != errfd && fd != sigpipes[0]
260 && (callback_trace & CBTR_FD))
261 warn ("CALLBACK_TRACE: %sfdcb %d%c %s <- %s\n",
262 timestring (), fd, "rwe"[i],
263 fdcbs[i][fd]->dest, fdcbs[i][fd]->line);
264 #endif /* WRAP_DEBUG */
265 (*fdcbs[i][fd]) ();
266 }
267 }
268 }
269
270 static void
sigcatch(int sig)271 sigcatch (int sig)
272 {
273 sigdocheck = 1;
274 sigcaught[sig] = 1;
275 selwait.tv_sec = selwait.tv_usec = 0;
276 /* On some operating systems, select is not a system call but is
277 * implemented inside libc. This may cause a race condition in
278 * which select ends up being called with the original (non-zero)
279 * value of selwait. We avoid the problem by writing to a pipe that
280 * will wake up the select. */
281 use (write (sigpipes[1], "", 1));
282 }
283
284 cbv::ptr
sigcb(int sig,cbv::ptr cb,int flags)285 sigcb (int sig, cbv::ptr cb, int flags)
286 {
287 sigset_t set;
288 if (!sigemptyset (&set) && !sigaddset (&set, sig))
289 sigprocmask (SIG_UNBLOCK, &set, NULL);
290
291 struct sigaction sa;
292 assert (sig > 0 && sig < nsig);
293 bzero (&sa, sizeof (sa));
294 sa.sa_handler = cb ? sigcatch : SIG_DFL;
295 sa.sa_flags = flags;
296 if (sigaction (sig, &sa, NULL) < 0) // Must be bad signal, serious bug
297 panic ("sigcb: sigaction: %m\n");
298 cbv::ptr ocb = sighandler[sig];
299 sighandler[sig] = cb;
300 return ocb;
301 }
302
303 static void
sigcb_check()304 sigcb_check ()
305 {
306 if (sigdocheck) {
307 char buf[64];
308 while (read (sigpipes[0], buf, sizeof (buf)) > 0)
309 ;
310 sigdocheck = 0;
311 for (int i = 1; i < nsig; i++)
312 if (sigcaught[i]) {
313 sigcaught[i] = 0;
314 if (cbv::ptr cb = sighandler[i]) {
315 #ifdef WRAP_DEBUG
316 if ((callback_trace & CBTR_SIG) && i != SIGCHLD) {
317 # ifdef NEED_SYS_SIGNAME_DECL
318 warn ("CALLBACK_TRACE: %ssignal %d %s <- %s\n",
319 timestring (), i, cb->dest, cb->line);
320 # else /* !NEED_SYS_SIGNAME_DECL */
321 warn ("CALLBACK_TRACE: %sSIG%s %s <- %s\n", timestring (),
322 sys_signame[i], cb->dest, cb->line);
323 # endif /* !NEED_SYS_SIGNAME_DECL */
324 }
325 #endif /* WRAP_DEBUG */
326 (*cb) ();
327 }
328 }
329 }
330 }
331
lazycb_t(time_t i,cbv c)332 lazycb_t::lazycb_t (time_t i, cbv c)
333 : interval (i), next (timenow + interval), cb (c)
334 {
335 lazylist->insert_head (this);
336 }
337
~lazycb_t()338 lazycb_t::~lazycb_t ()
339 {
340 lazylist->remove (this);
341 }
342
343 lazycb_t *
lazycb(time_t interval,cbv cb)344 lazycb (time_t interval, cbv cb)
345 {
346 return New lazycb_t (interval, cb);
347 }
348
349 void
lazycb_remove(lazycb_t * lazy)350 lazycb_remove (lazycb_t *lazy)
351 {
352 lazycb_removed = true;
353 delete lazy;
354 }
355
356 void
lazycb_check()357 lazycb_check ()
358 {
359 clock_gettime (CLOCK_REALTIME, &tsnow);
360 restart:
361 lazycb_removed = false;
362 for (lazycb_t *lazy = lazylist->first; lazy; lazy = lazylist->next (lazy)) {
363 if (timenow < lazy->next)
364 continue;
365 lazy->next = timenow + lazy->interval;
366 #ifdef WRAP_DEBUG
367 if (callback_trace & CBTR_LAZY)
368 warn ("CALLBACK_TRACE: %slazy %s <- %s\n", timestring (),
369 lazy->cb->dest, lazy->cb->line);
370 #endif /* WRAP_DEBUG */
371 (*lazy->cb) ();
372 if (lazycb_removed)
373 goto restart;
374 }
375 }
376
377 static void
ainit()378 ainit ()
379 {
380 if (sigpipes[0] == -1) {
381 if (pipe (sigpipes) < 0)
382 fatal ("could not create sigpipes: %m\n");
383
384 _make_async (sigpipes[0]);
385 _make_async (sigpipes[1]);
386 close_on_exec (sigpipes[0]);
387 close_on_exec (sigpipes[1]);
388 fdcb (sigpipes[0], selread, cbv_null);
389
390 /* Set SA_RESTART for SIGCHLD, primarily for the benefit of
391 * stdio-using code like lex/flex scanners. These tend to flip out
392 * if read ever returns EINTR. */
393 sigcb (SIGCHLD, wrap (chldcb_check), (SA_NOCLDSTOP
394 #ifdef SA_RESTART
395 | SA_RESTART
396 #endif /* SA_RESTART */
397 ));
398 sigcatch (SIGCHLD);
399 }
400 }
401
402 static inline void
_acheck()403 _acheck ()
404 {
405 if (amain_panic)
406 panic ("child process returned from afork ()\n");
407 lazycb_check ();
408 fdcb_check ();
409 sigcb_check ();
410
411 #if 0
412 /*
413 * On planet-lab's weird Linux kernel, sometimes processes stop
414 * receiving SIGCHLD signals. To avoid endlessly accumulating
415 * Zombie children, we periodically try to reap children anyway,
416 * even when we haven't received a SIGCHLD. This is kind of gross,
417 * but without the workaround one can end up completely filling the
418 * process table with zombies (making it hard to log in and fix the
419 * problem).
420 */
421 if (timenow > chldcb_check_last + 60)
422 chldcb_check ();
423 #endif
424
425 timecb_check ();
426 }
427
428 void
acheck()429 acheck ()
430 {
431 timecb_check ();
432 ainit ();
433 _acheck ();
434 }
435
436 void
amain()437 amain ()
438 {
439 static bool amain_called;
440 if (amain_called)
441 panic ("amain called recursively\n");
442 amain_called = true;
443
444 ainit ();
445 err_init ();
446
447 timecb_check ();
448 for (;;)
449 _acheck ();
450 }
451
452 int async_init::count;
453
454 void
start()455 async_init::start ()
456 {
457 static bool initialized;
458 if (initialized)
459 panic ("async_init called twice\n");
460 initialized = true;
461
462 /* Ignore SIGPIPE, since we may get a lot of these */
463 struct sigaction sa;
464 bzero (&sa, sizeof (sa));
465 sa.sa_handler = SIG_IGN;
466 sigaction (SIGPIPE, &sa, NULL);
467
468 if (!execsafe ()) {
469 int fdlim_hard = fdlim_get (1);
470 if (char *p = getenv ("FDLIM_HARD")) {
471 int n = atoi (p);
472 if (n > 0 && n < fdlim_hard) {
473 fdlim_hard = n;
474 fdlim_set (fdlim_hard, -1);
475 }
476 }
477 }
478 if (!getenv ("FDLIM_HARD") || !execsafe ()) {
479 str var = strbuf ("FDLIM_HARD=%d", fdlim_get (1));
480 xputenv (const_cast<char *> (var.cstr()));
481 var = strbuf ("FDLIM_SOFT=%d", fdlim_get (0));
482 xputenv (const_cast<char *> (var.cstr()));
483 }
484 #ifndef HAVE_WIDE_SELECT
485 fdlim_set (FD_SETSIZE, execsafe ());
486 maxfd = fdlim_get (0);
487 fd_set_bytes = sizeof (fd_set);
488 #else /* HAVE_WIDE_SELECT */
489 if (!execsafe () || fdlim_set (FDLIM_MAX, 1) < 0)
490 fdlim_set (fdlim_get (1), 0);
491 maxfd = fdlim_get (0);
492 fd_set_bytes = (maxfd+7)/8;
493 if (fd_set_bytes % FD_SETSIZE_ROUND)
494 fd_set_bytes += FD_SETSIZE_ROUND - (fd_set_bytes % FD_SETSIZE_ROUND);
495 #endif /* HAVE_WIDE_SELECT */
496
497 for (int i = 0; i < fdsn; i++) {
498 fdcbs[i] = New cbv::ptr[maxfd];
499 fdsp[i] = (fd_set *) xmalloc (fd_set_bytes);
500 bzero (fdsp[i], fd_set_bytes);
501 fdspt[i] = (fd_set *) xmalloc (fd_set_bytes);
502 bzero (fdspt[i], fd_set_bytes);
503 }
504
505 lazylist = New list<lazycb_t, &lazycb_t::link>;
506
507 #ifdef WRAP_DEBUG
508 if (char *p = getenv ("CALLBACK_TRACE")) {
509 if (strchr (p, 'f'))
510 callback_trace |= CBTR_FD;
511 if (strchr (p, 't'))
512 callback_trace |= CBTR_TIME;
513 if (strchr (p, 's'))
514 callback_trace |= CBTR_SIG;
515 if (strchr (p, 'c'))
516 callback_trace |= CBTR_CHLD;
517 if (strchr (p, 'l'))
518 callback_trace |= CBTR_LAZY;
519 if (strchr (p, 'a'))
520 callback_trace |= -1;
521 if (strchr (p, 'T'))
522 callback_time = true;
523 }
524 #endif /* WRAP_DEBUG */
525 }
526
527 void
stop()528 async_init::stop ()
529 {
530 err_flush ();
531 }
532