changeset 1354:0dff6a604b0a

acl_wl: add reload support using SIGUSR1
author Thomas Klausner <tk@giga.or.at>
date Fri, 17 May 2019 12:59:19 +0200
parents 0f226d35501e
children 70b6067f4552
files doc/acl_wl.conf.sample extensions/acl_wl/acl_wl.c extensions/acl_wl/acl_wl.h extensions/acl_wl/aw_conf.l extensions/acl_wl/aw_conf.y extensions/acl_wl/aw_tree.c
diffstat 6 files changed, 105 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/doc/acl_wl.conf.sample	Sun May 12 23:16:22 2019 +0800
+++ b/doc/acl_wl.conf.sample	Fri May 17 12:59:19 2019 +0200
@@ -3,6 +3,11 @@
 # This extension is meant to allow connection from remote peers, without actively
 # maintaining this connection ourselves (as it would be the case by declaring the 
 # peer in a ConnectPeer directive).
+#
+# This extension supports configuration reload at runtime. Send
+# signal SIGUSR1 to the process to cause the process to reload its
+# config.
+#
 # The format of this file is very simple. It contains a list of peer names
 # separated by spaces or newlines. 
 #
--- a/extensions/acl_wl/acl_wl.c	Sun May 12 23:16:22 2019 +0800
+++ b/extensions/acl_wl/acl_wl.c	Fri May 17 12:59:19 2019 +0200
@@ -37,8 +37,17 @@
  * Whitelist extension for freeDiameter.
  */
 
+#include <pthread.h>
+#include <signal.h>
+
 #include "acl_wl.h"
 
+static pthread_rwlock_t acl_wl_lock;
+
+#define MODULE_NAME "acl_wl"
+
+static char *acl_wl_config_file;
+
 /* The validator function */
 static int aw_validate(struct peer_info * info, int * auth, int (**cb2)(struct peer_info *))
 {
@@ -53,9 +62,20 @@
 	
 	/* Default to unknown result */
 	*auth = 0;
-	
+
+        if (pthread_rwlock_rdlock(&acl_wl_lock) != 0) {
+		fd_log_notice("%s: read-lock failed, skipping handler", MODULE_NAME);
+		return 0;
+	}
+
 	/* Now search the peer in our tree */
 	CHECK_FCT( aw_tree_lookup(info->pi_diamid, &res) );
+
+        if (pthread_rwlock_unlock(&acl_wl_lock) != 0) {
+		fd_log_notice("%s: read-unlock failed after aw_tree_lookup, exiting", MODULE_NAME);
+		exit(1);
+	}
+
 	if (res < 0) {
 		/* The peer is not whitelisted */
 		return 0;
@@ -87,20 +107,82 @@
 	return 0;
 }
 
+static volatile int in_signal_handler = 0;
+
+/* signal handler */
+static void sig_hdlr(void)
+{
+	struct fd_list old_tree;
+
+	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(&acl_wl_lock) != 0) {
+		fd_log_error("%s: locking failed, aborting config reload", MODULE_NAME);
+		return;
+	}
+
+	/* save old config in case reload goes wrong */
+	old_tree = tree_root;
+	fd_list_init(&tree_root, NULL);
+
+	if (aw_conf_handle(acl_wl_config_file) != 0) {
+		fd_log_error("%s: error reloading configuration, restoring previous configuration", MODULE_NAME);
+		aw_tree_destroy();
+		tree_root = old_tree;
+	} else {
+		struct fd_list new_tree;
+		new_tree = tree_root;
+		tree_root = old_tree;
+		aw_tree_destroy();
+		tree_root = new_tree;
+	}
+
+	if (pthread_rwlock_unlock(&acl_wl_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 aw_entry(char * conffile)
 {
 	TRACE_ENTRY("%p", conffile);
 	CHECK_PARAMS(conffile);
-	
+
+	acl_wl_config_file = conffile;
+
+        pthread_rwlock_init(&acl_wl_lock, NULL);
+
+        if (pthread_rwlock_wrlock(&acl_wl_lock) != 0) {
+                fd_log_notice("%s: write-lock failed, aborting", MODULE_NAME);
+                return EDEADLK;
+        }
+
 	/* Parse configuration file */
 	CHECK_FCT( aw_conf_handle(conffile) );
-	
+
 	TRACE_DEBUG(INFO, "Extension ACL_wl initialized with configuration: '%s'", conffile);
 	if (TRACE_BOOL(ANNOYING)) {
 		aw_tree_dump();
 	}
-	
+
+	if (pthread_rwlock_unlock(&acl_wl_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));
+
 	/* Register the validator function */
 	CHECK_FCT( fd_peer_validate_register ( aw_validate ) );
 
@@ -114,4 +196,4 @@
 	aw_tree_destroy();
 }
 
-EXTENSION_ENTRY("acl_wl", aw_entry);
+EXTENSION_ENTRY(MODULE_NAME, aw_entry);
--- a/extensions/acl_wl/acl_wl.h	Sun May 12 23:16:22 2019 +0800
+++ b/extensions/acl_wl/acl_wl.h	Fri May 17 12:59:19 2019 +0200
@@ -43,6 +43,8 @@
  
 #include <freeDiameter/extension.h>
 
+extern struct fd_list tree_root;
+
 /* Parse the configuration file */
 int aw_conf_handle(char * conffile);
 
--- a/extensions/acl_wl/aw_conf.l	Sun May 12 23:16:22 2019 +0800
+++ b/extensions/acl_wl/aw_conf.l	Fri May 17 12:59:19 2019 +0200
@@ -35,7 +35,7 @@
 
 /* Lex extension's configuration parser.
  *
- * The configuration file contains a default priority, and a list of peers with optional overwite priority.
+ * The configuration file contains a default priority, and a list of peers with optional overwrite priority.
  * -- see the app_test.conf.sample file for more detail.
  */
 
--- a/extensions/acl_wl/aw_conf.y	Sun May 12 23:16:22 2019 +0800
+++ b/extensions/acl_wl/aw_conf.y	Fri May 17 12:59:19 2019 +0200
@@ -57,6 +57,7 @@
 
 /* Forward declaration */
 int yyparse(char * conffile);
+void aw_confrestart(FILE *input_file);
 
 static int fqdn_added = 0;
 
@@ -74,19 +75,20 @@
 	if (aw_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, "acl_wl: Error occurred, message logged -- configuration file.");
 		return ret;
 	}
 
+	aw_confrestart(aw_confin);
 	ret = yyparse(conffile);
 
 	fclose(aw_confin);
 
 	if (ret != 0) {
-		TRACE_DEBUG (INFO, "Unable to parse the configuration file.");
+		TRACE_DEBUG (INFO, "acl_wl: Unable to parse the configuration file.");
 		return EINVAL;
 	} else {
-		TRACE_DEBUG(FULL, "Read %d FQDN entries successfully.", fqdn_added);
+		TRACE_DEBUG(FULL, "acl_wl: Read %d FQDN entries successfully.", fqdn_added);
 	}
 	
 	return 0;
@@ -98,7 +100,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, "acl_wl: 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);
@@ -130,11 +132,11 @@
 			| conffile FQDN
 			{
 				fqdn_added++;
-				TRACE_DEBUG(FULL, "Added FQDN: %s", $2);
+				TRACE_DEBUG(FULL, "acl_wl: Added FQDN: %s", $2);
 			}
 			| conffile LEX_ERROR
 			{
-				yyerror(&yylloc, conffile, "An error occurred while parsing the configuration file");
+				yyerror(&yylloc, conffile, "acl_wl: An error occurred while parsing the configuration file");
 				return EINVAL;
 			}
 			;
--- a/extensions/acl_wl/aw_tree.c	Sun May 12 23:16:22 2019 +0800
+++ b/extensions/acl_wl/aw_tree.c	Fri May 17 12:59:19 2019 +0200
@@ -69,9 +69,9 @@
 };
 
 /* The root of the tree */
-static struct fd_list tree_root = FD_LIST_INITIALIZER(tree_root);
+struct fd_list tree_root = FD_LIST_INITIALIZER(tree_root);
 
-/* Note: we don't need to lock, since we add only when parsing the conf, and then read only */
+/* Note: we lock accesses to the tree with acl_wl_lock because of config reload */
 
 
 /* The parsed name */
"Welcome to our mercurial repository"