Merge branch 'contrib_goto_focus'
[notion.git] / libmainloop / signal.c
blob76b70bba0f4eb32a804851bf1f2a046dd8d3f85b
1 /*
2 * ion/libmainloop/signal.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
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>
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>
25 #include "signal.h"
26 #include "hooks.h"
28 static int kill_sig=0;
29 #if 1
30 static int wait_sig=0;
31 #endif
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;
42 /*{{{ Timers */
45 static WTimer *queue=NULL;
48 int mainloop_gettime(struct timeval *val)
50 #if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK>=0)
51 struct timespec spec;
52 int ret;
53 static int checked=0;
55 if(checked>=0){
56 ret=clock_gettime(CLOCK_MONOTONIC, &spec);
58 if(ret==-1 && errno==EINVAL && checked==0){
59 checked=-1;
60 }else{
61 checked=1;
63 val->tv_sec=spec.tv_sec;
64 val->tv_usec=spec.tv_nsec/1000;
66 return ret;
69 #else
70 #warning "Monotonic clock unavailable; please fix your operating system."
71 #endif
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)
86 if(queue==NULL)
87 return FALSE;
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;
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;
108 }else{
109 had_tmr=TRUE;
110 return FALSE;
113 return TRUE;
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)))
126 had_tmr=TRUE;
127 }else if(!had_tmr){
128 setitimer(ITIMER_REAL, &val, NULL);
133 typedef struct{
134 pid_t pid;
135 int code;
136 } ChldParams;
139 static bool mrsh_chld(void (*fn)(pid_t, int), ChldParams *p)
141 fn(p->pid, p->code);
142 return TRUE;
146 static bool mrsh_chld_extl(ExtlFn fn, ChldParams *p)
148 ExtlTab t=extl_create_table();
149 bool ret;
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));
160 #ifdef WCOREDUMP
161 extl_table_sets_i(t, "coredump", WCOREDUMP(p->code));
162 #endif
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);
174 extl_unref_table(t);
176 return ret;
179 static bool mrsh_usr2(void (*fn)(void), void *UNUSED(p))
181 fn();
182 return TRUE;
185 static bool mrsh_usr2_extl(ExtlFn fn, void *UNUSED(p))
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;
195 bool mainloop_check_signals()
197 struct timeval current_time;
198 WTimer *q;
199 int ret=0;
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);
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);
223 #endif
225 if(kill_sig!=0)
226 return kill_sig;
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);
253 }else{
254 break;
257 do_timer_set();
260 return ret;
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)
278 long tmp_usec;
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;
287 /*EXTL_DOC
288 * Is timer set?
290 EXTL_EXPORT_MEMBER
291 bool timer_is_set(WTimer *timer)
293 WTimer *tmr;
294 for(tmr=queue; tmr!=NULL; tmr=tmr->next){
295 if(tmr==timer)
296 return TRUE;
298 return FALSE;
302 void timer_do_set(WTimer *timer, uint msecs, WTimerHandler *handler,
303 Obj *obj, ExtlFn fn)
305 WTimer *q, **qptr;
307 timer_reset(timer);
309 /* Initialize the new queue timer event */
310 add_to_current_time(&(timer->when), msecs);
311 timer->next=NULL;
312 timer->handler=handler;
313 timer->extl_handler=fn;
314 if(obj!=NULL)
315 watch_setup(&(timer->objwatch), obj, NULL);
316 else
317 watch_reset(&(timer->objwatch));
319 /* Add timerevent in place to queue */
320 q=queue;
321 qptr=&queue;
323 while(q!=NULL){
324 if(TIMEVAL_LATER(q->when, timer->when))
325 break;
326 qptr=&(q->next);
327 q=q->next;
330 timer->next=q;
331 *qptr=timer;
333 do_timer_set();
337 void timer_set(WTimer *timer, uint msecs, WTimerHandler *handler,
338 Obj *obj)
340 timer_do_set(timer, msecs, handler, obj, extl_fn_none());
344 /*EXTL_DOC
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));
354 /*EXTL_DOC
355 * Reset timer.
357 EXTL_EXPORT_MEMBER
358 void timer_reset(WTimer *timer)
360 WTimer *q=queue, **qptr=&queue;
362 while(q!=NULL){
363 if(q==timer){
364 *qptr=timer->next;
365 do_timer_set();
366 break;
368 qptr=&(q->next);
369 q=q->next;
373 timer->handler=NULL;
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;
384 timer->next=NULL;
385 timer->handler=NULL;
386 timer->extl_handler=extl_fn_none();
387 watch_init(&(timer->objwatch));
388 return TRUE;
391 void timer_deinit(WTimer *timer)
393 timer_reset(timer);
397 WTimer *create_timer()
399 CREATEOBJ_IMPL(WTimer, timer, (p));
402 /*EXTL_DOC
403 * Create a new timer.
405 EXTL_EXPORT_AS(mainloop, create_timer)
406 WTimer *create_timer_extl_owned()
408 WTimer *timer=create_timer();
409 if(timer!=NULL)
410 ((Obj*)timer)->flags|=OBJ_EXTL_OWNED;
411 return timer;
415 EXTL_EXPORT
416 IMPLCLASS(WTimer, Obj, timer_deinit, NULL);
419 /*}}}*/
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);
431 else*/
432 kill_sig=signal_num;
436 static void chld_handler(int UNUSED(signal_num))
438 #if 0
439 pid_t pid;
441 while((pid=waitpid(-1, NULL, WNOHANG|WUNTRACED))>0){
442 /* nothing */
444 #else
445 wait_sig=1;
446 #endif
449 static void usr2_handler(int UNUSED(signal_num))
451 usr2_sig=1;
455 static void exit_handler(int signal_num)
457 if(kill_sig>0){
458 warn(TR("Got signal %d while %d is still to be handled."),
459 signal_num, kill_sig);
461 kill_sig=signal_num;
465 static void timer_handler(int UNUSED(signal_num))
467 had_tmr=TRUE;
471 static void ignore_handler(int UNUSED(signal_num))
477 #ifndef SA_RESTART
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
480 * without it.
482 #define SA_RESTART 0
483 #endif
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)
492 struct sigaction sa;
493 sigset_t set, oldset;
494 sigset_t dummy;
496 if(which==NULL){
497 sigfillset(&dummy);
498 which=&dummy;
501 sigemptyset(&special_sigs);
502 sigemptyset(&set);
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
507 * response */
509 DEADLY(SIGHUP);
510 DEADLY(SIGQUIT);
511 DEADLY(SIGINT);
512 DEADLY(SIGABRT);
514 IGNORE(SIGTRAP);
515 /*IGNORE(SIGWINCH);*/
517 sigemptyset(&(sa.sa_mask));
519 IFTRAP(SIGALRM){
520 sa.sa_handler=timer_handler;
521 sa.sa_flags=SA_RESTART;
522 sigaction(SIGALRM, &sa, NULL);
523 sigaddset(&special_sigs, SIGALRM);
526 IFTRAP(SIGCHLD){
527 sa.sa_handler=chld_handler;
528 sa.sa_flags=SA_NOCLDSTOP|SA_RESTART;
529 sigaction(SIGCHLD, &sa, NULL);
530 sigaddset(&special_sigs, SIGCHLD);
533 IFTRAP(SIGUSR2){
534 sa.sa_handler=usr2_handler;
535 sa.sa_flags=SA_RESTART;
536 sigaction(SIGUSR2, &sa, NULL);
537 sigaddset(&special_sigs, SIGUSR2);
540 IFTRAP(SIGTERM){
541 sa.sa_handler=exit_handler;
542 sa.sa_flags=SA_RESTART;
543 sigaction(SIGTERM, &sa, NULL);
544 sigaddset(&special_sigs, SIGTERM);
547 IFTRAP(SIGUSR1){
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.
557 IFTRAP(SIGPIPE){
558 sa.sa_handler=ignore_handler;
559 sigaction(SIGPIPE, &sa, NULL);
564 #undef IGNORE
565 #undef DEADLY
568 /*}}}*/