2 * ion/libmainloop/signal.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
10 #include <sys/types.h>
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>
28 static int kill_sig
=0;
30 static int wait_sig
=0;
33 static int usr2_sig
=0;
34 static bool had_tmr
=FALSE
;
36 WHook
*mainloop_sigchld_hook
=NULL
;
37 WHook
*mainloop_sigusr2_hook
=NULL
;
39 static sigset_t special_sigs
;
45 static WTimer
*queue
=NULL
;
48 int mainloop_gettime(struct timeval
*val
)
50 #if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK>=0)
56 ret
=clock_gettime(CLOCK_MONOTONIC
, &spec
);
58 if(ret
==-1 && errno
==EINVAL
&& checked
==0){
63 val
->tv_sec
=spec
.tv_sec
;
64 val
->tv_usec
=spec
.tv_nsec
/1000;
70 #warning "Monotonic clock unavailable; please fix your operating system."
72 return gettimeofday(val
, NULL
);
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)))
81 #define USECS_IN_SEC 1000000
84 bool libmainloop_get_timeout(struct timeval
*tv
)
89 /* Subtract queue time from current time, don't go below zero */
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
;
97 tv
->tv_usec
=queue
->when
.tv_usec
-tv
->tv_usec
;
98 tv
->tv_sec
=queue
->when
.tv_sec
-tv
->tv_sec
;
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.
106 tv
->tv_sec
+=tv
->tv_usec
/USECS_IN_SEC
;
107 tv
->tv_usec
%=USECS_IN_SEC
;
117 static void do_timer_set()
119 struct itimerval val
={{0, 0}, {0, 0}};
121 if(libmainloop_get_timeout(&val
.it_value
)){
122 val
.it_interval
.tv_usec
=0;
123 val
.it_interval
.tv_sec
=0;
125 if((setitimer(ITIMER_REAL
, &val
, NULL
)))
128 setitimer(ITIMER_REAL
, &val
, NULL
);
139 static bool mrsh_chld(void (*fn
)(pid_t
, int), ChldParams
*p
)
146 static bool mrsh_chld_extl(ExtlFn fn
, ChldParams
*p
)
148 ExtlTab t
=extl_create_table();
151 extl_table_sets_i(t
, "pid", (int)p
->pid
);
153 if(WIFEXITED(p
->code
)){
154 extl_table_sets_b(t
, "exited", TRUE
);
155 extl_table_sets_i(t
, "exitstatus", WEXITSTATUS(p
->code
));
157 if(WIFSIGNALED(p
->code
)){
158 extl_table_sets_b(t
, "signaled", TRUE
);
159 extl_table_sets_i(t
, "termsig", WTERMSIG(p
->code
));
161 extl_table_sets_i(t
, "coredump", WCOREDUMP(p
->code
));
164 if(WIFSTOPPED(p
->code
)){
165 extl_table_sets_b(t
, "stopped", TRUE
);
166 extl_table_sets_i(t
, "stopsig", WSTOPSIG(p
->code
));
168 /*if(WIFCONTINUED(p->code)){
169 extl_table_sets_b(t, "continued", TRUE);
172 ret
=extl_call(fn
, "t", NULL
, t
);
179 static bool mrsh_usr2(void (*fn
)(void), void *UNUSED(p
))
185 static bool mrsh_usr2_extl(ExtlFn fn
, void *UNUSED(p
))
188 ExtlTab t
=extl_create_table();
189 ret
=extl_call(fn
, "t", NULL
, t
);
195 bool mainloop_check_signals()
197 struct timeval current_time
;
203 if(mainloop_sigusr2_hook
!=NULL
){
204 hook_call(mainloop_sigusr2_hook
, NULL
,
205 (WHookMarshall
*)mrsh_usr2
,
206 (WHookMarshallExtl
*)mrsh_usr2_extl
);
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
);
228 /* Check for timer events in the queue */
233 mainloop_gettime(¤t_time
);
235 if(TIMEVAL_LATER(current_time
, queue
->when
)){
239 if(q
->handler
!=NULL
){
240 WTimerHandler
*handler
=q
->handler
;
241 Obj
*obj
=q
->objwatch
.obj
;
243 watch_reset(&(q
->objwatch
));
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
);
264 void mainloop_block_signals(sigset_t
*oldmask
)
266 sigprocmask(SIG_BLOCK
, &special_sigs
, oldmask
);
270 bool mainloop_unhandled_signals()
272 return (usr2_sig
|| wait_sig
|| kill_sig
|| had_tmr
);
276 static void add_to_current_time(struct timeval
*when
, uint msecs
)
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;
291 bool timer_is_set(WTimer
*timer
)
294 for(tmr
=queue
; tmr
!=NULL
; tmr
=tmr
->next
){
302 void timer_do_set(WTimer
*timer
, uint msecs
, WTimerHandler
*handler
,
309 /* Initialize the new queue timer event */
310 add_to_current_time(&(timer
->when
), msecs
);
312 timer
->handler
=handler
;
313 timer
->extl_handler
=fn
;
315 watch_setup(&(timer
->objwatch
), obj
, NULL
);
317 watch_reset(&(timer
->objwatch
));
319 /* Add timerevent in place to queue */
324 if(TIMEVAL_LATER(q
->when
, timer
->when
))
337 void timer_set(WTimer
*timer
, uint msecs
, WTimerHandler
*handler
,
340 timer_do_set(timer
, msecs
, handler
, obj
, extl_fn_none());
345 * Set \var{timer} to call \var{fn} in \var{msecs} milliseconds.
347 EXTL_EXPORT_AS(WTimer
, set
)
348 void timer_set_extl(WTimer
*timer
, uint msecs
, ExtlFn fn
)
350 timer_do_set(timer
, msecs
, NULL
, NULL
, extl_ref_fn(fn
));
358 void timer_reset(WTimer
*timer
)
360 WTimer
*q
=queue
, **qptr
=&queue
;
374 extl_unref_fn(timer
->extl_handler
);
375 timer
->extl_handler
=extl_fn_none();
376 watch_reset(&(timer
->objwatch
));
380 bool timer_init(WTimer
*timer
)
382 timer
->when
.tv_sec
=0;
383 timer
->when
.tv_usec
=0;
386 timer
->extl_handler
=extl_fn_none();
387 watch_init(&(timer
->objwatch
));
391 void timer_deinit(WTimer
*timer
)
397 WTimer
*create_timer()
399 CREATEOBJ_IMPL(WTimer
, timer
, (p
));
403 * Create a new timer.
405 EXTL_EXPORT_AS(mainloop
, create_timer
)
406 WTimer
*create_timer_extl_owned()
408 WTimer
*timer
=create_timer();
410 ((Obj
*)timer
)->flags
|=OBJ_EXTL_OWNED
;
416 IMPLCLASS(WTimer
, Obj
, timer_deinit
, NULL
);
422 /*{{{ Signal handling */
424 static void deadly_signal_handler(int signal_num
)
426 set_warn_handler(NULL
);
427 warn(TR("Caught signal %d. Dying."), signal_num
);
428 signal(signal_num
, SIG_DFL
);
429 /*if(ioncore_g.opmode==IONCORE_OPMODE_INIT)
430 kill(getpid(), signal_num);
436 static void chld_handler(int UNUSED(signal_num
))
441 while((pid
=waitpid(-1, NULL
, WNOHANG
|WUNTRACED
))>0){
449 static void usr2_handler(int UNUSED(signal_num
))
455 static void exit_handler(int signal_num
)
458 warn(TR("Got signal %d while %d is still to be handled."),
459 signal_num
, kill_sig
);
465 static void timer_handler(int UNUSED(signal_num
))
471 static void ignore_handler(int UNUSED(signal_num
))
478 /* glibc is broken (?) and does not define SA_RESTART with
479 * '-ansi -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED', so just try to live
485 #define IFTRAP(X) if(sigismember(which, X))
486 #define DEADLY(X) IFTRAP(X) signal(X, deadly_signal_handler);
487 #define IGNORE(X) IFTRAP(X) signal(X, SIG_IGN)
490 void mainloop_trap_signals(const sigset_t
*which
)
493 sigset_t set
, oldset
;
501 sigemptyset(&special_sigs
);
503 sigemptyset(&oldset
);
504 sigprocmask(SIG_SETMASK
, &set
, &oldset
);
506 /* I do not handle SIG{ILL,SEGV,FPE,BUS} since there's not much I can do in
515 /*IGNORE(SIGWINCH);*/
517 sigemptyset(&(sa
.sa_mask
));
520 sa
.sa_handler
=timer_handler
;
521 sa
.sa_flags
=SA_RESTART
;
522 sigaction(SIGALRM
, &sa
, NULL
);
523 sigaddset(&special_sigs
, SIGALRM
);
527 sa
.sa_handler
=chld_handler
;
528 sa
.sa_flags
=SA_NOCLDSTOP
|SA_RESTART
;
529 sigaction(SIGCHLD
, &sa
, NULL
);
530 sigaddset(&special_sigs
, SIGCHLD
);
534 sa
.sa_handler
=usr2_handler
;
535 sa
.sa_flags
=SA_RESTART
;
536 sigaction(SIGUSR2
, &sa
, NULL
);
537 sigaddset(&special_sigs
, SIGUSR2
);
541 sa
.sa_handler
=exit_handler
;
542 sa
.sa_flags
=SA_RESTART
;
543 sigaction(SIGTERM
, &sa
, NULL
);
544 sigaddset(&special_sigs
, SIGTERM
);
548 sa
.sa_handler
=exit_handler
;
549 sa
.sa_flags
=SA_RESTART
;
550 sigaction(SIGUSR1
, &sa
, NULL
);
553 /* SIG_IGN is preserved over execve and since the the default action
554 * for SIGPIPE is not to ignore it, some programs may get upset if
555 * the behaviour is not the default.
558 sa
.sa_handler
=ignore_handler
;
559 sigaction(SIGPIPE
, &sa
, NULL
);