From: Willy Tarreau Date: Fri, 27 Aug 2010 15:56:48 +0000 (+0200) Subject: [MEDIUM] signals: add support for registering functions and tasks X-Git-Tag: v1.5-dev8~482 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=24f4efa670c527aba819f1188b02af3d51091858;p=thirdparty%2Fhaproxy.git [MEDIUM] signals: add support for registering functions and tasks The two new functions below make it possible to register any number of functions or tasks to a system signal. They will be called in the registration order when the signal is received. struct sig_handler *signal_register_fct(int sig, void (*fct)(struct sig_handler *), int arg); struct sig_handler *signal_register_task(int sig, struct task *task, int reason); --- diff --git a/include/proto/signal.h b/include/proto/signal.h index 273482744c..5b5a64aea3 100644 --- a/include/proto/signal.h +++ b/include/proto/signal.h @@ -1,7 +1,8 @@ /* + * include/proto/signal.h * Asynchronous signal delivery functions. * - * Copyright 2000-2009 Willy Tarreau + * Copyright 2000-2010 Willy Tarreau * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -16,14 +17,25 @@ extern int signal_queue_len; extern struct signal_descriptor signal_state[]; +extern struct pool_head *pool2_sig_handlers; -void signal_init(); -void signal_handler(int sig); -void signal_register(int sig, void (*handler)(int)); void __signal_process_queue(); +int signal_init(); +void deinit_signals(); +struct sig_handler *signal_register_fct(int sig, void (*fct)(struct sig_handler *), int arg); +struct sig_handler *signal_register_task(int sig, struct task *task, int reason); +void signal_unregister_handler(struct sig_handler *handler); +void signal_unregister_target(int sig, void *target); static inline void signal_process_queue() { if (unlikely(signal_queue_len > 0)) __signal_process_queue(); } + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + */ diff --git a/include/proto/task.h b/include/proto/task.h index 0cfe28f209..2c02ca6ef5 100644 --- a/include/proto/task.h +++ b/include/proto/task.h @@ -1,23 +1,23 @@ /* - include/proto/task.h - Functions for task management. - - Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation, version 2.1 - exclusively. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ + * include/proto/task.h + * Functions for task management. + * + * Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ #ifndef _PROTO_TASK_H #define _PROTO_TASK_H diff --git a/include/types/signal.h b/include/types/signal.h index b863d23b55..598001eeb6 100644 --- a/include/types/signal.h +++ b/include/types/signal.h @@ -1,7 +1,8 @@ /* + * include/types/signal.h * Asynchronous signal delivery functions descriptors. * - * Copyright 2000-2009 Willy Tarreau + * Copyright 2000-2010 Willy Tarreau * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -10,11 +11,39 @@ * */ +#ifndef _TYPES_SIGNAL_H +#define _TYPES_SIGNAL_H + + #include #include +#include #include +/* flags for -> flags */ +#define SIG_F_ONE_SHOOT 0x0001 /* unregister handler before calling it */ +#define SIG_F_TYPE_FCT 0x0002 /* handler is a function + arg */ +#define SIG_F_TYPE_TASK 0x0004 /* handler is a task + reason */ + +/* those are highly dynamic and stored in pools */ +struct sig_handler { + struct list list; + void *handler; /* function to call or task to wake up */ + int arg; /* arg to pass to function, or signals*/ + int flags; /* SIG_F_* */ +}; + +/* one per signal */ struct signal_descriptor { - int count; /* number of times raised */ - void (*handler)(int sig); + int count; /* number of times raised */ + struct list handlers; /* sig_handler */ }; + +#endif /* _TYPES_SIGNAL_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + */ diff --git a/include/types/task.h b/include/types/task.h index 41f889c88c..405404bd0d 100644 --- a/include/types/task.h +++ b/include/types/task.h @@ -1,23 +1,23 @@ /* - include/types/task.h - Macros, variables and structures for task management. - - Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation, version 2.1 - exclusively. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ + * include/types/task.h + * Macros, variables and structures for task management. + * + * Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ #ifndef _TYPES_TASK_H #define _TYPES_TASK_H @@ -44,6 +44,12 @@ TASK_WOKEN_IO|TASK_WOKEN_SIGNAL|TASK_WOKEN_MSG| \ TASK_WOKEN_RES) +/* Additional wakeup info may be passed in the state by lef-shifting the value + * by this number of bits. Not more than 8 bits are guaranteed to be delivered. + * System signals may use that too. + */ +#define TASK_REASON_SHIFT 8 + /* The base for all tasks */ struct task { struct eb32_node wq; /* ebtree node used to hold the task in the wait queue */ diff --git a/src/haproxy.c b/src/haproxy.c index ece95264c7..d9c3b6a4e9 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -246,17 +246,17 @@ void usage(char *name) /* * upon SIGUSR1, let's have a soft stop. */ -void sig_soft_stop(int sig) +void sig_soft_stop(struct sig_handler *sh) { soft_stop(); + signal_unregister_handler(sh); pool_gc2(); - signal_register(sig, SIG_IGN); } /* * upon SIGTTOU, we pause everything */ -void sig_pause(int sig) +void sig_pause(struct sig_handler *sh) { pause_proxies(); pool_gc2(); @@ -265,7 +265,7 @@ void sig_pause(int sig) /* * upon SIGTTIN, let's have a soft stop. */ -void sig_listen(int sig) +void sig_listen(struct sig_handler *sh) { listen_proxies(); } @@ -273,7 +273,7 @@ void sig_listen(int sig) /* * this function dumps every server's state when the process receives SIGHUP. */ -void sig_dump_state(int sig) +void sig_dump_state(struct sig_handler *sh) { struct proxy *p = proxy; @@ -319,70 +319,13 @@ void sig_dump_state(int sig) } } -void dump(int sig) +void dump(struct sig_handler *sh) { -#if 0 - struct task *t; - struct session *s; - struct rb_node *node; - - for(node = rb_first(&wait_queue[0]); - node != NULL; node = rb_next(node)) { - t = rb_entry(node, struct task, rb_node); - s = t->context; - qfprintf(stderr,"[dump] wq: task %p, still %ld ms, " - "cli=%d, srv=%d, req=%d, rep=%d\n", - s, tv_ms_remain(&now, &t->expire), - s->si[0].state, - s->si[1].state, - s->req->l, s->rep?s->rep->l:0); - } -#endif /* dump memory usage then free everything possible */ dump_pools(); pool_gc2(); } -#ifdef DEBUG_MEMORY -static void fast_stop(void) -{ - struct proxy *p; - p = proxy; - while (p) { - p->grace = 0; - p = p->next; - } - soft_stop(); -} - -void sig_int(int sig) -{ - /* This would normally be a hard stop, - but we want to be sure about deallocation, - and so on, so we do a soft stop with - 0 GRACE time - */ - fast_stop(); - pool_gc2(); - /* If we are killed twice, we decide to die */ - signal_register(sig, SIG_DFL); -} - -void sig_term(int sig) -{ - /* This would normally be a hard stop, - but we want to be sure about deallocation, - and so on, so we do a soft stop with - 0 GRACE time - */ - fast_stop(); - pool_gc2(); - /* If we are killed twice, we decide to die */ - signal_register(sig, SIG_DFL); -} -#endif - - /* * This function initializes all the necessary variables. It only returns * if everything is OK. If something fails, it exits. @@ -726,6 +669,7 @@ void deinit(void) struct uri_auth *uap, *ua = NULL; int i; + deinit_signals(); while (p) { free(p->id); free(p->check_req); @@ -924,6 +868,7 @@ void deinit(void) pool_destroy2(pool2_capture); pool_destroy2(pool2_appsess); pool_destroy2(pool2_pendconn); + pool_destroy2(pool2_sig_handlers); if (have_appsession) { pool_destroy2(apools.serverid); @@ -931,7 +876,6 @@ void deinit(void) } deinit_pollers(); - } /* end deinit() */ /* sends the signal to all pids found in . Returns the number of @@ -991,19 +935,15 @@ int main(int argc, char **argv) FILE *pidfile = NULL; init(argc, argv); - signal_register(SIGQUIT, dump); - signal_register(SIGUSR1, sig_soft_stop); - signal_register(SIGHUP, sig_dump_state); -#ifdef DEBUG_MEMORY - signal_register(SIGINT, sig_int); - signal_register(SIGTERM, sig_term); -#endif + signal_register_fct(SIGQUIT, dump, SIGQUIT); + signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1); + signal_register_fct(SIGHUP, sig_dump_state, SIGHUP); /* Always catch SIGPIPE even on platforms which define MSG_NOSIGNAL. * Some recent FreeBSD setups report broken pipes, and MSG_NOSIGNAL * was defined there, so let's stay on the safe side. */ - signal(SIGPIPE, SIG_IGN); + signal_register_fct(SIGPIPE, NULL, 0); /* We will loop at most 100 times with 10 ms delay each time. * That's at most 1 second. We only send a signal to old pids @@ -1061,8 +1001,8 @@ int main(int argc, char **argv) } /* prepare pause/play signals */ - signal_register(SIGTTOU, sig_pause); - signal_register(SIGTTIN, sig_listen); + signal_register_fct(SIGTTOU, sig_pause, SIGTTOU); + signal_register_fct(SIGTTIN, sig_listen, SIGTTIN); /* MODE_QUIET can inhibit alerts and warnings below this line */ diff --git a/src/signal.c b/src/signal.c index 9825c0d2ed..746ae9e0c3 100644 --- a/src/signal.c +++ b/src/signal.c @@ -1,7 +1,7 @@ /* * Asynchronous signal delivery functions. * - * Copyright 2000-2009 Willy Tarreau + * Copyright 2000-2010 Willy Tarreau * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -15,6 +15,7 @@ #include #include +#include /* Principle : we keep an in-order list of the first occurrence of all received * signals. All occurrences of a same signal are grouped though. The signal @@ -26,22 +27,16 @@ int signal_queue_len; /* length of signal queue, <= MAX_SIGNAL (1 entry per signal max) */ int signal_queue[MAX_SIGNAL]; /* in-order queue of received signals */ struct signal_descriptor signal_state[MAX_SIGNAL]; +struct pool_head *pool2_sig_handlers = NULL; sigset_t blocked_sig; -void signal_init() +/* Common signal handler, used by all signals. Received signals are queued. */ +static void signal_handler(int sig) { - signal_queue_len = 0; - memset(signal_queue, 0, sizeof(signal_queue)); - memset(signal_state, 0, sizeof(signal_state)); - sigfillset(&blocked_sig); -} - -void signal_handler(int sig) -{ - if (sig < 0 || sig > MAX_SIGNAL || !signal_state[sig].handler) { + if (sig < 0 || sig > MAX_SIGNAL) { /* unhandled signal */ - qfprintf(stderr, "Received unhandled signal %d. Signal has been disabled.\n", sig); signal(sig, SIG_IGN); + qfprintf(stderr, "Received unhandled signal %d. Signal has been disabled.\n", sig); return; } @@ -56,33 +51,6 @@ void signal_handler(int sig) signal(sig, signal_handler); /* re-arm signal */ } -/* Register a handler for signal . Set it to NULL, SIG_DFL or SIG_IGN to - * remove the handler. The signal's queue is flushed and the signal is really - * registered (or unregistered) for the process. The interface is the same as - * for standard signal delivery, except that the handler does not need to rearm - * the signal itself (it can disable it however). - */ -void signal_register(int sig, void (*handler)(int)) -{ - if (sig < 0 || sig > MAX_SIGNAL) { - qfprintf(stderr, "Failed to register signal %d : out of range [0..%d].\n", sig, MAX_SIGNAL); - return; - } - - signal_state[sig].count = 0; - if (handler == NULL) - handler = SIG_IGN; - - if (handler != SIG_IGN && handler != SIG_DFL) { - signal_state[sig].handler = handler; - signal(sig, signal_handler); - } - else { - signal_state[sig].handler = NULL; - signal(sig, handler); - } -} - /* Call handlers of all pending signals and clear counts and queue length. The * handlers may unregister themselves by calling signal_register() while they * are called, just like it is done with normal signal handlers. @@ -102,8 +70,13 @@ void __signal_process_queue() sig = signal_queue[cur_pos]; desc = &signal_state[sig]; if (desc->count) { - if (desc->handler) - desc->handler(sig); + struct sig_handler *sh, *shb; + list_for_each_entry_safe(sh, shb, &desc->handlers, list) { + if ((sh->flags & SIG_F_TYPE_FCT) && sh->handler) + ((void (*)(struct sig_handler *))sh->handler)(sh); + else if ((sh->flags & SIG_F_TYPE_TASK) && sh->handler) + task_wakeup(sh->handler, sh->arg | TASK_WOKEN_SIGNAL); + } desc->count = 0; } } @@ -112,3 +85,126 @@ void __signal_process_queue() /* restore signal delivery */ sigprocmask(SIG_SETMASK, &old_sig, NULL); } + +/* perform minimal intializations, report 0 in case of error, 1 if OK. */ +int signal_init() +{ + int sig; + + signal_queue_len = 0; + memset(signal_queue, 0, sizeof(signal_queue)); + memset(signal_state, 0, sizeof(signal_state)); + sigfillset(&blocked_sig); + for (sig = 0; sig < MAX_SIGNAL; sig++) + LIST_INIT(&signal_state[sig].handlers); + + pool2_sig_handlers = create_pool("sig_handlers", sizeof(struct sig_handler), MEM_F_SHARED); + return pool2_sig_handlers != NULL; +} + +/* releases all registered signal handlers */ +void deinit_signals() +{ + int sig; + struct sig_handler *sh, *shb; + + for (sig = 0; sig < MAX_SIGNAL; sig++) { + signal(sig, SIG_DFL); + list_for_each_entry_safe(sh, shb, &signal_state[sig].handlers, list) { + LIST_DEL(&sh->list); + pool_free2(pool2_sig_handlers, sh); + } + } +} + +/* Register a function and an integer argument on a signal. A pointer to the + * newly allocated sig_handler is returned, or NULL in case of any error. The + * caller is responsible for unregistering the function when not used anymore. + * Note that passing a NULL as the function pointer enables interception of the + * signal without processing, which is identical to SIG_IGN. + */ +struct sig_handler *signal_register_fct(int sig, void (*fct)(struct sig_handler *), int arg) +{ + struct sig_handler *sh; + + if (sig < 0 || sig > MAX_SIGNAL) + return NULL; + + signal(sig, signal_handler); + + if (!fct) + return NULL; + + sh = pool_alloc2(pool2_sig_handlers); + if (!sh) + return NULL; + + sh->handler = fct; + sh->arg = arg; + sh->flags = SIG_F_TYPE_FCT; + LIST_ADDQ(&signal_state[sig].handlers, &sh->list); + return sh; +} + +/* Register a task and a wake-up reason on a signal. A pointer to the newly + * allocated sig_handler is returned, or NULL in case of any error. The caller + * is responsible for unregistering the task when not used anymore. Note that + * passing a NULL as the task pointer enables interception of the signal + * without processing, which is identical to SIG_IGN. + */ +struct sig_handler *signal_register_task(int sig, struct task *task, int reason) +{ + struct sig_handler *sh; + + if (sig < 0 || sig > MAX_SIGNAL) + return NULL; + + signal(sig, signal_handler); + + if (!task) + return NULL; + + sh = pool_alloc2(pool2_sig_handlers); + if (!sh) + return NULL; + + sh->handler = task; + sh->arg = reason & ~TASK_WOKEN_ANY; + sh->flags = SIG_F_TYPE_TASK; + LIST_ADDQ(&signal_state[sig].handlers, &sh->list); + return sh; +} + +/* Immediately unregister a handler so that no further signals may be delivered + * to it. The struct is released so the caller may not reference it anymore. + */ +void signal_unregister_handler(struct sig_handler *handler) +{ + LIST_DEL(&handler->list); + pool_free2(pool2_sig_handlers, handler); +} + +/* Immediately unregister a handler so that no further signals may be delivered + * to it. The handler struct does not need to be known, only the function or + * task pointer. This method is expensive because it scans all the list, so it + * should only be used for rare cases (eg: exit). The struct is released so the + * caller may not reference it anymore. + */ +void signal_unregister_target(int sig, void *target) +{ + struct sig_handler *sh, *shb; + + if (sig < 0 || sig > MAX_SIGNAL) + return; + + if (!target) + return; + + list_for_each_entry_safe(sh, shb, &signal_state[sig].handlers, list) { + if (sh->handler == target) { + LIST_DEL(&sh->list); + pool_free2(pool2_sig_handlers, sh); + break; + } + } +}