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