Index: include/asterisk/_private.h =================================================================== --- include/asterisk/_private.h (revision 272925) +++ include/asterisk/_private.h (working copy) @@ -18,6 +18,7 @@ int load_modules(unsigned int); /*!< Provided by loader.c */ int load_pbx(void); /*!< Provided by pbx.c */ int init_logger(void); /*!< Provided by logger.c */ +void ast_logger_init2(void); /*!< Provided by logger.c */ void close_logger(void); /*!< Provided by logger.c */ int init_framer(void); /*!< Provided by frame.c */ int ast_term_init(void); /*!< Provided by term.c */ Index: include/asterisk/module.h =================================================================== --- include/asterisk/module.h (revision 272925) +++ include/asterisk/module.h (working copy) @@ -171,6 +171,12 @@ */ char *ast_module_helper(const char *line, const char *word, int pos, int state, int rpos, int needsreload); +/*! + * \brief Requests a module be loaded at preload time. + * \note If not run prior to the time that preload is complete, this function does nothing. + */ +void ast_module_preload(const char *resource, int required); + /* Opaque type for module handles generated by the loader */ struct ast_module; Index: main/config.c =================================================================== --- main/config.c (revision 272925) +++ main/config.c (working copy) @@ -47,6 +47,7 @@ #include "asterisk/app.h" #include "asterisk/astobj2.h" #include "asterisk/strings.h" /* for the ast_str_*() API */ +#include "asterisk/module.h" #define MAX_NESTED_COMMENTS 128 #define COMMENT_START ";--" @@ -1861,7 +1862,11 @@ { struct ast_config_map *map; int length; + char tmp[256]; + snprintf(tmp, sizeof(tmp), "res_config_%s", driver); + ast_module_preload(tmp, 0); + length = sizeof(*map); length += strlen(name) + 1; length += strlen(driver) + 1; Index: main/loader.c =================================================================== --- main/loader.c (revision 272925) +++ main/loader.c (working copy) @@ -867,7 +867,7 @@ AST_LIST_ENTRY(load_order_entry) entry; }; -AST_LIST_HEAD_NOLOCK(load_order, load_order_entry); +AST_LIST_HEAD_NOLOCK(load_order, load_order_entry) global_load_order = AST_LIST_HEAD_NOLOCK_INIT_VALUE; static struct load_order_entry *add_to_load_order(const char *resource, struct load_order *load_order, int required) { @@ -892,6 +892,14 @@ return order; } +void ast_module_preload(const char *name, int required) +{ + if (ast_fully_booted) { + return; + } + add_to_load_order(name, &global_load_order, required); +} + static int mod_load_cmp(void *a, void *b) { struct ast_module *a_mod = (struct ast_module *) a; @@ -1003,6 +1011,13 @@ embedded_module_list.first = NULL; } + /* First time, modules requested by the core; second time, modules requested by the preloaded modules */ + while ((order = AST_LIST_REMOVE_HEAD(&global_load_order, entry))) { + add_to_load_order(order->resource, &load_order, order->required); + ast_free(order->resource); + ast_free(order); + } + cfg = ast_config_load2(AST_MODULE_CONFIG, "" /* core, can't reload */, config_flags); if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) { ast_log(LOG_WARNING, "No '%s' found, no modules will be loaded.\n", AST_MODULE_CONFIG); Index: main/asterisk.c =================================================================== --- main/asterisk.c (revision 272926) +++ main/asterisk.c (working copy) @@ -3672,6 +3672,8 @@ exit(moduleresult == -2 ? 2 : 1); } + ast_logger_init2(); + if (dnsmgr_init()) { /* Initialize the DNS manager */ printf("%s", term_quit()); exit(1); Index: main/logger.c =================================================================== --- main/logger.c (revision 272925) +++ main/logger.c (working copy) @@ -83,6 +83,8 @@ static struct { unsigned int queue_log:1; + unsigned int queue_log_to_file:1; + unsigned int queue_adaptive_realtime:1; } logfiles = { 1 }; static char hostname[MAXHOSTNAMELEN]; @@ -291,41 +293,49 @@ const char *s; struct ast_flags config_flags = { 0 }; - if (!(cfg = ast_config_load2("logger.conf", "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) + if (!(cfg = ast_config_load2("logger.conf", "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) { return; + } /* delete our list of log channels */ - if (!locked) + if (!locked) { AST_RWLIST_WRLOCK(&logchannels); - while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list))) + } + while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list))) { ast_free(chan); + } global_logmask = 0; - if (!locked) + if (!locked) { AST_RWLIST_UNLOCK(&logchannels); - + } + errno = 0; /* close syslog */ closelog(); - + /* If no config file, we're fine, set default options. */ if (!cfg) { - if (errno) + if (errno) { fprintf(stderr, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno)); - else + } else { fprintf(stderr, "Errors detected in logger.conf: see above; default settings will be used.\n"); - if (!(chan = ast_calloc(1, sizeof(*chan)))) + } + if (!(chan = ast_calloc(1, sizeof(*chan)))) { return; + } chan->type = LOGTYPE_CONSOLE; chan->logmask = __LOG_WARNING | __LOG_NOTICE | __LOG_ERROR; - if (!locked) + if (!locked) { AST_RWLIST_WRLOCK(&logchannels); + } AST_RWLIST_INSERT_HEAD(&logchannels, chan, list); global_logmask |= chan->logmask; - if (!locked) + if (!locked) { AST_RWLIST_UNLOCK(&logchannels); + } return; } - + if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) { if (ast_true(s)) { if (gethostname(hostname, sizeof(hostname) - 1)) { @@ -340,21 +350,28 @@ ast_copy_string(dateformat, s, sizeof(dateformat)); else ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat)); - if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) + if ((s = ast_variable_retrieve(cfg, "general", "queue_log"))) { logfiles.queue_log = ast_true(s); - if ((s = ast_variable_retrieve(cfg, "general", "queue_log_name"))) + } + 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)); - if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate"))) + } + if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate"))) { ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate)); + } if ((s = ast_variable_retrieve(cfg, "general", "rotatestrategy"))) { - if (strcasecmp(s, "timestamp") == 0) + if (strcasecmp(s, "timestamp") == 0) { rotatestrategy = TIMESTAMP; - else if (strcasecmp(s, "rotate") == 0) + } else if (strcasecmp(s, "rotate") == 0) { rotatestrategy = ROTATE; - else if (strcasecmp(s, "sequential") == 0) + } else if (strcasecmp(s, "sequential") == 0) { rotatestrategy = SEQUENTIAL; - else + } else { fprintf(stderr, "Unknown rotatestrategy: %s\n", s); + } } else { if ((s = ast_variable_retrieve(cfg, "general", "rotatetimestamp"))) { rotatestrategy = ast_true(s) ? TIMESTAMP : SEQUENTIAL; @@ -362,17 +379,27 @@ } } - if (!locked) + if (!locked) { AST_RWLIST_WRLOCK(&logchannels); + } var = ast_variable_browse(cfg, "logfiles"); for (; var; var = var->next) { - if (!(chan = make_logchannel(var->name, var->value, var->lineno))) + if (!(chan = make_logchannel(var->name, var->value, var->lineno))) { continue; + } AST_RWLIST_INSERT_HEAD(&logchannels, chan, list); global_logmask |= chan->logmask; } - if (!locked) + if (qlog) { + char tmp[4096]; + fclose(qlog); + snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name); + qlog = fopen(tmp, "a"); + } + + if (!locked) { AST_RWLIST_UNLOCK(&logchannels); + } ast_config_destroy(cfg); } @@ -429,29 +456,64 @@ void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...) { va_list ap; + struct timeval tv; + struct ast_tm tm; char qlog_msg[8192]; int qlog_len; - char time_str[16]; + char time_str[30]; if (ast_check_realtime("queue_log")) { + tv = ast_tvnow(); + ast_localtime(&tv, &tm, NULL); + ast_strftime(time_str, sizeof(time_str), "%F %T.%6q", &tm); va_start(ap, fmt); vsnprintf(qlog_msg, sizeof(qlog_msg), fmt, ap); va_end(ap); - snprintf(time_str, sizeof(time_str), "%ld", (long)time(NULL)); - ast_store_realtime("queue_log", "time", time_str, - "callid", callid, - "queuename", queuename, - "agent", agent, - "event", event, - "data", qlog_msg, - SENTINEL); - } else { - 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); + 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], "")), + "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; + } + } + + 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); @@ -481,6 +543,8 @@ if (rename(filename, new)) { fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new); res = -1; + } else { + filename = new; } break; case TIMESTAMP: @@ -488,6 +552,8 @@ if (rename(filename, new)) { fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new); res = -1; + } else { + filename = new; } break; case ROTATE: @@ -553,25 +619,25 @@ int queue_rotate = rotate; struct logchannel *f; int res = 0; - struct stat st; AST_RWLIST_WRLOCK(&logchannels); if (qlog) { if (rotate < 0) { /* Check filesize - this one typically doesn't need an auto-rotate */ - snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name); - if (stat(old, &st) != 0 || st.st_size > 0x40000000) { /* Arbitrarily, 1 GB */ + if (ftello(qlog) > 0x40000000) { /* Arbitrarily, 1 GB */ fclose(qlog); qlog = NULL; - } else + } else { queue_rotate = 0; + } } else { fclose(qlog); qlog = NULL; } - } else + } else { queue_rotate = 0; + } ast_mkdir(ast_config_AST_LOG_DIR, 0777); @@ -581,10 +647,16 @@ manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename); } if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) { + int rotate_this = 0; + if (ftello(f->fileptr) > 0x40000000) { /* Arbitrarily, 1 GB */ + /* Be more proactive about rotating massive log files */ + rotate_this = 1; + } fclose(f->fileptr); /* Close file */ f->fileptr = NULL; - if (rotate) + if (rotate || rotate_this) { rotate_file(f->filename); + } } } @@ -593,20 +665,42 @@ init_logger_chain(1 /* locked */); if (logfiles.queue_log) { - snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name); - if (queue_rotate) - rotate_file(old); + do { + ast_unload_realtime("queue_log"); + 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; + } - qlog = fopen(old, "a"); - if (qlog) { - AST_RWLIST_UNLOCK(&logchannels); - ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", ""); - AST_RWLIST_WRLOCK(&logchannels); - ast_verb(1, "Asterisk Queue Logger restarted\n"); - } else { - ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno)); - res = -1; - } + if (!logfiles.queue_log_to_file) { + /* Skip the following section */ + break; + } + } + + fclose(qlog); + qlog = NULL; + snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name); + if (queue_rotate) { + rotate_file(old); + } + + qlog = fopen(old, "a"); + if (qlog) { + AST_RWLIST_UNLOCK(&logchannels); + ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", ""); + AST_RWLIST_WRLOCK(&logchannels); + ast_verb(1, "Asterisk Queue Logger restarted\n"); + } else { + ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno)); + res = -1; + } + } while (0); } AST_RWLIST_UNLOCK(&logchannels); @@ -927,11 +1021,26 @@ return NULL; } +void ast_logger_init2(void) +{ + /* Preloaded modules are up. */ + ast_unload_realtime("queue_log"); + if (logfiles.queue_log && 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; + } + } + + ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", ""); +} + int init_logger(void) { - char tmp[256]; - int res = 0; - /* auto rotate if sig SIGXFSZ comes a-knockin */ sigaction(SIGXFSZ, &handle_SIGXFSZ, NULL); @@ -946,16 +1055,11 @@ ast_cli_register_multiple(cli_logger, ARRAY_LEN(cli_logger)); ast_mkdir(ast_config_AST_LOG_DIR, 0777); - + /* create log channels */ init_logger_chain(0 /* locked */); - if (logfiles.queue_log) { - snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name); - qlog = fopen(tmp, "a"); - ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", ""); - } - return res; + return 0; } void close_logger(void) Index: configs/logger.conf.sample =================================================================== --- configs/logger.conf.sample (revision 272925) +++ configs/logger.conf.sample (working copy) @@ -26,18 +26,27 @@ ; (defaults to yes). ;queue_log = no ; +; Determines whether the queue_log always goes to a file, even +; when a realtime backend is present (defaults to no). +;queue_log_to_file = yes +; ; Set the queue_log filename ; (defaults to queue_log) ;queue_log_name = queue_log ; ; Log rotation strategy: ; sequential: Rename archived logs in order, such that the newest -; has the highest sequence number [default]. +; has the highest sequence number [default]. When +; exec_after_rotate is set, ${filename} will specify +; the new archived logfile. ; rotate: Rotate all the old files, such that the oldest has the ; highest sequence number [this is the expected behavior -; for Unix administrators]. +; for Unix administrators]. When exec_after_rotate is +; set, ${filename} will specify the original root filename. ; timestamp: Rename the logfiles using a timestamp instead of a ; sequence number when "logger rotate" is executed. +; When exec_after_rotate is set, ${filename} will +; specify the new archived logfile. ;rotatestrategy = rotate ; ; Run a system command after rotating the files. This is mainly Index: res/res_config_pgsql.c =================================================================== --- res/res_config_pgsql.c (revision 272925) +++ res/res_config_pgsql.c (working copy) @@ -1171,6 +1171,9 @@ } else if (strncmp(column->type, "float", 5) == 0 && !ast_rq_is_int(type) && type != RQ_FLOAT) { ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type); res = -1; + } else if (strncmp(column->type, "timestamp", 9) == 0 && type != RQ_DATETIME) { + ast_log(LOG_WARNING, "Column %s cannot be a %s\n", column->name, column->type); + res = -1; } else { /* There are other types that no module implements yet */ ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name); res = -1;