00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include "asterisk.h"
00034
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 278132 $")
00036
00037 #include <time.h>
00038
00039 #include "asterisk/paths.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/cdr.h"
00042 #include "asterisk/module.h"
00043 #include "asterisk/config.h"
00044 #include "asterisk/pbx.h"
00045 #include "asterisk/utils.h"
00046 #include "asterisk/lock.h"
00047 #include "asterisk/threadstorage.h"
00048 #include "asterisk/strings.h"
00049
00050 #define CUSTOM_LOG_DIR "/cdr_custom"
00051 #define CONFIG "cdr_custom.conf"
00052
00053 AST_THREADSTORAGE(custom_buf);
00054
00055 static const char name[] = "cdr-custom";
00056
00057 struct cdr_config {
00058 AST_DECLARE_STRING_FIELDS(
00059 AST_STRING_FIELD(filename);
00060 AST_STRING_FIELD(format);
00061 );
00062 ast_mutex_t lock;
00063 AST_RWLIST_ENTRY(cdr_config) list;
00064 };
00065
00066 static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
00067
00068 static void free_config(void)
00069 {
00070 struct cdr_config *sink;
00071 while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
00072 ast_mutex_destroy(&sink->lock);
00073 ast_free(sink);
00074 }
00075 }
00076
00077 static int load_config(void)
00078 {
00079 struct ast_config *cfg;
00080 struct ast_variable *var;
00081 struct ast_flags config_flags = { 0 };
00082 int res = 0;
00083
00084 cfg = ast_config_load(CONFIG, config_flags);
00085 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
00086 ast_log(LOG_ERROR, "Unable to load " CONFIG ". Not logging custom CSV CDRs.\n");
00087 return -1;
00088 }
00089
00090 var = ast_variable_browse(cfg, "mappings");
00091 while (var) {
00092 if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) {
00093 struct cdr_config *sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
00094
00095 if (!sink) {
00096 ast_log(LOG_ERROR, "Unable to allocate memory for configuration settings.\n");
00097 res = -2;
00098 break;
00099 }
00100
00101 ast_string_field_build(sink, format, "%s\n", var->value);
00102 ast_string_field_build(sink, filename, "%s/%s/%s", ast_config_AST_LOG_DIR, name, var->name);
00103 ast_mutex_init(&sink->lock);
00104
00105 AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
00106 } else {
00107 ast_log(LOG_NOTICE, "Mapping must have both a filename and a format at line %d\n", var->lineno);
00108 }
00109 var = var->next;
00110 }
00111 ast_config_destroy(cfg);
00112
00113 return res;
00114 }
00115
00116 static int custom_log(struct ast_cdr *cdr)
00117 {
00118 struct ast_channel *dummy;
00119 struct ast_str *str;
00120 struct cdr_config *config;
00121
00122
00123 if (!(str = ast_str_thread_get(&custom_buf, 16))) {
00124 return -1;
00125 }
00126
00127 dummy = ast_dummy_channel_alloc();
00128
00129 if (!dummy) {
00130 ast_log(LOG_ERROR, "Unable to allocate channel for variable subsitution.\n");
00131 return -1;
00132 }
00133
00134
00135
00136
00137 dummy->cdr = ast_cdr_dup(cdr);
00138
00139 AST_RWLIST_RDLOCK(&sinks);
00140
00141 AST_LIST_TRAVERSE(&sinks, config, list) {
00142 FILE *out;
00143
00144 ast_str_substitute_variables(&str, 0, dummy, config->format);
00145
00146
00147
00148
00149
00150 ast_mutex_lock(&config->lock);
00151
00152
00153
00154
00155 if ((out = fopen(config->filename, "a"))) {
00156 fputs(ast_str_buffer(str), out);
00157 fflush(out);
00158 fclose(out);
00159 } else {
00160 ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", config->filename, strerror(errno));
00161 }
00162
00163 ast_mutex_unlock(&config->lock);
00164 }
00165
00166 AST_RWLIST_UNLOCK(&sinks);
00167
00168 ast_channel_release(dummy);
00169
00170 return 0;
00171 }
00172
00173 static int unload_module(void)
00174 {
00175 ast_cdr_unregister(name);
00176
00177 if (AST_RWLIST_WRLOCK(&sinks)) {
00178 ast_cdr_register(name, ast_module_info->description, custom_log);
00179 ast_log(LOG_ERROR, "Unable to lock sink list. Unload failed.\n");
00180 return -1;
00181 }
00182
00183 free_config();
00184 AST_RWLIST_UNLOCK(&sinks);
00185 return 0;
00186 }
00187
00188 static enum ast_module_load_result load_module(void)
00189 {
00190 if (AST_RWLIST_WRLOCK(&sinks)) {
00191 ast_log(LOG_ERROR, "Unable to lock sink list. Load failed.\n");
00192 return AST_MODULE_LOAD_FAILURE;
00193 }
00194
00195 load_config();
00196 AST_RWLIST_UNLOCK(&sinks);
00197 ast_cdr_register(name, ast_module_info->description, custom_log);
00198 return AST_MODULE_LOAD_SUCCESS;
00199 }
00200
00201 static int reload(void)
00202 {
00203 if (AST_RWLIST_WRLOCK(&sinks)) {
00204 ast_log(LOG_ERROR, "Unable to lock sink list. Load failed.\n");
00205 return AST_MODULE_LOAD_FAILURE;
00206 }
00207
00208 free_config();
00209 load_config();
00210 AST_RWLIST_UNLOCK(&sinks);
00211 return AST_MODULE_LOAD_SUCCESS;
00212 }
00213
00214 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable Comma Separated Values CDR Backend",
00215 .load = load_module,
00216 .unload = unload_module,
00217 .reload = reload,
00218 .load_pri = AST_MODPRI_CDR_DRIVER,
00219 );
00220