res_pjproject: Add utility functions to convert between socket structures
[asterisk/asterisk.git] / res / res_pjproject.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@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 /*! \file
20  *
21  * \brief Bridge PJPROJECT logging to Asterisk logging.
22  * \author David M. Lee, II <dlee@digium.com>
23  *
24  * PJPROJECT logging doesn't exactly match Asterisk logging, but mapping the two is
25  * not too bad. PJPROJECT log levels are identified by a single int. Limits are
26  * not specified by PJPROJECT, but their implementation used 1 through 6.
27  *
28  * The mapping is as follows:
29  *  - 0: LOG_ERROR
30  *  - 1: LOG_ERROR
31  *  - 2: LOG_WARNING
32  *  - 3 and above: equivalent to ast_debug(level, ...) for res_pjproject.so
33  */
34
35 /*** MODULEINFO
36         <depend>pjproject</depend>
37         <support_level>core</support_level>
38  ***/
39
40 /*** DOCUMENTATION
41         <configInfo name="res_pjproject" language="en_US">
42                 <synopsis>pjproject common configuration</synopsis>
43                 <configFile name="pjproject.conf">
44                         <configObject name="startup">
45                                 <synopsis>Asterisk startup time options for PJPROJECT</synopsis>
46                                 <description>
47                                         <note><para>The id of this object, as well as its type, must be
48                                         'startup' or it won't be found.</para></note>
49                                 </description>
50                                 <configOption name="type">
51                                         <synopsis>Must be of type 'startup'.</synopsis>
52                                 </configOption>
53                                 <configOption name="log_level" default="2">
54                                         <synopsis>Initial maximum pjproject logging level to log.</synopsis>
55                                         <description>
56                                                 <para>Valid values are: 0-6, and default</para>
57                                         <note><para>
58                                                 This option is needed very early in the startup process
59                                                 so it can only be read from config files because the
60                                                 modules for other methods have not been loaded yet.
61                                         </para></note>
62                                         </description>
63                                 </configOption>
64                         </configObject>
65                         <configObject name="log_mappings">
66                                 <synopsis>PJPROJECT to Asterisk Log Level Mapping</synopsis>
67                                 <description><para>Warnings and errors in the pjproject libraries are generally handled
68                                         by Asterisk.  In many cases, Asterisk wouldn't even consider them to
69                                         be warnings or errors so the messages emitted by pjproject directly
70                                         are either superfluous or misleading.  The 'log_mappings'
71                                         object allows mapping the pjproject levels to Asterisk levels, or nothing.
72                                         </para>
73                                         <note><para>The id of this object, as well as its type, must be
74                                         'log_mappings' or it won't be found.</para></note>
75                                 </description>
76                                 <configOption name="type">
77                                         <synopsis>Must be of type 'log_mappings'.</synopsis>
78                                 </configOption>
79                                 <configOption name="asterisk_error" default="0,1">
80                                         <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_ERROR.</synopsis>
81                                 </configOption>
82                                 <configOption name="asterisk_warning" default="2">
83                                         <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_WARNING.</synopsis>
84                                 </configOption>
85                                 <configOption name="asterisk_notice" default="">
86                                         <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_NOTICE.</synopsis>
87                                 </configOption>
88                                 <configOption name="asterisk_debug" default="3,4,5,6">
89                                         <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_DEBUG.</synopsis>
90                                 </configOption>
91                                 <configOption name="asterisk_verbose" default="">
92                                         <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_VERBOSE.</synopsis>
93                                 </configOption>
94                         </configObject>
95                 </configFile>
96         </configInfo>
97  ***/
98
99 #include "asterisk.h"
100
101 #include <stdarg.h>
102 #include <pjlib.h>
103 #include <pjsip.h>
104 #include <pj/log.h>
105
106 #include "asterisk/options.h"
107 #include "asterisk/logger.h"
108 #include "asterisk/module.h"
109 #include "asterisk/cli.h"
110 #include "asterisk/res_pjproject.h"
111 #include "asterisk/vector.h"
112 #include "asterisk/sorcery.h"
113 #include "asterisk/test.h"
114 #include "asterisk/netsock2.h"
115
116 static struct ast_sorcery *pjproject_sorcery;
117 static pj_log_func *log_cb_orig;
118 static unsigned decor_orig;
119
120 static AST_VECTOR(buildopts, char *) buildopts;
121
122 /*! Protection from other log intercept instances.  There can be only one at a time. */
123 AST_MUTEX_DEFINE_STATIC(pjproject_log_intercept_lock);
124
125 struct pjproject_log_intercept_data {
126         pthread_t thread;
127         int fd;
128 };
129
130 static struct pjproject_log_intercept_data pjproject_log_intercept = {
131         .thread = AST_PTHREADT_NULL,
132         .fd = -1,
133 };
134
135 struct log_mappings {
136         /*! Sorcery object details */
137         SORCERY_OBJECT(details);
138         /*! These are all comma-separated lists of pjproject log levels */
139         AST_DECLARE_STRING_FIELDS(
140                 /*! pjproject log levels mapped to Asterisk ERROR */
141                 AST_STRING_FIELD(asterisk_error);
142                 /*! pjproject log levels mapped to Asterisk WARNING */
143                 AST_STRING_FIELD(asterisk_warning);
144                 /*! pjproject log levels mapped to Asterisk NOTICE */
145                 AST_STRING_FIELD(asterisk_notice);
146                 /*! pjproject log levels mapped to Asterisk VERBOSE */
147                 AST_STRING_FIELD(asterisk_verbose);
148                 /*! pjproject log levels mapped to Asterisk DEBUG */
149                 AST_STRING_FIELD(asterisk_debug);
150         );
151 };
152
153 static struct log_mappings *default_log_mappings;
154
155 static struct log_mappings *get_log_mappings(void)
156 {
157         struct log_mappings *mappings;
158
159         mappings = ast_sorcery_retrieve_by_id(pjproject_sorcery, "log_mappings", "log_mappings");
160         if (!mappings) {
161                 return ao2_bump(default_log_mappings);
162         }
163
164         return mappings;
165 }
166
167 #define __LOG_SUPPRESS -1
168
169 static int get_log_level(int pj_level)
170 {
171         int mapped_level;
172         unsigned char l;
173         struct log_mappings *mappings;
174
175         mappings = get_log_mappings();
176         if (!mappings) {
177                 return __LOG_ERROR;
178         }
179
180         l = '0' + fmin(pj_level, 9);
181
182         if (strchr(mappings->asterisk_error, l)) {
183                 mapped_level = __LOG_ERROR;
184         } else if (strchr(mappings->asterisk_warning, l)) {
185                 mapped_level = __LOG_WARNING;
186         } else if (strchr(mappings->asterisk_notice, l)) {
187                 mapped_level = __LOG_NOTICE;
188         } else if (strchr(mappings->asterisk_verbose, l)) {
189                 mapped_level = __LOG_VERBOSE;
190         } else if (strchr(mappings->asterisk_debug, l)) {
191                 mapped_level = __LOG_DEBUG;
192         } else {
193                 mapped_level = __LOG_SUPPRESS;
194         }
195
196         ao2_ref(mappings, -1);
197         return mapped_level;
198 }
199
200 static void log_forwarder(int level, const char *data, int len)
201 {
202         int ast_level;
203         /* PJPROJECT doesn't provide much in the way of source info */
204         const char * log_source = "pjproject";
205         int log_line = 0;
206         const char *log_func = "<?>";
207
208         if (pjproject_log_intercept.fd != -1
209                 && pjproject_log_intercept.thread == pthread_self()) {
210                 /*
211                  * We are handling a CLI command intercepting PJPROJECT
212                  * log output.
213                  */
214                 ast_cli(pjproject_log_intercept.fd, "%s\n", data);
215                 return;
216         }
217
218         ast_level = get_log_level(level);
219
220         if (ast_level == __LOG_SUPPRESS) {
221                 return;
222         }
223
224         /* PJPROJECT uses indention to indicate function call depth. We'll prepend
225          * log statements with a tab so they'll have a better shot at lining
226          * up */
227         ast_log(ast_level, log_source, log_line, log_func, "\t%s\n", data);
228 }
229
230 static void capture_buildopts_cb(int level, const char *data, int len)
231 {
232         char *dup;
233
234         if (strstr(data, "Teluu") || strstr(data, "Dumping")) {
235                 return;
236         }
237
238         dup = ast_strdup(ast_skip_blanks(data));
239         if (dup && AST_VECTOR_ADD_SORTED(&buildopts, dup, strcmp)) {
240                 ast_free(dup);
241         }
242 }
243
244 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
245 int ast_pjproject_get_buildopt(char *option, char *format_string, ...)
246 {
247         int res = 0;
248         char *format_temp;
249         int i;
250
251         format_temp = ast_alloca(strlen(option) + strlen(" : ") + strlen(format_string) + 1);
252         sprintf(format_temp, "%s : %s", option, format_string);
253
254         for (i = 0; i < AST_VECTOR_SIZE(&buildopts); i++) {
255                 va_list arg_ptr;
256                 va_start(arg_ptr, format_string);
257                 res = vsscanf(AST_VECTOR_GET(&buildopts, i), format_temp, arg_ptr);
258                 va_end(arg_ptr);
259                 if (res) {
260                         break;
261                 }
262         }
263
264         return res;
265 }
266 #pragma GCC diagnostic warning "-Wformat-nonliteral"
267
268 void ast_pjproject_log_intercept_begin(int fd)
269 {
270         /* Protect from other CLI instances trying to do this at the same time. */
271         ast_mutex_lock(&pjproject_log_intercept_lock);
272
273         pjproject_log_intercept.thread = pthread_self();
274         pjproject_log_intercept.fd = fd;
275 }
276
277 void ast_pjproject_log_intercept_end(void)
278 {
279         pjproject_log_intercept.fd = -1;
280         pjproject_log_intercept.thread = AST_PTHREADT_NULL;
281
282         ast_mutex_unlock(&pjproject_log_intercept_lock);
283 }
284
285 static char *handle_pjproject_show_buildopts(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
286 {
287         int i;
288
289         switch (cmd) {
290         case CLI_INIT:
291                 e->command = "pjproject show buildopts";
292                 e->usage =
293                         "Usage: pjproject show buildopts\n"
294                         "       Show the compile time config of the pjproject that Asterisk is\n"
295                         "       running against.\n";
296                 return NULL;
297         case CLI_GENERATE:
298                 return NULL;
299         }
300
301         ast_cli(a->fd, "PJPROJECT compile time config currently running against:\n");
302
303         for (i = 0; i < AST_VECTOR_SIZE(&buildopts); i++) {
304                 ast_cli(a->fd, "%s\n", AST_VECTOR_GET(&buildopts, i));
305         }
306
307         return CLI_SUCCESS;
308 }
309
310 static void mapping_destroy(void *object)
311 {
312         struct log_mappings *mappings = object;
313
314         ast_string_field_free_memory(mappings);
315 }
316
317 static void *mapping_alloc(const char *name)
318 {
319         struct log_mappings *mappings = ast_sorcery_generic_alloc(sizeof(*mappings), mapping_destroy);
320         if (!mappings) {
321                 return NULL;
322         }
323         ast_string_field_init(mappings, 128);
324
325         return mappings;
326 }
327
328 static char *handle_pjproject_show_log_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
329 {
330         struct ast_variable *objset;
331         struct ast_variable *i;
332         struct log_mappings *mappings;
333
334         switch (cmd) {
335         case CLI_INIT:
336                 e->command = "pjproject show log mappings";
337                 e->usage =
338                         "Usage: pjproject show log mappings\n"
339                         "       Show pjproject to Asterisk log mappings\n";
340                 return NULL;
341         case CLI_GENERATE:
342                 return NULL;
343         }
344
345         ast_cli(a->fd, "PJPROJECT to Asterisk log mappings:\n");
346         ast_cli(a->fd, "Asterisk Level   : PJPROJECT log levels\n");
347
348         mappings = get_log_mappings();
349         if (!mappings) {
350                 ast_log(LOG_ERROR, "Unable to retrieve pjproject log_mappings\n");
351                 return CLI_SUCCESS;
352         }
353
354         objset = ast_sorcery_objectset_create(pjproject_sorcery, mappings);
355         if (!objset) {
356                 ao2_ref(mappings, -1);
357                 return CLI_SUCCESS;
358         }
359
360         for (i = objset; i; i = i->next) {
361                 ast_cli(a->fd, "%-16s : %s\n", i->name, i->value);
362         }
363         ast_variables_destroy(objset);
364
365         ao2_ref(mappings, -1);
366         return CLI_SUCCESS;
367 }
368
369 struct max_pjproject_log_level_check {
370         /*!
371          * Compile time sanity check to determine if
372          * MAX_PJ_LOG_MAX_LEVEL matches CLI syntax.
373          */
374         char check[1 / (6 == MAX_PJ_LOG_MAX_LEVEL)];
375 };
376
377 static char *handle_pjproject_set_log_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
378 {
379         int level_new;
380         int level_old;
381
382         switch (cmd) {
383         case CLI_INIT:
384                 e->command = "pjproject set log level {default|0|1|2|3|4|5|6}";
385                 e->usage =
386                         "Usage: pjproject set log level {default|<level>}\n"
387                         "\n"
388                         "       Set the maximum active pjproject logging level.\n"
389                         "       See pjproject.conf.sample for additional information\n"
390                         "       about the various levels pjproject uses.\n";
391                 return NULL;
392         case CLI_GENERATE:
393                 return NULL;
394         }
395
396         if (a->argc != 5) {
397                 return CLI_SHOWUSAGE;
398         }
399
400         if (!strcasecmp(a->argv[4], "default")) {
401                 level_new = DEFAULT_PJ_LOG_MAX_LEVEL;
402         } else {
403                 if (sscanf(a->argv[4], "%30d", &level_new) != 1
404                         || level_new < 0 || MAX_PJ_LOG_MAX_LEVEL < level_new) {
405                         return CLI_SHOWUSAGE;
406                 }
407         }
408
409         /* Update pjproject logging level */
410         if (ast_pjproject_max_log_level < level_new) {
411                 level_new = ast_pjproject_max_log_level;
412                 ast_cli(a->fd,
413                         "Asterisk built or linked with pjproject PJ_LOG_MAX_LEVEL=%d.\n"
414                         "Lowering request to the max supported level.\n",
415                         ast_pjproject_max_log_level);
416         }
417         level_old = ast_option_pjproject_log_level;
418         if (level_old == level_new) {
419                 ast_cli(a->fd, "pjproject log level is still %d.\n", level_old);
420         } else {
421                 ast_cli(a->fd, "pjproject log level was %d and is now %d.\n",
422                         level_old, level_new);
423                 ast_option_pjproject_log_level = level_new;
424                 pj_log_set_level(level_new);
425         }
426
427         return CLI_SUCCESS;
428 }
429
430 static char *handle_pjproject_show_log_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
431 {
432         switch (cmd) {
433         case CLI_INIT:
434                 e->command = "pjproject show log level";
435                 e->usage =
436                         "Usage: pjproject show log level\n"
437                         "\n"
438                         "       Show the current maximum active pjproject logging level.\n"
439                         "       See pjproject.conf.sample for additional information\n"
440                         "       about the various levels pjproject uses.\n";
441                 return NULL;
442         case CLI_GENERATE:
443                 return NULL;
444         }
445
446         if (a->argc != 4) {
447                 return CLI_SHOWUSAGE;
448         }
449
450         ast_cli(a->fd, "pjproject log level is %d.%s\n",
451                 ast_option_pjproject_log_level,
452                 ast_option_pjproject_log_level == DEFAULT_PJ_LOG_MAX_LEVEL ? " (default)" : "");
453
454         return CLI_SUCCESS;
455 }
456
457 static struct ast_cli_entry pjproject_cli[] = {
458         AST_CLI_DEFINE(handle_pjproject_set_log_level, "Set the maximum active pjproject logging level"),
459         AST_CLI_DEFINE(handle_pjproject_show_buildopts, "Show the compiled config of the pjproject in use"),
460         AST_CLI_DEFINE(handle_pjproject_show_log_mappings, "Show pjproject to Asterisk log mappings"),
461         AST_CLI_DEFINE(handle_pjproject_show_log_level, "Show the maximum active pjproject logging level"),
462 };
463
464 void ast_pjproject_caching_pool_init(pj_caching_pool *cp,
465         const pj_pool_factory_policy *policy, pj_size_t max_capacity)
466 {
467         /* Passing a max_capacity of zero disables caching pools */
468         pj_caching_pool_init(cp, policy, ast_option_pjproject_cache_pools ? max_capacity : 0);
469 }
470
471 void ast_pjproject_caching_pool_destroy(pj_caching_pool *cp)
472 {
473         pj_caching_pool_destroy(cp);
474 }
475
476 int ast_sockaddr_to_pj_sockaddr(const struct ast_sockaddr *addr, pj_sockaddr *pjaddr)
477 {
478         if (addr->ss.ss_family == AF_INET) {
479                 struct sockaddr_in *sin = (struct sockaddr_in *) &addr->ss;
480                 pjaddr->ipv4.sin_family = pj_AF_INET();
481                 pjaddr->ipv4.sin_addr   = sin->sin_addr;
482                 pjaddr->ipv4.sin_port   = sin->sin_port;
483         } else if (addr->ss.ss_family == AF_INET6) {
484                 struct sockaddr_in6 *sin = (struct sockaddr_in6 *) &addr->ss;
485                 pjaddr->ipv6.sin6_family   = pj_AF_INET6();
486                 pjaddr->ipv6.sin6_port     = sin->sin6_port;
487                 pjaddr->ipv6.sin6_flowinfo = sin->sin6_flowinfo;
488                 pjaddr->ipv6.sin6_scope_id = sin->sin6_scope_id;
489                 memcpy(&pjaddr->ipv6.sin6_addr, &sin->sin6_addr, sizeof(pjaddr->ipv6.sin6_addr));
490         } else {
491                 memset(pjaddr, 0, sizeof(*pjaddr));
492                 return -1;
493         }
494         return 0;
495 }
496
497 int ast_sockaddr_from_pj_sockaddr(struct ast_sockaddr *addr, const pj_sockaddr *pjaddr)
498 {
499         if (pjaddr->addr.sa_family == pj_AF_INET()) {
500                 struct sockaddr_in *sin = (struct sockaddr_in *) &addr->ss;
501                 sin->sin_family = AF_INET;
502                 sin->sin_addr   = pjaddr->ipv4.sin_addr;
503                 sin->sin_port   = pjaddr->ipv4.sin_port;
504                 addr->len = sizeof(struct sockaddr_in);
505         } else if (pjaddr->addr.sa_family == pj_AF_INET6()) {
506                 struct sockaddr_in6 *sin = (struct sockaddr_in6 *) &addr->ss;
507                 sin->sin6_family   = AF_INET6;
508                 sin->sin6_port     = pjaddr->ipv6.sin6_port;
509                 sin->sin6_flowinfo = pjaddr->ipv6.sin6_flowinfo;
510                 sin->sin6_scope_id = pjaddr->ipv6.sin6_scope_id;
511                 memcpy(&sin->sin6_addr, &pjaddr->ipv6.sin6_addr, sizeof(sin->sin6_addr));
512                 addr->len = sizeof(struct sockaddr_in6);
513         } else {
514                 memset(addr, 0, sizeof(*addr));
515                 return -1;
516         }
517         return 0;
518 }
519
520 #ifdef TEST_FRAMEWORK
521 static void fill_with_garbage(void *x, ssize_t len)
522 {
523         unsigned char *w = x;
524         while (len > 0) {
525                 int r = ast_random();
526                 memcpy(w, &r, len > sizeof(r) ? sizeof(r) : len);
527                 w += sizeof(r);
528                 len -= sizeof(r);
529         }
530 }
531
532 AST_TEST_DEFINE(ast_sockaddr_to_pj_sockaddr_test)
533 {
534         char *candidates[] = {
535                 "127.0.0.1:5555",
536                 "[::]:4444",
537                 "192.168.0.100:0",
538                 "[fec0::1:80]:0",
539                 "[fec0::1]:80",
540                 NULL,
541         }, **candidate = candidates;
542
543         switch (cmd) {
544         case TEST_INIT:
545                 info->name = "ast_sockaddr_to_pj_sockaddr_test";
546                 info->category = "/res/res_pjproject/";
547                 info->summary = "Validate conversions from an ast_sockaddr to a pj_sockaddr";
548                 info->description = "This test converts an ast_sockaddr to a pj_sockaddr and validates\n"
549                         "that the two evaluate to the same string when formatted.";
550                 return AST_TEST_NOT_RUN;
551         case TEST_EXECUTE:
552                 break;
553         }
554
555         while (*candidate) {
556                 struct ast_sockaddr addr = {{0,}};
557                 pj_sockaddr pjaddr;
558                 char buffer[512];
559
560                 fill_with_garbage(&pjaddr, sizeof(pj_sockaddr));
561
562                 if (!ast_sockaddr_parse(&addr, *candidate, 0)) {
563                         ast_test_status_update(test, "Failed to parse candidate IP: %s\n", *candidate);
564                         return AST_TEST_FAIL;
565                 }
566
567                 if (ast_sockaddr_to_pj_sockaddr(&addr, &pjaddr)) {
568                         ast_test_status_update(test, "Failed to convert ast_sockaddr to pj_sockaddr: %s\n", *candidate);
569                         return AST_TEST_FAIL;
570                 }
571
572                 pj_sockaddr_print(&pjaddr, buffer, sizeof(buffer), 1 | 2);
573
574                 if (strcmp(*candidate, buffer)) {
575                         ast_test_status_update(test, "Converted sockaddrs do not match: \"%s\" and \"%s\"\n",
576                                 *candidate,
577                                 buffer);
578                         return AST_TEST_FAIL;
579                 }
580
581                 candidate++;
582         }
583
584         return AST_TEST_PASS;
585 }
586
587 AST_TEST_DEFINE(ast_sockaddr_from_pj_sockaddr_test)
588 {
589         char *candidates[] = {
590                 "127.0.0.1:5555",
591                 "[::]:4444",
592                 "192.168.0.100:0",
593                 "[fec0::1:80]:0",
594                 "[fec0::1]:80",
595                 NULL,
596         }, **candidate = candidates;
597
598         switch (cmd) {
599         case TEST_INIT:
600                 info->name = "ast_sockaddr_from_pj_sockaddr_test";
601                 info->category = "/res/res_pjproject/";
602                 info->summary = "Validate conversions from a pj_sockaddr to an ast_sockaddr";
603                 info->description = "This test converts a pj_sockaddr to an ast_sockaddr and validates\n"
604                         "that the two evaluate to the same string when formatted.";
605                 return AST_TEST_NOT_RUN;
606         case TEST_EXECUTE:
607                 break;
608         }
609
610         while (*candidate) {
611                 struct ast_sockaddr addr = {{0,}};
612                 pj_sockaddr pjaddr;
613                 pj_str_t t;
614                 char buffer[512];
615
616                 fill_with_garbage(&addr, sizeof(addr));
617
618                 pj_strset(&t, *candidate, strlen(*candidate));
619
620                 if (pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &t, &pjaddr) != PJ_SUCCESS) {
621                         ast_test_status_update(test, "Failed to parse candidate IP: %s\n", *candidate);
622                         return AST_TEST_FAIL;
623                 }
624
625                 if (ast_sockaddr_from_pj_sockaddr(&addr, &pjaddr)) {
626                         ast_test_status_update(test, "Failed to convert pj_sockaddr to ast_sockaddr: %s\n", *candidate);
627                         return AST_TEST_FAIL;
628                 }
629
630                 snprintf(buffer, sizeof(buffer), "%s", ast_sockaddr_stringify(&addr));
631
632                 if (strcmp(*candidate, buffer)) {
633                         ast_test_status_update(test, "Converted sockaddrs do not match: \"%s\" and \"%s\"\n",
634                                 *candidate,
635                                 buffer);
636                         return AST_TEST_FAIL;
637                 }
638
639                 candidate++;
640         }
641
642         return AST_TEST_PASS;
643 }
644 #endif
645
646 static int load_module(void)
647 {
648         ast_debug(3, "Starting PJPROJECT logging to Asterisk logger\n");
649
650         if (!(pjproject_sorcery = ast_sorcery_open())) {
651                 ast_log(LOG_ERROR, "Failed to open SIP sorcery failed to open\n");
652                 return AST_MODULE_LOAD_DECLINE;
653         }
654
655         ast_sorcery_apply_default(pjproject_sorcery, "log_mappings", "config", "pjproject.conf,criteria=type=log_mappings");
656         if (ast_sorcery_object_register(pjproject_sorcery, "log_mappings", mapping_alloc, NULL, NULL)) {
657                 ast_log(LOG_WARNING, "Failed to register pjproject log_mappings object with sorcery\n");
658                 ast_sorcery_unref(pjproject_sorcery);
659                 pjproject_sorcery = NULL;
660                 return AST_MODULE_LOAD_DECLINE;
661         }
662
663         ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "type", "", OPT_NOOP_T, 0, 0);
664         ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_debug", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_debug));
665         ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_error", "",  OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_error));
666         ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_warning", "",  OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_warning));
667         ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_notice", "",  OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_notice));
668         ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_verbose", "",  OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_verbose));
669
670         default_log_mappings = ast_sorcery_alloc(pjproject_sorcery, "log_mappings", "log_mappings");
671         if (!default_log_mappings) {
672                 ast_log(LOG_ERROR, "Unable to allocate memory for pjproject log_mappings\n");
673                 return AST_MODULE_LOAD_DECLINE;
674         }
675         ast_string_field_set(default_log_mappings, asterisk_error, "0,1");
676         ast_string_field_set(default_log_mappings, asterisk_warning, "2");
677         ast_string_field_set(default_log_mappings, asterisk_debug, "3,4,5,6");
678
679         ast_sorcery_load(pjproject_sorcery);
680
681         AST_PJPROJECT_INIT_LOG_LEVEL();
682         pj_init();
683
684         decor_orig = pj_log_get_decor();
685         log_cb_orig = pj_log_get_log_func();
686
687         if (AST_VECTOR_INIT(&buildopts, 64)) {
688                 return AST_MODULE_LOAD_DECLINE;
689         }
690
691         /*
692          * On startup, we want to capture the dump once and store it.
693          */
694         pj_log_set_log_func(capture_buildopts_cb);
695         pj_log_set_decor(0);
696         pj_log_set_level(MAX_PJ_LOG_MAX_LEVEL);/* Set level to guarantee the dump output. */
697         pj_dump_config();
698         pj_log_set_decor(PJ_LOG_HAS_SENDER | PJ_LOG_HAS_INDENT);
699         pj_log_set_log_func(log_forwarder);
700         if (ast_pjproject_max_log_level < ast_option_pjproject_log_level) {
701                 ast_log(LOG_WARNING,
702                         "Asterisk built or linked with pjproject PJ_LOG_MAX_LEVEL=%d which is too low for startup level: %d.\n",
703                         ast_pjproject_max_log_level, ast_option_pjproject_log_level);
704                 ast_option_pjproject_log_level = ast_pjproject_max_log_level;
705         }
706         pj_log_set_level(ast_option_pjproject_log_level);
707         if (!AST_VECTOR_SIZE(&buildopts)) {
708                 ast_log(LOG_NOTICE,
709                         "Asterisk built or linked with pjproject PJ_LOG_MAX_LEVEL=%d which is too low to get buildopts.\n",
710                         ast_pjproject_max_log_level);
711         }
712
713         ast_cli_register_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli));
714
715         AST_TEST_REGISTER(ast_sockaddr_to_pj_sockaddr_test);
716         AST_TEST_REGISTER(ast_sockaddr_from_pj_sockaddr_test);
717
718         return AST_MODULE_LOAD_SUCCESS;
719 }
720
721 #define NOT_EQUALS(a, b) (a != b)
722
723 static int unload_module(void)
724 {
725         ast_cli_unregister_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli));
726         pj_log_set_log_func(log_cb_orig);
727         pj_log_set_decor(decor_orig);
728
729         AST_VECTOR_CALLBACK_VOID(&buildopts, ast_free);
730         AST_VECTOR_FREE(&buildopts);
731
732         ast_debug(3, "Stopped PJPROJECT logging to Asterisk logger\n");
733
734         pj_shutdown();
735
736         ao2_cleanup(default_log_mappings);
737         default_log_mappings = NULL;
738
739         ast_sorcery_unref(pjproject_sorcery);
740
741         AST_TEST_UNREGISTER(ast_sockaddr_to_pj_sockaddr_test);
742         AST_TEST_UNREGISTER(ast_sockaddr_from_pj_sockaddr_test);
743
744         return 0;
745 }
746
747 static int reload_module(void)
748 {
749         if (pjproject_sorcery) {
750                 ast_sorcery_reload(pjproject_sorcery);
751         }
752
753         return AST_MODULE_LOAD_SUCCESS;
754 }
755
756 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJPROJECT Log and Utility Support",
757         .support_level = AST_MODULE_SUPPORT_CORE,
758         .load = load_module,
759         .unload = unload_module,
760         .reload = reload_module,
761         .load_pri = AST_MODPRI_CHANNEL_DEPEND - 6,
762 );