Asterisk - The Open Source Telephony Project  21.4.1
res_pjproject.c
Go to the documentation of this file.
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: equivalent to ast_debug(level, ...) for res_pjproject.so
33  * - 4: equivalent to ast_debug(level, ...) for res_pjproject.so
34  * - 5: equivalent to ast_trace(level, ...) for res_pjproject.so
35  * - 6: equivalent to ast_trace(level, ...) for res_pjproject.so
36  */
37 
38 /*** MODULEINFO
39  <depend>pjproject</depend>
40  <depend>res_sorcery_config</depend>
41  <support_level>core</support_level>
42  ***/
43 
44 /*** DOCUMENTATION
45  <configInfo name="res_pjproject" language="en_US">
46  <synopsis>pjproject common configuration</synopsis>
47  <configFile name="pjproject.conf">
48  <configObject name="startup">
49  <synopsis>Asterisk startup time options for PJPROJECT</synopsis>
50  <description>
51  <note><para>The id of this object, as well as its type, must be
52  'startup' or it won't be found.</para></note>
53  </description>
54  <configOption name="type">
55  <synopsis>Must be of type 'startup'.</synopsis>
56  </configOption>
57  <configOption name="log_level" default="2">
58  <synopsis>Initial maximum pjproject logging level to log.</synopsis>
59  <description>
60  <para>Valid values are: 0-6, and default</para>
61  <note><para>
62  This option is needed very early in the startup process
63  so it can only be read from config files because the
64  modules for other methods have not been loaded yet.
65  </para></note>
66  </description>
67  </configOption>
68  </configObject>
69  <configObject name="log_mappings">
70  <synopsis>PJPROJECT to Asterisk Log Level Mapping</synopsis>
71  <description><para>Warnings and errors in the pjproject libraries are generally handled
72  by Asterisk. In many cases, Asterisk wouldn't even consider them to
73  be warnings or errors so the messages emitted by pjproject directly
74  are either superfluous or misleading. The 'log_mappings'
75  object allows mapping the pjproject levels to Asterisk levels, or nothing.
76  </para>
77  <note><para>The id of this object, as well as its type, must be
78  'log_mappings' or it won't be found.</para></note>
79  </description>
80  <configOption name="type">
81  <synopsis>Must be of type 'log_mappings'.</synopsis>
82  </configOption>
83  <configOption name="asterisk_error" default="0,1">
84  <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_ERROR.</synopsis>
85  </configOption>
86  <configOption name="asterisk_warning" default="2">
87  <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_WARNING.</synopsis>
88  </configOption>
89  <configOption name="asterisk_notice" default="">
90  <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_NOTICE.</synopsis>
91  </configOption>
92  <configOption name="asterisk_verbose" default="">
93  <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_VERBOSE.</synopsis>
94  </configOption>
95  <configOption name="asterisk_debug" default="3,4">
96  <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_DEBUG.</synopsis>
97  </configOption>
98  <configOption name="asterisk_trace" default="5,6">
99  <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_TRACE.</synopsis>
100  </configOption>
101  </configObject>
102  </configFile>
103  </configInfo>
104  ***/
105 
106 #include "asterisk.h"
107 
108 #include <stdarg.h>
109 #include <pjlib.h>
110 #include <pjsip.h>
111 #include <pj/log.h>
112 
113 #include "asterisk/options.h"
114 #include "asterisk/logger.h"
115 #include "asterisk/module.h"
116 #include "asterisk/cli.h"
117 #include "asterisk/res_pjproject.h"
118 #include "asterisk/vector.h"
119 #include "asterisk/sorcery.h"
120 #include "asterisk/test.h"
121 #include "asterisk/netsock2.h"
122 
123 static struct ast_sorcery *pjproject_sorcery;
124 static pj_log_func *log_cb_orig;
125 static unsigned decor_orig;
126 
128 
129 /*! Protection from other log intercept instances. There can be only one at a time. */
130 AST_MUTEX_DEFINE_STATIC(pjproject_log_intercept_lock);
131 
133  pthread_t thread;
134  int fd;
135 };
136 
137 static struct pjproject_log_intercept_data pjproject_log_intercept = {
138  .thread = AST_PTHREADT_NULL,
139  .fd = -1,
140 };
141 
142 struct log_mappings {
143  /*! Sorcery object details */
144  SORCERY_OBJECT(details);
145  /*! These are all comma-separated lists of pjproject log levels */
147  /*! pjproject log levels mapped to Asterisk ERROR */
149  /*! pjproject log levels mapped to Asterisk WARNING */
151  /*! pjproject log levels mapped to Asterisk NOTICE */
153  /*! pjproject log levels mapped to Asterisk VERBOSE */
155  /*! pjproject log levels mapped to Asterisk DEBUG */
157  /*! pjproject log levels mapped to Asterisk TRACE */
159  );
160 };
161 
162 static struct log_mappings *default_log_mappings;
163 
164 static struct log_mappings *get_log_mappings(void)
165 {
166  struct log_mappings *mappings;
167 
168  mappings = ast_sorcery_retrieve_by_id(pjproject_sorcery, "log_mappings", "log_mappings");
169  if (!mappings) {
170  return ao2_bump(default_log_mappings);
171  }
172 
173  return mappings;
174 }
175 
176 #define __LOG_SUPPRESS -1
177 
178 static int get_log_level(int pj_level)
179 {
180  int mapped_level;
181  unsigned char l;
182  struct log_mappings *mappings;
183 
184  mappings = get_log_mappings();
185  if (!mappings) {
186  return __LOG_ERROR;
187  }
188 
189  l = '0' + fmin(pj_level, 9);
190 
191  if (strchr(mappings->asterisk_error, l)) {
192  mapped_level = __LOG_ERROR;
193  } else if (strchr(mappings->asterisk_warning, l)) {
194  mapped_level = __LOG_WARNING;
195  } else if (strchr(mappings->asterisk_notice, l)) {
196  mapped_level = __LOG_NOTICE;
197  } else if (strchr(mappings->asterisk_verbose, l)) {
198  mapped_level = __LOG_VERBOSE;
199  } else if (strchr(mappings->asterisk_debug, l)) {
200  mapped_level = __LOG_DEBUG;
201  } else if (strchr(mappings->asterisk_trace, l)) {
202  mapped_level = __LOG_TRACE;
203  } else {
204  mapped_level = __LOG_SUPPRESS;
205  }
206 
207  ao2_ref(mappings, -1);
208  return mapped_level;
209 }
210 
211 static void log_forwarder(int level, const char *data, int len)
212 {
213  int ast_level;
214  /* PJPROJECT doesn't provide much in the way of source info */
215  const char * log_source = "pjproject";
216  int log_line = 0;
217  const char *log_func = "<?>";
218 
219  if (pjproject_log_intercept.fd != -1
220  && pjproject_log_intercept.thread == pthread_self()) {
221  /*
222  * We are handling a CLI command intercepting PJPROJECT
223  * log output.
224  */
225  ast_cli(pjproject_log_intercept.fd, "%s\n", data);
226  return;
227  }
228 
229  ast_level = get_log_level(level);
230 
231  if (ast_level == __LOG_SUPPRESS) {
232  return;
233  }
234 
235  /* PJPROJECT uses indention to indicate function call depth. We'll prepend
236  * log statements with a tab so they'll have a better shot at lining
237  * up */
238  ast_log(ast_level, log_source, log_line, log_func, "\t%s\n", data);
239 }
240 
241 static void capture_buildopts_cb(int level, const char *data, int len)
242 {
243  char *dup;
244 
245  if (strstr(data, "Teluu") || strstr(data, "Dumping")) {
246  return;
247  }
248 
249  dup = ast_strdup(ast_skip_blanks(data));
250  if (dup && AST_VECTOR_ADD_SORTED(&buildopts, dup, strcmp)) {
251  ast_free(dup);
252  }
253 }
254 
255 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
256 int ast_pjproject_get_buildopt(char *option, char *format_string, ...)
257 {
258  int res = 0;
259  char *format_temp;
260  int i;
261 
262  format_temp = ast_alloca(strlen(option) + strlen(" : ") + strlen(format_string) + 1);
263  sprintf(format_temp, "%s : %s", option, format_string);
264 
265  for (i = 0; i < AST_VECTOR_SIZE(&buildopts); i++) {
266  va_list arg_ptr;
267  va_start(arg_ptr, format_string);
268  res = vsscanf(AST_VECTOR_GET(&buildopts, i), format_temp, arg_ptr);
269  va_end(arg_ptr);
270  if (res) {
271  break;
272  }
273  }
274 
275  return res;
276 }
277 #pragma GCC diagnostic warning "-Wformat-nonliteral"
278 
280 {
281  /* Protect from other CLI instances trying to do this at the same time. */
282  ast_mutex_lock(&pjproject_log_intercept_lock);
283 
284  pjproject_log_intercept.thread = pthread_self();
285  pjproject_log_intercept.fd = fd;
286 }
287 
289 {
290  pjproject_log_intercept.fd = -1;
291  pjproject_log_intercept.thread = AST_PTHREADT_NULL;
292 
293  ast_mutex_unlock(&pjproject_log_intercept_lock);
294 }
295 
296 static char *handle_pjproject_show_buildopts(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
297 {
298  int i;
299 
300  switch (cmd) {
301  case CLI_INIT:
302  e->command = "pjproject show buildopts";
303  e->usage =
304  "Usage: pjproject show buildopts\n"
305  " Show the compile time config of the pjproject that Asterisk is\n"
306  " running against.\n";
307  return NULL;
308  case CLI_GENERATE:
309  return NULL;
310  }
311 
312  ast_cli(a->fd, "PJPROJECT compile time config currently running against:\n");
313 
314  for (i = 0; i < AST_VECTOR_SIZE(&buildopts); i++) {
315  ast_cli(a->fd, "%s\n", AST_VECTOR_GET(&buildopts, i));
316  }
317 
318  return CLI_SUCCESS;
319 }
320 
321 static void mapping_destroy(void *object)
322 {
323  struct log_mappings *mappings = object;
324 
326 }
327 
328 static void *mapping_alloc(const char *name)
329 {
330  struct log_mappings *mappings = ast_sorcery_generic_alloc(sizeof(*mappings), mapping_destroy);
331  if (!mappings) {
332  return NULL;
333  }
334  ast_string_field_init(mappings, 128);
335 
336  return mappings;
337 }
338 
339 static char *handle_pjproject_show_log_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
340 {
341  struct ast_variable *objset;
342  struct ast_variable *i;
343  struct log_mappings *mappings;
344 
345  switch (cmd) {
346  case CLI_INIT:
347  e->command = "pjproject show log mappings";
348  e->usage =
349  "Usage: pjproject show log mappings\n"
350  " Show pjproject to Asterisk log mappings\n";
351  return NULL;
352  case CLI_GENERATE:
353  return NULL;
354  }
355 
356  ast_cli(a->fd, "PJPROJECT to Asterisk log mappings:\n");
357  ast_cli(a->fd, "Asterisk Level : PJPROJECT log levels\n");
358 
359  mappings = get_log_mappings();
360  if (!mappings) {
361  ast_log(LOG_ERROR, "Unable to retrieve pjproject log_mappings\n");
362  return CLI_SUCCESS;
363  }
364 
365  objset = ast_sorcery_objectset_create(pjproject_sorcery, mappings);
366  if (!objset) {
367  ao2_ref(mappings, -1);
368  return CLI_SUCCESS;
369  }
370 
371  for (i = objset; i; i = i->next) {
372  ast_cli(a->fd, "%-16s : %s\n", i->name, i->value);
373  }
374  ast_variables_destroy(objset);
375 
376  ao2_ref(mappings, -1);
377  return CLI_SUCCESS;
378 }
379 
381  /*!
382  * Compile time sanity check to determine if
383  * MAX_PJ_LOG_MAX_LEVEL matches CLI syntax.
384  */
385  char check[1 / (6 == MAX_PJ_LOG_MAX_LEVEL)];
386 };
387 
388 static char *handle_pjproject_set_log_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
389 {
390  int level_new;
391  int level_old;
392 
393  switch (cmd) {
394  case CLI_INIT:
395  e->command = "pjproject set log level {default|0|1|2|3|4|5|6}";
396  e->usage =
397  "Usage: pjproject set log level {default|<level>}\n"
398  "\n"
399  " Set the maximum active pjproject logging level.\n"
400  " See pjproject.conf.sample for additional information\n"
401  " about the various levels pjproject uses.\n"
402  " Note: setting this level at 4 or above may result in\n"
403  " raw packet logging.\n";
404  return NULL;
405  case CLI_GENERATE:
406  return NULL;
407  }
408 
409  if (a->argc != 5) {
410  return CLI_SHOWUSAGE;
411  }
412 
413  if (!strcasecmp(a->argv[4], "default")) {
414  level_new = DEFAULT_PJ_LOG_MAX_LEVEL;
415  } else {
416  if (sscanf(a->argv[4], "%30d", &level_new) != 1
417  || level_new < 0 || MAX_PJ_LOG_MAX_LEVEL < level_new) {
418  return CLI_SHOWUSAGE;
419  }
420  }
421 
422  /* Update pjproject logging level */
423  if (ast_pjproject_max_log_level < level_new) {
424  level_new = ast_pjproject_max_log_level;
425  ast_cli(a->fd,
426  "Asterisk built or linked with pjproject PJ_LOG_MAX_LEVEL=%d.\n"
427  "Lowering request to the max supported level.\n",
429  }
430  level_old = ast_option_pjproject_log_level;
431  if (level_old == level_new) {
432  ast_cli(a->fd, "pjproject log level is still %d.\n", level_old);
433  } else {
434  ast_cli(a->fd, "pjproject log level was %d and is now %d.\n",
435  level_old, level_new);
436  ast_option_pjproject_log_level = level_new;
437  pj_log_set_level(level_new);
438  }
439 
440  return CLI_SUCCESS;
441 }
442 
443 static char *handle_pjproject_show_log_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
444 {
445  switch (cmd) {
446  case CLI_INIT:
447  e->command = "pjproject show log level";
448  e->usage =
449  "Usage: pjproject show log level\n"
450  "\n"
451  " Show the current maximum active pjproject logging level.\n"
452  " See pjproject.conf.sample for additional information\n"
453  " about the various levels pjproject uses.\n";
454  return NULL;
455  case CLI_GENERATE:
456  return NULL;
457  }
458 
459  if (a->argc != 4) {
460  return CLI_SHOWUSAGE;
461  }
462 
463  ast_cli(a->fd, "pjproject log level is %d.%s\n",
466 
467  return CLI_SUCCESS;
468 }
469 
470 static struct ast_cli_entry pjproject_cli[] = {
471  AST_CLI_DEFINE(handle_pjproject_set_log_level, "Set the maximum active pjproject logging level"),
472  AST_CLI_DEFINE(handle_pjproject_show_buildopts, "Show the compiled config of the pjproject in use"),
473  AST_CLI_DEFINE(handle_pjproject_show_log_mappings, "Show pjproject to Asterisk log mappings"),
474  AST_CLI_DEFINE(handle_pjproject_show_log_level, "Show the maximum active pjproject logging level"),
475 };
476 
477 void ast_pjproject_caching_pool_init(pj_caching_pool *cp,
478  const pj_pool_factory_policy *policy, pj_size_t max_capacity)
479 {
480  /* Passing a max_capacity of zero disables caching pools */
481  pj_caching_pool_init(cp, policy, ast_option_pjproject_cache_pools ? max_capacity : 0);
482 }
483 
484 void ast_pjproject_caching_pool_destroy(pj_caching_pool *cp)
485 {
486  pj_caching_pool_destroy(cp);
487 }
488 
489 int ast_sockaddr_to_pj_sockaddr(const struct ast_sockaddr *addr, pj_sockaddr *pjaddr)
490 {
491  if (addr->ss.ss_family == AF_INET) {
492  struct sockaddr_in *sin = (struct sockaddr_in *) &addr->ss;
493  pjaddr->ipv4.sin_family = pj_AF_INET();
494 #if defined(HAVE_PJPROJECT_BUNDLED) && !defined(HAVE_PJPROJECT_BUNDLED_OOT)
495  pjaddr->ipv4.sin_addr = sin->sin_addr;
496 #else
497  pjaddr->ipv4.sin_addr.s_addr = sin->sin_addr.s_addr;
498 #endif
499  pjaddr->ipv4.sin_port = sin->sin_port;
500  } else if (addr->ss.ss_family == AF_INET6) {
501  struct sockaddr_in6 *sin = (struct sockaddr_in6 *) &addr->ss;
502  pjaddr->ipv6.sin6_family = pj_AF_INET6();
503  pjaddr->ipv6.sin6_port = sin->sin6_port;
504  pjaddr->ipv6.sin6_flowinfo = sin->sin6_flowinfo;
505  pjaddr->ipv6.sin6_scope_id = sin->sin6_scope_id;
506  memcpy(&pjaddr->ipv6.sin6_addr, &sin->sin6_addr, sizeof(pjaddr->ipv6.sin6_addr));
507  } else {
508  memset(pjaddr, 0, sizeof(*pjaddr));
509  return -1;
510  }
511  return 0;
512 }
513 
514 int ast_sockaddr_from_pj_sockaddr(struct ast_sockaddr *addr, const pj_sockaddr *pjaddr)
515 {
516  if (pjaddr->addr.sa_family == pj_AF_INET()) {
517  struct sockaddr_in *sin = (struct sockaddr_in *) &addr->ss;
518  sin->sin_family = AF_INET;
519 #if defined(HAVE_PJPROJECT_BUNDLED) && !defined(HAVE_PJPROJECT_BUNDLED_OOT)
520  sin->sin_addr = pjaddr->ipv4.sin_addr;
521 #else
522  sin->sin_addr.s_addr = pjaddr->ipv4.sin_addr.s_addr;
523 #endif
524  sin->sin_port = pjaddr->ipv4.sin_port;
525  memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
526  addr->len = sizeof(struct sockaddr_in);
527  } else if (pjaddr->addr.sa_family == pj_AF_INET6()) {
528  struct sockaddr_in6 *sin = (struct sockaddr_in6 *) &addr->ss;
529  sin->sin6_family = AF_INET6;
530  sin->sin6_port = pjaddr->ipv6.sin6_port;
531  sin->sin6_flowinfo = pjaddr->ipv6.sin6_flowinfo;
532  sin->sin6_scope_id = pjaddr->ipv6.sin6_scope_id;
533  memcpy(&sin->sin6_addr, &pjaddr->ipv6.sin6_addr, sizeof(sin->sin6_addr));
534  addr->len = sizeof(struct sockaddr_in6);
535  } else {
536  memset(addr, 0, sizeof(*addr));
537  return -1;
538  }
539  return 0;
540 }
541 
543  const pj_sockaddr *pjaddr)
544 {
545  struct ast_sockaddr temp_pjaddr;
546  int rc = 0;
547 
548  rc = ast_sockaddr_from_pj_sockaddr(&temp_pjaddr, pjaddr);
549  if (rc != 0) {
550  return -1;
551  }
552 
553  rc = ast_sockaddr_cmp(addr, &temp_pjaddr);
554  if (DEBUG_ATLEAST(4)) {
555  char *a_str = ast_strdupa(ast_sockaddr_stringify(addr));
556  char *pj_str = ast_strdupa(ast_sockaddr_stringify(&temp_pjaddr));
557  ast_debug(4, "Comparing %s -> %s rc: %d\n", a_str, pj_str, rc);
558  }
559 
560  return rc;
561 }
562 
563 #ifdef TEST_FRAMEWORK
564 static void fill_with_garbage(void *x, ssize_t len)
565 {
566  unsigned char *w = x;
567  while (len > 0) {
568  int r = ast_random();
569  memcpy(w, &r, len > sizeof(r) ? sizeof(r) : len);
570  w += sizeof(r);
571  len -= sizeof(r);
572  }
573 }
574 
575 AST_TEST_DEFINE(ast_sockaddr_to_pj_sockaddr_test)
576 {
577  char *candidates[] = {
578  "127.0.0.1:5555",
579  "[::]:4444",
580  "192.168.0.100:0",
581  "[fec0::1:80]:0",
582  "[fec0::1]:80",
583  NULL,
584  }, **candidate = candidates;
585 
586  switch (cmd) {
587  case TEST_INIT:
588  info->name = "ast_sockaddr_to_pj_sockaddr_test";
589  info->category = "/res/res_pjproject/";
590  info->summary = "Validate conversions from an ast_sockaddr to a pj_sockaddr";
591  info->description = "This test converts an ast_sockaddr to a pj_sockaddr and validates\n"
592  "that the two evaluate to the same string when formatted.";
593  return AST_TEST_NOT_RUN;
594  case TEST_EXECUTE:
595  break;
596  }
597 
598  while (*candidate) {
599  struct ast_sockaddr addr = {{0,}};
600  pj_sockaddr pjaddr;
601  char buffer[512];
602 
603  fill_with_garbage(&pjaddr, sizeof(pj_sockaddr));
604 
605  if (!ast_sockaddr_parse(&addr, *candidate, 0)) {
606  ast_test_status_update(test, "Failed to parse candidate IP: %s\n", *candidate);
607  return AST_TEST_FAIL;
608  }
609 
610  if (ast_sockaddr_to_pj_sockaddr(&addr, &pjaddr)) {
611  ast_test_status_update(test, "Failed to convert ast_sockaddr to pj_sockaddr: %s\n", *candidate);
612  return AST_TEST_FAIL;
613  }
614 
615  pj_sockaddr_print(&pjaddr, buffer, sizeof(buffer), 1 | 2);
616 
617  if (strcmp(*candidate, buffer)) {
618  ast_test_status_update(test, "Converted sockaddrs do not match: \"%s\" and \"%s\"\n",
619  *candidate,
620  buffer);
621  return AST_TEST_FAIL;
622  }
623 
624  candidate++;
625  }
626 
627  return AST_TEST_PASS;
628 }
629 
630 AST_TEST_DEFINE(ast_sockaddr_from_pj_sockaddr_test)
631 {
632  char *candidates[] = {
633  "127.0.0.1:5555",
634  "[::]:4444",
635  "192.168.0.100:0",
636  "[fec0::1:80]:0",
637  "[fec0::1]:80",
638  NULL,
639  }, **candidate = candidates;
640 
641  switch (cmd) {
642  case TEST_INIT:
643  info->name = "ast_sockaddr_from_pj_sockaddr_test";
644  info->category = "/res/res_pjproject/";
645  info->summary = "Validate conversions from a pj_sockaddr to an ast_sockaddr";
646  info->description = "This test converts a pj_sockaddr to an ast_sockaddr and validates\n"
647  "that the two evaluate to the same string when formatted.";
648  return AST_TEST_NOT_RUN;
649  case TEST_EXECUTE:
650  break;
651  }
652 
653  while (*candidate) {
654  struct ast_sockaddr addr = {{0,}};
655  pj_sockaddr pjaddr;
656  pj_str_t t;
657  char buffer[512];
658 
659  fill_with_garbage(&addr, sizeof(addr));
660 
661  pj_strset(&t, *candidate, strlen(*candidate));
662 
663  if (pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &t, &pjaddr) != PJ_SUCCESS) {
664  ast_test_status_update(test, "Failed to parse candidate IP: %s\n", *candidate);
665  return AST_TEST_FAIL;
666  }
667 
668  if (ast_sockaddr_from_pj_sockaddr(&addr, &pjaddr)) {
669  ast_test_status_update(test, "Failed to convert pj_sockaddr to ast_sockaddr: %s\n", *candidate);
670  return AST_TEST_FAIL;
671  }
672 
673  snprintf(buffer, sizeof(buffer), "%s", ast_sockaddr_stringify(&addr));
674 
675  if (strcmp(*candidate, buffer)) {
676  ast_test_status_update(test, "Converted sockaddrs do not match: \"%s\" and \"%s\"\n",
677  *candidate,
678  buffer);
679  return AST_TEST_FAIL;
680  }
681 
682  candidate++;
683  }
684 
685  return AST_TEST_PASS;
686 }
687 #endif
688 
689 static int load_module(void)
690 {
691  ast_debug(3, "Starting PJPROJECT logging to Asterisk logger\n");
692 
693  if (!(pjproject_sorcery = ast_sorcery_open())) {
694  ast_log(LOG_ERROR, "Failed to open SIP sorcery failed to open\n");
696  }
697 
698  ast_sorcery_apply_default(pjproject_sorcery, "log_mappings", "config", "pjproject.conf,criteria=type=log_mappings");
699  if (ast_sorcery_object_register(pjproject_sorcery, "log_mappings", mapping_alloc, NULL, NULL)) {
700  ast_log(LOG_WARNING, "Failed to register pjproject log_mappings object with sorcery\n");
701  ast_sorcery_unref(pjproject_sorcery);
702  pjproject_sorcery = NULL;
704  }
705 
706  ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "type", "", OPT_NOOP_T, 0, 0);
707  ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_debug", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_debug));
708  ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_error", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_error));
709  ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_warning", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_warning));
710  ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_notice", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_notice));
711  ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_verbose", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_verbose));
712  ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_trace", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_trace));
713 
714  default_log_mappings = ast_sorcery_alloc(pjproject_sorcery, "log_mappings", "log_mappings");
715  if (!default_log_mappings) {
716  ast_log(LOG_ERROR, "Unable to allocate memory for pjproject log_mappings\n");
718  }
719  ast_string_field_set(default_log_mappings, asterisk_error, "0,1");
720  ast_string_field_set(default_log_mappings, asterisk_warning, "2");
721  ast_string_field_set(default_log_mappings, asterisk_debug, "3,4");
722  ast_string_field_set(default_log_mappings, asterisk_trace, "5,6");
723 
724  ast_sorcery_load(pjproject_sorcery);
725 
727  pj_init();
728 
729  decor_orig = pj_log_get_decor();
730  log_cb_orig = pj_log_get_log_func();
731 
732  if (AST_VECTOR_INIT(&buildopts, 64)) {
734  }
735 
736  /*
737  * On startup, we want to capture the dump once and store it.
738  */
739  pj_log_set_log_func(capture_buildopts_cb);
740  pj_log_set_decor(0);
741  pj_log_set_level(MAX_PJ_LOG_MAX_LEVEL);/* Set level to guarantee the dump output. */
742  pj_dump_config();
743  pj_log_set_decor(PJ_LOG_HAS_SENDER | PJ_LOG_HAS_INDENT);
744  pj_log_set_log_func(log_forwarder);
746  ast_log(LOG_WARNING,
747  "Asterisk built or linked with pjproject PJ_LOG_MAX_LEVEL=%d which is too low for startup level: %d.\n",
750  }
751  pj_log_set_level(ast_option_pjproject_log_level);
752  if (!AST_VECTOR_SIZE(&buildopts)) {
753  ast_log(LOG_NOTICE,
754  "Asterisk built or linked with pjproject PJ_LOG_MAX_LEVEL=%d which is too low to get buildopts.\n",
756  }
757 
758  ast_cli_register_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli));
759 
760  AST_TEST_REGISTER(ast_sockaddr_to_pj_sockaddr_test);
761  AST_TEST_REGISTER(ast_sockaddr_from_pj_sockaddr_test);
762 
764 }
765 
766 #define NOT_EQUALS(a, b) (a != b)
767 
768 static int unload_module(void)
769 {
770  ast_cli_unregister_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli));
771  pj_log_set_log_func(log_cb_orig);
772  pj_log_set_decor(decor_orig);
773 
776 
777  ast_debug(3, "Stopped PJPROJECT logging to Asterisk logger\n");
778 
779  pj_shutdown();
780 
781  ao2_cleanup(default_log_mappings);
782  default_log_mappings = NULL;
783 
784  ast_sorcery_unref(pjproject_sorcery);
785 
786  AST_TEST_UNREGISTER(ast_sockaddr_to_pj_sockaddr_test);
787  AST_TEST_UNREGISTER(ast_sockaddr_from_pj_sockaddr_test);
788 
789  return 0;
790 }
791 
792 static int reload_module(void)
793 {
794  if (pjproject_sorcery) {
795  ast_sorcery_reload(pjproject_sorcery);
796  }
797 
799 }
800 
801 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJPROJECT Log and Utility Support",
802  .support_level = AST_MODULE_SUPPORT_CORE,
803  .load = load_module,
804  .unload = unload_module,
805  .reload = reload_module,
806  .load_pri = AST_MODPRI_CHANNEL_DEPEND - 6,
807  .requires = "res_sorcery_config",
808 );
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
struct ast_variable * next
#define AST_VECTOR_ADD_SORTED(vec, elem, cmp)
Add an element into a sorted vector.
Definition: vector.h:371
Asterisk main include file. File version handling, generic pbx functions.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition: netsock2.c:230
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define DEFAULT_PJ_LOG_MAX_LEVEL
Definition: options.h:149
descriptor for a cli entry.
Definition: cli.h:171
int ast_pjproject_max_log_level
Definition: options.c:73
const ast_string_field asterisk_notice
Structure for variables, used for configurations and for channel variables.
pthread_t thread
Definition: app_sla.c:329
Test Framework API.
Full structure for sorcery.
Definition: sorcery.c:230
Type for a default handler that should do nothing.
char check[1/(6==MAX_PJ_LOG_MAX_LEVEL)]
const ast_string_field asterisk_verbose
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
int ast_sockaddr_cmp(const struct ast_sockaddr *a, const struct ast_sockaddr *b)
Compares two ast_sockaddr structures.
Definition: netsock2.c:388
void ast_pjproject_caching_pool_init(pj_caching_pool *cp, const pj_pool_factory_policy *policy, pj_size_t max_capacity)
Initialize the caching pool factory.
Socket address structure.
Definition: netsock2.h:97
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition: sorcery.c:1853
#define ast_sorcery_unref(sorcery)
Decrease the reference count of a sorcery structure.
Definition: sorcery.h:1500
SORCERY_OBJECT(details)
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
static ast_mutex_t pjproject_log_intercept_lock
void ast_pjproject_log_intercept_end(void)
End PJPROJECT log interception for CLI output.
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
int ast_option_pjproject_log_level
Definition: options.c:74
void ast_pjproject_log_intercept_begin(int fd)
Begin PJPROJECT log interception for CLI output.
#define ast_debug(level,...)
Log a DEBUG message.
int ast_pjproject_get_buildopt(char *option, char *format_string,...)
Retrieve a pjproject build option.
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:837
Network socket handling.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
int ast_option_pjproject_cache_pools
Definition: options.c:75
#define AST_PJPROJECT_INIT_LOG_LEVEL()
Get maximum log level pjproject was compiled with.
Definition: options.h:167
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:161
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
Definition: sorcery.c:1744
void ast_pjproject_caching_pool_destroy(pj_caching_pool *cp)
Destroy caching pool factory and all cached pools.
#define ast_sorcery_objectset_create(sorcery, object)
Create an object set (KVP list) for an object.
Definition: sorcery.h:1137
char * command
Definition: cli.h:186
const ast_string_field asterisk_debug
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
Support for logging to various files, console and syslog Configuration in file logger.conf.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Vector container support.
const ast_string_field asterisk_trace
const char * usage
Definition: cli.h:177
#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
Definition: sorcery.h:955
void ast_sorcery_load(const struct ast_sorcery *sorcery)
Inform any wizards to load persistent objects.
Definition: sorcery.c:1377
void ast_sorcery_reload(const struct ast_sorcery *sorcery)
Inform any wizards to reload persistent objects.
Definition: sorcery.c:1408
#define MAX_PJ_LOG_MAX_LEVEL
Definition: options.h:141
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680
int ast_sockaddr_from_pj_sockaddr(struct ast_sockaddr *addr, const pj_sockaddr *pjaddr)
Fill an ast_sockaddr from a pj_sockaddr.
Standard Command Line Interface.
#define ast_sorcery_open()
Open a new sorcery structure.
Definition: sorcery.h:406
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
Type for default option handler for stringfields.
Options provided by main asterisk program.
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
int ast_sockaddr_pj_sockaddr_cmp(const struct ast_sockaddr *addr, const pj_sockaddr *pjaddr)
Compare an ast_sockaddr to a pj_sockaddr.
const ast_string_field asterisk_warning
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
const ast_string_field asterisk_error
int ast_sockaddr_to_pj_sockaddr(const struct ast_sockaddr *addr, pj_sockaddr *pjaddr)
Fill a pj_sockaddr from an ast_sockaddr.
#define AST_VECTOR_CALLBACK_VOID(vec, callback,...)
Execute a callback on every element in a vector disregarding callback return.
Definition: vector.h:862
Sorcery Data Access Layer API.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521