changeset 235:8773740401a5

Centralized signal handlers management in the library
author Sebastien Decugis <sdecugis@nict.go.jp>
date Fri, 05 Mar 2010 19:01:48 +0900
parents f91fe0b85928
children 60f34df3e025
files contrib/signalent.h.sh extensions/test_app/CMakeLists.txt extensions/test_app/ta_cli.c extensions/test_app/ta_sig.c extensions/test_app/test_app.c extensions/test_app/test_app.h freeDiameter/main.c freeDiameter/tests/tests.h include/freeDiameter/libfreeDiameter.h libfreeDiameter/CMakeLists.txt libfreeDiameter/init.c libfreeDiameter/libfD.h libfreeDiameter/sessions.c libfreeDiameter/signal.c
diffstat 14 files changed, 388 insertions(+), 136 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/signalent.h.sh	Fri Mar 05 19:01:48 2010 +0900
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# The goal here is to regenerate a file signalent.h if it is missing. This file contains only a list of the signals abbreviations.
+
+# This script needs to be improved...
+cpp -dM /usr/include/signal.h \
+	| tr -s '\t ' '  '  \
+	| sed 's:#define \(SIG[A-Z]\+[0-9]*\) \([0-9]\+\):\2 \1:p;d' \
+	| sort -n --unique
--- a/extensions/test_app/CMakeLists.txt	Thu Mar 04 17:32:00 2010 +0900
+++ b/extensions/test_app/CMakeLists.txt	Fri Mar 05 19:01:48 2010 +0900
@@ -13,7 +13,6 @@
 	lex.ta_conf.c
 	ta_conf.tab.c
 	ta_conf.tab.h
-	ta_sig.c
 	ta_dict.c
 	ta_cli.c
 	ta_serv.c
--- a/extensions/test_app/ta_cli.c	Thu Mar 04 17:32:00 2010 +0900
+++ b/extensions/test_app/ta_cli.c	Fri Mar 05 19:01:48 2010 +0900
@@ -135,7 +135,7 @@
 }
 
 /* Create a test message */
-static void ta_cli_test_message(void)
+static void ta_cli_test_message(int sig)
 {
 	struct msg * req = NULL;
 	struct avp * avp;
@@ -235,16 +235,16 @@
 {
 	CHECK_FCT( fd_sess_handler_create(&ta_cli_reg, free) );
 	
-	CHECK_FCT( ta_sig_init(ta_cli_test_message) );
+	CHECK_FCT( fd_sig_register(ta_conf->signal, "test_app.cli", ta_cli_test_message ) );
 	
 	return 0;
 }
 
 void ta_cli_fini(void)
 {
-	ta_sig_fini();
+	CHECK_FCT_DO( fd_sig_unregister(ta_conf->signal), /* continue */ );
 	
-	(void) fd_sess_handler_destroy(&ta_cli_reg);
+	CHECK_FCT_DO( fd_sess_handler_destroy(&ta_cli_reg), /* continue */ );
 	
 	return;
 };
--- a/extensions/test_app/ta_sig.c	Thu Mar 04 17:32:00 2010 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/*********************************************************************************************************
-* Software License Agreement (BSD License)                                                               *
-* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
-*													 *
-* Copyright (c) 2009, WIDE Project and NICT								 *
-* All rights reserved.											 *
-* 													 *
-* Redistribution and use of this software in source and binary forms, with or without modification, are  *
-* permitted provided that the following conditions are met:						 *
-* 													 *
-* * Redistributions of source code must retain the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer.										 *
-*    													 *
-* * Redistributions in binary form must reproduce the above 						 *
-*   copyright notice, this list of conditions and the 							 *
-*   following disclaimer in the documentation and/or other						 *
-*   materials provided with the distribution.								 *
-* 													 *
-* * Neither the name of the WIDE Project or NICT nor the 						 *
-*   names of its contributors may be used to endorse or 						 *
-*   promote products derived from this software without 						 *
-*   specific prior written permission of WIDE Project and 						 *
-*   NICT.												 *
-* 													 *
-* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
-* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
-* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
-* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
-* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
-* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
-* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
-* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
-*********************************************************************************************************/
-
-/* Install the signal handler */
-
-#include "test_app.h"
-
-static pthread_t th = (pthread_t) NULL;
-
-static void * ta_sig_th(void * arg)
-{
-	int sig;
-	int ret;
-	sigset_t ss;
-	void (*cb)(void) = arg;
-	
-	fd_log_threadname ( "app_test signal handler" );
-	
-	sigemptyset(&ss);
-	sigaddset(&ss, ta_conf->signal);
-	
-	while (1) {
-		ret = sigwait(&ss, &sig);
-		if (ret != 0) {
-			fd_log_debug("sigwait failed in the app_test extension: %s", strerror(errno));
-			break;
-		}
-		
-		TRACE_DEBUG(FULL, "Signal caught, calling function...");
-		
-		/* Call the cb function */
-		(*cb)();
-		
-	}
-	
-	return NULL;
-}
-
-int ta_sig_init(void (*cb)(void))
-{
-	return pthread_create( &th, NULL, ta_sig_th, (void *)cb );
-}
-
-void ta_sig_fini(void)
-{
-	void * th_ret = NULL;
-	
-	if (th != (pthread_t) NULL) {
-		(void) pthread_cancel(th);
-		(void) pthread_join(th, &th_ret);
-	}
-	
-	return;
-}
--- a/extensions/test_app/test_app.c	Thu Mar 04 17:32:00 2010 +0900
+++ b/extensions/test_app/test_app.c	Fri Mar 05 19:01:48 2010 +0900
@@ -38,7 +38,6 @@
  */
 
 #include "test_app.h"
-#include <signal.h>
 
 /* Initialize the configuration */
 struct ta_conf * ta_conf = NULL;
--- a/extensions/test_app/test_app.h	Thu Mar 04 17:32:00 2010 +0900
+++ b/extensions/test_app/test_app.h	Fri Mar 05 19:01:48 2010 +0900
@@ -41,6 +41,7 @@
  */
  
 #include <freeDiameter/extension.h>
+#include <signal.h>
 
 #ifndef TEST_APP_DEFAULT_SIGNAL
 #define TEST_APP_DEFAULT_SIGNAL	SIGUSR1
@@ -68,10 +69,6 @@
 /* Parse the configuration file */
 int ta_conf_handle(char * conffile);
 
-/* Start or stop the signal handler */
-int ta_sig_init(void (*cb)(void));
-void ta_sig_fini(void);
-
 /* Handle incoming messages (server) */
 int ta_serv_init(void);
 void ta_serv_fini(void);
--- a/freeDiameter/main.c	Thu Mar 04 17:32:00 2010 +0900
+++ b/freeDiameter/main.c	Fri Mar 05 19:01:48 2010 +0900
@@ -41,7 +41,7 @@
 #include <gcrypt.h>
 
 /* forward declarations */
-static void * sig_hdl(void * arg);
+static void fd_shutdown(int signal);
 static int main_cmdline(int argc, char *argv[]);
 static void main_version(void);
 static void main_help( void );
@@ -57,17 +57,11 @@
 int main(int argc, char * argv[])
 {
 	int ret;
-	pthread_t sig_th;
-	sigset_t sig_all;
 	
 	memset(fd_g_config, 0, sizeof(struct fd_config));
 	
-	/* Block all signals */
-	sigfillset(&sig_all);
-	CHECK_POSIX(  pthread_sigmask(SIG_BLOCK, &sig_all, NULL)  );
-	
 	/* Initialize the library -- must come first since it initializes the debug facility */
-	CHECK_FCT( fd_lib_init() );
+	CHECK_FCT( fd_lib_init(1) );
 	TRACE_DEBUG(INFO, "libfreeDiameter initialized.");
 	
 	/* Name this thread */
@@ -90,8 +84,9 @@
 	/* Parse the command-line */
 	CHECK_FCT(  main_cmdline(argc, argv)  );
 	
-	/* Allow SIGINT and SIGTERM from this point */
-	CHECK_POSIX(  pthread_create(&sig_th, NULL, sig_hdl, NULL)  );
+	/* Allow SIGINT and SIGTERM from this point to terminate the application */
+	CHECK_FCT( fd_sig_register(SIGINT,  "freeDiameter.main", fd_shutdown) );
+	CHECK_FCT( fd_sig_register(SIGTERM, "freeDiameter.main", fd_shutdown) );
 	
 	/* Add definitions of the base protocol */
 	CHECK_FCT( fd_dict_base_protocol(fd_g_config->cnf_dict) );
@@ -109,6 +104,7 @@
 	CHECK_FCT(  fd_ext_load()  );
 	
 	fd_conf_dump();
+	fd_sig_dump(FULL, 1);
 	
 	/* Start the servers */
 	CHECK_FCT( fd_servers_start() );
@@ -169,11 +165,12 @@
 	CHECK_FCT_DO( fd_ext_fini(), /* Cleanup all extensions */ );
 	CHECK_FCT_DO( fd_rtdisp_cleanup(), /* destroy remaining handlers */ );
 	
-	CHECK_FCT_DO( fd_thr_term(&sig_th), /* reclaim resources of the signal thread */ );
-	
 	GNUTLS_TRACE( gnutls_global_deinit() );
 	
 	fd_log_debug(FD_PROJECT_BINARY " daemon is terminated.\n");
+	
+	fd_lib_fini();
+	
 	return ret;
 }
 
@@ -318,33 +315,14 @@
   		"  -q, --quiet            Decrease verbosity then remove debug messages\n");
 }
 
-#ifdef HAVE_SIGNALENT_H
-const char *const signalstr[] = {
-# include "signalent.h"
-};
-const int nsignalstr = sizeof signalstr / sizeof signalstr[0];
-# define SIGNALSTR(sig) (((sig) < nsignalstr) ? signalstr[(sig)] : "unknown")
-#else /* HAVE_SIGNALENT_H */
-# define SIGNALSTR(sig) ("")
-#endif /* HAVE_SIGNALENT_H */
-
-/* signal handler */
-static void * sig_hdl(void * arg)
+/* Terminate the application */
+static void fd_shutdown(int signal)
 {
-	sigset_t sig_main;
-	int sig = 0;
-	
-	TRACE_ENTRY();
-	fd_log_threadname("Main signal handler");
+	TRACE_ENTRY("%d", signal);
 	
-	sigemptyset(&sig_main);
-	sigaddset(&sig_main, SIGINT);
-	sigaddset(&sig_main, SIGTERM);
-	
-	CHECK_SYS_DO(  sigwait(&sig_main, &sig), TRACE_DEBUG(INFO, "Error in sigwait function") );
+	TRACE_DEBUG(INFO, "Received signal %s (%d), exiting", SIGNALSTR(signal), signal);
+
+	CHECK_FCT_DO( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), exit(2) );
 	
-	TRACE_DEBUG(INFO, "Received signal %s (%d), exiting", SIGNALSTR(sig), sig);
-	CHECK_FCT_DO( fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), exit(2) );
-	return NULL;
+	return;
 }
-	
--- a/freeDiameter/tests/tests.h	Thu Mar 04 17:32:00 2010 +0900
+++ b/freeDiameter/tests/tests.h	Fri Mar 05 19:01:48 2010 +0900
@@ -105,7 +105,7 @@
 /* Minimum inits */
 #define INIT_FD() {								\
 	memset(fd_g_config, 0, sizeof(struct fd_config));			\
-	CHECK( 0, fd_lib_init() );						\
+	CHECK( 0, fd_lib_init(1) );						\
 	fd_log_threadname(basename(__FILE__));					\
 	(void) gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);	\
 	(void) gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);			\
--- a/include/freeDiameter/libfreeDiameter.h	Thu Mar 04 17:32:00 2010 +0900
+++ b/include/freeDiameter/libfreeDiameter.h	Fri Mar 05 19:01:48 2010 +0900
@@ -75,6 +75,22 @@
 #include <libgen.h>	/* for basename if --dbg_file is specified */
 #endif /* DEBUG */
 
+
+/*============================================================*/
+/*                          INIT                              */
+/*============================================================*/
+
+/* This function must be called first, before any call to another library function */
+/* If the parameter is not 0, the support for signals (fd_sig_register) is enabled, otherwise it is disabled */
+/* The function must be called while the application is single-threaded to enable support for signals */
+int fd_lib_init(int support_signals);
+
+/* Call this one when the application terminates, to destroy internal threads */
+void fd_lib_fini(void);
+
+
+
+
 /*============================================================*/
 /*                          DEBUG                             */
 /*============================================================*/
@@ -558,6 +574,31 @@
 
 
 /*============================================================*/
+/*                          SIGNALS                           */
+/*============================================================*/
+
+/* Register a new callback to be called on reception of a given signal (it receives the signal as parameter) */
+/* EALREADY will be returned if there is already a callback registered on this signal */
+/* NOTE: the signal handler will be called from a new detached thread */
+int fd_sig_register(int signal, char * modname, void (*callback)(int signal));
+
+/* Remove the handler for a given signal */
+int fd_sig_unregister(int signal);
+
+/* Dump list of handlers */
+void fd_sig_dump(int level, int indent);
+
+/* Name of signals */
+#ifdef HAVE_SIGNALENT_H
+extern const char *const fd_sig_str[];
+extern const int fd_sig_nstr;
+# define SIGNALSTR(sig) (((sig) < fd_sig_nstr) ? fd_sig_str[(sig)] : "[unknown signal]")
+#else /* HAVE_SIGNALENT_H */
+# define SIGNALSTR(sig) ("[no sig names]")
+#endif /* HAVE_SIGNALENT_H */
+
+
+/*============================================================*/
 /*                          LISTS                             */
 /*============================================================*/
 
--- a/libfreeDiameter/CMakeLists.txt	Thu Mar 04 17:32:00 2010 +0900
+++ b/libfreeDiameter/CMakeLists.txt	Fri Mar 05 19:01:48 2010 +0900
@@ -13,6 +13,7 @@
 	messages.c
 	rt_data.c
 	sessions.c
+	signal.c
 	)
 
 # Build as a shared library
--- a/libfreeDiameter/init.c	Thu Mar 04 17:32:00 2010 +0900
+++ b/libfreeDiameter/init.c	Fri Mar 05 19:01:48 2010 +0900
@@ -35,7 +35,8 @@
 
 #include "libfD.h"
 
-int fd_lib_init(void)
+/* Initialize library variables and threads */
+int fd_lib_init(int support_signals)
 {
 	int ret = 0;
 	
@@ -46,9 +47,21 @@
 		return ret;
 	}
 	
+	/* Initialize signals if requested */
+	if (support_signals) {
+		CHECK_FCT( fd_sig_init() );
+	}
+	
 	/* Initialize the modules that need it */
 	fd_msg_eteid_init();
 	CHECK_FCT( fd_sess_init() );
 	
 	return 0;
 }
+
+/* Stop all threads created in the library */
+void fd_lib_fini(void)
+{
+	fd_sess_fini();
+	fd_sig_fini();
+}
--- a/libfreeDiameter/libfD.h	Thu Mar 04 17:32:00 2010 +0900
+++ b/libfreeDiameter/libfD.h	Fri Mar 05 19:01:48 2010 +0900
@@ -45,6 +45,9 @@
 extern const char * type_base_name[];
 void fd_msg_eteid_init(void);
 int fd_sess_init(void);
+void fd_sess_fini(void);
+int fd_sig_init(void);
+void fd_sig_fini(void);
 
 /* Iterator on the rules of a parent object */
 int fd_dict_iterate_rules ( struct dict_object *parent, void * data, int (*cb)(void *, struct dict_rule_data *) );
--- a/libfreeDiameter/sessions.c	Thu Mar 04 17:32:00 2010 +0900
+++ b/libfreeDiameter/sessions.c	Fri Mar 05 19:01:48 2010 +0900
@@ -237,6 +237,14 @@
 	return 0;
 }
 
+/* Terminate */
+void fd_sess_fini(void)
+{
+	TRACE_ENTRY("");
+	CHECK_FCT_DO( fd_thr_term(&exp_thr), /* continue */ );
+	return;
+}
+
 /* Create a new handler */
 int fd_sess_handler_create_internal ( struct session_handler ** handler, void (*cleanup)(char * sid, session_state * state) )
 {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libfreeDiameter/signal.c	Fri Mar 05 19:01:48 2010 +0900
@@ -0,0 +1,290 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
+*													 *
+* Copyright (c) 2009, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "libfD.h"
+
+#include <signal.h>
+
+/* List of signal names */
+#ifdef HAVE_SIGNALENT_H
+const char *const fd_sig_str[] = {
+# include "signalent.h"
+};
+const int fd_sig_nstr = sizeof fd_sig_str / sizeof fd_sig_str[0];
+#endif /* HAVE_SIGNALENT_H */
+
+/* A structure to hold the registered signal handlers */
+struct sig_hdl {
+	struct fd_list	chain;		/* Link in the sig_hdl_list ordered by signal */
+	int 		signal;		/* The signal this handler is registered for */
+	void 		(*sig_hdl)(int);/* The callback to call when the signal is caught */
+	pthread_t	sig_thr;	/* The thread that receives the signal */
+	char 		*modname;	/* The name of the module who registered the signal (for debug) */
+};
+
+/* The list of sig_hdl, and the mutex to protect it */
+static struct fd_list	sig_hdl_list = FD_LIST_INITIALIZER(sig_hdl_list);
+static pthread_mutex_t sig_hdl_lock  = PTHREAD_MUTEX_INITIALIZER;
+
+/* Detached thread attribute */
+static pthread_attr_t detached;
+
+/* Note if the module was initialized */
+static int sig_initialized = 0;
+
+/* Initialize the support for signals */
+int fd_sig_init(void)
+{
+	sigset_t sig_all;
+	
+	TRACE_ENTRY("");
+	
+	if (sig_initialized)
+		return 0;
+	
+	/* Block all signals from the current thread and all its future children, so that signals are delivered only to our sig_hdl->sig_thr */
+	sigfillset(&sig_all);
+	CHECK_POSIX(  pthread_sigmask(SIG_BLOCK, &sig_all, NULL)  );
+	
+	/* Initialize the detached attribute */
+	CHECK_POSIX( pthread_attr_init(&detached) );
+	CHECK_POSIX( pthread_attr_setdetachstate(&detached, PTHREAD_CREATE_DETACHED) );
+	
+	sig_initialized++;
+	
+	/* That's all we have to do here ... */
+	return 0;
+}
+
+
+/* This thread (created detached) does call the signal handler */
+static void * signal_caller(void * arg)
+{
+	struct sig_hdl * me = arg;
+	char buf[40];
+	
+	/* Name this thread */
+	snprintf(buf, sizeof(buf), "cb %d:%s:%s", me->signal, SIGNALSTR(me->signal), me->modname);
+	fd_log_threadname ( buf );
+	
+	TRACE_ENTRY("%p", me);
+	
+	/* Call the signal handler */
+	(*me->sig_hdl)(me->signal);
+	
+	TRACE_DEBUG(CALL, "Callback completed");
+	
+	/* Done! */
+	return NULL;
+}
+
+/* This thread "really" receives the signal and starts the signal_caller thread */
+static void * signal_catcher(void * arg)
+{
+	struct sig_hdl * me = arg;
+	sigset_t ss;
+	char buf[40];
+	
+	ASSERT(arg);
+	
+	/* Name this thread */
+	snprintf(buf, sizeof(buf), "catch %d:%s:%s", me->signal, SIGNALSTR(me->signal), me->modname);
+	fd_log_threadname ( buf );
+	
+	TRACE_ENTRY("%p", me);
+	
+	sigemptyset(&ss);
+	sigaddset(&ss, me->signal);
+	
+	/* Now loop on the reception of the signal */
+	while (1) {
+		int sig;
+		pthread_t th;
+		
+		/* Wait to receive the next signal */
+		CHECK_POSIX_DO( sigwait(&ss, &sig), break );
+		
+		TRACE_DEBUG(FULL, "Signal %d caught", sig);
+		
+		/* Create the thread that will call the handler */
+		CHECK_POSIX_DO( pthread_create( &th, &detached, signal_caller, arg ), break );
+	}
+	
+	/* An error occurred... What should we do ? */
+	TRACE_DEBUG(INFO, "!!! ERROR !!! The signal catcher for %d:%s:%s is terminating!", me->signal, SIGNALSTR(me->signal), me->modname);
+	
+	/* Better way to handle this ? */
+	ASSERT(0);
+	return NULL;
+}
+
+/* Register a new callback to be called on reception of a given signal (it receives the signal as parameter) */
+/* EALREADY will be returned if there is already a callback registered on this signal */
+/* NOTE: the signal handler will be called from a new detached thread */
+int fd_sig_register(int signal, char * modname, void (*handler)(int signal))
+{
+	struct sig_hdl * new;
+	struct fd_list * next = NULL;
+	int matched = 0;
+	
+	TRACE_ENTRY("%d %p %p", signal, modname, handler);
+	CHECK_PARAMS( signal && modname && handler && sig_initialized );
+	
+	/* Alloc all memory before taking the lock  */
+	CHECK_MALLOC( new = malloc(sizeof(struct sig_hdl)) );
+	memset(new, 0, sizeof(struct sig_hdl));
+	fd_list_init(&new->chain, new);
+	new->signal = signal;
+	new->sig_hdl = handler;
+	CHECK_MALLOC_DO( new->modname = strdup(modname), { free(new); return ENOMEM; } );
+	
+	/* Search in the list if we already have a handler for this signal */
+	CHECK_POSIX(pthread_mutex_lock(&sig_hdl_lock));
+	for (next = sig_hdl_list.next; next != &sig_hdl_list; next = next->next) {
+		struct sig_hdl * nh = (struct sig_hdl *)next;
+		
+		if (nh->signal < signal)
+			continue;
+		if (nh->signal == signal) {
+			matched = 1;
+			TRACE_DEBUG(INFO, "Signal %d (%s) is already registered by module '%s'", signal, SIGNALSTR(signal), nh->modname);
+		}
+		break;
+	}
+	if (!matched)
+		/* Insert the new element before the next handle */
+		fd_list_insert_before(next, &new->chain);
+		
+	CHECK_POSIX(pthread_mutex_unlock(&sig_hdl_lock));
+	
+	if (matched)
+		return EALREADY; /* Already registered... */
+	
+	/* OK, now start the thread */
+	CHECK_POSIX( pthread_create( &new->sig_thr, NULL, signal_catcher, new ) );
+	
+	/* All good */
+	return 0;
+}
+
+/* Dump list of handlers */
+void fd_sig_dump(int level, int indent)
+{
+	struct fd_list * li = NULL;
+	
+	if (!TRACE_BOOL(level))
+		return;
+	
+	
+	CHECK_POSIX_DO(pthread_mutex_lock(&sig_hdl_lock), /* continue */);
+	if (!FD_IS_LIST_EMPTY(&sig_hdl_list))
+		fd_log_debug("%*sList of registered signal handlers:\n", indent, "");
+	
+	for (li = sig_hdl_list.next; li != &sig_hdl_list; li = li->next) {
+		struct sig_hdl * h = (struct sig_hdl *)li;
+		
+		fd_log_debug("%*s  - sig %*d (%s) => %p in module %s\n", indent, "", 2, h->signal, SIGNALSTR(h->signal), h->sig_hdl, h->modname);
+	}
+	CHECK_POSIX_DO(pthread_mutex_unlock(&sig_hdl_lock), /* continue */);
+	
+	return;
+}
+
+
+/* Remove a callback */
+int fd_sig_unregister(int signal)
+{
+	struct fd_list * li = NULL;
+	struct sig_hdl * h = NULL;
+	
+	TRACE_ENTRY("%d", signal);
+	CHECK_PARAMS( signal && sig_initialized );
+	
+	/* Search in the list if we already have a handler for this signal */
+	CHECK_POSIX(pthread_mutex_lock(&sig_hdl_lock));
+	for (li = sig_hdl_list.next; li != &sig_hdl_list; li = li->next) {
+		struct sig_hdl * nh = (struct sig_hdl *)li;
+		
+		if (nh->signal < signal)
+			continue;
+		if (nh->signal == signal) {
+			h = nh;
+			fd_list_unlink(&h->chain);
+		}
+		break;
+	}
+	CHECK_POSIX(pthread_mutex_unlock(&sig_hdl_lock));
+	
+	if (!h)
+		return ENOENT;
+	
+	/* Stop the catcher thread */
+	CHECK_FCT( fd_thr_term(&h->sig_thr) );
+
+	/* Free */
+	free(h->modname);
+	free(h);
+	
+	/* All good */
+	return 0;
+}
+
+/* Terminate the signal handler thread -- must be called if fd_sig_init was called */
+void fd_sig_fini(void)
+{
+	if (!sig_initialized)
+		return;
+	
+	/* For each registered handler, stop the thread, free the associated memory */
+	CHECK_POSIX_DO(pthread_mutex_lock(&sig_hdl_lock), /* continue */);
+	while (!FD_IS_LIST_EMPTY(&sig_hdl_list)) {
+		struct sig_hdl * h = (struct sig_hdl *)sig_hdl_list.next;
+		
+		/* Unlink */
+		fd_list_unlink(&h->chain);
+		
+		/* Stop the catcher thread */
+		CHECK_FCT_DO( fd_thr_term(&h->sig_thr), /* continue */ );
+		
+		/* Free */
+		free(h->modname);
+		free(h);
+	}
+	CHECK_POSIX_DO(pthread_mutex_unlock(&sig_hdl_lock), /* continue */);
+
+	/* Destroy the thread attribute */
+	CHECK_POSIX_DO( pthread_attr_destroy(&detached), /* continue */ );
+	return;
+}
"Welcome to our mercurial repository"