# HG changeset patch # User Thomas Klausner # Date 1554817610 -7200 # Node ID cec0812038bb6b0beec16c1206d52df7ef97f9d2 # Parent 870eecffa3feed8abc7352ed82ab9cac016f0ca4 rt_default: add reload support. When SIGUSR1 is sent to the freeDiameter process, rt_default reloads its config file. Written for Effortel Technologies SA, published with their consent. diff -r 870eecffa3fe -r cec0812038bb doc/rt_default.conf.sample --- a/doc/rt_default.conf.sample Tue Apr 09 15:43:23 2019 +0200 +++ b/doc/rt_default.conf.sample Tue Apr 09 15:46:50 2019 +0200 @@ -2,6 +2,10 @@ # # This extension provides configurable routing properties for freeDiameter. +# This extension supports configuration reload at runtime. Send +# signal SIGUSR1 to the process to cause the process to reload its +# config. + # Lines starting with a # are comments and ignored. ############################################################################## diff -r 870eecffa3fe -r cec0812038bb extensions/rt_default/rt_default.c --- a/extensions/rt_default/rt_default.c Tue Apr 09 15:43:23 2019 +0200 +++ b/extensions/rt_default/rt_default.c Tue Apr 09 15:46:50 2019 +0200 @@ -37,38 +37,101 @@ * Configurable routing of messages for freeDiameter. */ +#include + #include "rt_default.h" +#define MODULE_NAME "rt_default" + +#include + +static pthread_rwlock_t rtd_lock; + +static char *rtd_config_file; + /* The callback called on new messages */ static int rtd_out(void * cbdata, struct msg ** pmsg, struct fd_list * candidates) { struct msg * msg = *pmsg; TRACE_ENTRY("%p %p %p", cbdata, msg, candidates); + int ret; CHECK_PARAMS(msg && candidates); - + + if (pthread_rwlock_rdlock(&rtd_lock) != 0) { + fd_log_notice("%s: read-lock failed, skipping handler", MODULE_NAME); + return 0; + } /* Simply pass it to the appropriate function */ if (FD_IS_LIST_EMPTY(candidates)) { - return 0; + ret = 0; } else { - return rtd_process( msg, candidates ); + ret = rtd_process( msg, candidates ); } + if (pthread_rwlock_unlock(&rtd_lock) != 0) { + fd_log_notice("%s: read-unlock failed after rtd_out, exiting", MODULE_NAME); + exit(1); + } + return ret; } /* handler */ static struct fd_rt_out_hdl * rtd_hdl = NULL; +static volatile int in_signal_handler = 0; + +/* signal handler */ +static void sig_hdlr(void) +{ + if (in_signal_handler) { + fd_log_error("%s: already handling a signal, ignoring new one", MODULE_NAME); + return; + } + in_signal_handler = 1; + + if (pthread_rwlock_wrlock(&rtd_lock) != 0) { + fd_log_error("%s: locking failed, aborting config reload", MODULE_NAME); + return; + } + rtd_conf_reload(rtd_config_file); + if (pthread_rwlock_unlock(&rtd_lock) != 0) { + fd_log_error("%s: unlocking failed after config reload, exiting", MODULE_NAME); + exit(1); + } + + fd_log_notice("%s: reloaded configuration", MODULE_NAME); + + in_signal_handler = 0; +} + + /* entry point */ static int rtd_entry(char * conffile) { TRACE_ENTRY("%p", conffile); - + + rtd_config_file = conffile; + pthread_rwlock_init(&rtd_lock, NULL); + + if (pthread_rwlock_wrlock(&rtd_lock) != 0) { + fd_log_notice("%s: write-lock failed, aborting", MODULE_NAME); + return EDEADLK; + } + /* Initialize the repo */ CHECK_FCT( rtd_init() ); /* Parse the configuration file */ CHECK_FCT( rtd_conf_handle(conffile) ); - + + if (pthread_rwlock_unlock(&rtd_lock) != 0) { + fd_log_notice("%s: write-unlock failed, aborting", MODULE_NAME); + return EDEADLK; + } + + /* Register reload callback */ + CHECK_FCT(fd_event_trig_regcb(SIGUSR1, MODULE_NAME, sig_hdlr)); + #if 0 /* Dump the rules */ rtd_dump(); @@ -91,9 +154,11 @@ /* Destroy the data */ rtd_fini(); - + + pthread_rwlock_destroy(&rtd_lock); + /* Done */ return ; } -EXTENSION_ENTRY("rt_default", rtd_entry); +EXTENSION_ENTRY(MODULE_NAME, rtd_entry); diff -r 870eecffa3fe -r cec0812038bb extensions/rt_default/rt_default.h --- a/extensions/rt_default/rt_default.h Tue Apr 09 15:43:23 2019 +0200 +++ b/extensions/rt_default/rt_default.h Tue Apr 09 15:46:50 2019 +0200 @@ -82,5 +82,8 @@ /* Process a message & peer list through the rules repository, updating the scores */ int rtd_process( struct msg * msg, struct fd_list * candidates ); +/* Reload the config file */ +void rtd_conf_reload(char *config_file); + /* For debug: dump the rule repository */ void rtd_dump(void); diff -r 870eecffa3fe -r cec0812038bb extensions/rt_default/rtd_conf.y --- a/extensions/rt_default/rtd_conf.y Tue Apr 09 15:43:23 2019 +0200 +++ b/extensions/rt_default/rtd_conf.y Tue Apr 09 15:46:50 2019 +0200 @@ -54,6 +54,7 @@ /* Forward declaration */ int yyparse(char * conffile); +void rtd_confrestart(FILE *input_file); static int rules_added = 0; @@ -65,25 +66,27 @@ TRACE_ENTRY("%p", conffile); - TRACE_DEBUG (FULL, "Parsing configuration file: %s...", conffile); - + TRACE_DEBUG (FULL, "rt_default: Parsing configuration file: %s...", conffile); + + rules_added = 0; rtd_confin = fopen(conffile, "r"); if (rtd_confin == NULL) { ret = errno; fd_log_debug("Unable to open extension configuration file %s for reading: %s", conffile, strerror(ret)); - TRACE_DEBUG (INFO, "Error occurred, message logged -- configuration file."); + TRACE_DEBUG (INFO, "rt_default: Error occurred, message logged -- configuration file."); return ret; } + rtd_confrestart(rtd_confin); ret = yyparse(conffile); fclose(rtd_confin); if (ret != 0) { - TRACE_DEBUG (INFO, "Unable to parse the configuration file."); + TRACE_DEBUG (INFO, "rt_default: Unable to parse the configuration file."); return EINVAL; } else { - TRACE_DEBUG(FULL, "Added %d RULES routing entries successfully.", rules_added); + TRACE_DEBUG(INFO, "rt_default: Added %d RULES routing entries successfully.", rules_added); } return 0; @@ -95,7 +98,7 @@ /* Function to report the errors */ void yyerror (YYLTYPE *ploc, char * conffile, char const *s) { - TRACE_DEBUG(INFO, "Error in configuration parsing"); + TRACE_DEBUG(INFO, "rt_default: Error in configuration parsing"); if (ploc->first_line != ploc->last_line) fd_log_debug("%s:%d.%d-%d.%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s); diff -r 870eecffa3fe -r cec0812038bb extensions/rt_default/rtd_rules.c --- a/extensions/rt_default/rtd_rules.c Tue Apr 09 15:43:23 2019 +0200 +++ b/extensions/rt_default/rtd_rules.c Tue Apr 09 15:46:50 2019 +0200 @@ -46,7 +46,7 @@ * * Under each TARGET element, we have the list of RULES that are defined for this target, ordered by CRITERIA type, then is_regex, then string value. * - * Note: Except during configuration parsing and module termination, the lists are only ever accessed read-only, so we do not need a lock. + * Note: Access these only when holding rtd_lock; config reload may change the underlying data. */ /* Structure to hold the data that is used for matching. */ @@ -389,19 +389,23 @@ return 0; } -/* Destroy the module's data */ -void rtd_fini(void) +static void free_targets(void) { int i; - - TRACE_ENTRY(); for (i = 0; i < RTD_TAR_MAX; i++) { while (!FD_IS_LIST_EMPTY(&TARGETS[i])) { del_target((struct target *) TARGETS[i].next); } } - +} + +/* Destroy the module's data */ +void rtd_fini(void) +{ + TRACE_ENTRY(); + + free_targets(); } /* Add a new rule in the repository. this is called when the configuration file is being parsed */ @@ -494,6 +498,40 @@ return 0; } +void rtd_conf_reload(char *config_file) +{ + /* save old config in case reload goes wrong */ + struct fd_list old_config[RTD_TAR_MAX]; + int i; + + for (i = 0; i < RTD_TAR_MAX; i++) { + old_config[i] = TARGETS[i]; + } + memset(TARGETS, 0, sizeof(*TARGETS) * RTD_TAR_MAX); + for (i = 0; i < RTD_TAR_MAX; i++) { + fd_list_init(&TARGETS[i], NULL); + } + + if (rtd_conf_handle(config_file) != 0) { + fd_log_notice("rt_default: error reloading configuration, restoring previous configuration"); + free_targets(); + for (i = 0; i < RTD_TAR_MAX; i++) { + TARGETS[i] = old_config[i]; + } + } else { + /* this has to be done in this weird way because the items contain back pointers referencing TARGETS */ + struct fd_list save_config[RTD_TAR_MAX]; + for (i = 0; i < RTD_TAR_MAX; i++) { + save_config[i] = TARGETS[i]; + TARGETS[i] = old_config[i]; + } + free_targets(); + for (i = 0; i < RTD_TAR_MAX; i++) { + TARGETS[i] = save_config[i]; + } + } +} + /* Check if a message and list of eligible candidate match any of our rules, and update its score according to it. */ int rtd_process( struct msg * msg, struct fd_list * candidates ) {