Asterisk - The Open Source Telephony Project GIT-master-70eff7f
Loading...
Searching...
No Matches
res_pjsip_config_wizard.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2014, Fairview 5 Engineering, LLC
5 *
6 * George Joseph <george.joseph@fairview5.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief PJSIP Configuration Wizard
22 *
23 * \author George Joseph <george.joseph@fairview5.com>
24 */
25
26/*! \li \ref res_pjsip_config_wizard.c uses the configuration file \ref pjsip_wizard.conf
27 */
28
29/*!
30 * \page pjsip_wizard.conf pjsip_wizard.conf
31 * \verbinclude pjsip_wizard.conf.sample
32 */
33
34/*** MODULEINFO
35 <depend>pjproject</depend>
36 <depend>res_pjsip</depend>
37 <support_level>core</support_level>
38 ***/
39
40#include "asterisk.h"
41
42#include <regex.h>
43#include <pjsip.h>
44
45#include "asterisk/astobj2.h"
46#include "asterisk/cli.h"
47#include "asterisk/res_pjsip.h"
48#include "asterisk/module.h"
49#include "asterisk/pbx.h"
50#include "asterisk/sorcery.h"
51#include "asterisk/vector.h"
52#include "asterisk/stasis.h"
53#include "asterisk/acl.h"
55#include "asterisk/lock.h"
56
57/*** DOCUMENTATION
58 <configInfo name="res_pjsip_config_wizard" language="en_US">
59 <synopsis>Module that provides simple configuration wizard capabilities.</synopsis>
60 <description><para>
61 <emphasis>PJSIP Configuration Wizard</emphasis>
62 </para>
63 <para>This module allows creation of common PJSIP configuration scenarios
64 without having to specify individual endpoint, aor, auth, identify and registration objects.
65 </para>
66 <para> </para>
67
68 <para>For example, the following configuration snippet would create the
69 endpoint, aor, contact, auth and phoneprov objects necessary for a phone to
70 get phone provisioning information, register, and make and receive calls.
71 A hint is also created in the default context for extension 1000.</para>
72 <example title="myphone">
73 [myphone]
74 type = wizard
75 sends_auth = no
76 accepts_auth = yes
77 sends_registrations = no
78 accepts_registrations = yes
79 has_phoneprov = yes
80 transport = ipv4
81 has_hint = yes
82 hint_exten = 1000
83 inbound_auth/username = testname
84 inbound_auth/password = test password
85 endpoint/allow = ulaw
86 endpoint/context = default
87 phoneprov/MAC = 001122aa4455
88 phoneprov/PROFILE = profile1
89 </example>
90
91 <para>The first 8 items are specific to the wizard. The rest of the items
92 are passed verbatim to the underlying objects.</para>
93 <para> </para>
94
95 <para>The following configuration snippet would create the
96 endpoint, aor, contact, auth, identify and registration objects necessary for a trunk
97 to another pbx or ITSP that requires registration.</para>
98 <example title="mytrunk">
99 [mytrunk]
100 type = wizard
101 sends_auth = yes
102 accepts_auth = no
103 sends_registrations = yes
104 accepts_registrations = no
105 transport = ipv4
106 remote_hosts = sip1.myitsp.com:5060,sip2.myitsp.com:5060
107 outbound_auth/username = testname
108 outbound_auth/password = test password
109 endpoint/allow = ulaw
110 endpoint/context = default
111 </example>
112
113 <para>Of course, any of the items in either example could be placed into
114 templates and shared among wizard objects.</para>
115
116 <para> </para>
117 <para>For more information, visit:</para>
118 <para><literal>https://docs.asterisk.org/Configuration/Channel-Drivers/SIP/Configuring-res_pjsip/PJSIP-Configuration-Wizard/</literal></para>
119 </description>
120
121 <configFile name="pjsip_wizard.conf">
122 <configObject name="wizard">
123 <synopsis>Provides config wizard.</synopsis>
124 <description>
125 <para>For more information, visit:</para>
126 <para><literal>https://docs.asterisk.org/Configuration/Channel-Drivers/SIP/Configuring-res_pjsip/PJSIP-Configuration-Wizard/</literal></para>
127 </description>
128 <configOption name="type">
129 <synopsis>Must be 'wizard'.</synopsis>
130 </configOption>
131 <configOption name="transport">
132 <synopsis>The name of a transport to use for this object.</synopsis>
133 <description><para>If not specified,
134 the default will be used.</para></description>
135 </configOption>
136 <configOption name="remote_hosts">
137 <synopsis>List of remote hosts.</synopsis>
138 <description><para>A comma-separated list of remote hosts in the form of
139 <replaceable>host</replaceable>[:<replaceable>port</replaceable>].
140 If set, an aor static contact and an identify match will be created for each
141 entry in the list. If sends_registrations is also set, a registration will
142 also be created for each.</para></description>
143 </configOption>
144 <configOption name="outbound_proxy">
145 <synopsis>Shortcut for specifying proxy on individual objects.</synopsis>
146 <description><para>Shortcut for specifying endpoint/outbound_proxy,
147 aor/outbound_proxy, and registration/outbound_proxy individually.
148 </para></description>
149 </configOption>
150 <configOption name="sends_auth" default="no">
151 <synopsis>Send outbound authentication to remote hosts.</synopsis>
152 <description><para>At least outbound_auth/username is required.</para></description>
153 </configOption>
154 <configOption name="accepts_auth" default="no">
155 <synopsis>Accept incoming authentication from remote hosts.</synopsis>
156 <description><para>At least inbound_auth/username is required.</para></description>
157 </configOption>
158 <configOption name="sends_registrations" default="no">
159 <synopsis>Send outbound registrations to remote hosts.</synopsis>
160 <description><para>remote_hosts is required and a registration object will
161 be created for each host in the remote_hosts string. If authentication is required,
162 sends_auth and an outbound_auth/username must also be supplied.</para></description>
163 </configOption>
164 <configOption name="sends_line_with_registrations" default="no">
165 <synopsis>Sets "line" and "endpoint" parameters on registrations.</synopsis>
166 <description><para>Setting this to true will cause the wizard to skip the
167 creation of an identify object to match incoming requests to the endpoint and
168 instead add the line and endpoint parameters to the outbound registration object.
169 </para></description>
170 </configOption>
171 <configOption name="accepts_registrations" default="no">
172 <synopsis>Accept inbound registration from remote hosts.</synopsis>
173 <description><para>An AOR with dynamic contacts will be created. If
174 the number of contacts needs to be limited, set aor/max_contacts.</para></description>
175 </configOption>
176 <configOption name="has_phoneprov" default="no">
177 <synopsis>Create a phoneprov object for this endpoint.</synopsis>
178 <description><para>A phoneprov object will be created. phoneprov/MAC
179 must be specified.</para></description>
180 </configOption>
181 <configOption name="server_uri_pattern" default="sip:${REMOTE_HOST}">
182 <synopsis>A pattern to use for constructing outbound registration server_uris.</synopsis>
183 <description><para>
184 The literal <literal>${REMOTE_HOST}</literal> will be substituted with the
185 appropriate remote_host for each registration.</para></description>
186 </configOption>
187 <configOption name="client_uri_pattern" default="sip:${USERNAME}@${REMOTE_HOST}">
188 <synopsis>A pattern to use for constructing outbound registration client_uris.</synopsis>
189 <description><para>
190 The literals <literal>${REMOTE_HOST}</literal> and <literal>${USERNAME}</literal>
191 will be substituted with the appropriate remote_host and outbound_auth/username.</para></description>
192 </configOption>
193 <configOption name="contact_pattern" default="sip:${REMOTE_HOST}">
194 <synopsis>A pattern to use for constructing outbound contact uris.</synopsis>
195 <description><para>
196 The literal <literal>${REMOTE_HOST}</literal> will be substituted with the
197 appropriate remote_host for each contact.</para></description>
198 </configOption>
199 <configOption name="has_hint" default="no">
200 <synopsis>Create hint and optionally a default application.</synopsis>
201 <description><para>Create hint and optionally a default application.</para></description>
202 </configOption>
203 <configOption name="hint_context" default="endpoint/context or 'default'">
204 <synopsis>The context in which to place hints.</synopsis>
205 <description>
206 <para>Ignored if <literal>hint_exten</literal> is not specified otherwise specifies the
207 context into which the dialplan hints will be placed. If not specified,
208 defaults to the endpoint's context or <literal>default</literal> if that isn't
209 found.
210 </para></description>
211 </configOption>
212 <configOption name="hint_exten">
213 <synopsis>Extension to map a PJSIP hint to.</synopsis>
214 <description>
215 <para>Will create the following entry in <literal>hint_context</literal>:</para>
216 <para> <literal>exten =&gt; &lt;hint_exten&gt;,hint,PJSIP/&lt;wizard_id&gt;</literal></para>
217 <para> </para>
218 <para>Normal dialplan precedence rules apply so if there's already a hint for
219 this extension in <literal>hint_context</literal>, this one will be ignored.
220 For more information, visit: </para>
221 <para><literal>https://docs.asterisk.org/Configuration/Channel-Drivers/SIP/Configuring-res_pjsip/PJSIP-Configuration-Wizard/</literal></para>
222 </description>
223 </configOption>
224 <configOption name="hint_application">
225 <synopsis>Application to call when 'hint_exten' is dialed.</synopsis>
226 <description>
227 <para>Ignored if <literal>hint_exten</literal> isn't specified otherwise
228 will create the following priority 1 extension in <literal>hint_context</literal>:</para>
229 <para> <literal>exten =&gt; &lt;hint_exten&gt;,1,&lt;hint_application&gt;</literal></para>
230 <para> </para>
231 <para>You can specify any valid extensions.conf application expression.</para>
232 <example title="Valid expressions">
233 Dial(${HINT})
234 Gosub(stdexten,${EXTEN},1(${HINT}))
235 </example>
236 <para>Any extensions.conf style variables specified are passed directly to the
237 dialplan.</para>
238 <para> </para>
239 <para>Normal dialplan precedence rules apply so if there's already a priority 1
240 application for this specific extension in <literal>hint_context</literal>,
241 this one will be ignored. For more information, visit: </para>
242 <para><literal>https://docs.asterisk.org/Configuration/Channel-Drivers/SIP/Configuring-res_pjsip/PJSIP-Configuration-Wizard/</literal></para>
243 </description>
244 </configOption>
245 <configOption name="endpoint&#47;*">
246 <synopsis>Variables to be passed directly to the endpoint.</synopsis>
247 </configOption>
248 <configOption name="aor&#47;*">
249 <synopsis>Variables to be passed directly to the aor.</synopsis>
250 <description><para>If an aor/contact is explicitly defined then remote_hosts
251 will not be used to create contacts automatically.</para></description>
252 </configOption>
253 <configOption name="inbound_auth&#47;*">
254 <synopsis>Variables to be passed directly to the inbound auth.</synopsis>
255 </configOption>
256 <configOption name="outbound_auth&#47;*">
257 <synopsis>Variables to be passed directly to the outbound auth.</synopsis>
258 </configOption>
259 <configOption name="identify&#47;*">
260 <synopsis>Variables to be passed directly to the identify.</synopsis>
261 <description><para>If an identify/match is explicitly defined then remote_hosts
262 will not be used to create matches automatically.</para></description>
263 </configOption>
264 <configOption name="registration&#47;*">
265 <synopsis>Variables to be passed directly to the outbound registrations.</synopsis>
266 </configOption>
267 <configOption name="phoneprov&#47;*">
268 <synopsis>Variables to be passed directly to the phoneprov object.</synopsis>
269 <description><para>
270 To activate phoneprov, at least phoneprov/MAC must be set.</para></description>
271 </configOption>
272 </configObject>
273 </configFile>
274 </configInfo>
275 ***/
276
277 /*! \brief Defines the maximum number of characters that can be added to a wizard id. */
278#define MAX_ID_SUFFIX 20
279
280#define BASE_REGISTRAR "res_pjsip_config_wizard"
281
282/*! \brief A generic char * vector definition. */
284
285/*! \brief Keeps track of the sorcery wizard and last config for each object type */
293static AST_VECTOR_RW(object_type_wizards, struct object_type_wizard *) object_type_wizards;
294
295/*! \brief Callbacks for vector deletes */
296#define NOT_EQUALS(a, b) (a != b)
297#define OTW_DELETE_CB(otw) ({ \
298 ast_config_destroy(otw->last_config); \
299 ast_free(otw); \
300})
301
302const static char *object_types[] = {"phoneprov", "registration", "identify", "endpoint", "aor", "auth", NULL};
303
304/*
305 * Sorcery observers do not receive the reload reason, so track Named ACL
306 * initiated reloads while they are pending or running.
307 */
311
312#define IS_ACL_RELOAD_ACTIVE() (ast_atomic_fetchadd_int(&named_acl_reload_count, 0) > 0)
313
314static int reload_module(void);
315
316/*! \brief Callback for Named ACL changed */
318{
320 return;
321 }
322
323 ast_debug(3, "PJSIP Wizard: Named ACL change detected via Stasis. Triggering reload.\n");
327}
328
329static int is_one_of(const char *needle, const char *haystack[])
330{
331 int i;
332 for (i = 0; haystack[i]; i++) {
333 if (!strcmp(needle, haystack[i])) {
334 return 1;
335 }
336 }
337
338 return 0;
339}
340
341/*! \brief Finds the otw for the object type */
342static struct object_type_wizard *find_wizard(const char *object_type)
343{
344 int idx;
345
346 AST_VECTOR_RW_RDLOCK(&object_type_wizards);
347 for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
348 struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
349 if (!strcmp(otw->object_type, object_type)) {
350 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
351 return otw;
352 }
353 }
354 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
355
356 return NULL;
357}
358
359/*! \brief Creates a sorcery object and applies a variable list */
360static void *create_object(const struct ast_sorcery *sorcery,
361 const char *id, const char *type, struct ast_variable *vars)
362{
364
365 if (!obj) {
366 ast_log(LOG_ERROR, "Unable to allocate an object of type '%s' with id '%s'.\n", type, id);
367 return NULL;
368 }
369
370 if (ast_sorcery_objectset_apply(sorcery, obj, vars)) {
371 ast_log(LOG_ERROR, "Unable to apply object type '%s' with id '%s'. Check preceeding errors.\n", type, id);
372 ao2_ref(obj, -1);
373 return NULL;
374 }
375
376 return obj;
377}
378
379/*! \brief Finds the last variable in a list and tests it */
380static int is_variable_true(struct ast_variable *vars, const char *name)
381{
383}
384
385/*! \brief Appends a variable to the end of an existing list */
386static int variable_list_append(struct ast_variable **existing, const char *name, const char *value)
387{
388 struct ast_variable *new = ast_variable_new(name, value, "");
389
390 if (!new) {
391 ast_log(LOG_ERROR, "Unable to allocate memory for new variable '%s'.\n", name);
392 return -1;
393 }
394
395 ast_variable_list_append(existing, new);
396
397 return 0;
398}
399
400/*! \brief Appends a variable to the end of an existing list. On failure, cause the calling
401 * function to return -1 */
402#define variable_list_append_return(existing, name, value) ({ \
403 struct ast_variable *new = ast_variable_new(name, value, ""); \
404 if (!new) { \
405 ast_log(LOG_ERROR, "Unable to allocate memory for new variable '%s'.\n", name); \
406 return -1; \
407 } \
408 ast_variable_list_append(existing, new); \
409})
410
411/*! \brief We need to strip off the prefix from the name of each variable
412 * so they're suitable for objectset_apply.
413 * I.E. will transform outbound_auth/username to username.
414 */
415static struct ast_variable *get_object_variables(struct ast_variable *vars, char *prefix)
416{
417 struct ast_variable *return_vars = NULL;
418 struct ast_variable *v = vars;
419 int plen = strlen(prefix);
420
421 for(; v; v = v->next) {
422 if (ast_begins_with(v->name, prefix) && strlen(v->name) > plen) {
423 if (variable_list_append(&return_vars, v->name + plen, v->value)) {
424 ast_variables_destroy(return_vars);
425 return NULL;
426 }
427 }
428 }
429
430 return return_vars;
431}
432
433/* Don't call while holding context locks. */
434static int delete_extens(const char *context, const char *exten)
435{
436 struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
437
438 if (pbx_find_extension(NULL, NULL, &find_info, context, exten, PRIORITY_HINT, NULL, NULL, E_MATCH)) {
440 }
441
442 if (pbx_find_extension(NULL, NULL, &find_info, context, exten, 1, NULL, NULL, E_MATCH)) {
444 }
445
446 return 0;
447}
448
449static int add_extension(struct ast_context *context, const char *exten,
450 int priority, const char *application)
451{
452 struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
453 struct ast_exten *existing_exten;
454 char *data = NULL;
455 char *app = NULL;
456 void *free_ptr = NULL;
457 const char *paren;
458 const char *context_name;
459
460 if (!context || ast_strlen_zero(exten) || ast_strlen_zero(application)) {
461 return -1;
462 }
463
464 /* The incoming application has to be split into the app name and the
465 * arguments (data). The app name can be any storage type as add_extension
466 * copies it into its own buffer. Data however, needs to be dynamically
467 * allocated and a free function provided.
468 */
469
470 paren = strchr(application, '(');
471 if (!paren) {
472 app = (char *)application;
473 } else {
474 app = ast_strdupa(application);
475 app[paren - application] = '\0';
476 data = ast_strdup(paren + 1);
477 if (!data) {
478 return -1;
479 }
480 data[strlen(data) - 1] = '\0';
481 free_ptr = ast_free_ptr;
483 ast_free(data);
484 return -1;
485 }
486 }
487
488 /* Don't disturb existing, exact-match, entries. */
490 if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, context_name, exten,
491 priority, NULL, NULL, E_MATCH))) {
492 const char *existing_app = ast_get_extension_app(existing_exten);
493 const char *existing_data = ast_get_extension_app_data(existing_exten);
494 if (!strcmp(existing_app, app)
495 && !strcmp(existing_data ? existing_data : "", data ? data : "")) {
496 ast_free(data);
497 return 0;
498 }
499
501 }
502
504 app, data, free_ptr, BASE_REGISTRAR, NULL, 0)) {
505 return -1;
506 }
507
508 return 0;
509}
510
511static int add_hints(const char *context, const char *exten, const char *application, const char *id)
512{
513 struct ast_context *hint_context;
514 char *hint_device;
515
516 hint_device = ast_alloca(strlen("PJSIP/") + strlen(id) + 1);
517 sprintf(hint_device, "PJSIP/%s", id);
518
519 /* We need the contexts list locked to safely be able to both read and lock the specific context within */
520 if (ast_wrlock_contexts()) {
521 ast_log(LOG_ERROR, "Failed to lock the contexts list.\n");
522 return -1;
523 }
524
525 if (!(hint_context = ast_context_find_or_create(NULL, NULL, context, BASE_REGISTRAR))) {
526 ast_log(LOG_ERROR, "Unable to find or create hint context '%s'\n", context);
527 if (ast_unlock_contexts()) {
528 ast_assert(0);
529 }
530 return -1;
531 }
532
533 /* Transfer the all-contexts lock to the specific context */
534 if (ast_wrlock_context(hint_context)) {
536 ast_log(LOG_ERROR, "failed to obtain write lock on context\n");
537 return -1;
538 }
540
541 if (add_extension(hint_context, exten, PRIORITY_HINT, hint_device)) {
542 ast_log(LOG_ERROR, "Failed to add hint '%s@%s' to the PBX.\n",
543 exten, context);
544 }
545
546 if (!ast_strlen_zero(application)) {
547 if (add_extension(hint_context, exten, 1, application)) {
548 ast_log(LOG_ERROR, "Failed to add hint '%s@%s' to the PBX.\n",
549 exten, context);
550 }
551 } else {
552 ast_context_remove_extension2(hint_context, exten, 1, BASE_REGISTRAR, 1);
553 }
554
555 ast_unlock_context(hint_context);
556
557 return 0;
558}
559
560static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
561 struct ast_category *wiz, char *direction)
562{
563 struct ast_variable *wizvars = ast_category_first(wiz);
564 struct ast_sorcery_object *obj = NULL;
565 const char *id = ast_category_get_name(wiz);
566 char new_id[strlen(id) + MAX_ID_SUFFIX];
567 char prefix[strlen(direction) + strlen("_auth/") + 1];
568 char *test_variable = NULL;
570
571 snprintf(prefix, sizeof(prefix), "%s_auth/", direction);
572 vars = get_object_variables(wizvars, prefix);
573
574 if (!strcmp(direction, "outbound")) {
575 snprintf(new_id, sizeof(new_id), "%s-oauth", id);
576 test_variable = "sends_auth";
577 } else {
578 snprintf(new_id, sizeof(new_id), "%s-iauth", id);
579 test_variable = "accepts_auth";
580 }
581
582 if (is_variable_true(wizvars, test_variable)) {
583 if (!ast_variable_find_last_in_list(vars, "username")) {
585 "Wizard '%s' must have '%s_auth/username' if it %s.\n", id, direction, test_variable);
586 return -1;
587 }
588 } else {
589 /* Delete auth if sends or accepts is now false. */
590 obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "auth", new_id);
591 if (obj) {
592 otw->wizard->delete(sorcery, otw->wizard_data, obj);
593 ao2_ref(obj, -1);
594 }
595 return 0;
596 }
597
598 variable_list_append_return(&vars, "@pjsip_wizard", id);
599
600 /* If the user set auth_type, don't override it. */
601 if (!ast_variable_find_last_in_list(vars, "auth_type")) {
602 variable_list_append_return(&vars, "auth_type", "userpass");
603 }
604
605 obj = create_object(sorcery, new_id, "auth", vars);
606 if (!obj) {
607 return -1;
608 }
609
610 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
611 otw->wizard->create(sorcery, otw->wizard_data, obj);
612 }
613 ao2_ref(obj, -1);
614
615 return 0;
616}
617
618static int handle_auths(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
619 struct ast_category *wiz)
620{
621 int rc;
622
623 if ((rc = handle_auth(sorcery, otw, wiz, "outbound"))) {
624 return rc;
625 }
626
627 return handle_auth(sorcery, otw, wiz, "inbound");
628}
629
630static int handle_aor(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
631 struct ast_category *wiz, struct string_vector *remote_hosts_vector)
632{
633 struct ast_variable *wizvars = ast_category_first(wiz);
634 struct ast_sorcery_object *obj = NULL;
635 const char *id = ast_category_get_name(wiz);
636 const char *contact_pattern;
637 const char *outbound_proxy = ast_variable_find_last_in_list(wizvars, "outbound_proxy");
638 int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
639 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "aor/"), ast_variables_destroy);
640
641 variable_list_append(&vars, "@pjsip_wizard", id);
642
643 if (!ast_strlen_zero(outbound_proxy)) {
644 variable_list_append_return(&vars, "outbound_proxy", outbound_proxy);
645 }
646
647 /* If the user explicitly specified an aor/contact, don't use remote hosts. */
648 if (!ast_variable_find_last_in_list(vars, "contact")) {
649 if (!(contact_pattern = ast_variable_find_last_in_list(wizvars, "contact_pattern"))) {
650 contact_pattern = "sip:${REMOTE_HOST}";
651 }
652
653 if (host_count > 0 && !ast_strlen_zero(contact_pattern)) {
654 int host_counter;
655
656 /* ast_str_substitute_variables operate on a varshead list so we have
657 * to create one to hold the REPORT_HOST substitution, do the substitution,
658 * then append the result to the ast_variable list.
659 */
660 for (host_counter = 0; host_counter < host_count; host_counter++) {
661 RAII_VAR(struct ast_str *, new_str, ast_str_create(64), ast_free);
663 struct ast_var_t *var = ast_var_assign("REMOTE_HOST",
664 AST_VECTOR_GET(remote_hosts_vector, host_counter));
665
666 AST_VAR_LIST_INSERT_TAIL(subst_vars, var);
667 ast_str_substitute_variables_varshead(&new_str, 0, subst_vars,
668 contact_pattern);
669
670 variable_list_append_return(&vars, "contact", ast_str_buffer(new_str));
671 }
672 }
673 }
674
675 obj = create_object(sorcery, id, "aor", vars);
676 if (!obj) {
677 return -1;
678 }
679
680 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
681 otw->wizard->create(sorcery, otw->wizard_data, obj);
682 }
683 ao2_ref(obj, -1);
684
685 return 0;
686}
687
688static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
689 struct ast_category *wiz)
690{
691 struct ast_variable *wizvars = ast_category_first(wiz);
692 struct ast_sorcery_object *obj = NULL;
693 const char *id = ast_category_get_name(wiz);
694 const char *outbound_proxy = ast_variable_find_last_in_list(wizvars, "outbound_proxy");
695 const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
696 const char *hint_context = hint_context = ast_variable_find_last_in_list(wizvars, "hint_context");
697 const char *hint_exten = ast_variable_find_last_in_list(wizvars, "hint_exten");
698 const char *hint_application= ast_variable_find_last_in_list(wizvars, "hint_application");
699 char new_id[strlen(id) + MAX_ID_SUFFIX];
700 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "endpoint/"), ast_variables_destroy);
701
702 variable_list_append_return(&vars, "@pjsip_wizard", id);
703 variable_list_append_return(&vars, "aors", id);
704
705 if (!ast_strlen_zero(outbound_proxy)) {
706 variable_list_append_return(&vars, "outbound_proxy", outbound_proxy);
707 }
708
709 if (ast_strlen_zero(hint_context)) {
710 hint_context = ast_variable_find_last_in_list(vars, "context");
711 }
712
713 if (ast_strlen_zero(hint_context)) {
714 hint_context = "default";
715 }
716
717 if (!ast_strlen_zero(hint_exten)) {
718 /* These are added so we can find and delete the hints when the endpoint gets deleted */
719 variable_list_append_return(&vars, "@hint_context", hint_context);
720 variable_list_append_return(&vars, "@hint_exten", hint_exten);
721 }
722
723 if (!ast_strlen_zero(transport)) {
724 variable_list_append_return(&vars, "transport", transport);
725 }
726
727 if (is_variable_true(wizvars, "sends_auth")) {
728 snprintf(new_id, sizeof(new_id), "%s-oauth", id);
729 variable_list_append_return(&vars, "outbound_auth", new_id);
730 }
731
732 if (is_variable_true(wizvars, "accepts_auth")) {
733 snprintf(new_id, sizeof(new_id), "%s-iauth", id);
734 variable_list_append_return(&vars, "auth", new_id);
735 }
736
737 obj = create_object(sorcery, id, "endpoint", vars);
738 if (!obj) {
739 return -1;
740 }
741
742 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
743 otw->wizard->create(sorcery, otw->wizard_data, obj);
744 }
745 ao2_ref(obj, -1);
746
747 if (!ast_strlen_zero(hint_exten)) {
748 if (is_variable_true(wizvars, "has_hint")) {
749 add_hints(hint_context, hint_exten, hint_application, id);
750 } else {
751 delete_extens(hint_context, hint_exten);
752 }
753 }
754
755 return 0;
756}
757
758static int handle_identify(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
759 struct ast_category *wiz, struct string_vector *remote_hosts_vector)
760{
761 struct ast_variable *wizvars = ast_category_first(wiz);
762 struct ast_sorcery_object *obj = NULL;
763 const char *id = ast_category_get_name(wiz);
764 char new_id[strlen(id) + MAX_ID_SUFFIX];
765 int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
766 int host_counter;
767 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "identify/"), ast_variables_destroy);
768
769 snprintf(new_id, sizeof(new_id), "%s-identify", id);
770
771 /* If accepting registrations or we're sending line, we don't need an identify. */
772 if (is_variable_true(wizvars, "accepts_registrations")
773 || is_variable_true(wizvars, "sends_line_with_registrations")) {
774 /* If one exists, delete it. */
775 obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "identify", new_id);
776 if (obj) {
777 otw->wizard->delete(sorcery, otw->wizard_data, obj);
778 ao2_ref(obj, -1);
779 }
780 return 0;
781 }
782
783 if (!host_count) {
785 "Wizard '%s' must have 'remote_hosts' if it doesn't accept registrations.\n", id);
786 return -1;
787 }
788
789 variable_list_append_return(&vars, "endpoint", id);
790 variable_list_append_return(&vars, "@pjsip_wizard", id);
791
792 if (!ast_variable_find_last_in_list(vars, "match")) {
793 for (host_counter = 0; host_counter < host_count; host_counter++) {
794 variable_list_append_return(&vars, "match",
795 AST_VECTOR_GET(remote_hosts_vector, host_counter));
796 }
797 }
798
799 obj = create_object(sorcery, new_id, "identify", vars);
800 if (!obj) {
801 return -1;
802 }
803
804 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
805 otw->wizard->create(sorcery, otw->wizard_data, obj);
806 }
807 ao2_ref(obj, -1);
808
809 return 0;
810}
811
812static int handle_phoneprov(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
813 struct ast_category *wiz)
814{
815 struct ast_variable *wizvars = ast_category_first(wiz);
816 struct ast_sorcery_object *obj = NULL;
817 const char *id = ast_category_get_name(wiz);
818 char new_id[strlen(id) + MAX_ID_SUFFIX];
819 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "phoneprov/"), ast_variables_destroy);
820
821 snprintf(new_id, sizeof(new_id), "%s-phoneprov", id);
822
823 if (!is_variable_true(wizvars, "has_phoneprov")) {
824 obj = otw->wizard->retrieve_id(sorcery, otw->wizard_data, "phoneprov", new_id);
825 if (obj) {
826 otw->wizard->delete(sorcery, otw->wizard_data, obj);
827 ao2_ref(obj, -1);
828 }
829 return 0;
830 }
831
832 if (!ast_variable_find_last_in_list(wizvars, "phoneprov/MAC")) {
834 "Wizard '%s' must have 'phoneprov/MAC' if it has_phoneprov.\n", id);
835 return -1;
836 }
837
838 variable_list_append_return(&vars, "endpoint", id);
839 variable_list_append_return(&vars, "@pjsip_wizard", id);
840
841 obj = create_object(sorcery, new_id, "phoneprov", vars);
842 if (!obj) {
843 return -1;
844 }
845
846 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
847 otw->wizard->create(sorcery, otw->wizard_data, obj);
848 }
849 ao2_ref(obj, -1);
850
851 return 0;
852}
853
854static int delete_existing_cb(void *obj, void *arg, int flags)
855{
856 struct object_type_wizard *otw = arg;
857
858 if (!strcmp(otw->object_type, "endpoint")) {
859 const char *context = ast_sorcery_object_get_extended(obj, "hint_context");
860 const char *exten = ast_sorcery_object_get_extended(obj, "hint_exten");
861 if (!ast_strlen_zero(context) && !ast_strlen_zero(exten)) {
862 delete_extens(context, exten);
863 }
864 }
865
866 otw->wizard->delete(otw->sorcery, otw->wizard_data, obj);
867
868 return CMP_MATCH;
869}
870
871static int handle_registrations(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
872 struct ast_category *wiz, struct string_vector *remote_hosts_vector)
873{
874 struct ast_variable *search;
875 struct ast_variable *wizvars = ast_category_first(wiz);
876 const char *id = ast_category_get_name(wiz);
877 const char *server_uri_pattern;
878 const char *client_uri_pattern;
879 const char *outbound_proxy = ast_variable_find_last_in_list(wizvars, "outbound_proxy");
880 const char *transport = ast_variable_find_last_in_list(wizvars, "transport");
881 const char *username;
882 char new_id[strlen(id) + MAX_ID_SUFFIX];
883 int host_count = AST_VECTOR_SIZE(remote_hosts_vector);
884 int host_counter;
885 RAII_VAR(struct ast_variable *, vars, get_object_variables(wizvars, "registration/"), ast_variables_destroy);
886 RAII_VAR(struct ao2_container *, existing,
888
889 if (!existing) {
890 return -1;
891 }
892
893 /* Find any existing registrations. */
894 search = ast_variable_new("@pjsip_wizard", id, "");
895 if (!search) {
896 return -1;
897 }
898
899 if (!ast_strlen_zero(outbound_proxy)) {
900 variable_list_append_return(&vars, "outbound_proxy", outbound_proxy);
901 }
902
903 otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, "registration", existing, search);
904 ast_variables_destroy(search);
905
906 /* If not sending registrations, delete ALL existing registrations for this wizard. */
907 if (!is_variable_true(wizvars, "sends_registrations")) {
908 if (ao2_container_count(existing) > 0) {
910 }
911 return 0;
912 }
913
914 if (!host_count) {
915 ast_log(LOG_ERROR, "Wizard '%s' must have 'remote_hosts' if it sends registrations.\n", id);
916 return -1;
917 }
918
919 variable_list_append_return(&vars, "@pjsip_wizard", id);
920
921 if (!(server_uri_pattern = ast_variable_find_last_in_list(wizvars, "server_uri_pattern"))) {
922 server_uri_pattern = "sip:${REMOTE_HOST}";
923 }
924
925 if (!(client_uri_pattern = ast_variable_find_last_in_list(wizvars, "client_uri_pattern"))) {
926 client_uri_pattern = "sip:${USERNAME}@${REMOTE_HOST}";
927 }
928
929 if (is_variable_true(wizvars, "sends_auth")) {
930 if (!(username = ast_variable_find_last_in_list(wizvars, "outbound_auth/username"))) {
931 ast_log(LOG_ERROR, "Wizard '%s' must have 'outbound_auth/username' if it sends"
932 " authentication.\n", id);
933 return -1;
934 }
935 } else {
936 username = id;
937 }
938
939
940 /* Unlike aor and identify, we need to create a separate registration object
941 * for each remote host.
942 */
943 for (host_counter = 0; host_counter < host_count; host_counter++) {
944 struct ast_var_t *rh = ast_var_assign("REMOTE_HOST",
945 AST_VECTOR_GET(remote_hosts_vector, host_counter));
946 struct ast_var_t *un = ast_var_assign("USERNAME", username);
947 struct ast_sorcery_object *obj;
948 RAII_VAR(struct ast_str *, uri, ast_str_create(64), ast_free);
950 RAII_VAR(struct ast_variable *, registration_vars, vars ? ast_variables_dup(vars) : NULL, ast_variables_destroy);
951
952 AST_VAR_LIST_INSERT_TAIL(subst_vars, rh);
953 AST_VAR_LIST_INSERT_TAIL(subst_vars, un);
954
955 if (!ast_strlen_zero(server_uri_pattern)) {
956 ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
957 server_uri_pattern);
958 variable_list_append_return(&registration_vars, "server_uri", ast_str_buffer(uri));
959 }
960
961 if (!ast_strlen_zero(client_uri_pattern)) {
962 ast_str_reset(uri);
963 ast_str_substitute_variables_varshead(&uri, 0, subst_vars,
964 client_uri_pattern);
965 variable_list_append_return(&registration_vars, "client_uri", ast_str_buffer(uri));
966 }
967
968 if (is_variable_true(wizvars, "sends_auth")) {
969 snprintf(new_id, sizeof(new_id), "%s-oauth", id);
970 variable_list_append_return(&registration_vars, "outbound_auth", new_id);
971 }
972
973 if (!ast_strlen_zero(transport)) {
974 variable_list_append_return(&registration_vars, "transport", transport);
975 }
976
977 if (is_variable_true(wizvars, "sends_line_with_registrations")) {
978 variable_list_append_return(&registration_vars, "line", "yes");
979 variable_list_append_return(&registration_vars, "endpoint", id);
980 }
981
982 snprintf(new_id, sizeof(new_id), "%s-reg-%d", id, host_counter);
983
984 obj = create_object(sorcery, new_id, "registration", registration_vars);
985 if (!obj) {
986 return -1;
987 }
988
989 if (otw->wizard->update(sorcery, otw->wizard_data, obj)) {
990 otw->wizard->create(sorcery, otw->wizard_data, obj);
991 }
992 ao2_ref(obj, -1);
993
994 /* Unlink it from the 'existing' container. Any left will be deleted from
995 * sorcery. If it wasn't in the existing container, no harm.
996 */
998 }
999
1000 /* If there are any excess registrations, delete them. */
1001 if (ao2_container_count(existing) > 0) {
1003 }
1004
1005 return 0;
1006}
1007
1008static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object_type_wizard *otw,
1009 struct ast_category *wiz)
1010{
1011 struct ast_variable *wizvars = ast_category_first(wiz);
1012 struct string_vector remote_hosts_vector;
1013 const char *remote_hosts;
1014 int rc = -1;
1015
1016 AST_VECTOR_INIT(&remote_hosts_vector, 16);
1017 remote_hosts = ast_variable_find_last_in_list(wizvars, "remote_hosts");
1018
1019 if (!ast_strlen_zero(remote_hosts)) {
1020 char *host;
1021 char *hosts = ast_strdupa(remote_hosts);
1022
1023 while ((host = ast_strsep(&hosts, ',', AST_STRSEP_TRIM))) {
1024 host = ast_strdup(host);
1025 if (host && AST_VECTOR_APPEND(&remote_hosts_vector, host)) {
1026 ast_free(host);
1027 }
1028 }
1029 }
1030
1031 ast_debug(4, "%s handler starting.\n", otw->object_type);
1032
1033 if (!strcmp(otw->object_type, "auth")) {
1034 rc = handle_auths(sorcery, otw, wiz);
1035 } else if (!strcmp(otw->object_type, "aor")) {
1036 rc = handle_aor(sorcery, otw, wiz, &remote_hosts_vector);
1037 } else if (!strcmp(otw->object_type, "endpoint")) {
1038 rc = handle_endpoint(sorcery, otw, wiz);
1039 } else if (!strcmp(otw->object_type, "identify")) {
1040 rc = handle_identify(sorcery, otw, wiz, &remote_hosts_vector);
1041 } else if (!strcmp(otw->object_type, "phoneprov")) {
1042 rc = handle_phoneprov(sorcery, otw, wiz);
1043 } else if (!strcmp(otw->object_type, "registration")) {
1044 rc = handle_registrations(sorcery, otw, wiz, &remote_hosts_vector);
1045 }
1046
1048 AST_VECTOR_FREE(&remote_hosts_vector);
1049
1050 ast_debug(4, "%s handler complete. rc: %d\n", otw->object_type, rc);
1051
1052 return rc;
1053}
1054
1055/*
1056 * Everything below are the sorcery observers.
1057 */
1058static void instance_created_observer(const char *name, struct ast_sorcery *sorcery);
1059static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery);
1060static void object_type_loaded_observer(const char *name,
1061 const struct ast_sorcery *sorcery, const char *object_type, int reloaded);
1062static void object_type_registered_observer(const char *name,
1063 struct ast_sorcery *sorcery, const char *object_type);
1064
1069
1074
1075/*! \brief Called after an object type is loaded/reloaded */
1076static void object_type_loaded_observer(const char *name,
1077 const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
1078{
1079 struct ast_category *category = NULL;
1080 struct object_type_wizard *otw = NULL;
1081 char *filename = "pjsip_wizard.conf";
1082 struct ast_flags flags = { 0 };
1083 struct ast_config *cfg;
1085
1086 if (!strstr("auth aor endpoint identify registration phoneprov", object_type)) {
1087 /* Not interested. */
1088 return;
1089 }
1090
1091 otw = find_wizard(object_type);
1092 if (!otw) {
1093 ast_log(LOG_ERROR, "There was no wizard for object type '%s'\n", object_type);
1094 return;
1095 }
1096
1097 /* Only use the FILEUNCHANGED optimization if the ACLs haven't changed.
1098 * If ACLs changed, we force a reload of the config file to re-evaluate rules. */
1099 if (reloaded && otw->last_config && !IS_ACL_RELOAD_ACTIVE()) {
1101 }
1102
1103 cfg = ast_config_load2(filename, object_type, flags);
1104
1105 if (!cfg) {
1106 ast_log(LOG_ERROR, "Unable to load config file '%s'\n", filename);
1107 return;
1108 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1109 ast_debug(2, "Config file '%s' was unchanged for '%s'.\n", filename, object_type);
1110 return;
1111 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
1112 ast_log(LOG_ERROR, "Contents of config file '%s' are invalid and cannot be parsed\n", filename);
1113 return;
1114 }
1115
1116 while ((category = ast_category_browse_filtered(cfg, NULL, category, "type=^wizard$"))) {
1117 const char *id = ast_category_get_name(category);
1118 struct ast_category *last_cat = NULL;
1119 int changes = 0;
1120
1121 if (otw->last_config) {
1122 last_cat = ast_category_get(otw->last_config, id, "type=^wizard$");
1123 changes = !ast_variable_lists_match(ast_category_first(category), ast_category_first(last_cat), 1);
1124
1125 /* If the ACL has changed, we assume EVERYTHING might have changed.
1126 * We force an update for all wizard objects. */
1127 if (!changes && reloaded && IS_ACL_RELOAD_ACTIVE()) {
1128 ast_debug(3, "Forcing update of wizard '%s' due to global ACL change.\n", id);
1129 changes = 1;
1130 }
1131
1132 if (last_cat) {
1133 ast_category_delete(otw->last_config, last_cat);
1134 }
1135 }
1136
1137 if (!last_cat || changes) {
1138 ast_debug(3, "%s: %s(s) for wizard '%s'\n", reloaded ? "Reload" : "Load", object_type, id);
1139 if (wizard_apply_handler(sorcery, otw, category)) {
1140 ast_log(LOG_ERROR, "Unable to create objects for wizard '%s'\n", id);
1141 }
1142 }
1143 }
1144
1145 if (!otw->last_config) {
1146 otw->last_config = cfg;
1147 return;
1148 }
1149
1150 /* Only wizards that weren't in the new config are left in last_config now so we need to delete
1151 * all objects belonging to them.
1152 */
1153 category = NULL;
1154 while ((category = ast_category_browse_filtered(otw->last_config, NULL, category, "type=^wizard$"))) {
1155 const char *id = ast_category_get_name(category);
1156 struct ast_variable *search;
1157 RAII_VAR(struct ao2_container *, existing,
1159
1160 if (!existing) {
1161 ast_log(LOG_ERROR, "Unable to allocate temporary container.\n");
1162 break;
1163 }
1164
1165 search = ast_variable_new("@pjsip_wizard", id, "");
1166 if (!search) {
1167 ast_log(LOG_ERROR, "Unable to allocate memory for vaiable '@pjsip_wizard'.\n");
1168 break;
1169 }
1170 otw->wizard->retrieve_multiple(sorcery, otw->wizard_data, object_type, existing, search);
1171 ast_variables_destroy(search);
1172
1173 if (ao2_container_count(existing) > 0) {
1174 ast_debug(3, "Delete on %s: %d %s(s) for wizard: %s\n",
1175 reloaded ? "Reload" : "Load", ao2_container_count(existing), object_type, id);
1177 delete_existing_cb, otw);
1178 }
1179 }
1180
1182 otw->last_config = cfg;
1183}
1184
1185/*! \brief When each object type is registered, map a memory wizard to it. */
1187 struct ast_sorcery *sorcery, const char *object_type)
1188{
1189 struct ast_sorcery_wizard *wizard;
1190 void *wizard_data;
1191 struct object_type_wizard *otw;
1192
1193 if (is_one_of(object_type, object_types)) {
1197 ast_log(LOG_ERROR, "Unable to apply pjsip_wizard to object type '%s'\n", object_type);
1198 return;
1199 }
1200
1201 otw = ast_malloc(sizeof(*otw) + strlen(object_type) + 1);
1202 if (!otw) {
1203 return;
1204 }
1205
1206 otw->sorcery = sorcery;
1207 otw->wizard = wizard;
1208 otw->wizard_data = wizard_data;
1209 otw->last_config = NULL;
1210 strcpy(otw->object_type, object_type); /* Safe */
1211 AST_VECTOR_RW_WRLOCK(&object_type_wizards);
1212 if (AST_VECTOR_APPEND(&object_type_wizards, otw)) {
1213 ast_free(otw);
1214 } else {
1215 ast_debug(1, "Wizard mapped for object_type '%s'\n", object_type);
1216 }
1217 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
1218
1219 }
1220}
1221
1222/*! \brief When the res_pjsip instance is created, add an observer to it and initialize the wizard vector.
1223 * Also, bump the module's ref count so it can't be unloaded before the sorcery instance is
1224 * destroyed.
1225 */
1226static void instance_created_observer(const char *name, struct ast_sorcery *sorcery)
1227{
1228 if (strcmp(name, "res_pjsip")) {
1229 return;
1230 }
1233}
1234
1235/*! \brief When the res_pjsip instance is destroyed, remove the observer
1236 * and unref the module. This should then allow this module to unload cleanly.
1237 */
1238static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery)
1239{
1240 if (strcmp(name, "res_pjsip")) {
1241 return;
1242 }
1243
1246}
1247
1248static char *handle_export_primitives(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1249{
1250 struct ast_sorcery *sorcery;
1251 int idx;
1252 FILE *f = NULL;
1253 const char *fn = NULL;
1254
1255 switch (cmd) {
1256 case CLI_INIT:
1257 e->command = "pjsip export config_wizard primitives [to]";
1258 e->usage =
1259 "Usage: pjsip export config_wizard primitives [ to <filename> ]\n"
1260 " Export the config_wizard objects as pjsip primitives to\n"
1261 " the console or to <filename>\n";
1262 return NULL;
1263 case CLI_GENERATE:
1264 return NULL;
1265 }
1266
1267 if (a->argc > 5) {
1268 char date[256]="";
1269 time_t t;
1270 fn = a->argv[5];
1271
1272 time(&t);
1273 ast_copy_string(date, ctime(&t), sizeof(date));
1274 f = fopen(fn, "w");
1275 if (!f) {
1276 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
1277 return CLI_FAILURE;
1278 }
1279
1280 fprintf(f, ";!\n");
1281 fprintf(f, ";! Automatically generated configuration file\n");
1282 fprintf(f, ";! Filename: %s\n", fn);
1283 fprintf(f, ";! Generator: %s\n", "'pjsip export config_wizard primitives'");
1284 fprintf(f, ";! Creation Date: %s", date);
1285 fprintf(f, ";!\n");
1286 }
1287
1289
1290 AST_VECTOR_RW_RDLOCK(&object_type_wizards);
1291 for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
1292 struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
1293 struct ao2_container *container;
1294 struct ao2_iterator i;
1295 void *o;
1296
1298 if (!container) {
1299 continue;
1300 }
1301
1303 while ((o = ao2_iterator_next(&i))) {
1304 struct ast_variable *vars;
1305 struct ast_variable *v;
1306
1308 if (vars && ast_variable_find_in_list(vars, "@pjsip_wizard")) {
1309 if (f) {
1310 fprintf(f, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
1311 } else {
1312 ast_cli(a->fd, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
1313 }
1314 for (v = vars; v; v = v->next) {
1315 if (!ast_strlen_zero(v->value)) {
1316 if (f) {
1317 fprintf(f, "%s = %s\n", v->name, v->value);
1318 } else {
1319 ast_cli(a->fd, "%s = %s\n", v->name, v->value);
1320 }
1321 }
1322 }
1323 }
1325 ao2_ref(o, -1);
1326 }
1329 }
1330 AST_VECTOR_RW_UNLOCK(&object_type_wizards);
1331
1332 if (f) {
1333 fclose(f);
1334 ast_cli(a->fd, "Wrote configuration to %s\n", fn);
1335 }
1336
1337
1338 return CLI_SUCCESS;
1339}
1340
1342 AST_CLI_DEFINE(handle_export_primitives, "Export config wizard primitives"),
1343};
1344
1345/*!
1346 * \internal
1347 * \brief Reload configuration within a PJSIP thread
1348 */
1349static int reload_configuration_task(void *obj)
1350{
1352 if (sip_sorcery) {
1354 }
1355 return 0;
1356}
1357
1358static int reload_module(void)
1359{
1361 ast_log(LOG_WARNING, "Failed to reload PJSIP\n");
1362 return -1;
1363 }
1364
1365 return 0;
1366}
1367
1368static int load_module(void)
1369{
1370 AST_VECTOR_RW_INIT(&object_type_wizards, 12);
1373
1375
1376 /* If the PJSIP sorcery instance exists it means that we have been explicitly
1377 * loaded and things are potentially already set up. Since we won't receive any
1378 * observer callbacks informing us of this we add ourselves to the instance
1379 * as an observer in case it goes away, and determine which object types have
1380 * been registered and synthesize the observer callbacks for them. Once done
1381 * we force a reload so we are aware of the state of things.
1382 */
1383 if (ast_sip_get_sorcery()) {
1384 int i;
1385
1388
1389 /* See which object types already exist */
1390 for (i = 0; i < ARRAY_LEN(object_types); ++i) {
1391 if (!object_types[i]) {
1392 break;
1393 } else if (!ast_sorcery_get_object_type(ast_sip_get_sorcery(), object_types[i])) {
1394 continue;
1395 }
1396
1398 object_types[i]);
1399 }
1400
1402 }
1403
1405}
1406
1420
1422 .support_level = AST_MODULE_SUPPORT_CORE,
1423 .load = load_module,
1425 .unload = unload_module,
1426 .load_pri = AST_MODPRI_REALTIME_DRIVER,
Access Control of various sorts.
struct stasis_message_type * ast_named_acl_change_type(void)
a stasis_message_type for changes against a named ACL or the set of all named ACLs
#define paren
Definition ael_lex.c:962
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
static const char app[]
enum queue_result id
Definition app_queue.c:1790
ast_mutex_t lock
Definition app_sla.c:337
#define var
Definition ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition astmm.h:288
#define ast_free(a)
Definition astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
void ast_free_ptr(void *ptr)
free() wrapper
Definition astmm.c:1739
#define ast_malloc(len)
A wrapper for malloc()
Definition astmm.h:191
#define ast_log
Definition astobj2.c:42
#define ao2_iterator_next(iter)
Definition astobj2.h:1911
@ CMP_MATCH
Definition astobj2.h:1027
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition astobj2.h:367
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition astobj2.h:1693
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition astobj2.h:1934
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition astobj2.h:459
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_NODATA
Definition astobj2.h:1044
@ OBJ_MULTIPLE
Definition astobj2.h:1049
@ OBJ_UNLINK
Definition astobj2.h:1039
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition astobj2.h:1101
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition astobj2.h:1327
static int priority
static rc_handle * rh
Definition cdr_radius.c:96
static const char type[]
static struct ast_sorcery * sorcery
void ast_var_list_destroy(struct varshead *head)
Definition chanvars.c:109
static void AST_VAR_LIST_INSERT_TAIL(struct varshead *head, struct ast_var_t *var)
Definition chanvars.h:51
#define ast_var_assign(name, value)
Definition chanvars.h:40
struct varshead * ast_var_list_create(void)
Definition chanvars.c:97
Standard Command Line Interface.
#define CLI_SUCCESS
Definition cli.h:44
#define AST_CLI_DEFINE(fn, txt,...)
Definition cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition clicompat.c:6
@ CLI_INIT
Definition cli.h:152
@ CLI_GENERATE
Definition cli.h:153
#define CLI_FAILURE
Definition cli.h:46
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition cli.h:265
@ E_MATCH
Definition extconf.h:217
static const char name[]
Definition format_mp3.c:68
direction
static const char context_name[]
#define ast_sip_push_task_wait_servant(serializer, sip_task, task_data)
Definition res_pjsip.h:2165
static char prefix[MAX_PREFIX]
Definition http.c:145
@ CONFIG_FLAG_FILEUNCHANGED
const char * ast_category_get_name(const struct ast_category *category)
Return the name of the category.
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
struct ast_category * ast_category_delete(struct ast_config *cfg, struct ast_category *cat)
Delete a category.
const char * ast_variable_find_last_in_list(const struct ast_variable *list, const char *variable)
Gets the value of the LAST occurrence of a variable from a variable list.
const char * ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
Gets the value of a variable from a variable list by name.
#define ast_variable_new(name, value, filename)
#define ast_variable_list_append(head, new_var)
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition extconf.c:1287
struct ast_variable * ast_category_first(struct ast_category *cat)
given a pointer to a category, return the root variable.
int ast_variable_lists_match(const struct ast_variable *left, const struct ast_variable *right, int exact_match)
Tests 2 variable lists to see if they match.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name, const char *filter)
Retrieve a category if it exists.
struct ast_category * ast_category_browse_filtered(struct ast_config *config, const char *category_name, struct ast_category *prev, const char *filter)
Browse categories with filters.
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_WARNING
Asterisk locking-related definitions:
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition lock.h:764
#define SCOPED_MUTEX(varname, lock)
scoped lock specialization for mutexes
Definition lock.h:596
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition lock.h:527
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition module.h:330
#define ast_module_unref(mod)
Release a reference to the module.
Definition module.h:483
#define ast_module_ref(mod)
Hold a reference to the module.
Definition module.h:457
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODPRI_REALTIME_DRIVER
Definition module.h:337
@ AST_MODULE_SUPPORT_CORE
Definition module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
Core PBX routines and definitions.
void * ast_get_extension_app_data(struct ast_exten *e)
Definition pbx.c:6720
int ast_wrlock_contexts(void)
Write locks the context list.
Definition pbx.c:6621
int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar, int already_locked)
This functionc locks given context, search for the right extension and fires out all peer in this ext...
Definition pbx.c:3538
int ast_wrlock_context(struct ast_context *con)
Write locks a given context.
Definition pbx.c:6639
const char * ast_get_extension_app(struct ast_exten *e)
Definition pbx.c:6715
struct ast_context * ast_context_find_or_create(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *name, const char *registrar)
Register a new context or find an existing one.
Definition pbx.c:4542
int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
Simply remove extension from context.
Definition pbx.c:3508
int ast_unlock_context(struct ast_context *con)
Definition pbx.c:6649
void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)
const char * ast_get_context_name(struct ast_context *con)
Definition ael_main.c:421
#define PRIORITY_HINT
Definition pbx.h:54
int ast_add_extension2_nolock(struct ast_context *con, int replace, const char *extension, int priority, const char *label, const char *callerid, const char *application, void *data, void(*datad)(void *), const char *registrar, const char *registrar_file, int registrar_line)
Same as ast_add_extension2, but assumes you have already locked context.
Definition pbx.c:5548
struct ast_exten * pbx_find_extension(struct ast_channel *chan, struct ast_context *bypass, struct pbx_find_info *q, const char *context, const char *exten, int priority, const char *label, const char *callerid, enum ext_match_t action)
Definition ael_main.c:152
int ast_unlock_contexts(void)
Unlocks contexts.
Definition pbx.c:6631
static struct ast_sorcery * sip_sorcery
static struct stasis_subscription * sub
Statsd channel stats. Exmaple of how to subscribe to Stasis events.
static int reload(void)
struct ao2_container * container
Definition res_fax.c:603
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
static void instance_destroying_observer(const char *name, struct ast_sorcery *sorcery)
When the res_pjsip instance is destroyed, remove the observer and unref the module....
static ast_mutex_t config_wizard_observer_lock
static int handle_aor(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, struct string_vector *remote_hosts_vector)
#define BASE_REGISTRAR
#define NOT_EQUALS(a, b)
static int add_hints(const char *context, const char *exten, const char *application, const char *id)
static int is_variable_true(struct ast_variable *vars, const char *name)
Finds the last variable in a list and tests it.
static struct stasis_subscription * acl_change_sub
static int reload_configuration_task(void *obj)
static int delete_existing_cb(void *obj, void *arg, int flags)
#define OTW_DELETE_CB(otw)
static int add_extension(struct ast_context *context, const char *exten, int priority, const char *application)
static struct object_type_wizard * find_wizard(const char *object_type)
Finds the otw for the object type.
static int variable_list_append(struct ast_variable **existing, const char *name, const char *value)
Appends a variable to the end of an existing list.
static const struct ast_sorcery_global_observer global_observer
static int handle_auth(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, char *direction)
static void instance_created_observer(const char *name, struct ast_sorcery *sorcery)
When the res_pjsip instance is created, add an observer to it and initialize the wizard vector....
static int handle_endpoint(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
static void object_type_loaded_observer(const char *name, const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
Called after an object type is loaded/reloaded.
#define MAX_ID_SUFFIX
Defines the maximum number of characters that can be added to a wizard id.
static int handle_registrations(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, struct string_vector *remote_hosts_vector)
static int is_one_of(const char *needle, const char *haystack[])
static char * handle_export_primitives(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void object_type_registered_observer(const char *name, struct ast_sorcery *sorcery, const char *object_type)
When each object type is registered, map a memory wizard to it.
static int reload_module(void)
#define variable_list_append_return(existing, name, value)
Appends a variable to the end of an existing list. On failure, cause the calling function to return -...
#define IS_ACL_RELOAD_ACTIVE()
static struct ast_cli_entry config_wizard_cli[]
static void * create_object(const struct ast_sorcery *sorcery, const char *id, const char *type, struct ast_variable *vars)
Creates a sorcery object and applies a variable list.
static struct ast_variable * get_object_variables(struct ast_variable *vars, char *prefix)
We need to strip off the prefix from the name of each variable so they're suitable for objectset_appl...
struct ast_sorcery_instance_observer observer
static int named_acl_reload_count
static int delete_extens(const char *context, const char *exten)
static int load_module(void)
static int handle_identify(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz, struct string_vector *remote_hosts_vector)
static int unload_module(void)
static int wizard_apply_handler(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
static int handle_auths(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Callback for Named ACL changed.
static int handle_phoneprov(const struct ast_sorcery *sorcery, struct object_type_wizard *otw, struct ast_category *wiz)
#define NULL
Definition resample.c:96
Security Event Reporting API.
struct stasis_topic * ast_security_topic(void)
A stasis_topic which publishes messages for security related issues.
Sorcery Data Access Layer API.
const char * ast_sorcery_object_get_extended(const void *object, const char *name)
Get an extended field value from a sorcery object.
Definition sorcery.c:2399
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition sorcery.c:2381
#define ast_sorcery_objectset_create(sorcery, object)
Create an object set (KVP list) for an object.
Definition sorcery.h:1137
void ast_sorcery_instance_observer_remove(struct ast_sorcery *sorcery, const struct ast_sorcery_instance_observer *callbacks)
Remove an observer from a sorcery instance.
Definition sorcery.c:601
@ AST_RETRIEVE_FLAG_MULTIPLE
Return all matching objects.
Definition sorcery.h:120
struct ast_sorcery_object_type * ast_sorcery_get_object_type(const struct ast_sorcery *sorcery, const char *type)
Get the sorcery object type given a type name.
Definition sorcery.c:2558
void ast_sorcery_reload(const struct ast_sorcery *sorcery)
Inform any wizards to reload persistent objects.
Definition sorcery.c:1472
int ast_sorcery_object_id_compare(void *obj, void *arg, int flags)
ao2 object comparator based on sorcery id.
Definition sorcery.c:2528
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
Definition sorcery.c:1808
int ast_sorcery_objectset_apply(const struct ast_sorcery *sorcery, void *object, struct ast_variable *objectset)
Apply an object set (KVP list) to an object.
Definition sorcery.c:1696
void ast_sorcery_global_observer_remove(const struct ast_sorcery_global_observer *callbacks)
Remove a global observer from sorcery.
Definition sorcery.c:578
int ast_sorcery_global_observer_add(const struct ast_sorcery_global_observer *callbacks)
Add a global observer to sorcery.
Definition sorcery.c:562
int ast_sorcery_instance_observer_add(struct ast_sorcery *sorcery, const struct ast_sorcery_instance_observer *callbacks)
Add an observer to a sorcery instance.
Definition sorcery.c:584
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Definition sorcery.c:1961
@ AST_SORCERY_WIZARD_APPLY_ALLOW_DUPLICATE
Definition sorcery.h:583
@ AST_SORCERY_WIZARD_APPLY_READONLY
Definition sorcery.h:581
@ AST_SORCERY_APPLY_SUCCESS
Definition sorcery.h:427
#define ast_sorcery_object_type_apply_wizard(sorcery, object_type_name, wizard_type_name, wizard_args, flags, wizard, wizard_data)
Apply additional object wizard mappings returning wizard information.
Definition sorcery.h:678
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition stasis.c:1212
#define stasis_subscribe(topic, callback, data)
Definition stasis.h:649
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition utils.c:2233
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
@ AST_STRSEP_TRIM
Definition strings.h:256
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition strings.h:693
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition strings.h:659
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition strings.h:97
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition utils.c:1869
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition astobj2.h:1821
descriptor for a cli entry.
Definition cli.h:171
char * command
Definition cli.h:186
const char * usage
Definition cli.h:177
ast_context: An extension context
Definition pbx.c:254
ast_exten: An extension The dialplan is saved as a linked list with each context having it's own link...
Definition pbx.c:207
char * exten
Definition pbx.c:208
void * data
Definition pbx.c:218
Structure used to handle boolean flags.
Definition utils.h:220
unsigned int flags
Definition utils.h:221
struct ast_module * self
Definition module.h:356
Interface for the global sorcery observer.
Definition sorcery.h:220
void(* instance_created)(const char *name, struct ast_sorcery *sorcery)
Callback after an instance is created.
Definition sorcery.h:222
Interface for the sorcery instance observer.
Definition sorcery.h:237
void(* object_type_registered)(const char *name, struct ast_sorcery *sorcery, const char *object_type)
Callback after any object_type is registered.
Definition sorcery.h:252
Structure for internal sorcery object information.
Definition sorcery.c:128
Interface for a sorcery wizard.
Definition sorcery.h:276
void(* retrieve_multiple)(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields)
Optional callback for retrieving multiple objects using some optional field criteria.
Definition sorcery.h:313
void *(* retrieve_id)(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
Callback for retrieving an object using an id.
Definition sorcery.h:296
int(* create)(const struct ast_sorcery *sorcery, void *data, void *object)
Callback for creating an object.
Definition sorcery.h:293
int(* delete)(const struct ast_sorcery *sorcery, void *data, void *object)
Callback for deleting an object.
Definition sorcery.h:319
int(* update)(const struct ast_sorcery *sorcery, void *data, void *object)
Callback for updating an object.
Definition sorcery.h:316
Full structure for sorcery.
Definition sorcery.c:231
Support for dynamic strings.
Definition strings.h:623
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Keeps track of the sorcery wizard and last config for each object type.
struct ast_config * last_config
struct ast_sorcery * sorcery
struct ast_sorcery_wizard * wizard
A generic char * vector definition.
int value
Definition syslog.c:37
static struct test_val a
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:981
#define ast_assert(a)
Definition utils.h:779
#define ARRAY_LEN(a)
Definition utils.h:706
Vector container support.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition vector.h:637
#define AST_VECTOR_REMOVE_ALL_CMP_UNORDERED(vec, value, cmp, cleanup)
Remove all elements from a vector that matches the given comparison.
Definition vector.h:489
#define AST_VECTOR_RW_WRLOCK(vec)
Obtain write lock on vector.
Definition vector.h:915
#define AST_VECTOR_RW_UNLOCK(vec)
Unlock vector.
Definition vector.h:925
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition vector.h:185
#define AST_VECTOR_RW_FREE(vec)
Deallocates this locked vector.
Definition vector.h:213
#define AST_VECTOR_RW(name, type)
Define a vector structure with a read/write lock.
Definition vector.h:104
#define AST_VECTOR_RW_RDLOCK(vec)
Obtain read lock on vector.
Definition vector.h:905
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition vector.h:124
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition vector.h:267
#define AST_VECTOR(name, type)
Define a vector structure.
Definition vector.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition vector.h:708
#define AST_VECTOR_RW_INIT(vec, size)
Initialize a vector with a read/write lock.
Definition vector.h:169