sorcery/res_pjsip: Refactor for realtime performance
[asterisk/asterisk.git] / res / res_pjsip / config_global.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Mark Michelson <mmichelson@digium.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 #include "asterisk.h"
20
21 #include <pjsip.h>
22 #include <pjlib.h>
23
24 #include "asterisk/res_pjsip.h"
25 #include "include/res_pjsip_private.h"
26 #include "asterisk/sorcery.h"
27 #include "asterisk/ast_version.h"
28 #include "asterisk/res_pjsip_cli.h"
29
30 #define DEFAULT_MAX_FORWARDS 70
31 #define DEFAULT_KEEPALIVE_INTERVAL 0
32 #define DEFAULT_USERAGENT_PREFIX "Asterisk PBX"
33 #define DEFAULT_OUTBOUND_ENDPOINT "default_outbound_endpoint"
34 #define DEFAULT_DEBUG "no"
35 #define DEFAULT_ENDPOINT_IDENTIFIER_ORDER "ip,username,anonymous"
36 #define DEFAULT_MAX_INITIAL_QUALIFY_TIME 0
37 #define DEFAULT_FROM_USER "asterisk"
38 #define DEFAULT_REGCONTEXT ""
39 #define DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL 30
40
41 static char default_useragent[256];
42
43 struct global_config {
44         SORCERY_OBJECT(details);
45         AST_DECLARE_STRING_FIELDS(
46                 AST_STRING_FIELD(useragent);
47                 AST_STRING_FIELD(regcontext);
48                 AST_STRING_FIELD(default_outbound_endpoint);
49                 /*! Debug logging yes|no|host */
50                 AST_STRING_FIELD(debug);
51                 /*! Order by which endpoint identifiers are checked (comma separated list) */
52                 AST_STRING_FIELD(endpoint_identifier_order);
53                 /*! User name to place in From header if there is no better option */
54                 AST_STRING_FIELD(default_from_user);
55         );
56         /* Value to put in Max-Forwards header */
57         unsigned int max_forwards;
58         /* The interval at which to send keep alive messages to active connection-oriented transports */
59         unsigned int keep_alive_interval;
60         /* The maximum time for all contacts to be qualified at startup */
61         unsigned int max_initial_qualify_time;
62         /* The interval at which to check for expired contacts */
63         unsigned int contact_expiration_check_interval;
64 };
65
66 static void global_destructor(void *obj)
67 {
68         struct global_config *cfg = obj;
69
70         ast_string_field_free_memory(cfg);
71 }
72
73 static void *global_alloc(const char *name)
74 {
75         struct global_config *cfg;
76
77         cfg = ast_sorcery_generic_alloc(sizeof(*cfg), global_destructor);
78         if (!cfg || ast_string_field_init(cfg, 100)) {
79                 ao2_cleanup(cfg);
80                 return NULL;
81         }
82
83         return cfg;
84 }
85
86 static int global_apply(const struct ast_sorcery *sorcery, void *obj)
87 {
88         struct global_config *cfg = obj;
89         char max_forwards[10];
90
91         snprintf(max_forwards, sizeof(max_forwards), "%u", cfg->max_forwards);
92
93         ast_sip_add_global_request_header("Max-Forwards", max_forwards, 1);
94         ast_sip_add_global_request_header("User-Agent", cfg->useragent, 1);
95         ast_sip_add_global_response_header("Server", cfg->useragent, 1);
96         return 0;
97 }
98
99 static struct global_config *get_global_cfg(void)
100 {
101         struct global_config *cfg;
102         struct ao2_container *globals;
103
104         globals = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "global",
105                 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
106         if (!globals) {
107                 return NULL;
108         }
109
110         cfg = ao2_find(globals, NULL, 0);
111         ao2_ref(globals, -1);
112         return cfg;
113 }
114
115 char *ast_sip_global_default_outbound_endpoint(void)
116 {
117         char *str;
118         struct global_config *cfg;
119
120         cfg = get_global_cfg();
121         if (!cfg) {
122                 return ast_strdup(DEFAULT_OUTBOUND_ENDPOINT);
123         }
124
125         str = ast_strdup(cfg->default_outbound_endpoint);
126         ao2_ref(cfg, -1);
127         return str;
128 }
129
130 char *ast_sip_get_debug(void)
131 {
132         char *res;
133         struct global_config *cfg;
134
135         cfg = get_global_cfg();
136         if (!cfg) {
137                 return ast_strdup(DEFAULT_DEBUG);
138         }
139
140         res = ast_strdup(cfg->debug);
141         ao2_ref(cfg, -1);
142         return res;
143 }
144
145 char *ast_sip_get_regcontext(void)
146 {
147         char *res;
148         struct global_config *cfg;
149
150         cfg = get_global_cfg();
151         if (!cfg) {
152                 return ast_strdup(DEFAULT_REGCONTEXT);
153         }
154
155         res = ast_strdup(cfg->regcontext);
156         ao2_ref(cfg, -1);
157
158         return res;
159 }
160
161
162 char *ast_sip_get_endpoint_identifier_order(void)
163 {
164         char *res;
165         struct global_config *cfg;
166
167         cfg = get_global_cfg();
168         if (!cfg) {
169                 return ast_strdup(DEFAULT_ENDPOINT_IDENTIFIER_ORDER);
170         }
171
172         res = ast_strdup(cfg->endpoint_identifier_order);
173         ao2_ref(cfg, -1);
174         return res;
175 }
176
177 unsigned int ast_sip_get_keep_alive_interval(void)
178 {
179         unsigned int interval;
180         struct global_config *cfg;
181
182         cfg = get_global_cfg();
183         if (!cfg) {
184                 return DEFAULT_KEEPALIVE_INTERVAL;
185         }
186
187         interval = cfg->keep_alive_interval;
188         ao2_ref(cfg, -1);
189         return interval;
190 }
191
192 unsigned int ast_sip_get_contact_expiration_check_interval(void)
193 {
194         unsigned int interval;
195         struct global_config *cfg;
196
197         cfg = get_global_cfg();
198         if (!cfg) {
199                 return DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL;
200         }
201
202         interval = cfg->contact_expiration_check_interval;
203         ao2_ref(cfg, -1);
204         return interval;
205 }
206
207 unsigned int ast_sip_get_max_initial_qualify_time(void)
208 {
209         unsigned int time;
210         struct global_config *cfg;
211
212         cfg = get_global_cfg();
213         if (!cfg) {
214                 return DEFAULT_MAX_INITIAL_QUALIFY_TIME;
215         }
216
217         time = cfg->max_initial_qualify_time;
218         ao2_ref(cfg, -1);
219         return time;
220 }
221
222 void ast_sip_get_default_from_user(char *from_user, size_t size)
223 {
224         struct global_config *cfg;
225
226         cfg = get_global_cfg();
227         if (!cfg) {
228                 ast_copy_string(from_user, DEFAULT_FROM_USER, size);
229         } else {
230                 ast_copy_string(from_user, cfg->default_from_user, size);
231                 ao2_ref(cfg, -1);
232         }
233 }
234
235 /*!
236  * \internal
237  * \brief Observer to set default global object if none exist.
238  *
239  * \param name Module name owning the sorcery instance.
240  * \param sorcery Instance being observed.
241  * \param object_type Name of object being observed.
242  * \param reloaded Non-zero if the object is being reloaded.
243  *
244  * \return Nothing
245  */
246 static void global_loaded_observer(const char *name, const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
247 {
248         struct ao2_container *globals;
249         struct global_config *cfg;
250
251         if (strcmp(object_type, "global")) {
252                 /* Not interested */
253                 return;
254         }
255
256         globals = ast_sorcery_retrieve_by_fields(sorcery, "global",
257                 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
258         if (globals) {
259                 int count;
260
261                 count = ao2_container_count(globals);
262                 ao2_ref(globals, -1);
263
264                 if (1 < count) {
265                         ast_log(LOG_ERROR,
266                                 "At most one pjsip.conf type=global object can be defined.  You have %d defined.\n",
267                                 count);
268                         return;
269                 }
270                 if (count) {
271                         return;
272                 }
273         }
274
275         ast_debug(1, "No pjsip.conf type=global object exists so applying defaults.\n");
276         cfg = ast_sorcery_alloc(sorcery, "global", NULL);
277         if (!cfg) {
278                 return;
279         }
280         global_apply(sorcery, cfg);
281         ao2_ref(cfg, -1);
282 }
283
284 static const struct ast_sorcery_instance_observer observer_callbacks_global = {
285         .object_type_loaded = global_loaded_observer,
286 };
287
288 int sip_cli_print_global(struct ast_sip_cli_context *context)
289 {
290         struct global_config *cfg = get_global_cfg();
291
292         if (!cfg) {
293                 cfg = ast_sorcery_alloc(ast_sip_get_sorcery(), "global", NULL);
294                 if (!cfg) {
295                         return -1;
296                 }
297         }
298
299         ast_str_append(&context->output_buffer, 0, "\nGlobal Settings:\n\n");
300         ast_sip_cli_print_sorcery_objectset(cfg, context, 0);
301
302         ao2_ref(cfg, -1);
303         return 0;
304 }
305
306 int ast_sip_destroy_sorcery_global(void)
307 {
308         struct ast_sorcery *sorcery = ast_sip_get_sorcery();
309
310         ast_sorcery_instance_observer_remove(sorcery, &observer_callbacks_global);
311
312         return 0;
313 }
314
315 int ast_sip_initialize_sorcery_global(void)
316 {
317         struct ast_sorcery *sorcery = ast_sip_get_sorcery();
318
319         snprintf(default_useragent, sizeof(default_useragent), "%s %s",
320                 DEFAULT_USERAGENT_PREFIX, ast_get_version());
321
322         ast_sorcery_apply_default(sorcery, "global", "config", "pjsip.conf,criteria=type=global");
323
324         if (ast_sorcery_object_register(sorcery, "global", global_alloc, NULL, global_apply)) {
325                 return -1;
326         }
327
328         ast_sorcery_object_field_register(sorcery, "global", "type", "", OPT_NOOP_T, 0, 0);
329         ast_sorcery_object_field_register(sorcery, "global", "max_forwards",
330                 __stringify(DEFAULT_MAX_FORWARDS),
331                 OPT_UINT_T, 0, FLDSET(struct global_config, max_forwards));
332         ast_sorcery_object_field_register(sorcery, "global", "user_agent", default_useragent,
333                 OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, useragent));
334         ast_sorcery_object_field_register(sorcery, "global", "default_outbound_endpoint",
335                 DEFAULT_OUTBOUND_ENDPOINT,
336                 OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_outbound_endpoint));
337         ast_sorcery_object_field_register(sorcery, "global", "debug", DEFAULT_DEBUG,
338                 OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, debug));
339         ast_sorcery_object_field_register(sorcery, "global", "endpoint_identifier_order",
340                 DEFAULT_ENDPOINT_IDENTIFIER_ORDER,
341                 OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, endpoint_identifier_order));
342         ast_sorcery_object_field_register(sorcery, "global", "keep_alive_interval",
343                 __stringify(DEFAULT_KEEPALIVE_INTERVAL),
344                 OPT_UINT_T, 0, FLDSET(struct global_config, keep_alive_interval));
345         ast_sorcery_object_field_register(sorcery, "global", "max_initial_qualify_time",
346                 __stringify(DEFAULT_MAX_INITIAL_QUALIFY_TIME),
347                 OPT_UINT_T, 0, FLDSET(struct global_config, max_initial_qualify_time));
348         ast_sorcery_object_field_register(sorcery, "global", "default_from_user", DEFAULT_FROM_USER,
349                 OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_from_user));
350         ast_sorcery_object_field_register(sorcery, "global", "regcontext", DEFAULT_REGCONTEXT,
351                 OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, regcontext));
352         ast_sorcery_object_field_register(sorcery, "global", "contact_expiration_check_interval",
353                 __stringify(DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL),
354                 OPT_UINT_T, 0, FLDSET(struct global_config, contact_expiration_check_interval));
355
356
357         if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
358                 return -1;
359         }
360
361         return 0;
362 }