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