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_FILE_VERSION(__FILE__, "$Revision$")
/* 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"
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; /* Used to assign unique call_ids to calls */
static int display_callids;
static void unique_callid_cleanup(void *data);
static int logger_queue_size;
static int logger_queue_limit = 1000;
static int logger_messages_discarded;
static unsigned int high_water_alert;
struct ast_callid {
int call_identifier; /* Numerical value of the call displayed in the logs */
};
AST_THREADSTORAGE_CUSTOM(unique_callid, NULL, unique_callid_cleanup);
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);
enum logtypes {
LOGTYPE_SYSLOG,
LOGTYPE_FILE,
LOGTYPE_CONSOLE,
};
/*! 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;
struct ast_callid *callid;
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)
{
if (msg->callid) {
ast_callid_unref(msg->callid);
}
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,
216
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
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
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;
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;
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
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
358
/*!
* \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 locked The logchannels list is locked and this is a reload
* \param altconf Alternate configuration file to read.
*
* \retval 0 Success
* \retval -1 No config found or Failed
*/
static int init_logger_chain(int locked, 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
}
Tilghman Lesher
committed
if (!locked) {
AST_RWLIST_WRLOCK(&logchannels);
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
if (!locked) {
AST_RWLIST_UNLOCK(&logchannels);
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 = (1 << __LOG_WARNING) | (1 << __LOG_NOTICE) | (1 << __LOG_ERROR);
Tilghman Lesher
committed
if (!locked) {
AST_RWLIST_WRLOCK(&logchannels);
Tilghman Lesher
committed
}
AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
global_logmask |= chan->logmask;
Tilghman Lesher
committed
if (!locked) {
AST_RWLIST_UNLOCK(&logchannels);
Tilghman Lesher
committed
}
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");
}
}
if ((s = ast_variable_retrieve(cfg, "general", "logger_queue_limit"))) {
if (sscanf(s, "%30d", &logger_queue_limit) != 1) {
fprintf(stderr, "logger_queue_limit has an invalid value. Leaving at default of %d.\n",
logger_queue_limit);
}
if (logger_queue_limit < 10) {
fprintf(stderr, "logger_queue_limit must be >= 10. Setting to 10.\n");
logger_queue_limit = 10;
}
}
Tilghman Lesher
committed
if (!locked) {
AST_RWLIST_WRLOCK(&logchannels);
Tilghman Lesher
committed
}
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
}
if (!locked) {
AST_RWLIST_UNLOCK(&logchannels);
Tilghman Lesher
committed
}
return 0;
Tilghman Lesher
committed
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
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
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
"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;
}
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
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
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
/*!
* \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
* \brief Rotate the queue log file and restart.
*
* \param queue_rotate Log queue rotation mode.
*
* \note Assumes logchannels is write locked on entry.
*
* \retval 0 on success.
* \retval -1 on error.
*/
static int logger_queue_restart(int queue_rotate)
{
int res = 0;
char qfname[PATH_MAX];
if (logger_queue_rt_start()) {
return res;
}
snprintf(qfname, sizeof(qfname), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
if (qlog) {
/* Just in case it was still open. */
fclose(qlog);
qlog = NULL;
}
if (queue_rotate) {
rotate_file(qfname);
}
/* Open the log file. */
qlog = fopen(qfname, "a");
if (!qlog) {
ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
res = -1;
}
return res;
}
static int reload_logger(int rotate, const char *altconf)
int queue_rotate = rotate;
Tilghman Lesher
committed
int res = 0;
AST_RWLIST_WRLOCK(&logchannels);
Tilghman Lesher
committed
if (qlog) {
if (rotate < 0) {
/* Check filesize - this one typically doesn't need an auto-rotate */
Tilghman Lesher
committed
if (ftello(qlog) > 0x40000000) { /* Arbitrarily, 1 GB */
Tilghman Lesher
committed
fclose(qlog);
qlog = NULL;
Tilghman Lesher
committed
} else {
queue_rotate = 0;
Tilghman Lesher
committed
}
Tilghman Lesher
committed
} else {
fclose(qlog);
qlog = NULL;
}
Tilghman Lesher
committed
} else {
Tilghman Lesher
committed
}
Tilghman Lesher
committed
ast_mkdir(ast_config_AST_LOG_DIR, 0777);
AST_RWLIST_TRAVERSE(&logchannels, f, list) {
if (f->disabled) {
f->disabled = 0; /* Re-enable logging at reload */
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a logging channel is re-enabled after a reload operation.</synopsis>
<syntax>
<parameter name="Channel">
<para>The name of the logging channel.</para>
</parameter>
</syntax>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
}
if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
Tilghman Lesher
committed
int rotate_this = 0;
Jonathan Rose
committed
if (rotatestrategy != NONE && ftello(f->fileptr) > 0x40000000) { /* Arbitrarily, 1 GB */
Tilghman Lesher
committed
/* Be more proactive about rotating massive log files */
rotate_this = 1;
}
fclose(f->fileptr); /* Close file */
Tilghman Lesher
committed
if (rotate || rotate_this) {
Tilghman Lesher
committed
rotate_file(f->filename);
Tilghman Lesher
committed
}
Kevin P. Fleming
committed
filesize_reload_needed = 0;
Tilghman Lesher
committed
init_logger_chain(1 /* locked */, altconf);
ast_unload_realtime("queue_log");
res = logger_queue_restart(queue_rotate);
AST_RWLIST_UNLOCK(&logchannels);
ast_verb_update();
ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
ast_verb(1, "Asterisk Queue Logger restarted\n");
} else {
AST_RWLIST_UNLOCK(&logchannels);
ast_verb_update();
/*! \brief Reload the logger module without rotating log files (also used from loader.c during
a full Asterisk reload) */
int logger_reload(void)
{
if (reload_logger(0, NULL)) {
return RESULT_SUCCESS;
}
static char *handle_logger_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
switch (cmd) {
case CLI_INIT:
e->command = "logger reload";
e->usage =
"Usage: logger reload [<alt-conf>]\n"
" Reloads the logger subsystem state. Use after restarting syslogd(8) if you are using syslog logging.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (reload_logger(0, a->argc == 3 ? a->argv[2] : NULL)) {
ast_cli(a->fd, "Failed to reload the logger\n");
return CLI_FAILURE;
static char *handle_logger_rotate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
switch (cmd) {
case CLI_INIT:
e->command = "logger rotate";
"Usage: logger rotate\n"
" Rotates and Reopens the log files.\n";
return NULL;
case CLI_GENERATE:
if (reload_logger(1, NULL)) {
ast_cli(a->fd, "Failed to reload the logger and rotate log files\n");
return CLI_FAILURE;
int ast_logger_rotate()
{
return reload_logger(1, NULL);
}