From 8e257fe8196544ff962f1032a73134ad7c8cc4b4 Mon Sep 17 00:00:00 2001
From: Jonathan Rose <jrose@digium.com>
Date: Tue, 30 Apr 2013 22:37:24 +0000
Subject: [PATCH] Stasis Core: Refactor ACL Change events to go out over the
 stasis core msg bus

(issue ASTERISK-21103)
Reported by: Matt Jordan
Review: https://reviewboard.asterisk.org/r/2481/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@387037 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 channels/chan_iax2.c          | 31 ++++++++------
 channels/chan_sip.c           | 27 ++++++++-----
 include/asterisk/acl.h        | 18 +++++++++
 include/asterisk/event_defs.h |  6 +--
 include/asterisk/json.h       | 15 +++++++
 main/event.c                  |  1 -
 main/json.c                   | 21 ++++++++++
 main/manager.c                | 29 +++++++------
 main/named_acl.c              | 76 ++++++++++++++++++++++++++++-------
 9 files changed, 169 insertions(+), 55 deletions(-)

diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index c15aa85a2a..94573908c8 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -284,7 +284,7 @@ static char language[MAX_LANGUAGE] = "";
 static char regcontext[AST_MAX_CONTEXT] = "";
 
 static struct ast_event_sub *network_change_event_subscription; /*!< subscription id for network change events */
-static struct ast_event_sub *acl_change_event_subscription; /*!< subscription id for ACL change events */
+static struct stasis_subscription *acl_change_sub; /*!< subscription id for ACL change events */
 static int network_change_event_sched_id = -1;
 
 static int maxauthreq = 3;
@@ -1255,7 +1255,7 @@ static int get_unused_callno(enum callno_type type, int validated, callno_entry
 static int replace_callno(const void *obj);
 static void sched_delay_remove(struct sockaddr_in *sin, callno_entry entry);
 static void network_change_event_cb(const struct ast_event *, void *);
-static void acl_change_event_cb(const struct ast_event *, void *);
+static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message);
 
 static struct ast_channel_tech iax2_tech = {
 	.type = "IAX2",
@@ -1338,18 +1338,18 @@ static void network_change_event_unsubscribe(void)
 	}
 }
 
-static void acl_change_event_subscribe(void)
+static void acl_change_stasis_subscribe(void)
 {
-	if (!acl_change_event_subscription) {
-		acl_change_event_subscription = ast_event_subscribe(AST_EVENT_ACL_CHANGE,
-			acl_change_event_cb, "IAX2 ACL Change", NULL, AST_EVENT_IE_END);
+	if (!acl_change_sub) {
+		acl_change_sub = stasis_subscribe(ast_acl_topic(),
+			acl_change_stasis_cb, NULL);
 	}
 }
 
-static void acl_change_event_unsubscribe(void)
+static void acl_change_stasis_unsubscribe(void)
 {
-	if (acl_change_event_subscription) {
-		acl_change_event_subscription = ast_event_unsubscribe(acl_change_event_subscription);
+	if (acl_change_sub) {
+		acl_change_sub = stasis_unsubscribe(acl_change_sub);
 	}
 }
 
@@ -1375,8 +1375,13 @@ static void network_change_event_cb(const struct ast_event *event, void *userdat
 
 }
 
-static void acl_change_event_cb(const struct ast_event *event, void *userdata)
+static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub,
+	struct stasis_topic *topic, struct stasis_message *message)
 {
+	if (stasis_message_type(message) != ast_named_acl_change_type()) {
+		return;
+	}
+
 	ast_log(LOG_NOTICE, "Reloading chan_iax2 in response to ACL change event.\n");
 	reload_config(1);
 }
@@ -12700,7 +12705,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
 	}
 
 	if (subscribe_acl_change) {
-		acl_change_event_subscribe();
+		acl_change_stasis_subscribe();
 	}
 
 	return peer;
@@ -12972,7 +12977,7 @@ cleanup:
 	}
 
 	if (subscribe_acl_change) {
-		acl_change_event_subscribe();
+		acl_change_stasis_subscribe();
 	}
 
 	return user;
@@ -14284,7 +14289,7 @@ static int __unload_module(void)
 	int x;
 
 	network_change_event_unsubscribe();
-	acl_change_event_unsubscribe();
+	acl_change_stasis_unsubscribe();
 
 	ast_manager_unregister("IAXpeers");
 	ast_manager_unregister("IAXpeerlist");
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index c03f98ed70..9311551300 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -844,7 +844,7 @@ static struct ast_flags global_flags[3] = {{0}};  /*!< global SIP_ flags */
 static int global_t38_maxdatagram;                /*!< global T.38 FaxMaxDatagram override */
 
 static struct ast_event_sub *network_change_event_subscription; /*!< subscription id for network change events */
-static struct ast_event_sub *acl_change_event_subscription; /*!< subscription id for named ACL system change events */
+static struct stasis_subscription *acl_change_sub; /*!< subscription id for named ACL system change events */
 static int network_change_event_sched_id = -1;
 
 static char used_context[AST_MAX_CONTEXT];        /*!< name of automatically created context for unloading */
@@ -1285,7 +1285,7 @@ static void sip_poke_all_peers(void);
 static void sip_peer_hold(struct sip_pvt *p, int hold);
 static void mwi_event_cb(void *, struct stasis_subscription *, struct stasis_topic *, struct stasis_message *);
 static void network_change_event_cb(const struct ast_event *, void *);
-static void acl_change_event_cb(const struct ast_event *event, void *userdata);
+static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message);
 static void sip_keepalive_all_peers(void);
 
 /*--- Applications, functions, CLI and manager command helpers */
@@ -16719,19 +16719,19 @@ static void network_change_event_unsubscribe(void)
 	}
 }
 
-static void acl_change_event_subscribe(void)
+static void acl_change_stasis_subscribe(void)
 {
-	if (!acl_change_event_subscription) {
-		acl_change_event_subscription = ast_event_subscribe(AST_EVENT_ACL_CHANGE,
-		acl_change_event_cb, "Manager must react to Named ACL changes", NULL, AST_EVENT_IE_END);
+	if (!acl_change_sub) {
+		acl_change_sub = stasis_subscribe(ast_acl_topic(),
+		acl_change_stasis_cb, NULL);
 	}
 
 }
 
 static void acl_change_event_unsubscribe(void)
 {
-	if (acl_change_event_subscription) {
-		acl_change_event_subscription = ast_event_unsubscribe(acl_change_event_subscription);
+	if (acl_change_sub) {
+		acl_change_sub = stasis_unsubscribe(acl_change_sub);
 	}
 }
 
@@ -29238,8 +29238,13 @@ static int restart_monitor(void)
 	return 0;
 }
 
-static void acl_change_event_cb(const struct ast_event *event, void *userdata)
+static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub,
+	struct stasis_topic *topic, struct stasis_message *message)
 {
+	if (stasis_message_type(message) != ast_named_acl_change_type()) {
+		return;
+	}
+
 	ast_log(LOG_NOTICE, "Reloading chan_sip in response to ACL change event.\n");
 
 	ast_mutex_lock(&sip_reload_lock);
@@ -31383,7 +31388,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
 
 	/* If an ACL change subscription is needed and doesn't exist, we need one. */
 	if (acl_change_subscription_needed) {
-		acl_change_event_subscribe();
+		acl_change_stasis_subscribe();
 	}
 
 	return peer;
@@ -32621,7 +32626,7 @@ static int reload_config(enum channelreloadreason reason)
 
 	/* If an ACL change subscription is needed and doesn't exist, we need one. */
 	if (acl_change_subscription_needed) {
-		acl_change_event_subscribe();
+		acl_change_stasis_subscribe();
 	}
 
 	return 0;
diff --git a/include/asterisk/acl.h b/include/asterisk/acl.h
index 50510a7658..537a30d32f 100644
--- a/include/asterisk/acl.h
+++ b/include/asterisk/acl.h
@@ -385,6 +385,24 @@ int ast_named_acl_init(void);
  */
 int ast_named_acl_reload(void);
 
+/*!
+ * \brief accessor for the ACL stasis topic
+ * \since 12
+ *
+ * \retval NULL if the stasis topic hasn't been created or has been disabled
+ * \retval a pointer to the ACL stasis topic
+ */
+struct stasis_topic *ast_acl_topic(void);
+
+/*!
+ * \brief accessor for the named ACL change stasis message type
+ * \since 12
+ *
+ * \retval NULL if the ACL change message type hasn't been created or has been canceled
+ * \retval a pointer to the ACL change message type
+ */
+struct stasis_message_type *ast_named_acl_change_type(void);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
diff --git a/include/asterisk/event_defs.h b/include/asterisk/event_defs.h
index 10c76d0da0..83fc988895 100644
--- a/include/asterisk/event_defs.h
+++ b/include/asterisk/event_defs.h
@@ -56,12 +56,10 @@ enum ast_event_type {
 	AST_EVENT_NETWORK_CHANGE      = 0x09,
 	/*! The presence state for a presence provider */
 	AST_EVENT_PRESENCE_STATE      = 0x0a,
-	/*! Used to alert listeners when a named ACL has changed. */
-	AST_EVENT_ACL_CHANGE          = 0x0b,
 	/*! Send out a ping for debugging distributed events */
-	AST_EVENT_PING                = 0x0c,
+	AST_EVENT_PING                = 0x0b,
 	/*! Number of event types.  This should be the last event type + 1 */
-	AST_EVENT_TOTAL               = 0x0d,
+	AST_EVENT_TOTAL               = 0x0c,
 };
 
 /*! \brief Event Information Element types */
diff --git a/include/asterisk/json.h b/include/asterisk/json.h
index d06416f58e..7b89d82709 100644
--- a/include/asterisk/json.h
+++ b/include/asterisk/json.h
@@ -830,6 +830,21 @@ struct ast_json *ast_json_timeval(const struct timeval tv, const char *zone);
  */
 struct ast_json *ast_json_dialplan_cep(const char *context, const char *exten, int priority);
 
+struct ast_json_payload {
+	struct ast_json *json;
+};
+
+/*!
+ * \brief Create an ao2 object to pass json blobs as data payloads for stasis
+ * \since 12.0.0
+ *
+ * \param json the ast_json blob we are loading
+ *
+ * \retval NULL if we fail to alloc it
+ * \retval pointer to the ast_json_payload created
+ */
+struct ast_json_payload *ast_json_payload_create(struct ast_json *json);
+
 /*!@}*/
 
 #endif /* _ASTERISK_JSON_H */
diff --git a/main/event.c b/main/event.c
index d97b04336b..fa2557a77e 100644
--- a/main/event.c
+++ b/main/event.c
@@ -217,7 +217,6 @@ static const char * const event_names[AST_EVENT_TOTAL] = {
 	[AST_EVENT_SECURITY]            = "Security",
 	[AST_EVENT_NETWORK_CHANGE]      = "NetworkChange",
 	[AST_EVENT_PRESENCE_STATE]      = "PresenceState",
-	[AST_EVENT_ACL_CHANGE]          = "ACLChange",
 	[AST_EVENT_PING]                = "Ping",
 };
 
diff --git a/main/json.c b/main/json.c
index 1ff563871c..87971f04a6 100644
--- a/main/json.c
+++ b/main/json.c
@@ -39,6 +39,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/localtime.h"
 #include "asterisk/module.h"
 #include "asterisk/utils.h"
+#include "asterisk/astobj2.h"
 
 #include <jansson.h>
 #include <time.h>
@@ -531,3 +532,23 @@ void ast_json_init(void)
 	/* Setup to use Asterisk custom allocators */
 	ast_json_reset_alloc_funcs();
 }
+
+static void json_payload_destructor(void *obj)
+{
+	struct ast_json_payload *payload = obj;
+	ast_json_unref(payload->json);
+}
+
+struct ast_json_payload *ast_json_payload_create(struct ast_json *json)
+{
+	struct ast_json_payload *payload;
+
+	if (!(payload = ao2_alloc(sizeof(*payload), json_payload_destructor))) {
+		return NULL;
+	}
+
+	ast_json_ref(json);
+	payload->json = json;
+
+	return payload;
+}
diff --git a/main/manager.c b/main/manager.c
index dd920e26d8..9e500e8def 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -1040,7 +1040,7 @@ static char global_realm[MAXHOSTNAMELEN];	/*!< Default realm */
 
 static int block_sockets;
 static int unauth_sessions = 0;
-static struct ast_event_sub *acl_change_event_subscription;
+static struct stasis_subscription *acl_change_sub;
 
 #define MGR_SHOW_TERMINAL_WIDTH 80
 
@@ -1065,20 +1065,20 @@ static const struct {
 	{{ "restart", "gracefully", NULL }},
 };
 
-static void acl_change_event_cb(const struct ast_event *event, void *userdata);
+static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message);
 
-static void acl_change_event_subscribe(void)
+static void acl_change_stasis_subscribe(void)
 {
-	if (!acl_change_event_subscription) {
-		acl_change_event_subscription = ast_event_subscribe(AST_EVENT_ACL_CHANGE,
-			acl_change_event_cb, "Manager must react to Named ACL changes", NULL, AST_EVENT_IE_END);
+	if (!acl_change_sub) {
+		acl_change_sub = stasis_subscribe(ast_acl_topic(),
+			acl_change_stasis_cb, NULL);
 	}
 }
 
-static void acl_change_event_unsubscribe(void)
+static void acl_change_stasis_unsubscribe(void)
 {
-	if (acl_change_event_subscription) {
-		acl_change_event_subscription = ast_event_unsubscribe(acl_change_event_subscription);
+	if (acl_change_sub) {
+		acl_change_sub = stasis_unsubscribe(acl_change_sub);
 	}
 }
 
@@ -7587,7 +7587,7 @@ static int __init_manager(int reload, int by_external_config)
 
 	/* If this wasn't performed due to a forced reload (because those can be created by ACL change events, we need to unsubscribe to ACL change events. */
 	if (!by_external_config) {
-		acl_change_event_unsubscribe();
+		acl_change_stasis_unsubscribe();
 	}
 
 	/* default values */
@@ -7893,7 +7893,7 @@ static int __init_manager(int reload, int by_external_config)
 
 	/* Check the flag for named ACL event subscription and if we need to, register a subscription. */
 	if (acl_subscription_flag && !by_external_config) {
-		acl_change_event_subscribe();
+		acl_change_stasis_subscribe();
 	}
 
 	/* Perform cleanup - essentially prune out old users that no longer exist */
@@ -7965,8 +7965,13 @@ static int __init_manager(int reload, int by_external_config)
 	return 0;
 }
 
-static void acl_change_event_cb(const struct ast_event *event, void *userdata)
+static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub,
+	struct stasis_topic *topic, struct stasis_message *message)
 {
+	if (stasis_message_type(message) != ast_named_acl_change_type()) {
+		return;
+	}
+
 	/* For now, this is going to be performed simply and just execute a forced reload. */
 	ast_log(LOG_NOTICE, "Reloading manager in response to ACL change event.\n");
 	__init_manager(1, 1);
diff --git a/main/named_acl.c b/main/named_acl.c
index 1426931943..7bc859769b 100644
--- a/main/named_acl.c
+++ b/main/named_acl.c
@@ -33,13 +33,14 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/config.h"
 #include "asterisk/config_options.h"
-#include "asterisk/event.h"
 #include "asterisk/utils.h"
 #include "asterisk/module.h"
 #include "asterisk/cli.h"
 #include "asterisk/acl.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/paths.h"
+#include "asterisk/stasis.h"
+#include "asterisk/json.h"
 
 #define NACL_CONFIG "acl.conf"
 #define ACL_FAMILY "acls"
@@ -355,9 +356,39 @@ struct ast_ha *ast_named_acl_find(const char *name, int *is_realtime, int *is_un
 	return ha;
 }
 
+/*! \brief Topic for ACLs */
+static struct stasis_topic *acl_topic;
+
+/*! \brief Message type for named ACL changes */
+static struct stasis_message_type *named_acl_change_type;
+
 /*!
  * \internal
- * \brief Sends an update event corresponding to a given named ACL that has changed.
+ * \brief Initialize Named ACL related stasis topics/messages
+ */
+static void ast_acl_stasis_init(void)
+{
+	acl_topic = stasis_topic_create("ast_acl");
+	named_acl_change_type = stasis_message_type_create("ast_named_acl_change");
+}
+
+struct stasis_topic *ast_acl_topic(void)
+{
+	return acl_topic;
+}
+
+struct stasis_message_type *ast_named_acl_change_type(void)
+{
+	return named_acl_change_type;
+}
+
+/*!
+ * \internal
+ * \brief Sends a stasis message corresponding to a given named ACL that has changed or
+ *        that all ACLs have been updated and old copies must be refreshed. Consumers of
+ *        named ACLs should subscribe to the ast_acl_topic and respond to messages of the
+ *        ast_named_acl_change_type stasis message type in order to be able to accomodate
+ *        changes to named ACLs.
  *
  * \param name Name of the ACL that has changed. May be an empty string (but not NULL)
  *        If name is an empty string, then all ACLs must be refreshed.
@@ -365,23 +396,38 @@ struct ast_ha *ast_named_acl_find(const char *name, int *is_realtime, int *is_un
  * \retval 0 success
  * \retval 1 failure
  */
-static int push_acl_change_event(char *name)
+static int publish_acl_change(const char *name)
 {
-	struct ast_event *event = ast_event_new(AST_EVENT_ACL_CHANGE,
-							AST_EVENT_IE_DESCRIPTION, AST_EVENT_IE_PLTYPE_STR, name,
-							AST_EVENT_IE_END);
-	if (!event) {
-		ast_log(LOG_ERROR, "Failed to allocate acl.conf reload event. Some modules will have out of date ACLs.\n");
-		return -1;
+	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_json_payload *, json_payload, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_json *, json_object, ast_json_object_create(), ast_json_unref);
+
+	if (!json_object) {
+		goto publish_failure;
 	}
 
-	if (ast_event_queue(event)) {
-		ast_event_destroy(event);
-		ast_log(LOG_ERROR, "Failed to queue acl.conf reload event. Some modules will have out of date ACLs.\n");
-		return -1;
+	if (ast_json_object_set(json_object, "name", ast_json_string_create(name))) {
+		goto publish_failure;
+	}
+
+	if (!(json_payload = ast_json_payload_create(json_object))) {
+		goto publish_failure;
 	}
 
+	msg = stasis_message_create(ast_named_acl_change_type(), json_payload);
+
+	if (!msg) {
+		goto publish_failure;
+	}
+
+	stasis_publish(ast_acl_topic(), msg);
+
 	return 0;
+
+publish_failure:
+	ast_log(LOG_ERROR, "Failed to to issue ACL change message for %s.\n",
+		ast_strlen_zero(name) ? "all named ACLs" : name);
+	return -1;
 }
 
 /*!
@@ -409,7 +455,7 @@ int ast_named_acl_reload(void)
 	}
 
 	/* We need to push an ACL change event with no ACL name so that all subscribers update with all ACLs */
-	push_acl_change_event("");
+	publish_acl_change("");
 
 	return 0;
 }
@@ -549,6 +595,8 @@ int ast_named_acl_init()
 		return 0;
 	}
 
+	ast_acl_stasis_init();
+
 	/* Register the per level options. */
 	aco_option_register(&cfg_info, "permit", ACO_EXACT, named_acl_types, NULL, OPT_ACL_T, 1, FLDSET(struct named_acl, ha));
 	aco_option_register(&cfg_info, "deny", ACO_EXACT, named_acl_types, NULL, OPT_ACL_T, 0, FLDSET(struct named_acl, ha));
-- 
GitLab