Index: main/cdr.c =================================================================== --- main/cdr.c (revision 82750) +++ main/cdr.c (working copy) @@ -118,7 +118,7 @@ } } - if (!(i = ast_calloc(1, sizeof(*i)))) + if (!(i = ast_calloc(1, sizeof(struct ast_cdr_beitem)))) return -1; i->be = be; @@ -146,25 +146,25 @@ /*! unregister a CDR driver */ void ast_cdr_unregister(const char *name, const char *name_detail) { - struct ast_cdr_beitem *i = NULL; + struct ast_cdr_beitem *item = NULL; AST_RWLIST_WRLOCK(&be_list); - AST_RWLIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) { - if (!strcasecmp(name, i->name) && (!name_detail || !strcasecmp(name_detail, i->name_detail))) { - i->cancel_thread = 1; + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&be_list, item, list) { + if (!strcasecmp(name, item->name) && (!name_detail || !strcasecmp(name_detail, item->name_detail))) { + item->cancel_thread = 1; /* signal the thread so it can exit */ - ast_cond_signal(&i->cdr_pending_cond); + ast_cond_signal(&item->cdr_pending_cond); /* wait for thread to exit so we can clean up */ - pthread_join(i->cdr_thread, NULL); - i->cdr_thread = AST_PTHREADT_NULL; - ast_cond_destroy(&i->cdr_pending_cond); - ast_cond_destroy(&i->cdr_retry_cond); + pthread_join(item->cdr_thread, NULL); + item->cdr_thread = AST_PTHREADT_NULL; + ast_cond_destroy(&item->cdr_pending_cond); + ast_cond_destroy(&item->cdr_retry_cond); AST_RWLIST_REMOVE_CURRENT(&be_list, list); if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s - %s' CDR backend\n", i->name, i->name_detail); - if (i->cleanup) - i->cleanup(i->be_data); - ast_free(i); + ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s - %s' CDR backend\n", item->name, item->name_detail); + if (item->cleanup) + item->cleanup(item->be_data); + ast_free(item); if (name_detail) break; } Index: configs/cdr_custom.conf.sample =================================================================== --- configs/cdr_custom.conf.sample (revision 82750) +++ configs/cdr_custom.conf.sample (working copy) @@ -4,7 +4,8 @@ ; to get your csv output in a format tailored to your liking, uncomment the following ; and look for the output in the cdr-custom/Master.csv file (usually in /var/log/asterisk). ; -; + ;[mappings] ;Master.csv => "${CDR(clid)}","${CDR(src)}","${CDR(dst)}","${CDR(dcontext)}","${CDR(channel)}","${CDR(dstchannel)}","${CDR(lastapp)}","${CDR(lastdata)}","${CDR(start)}","${CDR(answer)}","${CDR(end)}","${CDR(duration)}","${CDR(billsec)}","${CDR(disposition)}","${CDR(amaflags)}","${CDR(accountcode)}","${CDR(uniqueid)}","${CDR(userfield)}" +;MasterSimple.csv => "${CDR(src)}","${CDR(dst)}" Index: cdr/cdr_custom.c =================================================================== --- cdr/cdr_custom.c (revision 82750) +++ cdr/cdr_custom.c (working copy) @@ -51,45 +51,83 @@ #include "asterisk/logger.h" #include "asterisk/utils.h" -#define CUSTOM_LOG_DIR "/cdr_custom" +#define CUSTOM_LOG_DIR "cdr-custom" -#define DATE_FORMAT "%Y-%m-%d %T" +#define DATE_FORMAT "%Y-%m-%d %T" +#define FORMAT_MAX 1024 +#define DEFAULT_BUFFER_SIZE 2048 AST_MUTEX_DEFINE_STATIC(lock); static char *name = "cdr-custom"; -static FILE *mf = NULL; +struct cdr_custom_sink +{ + char filename[PATH_MAX]; + char format[FORMAT_MAX]; -static char master[PATH_MAX]; -static char format[1024]=""; + FILE *output; +}; +/* Prototypes */ +static int custom_log(struct ast_cdr *cdr, void *data); + +static void free_config(void *data) +{ + struct cdr_custom_sink *sink = (struct cdr_custom_sink *) data; + + if (!sink) + return; + + if (sink->output) + fclose(sink->output); + + ast_free(sink); +} + static int load_config(int reload) { struct ast_config *cfg; struct ast_variable *var; struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + struct cdr_custom_sink *sink = NULL; int res = -1; if ((cfg = ast_config_load("cdr_custom.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) return 0; - strcpy(format, ""); - strcpy(master, ""); + if (reload) + ast_cdr_unregister(name, NULL); + ast_mutex_lock(&lock); if (cfg) { var = ast_variable_browse(cfg, "mappings"); while(var) { if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) { - if (strlen(var->value) > (sizeof(format) - 1)) + if (strlen(var->value) > (FORMAT_MAX - 1)) ast_log(LOG_WARNING, "Format string too long, will be truncated, at line %d\n", var->lineno); - ast_copy_string(format, var->value, sizeof(format) - 1); - strcat(format,"\n"); - snprintf(master, sizeof(master),"%s/%s/%s", ast_config_AST_LOG_DIR, name, var->name); - if (var->next) { - ast_log(LOG_NOTICE, "Sorry, only one mapping is supported at this time, mapping '%s' will be ignored at line %d.\n", var->next->name, var->next->lineno); + sink = (struct cdr_custom_sink *) ast_calloc(1, sizeof(struct cdr_custom_sink)); + if (!sink) { + ast_log(LOG_ERROR, "Unable to allocate CDR custom configuration.\n"); break; } + ast_copy_string(sink->format, var->value, FORMAT_MAX - 1); + strcat(sink->format, "\n"); + snprintf(sink->filename, PATH_MAX, "%s/%s/%s", ast_config_AST_LOG_DIR, CUSTOM_LOG_DIR, var->name); + /* Try to open the file */ + sink->output = fopen(sink->filename, "a"); + if (!sink->output) { + ast_log(LOG_ERROR, "Unable to open CDR log file \"%s\": %s\n", sink->filename, strerror(errno)); + ast_free(sink); + } else { + fclose(sink->output); + sink->output = NULL; + res = ast_cdr_register(name, var->name, ast_module_info->description, custom_log, free_config, sink); + if (res) { + ast_log(LOG_ERROR, "Failed to register CDR custom handler sink \"%s\" at line %d\n", var->name, var->lineno); + ast_free(sink); + } + } } else ast_log(LOG_NOTICE, "Mapping must have both filename and format at line %d\n", var->lineno); var = var->next; @@ -107,62 +145,53 @@ return res; } - - static int custom_log(struct ast_cdr *cdr, void *data) { /* Make sure we have a big enough buf */ - char buf[2048]; + char buf[DEFAULT_BUFFER_SIZE]; struct ast_channel dummy; + struct cdr_custom_sink *sink = (struct cdr_custom_sink *) data; /* Abort if no master file is specified */ - if (ast_strlen_zero(master)) + if (ast_strlen_zero(sink->filename)) return 0; - memset(buf, 0 , sizeof(buf)); + memset(buf, 0 , DEFAULT_BUFFER_SIZE); /* Quite possibly the first use of a static struct ast_channel, we need it so the var funcs will work */ memset(&dummy, 0, sizeof(dummy)); dummy.cdr = cdr; - pbx_substitute_variables_helper(&dummy, format, buf, sizeof(buf) - 1); + pbx_substitute_variables_helper(&dummy, sink->format, buf, DEFAULT_BUFFER_SIZE - 1); /* because of the absolutely unconditional need for the highest reliability possible in writing billing records, we open write and close the log file each time */ - mf = fopen(master, "a"); - if (!mf) { - ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", master, strerror(errno)); + sink->output = fopen(sink->filename, "a"); + if (!sink->output) { + ast_log(LOG_ERROR, "Unable to re-open CDR custom file %s : %s\n", sink->filename, strerror(errno)); return AST_CDR_POST_RETRY; + } else { + fputs(buf, sink->output); + fflush(sink->output); /* be particularly anal here */ + fclose(sink->output); + sink->output = NULL; } - if (mf) { - fputs(buf, mf); - fflush(mf); /* be particularly anal here */ - fclose(mf); - mf = NULL; - } + return AST_CDR_POST_OK; } static int unload_module(void) { - if (mf) - fclose(mf); ast_cdr_unregister(name, NULL); + return 0; } static int load_module(void) { - int res = 0; + if (!load_config(0)) + return AST_MODULE_LOAD_SUCCESS; - if (!load_config(0)) { - res = ast_cdr_register(name, NULL, ast_module_info->description, custom_log, NULL, NULL); - if (res) - ast_log(LOG_ERROR, "Unable to register custom CDR handling\n"); - if (mf) - fclose(mf); - return res; - } else - return AST_MODULE_LOAD_DECLINE; + return AST_MODULE_LOAD_DECLINE; } static int reload(void)