Newer
Older
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 1999 - 2006, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
* \brief Asterisk Logger
*
* \author Mark Spencer <markster@digium.com>
/*! \li \ref logger.c uses the configuration file \ref logger.conf
* \addtogroup configuration_file Configuration Files
*/
/*!
* \page logger.conf logger.conf
* \verbinclude logger.conf.sample
*/
/*** MODULEINFO
<support_level>core</support_level>
***/
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
Kevin P. Fleming
committed
/* When we include logger.h again it will trample on some stuff in syslog.h, but
* nothing we care about in here. */
#include <signal.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "asterisk/_private.h"
#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
Kevin P. Fleming
committed
#include "asterisk/logger.h"
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/term.h"
#include "asterisk/cli.h"
#include "asterisk/utils.h"
#include "asterisk/manager.h"
#include "asterisk/astobj2.h"
#include "asterisk/threadstorage.h"
#include "asterisk/strings.h"
Tilghman Lesher
committed
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/syslog.h"
#include "asterisk/buildinfo.h"
#include "asterisk/ast_version.h"
#include "asterisk/backtrace.h"
#include "asterisk/json.h"
Matthew Jordan
committed
/*** DOCUMENTATION
***/
static char dateformat[256] = "%b %e %T"; /* Original Asterisk Format */
static char queue_log_name[256] = QUEUELOG;
Tilghman Lesher
committed
static char exec_after_rotate[256] = "";
static int filesize_reload_needed;
static unsigned int global_logmask = 0xFFFF;
Tilghman Lesher
committed
static int queuelog_init;
static int logger_initialized;
static volatile int next_unique_callid = 1; /* Used to assign unique call_ids to calls */
static int display_callids;
Tilghman Lesher
committed
static enum rotatestrategy {
Jonathan Rose
committed
NONE = 0, /* Do not rotate log files at all, instead rely on external mechanisms */
Tilghman Lesher
committed
SEQUENTIAL = 1 << 0, /* Original method - create a new file, in order */
ROTATE = 1 << 1, /* Rotate all files, such that the oldest file has the highest suffix */
TIMESTAMP = 1 << 2, /* Append the epoch timestamp onto the end of the archived file */
} rotatestrategy = SEQUENTIAL;
static struct {
unsigned int queue_log:1;
Tilghman Lesher
committed
unsigned int queue_log_to_file:1;
unsigned int queue_adaptive_realtime:1;
unsigned int queue_log_realtime_use_gmt:1;
static char hostname[MAXHOSTNAMELEN];
AST_THREADSTORAGE_RAW(in_safe_log);
struct logchannel;
struct logmsg;
struct logformatter {
/* The name of the log formatter */
const char *name;
/* Pointer to the function that will format the log */
int (* const format_log)(struct logchannel *channel, struct logmsg *msg, char *buf, size_t size);
};
enum logtypes {
LOGTYPE_SYSLOG,
LOGTYPE_FILE,
LOGTYPE_CONSOLE,
};
/*! How the logs sent to this channel will be formatted */
struct logformatter formatter;
/*! What to log to this channel */
unsigned int logmask;
/*! If this channel is disabled or not */
int disabled;
/*! syslog facility */
int facility;
/*! Verbosity level. (-1 if use option_verbose for the level.) */
/*! Type of log channel */
enum logtypes type;
/*! logfile logging file pointer */
FILE *fileptr;
/*! Filename */
char filename[PATH_MAX];
/*! field for linking to list */
AST_LIST_ENTRY(logchannel) list;
/*! Line number from configuration file */
int lineno;
/*! Whether this log channel was created dynamically */
int dynamic;
/*! Components (levels) from last config load */
char components[0];
static AST_RWLIST_HEAD_STATIC(logchannels, logchannel);
enum logmsgtypes {
LOGMSG_NORMAL = 0,
LOGMSG_VERBOSE,
};
struct logmsg {
enum logmsgtypes type;
int level;
int line;
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(date);
AST_STRING_FIELD(file);
AST_STRING_FIELD(function);
AST_STRING_FIELD(message);
AST_STRING_FIELD(level_name);
);
AST_LIST_ENTRY(logmsg) list;
};
static void logmsg_free(struct logmsg *msg)
{
ast_string_field_free_memory(msg);
ast_free(msg);
}
static AST_LIST_HEAD_STATIC(logmsgs, logmsg);
static pthread_t logthread = AST_PTHREADT_NULL;
static ast_cond_t logcond;
static int close_logger_thread = 0;
/*! \brief Logging channels used in the Asterisk logging system
*
* The first 16 levels are reserved for system usage, and the remaining
* levels are reserved for usage by dynamic levels registered via
* ast_logger_register_level.
*/
/* Modifications to this array are protected by the rwlock in the
* logchannels list.
*/
static char *levels[NUMLOGLEVELS] = {
"---EVENT---", /* no longer used */
Kevin P. Fleming
committed
"VERBOSE",
"DTMF",
static const int colors[NUMLOGLEVELS] = {
COLOR_BRBLUE, /* no longer used */
COLOR_YELLOW,
COLOR_BRRED,
COLOR_RED,
Kevin P. Fleming
committed
COLOR_GREEN,
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
COLOR_BRGREEN,
0,
0,
0,
0,
0,
0,
0,
0,
0,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
COLOR_BRBLUE,
Russell Bryant
committed
AST_THREADSTORAGE(verbose_buf);
AST_THREADSTORAGE(verbose_build_buf);
#define VERBOSE_BUF_INIT_SIZE 256
Russell Bryant
committed
AST_THREADSTORAGE(log_buf);
#define LOG_BUF_INIT_SIZE 256
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
static int format_log_json(struct logchannel *channel, struct logmsg *msg, char *buf, size_t size)
{
struct ast_json *json;
char *str;
char call_identifier_str[13];
size_t json_str_len;
if (msg->callid) {
snprintf(call_identifier_str, sizeof(call_identifier_str), "[C-%08x]", msg->callid);
} else {
call_identifier_str[0] = '\0';
}
json = ast_json_pack("{s: s, s: s, "
"s: {s: i, s: s} "
"s: {s: {s: s, s: s, s: i}, "
"s: s, s: s} }",
"hostname", ast_config_AST_SYSTEM_NAME,
"timestamp", msg->date,
"identifiers",
"lwp", msg->lwp,
"callid", S_OR(call_identifier_str, ""),
"logmsg",
"location",
"filename", msg->file,
"function", msg->function,
"line", msg->line,
"level", msg->level_name,
"message", msg->message);
if (!json) {
return -1;
}
str = ast_json_dump_string(json);
if (!str) {
ast_json_unref(json);
return -1;
}
ast_copy_string(buf, str, size);
json_str_len = strlen(str);
if (json_str_len > size - 1) {
json_str_len = size - 1;
}
buf[json_str_len] = '\n';
buf[json_str_len + 1] = '\0';
term_strip(buf, buf, size);
ast_json_free(str);
ast_json_unref(json);
return 0;
}
static struct logformatter logformatter_json = {
.name = "json",
.format_log = format_log_json
};
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
static int logger_add_verbose_magic(struct logmsg *logmsg, char *buf, size_t size)
{
const char *p;
const char *fmt;
struct ast_str *prefixed;
signed char magic = logmsg->sublevel > 9 ? -10 : -logmsg->sublevel - 1; /* 0 => -1, 1 => -2, etc. Can't pass NUL, as it is EOS-delimiter */
/* For compatibility with modules still calling ast_verbose() directly instead of using ast_verb() */
if (logmsg->sublevel < 0) {
if (!strncmp(logmsg->message, VERBOSE_PREFIX_4, strlen(VERBOSE_PREFIX_4))) {
magic = -5;
} else if (!strncmp(logmsg->message, VERBOSE_PREFIX_3, strlen(VERBOSE_PREFIX_3))) {
magic = -4;
} else if (!strncmp(logmsg->message, VERBOSE_PREFIX_2, strlen(VERBOSE_PREFIX_2))) {
magic = -3;
} else if (!strncmp(logmsg->message, VERBOSE_PREFIX_1, strlen(VERBOSE_PREFIX_1))) {
magic = -2;
} else {
magic = -1;
}
}
if (!(prefixed = ast_str_thread_get(&verbose_buf, VERBOSE_BUF_INIT_SIZE))) {
return -1;
}
ast_str_reset(prefixed);
/* for every newline found in the buffer add verbose prefix data */
fmt = logmsg->message;
do {
if (!(p = strchr(fmt, '\n'))) {
p = strchr(fmt, '\0') - 1;
}
++p;
ast_str_append(&prefixed, 0, "%c", (char)magic);
ast_str_append_substr(&prefixed, 0, fmt, p - fmt);
fmt = p;
} while (p && *p);
snprintf(buf, size, "%s", ast_str_buffer(prefixed));
return 0;
}
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
static int format_log_default(struct logchannel *chan, struct logmsg *msg, char *buf, size_t size)
{
char call_identifier_str[13];
if (msg->callid) {
snprintf(call_identifier_str, sizeof(call_identifier_str), "[C-%08x]", msg->callid);
} else {
call_identifier_str[0] = '\0';
}
switch (chan->type) {
case LOGTYPE_SYSLOG:
snprintf(buf, size, "%s[%d]%s: %s:%d in %s: %s",
levels[msg->level], msg->lwp, call_identifier_str, msg->file,
msg->line, msg->function, msg->message);
term_strip(buf, buf, size);
break;
case LOGTYPE_FILE:
snprintf(buf, size, "[%s] %s[%d]%s %s: %s",
msg->date, msg->level_name, msg->lwp, call_identifier_str,
msg->file, msg->message);
term_strip(buf, buf, size);
break;
case LOGTYPE_CONSOLE:
{
char linestr[32];
/*
* Verbose messages are interpreted by console channels in their own
* special way
*/
if (msg->level == __LOG_VERBOSE) {
return logger_add_verbose_magic(msg, buf, size);
}
/* Turn the numeric line number into a string for colorization */
snprintf(linestr, sizeof(linestr), "%d", msg->line);
snprintf(buf, size, "[%s] " COLORIZE_FMT "[%d]%s: " COLORIZE_FMT ":" COLORIZE_FMT " " COLORIZE_FMT ": %s",
msg->date,
COLORIZE(colors[msg->level], 0, msg->level_name),
msg->lwp,
call_identifier_str,
COLORIZE(COLOR_BRWHITE, 0, msg->file),
COLORIZE(COLOR_BRWHITE, 0, linestr),
COLORIZE(COLOR_BRWHITE, 0, msg->function),
msg->message);
}
break;
}
return 0;
}
static struct logformatter logformatter_default = {
.name = "default",
.format_log = format_log_default,
};
static void make_components(struct logchannel *chan)
unsigned int logmask = 0;
char *stringp = ast_strdupa(chan->components);
unsigned int x;
Russell Bryant
committed
/* Default to using option_verbose as the verbosity level of the logging channel. */
verb_level = -1;
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
w = strchr(stringp, '[');
if (w) {
char *end = strchr(w + 1, ']');
if (!end) {
fprintf(stderr, "Logger Warning: bad formatter definition for %s in logger.conf\n", chan->filename);
} else {
char *formatter_name = w + 1;
*end = '\0';
stringp = end + 1;
if (!strcasecmp(formatter_name, "json")) {
memcpy(&chan->formatter, &logformatter_json, sizeof(chan->formatter));
} else if (!strcasecmp(formatter_name, "default")) {
memcpy(&chan->formatter, &logformatter_default, sizeof(chan->formatter));
} else {
fprintf(stderr, "Logger Warning: Unknown formatter definition %s for %s in logger.conf; using 'default'\n",
formatter_name, chan->filename);
memcpy(&chan->formatter, &logformatter_default, sizeof(chan->formatter));
}
}
}
if (!chan->formatter.name) {
memcpy(&chan->formatter, &logformatter_default, sizeof(chan->formatter));
}
Russell Bryant
committed
while ((w = strsep(&stringp, ","))) {
w = ast_strip(w);
if (ast_strlen_zero(w)) {
continue;
}
if (!strcmp(w, "*")) {
logmask = 0xFFFFFFFF;
} else if (!strncasecmp(w, "verbose(", 8)) {
if (levels[__LOG_VERBOSE] && sscanf(w + 8, "%30u)", &verb_level) == 1) {
logmask |= (1 << __LOG_VERBOSE);
}
} else {
for (x = 0; x < ARRAY_LEN(levels); ++x) {
if (levels[x] && !strcasecmp(w, levels[x])) {
logmask |= (1 << x);
break;
}
}
if (chan->type == LOGTYPE_CONSOLE) {
/*
* Force to use the root console verbose level so if the
* user specified any verbose level then it does not interfere
* with calculating the ast_verb_sys_level value.
*/
chan->verbosity = -1;
} else {
chan->verbosity = verb_level;
}
chan->logmask = logmask;
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
/*!
* \brief create the filename that will be used for a logger channel.
*
* \param channel The name of the logger channel
* \param[out] filename The filename for the logger channel
* \param size The size of the filename buffer
*/
static void make_filename(const char *channel, char *filename, size_t size)
{
const char *log_dir_prefix = "";
const char *log_dir_separator = "";
*filename = '\0';
if (!strcasecmp(channel, "console")) {
return;
}
if (!strncasecmp(channel, "syslog", 6)) {
ast_copy_string(filename, channel, size);
return;
}
/* It's a filename */
if (channel[0] != '/') {
log_dir_prefix = ast_config_AST_LOG_DIR;
log_dir_separator = "/";
}
if (!ast_strlen_zero(hostname)) {
snprintf(filename, size, "%s%s%s.%s",
log_dir_prefix, log_dir_separator, channel, hostname);
} else {
snprintf(filename, size, "%s%s%s",
log_dir_prefix, log_dir_separator, channel);
}
}
/*!
* \brief Find a particular logger channel by name
*
* \pre logchannels list is locked
*
* \param channel The name of the logger channel to find
* \retval non-NULL The corresponding logger channel
* \retval NULL Unable to find a logger channel with that particular name
*/
static struct logchannel *find_logchannel(const char *channel)
{
char filename[PATH_MAX];
struct logchannel *chan;
make_filename(channel, filename, sizeof(filename));
AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
if (!strcmp(chan->filename, filename)) {
return chan;
}
}
return NULL;
}
static struct logchannel *make_logchannel(const char *channel, const char *components, int lineno, int dynamic)
struct logchannel *chan;
char *facility;
struct ast_tm tm;
struct timeval now = ast_tvnow();
char datestring[256];
if (ast_strlen_zero(channel) || !(chan = ast_calloc(1, sizeof(*chan) + strlen(components) + 1)))
strcpy(chan->components, components);
chan->lineno = lineno;
make_filename(channel, chan->filename, sizeof(chan->filename));
Kevin P. Fleming
committed
if (!strcasecmp(channel, "console")) {
chan->type = LOGTYPE_CONSOLE;
} else if (!strncasecmp(channel, "syslog", 6)) {
/*
* syntax is:
* syslog.facility => level,level,level
*/
facility = strchr(channel, '.');
Joshua Colp
committed
if (!facility++ || !facility) {
Kevin P. Fleming
committed
facility = "local0";
}
chan->facility = ast_syslog_facility(facility);
if (chan->facility < 0) {
Kevin P. Fleming
committed
fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
Tilghman Lesher
committed
ast_free(chan);
Kevin P. Fleming
committed
return NULL;
}
Kevin P. Fleming
committed
chan->type = LOGTYPE_SYSLOG;
openlog("asterisk", LOG_PID, chan->facility);
Kevin P. Fleming
committed
} else {
if (!(chan->fileptr = fopen(chan->filename, "a"))) {
/* Can't do real logging here since we're called with a lock
* so log to any attached consoles */
ast_console_puts_mutable("ERROR: Unable to open log file '", __LOG_ERROR);
ast_console_puts_mutable(chan->filename, __LOG_ERROR);
ast_console_puts_mutable("': ", __LOG_ERROR);
ast_console_puts_mutable(strerror(errno), __LOG_ERROR);
ast_console_puts_mutable("'\n", __LOG_ERROR);
ast_free(chan);
return NULL;
} else {
/* Create our date/time */
ast_localtime(&now, &tm, NULL);
ast_strftime(datestring, sizeof(datestring), dateformat, &tm);
fprintf(chan->fileptr, "[%s] Asterisk %s built by %s @ %s on a %s running %s on %s\n",
datestring, ast_get_version(), ast_build_user, ast_build_hostname,
ast_build_machine, ast_build_os, ast_build_date);
fflush(chan->fileptr);
Kevin P. Fleming
committed
chan->type = LOGTYPE_FILE;
make_components(chan);
/* \brief Read config, setup channels.
* \param altconf Alternate configuration file to read.
*
* \pre logchannels list is write locked
*
* \retval 0 Success
* \retval -1 No config found or Failed
*/
static int init_logger_chain(const char *altconf)
struct ast_config *cfg;
struct ast_variable *var;
Tilghman Lesher
committed
const char *s;
Joshua Colp
committed
struct ast_flags config_flags = { 0 };
if (!(cfg = ast_config_load2(S_OR(altconf, "logger.conf"), "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
cfg = NULL;
Tilghman Lesher
committed
}
/* Set defaults */
hostname[0] = '\0';
display_callids = 1;
memset(&logfiles, 0, sizeof(logfiles));
logfiles.queue_log = 1;
ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
ast_copy_string(queue_log_name, QUEUELOG, sizeof(queue_log_name));
exec_after_rotate[0] = '\0';
rotatestrategy = SEQUENTIAL;
/* delete our list of log channels */
Tilghman Lesher
committed
while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list))) {
Tilghman Lesher
committed
ast_free(chan);
Tilghman Lesher
committed
}
global_logmask = 0;
Tilghman Lesher
committed
Steve Murphy
committed
errno = 0;
/* close syslog */
closelog();
Tilghman Lesher
committed
/* If no config file, we're fine, set default options. */
if (!cfg) {
if (!(chan = ast_calloc(1, sizeof(*chan) + 1))) {
fprintf(stderr, "Failed to initialize default logging\n");
return -1;
Tilghman Lesher
committed
}
chan->logmask = __LOG_WARNING | __LOG_NOTICE | __LOG_ERROR;
memcpy(&chan->formatter, &logformatter_default, sizeof(chan->formatter));
AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
global_logmask |= chan->logmask;
return -1;
Tilghman Lesher
committed
if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
if (ast_true(s)) {
if (gethostname(hostname, sizeof(hostname) - 1)) {
ast_copy_string(hostname, "unknown", sizeof(hostname));
Tilghman Lesher
committed
fprintf(stderr, "What box has no hostname???\n");
if ((s = ast_variable_retrieve(cfg, "general", "display_callids"))) {
display_callids = ast_true(s);
}
if ((s = ast_variable_retrieve(cfg, "general", "dateformat"))) {
ast_copy_string(dateformat, s, sizeof(dateformat));
Tilghman Lesher
committed
if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) {
logfiles.queue_log = ast_true(s);
Tilghman Lesher
committed
}
if ((s = ast_variable_retrieve(cfg, "general", "queue_log_to_file"))) {
logfiles.queue_log_to_file = ast_true(s);
}
if ((s = ast_variable_retrieve(cfg, "general", "queue_log_name"))) {
ast_copy_string(queue_log_name, s, sizeof(queue_log_name));
Tilghman Lesher
committed
}
if ((s = ast_variable_retrieve(cfg, "general", "queue_log_realtime_use_gmt"))) {
logfiles.queue_log_realtime_use_gmt = ast_true(s);
}
Tilghman Lesher
committed
if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate"))) {
Tilghman Lesher
committed
ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate));
Tilghman Lesher
committed
}
Tilghman Lesher
committed
if ((s = ast_variable_retrieve(cfg, "general", "rotatestrategy"))) {
Tilghman Lesher
committed
if (strcasecmp(s, "timestamp") == 0) {
Tilghman Lesher
committed
rotatestrategy = TIMESTAMP;
Tilghman Lesher
committed
} else if (strcasecmp(s, "rotate") == 0) {
Tilghman Lesher
committed
rotatestrategy = ROTATE;
Tilghman Lesher
committed
} else if (strcasecmp(s, "sequential") == 0) {
Tilghman Lesher
committed
rotatestrategy = SEQUENTIAL;
Jonathan Rose
committed
} else if (strcasecmp(s, "none") == 0) {
rotatestrategy = NONE;
Tilghman Lesher
committed
} else {
Tilghman Lesher
committed
fprintf(stderr, "Unknown rotatestrategy: %s\n", s);
Tilghman Lesher
committed
}
Tilghman Lesher
committed
} else {
if ((s = ast_variable_retrieve(cfg, "general", "rotatetimestamp"))) {
rotatestrategy = ast_true(s) ? TIMESTAMP : SEQUENTIAL;
fprintf(stderr, "rotatetimestamp option has been deprecated. Please use rotatestrategy instead.\n");
}
}
for (; var; var = var->next) {
if (!(chan = make_logchannel(var->name, var->value, var->lineno, 0))) {
/* Print error message directly to the consoles since the lock is held
* and we don't want to unlock with the list partially built */
ast_console_puts_mutable("ERROR: Unable to create log channel '", __LOG_ERROR);
ast_console_puts_mutable(var->name, __LOG_ERROR);
ast_console_puts_mutable("'\n", __LOG_ERROR);
Tilghman Lesher
committed
}
AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
global_logmask |= chan->logmask;
Tilghman Lesher
committed
if (qlog) {
fclose(qlog);
Tilghman Lesher
committed
}
return 0;
Tilghman Lesher
committed
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
void ast_child_verbose(int level, const char *fmt, ...)
{
char *msg = NULL, *emsg = NULL, *sptr, *eptr;
va_list ap, aq;
int size;
va_start(ap, fmt);
va_copy(aq, ap);
if ((size = vsnprintf(msg, 0, fmt, ap)) < 0) {
va_end(ap);
va_end(aq);
return;
}
va_end(ap);
if (!(msg = ast_malloc(size + 1))) {
va_end(aq);
return;
}
vsnprintf(msg, size + 1, fmt, aq);
va_end(aq);
if (!(emsg = ast_malloc(size * 2 + 1))) {
ast_free(msg);
return;
}
for (sptr = msg, eptr = emsg; ; sptr++) {
if (*sptr == '"') {
*eptr++ = '\\';
}
*eptr++ = *sptr;
if (*sptr == '\0') {
break;
}
}
ast_free(msg);
fprintf(stdout, "verbose \"%s\" %d\n", emsg, level);
fflush(stdout);
ast_free(emsg);
}
void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
{
va_list ap;
Tilghman Lesher
committed
struct timeval tv;
struct ast_tm tm;
char qlog_msg[8192];
int qlog_len;
Tilghman Lesher
committed
char time_str[30];
if (!logger_initialized) {
/* You are too early. We are not open yet! */
return;
}
Tilghman Lesher
committed
if (!queuelog_init) {
/* We must initialize now since someone is trying to log something. */
logger_queue_start();
Tilghman Lesher
committed
}
if (ast_check_realtime("queue_log")) {
Tilghman Lesher
committed
tv = ast_tvnow();
ast_localtime(&tv, &tm, logfiles.queue_log_realtime_use_gmt ? "GMT" : NULL);
Tilghman Lesher
committed
ast_strftime(time_str, sizeof(time_str), "%F %T.%6q", &tm);
vsnprintf(qlog_msg, sizeof(qlog_msg), fmt, ap);
Tilghman Lesher
committed
if (logfiles.queue_adaptive_realtime) {
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(data)[5];
);
AST_NONSTANDARD_APP_ARGS(args, qlog_msg, '|');
/* Ensure fields are large enough to receive data */
ast_realtime_require_field("queue_log",
"data1", RQ_CHAR, strlen(S_OR(args.data[0], "")),
Tilghman Lesher
committed
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
"data2", RQ_CHAR, strlen(S_OR(args.data[1], "")),
"data3", RQ_CHAR, strlen(S_OR(args.data[2], "")),
"data4", RQ_CHAR, strlen(S_OR(args.data[3], "")),
"data5", RQ_CHAR, strlen(S_OR(args.data[4], "")),
SENTINEL);
/* Store the log */
ast_store_realtime("queue_log", "time", time_str,
"callid", callid,
"queuename", queuename,
"agent", agent,
"event", event,
"data1", S_OR(args.data[0], ""),
"data2", S_OR(args.data[1], ""),
"data3", S_OR(args.data[2], ""),
"data4", S_OR(args.data[3], ""),
"data5", S_OR(args.data[4], ""),
SENTINEL);
} else {
ast_store_realtime("queue_log", "time", time_str,
"callid", callid,
"queuename", queuename,
"agent", agent,
"event", event,
"data", qlog_msg,
SENTINEL);
}
if (!logfiles.queue_log_to_file) {
return;
}
Tilghman Lesher
committed
}
if (qlog) {
va_start(ap, fmt);
qlog_len = snprintf(qlog_msg, sizeof(qlog_msg), "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
vsnprintf(qlog_msg + qlog_len, sizeof(qlog_msg) - qlog_len, fmt, ap);
va_end(ap);
AST_RWLIST_RDLOCK(&logchannels);
if (qlog) {
fprintf(qlog, "%s\n", qlog_msg);
fflush(qlog);
}
AST_RWLIST_UNLOCK(&logchannels);
}
Tilghman Lesher
committed
static int rotate_file(const char *filename)
{
char old[PATH_MAX];
char new[PATH_MAX];
int x, y, which, found, res = 0, fd;
char *suffixes[4] = { "", ".gz", ".bz2", ".Z" };
switch (rotatestrategy) {
Jonathan Rose
committed
case NONE:
/* No rotation */
break;
Tilghman Lesher
committed
case SEQUENTIAL:
for (x = 0; ; x++) {
snprintf(new, sizeof(new), "%s.%d", filename, x);
fd = open(new, O_RDONLY);
if (fd > -1)
close(fd);
else
break;
}
if (rename(filename, new)) {
fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
res = -1;
Tilghman Lesher
committed
} else {
filename = new;
Tilghman Lesher
committed
}
break;
case TIMESTAMP:
snprintf(new, sizeof(new), "%s.%ld", filename, (long)time(NULL));
if (rename(filename, new)) {
fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
res = -1;
Tilghman Lesher
committed
} else {
filename = new;
Tilghman Lesher
committed
}
break;
case ROTATE:
/* Find the next empty slot, including a possible suffix */
for (x = 0; ; x++) {
found = 0;
for (which = 0; which < ARRAY_LEN(suffixes); which++) {
Tilghman Lesher
committed
snprintf(new, sizeof(new), "%s.%d%s", filename, x, suffixes[which]);
fd = open(new, O_RDONLY);
Tilghman Lesher
committed
close(fd);
found = 1;
break;
}
}
Tilghman Lesher
committed
break;
Tilghman Lesher
committed
}
/* Found an empty slot */
for (which = 0; which < ARRAY_LEN(suffixes); which++) {
Tilghman Lesher
committed
snprintf(old, sizeof(old), "%s.%d%s", filename, y - 1, suffixes[which]);
fd = open(old, O_RDONLY);
if (fd > -1) {
/* Found the right suffix */
close(fd);
snprintf(new, sizeof(new), "%s.%d%s", filename, y, suffixes[which]);
if (rename(old, new)) {
fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
res = -1;
}
break;
}
}
}
/* Finally, rename the current file */
snprintf(new, sizeof(new), "%s.0", filename);
if (rename(filename, new)) {
fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
res = -1;
} else {
filename = new;
Tilghman Lesher
committed
}
}
if (!ast_strlen_zero(exec_after_rotate)) {
struct ast_channel *c = ast_dummy_channel_alloc();
Steve Murphy
committed
char buf[512];
Tilghman Lesher
committed
pbx_builtin_setvar_helper(c, "filename", filename);
pbx_substitute_variables_helper(c, exec_after_rotate, buf, sizeof(buf));
if (c) {
c = ast_channel_unref(c);
}
if (ast_safe_system(buf) == -1) {
ast_log(LOG_WARNING, "error executing '%s'\n", buf);
Kevin P. Fleming
committed
}
Tilghman Lesher
committed
}
return res;
}
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*!
* \internal
* \brief Start the realtime queue logging if configured.
*
* \retval TRUE if not to open queue log file.
*/
static int logger_queue_rt_start(void)
{
if (ast_check_realtime("queue_log")) {
if (!ast_realtime_require_field("queue_log",
"time", RQ_DATETIME, 26,
"data1", RQ_CHAR, 20,
"data2", RQ_CHAR, 20,
"data3", RQ_CHAR, 20,
"data4", RQ_CHAR, 20,
"data5", RQ_CHAR, 20,
SENTINEL)) {
logfiles.queue_adaptive_realtime = 1;
} else {
logfiles.queue_adaptive_realtime = 0;
}
if (!logfiles.queue_log_to_file) {
/* Don't open the log file. */
return 1;
}
}
return 0;
}
/*!
* \internal