Wed Jan 8 2020 09:49:48

Asterisk developer's documentation


logger.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@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 Asterisk Logger
22  *
23  * Logging routines
24  *
25  * \author Mark Spencer <markster@digium.com>
26  */
27 
28 /*** MODULEINFO
29  <support_level>core</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413586 $")
35 
36 /* When we include logger.h again it will trample on some stuff in syslog.h, but
37  * nothing we care about in here. */
38 #include <syslog.h>
39 
40 #include "asterisk/_private.h"
41 #include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
42 #include "asterisk/logger.h"
43 #include "asterisk/lock.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/config.h"
46 #include "asterisk/term.h"
47 #include "asterisk/cli.h"
48 #include "asterisk/utils.h"
49 #include "asterisk/manager.h"
50 #include "asterisk/threadstorage.h"
51 #include "asterisk/strings.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/app.h"
54 #include "asterisk/syslog.h"
55 
56 #include <signal.h>
57 #include <time.h>
58 #include <sys/stat.h>
59 #include <fcntl.h>
60 #ifdef HAVE_BKTR
61 #include <execinfo.h>
62 #define MAX_BACKTRACE_FRAMES 20
63 # if defined(HAVE_DLADDR) && defined(HAVE_BFD) && defined(BETTER_BACKTRACES)
64 # include <dlfcn.h>
65 # include <bfd.h>
66 # endif
67 #endif
68 
69 static char dateformat[256] = "%b %e %T"; /* Original Asterisk Format */
70 
71 static char queue_log_name[256] = QUEUELOG;
72 static char exec_after_rotate[256] = "";
73 
75 static unsigned int global_logmask = 0xFFFF;
76 static int queuelog_init;
77 static int logger_initialized;
78 
79 static enum rotatestrategy {
80  SEQUENTIAL = 1 << 0, /* Original method - create a new file, in order */
81  ROTATE = 1 << 1, /* Rotate all files, such that the oldest file has the highest suffix */
82  TIMESTAMP = 1 << 2, /* Append the epoch timestamp onto the end of the archived file */
84 
85 static struct {
86  unsigned int queue_log:1;
87  unsigned int queue_log_to_file:1;
88  unsigned int queue_adaptive_realtime:1;
89 } logfiles = { 1 };
90 
91 static char hostname[MAXHOSTNAMELEN];
92 
93 enum logtypes {
97 };
98 
99 struct logchannel {
100  /*! What to log to this channel */
101  unsigned int logmask;
102  /*! If this channel is disabled or not */
103  int disabled;
104  /*! syslog facility */
105  int facility;
106  /*! Type of log channel */
108  /*! logfile logging file pointer */
109  FILE *fileptr;
110  /*! Filename */
111  char filename[PATH_MAX];
112  /*! field for linking to list */
114  /*! Line number from configuration file */
115  int lineno;
116  /*! Components (levels) from last config load */
117  char components[0];
118 };
119 
121 
125 };
126 
127 struct logmsg {
129  int level;
130  int line;
133  AST_STRING_FIELD(date);
134  AST_STRING_FIELD(file);
135  AST_STRING_FIELD(function);
137  AST_STRING_FIELD(level_name);
138  );
140 };
141 
143 static pthread_t logthread = AST_PTHREADT_NULL;
145 static int close_logger_thread = 0;
146 
147 static FILE *qlog;
148 
149 /*! \brief Logging channels used in the Asterisk logging system
150  *
151  * The first 16 levels are reserved for system usage, and the remaining
152  * levels are reserved for usage by dynamic levels registered via
153  * ast_logger_register_level.
154  */
155 
156 /* Modifications to this array are protected by the rwlock in the
157  * logchannels list.
158  */
159 
160 static char *levels[NUMLOGLEVELS] = {
161  "DEBUG",
162  "---EVENT---", /* no longer used */
163  "NOTICE",
164  "WARNING",
165  "ERROR",
166  "VERBOSE",
167  "DTMF",
168 };
169 
170 /*! \brief Colors used in the console for logging */
171 static const int colors[NUMLOGLEVELS] = {
173  COLOR_BRBLUE, /* no longer used */
174  COLOR_YELLOW,
175  COLOR_BRRED,
176  COLOR_RED,
177  COLOR_GREEN,
179  0,
180  0,
181  0,
182  0,
183  0,
184  0,
185  0,
186  0,
187  0,
188  COLOR_BRBLUE,
189  COLOR_BRBLUE,
190  COLOR_BRBLUE,
191  COLOR_BRBLUE,
192  COLOR_BRBLUE,
193  COLOR_BRBLUE,
194  COLOR_BRBLUE,
195  COLOR_BRBLUE,
196  COLOR_BRBLUE,
197  COLOR_BRBLUE,
198  COLOR_BRBLUE,
199  COLOR_BRBLUE,
200  COLOR_BRBLUE,
201  COLOR_BRBLUE,
202  COLOR_BRBLUE,
203  COLOR_BRBLUE,
204 };
205 
207 #define VERBOSE_BUF_INIT_SIZE 256
208 
210 #define LOG_BUF_INIT_SIZE 256
211 
212 static void logger_queue_init(void);
213 
214 static unsigned int make_components(const char *s, int lineno)
215 {
216  char *w;
217  unsigned int res = 0;
218  char *stringp = ast_strdupa(s);
219  unsigned int x;
220 
221  while ((w = strsep(&stringp, ","))) {
222  w = ast_skip_blanks(w);
223 
224  if (!strcmp(w, "*")) {
225  res = 0xFFFFFFFF;
226  break;
227  } else for (x = 0; x < ARRAY_LEN(levels); x++) {
228  if (levels[x] && !strcasecmp(w, levels[x])) {
229  res |= (1 << x);
230  break;
231  }
232  }
233  }
234 
235  return res;
236 }
237 
238 static struct logchannel *make_logchannel(const char *channel, const char *components, int lineno)
239 {
240  struct logchannel *chan;
241  char *facility;
242 
243  if (ast_strlen_zero(channel) || !(chan = ast_calloc(1, sizeof(*chan) + strlen(components) + 1)))
244  return NULL;
245 
246  strcpy(chan->components, components);
247  chan->lineno = lineno;
248 
249  if (!strcasecmp(channel, "console")) {
250  chan->type = LOGTYPE_CONSOLE;
251  } else if (!strncasecmp(channel, "syslog", 6)) {
252  /*
253  * syntax is:
254  * syslog.facility => level,level,level
255  */
256  facility = strchr(channel, '.');
257  if (!facility++ || !facility) {
258  facility = "local0";
259  }
260 
261  chan->facility = ast_syslog_facility(facility);
262 
263  if (chan->facility < 0) {
264  fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
265  ast_free(chan);
266  return NULL;
267  }
268 
269  chan->type = LOGTYPE_SYSLOG;
270  ast_copy_string(chan->filename, channel, sizeof(chan->filename));
271  openlog("asterisk", LOG_PID, chan->facility);
272  } else {
273  const char *log_dir_prefix = "";
274  const char *log_dir_separator = "";
275 
276  if (channel[0] != '/') {
277  log_dir_prefix = ast_config_AST_LOG_DIR;
278  log_dir_separator = "/";
279  }
280 
281  if (!ast_strlen_zero(hostname)) {
282  snprintf(chan->filename, sizeof(chan->filename), "%s%s%s.%s",
283  log_dir_prefix, log_dir_separator, channel, hostname);
284  } else {
285  snprintf(chan->filename, sizeof(chan->filename), "%s%s%s",
286  log_dir_prefix, log_dir_separator, channel);
287  }
288 
289  if (!(chan->fileptr = fopen(chan->filename, "a"))) {
290  /* Can't do real logging here since we're called with a lock
291  * so log to any attached consoles */
292  ast_console_puts_mutable("ERROR: Unable to open log file '", __LOG_ERROR);
297  ast_free(chan);
298  return NULL;
299  }
300  chan->type = LOGTYPE_FILE;
301  }
302  chan->logmask = make_components(chan->components, lineno);
303 
304  return chan;
305 }
306 
307 static void init_logger_chain(int locked)
308 {
309  struct logchannel *chan;
310  struct ast_config *cfg;
311  struct ast_variable *var;
312  const char *s;
313  struct ast_flags config_flags = { 0 };
314 
315  if (!(cfg = ast_config_load2("logger.conf", "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
316  return;
317  }
318 
319  /* delete our list of log channels */
320  if (!locked) {
322  }
323  while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list))) {
324  ast_free(chan);
325  }
326  global_logmask = 0;
327  if (!locked) {
329  }
330 
331  errno = 0;
332  /* close syslog */
333  closelog();
334 
335  /* If no config file, we're fine, set default options. */
336  if (!cfg) {
337  if (errno) {
338  fprintf(stderr, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno));
339  } else {
340  fprintf(stderr, "Errors detected in logger.conf: see above; default settings will be used.\n");
341  }
342  if (!(chan = ast_calloc(1, sizeof(*chan)))) {
343  return;
344  }
345  chan->type = LOGTYPE_CONSOLE;
347  if (!locked) {
349  }
351  global_logmask |= chan->logmask;
352  if (!locked) {
354  }
355  return;
356  }
357 
358  if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
359  if (ast_true(s)) {
360  if (gethostname(hostname, sizeof(hostname) - 1)) {
361  ast_copy_string(hostname, "unknown", sizeof(hostname));
362  fprintf(stderr, "What box has no hostname???\n");
363  }
364  } else
365  hostname[0] = '\0';
366  } else
367  hostname[0] = '\0';
368  if ((s = ast_variable_retrieve(cfg, "general", "dateformat")))
369  ast_copy_string(dateformat, s, sizeof(dateformat));
370  else
371  ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
372  if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) {
373  logfiles.queue_log = ast_true(s);
374  }
375  if ((s = ast_variable_retrieve(cfg, "general", "queue_log_to_file"))) {
376  logfiles.queue_log_to_file = ast_true(s);
377  }
378  if ((s = ast_variable_retrieve(cfg, "general", "queue_log_name"))) {
379  ast_copy_string(queue_log_name, s, sizeof(queue_log_name));
380  }
381  if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate"))) {
382  ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate));
383  }
384  if ((s = ast_variable_retrieve(cfg, "general", "rotatestrategy"))) {
385  if (strcasecmp(s, "timestamp") == 0) {
387  } else if (strcasecmp(s, "rotate") == 0) {
389  } else if (strcasecmp(s, "sequential") == 0) {
391  } else {
392  fprintf(stderr, "Unknown rotatestrategy: %s\n", s);
393  }
394  } else {
395  if ((s = ast_variable_retrieve(cfg, "general", "rotatetimestamp"))) {
397  fprintf(stderr, "rotatetimestamp option has been deprecated. Please use rotatestrategy instead.\n");
398  }
399  }
400 
401  if (!locked) {
403  }
404  var = ast_variable_browse(cfg, "logfiles");
405  for (; var; var = var->next) {
406  if (!(chan = make_logchannel(var->name, var->value, var->lineno))) {
407  /* Print error message directly to the consoles since the lock is held
408  * and we don't want to unlock with the list partially built */
409  ast_console_puts_mutable("ERROR: Unable to create log channel '", __LOG_ERROR);
412  continue;
413  }
415  global_logmask |= chan->logmask;
416  }
417 
418  if (qlog) {
419  fclose(qlog);
420  qlog = NULL;
421  }
422 
423  if (!locked) {
425  }
426 
427  ast_config_destroy(cfg);
428 }
429 
430 void ast_child_verbose(int level, const char *fmt, ...)
431 {
432  char *msg = NULL, *emsg = NULL, *sptr, *eptr;
433  va_list ap, aq;
434  int size;
435 
436  /* Don't bother, if the level isn't that high */
437  if (option_verbose < level) {
438  return;
439  }
440 
441  va_start(ap, fmt);
442  va_copy(aq, ap);
443  if ((size = vsnprintf(msg, 0, fmt, ap)) < 0) {
444  va_end(ap);
445  va_end(aq);
446  return;
447  }
448  va_end(ap);
449 
450  if (!(msg = ast_malloc(size + 1))) {
451  va_end(aq);
452  return;
453  }
454 
455  vsnprintf(msg, size + 1, fmt, aq);
456  va_end(aq);
457 
458  if (!(emsg = ast_malloc(size * 2 + 1))) {
459  ast_free(msg);
460  return;
461  }
462 
463  for (sptr = msg, eptr = emsg; ; sptr++) {
464  if (*sptr == '"') {
465  *eptr++ = '\\';
466  }
467  *eptr++ = *sptr;
468  if (*sptr == '\0') {
469  break;
470  }
471  }
472  ast_free(msg);
473 
474  fprintf(stdout, "verbose \"%s\" %d\n", emsg, level);
475  fflush(stdout);
476  ast_free(emsg);
477 }
478 
479 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
480 {
481  va_list ap;
482  struct timeval tv;
483  struct ast_tm tm;
484  char qlog_msg[8192];
485  int qlog_len;
486  char time_str[30];
487 
488  if (!logger_initialized) {
489  /* You are too early. We are not open yet! */
490  return;
491  }
492  if (!queuelog_init) {
494  if (!queuelog_init) {
495  /*
496  * We have delayed initializing the queue logging system so
497  * preloaded realtime modules can get up. We must initialize
498  * now since someone is trying to log something.
499  */
501  queuelog_init = 1;
503  ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
504  } else {
506  }
507  }
508 
509  if (ast_check_realtime("queue_log")) {
510  tv = ast_tvnow();
511  ast_localtime(&tv, &tm, NULL);
512  ast_strftime(time_str, sizeof(time_str), "%F %T.%6q", &tm);
513  va_start(ap, fmt);
514  vsnprintf(qlog_msg, sizeof(qlog_msg), fmt, ap);
515  va_end(ap);
516  if (logfiles.queue_adaptive_realtime) {
518  AST_APP_ARG(data)[5];
519  );
520  AST_NONSTANDARD_APP_ARGS(args, qlog_msg, '|');
521  /* Ensure fields are large enough to receive data */
522  ast_realtime_require_field("queue_log",
523  "data1", RQ_CHAR, strlen(S_OR(args.data[0], "")),
524  "data2", RQ_CHAR, strlen(S_OR(args.data[1], "")),
525  "data3", RQ_CHAR, strlen(S_OR(args.data[2], "")),
526  "data4", RQ_CHAR, strlen(S_OR(args.data[3], "")),
527  "data5", RQ_CHAR, strlen(S_OR(args.data[4], "")),
528  SENTINEL);
529 
530  /* Store the log */
531  ast_store_realtime("queue_log", "time", time_str,
532  "callid", callid,
533  "queuename", queuename,
534  "agent", agent,
535  "event", event,
536  "data1", S_OR(args.data[0], ""),
537  "data2", S_OR(args.data[1], ""),
538  "data3", S_OR(args.data[2], ""),
539  "data4", S_OR(args.data[3], ""),
540  "data5", S_OR(args.data[4], ""),
541  SENTINEL);
542  } else {
543  ast_store_realtime("queue_log", "time", time_str,
544  "callid", callid,
545  "queuename", queuename,
546  "agent", agent,
547  "event", event,
548  "data", qlog_msg,
549  SENTINEL);
550  }
551 
552  if (!logfiles.queue_log_to_file) {
553  return;
554  }
555  }
556 
557  if (qlog) {
558  va_start(ap, fmt);
559  qlog_len = snprintf(qlog_msg, sizeof(qlog_msg), "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
560  vsnprintf(qlog_msg + qlog_len, sizeof(qlog_msg) - qlog_len, fmt, ap);
561  va_end(ap);
563  if (qlog) {
564  fprintf(qlog, "%s\n", qlog_msg);
565  fflush(qlog);
566  }
568  }
569 }
570 
571 static int rotate_file(const char *filename)
572 {
573  char old[PATH_MAX];
574  char new[PATH_MAX];
575  int x, y, which, found, res = 0, fd;
576  char *suffixes[4] = { "", ".gz", ".bz2", ".Z" };
577 
578  switch (rotatestrategy) {
579  case SEQUENTIAL:
580  for (x = 0; ; x++) {
581  snprintf(new, sizeof(new), "%s.%d", filename, x);
582  fd = open(new, O_RDONLY);
583  if (fd > -1)
584  close(fd);
585  else
586  break;
587  }
588  if (rename(filename, new)) {
589  fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
590  res = -1;
591  } else {
592  filename = new;
593  }
594  break;
595  case TIMESTAMP:
596  snprintf(new, sizeof(new), "%s.%ld", filename, (long)time(NULL));
597  if (rename(filename, new)) {
598  fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
599  res = -1;
600  } else {
601  filename = new;
602  }
603  break;
604  case ROTATE:
605  /* Find the next empty slot, including a possible suffix */
606  for (x = 0; ; x++) {
607  found = 0;
608  for (which = 0; which < ARRAY_LEN(suffixes); which++) {
609  snprintf(new, sizeof(new), "%s.%d%s", filename, x, suffixes[which]);
610  fd = open(new, O_RDONLY);
611  if (fd > -1) {
612  close(fd);
613  found = 1;
614  break;
615  }
616  }
617  if (!found) {
618  break;
619  }
620  }
621 
622  /* Found an empty slot */
623  for (y = x; y > 0; y--) {
624  for (which = 0; which < ARRAY_LEN(suffixes); which++) {
625  snprintf(old, sizeof(old), "%s.%d%s", filename, y - 1, suffixes[which]);
626  fd = open(old, O_RDONLY);
627  if (fd > -1) {
628  /* Found the right suffix */
629  close(fd);
630  snprintf(new, sizeof(new), "%s.%d%s", filename, y, suffixes[which]);
631  if (rename(old, new)) {
632  fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
633  res = -1;
634  }
635  break;
636  }
637  }
638  }
639 
640  /* Finally, rename the current file */
641  snprintf(new, sizeof(new), "%s.0", filename);
642  if (rename(filename, new)) {
643  fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
644  res = -1;
645  } else {
646  filename = new;
647  }
648  }
649 
650  if (!ast_strlen_zero(exec_after_rotate)) {
651  struct ast_channel *c = ast_dummy_channel_alloc();
652  char buf[512];
653 
654  pbx_builtin_setvar_helper(c, "filename", filename);
655  pbx_substitute_variables_helper(c, exec_after_rotate, buf, sizeof(buf));
656  if (c) {
657  c = ast_channel_unref(c);
658  }
659  if (ast_safe_system(buf) == -1) {
660  ast_log(LOG_WARNING, "error executing '%s'\n", buf);
661  }
662  }
663  return res;
664 }
665 
666 /*!
667  * \internal
668  * \brief Start the realtime queue logging if configured.
669  *
670  * \retval TRUE if not to open queue log file.
671  */
672 static int logger_queue_rt_start(void)
673 {
674  if (ast_check_realtime("queue_log")) {
675  if (!ast_realtime_require_field("queue_log",
676  "time", RQ_DATETIME, 26,
677  "data1", RQ_CHAR, 20,
678  "data2", RQ_CHAR, 20,
679  "data3", RQ_CHAR, 20,
680  "data4", RQ_CHAR, 20,
681  "data5", RQ_CHAR, 20,
682  SENTINEL)) {
683  logfiles.queue_adaptive_realtime = 1;
684  } else {
685  logfiles.queue_adaptive_realtime = 0;
686  }
687 
688  if (!logfiles.queue_log_to_file) {
689  /* Don't open the log file. */
690  return 1;
691  }
692  }
693  return 0;
694 }
695 
696 /*!
697  * \internal
698  * \brief Rotate the queue log file and restart.
699  *
700  * \param queue_rotate Log queue rotation mode.
701  *
702  * \note Assumes logchannels is write locked on entry.
703  *
704  * \retval 0 on success.
705  * \retval -1 on error.
706  */
707 static int logger_queue_restart(int queue_rotate)
708 {
709  int res = 0;
710  char qfname[PATH_MAX];
711 
712  if (logger_queue_rt_start()) {
713  return res;
714  }
715 
716  snprintf(qfname, sizeof(qfname), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
717  if (qlog) {
718  /* Just in case it was still open. */
719  fclose(qlog);
720  qlog = NULL;
721  }
722  if (queue_rotate) {
723  rotate_file(qfname);
724  }
725 
726  /* Open the log file. */
727  qlog = fopen(qfname, "a");
728  if (!qlog) {
729  ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
730  res = -1;
731  }
732  return res;
733 }
734 
735 static int reload_logger(int rotate)
736 {
737  int queue_rotate = rotate;
738  struct logchannel *f;
739  int res = 0;
740 
742 
743  if (qlog) {
744  if (rotate < 0) {
745  /* Check filesize - this one typically doesn't need an auto-rotate */
746  if (ftello(qlog) > 0x40000000) { /* Arbitrarily, 1 GB */
747  fclose(qlog);
748  qlog = NULL;
749  } else {
750  queue_rotate = 0;
751  }
752  } else {
753  fclose(qlog);
754  qlog = NULL;
755  }
756  } else {
757  queue_rotate = 0;
758  }
759 
761 
763  if (f->disabled) {
764  f->disabled = 0; /* Re-enable logging at reload */
765  manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
766  }
767  if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
768  int rotate_this = 0;
769  if (ftello(f->fileptr) > 0x40000000) { /* Arbitrarily, 1 GB */
770  /* Be more proactive about rotating massive log files */
771  rotate_this = 1;
772  }
773  fclose(f->fileptr); /* Close file */
774  f->fileptr = NULL;
775  if (rotate || rotate_this) {
776  rotate_file(f->filename);
777  }
778  }
779  }
780 
781  filesize_reload_needed = 0;
782 
783  init_logger_chain(1 /* locked */);
784 
785  ast_unload_realtime("queue_log");
786  if (logfiles.queue_log) {
787  res = logger_queue_restart(queue_rotate);
789  ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
790  ast_verb(1, "Asterisk Queue Logger restarted\n");
791  } else {
793  }
794 
795  return res;
796 }
797 
798 /*! \brief Reload the logger module without rotating log files (also used from loader.c during
799  a full Asterisk reload) */
800 int logger_reload(void)
801 {
802  if (reload_logger(0)) {
803  return RESULT_FAILURE;
804  }
805  return RESULT_SUCCESS;
806 }
807 
808 static char *handle_logger_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
809 {
810  switch (cmd) {
811  case CLI_INIT:
812  e->command = "logger reload";
813  e->usage =
814  "Usage: logger reload\n"
815  " Reloads the logger subsystem state. Use after restarting syslogd(8) if you are using syslog logging.\n";
816  return NULL;
817  case CLI_GENERATE:
818  return NULL;
819  }
820  if (reload_logger(0)) {
821  ast_cli(a->fd, "Failed to reload the logger\n");
822  return CLI_FAILURE;
823  }
824  return CLI_SUCCESS;
825 }
826 
827 static char *handle_logger_rotate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
828 {
829  switch (cmd) {
830  case CLI_INIT:
831  e->command = "logger rotate";
832  e->usage =
833  "Usage: logger rotate\n"
834  " Rotates and Reopens the log files.\n";
835  return NULL;
836  case CLI_GENERATE:
837  return NULL;
838  }
839  if (reload_logger(1)) {
840  ast_cli(a->fd, "Failed to reload the logger and rotate log files\n");
841  return CLI_FAILURE;
842  }
843  return CLI_SUCCESS;
844 }
845 
846 static char *handle_logger_set_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
847 {
848  int x;
849  int state;
850  int level = -1;
851 
852  switch (cmd) {
853  case CLI_INIT:
854  e->command = "logger set level {DEBUG|NOTICE|WARNING|ERROR|VERBOSE|DTMF} {on|off}";
855  e->usage =
856  "Usage: logger set level {DEBUG|NOTICE|WARNING|ERROR|VERBOSE|DTMF} {on|off}\n"
857  " Set a specific log level to enabled/disabled for this console.\n";
858  return NULL;
859  case CLI_GENERATE:
860  return NULL;
861  }
862 
863  if (a->argc < 5)
864  return CLI_SHOWUSAGE;
865 
867 
868  for (x = 0; x < ARRAY_LEN(levels); x++) {
869  if (levels[x] && !strcasecmp(a->argv[3], levels[x])) {
870  level = x;
871  break;
872  }
873  }
874 
876 
877  state = ast_true(a->argv[4]) ? 1 : 0;
878 
879  if (level != -1) {
880  ast_console_toggle_loglevel(a->fd, level, state);
881  ast_cli(a->fd, "Logger status for '%s' has been set to '%s'.\n", levels[level], state ? "on" : "off");
882  } else
883  return CLI_SHOWUSAGE;
884 
885  return CLI_SUCCESS;
886 }
887 
888 /*! \brief CLI command to show logging system configuration */
889 static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
890 {
891 #define FORMATL "%-35.35s %-8.8s %-9.9s "
892  struct logchannel *chan;
893  switch (cmd) {
894  case CLI_INIT:
895  e->command = "logger show channels";
896  e->usage =
897  "Usage: logger show channels\n"
898  " List configured logger channels.\n";
899  return NULL;
900  case CLI_GENERATE:
901  return NULL;
902  }
903  ast_cli(a->fd, FORMATL, "Channel", "Type", "Status");
904  ast_cli(a->fd, "Configuration\n");
905  ast_cli(a->fd, FORMATL, "-------", "----", "------");
906  ast_cli(a->fd, "-------------\n");
909  unsigned int level;
910 
911  ast_cli(a->fd, FORMATL, chan->filename, chan->type == LOGTYPE_CONSOLE ? "Console" : (chan->type == LOGTYPE_SYSLOG ? "Syslog" : "File"),
912  chan->disabled ? "Disabled" : "Enabled");
913  ast_cli(a->fd, " - ");
914  for (level = 0; level < ARRAY_LEN(levels); level++) {
915  if ((chan->logmask & (1 << level)) && levels[level]) {
916  ast_cli(a->fd, "%s ", levels[level]);
917  }
918  }
919  ast_cli(a->fd, "\n");
920  }
922  ast_cli(a->fd, "\n");
923 
924  return CLI_SUCCESS;
925 }
926 
927 struct verb {
928  void (*verboser)(const char *string);
930 };
931 
933 
934 static struct ast_cli_entry cli_logger[] = {
935  AST_CLI_DEFINE(handle_logger_show_channels, "List configured log channels"),
936  AST_CLI_DEFINE(handle_logger_reload, "Reopens the log files"),
937  AST_CLI_DEFINE(handle_logger_rotate, "Rotates and reopens the log files"),
938  AST_CLI_DEFINE(handle_logger_set_level, "Enables/Disables a specific logging level for this console"),
939 };
940 
941 static void _handle_SIGXFSZ(int sig)
942 {
943  /* Indicate need to reload */
944  filesize_reload_needed = 1;
945 }
946 
947 static struct sigaction handle_SIGXFSZ = {
948  .sa_handler = _handle_SIGXFSZ,
949  .sa_flags = SA_RESTART,
950 };
951 
952 static void ast_log_vsyslog(struct logmsg *msg)
953 {
954  char buf[BUFSIZ];
955  int syslog_level = ast_syslog_priority_from_loglevel(msg->level);
956 
957  if (syslog_level < 0) {
958  /* we are locked here, so cannot ast_log() */
959  fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", msg->level);
960  return;
961  }
962 
963  snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: %s",
964  levels[msg->level], msg->process_id, msg->file, msg->line, msg->function, msg->message);
965 
966  term_strip(buf, buf, strlen(buf) + 1);
967  syslog(syslog_level, "%s", buf);
968 }
969 
970 /*! \brief Print a normal log message to the channels */
971 static void logger_print_normal(struct logmsg *logmsg)
972 {
973  struct logchannel *chan = NULL;
974  char buf[BUFSIZ];
975  struct verb *v = NULL;
976 
977  if (logmsg->level == __LOG_VERBOSE) {
978  char *tmpmsg = ast_strdupa(logmsg->message + 1);
979  /* Iterate through the list of verbosers and pass them the log message string */
982  v->verboser(logmsg->message);
984  ast_string_field_set(logmsg, message, tmpmsg);
985  }
986 
988 
989  if (!AST_RWLIST_EMPTY(&logchannels)) {
991  /* If the channel is disabled, then move on to the next one */
992  if (chan->disabled)
993  continue;
994  /* Check syslog channels */
995  if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << logmsg->level))) {
996  ast_log_vsyslog(logmsg);
997  /* Console channels */
998  } else if (chan->type == LOGTYPE_CONSOLE && (chan->logmask & (1 << logmsg->level))) {
999  char linestr[128];
1000  char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
1001 
1002  /* If the level is verbose, then skip it */
1003  if (logmsg->level == __LOG_VERBOSE)
1004  continue;
1005 
1006  /* Turn the numerical line number into a string */
1007  snprintf(linestr, sizeof(linestr), "%d", logmsg->line);
1008  /* Build string to print out */
1009  snprintf(buf, sizeof(buf), "[%s] %s[%ld]: %s:%s %s: %s",
1010  logmsg->date,
1011  term_color(tmp1, logmsg->level_name, colors[logmsg->level], 0, sizeof(tmp1)),
1012  logmsg->process_id,
1013  term_color(tmp2, logmsg->file, COLOR_BRWHITE, 0, sizeof(tmp2)),
1014  term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
1015  term_color(tmp4, logmsg->function, COLOR_BRWHITE, 0, sizeof(tmp4)),
1016  logmsg->message);
1017  /* Print out */
1018  ast_console_puts_mutable(buf, logmsg->level);
1019  /* File channels */
1020  } else if (chan->type == LOGTYPE_FILE && (chan->logmask & (1 << logmsg->level))) {
1021  int res = 0;
1022 
1023  /* If no file pointer exists, skip it */
1024  if (!chan->fileptr) {
1025  continue;
1026  }
1027 
1028  /* Print out to the file */
1029  res = fprintf(chan->fileptr, "[%s] %s[%ld] %s: %s",
1030  logmsg->date, logmsg->level_name, logmsg->process_id, logmsg->file, term_strip(buf, logmsg->message, BUFSIZ));
1031  if (res <= 0 && !ast_strlen_zero(logmsg->message)) {
1032  fprintf(stderr, "**** Asterisk Logging Error: ***********\n");
1033  if (errno == ENOMEM || errno == ENOSPC)
1034  fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
1035  else
1036  fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
1037  manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
1038  chan->disabled = 1;
1039  } else if (res > 0) {
1040  fflush(chan->fileptr);
1041  }
1042  }
1043  }
1044  } else if (logmsg->level != __LOG_VERBOSE) {
1045  fputs(logmsg->message, stdout);
1046  }
1047 
1049 
1050  /* If we need to reload because of the file size, then do so */
1051  if (filesize_reload_needed) {
1052  reload_logger(-1);
1053  ast_verb(1, "Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
1054  }
1055 
1056  return;
1057 }
1058 
1059 /*! \brief Actual logging thread */
1060 static void *logger_thread(void *data)
1061 {
1062  struct logmsg *next = NULL, *msg = NULL;
1063 
1064  for (;;) {
1065  /* We lock the message list, and see if any message exists... if not we wait on the condition to be signalled */
1067  if (AST_LIST_EMPTY(&logmsgs)) {
1068  if (close_logger_thread) {
1070  break;
1071  } else {
1073  }
1074  }
1075  next = AST_LIST_FIRST(&logmsgs);
1078 
1079  /* Otherwise go through and process each message in the order added */
1080  while ((msg = next)) {
1081  /* Get the next entry now so that we can free our current structure later */
1082  next = AST_LIST_NEXT(msg, list);
1083 
1084  /* Depending on the type, send it to the proper function */
1085  logger_print_normal(msg);
1086 
1087  /* Free the data since we are done */
1088  ast_free(msg);
1089  }
1090  }
1091 
1092  return NULL;
1093 }
1094 
1095 /*!
1096  * \internal
1097  * \brief Initialize the logger queue.
1098  *
1099  * \note Assumes logchannels is write locked on entry.
1100  *
1101  * \return Nothing
1102  */
1103 static void logger_queue_init(void)
1104 {
1105  ast_unload_realtime("queue_log");
1106  if (logfiles.queue_log) {
1107  char qfname[PATH_MAX];
1108 
1109  if (logger_queue_rt_start()) {
1110  return;
1111  }
1112 
1113  /* Open the log file. */
1114  snprintf(qfname, sizeof(qfname), "%s/%s", ast_config_AST_LOG_DIR,
1115  queue_log_name);
1116  if (qlog) {
1117  /* Just in case it was already open. */
1118  fclose(qlog);
1119  }
1120  qlog = fopen(qfname, "a");
1121  if (!qlog) {
1122  ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
1123  }
1124  }
1125 }
1126 
1127 int init_logger(void)
1128 {
1129  /* auto rotate if sig SIGXFSZ comes a-knockin */
1130  sigaction(SIGXFSZ, &handle_SIGXFSZ, NULL);
1131 
1132  /* Re-initialize the logmsgs mutex. The recursive mutex can be accessed prior
1133  * to Asterisk being forked into the background, which can cause the thread
1134  * ID tracked by the underlying pthread mutex to be different than the ID of
1135  * the thread that unlocks the mutex. Since init_logger is called after the
1136  * fork, it is safe to initialize the mutex here for future accesses.
1137  */
1140  ast_cond_init(&logcond, NULL);
1141 
1142  /* start logger thread */
1143  if (ast_pthread_create(&logthread, NULL, logger_thread, NULL) < 0) {
1145  return -1;
1146  }
1147 
1148  /* register the logger cli commands */
1150 
1152 
1153  /* create log channels */
1154  init_logger_chain(0 /* locked */);
1155  logger_initialized = 1;
1156 
1157  return 0;
1158 }
1159 
1160 void close_logger(void)
1161 {
1162  struct logchannel *f = NULL;
1163  struct verb *cur = NULL;
1164 
1166 
1167  logger_initialized = 0;
1168 
1169  /* Stop logger thread */
1171  close_logger_thread = 1;
1174 
1176  pthread_join(logthread, NULL);
1177 
1179  while ((cur = AST_LIST_REMOVE_HEAD(&verbosers, list))) {
1180  ast_free(cur);
1181  }
1183 
1185 
1186  if (qlog) {
1187  fclose(qlog);
1188  qlog = NULL;
1189  }
1190 
1191  while ((f = AST_LIST_REMOVE_HEAD(&logchannels, list))) {
1192  if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
1193  fclose(f->fileptr);
1194  f->fileptr = NULL;
1195  }
1196  ast_free(f);
1197  }
1198 
1199  closelog(); /* syslog */
1200 
1202 }
1203 
1204 /*!
1205  * \brief send log messages to syslog and/or the console
1206  */
1207 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
1208 {
1209  struct logmsg *logmsg = NULL;
1210  struct ast_str *buf = NULL;
1211  struct ast_tm tm;
1212  struct timeval now = ast_tvnow();
1213  int res = 0;
1214  va_list ap;
1215  char datestring[256];
1216 
1218  return;
1219 
1220  if (level != __LOG_VERBOSE && AST_RWLIST_EMPTY(&logchannels)) {
1221  /*
1222  * we don't have the logger chain configured yet,
1223  * so just log to stdout
1224  */
1225  int result;
1226  va_start(ap, fmt);
1227  result = ast_str_set_va(&buf, BUFSIZ, fmt, ap); /* XXX BUFSIZ ? */
1228  va_end(ap);
1229  if (result != AST_DYNSTR_BUILD_FAILED) {
1231  fputs(ast_str_buffer(buf), stdout);
1232  }
1233  return;
1234  }
1235 
1236  /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug
1237  are non-zero; LOG_DEBUG messages can still be displayed if option_debug
1238  is zero, if option_verbose is non-zero (this allows for 'level zero'
1239  LOG_DEBUG messages to be displayed, if the logmask on any channel
1240  allows it)
1241  */
1242  if (!option_verbose && !option_debug && (level == __LOG_DEBUG))
1243  return;
1244 
1245  /* Ignore anything that never gets logged anywhere */
1246  if (level != __LOG_VERBOSE && !(global_logmask & (1 << level)))
1247  return;
1248 
1249  /* Build string */
1250  va_start(ap, fmt);
1251  res = ast_str_set_va(&buf, BUFSIZ, fmt, ap);
1252  va_end(ap);
1253 
1254  /* If the build failed, then abort and free this structure */
1255  if (res == AST_DYNSTR_BUILD_FAILED)
1256  return;
1257 
1258  /* Create a new logging message */
1259  if (!(logmsg = ast_calloc_with_stringfields(1, struct logmsg, res + 128)))
1260  return;
1261 
1262  /* Copy string over */
1264 
1265  /* Set type */
1266  if (level == __LOG_VERBOSE) {
1267  logmsg->type = LOGMSG_VERBOSE;
1268  } else {
1269  logmsg->type = LOGMSG_NORMAL;
1270  }
1271 
1272  /* Create our date/time */
1273  ast_localtime(&now, &tm, NULL);
1274  ast_strftime(datestring, sizeof(datestring), dateformat, &tm);
1275  ast_string_field_set(logmsg, date, datestring);
1276 
1277  /* Copy over data */
1278  logmsg->level = level;
1279  logmsg->line = line;
1280  ast_string_field_set(logmsg, level_name, levels[level]);
1281  ast_string_field_set(logmsg, file, file);
1282  ast_string_field_set(logmsg, function, function);
1283  logmsg->process_id = (long) ast_get_tid();
1284 
1285  /* If the logger thread is active, append it to the tail end of the list - otherwise skip that step */
1286  if (logthread != AST_PTHREADT_NULL) {
1288  if (close_logger_thread) {
1289  /* Logger is either closing or closed. We cannot log this message. */
1290  ast_free(logmsg);
1291  } else {
1292  AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
1294  }
1296  } else {
1297  logger_print_normal(logmsg);
1298  ast_free(logmsg);
1299  }
1300 }
1301 
1302 #ifdef HAVE_BKTR
1303 
1304 struct ast_bt *ast_bt_create(void)
1305 {
1306  struct ast_bt *bt = ast_calloc(1, sizeof(*bt));
1307  if (!bt) {
1308  ast_log(LOG_ERROR, "Unable to allocate memory for backtrace structure!\n");
1309  return NULL;
1310  }
1311 
1312  bt->alloced = 1;
1313 
1315 
1316  return bt;
1317 }
1318 
1320 {
1321  bt->num_frames = backtrace(bt->addresses, AST_MAX_BT_FRAMES);
1322 
1323  return 0;
1324 }
1325 
1326 void *ast_bt_destroy(struct ast_bt *bt)
1327 {
1328  if (bt->alloced) {
1329  ast_free(bt);
1330  }
1331 
1332  return NULL;
1333 }
1334 
1336 {
1337  char **strings;
1338 #if defined(BETTER_BACKTRACES)
1339  int stackfr;
1340  bfd *bfdobj; /* bfd.h */
1341  Dl_info dli; /* dlfcn.h */
1342  long allocsize;
1343  asymbol **syms = NULL; /* bfd.h */
1344  bfd_vma offset; /* bfd.h */
1345  const char *lastslash;
1346  asection *section;
1347  const char *file, *func;
1348  unsigned int line;
1349  char address_str[128];
1350  char msg[1024];
1351  size_t strings_size;
1352  size_t *eachlen;
1353 #endif
1354 
1355 #if defined(BETTER_BACKTRACES)
1356  strings_size = num_frames * sizeof(*strings);
1357 
1358  eachlen = ast_calloc(num_frames, sizeof(*eachlen));
1359  strings = ast_std_calloc(num_frames, sizeof(*strings));
1360  if (!eachlen || !strings) {
1361  ast_free(eachlen);
1362  ast_std_free(strings);
1363  return NULL;
1364  }
1365 
1366  for (stackfr = 0; stackfr < num_frames; stackfr++) {
1367  int found = 0, symbolcount;
1368 
1369  msg[0] = '\0';
1370 
1371  if (!dladdr(addresses[stackfr], &dli)) {
1372  continue;
1373  }
1374 
1375  if (strcmp(dli.dli_fname, "asterisk") == 0) {
1376  char asteriskpath[256];
1377 
1378  if (!(dli.dli_fname = ast_utils_which("asterisk", asteriskpath, sizeof(asteriskpath)))) {
1379  /* This will fail to find symbols */
1380  ast_log(LOG_DEBUG, "Failed to find asterisk binary for debug symbols.\n");
1381  dli.dli_fname = "asterisk";
1382  }
1383  }
1384 
1385  lastslash = strrchr(dli.dli_fname, '/');
1386  if ((bfdobj = bfd_openr(dli.dli_fname, NULL)) &&
1387  bfd_check_format(bfdobj, bfd_object) &&
1388  (allocsize = bfd_get_symtab_upper_bound(bfdobj)) > 0 &&
1389  (syms = ast_malloc(allocsize)) &&
1390  (symbolcount = bfd_canonicalize_symtab(bfdobj, syms))) {
1391 
1392  if (bfdobj->flags & DYNAMIC) {
1393  offset = addresses[stackfr] - dli.dli_fbase;
1394  } else {
1395  offset = addresses[stackfr] - (void *) 0;
1396  }
1397 
1398  for (section = bfdobj->sections; section; section = section->next) {
1399  if (!bfd_get_section_flags(bfdobj, section) & SEC_ALLOC ||
1400  section->vma > offset ||
1401  section->size + section->vma < offset) {
1402  continue;
1403  }
1404 
1405  if (!bfd_find_nearest_line(bfdobj, section, syms, offset - section->vma, &file, &func, &line)) {
1406  continue;
1407  }
1408 
1409  /* file can possibly be null even with a success result from bfd_find_nearest_line */
1410  file = file ? file : "";
1411 
1412  /* Stack trace output */
1413  found++;
1414  if ((lastslash = strrchr(file, '/'))) {
1415  const char *prevslash;
1416 
1417  for (prevslash = lastslash - 1; *prevslash != '/' && prevslash >= file; prevslash--) {
1418  }
1419  if (prevslash >= file) {
1420  lastslash = prevslash;
1421  }
1422  }
1423  if (dli.dli_saddr == NULL) {
1424  address_str[0] = '\0';
1425  } else {
1426  snprintf(address_str, sizeof(address_str), " (%p+%lX)",
1427  dli.dli_saddr,
1428  (unsigned long) (addresses[stackfr] - dli.dli_saddr));
1429  }
1430  snprintf(msg, sizeof(msg), "%s:%u %s()%s",
1431  lastslash ? lastslash + 1 : file, line,
1432  S_OR(func, "???"),
1433  address_str);
1434 
1435  break; /* out of section iteration */
1436  }
1437  }
1438  if (bfdobj) {
1439  bfd_close(bfdobj);
1440  ast_free(syms);
1441  }
1442 
1443  /* Default output, if we cannot find the information within BFD */
1444  if (!found) {
1445  if (dli.dli_saddr == NULL) {
1446  address_str[0] = '\0';
1447  } else {
1448  snprintf(address_str, sizeof(address_str), " (%p+%lX)",
1449  dli.dli_saddr,
1450  (unsigned long) (addresses[stackfr] - dli.dli_saddr));
1451  }
1452  snprintf(msg, sizeof(msg), "%s %s()%s",
1453  lastslash ? lastslash + 1 : dli.dli_fname,
1454  S_OR(dli.dli_sname, "<unknown>"),
1455  address_str);
1456  }
1457 
1458  if (!ast_strlen_zero(msg)) {
1459  char **tmp;
1460 
1461  eachlen[stackfr] = strlen(msg) + 1;
1462  if (!(tmp = ast_std_realloc(strings, strings_size + eachlen[stackfr]))) {
1463  ast_std_free(strings);
1464  strings = NULL;
1465  break; /* out of stack frame iteration */
1466  }
1467  strings = tmp;
1468  strings[stackfr] = (char *) strings + strings_size;
1469  strcpy(strings[stackfr], msg);/* Safe since we just allocated the room. */
1470  strings_size += eachlen[stackfr];
1471  }
1472  }
1473 
1474  if (strings) {
1475  /* Recalculate the offset pointers because of the reallocs. */
1476  strings[0] = (char *) strings + num_frames * sizeof(*strings);
1477  for (stackfr = 1; stackfr < num_frames; stackfr++) {
1478  strings[stackfr] = strings[stackfr - 1] + eachlen[stackfr - 1];
1479  }
1480  }
1481  ast_free(eachlen);
1482 
1483 #else /* !defined(BETTER_BACKTRACES) */
1484 
1485  strings = backtrace_symbols(addresses, num_frames);
1486 #endif /* defined(BETTER_BACKTRACES) */
1487  return strings;
1488 }
1489 
1490 #endif /* HAVE_BKTR */
1491 
1492 void ast_backtrace(void)
1493 {
1494 #ifdef HAVE_BKTR
1495  struct ast_bt *bt;
1496  int i = 0;
1497  char **strings;
1498 
1499  if (!(bt = ast_bt_create())) {
1500  ast_log(LOG_WARNING, "Unable to allocate space for backtrace structure\n");
1501  return;
1502  }
1503 
1504  if ((strings = ast_bt_get_symbols(bt->addresses, bt->num_frames))) {
1505  ast_debug(1, "Got %d backtrace record%c\n", bt->num_frames, bt->num_frames != 1 ? 's' : ' ');
1506  for (i = 3; i < bt->num_frames - 2; i++) {
1507  ast_log(LOG_DEBUG, "#%d: [%p] %s\n", i - 3, bt->addresses[i], strings[i]);
1508  }
1509 
1510  ast_std_free(strings);
1511  } else {
1512  ast_debug(1, "Could not allocate memory for backtrace\n");
1513  }
1514  ast_bt_destroy(bt);
1515 #else
1516  ast_log(LOG_WARNING, "Must run configure with '--with-execinfo' for stack backtraces.\n");
1517 #endif /* defined(HAVE_BKTR) */
1518 }
1519 
1520 void __ast_verbose_ap(const char *file, int line, const char *func, const char *fmt, va_list ap)
1521 {
1522  struct ast_str *buf = NULL;
1523  int res = 0;
1524 
1526  return;
1527 
1528  if (ast_opt_timestamp) {
1529  struct timeval now;
1530  struct ast_tm tm;
1531  char date[40];
1532  char *datefmt;
1533 
1534  now = ast_tvnow();
1535  ast_localtime(&now, &tm, NULL);
1536  ast_strftime(date, sizeof(date), dateformat, &tm);
1537  datefmt = ast_alloca(strlen(date) + 3 + strlen(fmt) + 1);
1538  sprintf(datefmt, "%c[%s] %s", 127, date, fmt);
1539  fmt = datefmt;
1540  } else {
1541  char *tmp = ast_alloca(strlen(fmt) + 2);
1542  sprintf(tmp, "%c%s", 127, fmt);
1543  fmt = tmp;
1544  }
1545 
1546  /* Build string */
1547  res = ast_str_set_va(&buf, 0, fmt, ap);
1548 
1549  /* If the build failed then we can drop this allocated message */
1550  if (res == AST_DYNSTR_BUILD_FAILED)
1551  return;
1552 
1553  ast_log(__LOG_VERBOSE, file, line, func, "%s", ast_str_buffer(buf));
1554 }
1555 
1556 void __ast_verbose(const char *file, int line, const char *func, const char *fmt, ...)
1557 {
1558  va_list ap;
1559 
1560  va_start(ap, fmt);
1561  __ast_verbose_ap(file, line, func, fmt, ap);
1562  va_end(ap);
1563 }
1564 
1565 /* No new code should use this directly, but we have the ABI for backwards compat */
1566 #undef ast_verbose
1567 void __attribute__((format(printf, 1,2))) ast_verbose(const char *fmt, ...);
1568 void ast_verbose(const char *fmt, ...)
1569 {
1570  va_list ap;
1571 
1572  va_start(ap, fmt);
1573  __ast_verbose_ap("", 0, "", fmt, ap);
1574  va_end(ap);
1575 }
1576 
1577 int ast_register_verbose(void (*v)(const char *string))
1578 {
1579  struct verb *verb;
1580 
1581  if (!(verb = ast_malloc(sizeof(*verb))))
1582  return -1;
1583 
1584  verb->verboser = v;
1585 
1589 
1590  return 0;
1591 }
1592 
1593 int ast_unregister_verbose(void (*v)(const char *string))
1594 {
1595  struct verb *cur;
1596 
1599  if (cur->verboser == v) {
1601  ast_free(cur);
1602  break;
1603  }
1604  }
1607 
1608  return cur ? 0 : -1;
1609 }
1610 
1611 static void update_logchannels(void)
1612 {
1613  struct logchannel *cur;
1614 
1616 
1617  global_logmask = 0;
1618 
1620  cur->logmask = make_components(cur->components, cur->lineno);
1621  global_logmask |= cur->logmask;
1622  }
1623 
1625 }
1626 
1628 {
1629  unsigned int level;
1630  unsigned int available = 0;
1631 
1633 
1634  for (level = 0; level < ARRAY_LEN(levels); level++) {
1635  if ((level >= 16) && !available && !levels[level]) {
1636  available = level;
1637  continue;
1638  }
1639 
1640  if (levels[level] && !strcasecmp(levels[level], name)) {
1642  "Unable to register dynamic logger level '%s': a standard logger level uses that name.\n",
1643  name);
1645 
1646  return -1;
1647  }
1648  }
1649 
1650  if (!available) {
1652  "Unable to register dynamic logger level '%s'; maximum number of levels registered.\n",
1653  name);
1655 
1656  return -1;
1657  }
1658 
1659  levels[available] = ast_strdup(name);
1660 
1662 
1663  ast_debug(1, "Registered dynamic logger level '%s' with index %u.\n", name, available);
1664 
1666 
1667  return available;
1668 }
1669 
1671 {
1672  unsigned int found = 0;
1673  unsigned int x;
1674 
1676 
1677  for (x = 16; x < ARRAY_LEN(levels); x++) {
1678  if (!levels[x]) {
1679  continue;
1680  }
1681 
1682  if (strcasecmp(levels[x], name)) {
1683  continue;
1684  }
1685 
1686  found = 1;
1687  break;
1688  }
1689 
1690  if (found) {
1691  /* take this level out of the global_logmask, to ensure that no new log messages
1692  * will be queued for it
1693  */
1694 
1695  global_logmask &= ~(1 << x);
1696 
1697  ast_free(levels[x]);
1698  levels[x] = NULL;
1700 
1701  ast_debug(1, "Unregistered dynamic logger level '%s' with index %u.\n", name, x);
1702 
1704  } else {
1706  }
1707 }
1708 
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
Definition: pbx.c:4676
struct ast_bt * ast_bt_create(void)
Definition: logger.c:1304
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:81
#define FORMATL
enum sip_cc_notify_state state
Definition: chan_sip.c:842
static void logger_print_normal(struct logmsg *logmsg)
Print a normal log message to the channels.
Definition: logger.c:971
static struct ast_cli_entry cli_logger[]
Definition: logger.c:934
void ast_std_free(void *ptr)
Main Channel structure associated with a channel.
Definition: channel.h:742
static char * handle_logger_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: logger.c:808
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
int ast_store_realtime(const char *family,...) attribute_sentinel
Create realtime configuration.
Definition: config.c:2726
int line
Definition: logger.c:130
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
static int queuelog_init
Definition: logger.c:76
int ast_safe_system(const char *s)
Safely spawn an external program while closing file descriptors.
Definition: asterisk.c:1077
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
Definition: config.c:625
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
char * strsep(char **str, const char *delims)
#define __LOG_DEBUG
Definition: logger.h:121
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
Definition: linkedlists.h:332
static unsigned int global_logmask
Definition: logger.c:75
String manipulation functions.
#define ast_strdup(a)
Definition: astmm.h:109
static struct sigaction handle_SIGXFSZ
Definition: logger.c:947
static const int colors[NUMLOGLEVELS]
Colors used in the console for logging.
Definition: logger.c:171
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: utils.h:653
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: cli.c:2177
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2502
static pthread_t logthread
Definition: logger.c:143
int option_debug
Definition: asterisk.c:182
int facility
Definition: logger.c:105
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: config.c:2630
static int rotate_file(const char *filename)
Definition: logger.c:571
Time-related functions and macros.
logtypes
Definition: logger.c:93
#define NUMLOGLEVELS
Definition: logger.h:184
#define COLOR_YELLOW
Definition: term.h:54
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
void ast_backtrace(void)
Definition: logger.c:1492
char components[0]
Definition: logger.c:117
descriptor for a cli entry.
Definition: cli.h:165
const int argc
Definition: cli.h:154
#define LOG_WARNING
Definition: logger.h:144
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
Definition: config.c:597
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
char * ast_utils_which(const char *binary, char *fullpath, size_t fullpath_size)
Resolve a binary to a full pathname.
Definition: utils.c:2364
static unsigned int make_components(const char *s, int lineno)
Definition: logger.c:214
int lineno
Definition: config.h:87
void ast_verbose(const char *fmt,...)
Definition: logger.c:1568
static struct logchannel * make_logchannel(const char *channel, const char *components, int lineno)
Definition: logger.c:238
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1570
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
#define LOG_BUF_INIT_SIZE
Definition: logger.c:210
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
struct logchannel::@283 list
#define var
Definition: ast_expr2f.c:606
void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt,...)
Definition: logger.c:479
int ast_str_set_va(struct ast_str **buf, ssize_t max_len, const char *fmt, va_list ap)
Set a dynamic string from a va_list.
Definition: strings.h:792
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:438
void * ast_std_realloc(void *ptr, size_t size)
Definition: cli.h:146
Configuration File Parser.
char * term_strip(char *outbuf, const char *inbuf, int maxout)
Definition: term.c:287
int option_verbose
Definition: asterisk.c:181
#define ast_calloc_with_stringfields(n, type, size)
Allocate a structure with embedded stringfields in a single allocation.
Definition: stringfields.h:275
char ** ast_bt_get_symbols(void **addresses, size_t num_frames)
Definition: logger.c:1335
static char * levels[NUMLOGLEVELS]
Logging channels used in the Asterisk logging system.
Definition: logger.c:160
#define ast_cond_wait(cond, mutex)
Definition: lock.h:171
#define ast_cond_init(cond, attr)
Definition: lock.h:167
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:142
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:235
#define __LOG_WARNING
Definition: logger.h:143
unsigned int queue_log_to_file
Definition: logger.c:87
#define COLOR_GREEN
Definition: term.h:51
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
#define __LOG_ERROR
Definition: logger.h:154
#define MAXHOSTNAMELEN
Definition: network.h:69
void ast_console_toggle_loglevel(int fd, int level, int state)
enables or disables logging of a specified level to the console fd specifies the index of the console...
Definition: asterisk.c:1136
Definitions to aid in the use of thread local storage.
#define COLOR_BRWHITE
Definition: term.h:62
void ast_cli(int fd, const char *fmt,...)
Definition: cli.c:105
#define LOG_DEBUG
Definition: logger.h:122
int ast_syslog_priority_from_loglevel(int level)
Maps an Asterisk log level (i.e. LOG_ERROR) to a syslog priority constant.
Definition: syslog.c:164
#define ast_cond_signal(cond)
Definition: lock.h:169
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
Definition: config.c:2499
static FILE * qlog
Definition: logger.c:147
void __ast_verbose(const char *file, int line, const char *func, const char *fmt,...)
This works like ast_log, but prints verbose messages to the console depending on verbosity level set...
Definition: logger.c:1556
#define ast_verb(level,...)
Definition: logger.h:243
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
static struct @282 logfiles
#define VERBOSE_BUF_INIT_SIZE
Definition: logger.c:207
static void update_logchannels(void)
Definition: logger.c:1611
Utility functions.
#define COLOR_BRRED
Definition: term.h:50
pthread_cond_t ast_cond_t
Definition: lock.h:144
Definition: logger.c:81
static struct ast_threadstorage log_buf
Definition: logger.c:209
static int logger_initialized
Definition: logger.c:77
void ast_child_verbose(int level, const char *fmt,...)
Definition: logger.c:430
void ast_logger_unregister_level(const char *name)
Unregister a previously registered logger level.
Definition: logger.c:1670
static int filesize_reload_needed
Definition: logger.c:74
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
static int logger_queue_rt_start(void)
Definition: logger.c:672
#define AST_RWLIST_INSERT_HEAD
Definition: linkedlists.h:703
#define EVENT_FLAG_SYSTEM
Definition: manager.h:71
Definition: config.h:68
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
#define SENTINEL
Definition: compiler.h:75
const char * value
Definition: config.h:79
Definition: logger.h:267
Definition: logger.c:127
void close_logger(void)
Definition: logger.c:1160
General Asterisk PBX channel definitions.
#define ast_opt_timestamp
Definition: options.h:117
int ast_register_verbose(void(*verboser)(const char *string)) attribute_warn_unused_result
Definition: logger.c:1577
Asterisk file paths, configured in asterisk.conf.
int ast_get_tid(void)
Get current thread ID.
Definition: utils.c:2346
const int fd
Definition: cli.h:153
static char * handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command to show logging system configuration.
Definition: logger.c:889
#define AST_PTHREADT_NULL
Definition: lock.h:65
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
unsigned int logmask
Definition: logger.c:101
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:493
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:220
void term_filter_escapes(char *line)
Definition: term.c:338
int init_logger(void)
Definition: logger.c:1127
#define COLOR_BRGREEN
Definition: term.h:52
#define AST_RWLIST_REMOVE_CURRENT
Definition: linkedlists.h:565
int logger_reload(void)
Reload logger without rotating log files.
Definition: logger.c:800
const ast_string_field function
Definition: logger.c:138
int level
Definition: logger.c:129
static char queue_log_name[256]
Definition: logger.c:71
unsigned int alloced
Definition: logger.h:273
const char * name
Definition: config.h:77
int disabled
Definition: logger.c:103
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
static char exec_after_rotate[256]
Definition: logger.c:72
int ast_logger_register_level(const char *name)
Register a new logger level.
Definition: logger.c:1627
static void _handle_SIGXFSZ(int sig)
Definition: logger.c:941
Syslog support functions for Asterisk logging.
Core PBX routines and definitions.
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:542
Definition: logger.c:927
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:290
#define COLOR_RED
Definition: term.h:49
char * term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int maxout)
Definition: term.c:184
const char *const * argv
Definition: cli.h:155
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
#define AST_RWLIST_EMPTY
Definition: linkedlists.h:451
static void ast_log_vsyslog(struct logmsg *msg)
Definition: logger.c:952
#define LOG_ERROR
Definition: logger.h:155
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is &quot;true&quot;. This function checks to see whether a string passed to it is an indication of an &quot;true&quot; value. It checks to see if the string is &quot;yes&quot;, &quot;true&quot;, &quot;y&quot;, &quot;t&quot;, &quot;on&quot; or &quot;1&quot;.
Definition: utils.c:1533
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
static struct @350 args
#define CLI_SHOWUSAGE
Definition: cli.h:44
const ast_string_field date
Definition: logger.c:138
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:97
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define ast_cond_destroy(cond)
Definition: lock.h:168
enum logtypes type
Definition: logger.c:107
void(* verboser)(const char *string)
Definition: logger.c:928
const char * ast_config_AST_LOG_DIR
Definition: asterisk.c:263
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
static void * logger_thread(void *data)
Actual logging thread.
Definition: logger.c:1060
unsigned int queue_log
Definition: logger.c:86
#define CLI_FAILURE
Definition: cli.h:45
int errno
static const char name[]
#define ast_free(a)
Definition: astmm.h:97
char * command
Definition: cli.h:180
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:418
static void logger_queue_init(void)
Definition: logger.c:1103
void * ast_std_calloc(size_t nmemb, size_t size)
static struct ast_format f[]
Definition: format_g726.c:181
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:829
Prototypes for public functions only of internal interest,.
void ast_console_puts_mutable(const char *string, int level)
log the string to the console, and all attached console clients
Definition: asterisk.c:1199
char string[1]
Definition: utils.c:1722
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2351
void * addresses[AST_MAX_BT_FRAMES]
Definition: logger.h:269
enum logmsgtypes type
Definition: logger.c:128
Structure used to handle boolean flags.
Definition: utils.h:200
FILE * fileptr
Definition: logger.c:109
#define AST_MAX_BT_FRAMES
Definition: logger.h:261
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
Definition: config.c:2606
Support for logging to various files, console and syslog Configuration in file logger.conf.
const char * usage
Definition: cli.h:171
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
Definition: pbx.c:10546
char filename[PATH_MAX]
Definition: logger.c:111
const ast_string_field level_name
Definition: logger.c:138
static int available(struct dahdi_pvt **pvt, int is_specific_channel)
Definition: chan_dahdi.c:13288
#define CLI_SUCCESS
Definition: cli.h:43
#define __LOG_NOTICE
Definition: logger.h:132
static char dateformat[256]
Definition: logger.c:69
rotatestrategy
Definition: logger.c:79
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:666
int num_frames
Definition: logger.h:271
Standard Command Line Interface.
int lineno
Definition: logger.c:115
#define ast_calloc(a, b)
Definition: astmm.h:82
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
#define COLOR_BRBLUE
Definition: term.h:56
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:77
#define __LOG_VERBOSE
Definition: logger.h:165
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Register multiple commands.
Definition: cli.c:2167
int ast_syslog_facility(const char *facility)
Maps a syslog facility name from a string to a syslog facility constant.
Definition: syslog.c:87
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
int ast_bt_get_addresses(struct ast_bt *bt)
Definition: logger.c:1319
const ast_string_field message
Definition: logger.c:138
static struct ast_threadstorage verbose_buf
Definition: logger.c:206
long process_id
Definition: logger.c:131
struct logchannel * next
Definition: logger.c:113
Handy terminal functions for vt* terms.
static int reload_logger(int rotate)
Definition: logger.c:735
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
struct ast_variable * next
Definition: config.h:82
#define ast_mutex_init(pmutex)
Definition: lock.h:152
static void init_logger_chain(int locked)
Definition: logger.c:307
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:669
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
const ast_string_field file
Definition: logger.c:138
#define ast_mutex_destroy(a)
Definition: lock.h:154
static int close_logger_thread
Definition: logger.c:145
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the &#39;nonstandard&#39; argument separation process for an application.
Definition: app.h:619
struct ast_channel * ast_dummy_channel_alloc(void)
Create a fake channel structure.
Definition: channel.c:1391
int ast_unregister_verbose(void(*verboser)(const char *string)) attribute_warn_unused_result
Definition: logger.c:1593
static int logger_queue_restart(int queue_rotate)
Definition: logger.c:707
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
Definition: manager.h:219
#define RESULT_SUCCESS
Definition: cli.h:39
#define ast_malloc(a)
Definition: astmm.h:91
ast_mutex_t lock
Definition: logger.c:142
void __ast_verbose_ap(const char *file, int line, const char *func, const char *fmt, va_list ap)
Definition: logger.c:1520
static snd_pcm_format_t format
Definition: chan_alsa.c:93
static char * handle_logger_set_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: logger.c:846
static ast_cond_t logcond
Definition: logger.c:144
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:602
void * ast_bt_destroy(struct ast_bt *bt)
Definition: logger.c:1326
#define RESULT_FAILURE
Definition: cli.h:41
static char * handle_logger_rotate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: logger.c:827
logmsgtypes
Definition: logger.c:122
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
#define QUEUELOG
Definition: logger.h:35
static char hostname[MAXHOSTNAMELEN]
Definition: logger.c:91
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2151
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:344
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: config.c:2587
unsigned int queue_adaptive_realtime
Definition: logger.c:88