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 *p
)
185 static bool mrsh_usr2_extl(ExtlFn fn
, void *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
,
310 /* Initialize the new queue timer event */
311 add_to_current_time(&(timer
->when
), msecs
);
313 timer
->handler
=handler
;
314 timer
->extl_handler
=fn
;
316 watch_setup(&(timer
->objwatch
), obj
, NULL
);
318 watch_reset(&(timer
->objwatch
));
320 /* Add timerevent in place to queue */
325 if(TIMEVAL_LATER(q
->when
, timer
->when
))
338 void timer_set(WTimer
*timer
, uint msecs
, WTimerHandler
*handler
,
341 timer_do_set(timer
, msecs
, handler
, obj
, extl_fn_none());
346 * Set \var{timer} to call \var{fn} in \var{msecs} milliseconds.
348 EXTL_EXPORT_AS(WTimer
, set
)
349 void timer_set_extl(WTimer
*timer
, uint msecs
, ExtlFn fn
)
351 timer_do_set(timer
, msecs
, NULL
, NULL
, extl_ref_fn(fn
));
359 void timer_reset(WTimer
*timer
)
361 WTimer
*q
=queue
, **qptr
=&queue
;
375 extl_unref_fn(timer
->extl_handler
);
376 timer
->extl_handler
=extl_fn_none();
377 watch_reset(&(timer
->objwatch
));
381 bool timer_init(WTimer
*timer
)
383 timer
->when
.tv_sec
=0;
384 timer
->when
.tv_usec
=0;
387 timer
->extl_handler
=extl_fn_none();
388 watch_init(&(timer
->objwatch
));
392 void timer_deinit(WTimer
*timer
)
398 WTimer
*create_timer()
400 CREATEOBJ_IMPL(WTimer
, timer
, (p
));
404 * Create a new timer.
406 EXTL_EXPORT_AS(mainloop
, create_timer
)
407 WTimer
*create_timer_extl_owned()
409 WTimer
*timer
=create_timer();
411 ((Obj
*)timer
)->flags
|=OBJ_EXTL_OWNED
;
417 IMPLCLASS(WTimer
, Obj
, timer_deinit
, NULL
);
423 /*{{{ Signal handling */
426 static void fatal_signal_handler(int signal_num
)
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
);
435 static void deadly_signal_handler(int signal_num
)
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);
447 static void chld_handler(int signal_num
)
452 while((pid
=waitpid(-1, NULL
, WNOHANG
|WUNTRACED
))>0){
460 static void usr2_handler(int signal_num
)
466 static void exit_handler(int signal_num
)
469 warn(TR("Got signal %d while %d is still to be handled."),
470 signal_num
, kill_sig
);
476 static void timer_handler(int signal_num
)
482 static void ignore_handler(int signal_num
)
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
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)
502 void mainloop_trap_signals(const sigset_t
*which
)
505 sigset_t set
, oldset
;
513 sigemptyset(&special_sigs
);
515 sigemptyset(&oldset
);
516 sigprocmask(SIG_SETMASK
, &set
, &oldset
);
529 /*IGNORE(SIGWINCH);*/
531 sigemptyset(&(sa
.sa_mask
));
534 sa
.sa_handler
=timer_handler
;
535 sa
.sa_flags
=SA_RESTART
;
536 sigaction(SIGALRM
, &sa
, NULL
);
537 sigaddset(&special_sigs
, SIGALRM
);
541 sa
.sa_handler
=chld_handler
;
542 sa
.sa_flags
=SA_NOCLDSTOP
|SA_RESTART
;
543 sigaction(SIGCHLD
, &sa
, NULL
);
544 sigaddset(&special_sigs
, SIGCHLD
);
548 sa
.sa_handler
=usr2_handler
;
549 sa
.sa_flags
=SA_RESTART
;
550 sigaction(SIGUSR2
, &sa
, NULL
);
551 sigaddset(&special_sigs
, SIGUSR2
);
555 sa
.sa_handler
=exit_handler
;
556 sa
.sa_flags
=SA_RESTART
;
557 sigaction(SIGTERM
, &sa
, NULL
);
558 sigaddset(&special_sigs
, SIGTERM
);
562 sa
.sa_handler
=exit_handler
;
563 sa
.sa_flags
=SA_RESTART
;
564 sigaction(SIGUSR1
, &sa
, NULL
);
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.
572 sa
.sa_handler
=ignore_handler
;
573 sigaction(SIGPIPE
, &sa
, NULL
);