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