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