Commit b5c7cd71 authored by Sebastien Decugis's avatar Sebastien Decugis
Browse files

Centralized signal handlers management in the library

parent 532d872b
#!/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
......@@ -13,7 +13,6 @@ SET( APP_TEST_SRC
lex.ta_conf.c
ta_conf.tab.c
ta_conf.tab.h
ta_sig.c
ta_dict.c
ta_cli.c
ta_serv.c
......
......@@ -135,7 +135,7 @@ static void ta_cb_ans(void * data, struct msg ** msg)
}
/* 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 @@ int ta_cli_init(void)
{
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;
};
/*********************************************************************************************************
* 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;
}
......@@ -38,7 +38,6 @@
*/
#include "test_app.h"
#include <signal.h>
/* Initialize the configuration */
struct ta_conf * ta_conf = NULL;
......
......@@ -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 @@ extern struct ta_conf * ta_conf;
/* 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);
......
......@@ -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 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL;
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 @@ int main(int argc, char * argv[])
/* 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 @@ int main(int argc, char * argv[])
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 @@ end:
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 @@ static void main_help( void )
" -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(sig), sig);
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) );
return NULL;
}
return;
}
......@@ -105,7 +105,7 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL;
/* 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); \
......
......@@ -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 */
/*============================================================*/
......@@ -557,6 +573,31 @@ static __inline__ void fd_cleanup_socket(void * sockptr)
}
/*============================================================*/
/* 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 */
/*============================================================*/
......
......@@ -13,6 +13,7 @@ SET(LFD_SRC
messages.c
rt_data.c
sessions.c
signal.c
)
# Build as a shared library
......
......@@ -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 @@ int fd_lib_init(void)
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();
}
......@@ -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 *) );
......
......@@ -237,6 +237,14 @@ int fd_sess_init(void)
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) )
{
......
/*********************************************************************************************************
* 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)