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