Asterisk - The Open Source Telephony Project  21.4.1
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 /*! \li \ref logger.c uses the configuration file \ref logger.conf
29  * \addtogroup configuration_file Configuration Files
30  */
31 
32 /*!
33  * \page logger.conf logger.conf
34  * \verbinclude logger.conf.sample
35  */
36 
37 /*** MODULEINFO
38  <support_level>core</support_level>
39  ***/
40 
41 #include "asterisk.h"
42 
43 /* When we include logger.h again it will trample on some stuff in syslog.h, but
44  * nothing we care about in here. */
45 #include <syslog.h>
46 #include <signal.h>
47 #include <time.h>
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 
51 #include "asterisk/_private.h"
52 #include "asterisk/module.h"
53 #include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
54 #include "asterisk/logger.h"
55 #include "asterisk/logger_category.h"
56 #include "asterisk/lock.h"
57 #include "asterisk/channel.h"
58 #include "asterisk/config.h"
59 #include "asterisk/term.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/utils.h"
62 #include "asterisk/manager.h"
63 #include "asterisk/astobj2.h"
64 #include "asterisk/threadstorage.h"
65 #include "asterisk/strings.h"
66 #include "asterisk/pbx.h"
67 #include "asterisk/app.h"
68 #include "asterisk/syslog.h"
69 #include "asterisk/buildinfo.h"
70 #include "asterisk/ast_version.h"
71 #include "asterisk/backtrace.h"
72 #include "asterisk/json.h"
73 
74 /*** DOCUMENTATION
75  ***/
76 
77 static int logger_register_level(const char *name);
78 static int logger_unregister_level(const char *name);
79 static int logger_get_dynamic_level(const char *name);
80 
81 static char dateformat[256] = "%b %e %T"; /* Original Asterisk Format */
82 
83 static char queue_log_name[256] = QUEUELOG;
84 static char exec_after_rotate[256] = "";
85 
86 static int filesize_reload_needed;
87 static unsigned int global_logmask = 0xFFFF;
88 static int queuelog_init;
89 static int logger_initialized;
90 static volatile int next_unique_callid = 1; /* Used to assign unique call_ids to calls */
91 static int display_callids;
92 
93 AST_THREADSTORAGE(unique_callid);
94 
95 static int logger_queue_size;
96 static int logger_queue_limit = 1000;
97 static int logger_messages_discarded;
98 static unsigned int high_water_alert;
99 
100 /* On some platforms, like those with MUSL as the runtime, BUFSIZ is
101  * unreasonably small (1024). Use a larger value in those environments.
102  */
103 #define LOGMSG_SIZE MAX(BUFSIZ, 8192)
104 
105 static enum rotatestrategy {
106  NONE = 0, /* Do not rotate log files at all, instead rely on external mechanisms */
107  SEQUENTIAL = 1 << 0, /* Original method - create a new file, in order */
108  ROTATE = 1 << 1, /* Rotate all files, such that the oldest file has the highest suffix */
109  TIMESTAMP = 1 << 2, /* Append the epoch timestamp onto the end of the archived file */
110 } rotatestrategy = SEQUENTIAL;
111 
112 static struct {
113  unsigned int queue_log:1;
114  unsigned int queue_log_to_file:1;
115  unsigned int queue_adaptive_realtime:1;
116  unsigned int queue_log_realtime_use_gmt:1;
117 } logfiles = { 1 };
118 
119 static char hostname[MAXHOSTNAMELEN];
120 AST_THREADSTORAGE_RAW(in_safe_log);
121 
122 struct logchannel;
123 struct logmsg;
124 
125 struct logformatter {
126  /* The name of the log formatter */
127  const char *name;
128  /* Pointer to the function that will format the log */
129  int (* const format_log)(struct logchannel *channel, struct logmsg *msg, char *buf, size_t size);
130 };
131 
132 enum logtypes {
133  LOGTYPE_SYSLOG,
134  LOGTYPE_FILE,
135  LOGTYPE_CONSOLE,
136 };
137 
138 struct logchannel {
139  /*! How the logs sent to this channel will be formatted */
141  /*! What to log to this channel */
142  unsigned int logmask;
143  /*! If this channel is disabled or not */
144  int disabled;
145  /*! syslog facility */
146  int facility;
147  /*! Verbosity level. (-1 if use option_verbose for the level.) */
149  /*! Type of log channel */
150  enum logtypes type;
151  /*! logfile logging file pointer */
152  FILE *fileptr;
153  /*! Filename */
154  char filename[PATH_MAX];
155  /*! field for linking to list */
157  /*! Line number from configuration file */
158  int lineno;
159  /*! Whether this log channel was created dynamically */
160  int dynamic;
161  /*! Components (levels) from last config load */
162  char components[0];
163 };
164 
166 
167 enum logmsgtypes {
168  LOGMSG_NORMAL = 0,
169  LOGMSG_VERBOSE,
170 };
171 
172 struct logmsg {
173  enum logmsgtypes type;
174  int level;
175  int sublevel;
176  int line;
177  int lwp;
178  ast_callid callid;
179  unsigned int hidecli:1; /*!< Whether to suppress log message from CLI output (but log normally to other log channels */
181  AST_STRING_FIELD(date);
182  AST_STRING_FIELD(file);
183  AST_STRING_FIELD(function);
185  AST_STRING_FIELD(level_name);
186  );
188 };
189 
190 static void logmsg_free(struct logmsg *msg)
191 {
193  ast_free(msg);
194 }
195 
197 static pthread_t logthread = AST_PTHREADT_NULL;
198 static ast_cond_t logcond;
199 static int close_logger_thread = 0;
200 
201 static FILE *qlog;
202 
203 /*! \brief Logging channels used in the Asterisk logging system
204  *
205  * The first 16 levels are reserved for system usage, and the remaining
206  * levels are reserved for usage by dynamic levels registered via
207  * ast_logger_register_level.
208  */
209 
210 /* Modifications to this array are protected by the rwlock in the
211  * logchannels list.
212  */
213 
214 static char *levels[NUMLOGLEVELS] = {
215  "DEBUG",
216  "TRACE",
217  "NOTICE",
218  "WARNING",
219  "ERROR",
220  "VERBOSE",
221  "DTMF",
222 };
223 
224 /*! \brief Custom dynamic logging levels added by the user
225  *
226  * The first 16 levels are reserved for system usage, and the remaining
227  * levels are reserved for usage by dynamic levels registered via
228  * ast_logger_register_level.
229  */
230 
231 static char *custom_dynamic_levels[NUMLOGLEVELS];
232 
233 /*! \brief Colors used in the console for logging */
234 static const int colors[NUMLOGLEVELS] = {
235  COLOR_BRGREEN,
236  COLOR_BRBLUE, /* no longer used */
237  COLOR_YELLOW,
238  COLOR_BRRED,
239  COLOR_RED,
240  COLOR_GREEN,
241  COLOR_BRGREEN,
242  0,
243  0,
244  0,
245  0,
246  0,
247  0,
248  0,
249  0,
250  0,
251  COLOR_BRBLUE,
252  COLOR_BRBLUE,
253  COLOR_BRBLUE,
254  COLOR_BRBLUE,
255  COLOR_BRBLUE,
256  COLOR_BRBLUE,
257  COLOR_BRBLUE,
258  COLOR_BRBLUE,
259  COLOR_BRBLUE,
260  COLOR_BRBLUE,
261  COLOR_BRBLUE,
262  COLOR_BRBLUE,
263  COLOR_BRBLUE,
264  COLOR_BRBLUE,
265  COLOR_BRBLUE,
266  COLOR_BRBLUE,
267 };
268 
269 AST_THREADSTORAGE(verbose_buf);
270 AST_THREADSTORAGE(verbose_build_buf);
271 #define VERBOSE_BUF_INIT_SIZE 256
272 
273 AST_THREADSTORAGE(log_buf);
274 #define LOG_BUF_INIT_SIZE 256
275 
276 static int format_log_json(struct logchannel *channel, struct logmsg *msg, char *buf, size_t size)
277 {
278  struct ast_json *json;
279  char *str;
280  char call_identifier_str[13];
281  size_t json_str_len;
282 
283  if (msg->callid) {
284  snprintf(call_identifier_str, sizeof(call_identifier_str), "[C-%08x]", msg->callid);
285  } else {
286  call_identifier_str[0] = '\0';
287  }
288 
289  json = ast_json_pack("{s: s, s: s, "
290  "s: {s: i, s: s} "
291  "s: {s: {s: s, s: s, s: i}, "
292  "s: s, s: s} }",
293  "hostname", ast_config_AST_SYSTEM_NAME,
294  "timestamp", msg->date,
295  "identifiers",
296  "lwp", msg->lwp,
297  "callid", S_OR(call_identifier_str, ""),
298  "logmsg",
299  "location",
300  "filename", msg->file,
301  "function", msg->function,
302  "line", msg->line,
303  "level", msg->level_name,
304  "message", msg->message);
305  if (!json) {
306  return -1;
307  }
308 
309  str = ast_json_dump_string(json);
310  if (!str) {
311  ast_json_unref(json);
312  return -1;
313  }
314 
315  ast_copy_string(buf, str, size);
316  json_str_len = strlen(str);
317  if (json_str_len > size - 1) {
318  json_str_len = size - 1;
319  }
320  buf[json_str_len] = '\n';
321  buf[json_str_len + 1] = '\0';
322 
323  term_strip(buf, buf, size);
324 
325  ast_json_free(str);
326  ast_json_unref(json);
327 
328  return 0;
329 }
330 
331 static struct logformatter logformatter_json = {
332  .name = "json",
333  .format_log = format_log_json
334 };
335 
336 static int logger_add_verbose_magic(struct logmsg *logmsg, char *buf, size_t size)
337 {
338  const char *p;
339  const char *fmt;
340  struct ast_str *prefixed;
341  signed char magic = logmsg->sublevel > 9 ? -10 : -logmsg->sublevel - 1; /* 0 => -1, 1 => -2, etc. Can't pass NUL, as it is EOS-delimiter */
342 
343  /* For compatibility with modules still calling ast_verbose() directly instead of using ast_verb() */
344  if (logmsg->sublevel < 0) {
345  if (!strncmp(logmsg->message, VERBOSE_PREFIX_10, strlen(VERBOSE_PREFIX_10))) {
346  magic = -11;
347  } else if (!strncmp(logmsg->message, VERBOSE_PREFIX_9, strlen(VERBOSE_PREFIX_9))) {
348  magic = -10;
349  } else if (!strncmp(logmsg->message, VERBOSE_PREFIX_8, strlen(VERBOSE_PREFIX_8))) {
350  magic = -9;
351  } else if (!strncmp(logmsg->message, VERBOSE_PREFIX_7, strlen(VERBOSE_PREFIX_7))) {
352  magic = -8;
353  } else if (!strncmp(logmsg->message, VERBOSE_PREFIX_6, strlen(VERBOSE_PREFIX_6))) {
354  magic = -7;
355  } else if (!strncmp(logmsg->message, VERBOSE_PREFIX_5, strlen(VERBOSE_PREFIX_5))) {
356  magic = -6;
357  } else if (!strncmp(logmsg->message, VERBOSE_PREFIX_4, strlen(VERBOSE_PREFIX_4))) {
358  magic = -5;
359  } else if (!strncmp(logmsg->message, VERBOSE_PREFIX_3, strlen(VERBOSE_PREFIX_3))) {
360  magic = -4;
361  } else if (!strncmp(logmsg->message, VERBOSE_PREFIX_2, strlen(VERBOSE_PREFIX_2))) {
362  magic = -3;
363  } else if (!strncmp(logmsg->message, VERBOSE_PREFIX_1, strlen(VERBOSE_PREFIX_1))) {
364  magic = -2;
365  } else {
366  magic = -1;
367  }
368  }
369 
370  if (!(prefixed = ast_str_thread_get(&verbose_buf, VERBOSE_BUF_INIT_SIZE))) {
371  return -1;
372  }
373 
374  ast_str_reset(prefixed);
375 
376  /* for every newline found in the buffer add verbose prefix data */
377  fmt = logmsg->message;
378  do {
379  if (!(p = strchr(fmt, '\n'))) {
380  p = strchr(fmt, '\0') - 1;
381  }
382  ++p;
383 
384  ast_str_append(&prefixed, 0, "%c", (char)magic);
385  ast_str_append_substr(&prefixed, 0, fmt, p - fmt);
386  fmt = p;
387  } while (p && *p);
388 
389  snprintf(buf, size, "%s", ast_str_buffer(prefixed));
390 
391  return 0;
392 }
393 
394 static int format_log_default(struct logchannel *chan, struct logmsg *msg, char *buf, size_t size)
395 {
396  char call_identifier_str[13];
397 
398  if (msg->callid) {
399  snprintf(call_identifier_str, sizeof(call_identifier_str), "[C-%08x]", msg->callid);
400  } else {
401  call_identifier_str[0] = '\0';
402  }
403 
404  switch (chan->type) {
405  case LOGTYPE_SYSLOG:
406  snprintf(buf, size, "%s[%d]%s: %s:%d in %s: %s",
407  levels[msg->level], msg->lwp, call_identifier_str, msg->file,
408  msg->line, msg->function, msg->message);
409  term_strip(buf, buf, size);
410  break;
411  case LOGTYPE_FILE:
412  snprintf(buf, size, "[%s] %s[%d]%s %s: %s",
413  msg->date, msg->level_name, msg->lwp, call_identifier_str,
414  msg->file, msg->message);
415  term_strip(buf, buf, size);
416  break;
417  case LOGTYPE_CONSOLE:
418  {
419  char linestr[32];
420  int has_file = !ast_strlen_zero(msg->file);
421  int has_line = (msg->line > 0);
422  int has_func = !ast_strlen_zero(msg->function);
423 
424  /*
425  * Verbose messages are interpreted by console channels in their own
426  * special way
427  */
428  if (msg->level == __LOG_VERBOSE) {
429  return logger_add_verbose_magic(msg, buf, size);
430  }
431 
432  /* Turn the numerical line number into a string */
433  snprintf(linestr, sizeof(linestr), "%d", msg->line);
434  /* Build string to print out */
435  snprintf(buf, size, "[%s] " COLORIZE_FMT "[%d]%s: " COLORIZE_FMT "%s" COLORIZE_FMT " " COLORIZE_FMT "%s %s",
436  msg->date,
437  COLORIZE(colors[msg->level], 0, msg->level_name),
438  msg->lwp,
439  call_identifier_str,
440  COLORIZE(COLOR_BRWHITE, 0, has_file ? msg->file : ""),
441  has_file ? ":" : "",
442  COLORIZE(COLOR_BRWHITE, 0, has_line ? linestr : ""),
443  COLORIZE(COLOR_BRWHITE, 0, has_func ? msg->function : ""),
444  has_func ? ":" : "",
445  msg->message);
446  }
447  break;
448  }
449 
450  return 0;
451 }
452 
453 static struct logformatter logformatter_default = {
454  .name = "default",
455  .format_log = format_log_default,
456 };
457 
458 static int format_log_plain(struct logchannel *chan, struct logmsg *msg, char *buf, size_t size)
459 {
460  char call_identifier_str[13];
461  char linestr[32];
462  int has_file = !ast_strlen_zero(msg->file);
463  int has_line = (msg->line > 0);
464  int has_func = !ast_strlen_zero(msg->function);
465 
466  if (msg->callid) {
467  snprintf(call_identifier_str, sizeof(call_identifier_str), "[C-%08x]", msg->callid);
468  } else {
469  call_identifier_str[0] = '\0';
470  }
471 
472  switch (chan->type) {
473  case LOGTYPE_SYSLOG:
474  snprintf(buf, size, "%s[%d]%s: %s:%d in %s: %s",
475  levels[msg->level], msg->lwp, call_identifier_str, msg->file,
476  msg->line, msg->function, msg->message);
477  term_strip(buf, buf, size);
478  break;
479  case LOGTYPE_FILE:
480  case LOGTYPE_CONSOLE:
481  /* Turn the numerical line number into a string */
482  snprintf(linestr, sizeof(linestr), "%d", msg->line);
483  /* Build string to print out */
484  snprintf(buf, size, "[%s] %s[%d]%s: %s%s%s%s%s%s%s",
485  msg->date,
486  msg->level_name,
487  msg->lwp,
488  call_identifier_str,
489  has_file ? msg->file : "",
490  has_file ? ":" : "",
491  has_line ? linestr : "",
492  has_line ? " " : "",
493  has_func ? msg->function : "",
494  has_func ? ": " : "",
495  msg->message);
496  term_strip(buf, buf, size);
497  break;
498  }
499 
500  return 0;
501 }
502 
503 static struct logformatter logformatter_plain = {
504  .name = "plain",
505  .format_log = format_log_plain,
506 };
507 
508 static void make_components(struct logchannel *chan)
509 {
510  char *w;
511  unsigned int logmask = 0;
512  char *stringp = ast_strdupa(chan->components);
513  unsigned int x;
514  unsigned int verb_level;
515 
516  /* Default to using option_verbose as the verbosity level of the logging channel. */
517  verb_level = -1;
518 
519  w = strchr(stringp, '[');
520  if (w) {
521  char *end = strchr(w + 1, ']');
522  if (!end) {
523  fprintf(stderr, "Logger Warning: bad formatter definition for %s in logger.conf\n", chan->filename);
524  } else {
525  char *formatter_name = w + 1;
526 
527  *end = '\0';
528  stringp = end + 1;
529 
530  if (!strcasecmp(formatter_name, "json")) {
531  memcpy(&chan->formatter, &logformatter_json, sizeof(chan->formatter));
532  } else if (!strcasecmp(formatter_name, "default")) {
533  memcpy(&chan->formatter, &logformatter_default, sizeof(chan->formatter));
534  } else if (!strcasecmp(formatter_name, "plain")) {
535  memcpy(&chan->formatter, &logformatter_plain, sizeof(chan->formatter));
536  } else {
537  fprintf(stderr, "Logger Warning: Unknown formatter definition %s for %s in logger.conf; using 'default'\n",
538  formatter_name, chan->filename);
539  memcpy(&chan->formatter, &logformatter_default, sizeof(chan->formatter));
540  }
541  }
542  }
543 
544  if (!chan->formatter.name) {
545  memcpy(&chan->formatter, &logformatter_default, sizeof(chan->formatter));
546  }
547 
548  while ((w = strsep(&stringp, ","))) {
549  w = ast_strip(w);
550  if (ast_strlen_zero(w)) {
551  continue;
552  }
553  if (!strcmp(w, "*")) {
554  logmask = 0xFFFFFFFF;
555  } else if (!strncasecmp(w, "verbose(", 8)) {
556  if (levels[__LOG_VERBOSE] && sscanf(w + 8, "%30u)", &verb_level) == 1) {
557  logmask |= (1 << __LOG_VERBOSE);
558  }
559  } else {
560  for (x = 0; x < ARRAY_LEN(levels); ++x) {
561  if (levels[x] && !strcasecmp(w, levels[x])) {
562  logmask |= (1 << x);
563  break;
564  }
565  }
566  }
567  }
568  if (chan->type == LOGTYPE_CONSOLE) {
569  /*
570  * Force to use the root console verbose level so if the
571  * user specified any verbose level then it does not interfere
572  * with calculating the ast_verb_sys_level value.
573  */
574  chan->verbosity = -1;
575  logmask |= (1 << __LOG_VERBOSE);
576  } else {
577  chan->verbosity = verb_level;
578  }
579  chan->logmask = logmask;
580 }
581 
582 /*!
583  * \brief create the filename that will be used for a logger channel.
584  *
585  * \param channel The name of the logger channel
586  * \param[out] filename The filename for the logger channel
587  * \param size The size of the filename buffer
588  */
589 static void make_filename(const char *channel, char *filename, size_t size)
590 {
591  const char *log_dir_prefix = "";
592  const char *log_dir_separator = "";
593 
594  *filename = '\0';
595 
596  if (!strcasecmp(channel, "console")) {
597  return;
598  }
599 
600  if (!strncasecmp(channel, "syslog", 6)) {
601  ast_copy_string(filename, channel, size);
602  return;
603  }
604 
605  /* It's a filename */
606 
607  if (channel[0] != '/') {
608  log_dir_prefix = ast_config_AST_LOG_DIR;
609  log_dir_separator = "/";
610  }
611 
612  if (!ast_strlen_zero(hostname)) {
613  snprintf(filename, size, "%s%s%s.%s",
614  log_dir_prefix, log_dir_separator, channel, hostname);
615  } else {
616  snprintf(filename, size, "%s%s%s",
617  log_dir_prefix, log_dir_separator, channel);
618  }
619 }
620 
621 /*!
622  * \brief Find a particular logger channel by name
623  *
624  * \pre logchannels list is locked
625  *
626  * \param channel The name of the logger channel to find
627  * \retval non-NULL The corresponding logger channel
628  * \retval NULL Unable to find a logger channel with that particular name
629  */
630 static struct logchannel *find_logchannel(const char *channel)
631 {
632  char filename[PATH_MAX];
633  struct logchannel *chan;
634 
635  make_filename(channel, filename, sizeof(filename));
636 
637  AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
638  if (!strcmp(chan->filename, filename)) {
639  return chan;
640  }
641  }
642 
643  return NULL;
644 }
645 
646 static struct logchannel *make_logchannel(const char *channel, const char *components, int lineno, int dynamic)
647 {
648  struct logchannel *chan;
649  char *facility;
650  struct ast_tm tm;
651  struct timeval now = ast_tvnow();
652  char datestring[256];
653 
654  if (ast_strlen_zero(channel) || !(chan = ast_calloc(1, sizeof(*chan) + strlen(components) + 1)))
655  return NULL;
656 
657  strcpy(chan->components, components);
658  chan->lineno = lineno;
659  chan->dynamic = dynamic;
660 
661  make_filename(channel, chan->filename, sizeof(chan->filename));
662 
663  if (!strcasecmp(channel, "console")) {
664  chan->type = LOGTYPE_CONSOLE;
665  } else if (!strncasecmp(channel, "syslog", 6)) {
666  /*
667  * syntax is:
668  * syslog.facility => level,level,level
669  */
670  facility = strchr(channel, '.');
671  if (!facility++ || !facility) {
672  facility = "local0";
673  }
674 
675  chan->facility = ast_syslog_facility(facility);
676 
677  if (chan->facility < 0) {
678  fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
679  ast_free(chan);
680  return NULL;
681  }
682 
683  chan->type = LOGTYPE_SYSLOG;
684  openlog("asterisk", LOG_PID, chan->facility);
685  } else {
686  if (!(chan->fileptr = fopen(chan->filename, "a"))) {
687  /* Can't do real logging here since we're called with a lock
688  * so log to any attached consoles */
689  ast_console_puts_mutable("ERROR: Unable to open log file '", __LOG_ERROR);
690  ast_console_puts_mutable(chan->filename, __LOG_ERROR);
691  ast_console_puts_mutable("': ", __LOG_ERROR);
692  ast_console_puts_mutable(strerror(errno), __LOG_ERROR);
693  ast_console_puts_mutable("'\n", __LOG_ERROR);
694  ast_free(chan);
695  return NULL;
696  } else {
697  /* Create our date/time */
698  ast_localtime(&now, &tm, NULL);
699  ast_strftime(datestring, sizeof(datestring), dateformat, &tm);
700 
701  fprintf(chan->fileptr, "[%s] Asterisk %s built by %s @ %s on a %s running %s on %s\n",
702  datestring, ast_get_version(), ast_build_user, ast_build_hostname,
703  ast_build_machine, ast_build_os, ast_build_date);
704  fflush(chan->fileptr);
705  }
706  chan->type = LOGTYPE_FILE;
707  }
708  make_components(chan);
709 
710  return chan;
711 }
712 
714 {
715  struct ast_config *cfg;
716  const char *s;
717  struct ast_flags config_flags = { 0 };
718 
719  if (!(cfg = ast_config_load2("logger.conf", "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
720  return;
721  }
722 
723  if ((s = ast_variable_retrieve(cfg, "general", "dateformat"))) {
724  ast_copy_string(dateformat, s, sizeof(dateformat));
725  }
726 
727  ast_config_destroy(cfg);
728 }
729 
730 /*!
731  * \brief Checks if level exists in array of level names
732  * \param levels Array of level names
733  * \param level Name to search for
734  * \param len Size of levels
735  *
736  * \retval 1 Found
737  * \retval 0 Not Found
738  */
739 static int custom_level_still_exists(char **levels, char *level, size_t len)
740 {
741  int i;
742  for (i = 0; i < len; i++) {
743  if (!strcmp(levels[i], level)) {
744  return 1;
745  }
746  }
747  return 0;
748 }
749 
750 /*!
751  * \brief Read config, setup channels.
752  * \param altconf Alternate configuration file to read.
753  *
754  * \pre logchannels list is write locked
755  *
756  * \retval 0 Success
757  * \retval -1 No config found or Failed
758  */
759 static int init_logger_chain(const char *altconf)
760 {
761  struct logchannel *chan;
762  struct ast_config *cfg;
763  struct ast_variable *var;
764  const char *s;
765  struct ast_flags config_flags = { 0 };
766 
767  if (!(cfg = ast_config_load2(S_OR(altconf, "logger.conf"), "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
768  cfg = NULL;
769  }
770 
771  /* Set defaults */
772  hostname[0] = '\0';
773  display_callids = 1;
774  memset(&logfiles, 0, sizeof(logfiles));
775  logfiles.queue_log = 1;
776  ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
777  ast_copy_string(queue_log_name, QUEUELOG, sizeof(queue_log_name));
778  exec_after_rotate[0] = '\0';
779  rotatestrategy = SEQUENTIAL;
780 
781  /* delete our list of log channels */
782  while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list))) {
783  ast_free(chan);
784  }
785  global_logmask = 0;
786 
787  errno = 0;
788  /* close syslog */
789  closelog();
790 
791  /* If no config file, we're fine, set default options. */
792  if (!cfg) {
793  chan = make_logchannel("console", "error,warning,notice,verbose", 0, 0);
794  if (!chan) {
795  fprintf(stderr, "ERROR: Failed to initialize default logging\n");
796  return -1;
797  }
798 
799  AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
800  global_logmask |= chan->logmask;
801 
802  return -1;
803  }
804 
805  if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
806  if (ast_true(s)) {
807  if (gethostname(hostname, sizeof(hostname) - 1)) {
808  ast_copy_string(hostname, "unknown", sizeof(hostname));
809  fprintf(stderr, "What box has no hostname???\n");
810  }
811  }
812  }
813  if ((s = ast_variable_retrieve(cfg, "general", "display_callids"))) {
814  display_callids = ast_true(s);
815  }
816  if ((s = ast_variable_retrieve(cfg, "general", "dateformat"))) {
817  ast_copy_string(dateformat, s, sizeof(dateformat));
818  }
819  if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) {
820  logfiles.queue_log = ast_true(s);
821  }
822  if ((s = ast_variable_retrieve(cfg, "general", "queue_log_to_file"))) {
823  logfiles.queue_log_to_file = ast_true(s);
824  }
825  if ((s = ast_variable_retrieve(cfg, "general", "queue_log_name"))) {
826  ast_copy_string(queue_log_name, s, sizeof(queue_log_name));
827  }
828  if ((s = ast_variable_retrieve(cfg, "general", "queue_log_realtime_use_gmt"))) {
829  logfiles.queue_log_realtime_use_gmt = ast_true(s);
830  }
831  if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate"))) {
832  ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate));
833  }
834  if ((s = ast_variable_retrieve(cfg, "general", "rotatestrategy"))) {
835  if (strcasecmp(s, "timestamp") == 0) {
836  rotatestrategy = TIMESTAMP;
837  } else if (strcasecmp(s, "rotate") == 0) {
838  rotatestrategy = ROTATE;
839  } else if (strcasecmp(s, "sequential") == 0) {
840  rotatestrategy = SEQUENTIAL;
841  } else if (strcasecmp(s, "none") == 0) {
842  rotatestrategy = NONE;
843  } else {
844  fprintf(stderr, "Unknown rotatestrategy: %s\n", s);
845  }
846  } else {
847  if ((s = ast_variable_retrieve(cfg, "general", "rotatetimestamp"))) {
848  rotatestrategy = ast_true(s) ? TIMESTAMP : SEQUENTIAL;
849  fprintf(stderr, "rotatetimestamp option has been deprecated. Please use rotatestrategy instead.\n");
850  }
851  }
852  if ((s = ast_variable_retrieve(cfg, "general", "logger_queue_limit"))) {
853  if (sscanf(s, "%30d", &logger_queue_limit) != 1) {
854  fprintf(stderr, "logger_queue_limit has an invalid value. Leaving at default of %d.\n",
855  logger_queue_limit);
856  }
857  if (logger_queue_limit < 10) {
858  fprintf(stderr, "logger_queue_limit must be >= 10. Setting to 10.\n");
859  logger_queue_limit = 10;
860  }
861  }
862 
863  /* Custom dynamic logging levels defined by user */
864  if ((s = ast_variable_retrieve(cfg, "general", "custom_levels"))) {
865  char *customlogs = ast_strdupa(s);
866  char *logfile;
867  char *new_custom_levels[16] = { };
868  unsigned int level, new_level = 0;
869 
870  /* get the custom levels we need to register or reload */
871  while ((logfile = strsep(&customlogs, ","))) {
872  new_custom_levels[new_level++] = logfile;
873  }
874 
875  /* unregister existing custom levels, if they're not still
876  specified in customlogs, to make room for new levels */
877  for (level = 16; level < ARRAY_LEN(levels); level++) {
878  if (levels[level] && custom_dynamic_levels[level] &&
879  !custom_level_still_exists(new_custom_levels, levels[level], ARRAY_LEN(new_custom_levels))) {
880  logger_unregister_level(levels[level]);
881  custom_dynamic_levels[level] = 0;
882  }
883  }
884 
885  new_level = 0;
886  while ((logfile = new_custom_levels[new_level++])) {
887  /* Lock already held, so directly register the level,
888  unless it's already registered (as during reload) */
889  if (logger_get_dynamic_level(logfile) == -1) {
890  int custom_level = logger_register_level(logfile);
891  custom_dynamic_levels[custom_level] = logfile;
892  }
893  }
894  }
895 
896  var = ast_variable_browse(cfg, "logfiles");
897  for (; var; var = var->next) {
898  chan = make_logchannel(var->name, var->value, var->lineno, 0);
899  if (!chan) {
900  /* Print error message directly to the consoles since the lock is held
901  * and we don't want to unlock with the list partially built */
902  ast_console_puts_mutable("ERROR: Unable to create log channel '", __LOG_ERROR);
903  ast_console_puts_mutable(var->name, __LOG_ERROR);
904  ast_console_puts_mutable("'\n", __LOG_ERROR);
905  continue;
906  }
907  AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
908  global_logmask |= chan->logmask;
909  }
910 
911  if (qlog) {
912  fclose(qlog);
913  qlog = NULL;
914  }
915 
916  ast_config_destroy(cfg);
917 
918  return 0;
919 }
920 
921 void ast_child_verbose(int level, const char *fmt, ...)
922 {
923  char *msg = NULL, *emsg = NULL, *sptr, *eptr;
924  va_list ap, aq;
925  int size;
926 
927  va_start(ap, fmt);
928  va_copy(aq, ap);
929  if ((size = vsnprintf(msg, 0, fmt, ap)) < 0) {
930  va_end(ap);
931  va_end(aq);
932  return;
933  }
934  va_end(ap);
935 
936  if (!(msg = ast_malloc(size + 1))) {
937  va_end(aq);
938  return;
939  }
940 
941  vsnprintf(msg, size + 1, fmt, aq);
942  va_end(aq);
943 
944  if (!(emsg = ast_malloc(size * 2 + 1))) {
945  ast_free(msg);
946  return;
947  }
948 
949  for (sptr = msg, eptr = emsg; ; sptr++) {
950  if (*sptr == '"') {
951  *eptr++ = '\\';
952  }
953  *eptr++ = *sptr;
954  if (*sptr == '\0') {
955  break;
956  }
957  }
958  ast_free(msg);
959 
960  fprintf(stdout, "verbose \"%s\" %d\n", emsg, level);
961  fflush(stdout);
962  ast_free(emsg);
963 }
964 
965 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
966 {
967  va_list ap;
968  struct timeval tv;
969  struct ast_tm tm;
970  char qlog_msg[8192];
971  int qlog_len;
972  char time_str[30];
973 
974  if (!logger_initialized) {
975  /* You are too early. We are not open yet! */
976  return;
977  }
978  if (!queuelog_init) {
979  /* We must initialize now since someone is trying to log something. */
981  }
982 
983  if (ast_check_realtime("queue_log")) {
984  tv = ast_tvnow();
985  ast_localtime(&tv, &tm, logfiles.queue_log_realtime_use_gmt ? "GMT" : NULL);
986  ast_strftime(time_str, sizeof(time_str), "%F %T.%6q", &tm);
987  va_start(ap, fmt);
988  vsnprintf(qlog_msg, sizeof(qlog_msg), fmt, ap);
989  va_end(ap);
990  if (logfiles.queue_adaptive_realtime) {
992  AST_APP_ARG(data)[5];
993  );
994  AST_NONSTANDARD_APP_ARGS(args, qlog_msg, '|');
995  /* Ensure fields are large enough to receive data */
996  ast_realtime_require_field("queue_log",
997  "data1", RQ_CHAR, strlen(S_OR(args.data[0], "")),
998  "data2", RQ_CHAR, strlen(S_OR(args.data[1], "")),
999  "data3", RQ_CHAR, strlen(S_OR(args.data[2], "")),
1000  "data4", RQ_CHAR, strlen(S_OR(args.data[3], "")),
1001  "data5", RQ_CHAR, strlen(S_OR(args.data[4], "")),
1002  SENTINEL);
1003 
1004  /* Store the log */
1005  ast_store_realtime("queue_log", "time", time_str,
1006  "callid", callid,
1007  "queuename", queuename,
1008  "agent", agent,
1009  "event", event,
1010  "data1", S_OR(args.data[0], ""),
1011  "data2", S_OR(args.data[1], ""),
1012  "data3", S_OR(args.data[2], ""),
1013  "data4", S_OR(args.data[3], ""),
1014  "data5", S_OR(args.data[4], ""),
1015  SENTINEL);
1016  } else {
1017  ast_store_realtime("queue_log", "time", time_str,
1018  "callid", callid,
1019  "queuename", queuename,
1020  "agent", agent,
1021  "event", event,
1022  "data", qlog_msg,
1023  SENTINEL);
1024  }
1025 
1026  if (!logfiles.queue_log_to_file) {
1027  return;
1028  }
1029  }
1030 
1031  if (qlog) {
1032  va_start(ap, fmt);
1033  qlog_len = snprintf(qlog_msg, sizeof(qlog_msg), "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
1034  vsnprintf(qlog_msg + qlog_len, sizeof(qlog_msg) - qlog_len, fmt, ap);
1035  va_end(ap);
1037  if (qlog) {
1038  fprintf(qlog, "%s\n", qlog_msg);
1039  fflush(qlog);
1040  }
1042  }
1043 }
1044 
1045 static int rotate_file(const char *filename)
1046 {
1047  char old[PATH_MAX];
1048  char new[PATH_MAX];
1049  int x, y, which, found, res = 0, fd;
1050  char *suffixes[4] = { "", ".gz", ".bz2", ".Z" };
1051 
1052  switch (rotatestrategy) {
1053  case NONE:
1054  /* No rotation */
1055  break;
1056  case SEQUENTIAL:
1057  for (x = 0; ; x++) {
1058  snprintf(new, sizeof(new), "%s.%d", filename, x);
1059  fd = open(new, O_RDONLY);
1060  if (fd > -1)
1061  close(fd);
1062  else
1063  break;
1064  }
1065  if (rename(filename, new)) {
1066  fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
1067  res = -1;
1068  } else {
1069  filename = new;
1070  }
1071  break;
1072  case TIMESTAMP:
1073  snprintf(new, sizeof(new), "%s.%ld", filename, (long)time(NULL));
1074  if (rename(filename, new)) {
1075  fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
1076  res = -1;
1077  } else {
1078  filename = new;
1079  }
1080  break;
1081  case ROTATE:
1082  /* Find the next empty slot, including a possible suffix */
1083  for (x = 0; ; x++) {
1084  found = 0;
1085  for (which = 0; which < ARRAY_LEN(suffixes); which++) {
1086  snprintf(new, sizeof(new), "%s.%d%s", filename, x, suffixes[which]);
1087  fd = open(new, O_RDONLY);
1088  if (fd > -1) {
1089  close(fd);
1090  found = 1;
1091  break;
1092  }
1093  }
1094  if (!found) {
1095  break;
1096  }
1097  }
1098 
1099  /* Found an empty slot */
1100  for (y = x; y > 0; y--) {
1101  for (which = 0; which < ARRAY_LEN(suffixes); which++) {
1102  snprintf(old, sizeof(old), "%s.%d%s", filename, y - 1, suffixes[which]);
1103  fd = open(old, O_RDONLY);
1104  if (fd > -1) {
1105  /* Found the right suffix */
1106  close(fd);
1107  snprintf(new, sizeof(new), "%s.%d%s", filename, y, suffixes[which]);
1108  if (rename(old, new)) {
1109  fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
1110  res = -1;
1111  }
1112  break;
1113  }
1114  }
1115  }
1116 
1117  /* Finally, rename the current file */
1118  snprintf(new, sizeof(new), "%s.0", filename);
1119  if (rename(filename, new)) {
1120  fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
1121  res = -1;
1122  } else {
1123  filename = new;
1124  }
1125  }
1126 
1127  if (!ast_strlen_zero(exec_after_rotate)) {
1128  struct ast_channel *c = ast_dummy_channel_alloc();
1129  char buf[512];
1130 
1131  pbx_builtin_setvar_helper(c, "filename", filename);
1132  pbx_substitute_variables_helper(c, exec_after_rotate, buf, sizeof(buf));
1133  if (c) {
1134  c = ast_channel_unref(c);
1135  }
1136  if (ast_safe_system(buf) == -1) {
1137  ast_log(LOG_WARNING, "error executing '%s'\n", buf);
1138  }
1139  }
1140  return res;
1141 }
1142 
1143 /*!
1144  * \internal
1145  * \brief Start the realtime queue logging if configured.
1146  *
1147  * \retval TRUE if not to open queue log file.
1148  */
1149 static int logger_queue_rt_start(void)
1150 {
1151  if (ast_check_realtime("queue_log")) {
1152  if (!ast_realtime_require_field("queue_log",
1153  "time", RQ_DATETIME, 26,
1154  "data1", RQ_CHAR, 20,
1155  "data2", RQ_CHAR, 20,
1156  "data3", RQ_CHAR, 20,
1157  "data4", RQ_CHAR, 20,
1158  "data5", RQ_CHAR, 20,
1159  SENTINEL)) {
1160  logfiles.queue_adaptive_realtime = 1;
1161  } else {
1162  logfiles.queue_adaptive_realtime = 0;
1163  }
1164 
1165  if (!logfiles.queue_log_to_file) {
1166  /* Don't open the log file. */
1167  return 1;
1168  }
1169  }
1170  return 0;
1171 }
1172 
1173 /*!
1174  * \internal
1175  * \brief Rotate the queue log file and restart.
1176  *
1177  * \param queue_rotate Log queue rotation mode.
1178  *
1179  * \note Assumes logchannels is write locked on entry.
1180  *
1181  * \retval 0 on success.
1182  * \retval -1 on error.
1183  */
1184 static int logger_queue_restart(int queue_rotate)
1185 {
1186  int res = 0;
1187  char qfname[PATH_MAX];
1188 
1189  if (logger_queue_rt_start()) {
1190  return res;
1191  }
1192 
1193  snprintf(qfname, sizeof(qfname), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
1194  if (qlog) {
1195  /* Just in case it was still open. */
1196  fclose(qlog);
1197  qlog = NULL;
1198  }
1199  if (queue_rotate) {
1200  rotate_file(qfname);
1201  }
1202 
1203  /* Open the log file. */
1204  qlog = fopen(qfname, "a");
1205  if (!qlog) {
1206  ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
1207  res = -1;
1208  }
1209  return res;
1210 }
1211 
1212 static int reload_logger(int rotate, const char *altconf)
1213 {
1214  int queue_rotate = rotate;
1215  struct logchannel *f;
1216  int res = 0;
1217 
1219 
1220  if (qlog) {
1221  if (rotate < 0) {
1222  /* Check filesize - this one typically doesn't need an auto-rotate */
1223  if (ftello(qlog) > 0x40000000) { /* Arbitrarily, 1 GB */
1224  fclose(qlog);
1225  qlog = NULL;
1226  } else {
1227  queue_rotate = 0;
1228  }
1229  } else {
1230  fclose(qlog);
1231  qlog = NULL;
1232  }
1233  } else {
1234  queue_rotate = 0;
1235  }
1236 
1237  ast_mkdir(ast_config_AST_LOG_DIR, 0777);
1238 
1239  AST_RWLIST_TRAVERSE(&logchannels, f, list) {
1240  if (f->disabled) {
1241  f->disabled = 0; /* Re-enable logging at reload */
1242  /*** DOCUMENTATION
1243  <managerEvent language="en_US" name="LogChannel">
1244  <managerEventInstance class="EVENT_FLAG_SYSTEM">
1245  <synopsis>Raised when a logging channel is re-enabled after a reload operation.</synopsis>
1246  <syntax>
1247  <parameter name="Channel">
1248  <para>The name of the logging channel.</para>
1249  </parameter>
1250  </syntax>
1251  </managerEventInstance>
1252  </managerEvent>
1253  ***/
1254  manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
1255  }
1256  if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
1257  int rotate_this = 0;
1258  if (rotatestrategy != NONE && ftello(f->fileptr) > 0x40000000) { /* Arbitrarily, 1 GB */
1259  /* Be more proactive about rotating massive log files */
1260  rotate_this = 1;
1261  }
1262  fclose(f->fileptr); /* Close file */
1263  f->fileptr = NULL;
1264  if (rotate || rotate_this) {
1265  rotate_file(f->filename);
1266  }
1267  }
1268  }
1269 
1270  filesize_reload_needed = 0;
1271 
1272  init_logger_chain(altconf);
1273 
1274  ast_unload_realtime("queue_log");
1275  if (logfiles.queue_log) {
1276  res = logger_queue_restart(queue_rotate);
1278  ast_verb_update();
1279  ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
1280  ast_verb(1, "Asterisk Queue Logger restarted\n");
1281  } else {
1283  ast_verb_update();
1284  }
1285 
1286  return res;
1287 }
1288 
1289 static char *handle_logger_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1290 {
1291  switch (cmd) {
1292  case CLI_INIT:
1293  e->command = "logger reload";
1294  e->usage =
1295  "Usage: logger reload [<alt-conf>]\n"
1296  " Reloads the logger subsystem state. Use after restarting syslogd(8) if you are using syslog logging.\n";
1297  return NULL;
1298  case CLI_GENERATE:
1299  return NULL;
1300  }
1301  if (reload_logger(0, a->argc == 3 ? a->argv[2] : NULL)) {
1302  ast_cli(a->fd, "Failed to reload the logger\n");
1303  return CLI_FAILURE;
1304  }
1305  return CLI_SUCCESS;
1306 }
1307 
1308 static char *handle_logger_rotate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1309 {
1310  switch (cmd) {
1311  case CLI_INIT:
1312  e->command = "logger rotate";
1313  e->usage =
1314  "Usage: logger rotate\n"
1315  " Rotates and Reopens the log files.\n";
1316  return NULL;
1317  case CLI_GENERATE:
1318  return NULL;
1319  }
1320  if (reload_logger(1, NULL)) {
1321  ast_cli(a->fd, "Failed to reload the logger and rotate log files\n");
1322  return CLI_FAILURE;
1323  }
1324  return CLI_SUCCESS;
1325 }
1326 
1328 {
1329  return reload_logger(1, NULL);
1330 }
1331 
1332 int ast_logger_rotate_channel(const char *log_channel)
1333 {
1334  struct logchannel *f;
1335  int success = AST_LOGGER_FAILURE;
1336  char filename[PATH_MAX];
1337 
1338  make_filename(log_channel, filename, sizeof(filename));
1339 
1341 
1342  ast_mkdir(ast_config_AST_LOG_DIR, 0644);
1343 
1344  AST_RWLIST_TRAVERSE(&logchannels, f, list) {
1345  if (f->disabled) {
1346  f->disabled = 0; /* Re-enable logging at reload */
1347  manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n",
1348  f->filename);
1349  }
1350  if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
1351  fclose(f->fileptr); /* Close file */
1352  f->fileptr = NULL;
1353  if (strcmp(filename, f->filename) == 0) {
1354  rotate_file(f->filename);
1355  success = AST_LOGGER_SUCCESS;
1356  }
1357  }
1358  }
1359 
1360  init_logger_chain(NULL);
1361 
1363 
1364  return success;
1365 }
1366 
1367 static char *handle_logger_set_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1368 {
1369  int x;
1370  int state;
1371  int level = -1;
1372 
1373  switch (cmd) {
1374  case CLI_INIT:
1375  e->command = "logger set level {DEBUG|TRACE|NOTICE|WARNING|ERROR|VERBOSE|DTMF} {on|off}";
1376  e->usage =
1377  "Usage: logger set level {DEBUG|TRACE|NOTICE|WARNING|ERROR|VERBOSE|DTMF} {on|off}\n"
1378  " Set a specific log level to enabled/disabled for this console.\n";
1379  return NULL;
1380  case CLI_GENERATE:
1381  return NULL;
1382  }
1383 
1384  if (a->argc < 5)
1385  return CLI_SHOWUSAGE;
1386 
1388 
1389  for (x = 0; x < ARRAY_LEN(levels); x++) {
1390  if (levels[x] && !strcasecmp(a->argv[3], levels[x])) {
1391  level = x;
1392  break;
1393  }
1394  }
1395 
1397 
1398  state = ast_true(a->argv[4]) ? 1 : 0;
1399 
1400  if (level != -1) {
1401  ast_console_toggle_loglevel(a->fd, level, state);
1402  ast_cli(a->fd, "Logger status for '%s' has been set to '%s'.\n", levels[level], state ? "on" : "off");
1403  } else
1404  return CLI_SHOWUSAGE;
1405 
1406  return CLI_SUCCESS;
1407 }
1408 
1409 int ast_logger_get_channels(int (*logentry)(const char *channel, const char *type,
1410  const char *status, const char *configuration, void *data), void *data)
1411 {
1412  struct logchannel *chan;
1413  struct ast_str *configs = ast_str_create(64);
1414  int res = AST_LOGGER_SUCCESS;
1415 
1416  if (!configs) {
1417  return AST_LOGGER_ALLOC_ERROR;
1418  }
1419 
1421  AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
1422  unsigned int level;
1423 
1424  ast_str_reset(configs);
1425 
1426  for (level = 0; level < ARRAY_LEN(levels); level++) {
1427  if ((chan->logmask & (1 << level)) && levels[level]) {
1428  ast_str_append(&configs, 0, "%s ", levels[level]);
1429  }
1430  }
1431 
1432  res = logentry(chan->filename, chan->type == LOGTYPE_CONSOLE ? "Console" :
1433  (chan->type == LOGTYPE_SYSLOG ? "Syslog" : "File"), chan->disabled ?
1434  "Disabled" : "Enabled", ast_str_buffer(configs), data);
1435 
1436  if (res) {
1438  ast_free(configs);
1439  configs = NULL;
1440  return AST_LOGGER_FAILURE;
1441  }
1442  }
1444 
1445  ast_free(configs);
1446  configs = NULL;
1447 
1448  return AST_LOGGER_SUCCESS;
1449 }
1450 
1451 /*! \brief CLI command to show logging system configuration */
1452 static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1453 {
1454 #define FORMATL "%-35.35s %-8.8s %-10.10s %-9.9s "
1455  struct logchannel *chan;
1456  switch (cmd) {
1457  case CLI_INIT:
1458  e->command = "logger show channels";
1459  e->usage =
1460  "Usage: logger show channels\n"
1461  " List configured logger channels.\n";
1462  return NULL;
1463  case CLI_GENERATE:
1464  return NULL;
1465  }
1466  ast_cli(a->fd, "Logger queue limit: %d\n\n", logger_queue_limit);
1467  ast_cli(a->fd, FORMATL, "Channel", "Type", "Formatter", "Status");
1468  ast_cli(a->fd, "Configuration\n");
1469  ast_cli(a->fd, FORMATL, "-------", "----", "---------", "------");
1470  ast_cli(a->fd, "-------------\n");
1472  AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
1473  unsigned int level;
1474 
1475  ast_cli(a->fd, FORMATL, chan->filename, chan->type == LOGTYPE_CONSOLE ? "Console" : (chan->type == LOGTYPE_SYSLOG ? "Syslog" : "File"),
1476  chan->formatter.name,
1477  chan->disabled ? "Disabled" : "Enabled");
1478  ast_cli(a->fd, " - ");
1479  for (level = 0; level < ARRAY_LEN(levels); level++) {
1480  if ((chan->logmask & (1 << level)) && levels[level]) {
1481  ast_cli(a->fd, "%s ", levels[level]);
1482  }
1483  }
1484  ast_cli(a->fd, "\n");
1485  }
1487  ast_cli(a->fd, "\n");
1488 
1489  return CLI_SUCCESS;
1490 }
1491 
1492 /*! \brief CLI command to show logging levels */
1493 static char *handle_logger_show_levels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1494 {
1495 #define FORMATL2 "%5s %s\n"
1496  unsigned int level;
1497  switch (cmd) {
1498  case CLI_INIT:
1499  e->command = "logger show levels";
1500  e->usage =
1501  "Usage: logger show levels\n"
1502  " List configured logger levels.\n";
1503  return NULL;
1504  case CLI_GENERATE:
1505  return NULL;
1506  }
1507  ast_cli(a->fd, FORMATL2, "Level", "Name");
1508  ast_cli(a->fd, FORMATL2, "-----", "----");
1510  for (level = 0; level < ARRAY_LEN(levels); level++) {
1511  if (levels[level]) {
1512  ast_cli(a->fd, "%5d %s\n", level, levels[level]);
1513  }
1514  }
1516  ast_cli(a->fd, "\n");
1517 
1518  return CLI_SUCCESS;
1519 }
1520 
1521 int ast_logger_create_channel(const char *log_channel, const char *components)
1522 {
1523  struct logchannel *chan;
1524 
1525  if (ast_strlen_zero(components)) {
1526  return AST_LOGGER_DECLINE;
1527  }
1528 
1530 
1531  chan = find_logchannel(log_channel);
1532  if (chan) {
1534  return AST_LOGGER_FAILURE;
1535  }
1536 
1537  chan = make_logchannel(log_channel, components, 0, 1);
1538  if (!chan) {
1540  return AST_LOGGER_ALLOC_ERROR;
1541  }
1542 
1543  AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
1544  global_logmask |= chan->logmask;
1545 
1547 
1548  return AST_LOGGER_SUCCESS;
1549 }
1550 
1551 static char *handle_logger_add_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1552 {
1553  switch (cmd) {
1554  case CLI_INIT:
1555  e->command = "logger add channel";
1556  e->usage =
1557  "Usage: logger add channel <name> <levels>\n"
1558  " Adds a temporary logger channel. This logger channel\n"
1559  " will exist until removed or until Asterisk is restarted.\n"
1560  " <levels> is a comma-separated list of desired logger\n"
1561  " levels such as: verbose,warning,error\n"
1562  " An optional formatter may be specified with the levels;\n"
1563  " valid values are '[json]' and '[default]'.\n";
1564  return NULL;
1565  case CLI_GENERATE:
1566  return NULL;
1567  }
1568 
1569  if (a->argc < 5) {
1570  return CLI_SHOWUSAGE;
1571  }
1572 
1573  switch (ast_logger_create_channel(a->argv[3], a->argv[4])) {
1574  case AST_LOGGER_SUCCESS:
1575  return CLI_SUCCESS;
1576  case AST_LOGGER_FAILURE:
1577  ast_cli(a->fd, "Logger channel '%s' already exists\n", a->argv[3]);
1578  return CLI_SUCCESS;
1579  case AST_LOGGER_DECLINE:
1581  default:
1582  ast_cli(a->fd, "ERROR: Unable to create log channel '%s'\n", a->argv[3]);
1583  return CLI_FAILURE;
1584  }
1585 }
1586 
1587 int ast_logger_remove_channel(const char *log_channel)
1588 {
1589  struct logchannel *chan;
1590 
1592 
1593  chan = find_logchannel(log_channel);
1594  if (chan && chan->dynamic) {
1595  AST_RWLIST_REMOVE(&logchannels, chan, list);
1596  } else {
1598  return AST_LOGGER_FAILURE;
1599  }
1601 
1602  if (chan->fileptr) {
1603  fclose(chan->fileptr);
1604  chan->fileptr = NULL;
1605  }
1606  ast_free(chan);
1607  chan = NULL;
1608 
1609  return AST_LOGGER_SUCCESS;
1610 }
1611 
1612 static char *handle_logger_remove_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1613 {
1614  struct logchannel *chan;
1615  int gen_count = 0;
1616  char *gen_ret = NULL;
1617 
1618  switch (cmd) {
1619  case CLI_INIT:
1620  e->command = "logger remove channel";
1621  e->usage =
1622  "Usage: logger remove channel <name>\n"
1623  " Removes a temporary logger channel.\n";
1624  return NULL;
1625  case CLI_GENERATE:
1626  if (a->argc > 4 || (a->argc == 4 && a->pos > 3)) {
1627  return NULL;
1628  }
1630  AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
1631  if (chan->dynamic && (ast_strlen_zero(a->argv[3])
1632  || !strncmp(a->argv[3], chan->filename, strlen(a->argv[3])))) {
1633  if (gen_count == a->n) {
1634  gen_ret = ast_strdup(chan->filename);
1635  break;
1636  }
1637  gen_count++;
1638  }
1639  }
1641  return gen_ret;
1642  }
1643 
1644  if (a->argc < 4) {
1645  return CLI_SHOWUSAGE;
1646  }
1647 
1648  switch (ast_logger_remove_channel(a->argv[3])) {
1649  case AST_LOGGER_SUCCESS:
1650  ast_cli(a->fd, "Removed dynamic logger channel '%s'\n", a->argv[3]);
1651  return CLI_SUCCESS;
1652  case AST_LOGGER_FAILURE:
1653  ast_cli(a->fd, "Unable to find dynamic logger channel '%s'\n", a->argv[3]);
1654  return CLI_SUCCESS;
1655  default:
1656  ast_cli(a->fd, "Internal failure attempting to delete dynamic logger channel '%s'\n", a->argv[3]);
1657  return CLI_FAILURE;
1658  }
1659 }
1660 
1661 /* Call ID filtering */
1662 
1663 AST_THREADSTORAGE(callid_group_name);
1664 
1665 /*! \brief map call ID to group */
1667  AST_RWLIST_ENTRY(chan_group_lock) entry;
1668  char name[0];
1669 };
1670 
1672 
1673 static int callid_filtering = 0;
1674 
1675 static const char *get_callid_group(void)
1676 {
1677  char **callid_group;
1678  callid_group = ast_threadstorage_get(&callid_group_name, sizeof(*callid_group));
1679  return callid_group ? *callid_group : NULL;
1680 }
1681 
1682 static int callid_set_chanloggroup(const char *group)
1683 {
1684  /* Use threadstorage for constant time access, rather than a linked list */
1685  ast_callid callid;
1686  char **callid_group;
1687 
1688  callid = ast_read_threadstorage_callid();
1689  if (!callid) {
1690  /* Should never be called on non-PBX threads */
1691  ast_assert(0);
1692  return -1;
1693  }
1694 
1695  callid_group = ast_threadstorage_get(&callid_group_name, sizeof(*callid_group));
1696 
1697  if (!group) {
1698  /* Remove from list */
1699  if (!*callid_group) {
1700  return 0; /* Wasn't in any group to begin with */
1701  }
1702  ast_free(*callid_group);
1703  return 0; /* Set Call ID group for the first time */
1704  }
1705  /* Existing group */
1706  ast_free(*callid_group);
1707  *callid_group = ast_strdup(group);
1708  if (!*callid_group) {
1709  return -1;
1710  }
1711  return 0; /* Set Call ID group for the first time */
1712 }
1713 
1714 static int callid_group_remove_filters(void)
1715 {
1716  int i = 0;
1717  struct chan_group_lock *cgl;
1718 
1720  while ((cgl = AST_RWLIST_REMOVE_HEAD(&chan_group_lock_list, entry))) {
1721  ast_free(cgl);
1722  i++;
1723  }
1724  callid_filtering = 0;
1726  return i;
1727 }
1728 
1729 static int callid_group_set_filter(const char *group, int enabled)
1730 {
1731  struct chan_group_lock *cgl;
1732 
1734  AST_RWLIST_TRAVERSE_SAFE_BEGIN(&chan_group_lock_list, cgl, entry) {
1735  if (!strcmp(group, cgl->name)) {
1736  if (!enabled) {
1737  AST_RWLIST_REMOVE_CURRENT(entry);
1738  ast_free(cgl);
1739  }
1740  break;
1741  }
1742  }
1743  AST_RWLIST_TRAVERSE_SAFE_END;
1744 
1745  if (!enabled) {
1747  callid_filtering = 0;
1748  }
1750  return 0;
1751  }
1752 
1753  if (!cgl) {
1754  cgl = ast_calloc(1, sizeof(*cgl) + strlen(group) + 1);
1755  if (!cgl) {
1757  return -1;
1758  }
1759  strcpy(cgl->name, group); /* Safe */
1760  AST_RWLIST_INSERT_HEAD(&chan_group_lock_list, cgl, entry);
1761  } /* else, already existed, and was already enabled, no change */
1762  callid_filtering = 1;
1764  return 0;
1765 }
1766 
1767 static int callid_logging_enabled(void)
1768 {
1769  struct chan_group_lock *cgl;
1770  const char *callidgroup;
1771 
1772  if (!callid_filtering) {
1773  return 1; /* Everything enabled by default, if no filtering */
1774  }
1775 
1776  callidgroup = get_callid_group();
1777  if (!callidgroup) {
1778  return 0; /* Filtering, but no call group, not enabled */
1779  }
1780 
1782  AST_RWLIST_TRAVERSE(&chan_group_lock_list, cgl, entry) {
1783  if (!strcmp(callidgroup, cgl->name)) {
1784  break;
1785  }
1786  }
1788  return cgl ? 1 : 0; /* If found, enabled, otherwise not */
1789 }
1790 
1791 static int log_group_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
1792 {
1793  int res = callid_set_chanloggroup(value);
1794  if (res) {
1795  ast_log(LOG_ERROR, "Failed to set channel log group for %s\n", ast_channel_name(chan));
1796  return -1;
1797  }
1798  return 0;
1799 }
1800 
1801 static struct ast_custom_function log_group_function = {
1802  .name = "LOG_GROUP",
1803  .write = log_group_write,
1804 };
1805 
1806 static char *handle_logger_chanloggroup_filter(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1807 {
1808  int enabled;
1809 
1810  switch (cmd) {
1811  case CLI_INIT:
1812  e->command = "logger filter changroup";
1813  e->usage =
1814  "Usage: logger filter changroup <group> {on|off}\n"
1815  " Add or remove channel groups from log filtering.\n"
1816  " If filtering is active, only channels assigned\n"
1817  " to a group that has been enabled using this command\n"
1818  " will have execution shown in the CLI.\n";
1819  return NULL;
1820  case CLI_GENERATE:
1821  return NULL;
1822  }
1823 
1824  if (a->argc < 5) {
1825  return CLI_SHOWUSAGE;
1826  }
1827 
1828  enabled = ast_true(a->argv[4]) ? 1 : 0;
1829  if (callid_group_set_filter(a->argv[3], enabled)) {
1830  ast_cli(a->fd, "Failed to set channel group filter for group %s\n", a->argv[3]);
1831  return CLI_FAILURE;
1832  }
1833 
1834  ast_cli(a->fd, "Logging of channel group '%s' is now %s\n", a->argv[3], enabled ? "enabled" : "disabled");
1835  return CLI_SUCCESS;
1836 }
1837 
1838 static char *handle_logger_filter_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1839 {
1840  int i = 0;
1841  struct chan_group_lock *cgl;
1842 
1843  switch (cmd) {
1844  case CLI_INIT:
1845  e->command = "logger filter show";
1846  e->usage =
1847  "Usage: logger filter show\n"
1848  " Show current logger filtering settings.\n";
1849  return NULL;
1850  case CLI_GENERATE:
1851  return NULL;
1852  }
1853 
1855  AST_RWLIST_TRAVERSE(&chan_group_lock_list, cgl, entry) {
1856  ast_cli(a->fd, "%3d %-32s\n", ++i, cgl->name);
1857  }
1859 
1860  if (i) {
1861  ast_cli(a->fd, "%d channel group%s currently enabled\n", i, ESS(i));
1862  } else {
1863  ast_cli(a->fd, "No filtering currently active\n");
1864  }
1865  return CLI_SUCCESS;
1866 }
1867 
1868 static char *handle_logger_filter_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1869 {
1870  int removed;
1871 
1872  switch (cmd) {
1873  case CLI_INIT:
1874  e->command = "logger filter reset";
1875  e->usage =
1876  "Usage: logger filter reset\n"
1877  " Reset the logger filter.\n"
1878  " This removes any channel groups from filtering\n"
1879  " (all channel execution will be shown)\n";
1880  return NULL;
1881  case CLI_GENERATE:
1882  return NULL;
1883  }
1884 
1885  removed = callid_group_remove_filters();
1886 
1887  ast_cli(a->fd, "Log filtering has been reset (%d filter%s removed)\n", removed, ESS(removed));
1888  return CLI_SUCCESS;
1889 }
1890 
1891 static struct ast_cli_entry cli_logger[] = {
1892  AST_CLI_DEFINE(handle_logger_show_channels, "List configured log channels"),
1893  AST_CLI_DEFINE(handle_logger_show_levels, "List configured log levels"),
1894  AST_CLI_DEFINE(handle_logger_reload, "Reopens the log files"),
1895  AST_CLI_DEFINE(handle_logger_rotate, "Rotates and reopens the log files"),
1896  AST_CLI_DEFINE(handle_logger_set_level, "Enables/Disables a specific logging level for this console"),
1897  AST_CLI_DEFINE(handle_logger_add_channel, "Adds a new logging channel"),
1898  AST_CLI_DEFINE(handle_logger_remove_channel, "Removes a logging channel"),
1899  AST_CLI_DEFINE(handle_logger_chanloggroup_filter, "Filter PBX logs by channel log group"),
1900  AST_CLI_DEFINE(handle_logger_filter_show, "Show current PBX channel filtering"),
1901  AST_CLI_DEFINE(handle_logger_filter_reset, "Reset PBX channel filtering"),
1902 };
1903 
1904 static void _handle_SIGXFSZ(int sig)
1905 {
1906  /* Indicate need to reload */
1907  filesize_reload_needed = 1;
1908 }
1909 
1910 static struct sigaction handle_SIGXFSZ = {
1911  .sa_handler = _handle_SIGXFSZ,
1912  .sa_flags = SA_RESTART,
1913 };
1914 
1915 /*! \brief Print a normal log message to the channels */
1916 static void logger_print_normal(struct logmsg *logmsg)
1917 {
1918  struct logchannel *chan = NULL;
1919  char buf[LOGMSG_SIZE];
1920  int level = 0;
1921 
1923  if (!AST_RWLIST_EMPTY(&logchannels)) {
1924  AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
1925 
1926  /* If the channel is disabled, then move on to the next one */
1927  if (chan->disabled) {
1928  continue;
1929  }
1930  if (logmsg->level == __LOG_VERBOSE
1931  && (((chan->verbosity < 0) ? option_verbose : chan->verbosity)) < level) {
1932  continue;
1933  }
1934 
1935  if (!(chan->logmask & (1 << logmsg->level))) {
1936  continue;
1937  }
1938 
1939  switch (chan->type) {
1940  case LOGTYPE_SYSLOG:
1941  {
1942  int syslog_level = ast_syslog_priority_from_loglevel(logmsg->level);
1943 
1944  if (syslog_level < 0) {
1945  /* we are locked here, so cannot ast_log() */
1946  fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", logmsg->level);
1947  continue;
1948  }
1949 
1950  /* Don't use LOG_MAKEPRI because it's broken in glibc<2.17 */
1951  syslog_level = chan->facility | syslog_level; /* LOG_MAKEPRI(chan->facility, syslog_level); */
1952  if (!chan->formatter.format_log(chan, logmsg, buf, sizeof(buf))) {
1953  syslog(syslog_level, "%s", buf);
1954  }
1955  }
1956  break;
1957  case LOGTYPE_CONSOLE:
1958  if (!logmsg->hidecli && !chan->formatter.format_log(chan, logmsg, buf, sizeof(buf))) {
1959  ast_console_puts_mutable_full(buf, logmsg->level, logmsg->sublevel);
1960  }
1961  break;
1962  case LOGTYPE_FILE:
1963  {
1964  int res = 0;
1965 
1966  if (!chan->fileptr) {
1967  continue;
1968  }
1969 
1970  if (chan->formatter.format_log(chan, logmsg, buf, sizeof(buf))) {
1971  continue;
1972  }
1973 
1974  /* Print out to the file */
1975  res = fprintf(chan->fileptr, "%s", buf);
1976  if (res > 0) {
1977  fflush(chan->fileptr);
1978  } else if (res <= 0 && !ast_strlen_zero(logmsg->message)) {
1979  fprintf(stderr, "**** Asterisk Logging Error: ***********\n");
1980  if (errno == ENOMEM || errno == ENOSPC) {
1981  fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
1982  } else {
1983  fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
1984  }
1985 
1986  /*** DOCUMENTATION
1987  <managerEventInstance>
1988  <synopsis>Raised when a logging channel is disabled.</synopsis>
1989  <syntax>
1990  <parameter name="Channel">
1991  <para>The name of the logging channel.</para>
1992  </parameter>
1993  </syntax>
1994  </managerEventInstance>
1995  ***/
1996  manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
1997  chan->disabled = 1;
1998  }
1999  }
2000  break;
2001  }
2002  }
2003  } else if (logmsg->level != __LOG_VERBOSE || option_verbose >= logmsg->sublevel) {
2004  fputs(logmsg->message, stdout);
2005  }
2006 
2008 
2009  /* If we need to reload because of the file size, then do so */
2010  if (filesize_reload_needed) {
2011  reload_logger(-1, NULL);
2012  ast_verb(1, "Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
2013  }
2014 
2015  return;
2016 }
2017 
2018 static struct logmsg * __attribute__((format(printf, 7, 0))) format_log_message_ap(int level,
2019  int sublevel, const char *file, int line, const char *function, ast_callid callid,
2020  const char *fmt, va_list ap)
2021 {
2022  struct logmsg *logmsg = NULL;
2023  struct ast_str *buf = NULL;
2024  struct ast_tm tm;
2025  struct timeval now = ast_tvnow();
2026  int res = 0;
2027  char datestring[256];
2028 
2029  if (!(buf = ast_str_thread_get(&log_buf, LOG_BUF_INIT_SIZE))) {
2030  return NULL;
2031  }
2032 
2033  /* Build string */
2034  res = ast_str_set_va(&buf, LOGMSG_SIZE, fmt, ap);
2035 
2036  /* If the build failed, then abort and free this structure */
2037  if (res == AST_DYNSTR_BUILD_FAILED) {
2038  return NULL;
2039  }
2040 
2041  /* Automatically add a newline to format strings that don't have one */
2042  if (!ast_ends_with(ast_str_buffer(buf), "\n")) {
2043  ast_str_append(&buf, 0, "\n");
2044  }
2045 
2046  /* Create a new logging message */
2047  if (!(logmsg = ast_calloc_with_stringfields(1, struct logmsg, res + 128))) {
2048  return NULL;
2049  }
2050 
2051  /* Copy string over */
2053 
2054  /* Set type */
2055  if (level == __LOG_VERBOSE) {
2056  logmsg->type = LOGMSG_VERBOSE;
2057  } else {
2058  logmsg->type = LOGMSG_NORMAL;
2059  }
2060 
2061  if (display_callids && callid) {
2062  logmsg->callid = callid;
2063  }
2064 
2065  /* Create our date/time */
2066  ast_localtime(&now, &tm, NULL);
2067  ast_strftime(datestring, sizeof(datestring), dateformat, &tm);
2068  ast_string_field_set(logmsg, date, datestring);
2069 
2070  /* Copy over data */
2071  logmsg->level = level;
2072  logmsg->sublevel = sublevel;
2073  logmsg->line = line;
2074  ast_string_field_set(logmsg, level_name, levels[level]);
2075  ast_string_field_set(logmsg, file, file);
2076  ast_string_field_set(logmsg, function, function);
2077  logmsg->lwp = ast_get_tid();
2078 
2079  return logmsg;
2080 }
2081 
2082 static struct logmsg * __attribute__((format(printf, 7, 0))) format_log_message(int level,
2083  int sublevel, const char *file, int line, const char *function, ast_callid callid,
2084  const char *fmt, ...)
2085 {
2086  struct logmsg *logmsg;
2087  va_list ap;
2088 
2089  va_start(ap, fmt);
2090  logmsg = format_log_message_ap(level, sublevel, file, line, function, callid, fmt, ap);
2091  va_end(ap);
2092 
2093  return logmsg;
2094 }
2095 
2096 /*! \brief Actual logging thread */
2097 static void *logger_thread(void *data)
2098 {
2099  struct logmsg *next = NULL, *msg = NULL;
2100 
2101  for (;;) {
2102  /* We lock the message list, and see if any message exists... if not we wait on the condition to be signalled */
2104  if (AST_LIST_EMPTY(&logmsgs)) {
2105  if (close_logger_thread) {
2107  break;
2108  } else {
2109  ast_cond_wait(&logcond, &logmsgs.lock);
2110  }
2111  }
2112 
2113  if (high_water_alert) {
2114  msg = format_log_message(__LOG_WARNING, 0, "logger", 0, "***", 0,
2115  "Logging resumed. %d message%s discarded.\n",
2116  logger_messages_discarded, logger_messages_discarded == 1 ? "" : "s");
2117  if (msg) {
2119  }
2120  high_water_alert = 0;
2121  logger_messages_discarded = 0;
2122  }
2123 
2124  next = AST_LIST_FIRST(&logmsgs);
2126  logger_queue_size = 0;
2128 
2129  /* Otherwise go through and process each message in the order added */
2130  while ((msg = next)) {
2131  /* Get the next entry now so that we can free our current structure later */
2132  next = AST_LIST_NEXT(msg, list);
2133 
2134  /* Depending on the type, send it to the proper function */
2135  logger_print_normal(msg);
2136 
2137  /* Free the data since we are done */
2138  logmsg_free(msg);
2139  }
2140  }
2141 
2142  return NULL;
2143 }
2144 
2145 /*!
2146  * \internal
2147  * \brief Initialize the logger queue.
2148  *
2149  * \note Assumes logchannels is write locked on entry.
2150  */
2151 static void logger_queue_init(void)
2152 {
2153  ast_unload_realtime("queue_log");
2154  if (logfiles.queue_log) {
2155  char qfname[PATH_MAX];
2156 
2157  if (logger_queue_rt_start()) {
2158  return;
2159  }
2160 
2161  /* Open the log file. */
2162  snprintf(qfname, sizeof(qfname), "%s/%s", ast_config_AST_LOG_DIR,
2163  queue_log_name);
2164  if (qlog) {
2165  /* Just in case it was already open. */
2166  fclose(qlog);
2167  }
2168  qlog = fopen(qfname, "a");
2169  if (!qlog) {
2170  ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
2171  }
2172  }
2173 }
2174 
2176 {
2177  return logger_initialized;
2178 }
2179 
2180 /*!
2181  * \brief Start the ast_queue_log() logger.
2182  *
2183  * \note Called when the system is fully booted after startup
2184  * so preloaded realtime modules can get up.
2185  */
2187 {
2188  /* Must not be called before the logger is initialized. */
2189  ast_assert(logger_initialized);
2190 
2192  if (!queuelog_init) {
2193  logger_queue_init();
2194  queuelog_init = 1;
2196  ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
2197  } else {
2199  }
2200 }
2201 
2202 int init_logger(void)
2203 {
2204  int res;
2205  /* auto rotate if sig SIGXFSZ comes a-knockin */
2206  sigaction(SIGXFSZ, &handle_SIGXFSZ, NULL);
2207 
2208  /* Re-initialize the logmsgs mutex. The recursive mutex can be accessed prior
2209  * to Asterisk being forked into the background, which can cause the thread
2210  * ID tracked by the underlying pthread mutex to be different than the ID of
2211  * the thread that unlocks the mutex. Since init_logger is called after the
2212  * fork, it is safe to initialize the mutex here for future accesses.
2213  */
2214  ast_mutex_destroy(&logmsgs.lock);
2215  ast_mutex_init(&logmsgs.lock);
2216  ast_cond_init(&logcond, NULL);
2217 
2218  /* start logger thread */
2219  if (ast_pthread_create(&logthread, NULL, logger_thread, NULL) < 0) {
2220  ast_cond_destroy(&logcond);
2221  return -1;
2222  }
2223 
2224  /* register the logger cli commands */
2225  ast_cli_register_multiple(cli_logger, ARRAY_LEN(cli_logger));
2226  ast_custom_function_register(&log_group_function);
2227 
2228  ast_mkdir(ast_config_AST_LOG_DIR, 0777);
2229 
2230  /* create log channels */
2232  res = init_logger_chain(NULL);
2234  ast_verb_update();
2235  logger_initialized = 1;
2236  if (res) {
2237  ast_log(LOG_ERROR, "Errors detected in logger.conf. Default console logging is being used.\n");
2238  }
2239 
2240  ast_logger_category_load();
2241 
2242  return 0;
2243 }
2244 
2245 void close_logger(void)
2246 {
2247  struct logchannel *f = NULL;
2248 
2249  ast_logger_category_unload();
2250 
2251  ast_custom_function_unregister(&log_group_function);
2252  ast_cli_unregister_multiple(cli_logger, ARRAY_LEN(cli_logger));
2253 
2254  logger_initialized = 0;
2255 
2256  /* Stop logger thread */
2258  close_logger_thread = 1;
2259  ast_cond_signal(&logcond);
2261 
2262  if (logthread != AST_PTHREADT_NULL) {
2263  pthread_join(logthread, NULL);
2264  }
2265 
2267 
2268  if (qlog) {
2269  fclose(qlog);
2270  qlog = NULL;
2271  }
2272 
2273  while ((f = AST_LIST_REMOVE_HEAD(&logchannels, list))) {
2274  if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
2275  fclose(f->fileptr);
2276  f->fileptr = NULL;
2277  }
2278  ast_free(f);
2279  }
2280 
2281  callid_group_remove_filters();
2282 
2283  closelog(); /* syslog */
2284 
2286 }
2287 
2288 void ast_callid_strnprint(char *buffer, size_t buffer_size, ast_callid callid)
2289 {
2290  snprintf(buffer, buffer_size, "[C-%08x]", callid);
2291 }
2292 
2293 ast_callid ast_create_callid(void)
2294 {
2295  return ast_atomic_fetchadd_int(&next_unique_callid, +1);
2296 }
2297 
2299 {
2300  ast_callid *callid;
2301 
2302  callid = ast_threadstorage_get(&unique_callid, sizeof(*callid));
2303 
2304  return callid ? *callid : 0;
2305 }
2306 
2307 int ast_callid_threadassoc_change(ast_callid callid)
2308 {
2309  ast_callid *id = ast_threadstorage_get(&unique_callid, sizeof(*id));
2310 
2311  if (!id) {
2312  return -1;
2313  }
2314 
2315  *id = callid;
2316 
2317  return 0;
2318 }
2319 
2320 int ast_callid_threadassoc_add(ast_callid callid)
2321 {
2322  ast_callid *pointing;
2323 
2324  pointing = ast_threadstorage_get(&unique_callid, sizeof(*pointing));
2325  if (!pointing) {
2326  return -1;
2327  }
2328 
2329  if (*pointing) {
2330  ast_log(LOG_ERROR, "ast_callid_threadassoc_add(C-%08x) on thread "
2331  "already associated with callid [C-%08x].\n", callid, *pointing);
2332  return 1;
2333  }
2334 
2335  *pointing = callid;
2336  return 0;
2337 }
2338 
2340 {
2341  ast_callid *pointing;
2342 
2343  pointing = ast_threadstorage_get(&unique_callid, sizeof(*pointing));
2344  if (!pointing) {
2345  return -1;
2346  }
2347 
2348  if (*pointing) {
2349  *pointing = 0;
2350  return 0;
2351  }
2352 
2353  return -1;
2354 }
2355 
2356 int ast_callid_threadstorage_auto(ast_callid *callid)
2357 {
2358  ast_callid tmp;
2359 
2360  /* Start by trying to see if a callid is available from thread storage */
2362  if (tmp) {
2363  *callid = tmp;
2364  return 0;
2365  }
2366 
2367  /* If that failed, try to create a new one and bind it. */
2368  *callid = ast_create_callid();
2369  if (*callid) {
2370  ast_callid_threadassoc_add(*callid);
2371  return 1;
2372  }
2373 
2374  /* If neither worked, then something must have gone wrong. */
2375  return -1;
2376 }
2377 
2378 void ast_callid_threadstorage_auto_clean(ast_callid callid, int callid_created)
2379 {
2380  if (callid && callid_created) {
2381  /* If the callid was created rather than simply grabbed from the thread storage, we need to unbind here. */
2383  }
2384 }
2385 
2386 /*!
2387  * \brief send log messages to syslog and/or the console
2388  */
2389 static void __attribute__((format(printf, 7, 0))) ast_log_full(int level, int sublevel,
2390  const char *file, int line, const char *function, ast_callid callid,
2391  const char *fmt, va_list ap)
2392 {
2393  int hidecli = 0;
2394  struct logmsg *logmsg = NULL;
2395 
2396  if (level == __LOG_VERBOSE && ast_opt_remote && ast_opt_exec) {
2397  return;
2398  }
2399 
2400  if (callid_filtering && !callid_logging_enabled()) {
2401  switch (level) {
2402  case __LOG_VERBOSE:
2403  case __LOG_DEBUG:
2404  case __LOG_TRACE:
2405  case __LOG_DTMF:
2406  hidecli = 1; /* Hide the message from the CLI, but still log to any log files */
2407  default: /* Always show NOTICE, WARNING, ERROR, etc. */
2408  break;
2409  }
2410  return;
2411  }
2412 
2414  if (logger_queue_size >= logger_queue_limit && !close_logger_thread) {
2415  logger_messages_discarded++;
2416  if (!high_water_alert && !close_logger_thread) {
2417  logmsg = format_log_message(__LOG_WARNING, 0, "logger", 0, "***", 0,
2418  "Log queue threshold (%d) exceeded. Discarding new messages.\n", logger_queue_limit);
2419  AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
2420  high_water_alert = 1;
2421  ast_cond_signal(&logcond);
2422  }
2424  return;
2425  }
2427 
2428  logmsg = format_log_message_ap(level, sublevel, file, line, function, callid, fmt, ap);
2429  if (!logmsg) {
2430  return;
2431  }
2432 
2433  logmsg->hidecli = hidecli;
2434 
2435  /* If the logger thread is active, append it to the tail end of the list - otherwise skip that step */
2436  if (logthread != AST_PTHREADT_NULL) {
2438  if (close_logger_thread) {
2439  /* Logger is either closing or closed. We cannot log this message. */
2440  logmsg_free(logmsg);
2441  } else {
2442  AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
2443  logger_queue_size++;
2444  ast_cond_signal(&logcond);
2445  }
2447  } else {
2448  logger_print_normal(logmsg);
2449  logmsg_free(logmsg);
2450  }
2451 }
2452 
2453 void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
2454 {
2455  va_list ap;
2456 
2457  va_start(ap, fmt);
2458  ast_log_ap(level, file, line, function, fmt, ap);
2459  va_end(ap);
2460 }
2461 
2462 void ast_log_ap(int level, const char *file, int line, const char *function, const char *fmt, va_list ap)
2463 {
2464  ast_callid callid;
2465 
2466  callid = ast_read_threadstorage_callid();
2467 
2468  if (level == __LOG_VERBOSE) {
2469  __ast_verbose_ap(file, line, function, 0, callid, fmt, ap);
2470  } else {
2471  ast_log_full(level, -1, file, line, function, callid, fmt, ap);
2472  }
2473 }
2474 
2475 void ast_log_safe(int level, const char *file, int line, const char *function, const char *fmt, ...)
2476 {
2477  va_list ap;
2478  void *recursed = ast_threadstorage_get_ptr(&in_safe_log);
2479  ast_callid callid;
2480 
2481  if (recursed) {
2482  return;
2483  }
2484 
2485  if (ast_threadstorage_set_ptr(&in_safe_log, &(int) { 1 })) {
2486  /* We've failed to set the flag that protects against
2487  * recursion, so bail. */
2488  return;
2489  }
2490 
2491  callid = ast_read_threadstorage_callid();
2492 
2493  va_start(ap, fmt);
2494  ast_log_full(level, -1, file, line, function, callid, fmt, ap);
2495  va_end(ap);
2496 
2497  /* Clear flag so the next allocation failure can be logged. */
2498  ast_threadstorage_set_ptr(&in_safe_log, NULL);
2499 }
2500 
2501 void ast_log_callid(int level, const char *file, int line, const char *function, ast_callid callid, const char *fmt, ...)
2502 {
2503  va_list ap;
2504  va_start(ap, fmt);
2505  ast_log_full(level, -1, file, line, function, callid, fmt, ap);
2506  va_end(ap);
2507 }
2508 
2509 
2511 {
2512 #ifdef HAVE_BKTR
2513  struct ast_bt *bt;
2514  int i = 0;
2515  struct ast_vector_string *strings;
2516 
2517  if (!(bt = ast_bt_create())) {
2518  ast_log(LOG_WARNING, "Unable to allocate space for backtrace structure\n");
2519  return;
2520  }
2521 
2522  if ((strings = ast_bt_get_symbols(bt->addresses, bt->num_frames))) {
2523  int count = AST_VECTOR_SIZE(strings);
2524  struct ast_str *buf = ast_str_create(bt->num_frames * 64);
2525 
2526  if (buf) {
2527  ast_str_append(&buf, 0, "Got %d backtrace record%c\n", count - 3, count - 3 != 1 ? 's' : ' ');
2528  for (i = 3; i < AST_VECTOR_SIZE(strings); i++) {
2529  ast_str_append(&buf, 0, "#%2d: %s\n", i - 3, AST_VECTOR_GET(strings, i));
2530  }
2531  ast_log_safe(__LOG_ERROR, NULL, 0, NULL, "%s\n", ast_str_buffer(buf));
2532  ast_free(buf);
2533  }
2534 
2535  ast_bt_free_symbols(strings);
2536  } else {
2537  ast_log(LOG_ERROR, "Could not allocate memory for backtrace\n");
2538  }
2539  ast_bt_destroy(bt);
2540 #else
2541  ast_log(LOG_WARNING, "Must run configure with '--with-execinfo' for stack backtraces.\n");
2542 #endif /* defined(HAVE_BKTR) */
2543 }
2544 
2545 void __ast_verbose_ap(const char *file, int line, const char *func, int level, ast_callid callid, const char *fmt, va_list ap)
2546 {
2547  ast_log_full(__LOG_VERBOSE, level, file, line, func, callid, fmt, ap);
2548 }
2549 
2550 void __ast_verbose(const char *file, int line, const char *func, int level, const char *fmt, ...)
2551 {
2552  ast_callid callid;
2553  va_list ap;
2554 
2555  callid = ast_read_threadstorage_callid();
2556 
2557  va_start(ap, fmt);
2558  __ast_verbose_ap(file, line, func, level, callid, fmt, ap);
2559  va_end(ap);
2560 }
2561 
2562 void __ast_verbose_callid(const char *file, int line, const char *func, int level, ast_callid callid, const char *fmt, ...)
2563 {
2564  va_list ap;
2565  va_start(ap, fmt);
2566  __ast_verbose_ap(file, line, func, level, callid, fmt, ap);
2567  va_end(ap);
2568 }
2569 
2570 /*! Console verbosity level node. */
2572  /*! List node link */
2574  /*! Console verbosity level. */
2575  int *level;
2576 };
2577 
2578 /*! Registered console verbosity levels */
2580 
2581 /*! ast_verb_update() reentrancy protection lock. */
2582 AST_MUTEX_DEFINE_STATIC(verb_update_lock);
2583 
2585 {
2586  struct logchannel *log;
2587  struct verb_console *console;
2588  int verb_level;
2589 
2590  ast_mutex_lock(&verb_update_lock);
2591 
2593 
2594  /* Default to the root console verbosity. */
2595  verb_level = option_verbose;
2596 
2597  /* Determine max remote console level. */
2598  AST_LIST_TRAVERSE(&verb_consoles, console, node) {
2599  if (verb_level < *console->level) {
2600  verb_level = *console->level;
2601  }
2602  }
2604 
2605  /* Determine max logger channel level. */
2607  AST_RWLIST_TRAVERSE(&logchannels, log, list) {
2608  if (verb_level < log->verbosity) {
2609  verb_level = log->verbosity;
2610  }
2611  }
2613 
2614  ast_verb_sys_level = verb_level;
2615 
2616  ast_mutex_unlock(&verb_update_lock);
2617 }
2618 
2619 /*!
2620  * \internal
2621  * \brief Unregister a console verbose level.
2622  *
2623  * \param console Which console to unregister.
2624  */
2625 static void verb_console_unregister(struct verb_console *console)
2626 {
2628  console = AST_RWLIST_REMOVE(&verb_consoles, console, node);
2630  if (console) {
2631  ast_verb_update();
2632  }
2633 }
2634 
2635 static void verb_console_free(void *v_console)
2636 {
2637  struct verb_console *console = v_console;
2638 
2639  verb_console_unregister(console);
2640  ast_free(console);
2641 }
2642 
2643 /*! Thread specific console verbosity level node. */
2644 AST_THREADSTORAGE_CUSTOM(my_verb_console, NULL, verb_console_free);
2645 
2647 {
2648  struct verb_console *console;
2649 
2650  console = ast_threadstorage_get(&my_verb_console, sizeof(*console));
2651  if (!console || !level) {
2652  return;
2653  }
2654  console->level = level;
2655 
2657  AST_RWLIST_INSERT_HEAD(&verb_consoles, console, node);
2659  ast_verb_update();
2660 }
2661 
2663 {
2664  struct verb_console *console;
2665 
2666  console = ast_threadstorage_get(&my_verb_console, sizeof(*console));
2667  if (!console) {
2668  return;
2669  }
2670  verb_console_unregister(console);
2671 }
2672 
2674 {
2675  struct verb_console *console;
2676  int verb_level;
2677 
2678  console = ast_threadstorage_get(&my_verb_console, sizeof(*console));
2680  if (!console) {
2681  verb_level = 0;
2682  } else if (console->level) {
2683  verb_level = *console->level;
2684  } else {
2685  verb_level = option_verbose;
2686  }
2688  return verb_level;
2689 }
2690 
2691 void ast_verb_console_set(int verb_level)
2692 {
2693  struct verb_console *console;
2694 
2695  console = ast_threadstorage_get(&my_verb_console, sizeof(*console));
2696  if (!console) {
2697  return;
2698  }
2699 
2701  if (console->level) {
2702  *console->level = verb_level;
2703  } else {
2704  option_verbose = verb_level;
2705  }
2707  ast_verb_update();
2708 }
2709 
2710 static void update_logchannels(void)
2711 {
2712  struct logchannel *cur;
2713 
2714  global_logmask = 0;
2715 
2716  AST_RWLIST_TRAVERSE(&logchannels, cur, list) {
2717  make_components(cur);
2718  global_logmask |= cur->logmask;
2719  }
2720 }
2721 
2722 #ifdef AST_DEVMODE
2723 
2724 AST_THREADSTORAGE_RAW(trace_indent);
2725 #define LOTS_O_SPACES " "
2726 
2727 unsigned long _ast_trace_get_indent(void)
2728 {
2729  return (unsigned long)ast_threadstorage_get_ptr(&trace_indent);
2730 }
2731 
2732 void _ast_trace_set_indent(unsigned long indent)
2733 {
2734  ast_threadstorage_set_ptr(&trace_indent, (void*)indent);
2735 }
2736 
2737 unsigned long _ast_trace_inc_indent(void)
2738 {
2739  unsigned long indent = (unsigned long)ast_threadstorage_get_ptr(&trace_indent);
2740  indent++;
2741  ast_threadstorage_set_ptr(&trace_indent, (void*)indent);
2742 
2743  return indent;
2744 }
2745 
2746 unsigned long _ast_trace_dec_indent(void)
2747 {
2748  unsigned long indent = (unsigned long)ast_threadstorage_get_ptr(&trace_indent);
2749  indent--;
2750  ast_threadstorage_set_ptr(&trace_indent, (void*)indent);
2751 
2752  return indent;
2753 }
2754 
2755 void __ast_trace(const char *file, int line, const char *func, enum ast_trace_indent_type indent_type,
2756  unsigned long new_indent, const char* format, ...)
2757 {
2758  va_list ap;
2759  unsigned long indent = (unsigned long)ast_threadstorage_get_ptr(&trace_indent);
2760  struct ast_str *fmt = ast_str_create(128);
2761  const char *direction = "";
2762 
2763  if (!fmt) {
2764  return;
2765  }
2766 
2767  if (indent_type == AST_TRACE_INDENT_PROVIDED) {
2768  indent = new_indent;
2769  ast_threadstorage_set_ptr(&trace_indent, (void*)indent);
2770  } else if (indent_type == AST_TRACE_INDENT_INC_BEFORE) {
2771  indent++;
2772  ast_threadstorage_set_ptr(&trace_indent, (void*)indent);
2773  } else if (indent_type == AST_TRACE_INDENT_DEC_BEFORE) {
2774  indent--;
2775  ast_threadstorage_set_ptr(&trace_indent, (void*)indent);
2776  }
2777 
2778  switch(indent_type) {
2779  case AST_TRACE_INDENT_NONE:
2780  case AST_TRACE_INDENT_SAME:
2781  direction = "";
2782  break;
2786  direction = "--> ";
2787  break;
2790  direction = "<-- ";
2791  break;
2792  }
2793 
2794  ast_str_set(&fmt, 0, "%2d %-.*s%s%s:%d %s: %s", (int)indent, (indent_type == AST_TRACE_INDENT_NONE ? 0 : (int)(indent * 4)),
2795  LOTS_O_SPACES, direction, file, line, func, S_OR(ast_skip_blanks(format), "\n"));
2796 
2797  if (indent_type == AST_TRACE_INDENT_INC_AFTER || indent_type == AST_TRACE_INDENT_PROVIDED) {
2798  indent++;
2799  ast_threadstorage_set_ptr(&trace_indent, (void*)indent);
2800  }
2801  if (indent_type == AST_TRACE_INDENT_DEC_AFTER) {
2802  indent--;
2803  ast_threadstorage_set_ptr(&trace_indent, (void*)indent);
2804  }
2805 
2806  va_start(ap, format);
2807  ast_log_full(__LOG_TRACE, -1, NULL, 0, NULL, 0, ast_str_buffer(fmt), ap);
2808  va_end(ap);
2809  ast_free(fmt);
2810 }
2811 #endif
2812 
2813 /* Lock should be held before calling this function */
2814 static int logger_register_level(const char *name)
2815 {
2816  unsigned int level;
2817  unsigned int available = 0;
2818 
2819  for (level = 0; level < ARRAY_LEN(levels); level++) {
2820  if ((level >= 16) && !available && !levels[level]) {
2821  available = level;
2822  continue;
2823  }
2824 
2825  if (levels[level] && !strcasecmp(levels[level], name)) {
2826  ast_log(LOG_WARNING,
2827  "Unable to register dynamic logger level '%s': a standard logger level uses that name.\n",
2828  name);
2829 
2830  return -1;
2831  }
2832  }
2833 
2834  if (!available) {
2835  ast_log(LOG_WARNING,
2836  "Unable to register dynamic logger level '%s'; maximum number of levels registered.\n",
2837  name);
2838 
2839  return -1;
2840  }
2841 
2842  levels[available] = ast_strdup(name);
2843 
2844  ast_debug(1, "Registered dynamic logger level '%s' with index %u.\n", name, available);
2845 
2846  update_logchannels();
2847 
2848  return available;
2849 }
2850 
2851 int ast_logger_register_level(const char *name)
2852 {
2853  int available = 0;
2854 
2856  available = logger_register_level(name);
2858 
2859  return available;
2860 }
2861 
2862 static int logger_get_dynamic_level(const char *name)
2863 {
2864  int level = -1;
2865  unsigned int x;
2866 
2867  for (x = 16; x < ARRAY_LEN(levels); x++) {
2868  if (!levels[x]) {
2869  continue;
2870  }
2871  if (!strcasecmp(levels[x], name)) {
2872  level = x;
2873  break;
2874  }
2875  }
2876 
2877  return level;
2878 }
2879 
2880 int ast_logger_get_dynamic_level(const char *name)
2881 {
2882  int level = -1;
2883 
2885 
2886  level = logger_get_dynamic_level(name);
2887 
2889 
2890  return level;
2891 }
2892 
2893 static int logger_unregister_level(const char *name) {
2894  unsigned int x;
2895 
2896  x = logger_get_dynamic_level(name);
2897  if (x == -1) {
2898  return 0;
2899  }
2900  /* take this level out of the global_logmask, to ensure that no new log messages
2901  * will be queued for it
2902  */
2903  global_logmask &= ~(1 << x);
2904  ast_free(levels[x]);
2905  levels[x] = NULL;
2906  return x;
2907 }
2908 
2909 void ast_logger_unregister_level(const char *name)
2910 {
2911  int x;
2912 
2914  x = logger_unregister_level(name);
2915 
2916  if (x) {
2917  update_logchannels();
2918  }
2919 
2921 
2922  if (x) {
2923  ast_debug(1, "Unregistered dynamic logger level '%s' with index %u.\n", name, x);
2924  }
2925 }
2926 
2927 const char *ast_logger_get_dateformat(void)
2928 {
2929  return dateformat;
2930 }
2931 
2932 void ast_logger_set_queue_limit(int queue_limit)
2933 {
2934  logger_queue_limit = queue_limit;
2935 }
2936 
2938 {
2939  return logger_queue_limit;
2940 }
2941 
2942 static int reload_module(void)
2943 {
2944  return reload_logger(0, NULL);
2945 }
2946 
2947 static int unload_module(void)
2948 {
2949  return 0;
2950 }
2951 
2952 static int load_module(void)
2953 {
2954  return AST_MODULE_LOAD_SUCCESS;
2955 }
2956 
2957 /* Logger is initialized separate from the module loader, only reload_module does anything. */
2958 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Logger",
2959  .support_level = AST_MODULE_SUPPORT_CORE,
2960  .load = load_module,
2961  .unload = unload_module,
2962  /* This reload does not support realtime so it does not require "extconfig". */
2963  .reload = reload_module,
2964  .load_pri = 0,
2965 );
const char * name
Definition: pbx.h:119
struct ast_variable * next
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
static void logger_print_normal(struct logmsg *logmsg)
Print a normal log message to the channels.
Definition: logger.c:1916
Main Channel structure associated with a channel.
Definition: test_heap.c:38
void ast_verb_console_unregister(void)
Unregister this thread's console verbosity level.
Definition: logger.c:2662
struct logformatter formatter
Definition: logger.c:140
static int init_logger_chain(const char *altconf)
Read config, setup channels.
Definition: logger.c:759
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
const char * ast_logger_get_dateformat(void)
Get the logger configured date format.
Definition: logger.c:2927
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
static ast_mutex_t verb_update_lock
Definition: logger.c:2582
int ast_logger_remove_channel(const char *log_channel)
Delete the specified log channel.
Definition: logger.c:1587
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
Asterisk backtrace generation.
String manipulation functions.
static const int colors[NUMLOGLEVELS]
Colors used in the console for logging.
Definition: logger.c:234
Asterisk version information.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
int ast_verb_console_get(void)
Get this thread's console verbosity level.
Definition: logger.c:2673
int facility
Definition: logger.c:146
Time-related functions and macros.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
static int force_inline attribute_pure ast_ends_with(const char *str, const char *suffix)
Checks whether a string ends with another.
Definition: strings.h:116
static void make_filename(const char *channel, char *filename, size_t size)
create the filename that will be used for a logger channel.
Definition: logger.c:589
const char * ast_get_version(void)
Retrieve the Asterisk version string.
Definition: version.c:18
void ast_verb_console_register(int *level)
Register this thread's console verbosity level pointer.
Definition: logger.c:2646
void ast_verb_update(void)
Re-evaluate the system max verbosity level (ast_verb_sys_level).
Definition: logger.c:2584
char components[0]
Definition: logger.c:162
descriptor for a cli entry.
Definition: cli.h:171
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: main/config.c:3576
void ast_json_free(void *p)
Asterisk's custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
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:1739
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3530
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
Definition: main/config.c:3321
Structure for variables, used for configurations and for channel variables.
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:1030
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Definition: json.h:810
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
void ast_verb_console_set(int verb_level)
Set this thread's console verbosity level.
Definition: logger.c:2691
AST_THREADSTORAGE_RAW(in_intercept_routine)
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
static char * custom_dynamic_levels[NUMLOGLEVELS]
Custom dynamic logging levels added by the user.
Definition: logger.c:231
void ast_log_backtrace(void)
Log a backtrace of the current thread's execution stack to the Asterisk log.
Definition: logger.c:2510
char * term_strip(char *outbuf, const char *inbuf, int maxout)
Remove colorings from a specified string.
Definition: term.c:362
int option_verbose
Definition: options.c:67
int ast_logger_rotate_channel(const char *log_channel)
Rotate the specified log channel.
Definition: logger.c:1332
#define ast_calloc_with_stringfields(n, type, size)
Allocate a structure with embedded stringfields in a single allocation.
Definition: stringfields.h:432
Definition: astman.c:222
unsigned int hidecli
Definition: logger.c:179
static char * levels[NUMLOGLEVELS]
Logging channels used in the Asterisk logging system.
Definition: logger.c:214
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
void logger_queue_start(void)
Start the ast_queue_log() logger.
Definition: logger.c:2186
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
int ast_logger_register_level(const char *name)
Register a new logger level.
Definition: logger.c:2851
Definitions to aid in the use of thread local storage.
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:162
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:757
void ast_log_safe(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message with protection against recursion.
Definition: logger.c:2475
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
void __ast_verbose_callid(const char *file, int line, const char *func, int level, ast_callid callid, const char *fmt,...)
Send a verbose message (based on verbose level) with deliberately specified callid.
Definition: logger.c:2562
Utility functions.
int verbosity
Definition: logger.c:148
void ast_logger_set_queue_limit(int queue_limit)
Set the maximum number of messages allowed in the processing queue.
Definition: logger.c:2932
#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:333
int ast_logger_get_channels(int(*logentry)(const char *channel, const char *type, const char *status, const char *configuration, void *data), void *data)
Retrieve the existing log channels.
Definition: logger.c:1409
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
void ast_callid_strnprint(char *buffer, size_t buffer_size, ast_callid callid)
copy a string representation of the callid into a target string
Definition: logger.c:2288
Configuration File Parser.
A structure to hold backtrace information. This structure provides an easy means to store backtrace i...
Definition: backtrace.h:50
int ast_store_realtime(const char *family,...) attribute_sentinel
Create realtime configuration.
Definition: main/config.c:3740
Definition: logger.c:172
General Asterisk PBX channel definitions.
Asterisk JSON abstraction layer.
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
Definition: main/config.c:3549
Asterisk file paths, configured in asterisk.conf.
int ast_threadstorage_set_ptr(struct ast_threadstorage *ts, void *ptr)
Set a raw pointer from threadstorage.
int ast_get_tid(void)
Get current thread ID.
Definition: utils.c:2752
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
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:1452
int ast_logger_rotate()
Reload logger while rotating log files.
Definition: logger.c:1327
int dynamic
Definition: logger.c:160
static int custom_level_still_exists(char **levels, char *level, size_t len)
Checks if level exists in array of level names.
Definition: logger.c:739
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Definition: channel.h:1282
char * ast_str_append_substr(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Append a non-NULL terminated substring to the end of a dynamic string.
Definition: strings.h:1062
int ast_logger_get_dynamic_level(const char *name)
Retrieve dynamic logging level id.
Definition: logger.c:2880
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
unsigned int logmask
Definition: logger.c:142
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
#define COLORIZE_FMT
Shortcut macros for coloring a set of text.
Definition: term.h:71
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_debug(level,...)
Log a DEBUG message.
int disabled
Definition: logger.c:144
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
void ast_log_callid(int level, const char *file, int line, const char *function, ast_callid callid, const char *fmt,...)
Used for sending a log message with a known call_id This is a modified logger function which is funct...
Definition: logger.c:2501
Syslog support functions for Asterisk logging.
Core PBX routines and definitions.
int ast_verb_sys_level
Definition: options.c:64
#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:291
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
int ast_callid_threadassoc_remove(void)
Removes callid from thread storage of the calling thread.
Definition: logger.c:2339
ast_callid ast_read_threadstorage_callid(void)
extracts the callerid from the thread
Definition: logger.c:2298
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
int ast_is_logger_initialized(void)
Test if logger is initialized.
Definition: logger.c:2175
Support for dynamic strings.
Definition: strings.h:623
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
Definition: extconf.c:829
void __ast_verbose(const char *file, int line, const char *func, int level, const char *fmt,...)
Send a verbose message (based on verbose level)
Definition: logger.c:2550
int ast_callid_threadassoc_add(ast_callid callid)
Adds a known callid to thread storage of the calling thread.
Definition: logger.c:2320
map call ID to group
Definition: logger.c:1666
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:161
ast_trace_indent_type
Controls if and when indenting is applied.
ast_callid ast_create_callid(void)
factory function to create a new uniquely identifying callid.
Definition: logger.c:2293
enum logtypes type
Definition: logger.c:150
static struct logchannel * find_logchannel(const char *channel)
Find a particular logger channel by name.
Definition: logger.c:630
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
static void * logger_thread(void *data)
Actual logging thread.
Definition: logger.c:2097
int ast_logger_create_channel(const char *log_channel, const char *components)
Create a log channel.
Definition: logger.c:1521
#define ESS(x)
Definition: cli.h:59
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
Support for logging to various files, console and syslog Configuration in file logger.conf.
Prototypes for public functions only of internal interest,.
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:2524
struct logchannel::@372 list
void * ast_threadstorage_get_ptr(struct ast_threadstorage *ts)
Retrieve a raw pointer from threadstorage.
void * addresses[AST_MAX_BT_FRAMES]
Definition: backtrace.h:52
Structure used to handle boolean flags.
Definition: utils.h:199
FILE * fileptr
Definition: logger.c:152
const char * usage
Definition: cli.h:177
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:1251
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...
#define AST_THREADSTORAGE_CUSTOM(a, b, c)
Define a thread storage variable, with custom initialization and cleanup.
char filename[PATH_MAX]
Definition: logger.c:154
void ast_init_logger_for_socket_console(void)
load logger.conf configuration for console socket connections
Definition: logger.c:713
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
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:2453
void ast_console_puts_mutable_full(const char *message, int level, int sublevel)
log the string to the console, and all attached console clients
Definition: asterisk.c:1323
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
int ast_callid_threadassoc_change(ast_callid callid)
Sets what is stored in the thread storage to the given callid if it does not match what is already th...
Definition: logger.c:2307
int num_frames
Definition: backtrace.h:54
Standard Command Line Interface.
int lineno
Definition: logger.c:158
static int enabled
Whether or not we are storing history.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
int init_logger(void)
Definition: logger.c:2202
int ast_syslog_facility(const char *facility)
Maps a syslog facility name from a string to a syslog facility constant.
Definition: syslog.c:85
static void ast_log_full(int level, int sublevel, const char *file, int line, const char *function, ast_callid callid, const char *fmt, va_list ap)
send log messages to syslog and/or the console
Definition: logger.c:2389
int ast_logger_get_queue_limit(void)
Get the maximum number of messages allowed in the processing queue.
Definition: logger.c:2937
Abstract JSON element (object, array, string, int, ...).
Definition: search.h:40
Handy terminal functions for vt* terms.
static char * handle_logger_show_levels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command to show logging levels.
Definition: logger.c:1493
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
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:909
String vector definitions.
Definition: vector.h:55
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
int * level
Definition: logger.c:2575
void ast_console_puts_mutable(const char *string, int level)
log the string to the console, and all attached console clients
Definition: asterisk.c:1316
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
Definition: manager.h:253
Asterisk module definitions.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
void ast_callid_threadstorage_auto_clean(ast_callid callid, int callid_created)
Use in conjunction with ast_callid_threadstorage_auto. Cleans up the references and if the callid was...
Definition: logger.c:2378
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
int ast_callid_threadstorage_auto(ast_callid *callid)
Checks thread storage for a callid and stores a reference if it exists. If not, then a new one will b...
Definition: logger.c:2356
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
void close_logger(void)
Definition: logger.c:2245
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
void ast_logger_unregister_level(const char *name)
Unregister a previously registered logger level.
Definition: logger.c:2909
#define AST_APP_ARG(name)
Define an application argument.
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2479
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521