summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
authorKinsey Moore <kmoore@digium.com>2013-08-02 12:40:03 +0000
committerKinsey Moore <kmoore@digium.com>2013-08-02 12:40:03 +0000
commit41cd06e03f6e4a783fbe5323efe71990cf35dfb8 (patch)
tree4a83dc964a5993211f8354228e8ab22cb9b0cf46 /res
parent4f07502000d3c9ca3195629597dc0ecf5a25d94d (diff)
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) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396087 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res')
-rw-r--r--res/res_pjsip.c15
-rw-r--r--res/res_pjsip/include/res_pjsip_private.h5
-rw-r--r--res/res_pjsip/pjsip_options.c65
-rw-r--r--res/res_pjsip_notify.c44
-rw-r--r--res/res_pjsip_outbound_registration.c167
5 files changed, 279 insertions, 17 deletions
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 2326c9add..81816a5fa 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -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_pjsip(void *data)
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();
diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h
index 20586b708..6011b25bf 100644
--- a/res/res_pjsip/include/res_pjsip_private.h
+++ b/res/res_pjsip/include/res_pjsip_private.h
@@ -71,4 +71,9 @@ int ast_sip_initialize_system(void);
*/
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_ */
diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c
index e833a5056..ce0eeb2b7 100644
--- a/res/res_pjsip/pjsip_options.c
+++ b/res/res_pjsip/pjsip_options.c
@@ -652,6 +652,64 @@ static char *cli_qualify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
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 @@ int ast_res_pjsip_init_options_handling(int reload)
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");
+}
diff --git a/res/res_pjsip_notify.c b/res/res_pjsip_notify.c
index 8bda962cb..2f0cfd31a 100644
--- a/res/res_pjsip_notify.c
+++ b/res/res_pjsip_notify.c
@@ -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 @@ static char *cli_complete_notify(const char *line, const char *word,
{
char *c = NULL;
- if (pos == 2) {
+ if (pos == 3) {
int which = 0;
int wordlen = strlen(word);
@@ -564,7 +582,7 @@ static char *cli_complete_notify(const char *line, const char *word,
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 @@ static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
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 @@ static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
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 struct ast_cli_entry cli_options[] = {
*/
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 @@ static int manager_notify(struct mansession *s, const struct message *m)
break;
}
- astman_send_ack(s, m, "Notify Sent");
+ astman_send_ack(s, m, "NOTIFY sent");
return 0;
}
@@ -706,7 +724,7 @@ static int unload_module(void)
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,
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 6d9368ec1..c104b3edf 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -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 @@ static int handle_client_registration(void *data)
/*! \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 @@ static int handle_registration_response(void *data)
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 @@ static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *par
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);
@@ -397,8 +413,6 @@ static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *par
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");
ao2_cleanup(response);
@@ -704,6 +718,147 @@ static int outbound_auth_handler(const struct aco_option *opt, struct ast_variab
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)
{
ast_sorcery_apply_default(ast_sip_get_sorcery(), "registration", "config", "pjsip.conf,criteria=type=registration");
@@ -726,6 +881,8 @@ static int load_module(void)
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 reload_module(void)
static int unload_module(void)
{
+ ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
+ ast_manager_unregister("PJSIPUnregister");
return 0;
}