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