[asterisk-commits] kmoore: trunk r396087 - in /trunk/res: ./ res_pjsip/ res_pjsip/include/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Aug 2 07:40:16 CDT 2013


Author: kmoore
Date: Fri Aug  2 07:40:03 2013
New Revision: 396087

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=396087
Log:
Add CLI/AMI commands to force chan_pjsip actions

For chan_pjsip, this introduces CLI/AMI remote unregistration commands,
reworks CLI syntax for sending NOTIFYs, adds AMI qualification support,
and adds documentation for PJSIPNotify.

This also fixes two refcounting bugs in the outbound registration code.

Review: https://reviewboard.asterisk.org/r/2695/
(closes issue ASTERISK-21939)

Modified:
    trunk/res/res_pjsip.c
    trunk/res/res_pjsip/include/res_pjsip_private.h
    trunk/res/res_pjsip/pjsip_options.c
    trunk/res/res_pjsip_notify.c
    trunk/res/res_pjsip_outbound_registration.c

Modified: trunk/res/res_pjsip.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip.c?view=diff&rev=396087&r1=396086&r2=396087
==============================================================================
--- trunk/res/res_pjsip.c (original)
+++ trunk/res/res_pjsip.c Fri Aug  2 07:40:03 2013
@@ -951,6 +951,20 @@
 			</configObject>
 		</configFile>
 	</configInfo>
+	<manager name="PJSIPQualify" language="en_US">
+		<synopsis>
+			Qualify a chan_pjsip endpoint.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Endpoint" required="true">
+				<para>The endpoint you want to qualify.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Qualify a chan_pjsip endpoint.</para>
+		</description>
+	</manager>
  ***/
 
 
@@ -1876,6 +1890,7 @@
 
 static int unload_module(void)
 {
+	ast_res_pjsip_cleanup_options_handling();
 	ast_sip_destroy_distributor();
 	ast_res_pjsip_destroy_configuration();
 	ast_sip_destroy_global_headers();

Modified: trunk/res/res_pjsip/include/res_pjsip_private.h
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip/include/res_pjsip_private.h?view=diff&rev=396087&r1=396086&r2=396087
==============================================================================
--- trunk/res/res_pjsip/include/res_pjsip_private.h (original)
+++ trunk/res/res_pjsip/include/res_pjsip_private.h Fri Aug  2 07:40:03 2013
@@ -71,4 +71,9 @@
  */
 int ast_sip_initialize_global(void);
 
+/*!
+ * \brief Clean up res_pjsip options handling
+ */
+void ast_res_pjsip_cleanup_options_handling(void);
+
 #endif /* RES_PJSIP_PRIVATE_H_ */

Modified: trunk/res/res_pjsip/pjsip_options.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip/pjsip_options.c?view=diff&rev=396087&r1=396086&r2=396087
==============================================================================
--- trunk/res/res_pjsip/pjsip_options.c (original)
+++ trunk/res/res_pjsip/pjsip_options.c Fri Aug  2 07:40:03 2013
@@ -652,6 +652,64 @@
 	return CLI_SUCCESS;
 }
 
+/*!
+ * \internal
+ * \brief Send qualify request to the given contact.
+ */
+static int ami_contact_cb(void *obj, void *arg, int flags)
+{
+	struct ast_sip_contact *contact = obj;
+	ao2_ref(contact, +1);
+	if (ast_sip_push_task(NULL, qualify_contact_task, contact)) {
+		ao2_cleanup(contact);
+	}
+	return 0;
+}
+
+static int ami_sip_qualify(struct mansession *s, const struct message *m)
+{
+	const char *endpoint_name = astman_get_header(m, "Endpoint");
+	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
+	char *aor_name, *aors;
+
+	if (ast_strlen_zero(endpoint_name)) {
+		astman_send_error(s, m, "Endpoint parameter missing.");
+		return 0;
+	}
+
+	endpoint = ast_sorcery_retrieve_by_id(
+		ast_sip_get_sorcery(),
+		"endpoint",
+		endpoint_name);
+	if (!endpoint) {
+		astman_send_error(s, m, "Unable to retrieve endpoint\n");
+		return 0;
+	}
+
+	/* send a qualify for all contacts registered with the endpoint */
+	if (ast_strlen_zero(endpoint->aors)) {
+		astman_send_error(s, m, "No AoRs configured for endpoint\n");
+		return 0;
+	}
+
+	aors = ast_strdupa(endpoint->aors);
+
+	while ((aor_name = strsep(&aors, ","))) {
+		RAII_VAR(struct ast_sip_aor *, aor,
+			 ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
+		RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
+
+		if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
+			continue;
+		}
+
+		ao2_callback(contacts, OBJ_NODATA, ami_contact_cb, NULL);
+	}
+
+	astman_send_ack(s, m, "Endpoint found, will qualify");
+	return 0;
+}
+
 static struct ast_cli_entry cli_options[] = {
 	AST_CLI_DEFINE(cli_qualify, "Send an OPTIONS request to a PJSIP endpoint")
 };
@@ -778,6 +836,13 @@
 
 	qualify_and_schedule_permanent();
 	ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
+	ast_manager_register2("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_sip_qualify, NULL, NULL, NULL);
 
 	return 0;
 }
+
+void ast_res_pjsip_cleanup_options_handling(void)
+{
+	ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
+	ast_manager_unregister("PJSIPQualify");
+}

Modified: trunk/res/res_pjsip_notify.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip_notify.c?view=diff&rev=396087&r1=396086&r2=396087
==============================================================================
--- trunk/res/res_pjsip_notify.c (original)
+++ trunk/res/res_pjsip_notify.c Fri Aug  2 07:40:03 2013
@@ -34,6 +34,24 @@
 #include "asterisk/res_pjsip.h"
 #include "asterisk/sorcery.h"
 
+/*** DOCUMENTATION
+	<manager name="PJSIPNotify" language="en_US">
+		<synopsis>
+			Send a NOTIFY to an endpoint.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Endpoint" required="true">
+				<para>The endpoint to which to send the NOTIFY.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Send a NOTIFY to an endpoint.</para>
+			<para>Parameters will be placed into the notify as SIP headers.</para>
+		</description>
+	</manager>
+ ***/
+
 #define CONTENT_TYPE_SIZE 64
 #define CONTENT_SIZE 512
 
@@ -541,7 +559,7 @@
 {
 	char *c = NULL;
 
-	if (pos == 2) {
+	if (pos == 3) {
 		int which = 0;
 		int wordlen = strlen(word);
 
@@ -564,7 +582,7 @@
 		ao2_iterator_destroy(&i);
 		return c;
 	}
-	return pos > 2 ? cli_complete_endpoint(word, state) : NULL;
+	return pos > 3 ? cli_complete_endpoint(word, state) : NULL;
 }
 
 /*!
@@ -584,9 +602,9 @@
 
 	switch (cmd) {
 	case CLI_INIT:
-		e->command = "pjsip notify";
+		e->command = "pjsip send notify";
 		e->usage =
-			"Usage: pjsip notify <type> <peer> [<peer>...]\n"
+			"Usage: pjsip send notify <type> <peer> [<peer>...]\n"
 			"       Send a NOTIFY request to an endpoint\n"
 			"       Message types are defined in sip_notify.conf\n";
 		return NULL;
@@ -594,22 +612,22 @@
 		return cli_complete_notify(a->line, a->word, a->pos, a->n);
 	}
 
-	if (a->argc < 4) {
+	if (a->argc < 5) {
 		return CLI_SHOWUSAGE;
 	}
 
 	cfg = ao2_global_obj_ref(globals);
 
-	if (!(option = notify_option_find(cfg->notify_options, a->argv[2])))
+	if (!(option = notify_option_find(cfg->notify_options, a->argv[3])))
 	{
 		ast_cli(a->fd, "Unable to find notify type '%s'\n",
-			a->argv[2]);
+			a->argv[3]);
 		return CLI_FAILURE;
 	}
 
-	for (i = 3; i < a->argc; ++i) {
+	for (i = 4; i < a->argc; ++i) {
 		ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n",
-			a->argv[2], a->argv[i]);
+			a->argv[3], a->argv[i]);
 
 		switch (push_notify(a->argv[i], option,
 				    notify_cli_data_create)) {
@@ -641,11 +659,11 @@
  */
 static int manager_notify(struct mansession *s, const struct message *m)
 {
-	const char *endpoint_name = astman_get_header(m, "Channel");
+	const char *endpoint_name = astman_get_header(m, "Endpoint");
 	struct ast_variable *vars = astman_get_variables(m);
 
 	if (ast_strlen_zero(endpoint_name)) {
-		astman_send_error(s, m, "PJSIPNotify requires a channel name");
+		astman_send_error(s, m, "PJSIPNotify requires an endpoint name");
 		return 0;
 	}
 
@@ -668,7 +686,7 @@
 		break;
 	}
 
-	astman_send_ack(s, m, "Notify Sent");
+	astman_send_ack(s, m, "NOTIFY sent");
 	return 0;
 }
 
@@ -706,7 +724,7 @@
 	return 0;
 }
 
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "CLI/AMI PJSIP Notify Support",
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "CLI/AMI PJSIP NOTIFY Support",
 		.load = load_module,
 		.reload = reload_module,
 		.unload = unload_module,

Modified: trunk/res/res_pjsip_outbound_registration.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_pjsip_outbound_registration.c?view=diff&rev=396087&r1=396086&r2=396087
==============================================================================
--- trunk/res/res_pjsip_outbound_registration.c (original)
+++ trunk/res/res_pjsip_outbound_registration.c Fri Aug  2 07:40:03 2013
@@ -30,6 +30,7 @@
 #include "asterisk/res_pjsip.h"
 #include "asterisk/module.h"
 #include "asterisk/taskprocessor.h"
+#include "asterisk/cli.h"
 
 /*** DOCUMENTATION
 	<configInfo name="res_pjsip_outbound_registration" language="en_US">
@@ -90,6 +91,17 @@
 			</configObject>
 		</configFile>
 	</configInfo>
+	<manager name="PJSIPUnregister" language="en_US">
+		<synopsis>
+			Unregister an outbound registration.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Registration" required="true">
+				<para>The outbound registration to unregister.</para>
+			</parameter>
+		</syntax>
+	</manager>
  ***/
 
 /*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */
@@ -209,7 +221,7 @@
 /*! \brief Timer callback function, used just for registrations */
 static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
 {
-	RAII_VAR(struct sip_outbound_registration_client_state *, client_state, entry->user_data, ao2_cleanup);
+	struct sip_outbound_registration_client_state *client_state = entry->user_data;
 
 	ao2_ref(client_state, +1);
 	if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
@@ -333,7 +345,10 @@
 		pjsip_tx_data *tdata;
 		if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,
 				response->rdata, response->tsx, &tdata)) {
-			pjsip_regc_send(response->client_state->client, tdata);
+			ao2_ref(response->client_state, +1);
+			if (pjsip_regc_send(response->client_state->client, tdata) != PJ_SUCCESS) {
+				ao2_cleanup(response->client_state);
+			}
 			return 0;
 		}
 		/* Otherwise, fall through so the failure is processed appropriately */
@@ -388,6 +403,7 @@
 	response->code = param->code;
 	response->expiration = param->expiration;
 	response->client_state = client_state;
+	ao2_ref(response->client_state, +1);
 
 	if (param->rdata) {
 		struct pjsip_retry_after_hdr *retry_after = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_RETRY_AFTER, NULL);
@@ -396,8 +412,6 @@
 		response->tsx = pjsip_rdata_get_tsx(param->rdata);
 		pjsip_rx_data_clone(param->rdata, 0, &response->rdata);
 	}
-
-	ao2_ref(response->client_state, +1);
 
 	if (ast_sip_push_task(client_state->serializer, handle_registration_response, response)) {
 		ast_log(LOG_WARNING, "Failed to pass incoming registration response to threadpool\n");
@@ -703,6 +717,147 @@
 
 	return ast_sip_auth_array_init(&registration->outbound_auths, var->value);
 }
+
+static struct sip_outbound_registration *retrieve_registration(const char *registration_name)
+{
+	return ast_sorcery_retrieve_by_id(
+		ast_sip_get_sorcery(),
+		"registration",
+		registration_name);
+}
+
+static int unregister_task(void *obj)
+{
+	RAII_VAR(struct sip_outbound_registration*, registration, obj, ao2_cleanup);
+	struct pjsip_regc *client = registration->state->client_state->client;
+	pjsip_tx_data *tdata;
+
+	if (pjsip_regc_unregister(client, &tdata) != PJ_SUCCESS) {
+		return 0;
+	}
+
+	ao2_ref(registration->state->client_state, +1);
+	if (pjsip_regc_send(client, tdata) != PJ_SUCCESS) {
+		ao2_cleanup(registration->state->client_state);
+	}
+
+	return 0;
+}
+
+static int queue_unregister(struct sip_outbound_registration *registration)
+{
+	ao2_ref(registration, +1);
+	if (ast_sip_push_task(registration->state->client_state->serializer, unregister_task, registration)) {
+		ao2_cleanup(registration);
+		return -1;
+	}
+	return 0;
+}
+
+static char *cli_complete_registration(const char *line, const char *word,
+int pos, int state)
+{
+	char *result = NULL;
+	int wordlen;
+	int which = 0;
+	struct sip_outbound_registration *registration;
+	RAII_VAR(struct ao2_container *, registrations, NULL, ao2_cleanup);
+	struct ao2_iterator i;
+
+	if (pos != 3) {
+		return NULL;
+	}
+
+	wordlen = strlen(word);
+	registrations = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration",
+		AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+	if (!registrations) {
+		return NULL;
+	}
+
+	i = ao2_iterator_init(registrations, 0);
+	while ((registration = ao2_iterator_next(&i))) {
+		const char *name = ast_sorcery_object_get_id(registration);
+		if (!strncasecmp(word, name, wordlen) && ++which > state) {
+			result = ast_strdup(name);
+		}
+
+		ao2_cleanup(registration);
+		if (result) {
+			break;
+		}
+	}
+	ao2_iterator_destroy(&i);
+	return result;
+}
+
+static char *cli_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	RAII_VAR(struct sip_outbound_registration *, registration, NULL, ao2_cleanup);
+	const char *registration_name;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "pjsip send unregister";
+		e->usage =
+			"Usage: pjsip send unregister <registration>\n"
+			"       Send a SIP REGISTER request to the specified outbound "
+			"registration with an expiration of 0. This will cause the contact "
+			"added by this registration to be removed on the remote system.\n";
+		return NULL;
+	case CLI_GENERATE:
+		return cli_complete_registration(a->line, a->word, a->pos, a->n);
+	}
+
+	if (a->argc != 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	registration_name = a->argv[3];
+
+	registration = retrieve_registration(registration_name);
+	if (!registration) {
+		ast_cli(a->fd, "Unable to retrieve registration %s\n", registration_name);
+		return CLI_FAILURE;
+	}
+
+	if (queue_unregister(registration)) {
+		ast_cli(a->fd, "Failed to queue unregistration");
+		return 0;
+	}
+
+	return CLI_SUCCESS;
+}
+
+static int ami_unregister(struct mansession *s, const struct message *m)
+{
+	const char *registration_name = astman_get_header(m, "Registration");
+	RAII_VAR(struct sip_outbound_registration *, registration, NULL, ao2_cleanup);
+
+	if (ast_strlen_zero(registration_name)) {
+		astman_send_error(s, m, "Registration parameter missing.");
+		return 0;
+	}
+
+	registration = retrieve_registration(registration_name);
+	if (!registration) {
+		astman_send_error(s, m, "Unable to retrieve registration entry\n");
+		return 0;
+	}
+
+
+	if (queue_unregister(registration)) {
+		astman_send_ack(s, m, "Failed to queue unregistration");
+		return 0;
+	}
+
+	astman_send_ack(s, m, "Unregistration sent");
+	return 0;
+}
+
+static struct ast_cli_entry cli_outbound_registration[] = {
+	AST_CLI_DEFINE(cli_unregister, "Send a REGISTER request to an outbound registration target with a expiration of 0")
+};
 
 static int load_module(void)
 {
@@ -726,6 +881,8 @@
 	ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
 	sip_outbound_registration_perform_all();
 
+	ast_cli_register_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
+	ast_manager_register_xml("PJSIPUnregister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_unregister);
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
@@ -738,6 +895,8 @@
 
 static int unload_module(void)
 {
+	ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
+	ast_manager_unregister("PJSIPUnregister");
 	return 0;
 }
 




More information about the asterisk-commits mailing list