Asterisk - The Open Source Telephony Project  18.5.0
Data Structures | Macros | Functions | Variables
res_pjproject.c File Reference

Bridge PJPROJECT logging to Asterisk logging. More...

#include "asterisk.h"
#include <stdarg.h>
#include <pjlib.h>
#include <pjsip.h>
#include <pj/log.h>
#include "asterisk/options.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/cli.h"
#include "asterisk/res_pjproject.h"
#include "asterisk/vector.h"
#include "asterisk/sorcery.h"
#include "asterisk/test.h"
#include "asterisk/netsock2.h"
Include dependency graph for res_pjproject.c:

Go to the source code of this file.

Data Structures

struct  log_mappings
 
struct  max_pjproject_log_level_check
 
struct  pjproject_log_intercept_data
 

Macros

#define __LOG_SUPPRESS   -1
 
#define NOT_EQUALS(a, b)   (a != b)
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
void ast_pjproject_caching_pool_destroy (pj_caching_pool *cp)
 Destroy caching pool factory and all cached pools. More...
 
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. More...
 
int ast_pjproject_get_buildopt (char *option, char *format_string,...)
 Retrieve a pjproject build option. More...
 
void ast_pjproject_log_intercept_begin (int fd)
 Begin PJPROJECT log interception for CLI output. More...
 
void ast_pjproject_log_intercept_end (void)
 End PJPROJECT log interception for CLI output. More...
 
int ast_sockaddr_from_pj_sockaddr (struct ast_sockaddr *addr, const pj_sockaddr *pjaddr)
 Fill an ast_sockaddr from a pj_sockaddr. More...
 
int ast_sockaddr_to_pj_sockaddr (const struct ast_sockaddr *addr, pj_sockaddr *pjaddr)
 Fill a pj_sockaddr from an ast_sockaddr. More...
 
 AST_TEST_DEFINE (ast_sockaddr_to_pj_sockaddr_test)
 
 AST_TEST_DEFINE (ast_sockaddr_from_pj_sockaddr_test)
 
static AST_VECTOR (buildopts, char *)
 
static void capture_buildopts_cb (int level, const char *data, int len)
 
static void fill_with_garbage (void *x, ssize_t len)
 
static int get_log_level (int pj_level)
 
static struct log_mappingsget_log_mappings (void)
 
static char * handle_pjproject_set_log_level (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * handle_pjproject_show_buildopts (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * handle_pjproject_show_log_level (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * handle_pjproject_show_log_mappings (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static int load_module (void)
 
static void log_forwarder (int level, const char *data, int len)
 
static void * mapping_alloc (const char *name)
 
static void mapping_destroy (void *object)
 
static int reload_module (void)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "PJPROJECT Log and Utility Support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "30ef0c93b36035ec78c9cfd712d36d9b" , .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload_module, .load_pri = AST_MODPRI_CHANNEL_DEPEND - 6, .requires = "res_sorcery_config", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static unsigned decor_orig
 
static struct log_mappingsdefault_log_mappings
 
static pj_log_func * log_cb_orig
 
static struct ast_cli_entry pjproject_cli []
 
static struct pjproject_log_intercept_data pjproject_log_intercept
 
static struct ast_sorcerypjproject_sorcery
 

Detailed Description

Bridge PJPROJECT logging to Asterisk logging.

Author
David M. Lee, II dlee@.nosp@m.digi.nosp@m.um.co.nosp@m.m

PJPROJECT logging doesn't exactly match Asterisk logging, but mapping the two is not too bad. PJPROJECT log levels are identified by a single int. Limits are not specified by PJPROJECT, but their implementation used 1 through 6.

The mapping is as follows:

Definition in file res_pjproject.c.

Macro Definition Documentation

◆ __LOG_SUPPRESS

#define __LOG_SUPPRESS   -1

Definition at line 168 of file res_pjproject.c.

Referenced by get_log_level(), and log_forwarder().

◆ NOT_EQUALS

#define NOT_EQUALS (   a,
  b 
)    (a != b)

Definition at line 730 of file res_pjproject.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 772 of file res_pjproject.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 772 of file res_pjproject.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 772 of file res_pjproject.c.

◆ ast_pjproject_caching_pool_destroy()

void ast_pjproject_caching_pool_destroy ( pj_caching_pool *  cp)

Destroy caching pool factory and all cached pools.

Since
13.21.0
Parameters
cpCaching pool factory to destroy
Returns
Nothing

Definition at line 472 of file res_pjproject.c.

Referenced by rtp_terminate_pjproject(), unload_module(), and unload_pjsip().

473 {
474  pj_caching_pool_destroy(cp);
475 }

◆ ast_pjproject_caching_pool_init()

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.

Since
13.21.0
Parameters
cpCaching pool factory to initialize
policyPool factory policy
max_capacityTotal capacity to be retained in the cache. Zero disables caching.
Returns
Nothing

Definition at line 465 of file res_pjproject.c.

References ast_option_pjproject_cache_pools.

Referenced by load_module(), and load_pjsip().

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 }
int ast_option_pjproject_cache_pools
Definition: options.c:75

◆ ast_pjproject_get_buildopt()

int ast_pjproject_get_buildopt ( char *  option,
char *  format_string,
  ... 
)

Retrieve a pjproject build option.

Parameters
optionThe build option requested
format_stringA scanf-style format string to parse the option value into
...Pointers to variables to receive the values parsed
Return values
Thenumber of values parsed
Since
13.8.0
Note
The option requested must be from those returned by pj_dump_config() which can be displayed with the 'pjsip show buildopts' CLI command.

Sample Usage:

int max_hostname;
ast_sip_get_pjproject_buildopt("PJ_MAX_HOSTNAME", "%d", &max_hostname);

Definition at line 246 of file res_pjproject.c.

References ast_alloca, AST_VECTOR_GET, and AST_VECTOR_SIZE.

Referenced by ast_sip_initialize_sorcery_location(), and load_module().

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 }
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:682
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611

◆ ast_pjproject_log_intercept_begin()

void ast_pjproject_log_intercept_begin ( int  fd)

Begin PJPROJECT log interception for CLI output.

Since
13.8.0
Parameters
fdCLI file descriptior to send intercepted output.
Note
ast_pjproject_log_intercept_begin() and ast_pjproject_log_intercept_end() must always be called in pairs.
Returns
Nothing

Definition at line 269 of file res_pjproject.c.

References ast_mutex_lock, pjproject_log_intercept_data::fd, and pjproject_log_intercept_data::thread.

Referenced by do_cli_dump_endpt().

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();
276 }
static struct pjproject_log_intercept_data pjproject_log_intercept
#define ast_mutex_lock(a)
Definition: lock.h:187

◆ ast_pjproject_log_intercept_end()

void ast_pjproject_log_intercept_end ( void  )

End PJPROJECT log interception for CLI output.

Since
13.8.0
Note
ast_pjproject_log_intercept_begin() and ast_pjproject_log_intercept_end() must always be called in pairs.
Returns
Nothing

Definition at line 278 of file res_pjproject.c.

References ast_mutex_unlock, AST_PTHREADT_NULL, pjproject_log_intercept_data::fd, and pjproject_log_intercept_data::thread.

Referenced by do_cli_dump_endpt().

279 {
282 
283  ast_mutex_unlock(&pjproject_log_intercept_lock);
284 }
static struct pjproject_log_intercept_data pjproject_log_intercept
#define AST_PTHREADT_NULL
Definition: lock.h:66
#define ast_mutex_unlock(a)
Definition: lock.h:188

◆ ast_sockaddr_from_pj_sockaddr()

int ast_sockaddr_from_pj_sockaddr ( struct ast_sockaddr addr,
const pj_sockaddr *  pjaddr 
)

Fill an ast_sockaddr from a pj_sockaddr.

Since
13.24.0
Parameters
addrThe target address to receive the copied address
pjaddrThe source address to copy
Return values
0Success
-1Failure

Definition at line 502 of file res_pjproject.c.

References HAVE_PJPROJECT_BUNDLED, ast_sockaddr::len, and ast_sockaddr::ss.

Referenced by AST_TEST_DEFINE().

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 }
struct sockaddr_storage ss
Definition: netsock2.h:98
socklen_t len
Definition: netsock2.h:99
#define HAVE_PJPROJECT_BUNDLED
Definition: autoconfig.h:632

◆ ast_sockaddr_to_pj_sockaddr()

int ast_sockaddr_to_pj_sockaddr ( const struct ast_sockaddr addr,
pj_sockaddr *  pjaddr 
)

Fill a pj_sockaddr from an ast_sockaddr.

Since
13.24.0
Parameters
addrThe source address to copy
pjaddrThe target address to receive the copied address
Return values
0Success
-1Failure

Definition at line 477 of file res_pjproject.c.

References ast_sockaddr::ss.

Referenced by AST_TEST_DEFINE(), and rtp_add_candidates_to_ice().

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 }
struct sockaddr_storage ss
Definition: netsock2.h:98

◆ AST_TEST_DEFINE() [1/2]

AST_TEST_DEFINE ( ast_sockaddr_to_pj_sockaddr_test  )

Definition at line 541 of file res_pjproject.c.

References ast_sockaddr_parse(), ast_sockaddr_to_pj_sockaddr(), AST_TEST_FAIL, AST_TEST_NOT_RUN, AST_TEST_PASS, ast_test_status_update, fill_with_garbage(), sip_to_pjsip::info(), NULL, TEST_EXECUTE, and TEST_INIT.

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 }
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition: netsock2.c:230
#define NULL
Definition: resample.c:96
Socket address structure.
Definition: netsock2.h:97
static void fill_with_garbage(void *x, ssize_t len)
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
def info(msg)
int ast_sockaddr_to_pj_sockaddr(const struct ast_sockaddr *addr, pj_sockaddr *pjaddr)
Fill a pj_sockaddr from an ast_sockaddr.

◆ AST_TEST_DEFINE() [2/2]

AST_TEST_DEFINE ( ast_sockaddr_from_pj_sockaddr_test  )

Definition at line 596 of file res_pjproject.c.

References ast_sockaddr_from_pj_sockaddr(), ast_sockaddr_stringify(), AST_TEST_FAIL, AST_TEST_NOT_RUN, AST_TEST_PASS, ast_test_status_update, fill_with_garbage(), sip_to_pjsip::info(), NULL, TEST_EXECUTE, and TEST_INIT.

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 }
#define NULL
Definition: resample.c:96
Socket address structure.
Definition: netsock2.h:97
static void fill_with_garbage(void *x, ssize_t len)
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
def info(msg)
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:260
int ast_sockaddr_from_pj_sockaddr(struct ast_sockaddr *addr, const pj_sockaddr *pjaddr)
Fill an ast_sockaddr from a pj_sockaddr.

◆ AST_VECTOR()

static AST_VECTOR (   buildopts,
char *   
)
static

Protection from other log intercept instances. There can be only one at a time.

Definition at line 121 of file res_pjproject.c.

126  {

◆ capture_buildopts_cb()

static void capture_buildopts_cb ( int  level,
const char *  data,
int  len 
)
static

Definition at line 231 of file res_pjproject.c.

References ast_free, ast_skip_blanks(), ast_strdup, and AST_VECTOR_ADD_SORTED.

Referenced by load_module().

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 }
#define AST_VECTOR_ADD_SORTED(vec, elem, cmp)
Add an element into a sorted vector.
Definition: vector.h:371
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:157
#define ast_free(a)
Definition: astmm.h:182

◆ fill_with_garbage()

static void fill_with_garbage ( void *  x,
ssize_t  len 
)
static

Definition at line 530 of file res_pjproject.c.

References ast_random().

Referenced by AST_TEST_DEFINE().

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 }
long int ast_random(void)
Definition: main/utils.c:2064
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)

◆ get_log_level()

static int get_log_level ( int  pj_level)
static

Definition at line 170 of file res_pjproject.c.

References __LOG_DEBUG, __LOG_ERROR, __LOG_NOTICE, __LOG_SUPPRESS, __LOG_VERBOSE, __LOG_WARNING, ao2_ref, log_mappings::asterisk_debug, log_mappings::asterisk_error, log_mappings::asterisk_notice, log_mappings::asterisk_verbose, log_mappings::asterisk_warning, get_log_mappings(), and mappings.

Referenced by log_forwarder().

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 }
#define __LOG_DEBUG
Definition: logger.h:240
const ast_string_field asterisk_notice
const ast_string_field asterisk_verbose
#define __LOG_WARNING
Definition: logger.h:273
#define __LOG_ERROR
Definition: logger.h:284
#define __LOG_SUPPRESS
#define ao2_ref(o, delta)
Definition: astobj2.h:464
static struct log_mappings * get_log_mappings(void)
const ast_string_field asterisk_debug
#define __LOG_NOTICE
Definition: logger.h:262
static struct mappings mappings
#define __LOG_VERBOSE
Definition: logger.h:295
const ast_string_field asterisk_warning
const ast_string_field asterisk_error

◆ get_log_mappings()

static struct log_mappings* get_log_mappings ( void  )
static

Definition at line 156 of file res_pjproject.c.

References ao2_bump, ast_sorcery_retrieve_by_id(), and mappings.

Referenced by get_log_level(), and handle_pjproject_show_log_mappings().

157 {
158  struct log_mappings *mappings;
159 
160  mappings = ast_sorcery_retrieve_by_id(pjproject_sorcery, "log_mappings", "log_mappings");
161  if (!mappings) {
163  }
164 
165  return mappings;
166 }
static struct log_mappings * default_log_mappings
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 ao2_bump(obj)
Definition: astobj2.h:491
static struct ast_sorcery * pjproject_sorcery
static struct mappings mappings

◆ handle_pjproject_set_log_level()

static char* handle_pjproject_set_log_level ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
)
static

Definition at line 378 of file res_pjproject.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_option_pjproject_log_level, ast_pjproject_max_log_level, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, DEFAULT_PJ_LOG_MAX_LEVEL, ast_cli_args::fd, MAX_PJ_LOG_MAX_LEVEL, NULL, and ast_cli_entry::usage.

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 }
#define DEFAULT_PJ_LOG_MAX_LEVEL
Definition: options.h:148
const int argc
Definition: cli.h:160
int ast_pjproject_max_log_level
Definition: options.c:73
Definition: cli.h:152
#define NULL
Definition: resample.c:96
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
const int fd
Definition: cli.h:159
int ast_option_pjproject_log_level
Definition: options.c:74
const char *const * argv
Definition: cli.h:161
#define CLI_SHOWUSAGE
Definition: cli.h:45
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
#define CLI_SUCCESS
Definition: cli.h:44
#define MAX_PJ_LOG_MAX_LEVEL
Definition: options.h:140

◆ handle_pjproject_show_buildopts()

static char* handle_pjproject_show_buildopts ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
)
static

Definition at line 286 of file res_pjproject.c.

References ast_cli(), AST_VECTOR_GET, AST_VECTOR_SIZE, CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, NULL, and ast_cli_entry::usage.

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 }
Definition: cli.h:152
#define NULL
Definition: resample.c:96
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
const int fd
Definition: cli.h:159
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
#define CLI_SUCCESS
Definition: cli.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:682
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611

◆ handle_pjproject_show_log_level()

static char* handle_pjproject_show_log_level ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
)
static

Definition at line 431 of file res_pjproject.c.

References ast_cli_args::argc, ast_cli(), ast_option_pjproject_log_level, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, DEFAULT_PJ_LOG_MAX_LEVEL, ast_cli_args::fd, NULL, and ast_cli_entry::usage.

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 }
#define DEFAULT_PJ_LOG_MAX_LEVEL
Definition: options.h:148
const int argc
Definition: cli.h:160
Definition: cli.h:152
#define NULL
Definition: resample.c:96
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
const int fd
Definition: cli.h:159
int ast_option_pjproject_log_level
Definition: options.c:74
#define CLI_SHOWUSAGE
Definition: cli.h:45
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
#define CLI_SUCCESS
Definition: cli.h:44

◆ handle_pjproject_show_log_mappings()

static char* handle_pjproject_show_log_mappings ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
)
static

Definition at line 329 of file res_pjproject.c.

References ao2_ref, ast_cli(), ast_log, ast_sorcery_objectset_create, ast_variables_destroy(), CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, get_log_mappings(), LOG_ERROR, mappings, ast_variable::name, ast_variable::next, NULL, ast_cli_entry::usage, and ast_variable::value.

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 
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 }
struct ast_variable * next
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
Structure for variables, used for configurations and for channel variables.
Definition: cli.h:152
#define NULL
Definition: resample.c:96
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
#define ast_log
Definition: astobj2.c:42
const int fd
Definition: cli.h:159
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define LOG_ERROR
Definition: logger.h:285
static struct ast_sorcery * pjproject_sorcery
static struct log_mappings * get_log_mappings(void)
#define ast_sorcery_objectset_create(sorcery, object)
Create an object set (KVP list) for an object.
Definition: sorcery.h:1136
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
#define CLI_SUCCESS
Definition: cli.h:44
static struct mappings mappings

◆ load_module()

static int load_module ( void  )
static

Definition at line 655 of file res_pjproject.c.

References ARRAY_LEN, ast_cli_register_multiple, ast_debug, ast_log, AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_option_pjproject_log_level, AST_PJPROJECT_INIT_LOG_LEVEL, ast_pjproject_max_log_level, ast_sorcery_alloc(), ast_sorcery_apply_default, ast_sorcery_load(), ast_sorcery_object_field_register, ast_sorcery_object_register, ast_sorcery_open, ast_sorcery_unref, ast_string_field_set, AST_TEST_REGISTER, AST_VECTOR_INIT, AST_VECTOR_SIZE, capture_buildopts_cb(), decor_orig, log_cb_orig, LOG_ERROR, log_forwarder(), LOG_NOTICE, LOG_WARNING, mapping_alloc(), MAX_PJ_LOG_MAX_LEVEL, NULL, OPT_NOOP_T, OPT_STRINGFIELD_T, and STRFLDSET.

Referenced by reload_module().

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");
666  ast_log(LOG_WARNING, "Failed to register pjproject log_mappings object with sorcery\n");
670  }
671 
672  ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "type", "", OPT_NOOP_T, 0, 0);
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  }
687 
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 
723 
724  AST_TEST_REGISTER(ast_sockaddr_to_pj_sockaddr_test);
725  AST_TEST_REGISTER(ast_sockaddr_from_pj_sockaddr_test);
726 
728 }
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static struct log_mappings * default_log_mappings
static void * mapping_alloc(const char *name)
static pj_log_func * log_cb_orig
#define LOG_WARNING
Definition: logger.h:274
int ast_pjproject_max_log_level
Definition: options.c:73
const ast_string_field asterisk_notice
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
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
static struct ast_cli_entry pjproject_cli[]
#define NULL
Definition: resample.c:96
#define ast_sorcery_unref(sorcery)
Decrease the reference count of a sorcery structure.
Definition: sorcery.h:1502
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
static unsigned decor_orig
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
int ast_option_pjproject_log_level
Definition: options.c:74
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:838
static void capture_buildopts_cb(int level, const char *data, int len)
#define LOG_ERROR
Definition: logger.h:285
static struct ast_sorcery * pjproject_sorcery
#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
#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
const ast_string_field asterisk_debug
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
#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 MAX_PJ_LOG_MAX_LEVEL
Definition: options.h:140
#define ast_sorcery_open()
Definition: sorcery.h:408
Type for default option handler for stringfields.
const ast_string_field asterisk_warning
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611
const ast_string_field asterisk_error
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514

◆ log_forwarder()

static void log_forwarder ( int  level,
const char *  data,
int  len 
)
static

Definition at line 201 of file res_pjproject.c.

References __LOG_SUPPRESS, ast_cli(), ast_log, pjproject_log_intercept_data::fd, get_log_level(), and pjproject_log_intercept_data::thread.

Referenced by load_module().

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 }
static struct pjproject_log_intercept_data pjproject_log_intercept
#define __LOG_SUPPRESS
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
#define ast_log
Definition: astobj2.c:42
static int get_log_level(int pj_level)

◆ mapping_alloc()

static void* mapping_alloc ( const char *  name)
static

Definition at line 318 of file res_pjproject.c.

References ast_sorcery_generic_alloc(), ast_string_field_init, mapping_destroy(), mappings, and NULL.

Referenced by load_module().

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 }
static void mapping_destroy(void *object)
#define NULL
Definition: resample.c:96
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
static struct mappings mappings
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728

◆ mapping_destroy()

static void mapping_destroy ( void *  object)
static

Definition at line 311 of file res_pjproject.c.

References ast_string_field_free_memory.

Referenced by mapping_alloc().

312 {
313  struct log_mappings *mappings = object;
314 
316 }
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368

◆ reload_module()

static int reload_module ( void  )
static

Definition at line 756 of file res_pjproject.c.

References AST_MODFLAG_GLOBAL_SYMBOLS, AST_MODFLAG_LOAD_ORDER, AST_MODPRI_CHANNEL_DEPEND, AST_MODULE_INFO(), AST_MODULE_LOAD_SUCCESS, AST_MODULE_SUPPORT_CORE, ast_sorcery_reload(), ASTERISK_GPL_KEY, load_module(), reload(), and unload_module().

757 {
758  if (pjproject_sorcery) {
760  }
761 
763 }
static struct ast_sorcery * pjproject_sorcery
void ast_sorcery_reload(const struct ast_sorcery *sorcery)
Inform any wizards to reload persistent objects.
Definition: sorcery.c:1408

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 732 of file res_pjproject.c.

References ao2_cleanup, ARRAY_LEN, ast_cli_unregister_multiple(), ast_debug, ast_free, ast_sorcery_unref, AST_TEST_UNREGISTER, AST_VECTOR_CALLBACK_VOID, AST_VECTOR_FREE, decor_orig, log_cb_orig, and NULL.

Referenced by reload_module().

733 {
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 
747 
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 }
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static struct log_mappings * default_log_mappings
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
static pj_log_func * log_cb_orig
static struct ast_cli_entry pjproject_cli[]
#define NULL
Definition: resample.c:96
#define ast_sorcery_unref(sorcery)
Decrease the reference count of a sorcery structure.
Definition: sorcery.h:1502
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
static unsigned decor_orig
static struct ast_sorcery * pjproject_sorcery
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
#define ast_free(a)
Definition: astmm.h:182
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
#define AST_VECTOR_CALLBACK_VOID(vec, callback,...)
Execute a callback on every element in a vector disregarding callback return.
Definition: vector.h:865

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "PJPROJECT Log and Utility Support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "30ef0c93b36035ec78c9cfd712d36d9b" , .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload_module, .load_pri = AST_MODPRI_CHANNEL_DEPEND - 6, .requires = "res_sorcery_config", }
static

Definition at line 772 of file res_pjproject.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 772 of file res_pjproject.c.

◆ decor_orig

unsigned decor_orig
static

Definition at line 119 of file res_pjproject.c.

Referenced by load_module(), and unload_module().

◆ default_log_mappings

struct log_mappings* default_log_mappings
static

Definition at line 154 of file res_pjproject.c.

◆ log_cb_orig

pj_log_func* log_cb_orig
static

Definition at line 118 of file res_pjproject.c.

Referenced by load_module(), and unload_module().

◆ pjproject_cli

struct ast_cli_entry pjproject_cli[]
static

Definition at line 458 of file res_pjproject.c.

◆ pjproject_log_intercept

struct pjproject_log_intercept_data pjproject_log_intercept
static
Initial value:
= {
.thread = AST_PTHREADT_NULL,
.fd = -1,
}
#define AST_PTHREADT_NULL
Definition: lock.h:66

Definition at line 131 of file res_pjproject.c.

◆ pjproject_sorcery

struct ast_sorcery* pjproject_sorcery
static

Definition at line 117 of file res_pjproject.c.