[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);
This commit is contained in:
Willy Tarreau 2010-08-27 17:56:48 +02:00
parent 08c4b79f9a
commit 24f4efa670
6 changed files with 244 additions and 161 deletions

View File

@ -1,7 +1,8 @@
/*
* include/proto/signal.h
* Asynchronous signal delivery functions.
*
* Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
* Copyright 2000-2010 Willy Tarreau <w@1wt.eu>
*
* 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:
*/

View File

@ -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

View File

@ -1,7 +1,8 @@
/*
* include/types/signal.h
* Asynchronous signal delivery functions descriptors.
*
* Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
* Copyright 2000-2010 Willy Tarreau <w@1wt.eu>
*
* 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 <signal.h>
#include <common/config.h>
#include <common/mini-clist.h>
#include <common/standard.h>
struct signal_descriptor {
int count; /* number of times raised */
void (*handler)(int sig);
/* 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 */
struct list handlers; /* sig_handler */
};
#endif /* _TYPES_SIGNAL_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -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 */

View File

@ -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 <sig> to all pids found in <oldpids>. 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 */

View File

@ -1,7 +1,7 @@
/*
* Asynchronous signal delivery functions.
*
* Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
* Copyright 2000-2010 Willy Tarreau <w@1wt.eu>
*
* 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 <proto/signal.h>
#include <proto/log.h>
#include <proto/task.h>
/* 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 <sig>. 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;
}
}
}