2 * ion/libmainloop/signal.c
4 * Copyright (c) Tuomo Valkonen 1999-2007.
6 * See the included file LICENSE for details.
10 #include <sys/types.h>
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>
27 static int kill_sig=0;
29 static int wait_sig=0;
32 static int usr2_sig=0;
33 static bool had_tmr=FALSE;
35 WHook *mainloop_sigchld_hook=NULL;
36 WHook *mainloop_sigusr2_hook=NULL;
42 static WTimer *queue=NULL;
45 int mainloop_gettime(struct timeval *val)
47 #ifdef _POSIX_MONOTONIC_CLOCK
51 ret=clock_gettime(CLOCK_MONOTONIC, &spec);
53 val->tv_sec=spec.tv_sec;
54 val->tv_usec=spec.tv_nsec/1000;
58 return gettimeofday(&val, NULL);
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)))
68 #define USECS_IN_SEC 1000000
71 static void do_timer_set()
73 struct itimerval val={{0, 0}, {0, 0}};
76 setitimer(ITIMER_REAL, &val, NULL);
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;
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.
97 val.it_value.tv_sec+=val.it_value.tv_usec/USECS_IN_SEC;
98 val.it_value.tv_usec%=USECS_IN_SEC;
104 val.it_interval.tv_usec=val.it_value.tv_usec;
105 val.it_interval.tv_sec=val.it_value.tv_sec;
107 if((setitimer(ITIMER_REAL, &val, NULL))){
119 static bool mrsh_chld(void (*fn)(pid_t, int), ChldParams *p)
126 static bool mrsh_chld_extl(ExtlFn fn, ChldParams *p)
128 ExtlTab t=extl_create_table();
131 extl_table_sets_i(t, "pid", (int)p->pid);
133 if(WIFEXITED(p->code)){
134 extl_table_sets_b(t, "exited", TRUE);
135 extl_table_sets_i(t, "exitstatus", WEXITSTATUS(p->code));
137 if(WIFSIGNALED(p->code)){
138 extl_table_sets_b(t, "signaled", TRUE);
139 extl_table_sets_i(t, "termsig", WTERMSIG(p->code));
141 extl_table_sets_i(t, "coredump", WCOREDUMP(p->code));
144 if(WIFSTOPPED(p->code)){
145 extl_table_sets_b(t, "stopped", TRUE);
146 extl_table_sets_i(t, "stopsig", WSTOPSIG(p->code));
148 /*if(WIFCONTINUED(p->code)){
149 extl_table_sets_b(t, "continued", TRUE);
152 ret=extl_call(fn, "t", NULL, t);
159 static bool mrsh_usr2(void (*fn)(void), void *p)
165 static bool mrsh_usr2_extl(ExtlFn fn, void *p)
168 ExtlTab t=extl_create_table();
169 ret=extl_call(fn, "t", NULL, t);
175 bool mainloop_check_signals()
177 struct timeval current_time;
183 if(mainloop_sigusr2_hook!=NULL){
184 hook_call(mainloop_sigusr2_hook, NULL,
185 (WHookMarshall*)mrsh_usr2,
186 (WHookMarshallExtl*)mrsh_usr2_extl);
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);
208 /* Check for timer events in the queue */
209 while(had_tmr && queue!=NULL){
211 mainloop_gettime(¤t_time);
213 if(TIMEVAL_LATER(current_time, queue->when)){
217 if(q->handler!=NULL){
218 WTimerHandler *handler=q->handler;
219 Obj *obj=q->objwatch.obj;
221 watch_reset(&(q->objwatch));
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);
242 static void add_to_current_time(struct timeval *when, uint msecs)
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;
257 bool timer_is_set(WTimer *timer)
260 for(tmr=queue; tmr!=NULL; tmr=tmr->next){
268 void timer_do_set(WTimer *timer, uint msecs, WTimerHandler *handler,
276 /* Initialize the new queue timer event */
277 add_to_current_time(&(timer->when), msecs);
279 timer->handler=handler;
280 timer->extl_handler=fn;
282 watch_setup(&(timer->objwatch), obj, NULL);
284 watch_reset(&(timer->objwatch));
286 /* Add timerevent in place to queue */
291 if(TIMEVAL_LATER(q->when, timer->when))
304 void timer_set(WTimer *timer, uint msecs, WTimerHandler *handler,
307 timer_do_set(timer, msecs, handler, obj, extl_fn_none());
312 * Set \var{timer} to call \var{fn} in \var{msecs} milliseconds.
314 EXTL_EXPORT_AS(WTimer, set)
315 void timer_set_extl(WTimer *timer, uint msecs, ExtlFn fn)
317 timer_do_set(timer, msecs, NULL, NULL, extl_ref_fn(fn));
325 void timer_reset(WTimer *timer)
327 WTimer *q=queue, **qptr=&queue;
341 extl_unref_fn(timer->extl_handler);
342 timer->extl_handler=extl_fn_none();
343 watch_reset(&(timer->objwatch));
347 bool timer_init(WTimer *timer)
349 timer->when.tv_sec=0;
350 timer->when.tv_usec=0;
353 timer->extl_handler=extl_fn_none();
354 watch_init(&(timer->objwatch));
358 void timer_deinit(WTimer *timer)
364 WTimer *create_timer()
366 CREATEOBJ_IMPL(WTimer, timer, (p));
370 * Create a new timer.
372 EXTL_EXPORT_AS(mainloop, create_timer)
373 WTimer *create_timer_extl_owned()
375 WTimer *timer=create_timer();
377 ((Obj*)timer)->flags|=OBJ_EXTL_OWNED;
383 IMPLCLASS(WTimer, Obj, timer_deinit, NULL);
389 /*{{{ Signal handling */
392 static void fatal_signal_handler(int signal_num)
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);
401 static void deadly_signal_handler(int signal_num)
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);
413 static void chld_handler(int signal_num)
418 while((pid=waitpid(-1, NULL, WNOHANG|WUNTRACED))>0){
426 static void usr2_handler(int signal_num)
432 static void exit_handler(int signal_num)
435 warn(TR("Got signal %d while %d is still to be handled."),
436 signal_num, kill_sig);
442 static void timer_handler(int signal_num)
448 static void ignore_handler(int signal_num)
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
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)
467 void mainloop_trap_signals(const sigset_t *which)
470 sigset_t set, oldset;
479 sigemptyset(&oldset);
480 sigprocmask(SIG_SETMASK, &set, &oldset);
493 /*IGNORE(SIGWINCH);*/
495 sigemptyset(&(sa.sa_mask));
498 sa.sa_handler=timer_handler;
499 sa.sa_flags=SA_RESTART;
500 sigaction(SIGALRM, &sa, NULL);
504 sa.sa_handler=chld_handler;
505 sa.sa_flags=SA_NOCLDSTOP|SA_RESTART;
506 sigaction(SIGCHLD, &sa, NULL);
510 sa.sa_handler=usr2_handler;
511 sa.sa_flags=SA_RESTART;
512 sigaction(SIGUSR2, &sa, NULL);
516 sa.sa_handler=exit_handler;
517 sa.sa_flags=SA_RESTART;
518 sigaction(SIGTERM, &sa, NULL);
522 sa.sa_handler=exit_handler;
523 sa.sa_flags=SA_RESTART;
524 sigaction(SIGUSR1, &sa, NULL);
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.
532 sa.sa_handler=ignore_handler;
533 sigaction(SIGPIPE, &sa, NULL);