]> git.decadent.org.uk Git - ion3.git/blob - libmainloop/signal.c
[svn-upgrade] Integrating new upstream version, ion3 (20070608)
[ion3.git] / libmainloop / signal.c
1 /*
2  * ion/libmainloop/signal.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <unistd.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 #include <sys/time.h>
13 #include <time.h>
14 #include <signal.h>
15 #include <string.h>
16 #include <stdlib.h>
17
18 #include <libtu/objp.h>
19 #include <libtu/types.h>
20 #include <libtu/misc.h>
21 #include <libtu/locale.h>
22 #include <libtu/output.h>
23
24 #include "signal.h"
25 #include "hooks.h"
26
27 static int kill_sig=0;
28 #if 1
29 static int wait_sig=0;
30 #endif
31
32 static int usr2_sig=0;
33 static bool had_tmr=FALSE;
34
35 WHook *mainloop_sigchld_hook=NULL;
36 WHook *mainloop_sigusr2_hook=NULL;
37
38
39 /*{{{ Timers */
40
41
42 static WTimer *queue=NULL;
43
44
45 int mainloop_gettime(struct timeval *val)
46 {
47 #ifdef _POSIX_MONOTONIC_CLOCK
48     struct timespec spec;
49     int ret;
50     
51     ret=clock_gettime(CLOCK_MONOTONIC, &spec);
52     
53     val->tv_sec=spec.tv_sec;
54     val->tv_usec=spec.tv_nsec/1000;
55     
56     return ret;
57 #else
58     return gettimeofday(&val, NULL);
59 #endif
60 }
61
62
63 #define TIMEVAL_LATER(a, b) \
64     ((a.tv_sec > b.tv_sec) || \
65     ((a.tv_sec == b.tv_sec) && \
66      (a.tv_usec > b.tv_usec)))
67
68 #define USECS_IN_SEC 1000000
69
70
71 static void do_timer_set()
72 {
73     struct itimerval val={{0, 0}, {0, 0}};
74     
75     if(queue==NULL){
76         setitimer(ITIMER_REAL, &val, NULL);
77         return;
78     }
79
80     /* Subtract queue time from current time, don't go below zero */
81     mainloop_gettime(&(val.it_value));
82     if(TIMEVAL_LATER((queue)->when, val.it_value)){
83         if(queue->when.tv_usec<val.it_value.tv_usec){
84             queue->when.tv_usec+=USECS_IN_SEC;
85             queue->when.tv_sec--;
86         }
87         val.it_value.tv_usec=queue->when.tv_usec-val.it_value.tv_usec;
88         val.it_value.tv_sec=queue->when.tv_sec-val.it_value.tv_sec;
89         if(val.it_value.tv_usec<0)
90             val.it_value.tv_usec=0;
91         /* POSIX and some kernels have been designed by absolute morons and 
92          * contain idiotic artificial restrictions on the value of tv_usec, 
93          * that will only cause more code being run and clock cycles being 
94          * spent to do the same thing, as the kernel will in any case convert
95          * the seconds to some other units.
96          */
97          val.it_value.tv_sec+=val.it_value.tv_usec/USECS_IN_SEC;
98          val.it_value.tv_usec%=USECS_IN_SEC;
99     }else{
100         had_tmr=TRUE;
101         return;
102     }
103
104     val.it_interval.tv_usec=val.it_value.tv_usec;
105     val.it_interval.tv_sec=val.it_value.tv_sec;
106     
107     if((setitimer(ITIMER_REAL, &val, NULL))){
108         had_tmr=TRUE;
109     }
110 }
111
112
113 typedef struct{
114     pid_t pid;
115     int code;
116 } ChldParams;
117
118
119 static bool mrsh_chld(void (*fn)(pid_t, int), ChldParams *p)
120 {
121     fn(p->pid, p->code);
122     return TRUE;
123 }
124
125
126 static bool mrsh_chld_extl(ExtlFn fn, ChldParams *p)
127 {
128     ExtlTab t=extl_create_table();
129     bool ret;
130
131     extl_table_sets_i(t, "pid", (int)p->pid);
132     
133     if(WIFEXITED(p->code)){
134         extl_table_sets_b(t, "exited", TRUE);
135         extl_table_sets_i(t, "exitstatus", WEXITSTATUS(p->code));
136     }
137     if(WIFSIGNALED(p->code)){
138         extl_table_sets_b(t, "signaled", TRUE);
139         extl_table_sets_i(t, "termsig", WTERMSIG(p->code));
140 #ifdef WCOREDUMP 
141         extl_table_sets_i(t, "coredump", WCOREDUMP(p->code));
142 #endif
143     }
144     if(WIFSTOPPED(p->code)){
145         extl_table_sets_b(t, "stopped", TRUE);
146         extl_table_sets_i(t, "stopsig", WSTOPSIG(p->code));
147     }
148     /*if(WIFCONTINUED(p->code)){
149         extl_table_sets_b(t, "continued", TRUE);
150     }*/
151     
152     ret=extl_call(fn, "t", NULL, t);
153     
154     extl_unref_table(t);
155     
156     return ret;
157 }
158
159 static bool mrsh_usr2(void (*fn)(void), void *p)
160 {
161     fn();
162     return TRUE;
163 }
164
165 static bool mrsh_usr2_extl(ExtlFn fn, void *p)
166 {
167     bool ret;
168     ExtlTab t=extl_create_table();
169     ret=extl_call(fn, "t", NULL, t);
170     extl_unref_table(t);
171     return ret;
172 }
173
174
175 bool mainloop_check_signals()
176 {
177     struct timeval current_time;
178     WTimer *q;
179     int ret=0;
180
181     if(usr2_sig!=0){
182         usr2_sig=0;
183         if(mainloop_sigusr2_hook!=NULL){
184             hook_call(mainloop_sigusr2_hook, NULL,
185                       (WHookMarshall*)mrsh_usr2,
186                       (WHookMarshallExtl*)mrsh_usr2_extl);
187         }
188     }
189
190 #if 1    
191     if(wait_sig!=0){
192         ChldParams p;
193         wait_sig=0;
194         while((p.pid=waitpid(-1, &p.code, WNOHANG|WUNTRACED))>0){
195             if(mainloop_sigchld_hook!=NULL &&
196                (WIFEXITED(p.code) || WIFSIGNALED(p.code))){
197                 hook_call(mainloop_sigchld_hook, &p, 
198                           (WHookMarshall*)mrsh_chld,
199                           (WHookMarshallExtl*)mrsh_chld_extl);
200             }
201         }
202     }
203 #endif
204     
205     if(kill_sig!=0)
206         return kill_sig;
207
208     /* Check for timer events in the queue */
209     while(had_tmr && queue!=NULL){
210         had_tmr=FALSE;
211         mainloop_gettime(&current_time);
212         while(queue!=NULL){
213             if(TIMEVAL_LATER(current_time, queue->when)){
214                 q=queue;
215                 queue=q->next;
216                 q->next=NULL;
217                 if(q->handler!=NULL){
218                     WTimerHandler *handler=q->handler;
219                     Obj *obj=q->objwatch.obj;
220                     q->handler=NULL;
221                     watch_reset(&(q->objwatch));
222                     handler(q, obj);
223                 }else if(q->extl_handler!=extl_fn_none()){
224                     ExtlFn fn=q->extl_handler;
225                     Obj *obj=q->objwatch.obj;
226                     watch_reset(&(q->objwatch));
227                     q->extl_handler=extl_fn_none();
228                     extl_call(fn, "o", NULL, obj);
229                     extl_unref_fn(fn);
230                 }
231             }else{
232                 break;
233             }
234         }
235         do_timer_set();
236     }
237     
238     return ret;
239 }
240
241
242 static void add_to_current_time(struct timeval *when, uint msecs)
243 {
244     long tmp_usec;
245
246     mainloop_gettime(when);
247     tmp_usec=when->tv_usec + (msecs * 1000);
248     when->tv_usec=tmp_usec % 1000000;
249     when->tv_sec+=tmp_usec / 1000000;
250 }
251
252
253 /*EXTL_DOC
254  * Is timer set?
255  */
256 EXTL_EXPORT_MEMBER
257 bool timer_is_set(WTimer *timer)
258 {
259     WTimer *tmr;
260     for(tmr=queue; tmr!=NULL; tmr=tmr->next){
261         if(tmr==timer)
262             return TRUE;
263     }
264     return FALSE;
265 }
266
267
268 void timer_do_set(WTimer *timer, uint msecs, WTimerHandler *handler,
269                   Obj *obj, ExtlFn fn)
270 {
271     WTimer *q, **qptr;
272     bool set=FALSE;
273     
274     timer_reset(timer);
275
276     /* Initialize the new queue timer event */
277     add_to_current_time(&(timer->when), msecs);
278     timer->next=NULL;
279     timer->handler=handler;
280     timer->extl_handler=fn;
281     if(obj!=NULL)
282         watch_setup(&(timer->objwatch), obj, NULL);
283     else
284         watch_reset(&(timer->objwatch));
285
286     /* Add timerevent in place to queue */
287     q=queue;
288     qptr=&queue;
289     
290     while(q!=NULL){
291         if(TIMEVAL_LATER(q->when, timer->when))
292             break;
293         qptr=&(q->next);
294         q=q->next;
295     }
296     
297     timer->next=q;
298     *qptr=timer;
299
300     do_timer_set();
301 }
302
303
304 void timer_set(WTimer *timer, uint msecs, WTimerHandler *handler,
305                Obj *obj)
306 {
307     timer_do_set(timer, msecs, handler, obj, extl_fn_none());
308 }
309
310
311 /*EXTL_DOC
312  * Set \var{timer} to call \var{fn} in \var{msecs} milliseconds.
313  */
314 EXTL_EXPORT_AS(WTimer, set)
315 void timer_set_extl(WTimer *timer, uint msecs, ExtlFn fn)
316 {
317     timer_do_set(timer, msecs, NULL, NULL, extl_ref_fn(fn));
318 }
319
320     
321 /*EXTL_DOC
322  * Reset timer.
323  */
324 EXTL_EXPORT_MEMBER
325 void timer_reset(WTimer *timer)
326 {
327     WTimer *q=queue, **qptr=&queue;
328     
329     while(q!=NULL){
330         if(q==timer){
331             *qptr=timer->next;
332             do_timer_set();
333             break;
334         }
335         qptr=&(q->next);
336         q=q->next;
337         
338     }
339     
340     timer->handler=NULL;
341     extl_unref_fn(timer->extl_handler);
342     timer->extl_handler=extl_fn_none();
343     watch_reset(&(timer->objwatch));
344 }
345
346
347 bool timer_init(WTimer *timer)
348 {
349     timer->when.tv_sec=0;
350     timer->when.tv_usec=0;
351     timer->next=NULL;
352     timer->handler=NULL;
353     timer->extl_handler=extl_fn_none();
354     watch_init(&(timer->objwatch));
355     return TRUE;
356 }
357
358 void timer_deinit(WTimer *timer)
359 {
360     timer_reset(timer);
361 }
362
363
364 WTimer *create_timer()
365 {
366     CREATEOBJ_IMPL(WTimer, timer, (p));
367 }
368
369 /*EXTL_DOC
370  * Create a new timer.
371  */
372 EXTL_EXPORT_AS(mainloop, create_timer)
373 WTimer *create_timer_extl_owned()
374 {
375     WTimer *timer=create_timer();
376     if(timer!=NULL)
377         ((Obj*)timer)->flags|=OBJ_EXTL_OWNED;
378     return timer;
379 }
380
381
382 EXTL_EXPORT
383 IMPLCLASS(WTimer, Obj, timer_deinit, NULL);
384
385
386 /*}}}*/
387
388
389 /*{{{ Signal handling */
390
391
392 static void fatal_signal_handler(int signal_num)
393 {
394     set_warn_handler(NULL);
395     warn(TR("Caught fatal signal %d. Dying without deinit."), signal_num); 
396     signal(signal_num, SIG_DFL);
397     kill(getpid(), signal_num);
398 }
399
400            
401 static void deadly_signal_handler(int signal_num)
402 {
403     set_warn_handler(NULL);
404     warn(TR("Caught signal %d. Dying."), signal_num);
405     signal(signal_num, SIG_DFL);
406     /*if(ioncore_g.opmode==IONCORE_OPMODE_INIT)
407         kill(getpid(), signal_num);
408     else*/
409         kill_sig=signal_num;
410 }
411
412
413 static void chld_handler(int signal_num)
414 {
415 #if 0
416     pid_t pid;
417
418     while((pid=waitpid(-1, NULL, WNOHANG|WUNTRACED))>0){
419         /* nothing */
420     }
421 #else
422     wait_sig=1;
423 #endif
424 }
425
426 static void usr2_handler(int signal_num)
427 {
428     usr2_sig=1;
429 }
430
431
432 static void exit_handler(int signal_num)
433 {
434     if(kill_sig>0){
435         warn(TR("Got signal %d while %d is still to be handled."),
436              signal_num, kill_sig);
437     }
438     kill_sig=signal_num;
439 }
440
441
442 static void timer_handler(int signal_num)
443 {
444     had_tmr=TRUE;
445 }
446
447
448 static void ignore_handler(int signal_num)
449 {
450     
451 }
452
453
454 #ifndef SA_RESTART
455  /* glibc is broken (?) and does not define SA_RESTART with
456   * '-ansi -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED', so just try to live
457   * without it.
458   */
459 #define SA_RESTART 0
460 #endif
461
462 #define IFTRAP(X) if(sigismember(which, X))
463 #define DEADLY(X) IFTRAP(X) signal(X, deadly_signal_handler);
464 #define FATAL(X) IFTRAP(X) signal(X, fatal_signal_handler);
465 #define IGNORE(X) IFTRAP(X) signal(X, SIG_IGN)
466
467 void mainloop_trap_signals(const sigset_t *which)
468 {
469     struct sigaction sa;
470     sigset_t set, oldset;
471     sigset_t dummy;
472
473     if(which==NULL){
474         sigfillset(&dummy);
475         which=&dummy;
476     }
477     
478     sigemptyset(&set);
479     sigemptyset(&oldset);
480     sigprocmask(SIG_SETMASK, &set, &oldset);
481     
482     DEADLY(SIGHUP);
483     DEADLY(SIGQUIT);
484     DEADLY(SIGINT);
485     DEADLY(SIGABRT);
486
487     FATAL(SIGILL);
488     FATAL(SIGSEGV);
489     FATAL(SIGFPE);
490     FATAL(SIGBUS);
491     
492     IGNORE(SIGTRAP);
493     /*IGNORE(SIGWINCH);*/
494
495     sigemptyset(&(sa.sa_mask));
496
497     IFTRAP(SIGALRM){
498         sa.sa_handler=timer_handler;
499         sa.sa_flags=SA_RESTART;
500         sigaction(SIGALRM, &sa, NULL);
501     }
502
503     IFTRAP(SIGCHLD){
504         sa.sa_handler=chld_handler;
505         sa.sa_flags=SA_NOCLDSTOP|SA_RESTART;
506         sigaction(SIGCHLD, &sa, NULL);
507     }
508
509     IFTRAP(SIGUSR2){
510         sa.sa_handler=usr2_handler;
511         sa.sa_flags=SA_RESTART;
512         sigaction(SIGUSR2, &sa, NULL);
513     }
514
515     IFTRAP(SIGTERM){
516         sa.sa_handler=exit_handler;
517         sa.sa_flags=SA_RESTART;
518         sigaction(SIGTERM, &sa, NULL);
519     }
520     
521     IFTRAP(SIGUSR1){
522         sa.sa_handler=exit_handler;
523         sa.sa_flags=SA_RESTART;
524         sigaction(SIGUSR1, &sa, NULL);
525     }
526     
527     /* SIG_IGN is preserved over execve and since the the default action
528      * for SIGPIPE is not to ignore it, some programs may get upset if
529      * the behaviour is not the default.
530      */
531     IFTRAP(SIGPIPE){
532         sa.sa_handler=ignore_handler;
533         sigaction(SIGPIPE, &sa, NULL);
534     }
535
536 }
537
538 #undef IGNORE
539 #undef FATAL
540 #undef DEADLY
541
542
543 /*}}}*/
544