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
00034
00035
00036
00037
00038
00039 #include "asterisk.h"
00040
00041 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 278132 $")
00042
00043 #include <sqlite3.h>
00044
00045 #include "asterisk/paths.h"
00046 #include "asterisk/channel.h"
00047 #include "asterisk/cdr.h"
00048 #include "asterisk/module.h"
00049 #include "asterisk/config.h"
00050 #include "asterisk/pbx.h"
00051 #include "asterisk/utils.h"
00052 #include "asterisk/cli.h"
00053 #include "asterisk/app.h"
00054
00055 AST_MUTEX_DEFINE_STATIC(lock);
00056
00057 static const char config_file[] = "cdr_sqlite3_custom.conf";
00058
00059 static const char desc[] = "Customizable SQLite3 CDR Backend";
00060 static const char name[] = "cdr_sqlite3_custom";
00061 static sqlite3 *db = NULL;
00062
00063 static char table[80];
00064 static char *columns;
00065
00066 struct values {
00067 AST_LIST_ENTRY(values) list;
00068 char expression[1];
00069 };
00070
00071 static AST_LIST_HEAD_STATIC(sql_values, values);
00072
00073 static void free_config(int reload);
00074
00075 static int load_column_config(const char *tmp)
00076 {
00077 char *col = NULL;
00078 char *cols = NULL, *save = NULL;
00079 char *escaped = NULL;
00080 struct ast_str *column_string = NULL;
00081
00082 if (ast_strlen_zero(tmp)) {
00083 ast_log(LOG_WARNING, "Column names not specified. Module not loaded.\n");
00084 return -1;
00085 }
00086 if (!(column_string = ast_str_create(1024))) {
00087 ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
00088 return -1;
00089 }
00090 if (!(save = cols = ast_strdup(tmp))) {
00091 ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
00092 ast_free(column_string);
00093 return -1;
00094 }
00095 while ((col = strsep(&cols, ","))) {
00096 col = ast_strip(col);
00097 escaped = sqlite3_mprintf("%q", col);
00098 if (!escaped) {
00099 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s.'\n", col, table);
00100 ast_free(column_string);
00101 ast_free(save);
00102 return -1;
00103 }
00104 ast_str_append(&column_string, 0, "%s%s", ast_str_strlen(column_string) ? "," : "", escaped);
00105 sqlite3_free(escaped);
00106 }
00107 if (!(columns = ast_strdup(ast_str_buffer(column_string)))) {
00108 ast_log(LOG_ERROR, "Out of memory copying columns string for table '%s.'\n", table);
00109 ast_free(column_string);
00110 ast_free(save);
00111 return -1;
00112 }
00113 ast_free(column_string);
00114 ast_free(save);
00115
00116 return 0;
00117 }
00118
00119 static int load_values_config(const char *tmp)
00120 {
00121 char *vals = NULL, *save = NULL;
00122 struct values *value = NULL;
00123 int i;
00124 AST_DECLARE_APP_ARGS(val,
00125 AST_APP_ARG(ues)[200];
00126 );
00127
00128 if (ast_strlen_zero(tmp)) {
00129 ast_log(LOG_WARNING, "Values not specified. Module not loaded.\n");
00130 return -1;
00131 }
00132 if (!(save = vals = ast_strdup(tmp))) {
00133 ast_log(LOG_ERROR, "Out of memory creating temporary buffer for value '%s'\n", tmp);
00134 return -1;
00135 }
00136 AST_STANDARD_RAW_ARGS(val, vals);
00137 for (i = 0; i < val.argc; i++) {
00138
00139 char *v = ast_strip_quoted(val.ues[i], "'", "'");
00140 value = ast_calloc(sizeof(char), sizeof(*value) + strlen(v));
00141 if (!value) {
00142 ast_log(LOG_ERROR, "Out of memory creating entry for value '%s'\n", v);
00143 ast_free(save);
00144 return -1;
00145 }
00146 strcpy(value->expression, v);
00147 AST_LIST_INSERT_TAIL(&sql_values, value, list);
00148 }
00149 ast_free(save);
00150
00151 return 0;
00152 }
00153
00154 static int load_config(int reload)
00155 {
00156 struct ast_config *cfg;
00157 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00158 struct ast_variable *mappingvar;
00159 const char *tmp;
00160
00161 if ((cfg = ast_config_load(config_file, config_flags)) == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
00162 ast_log(LOG_WARNING, "Failed to %sload configuration file. %s\n", reload ? "re" : "", reload ? "" : "Module not activated.");
00163 return -1;
00164 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00165 return 0;
00166 }
00167
00168 if (reload) {
00169 free_config(1);
00170 }
00171
00172 if (!(mappingvar = ast_variable_browse(cfg, "master"))) {
00173
00174 ast_config_destroy(cfg);
00175 return -1;
00176 }
00177
00178
00179 if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "master", "table"))) {
00180 ast_copy_string(table, tmp, sizeof(table));
00181 } else {
00182 ast_log(LOG_WARNING, "Table name not specified. Assuming cdr.\n");
00183 strcpy(table, "cdr");
00184 }
00185
00186
00187 if (load_column_config(ast_variable_retrieve(cfg, "master", "columns"))) {
00188 ast_config_destroy(cfg);
00189 free_config(0);
00190 return -1;
00191 }
00192
00193
00194 if (load_values_config(ast_variable_retrieve(cfg, "master", "values"))) {
00195 ast_config_destroy(cfg);
00196 free_config(0);
00197 return -1;
00198 }
00199
00200 ast_verb(3, "cdr_sqlite3_custom: Logging CDR records to table '%s' in 'master.db'\n", table);
00201
00202 ast_config_destroy(cfg);
00203
00204 return 0;
00205 }
00206
00207 static void free_config(int reload)
00208 {
00209 struct values *value;
00210
00211 if (!reload && db) {
00212 sqlite3_close(db);
00213 db = NULL;
00214 }
00215
00216 if (columns) {
00217 ast_free(columns);
00218 columns = NULL;
00219 }
00220
00221 while ((value = AST_LIST_REMOVE_HEAD(&sql_values, list))) {
00222 ast_free(value);
00223 }
00224 }
00225
00226 static int write_cdr(struct ast_cdr *cdr)
00227 {
00228 int res = 0;
00229 char *error = NULL;
00230 char *sql = NULL;
00231 int count = 0;
00232
00233 if (db == NULL) {
00234
00235 return 0;
00236 }
00237
00238 ast_mutex_lock(&lock);
00239
00240 {
00241 char *escaped;
00242 char subst_buf[2048];
00243 struct values *value;
00244 struct ast_channel *dummy;
00245 struct ast_str *value_string = ast_str_create(1024);
00246
00247 dummy = ast_dummy_channel_alloc();
00248 if (!dummy) {
00249 ast_log(LOG_ERROR, "Unable to allocate channel for variable subsitution.\n");
00250 ast_free(value_string);
00251 ast_mutex_unlock(&lock);
00252 return 0;
00253 }
00254 dummy->cdr = ast_cdr_dup(cdr);
00255 AST_LIST_TRAVERSE(&sql_values, value, list) {
00256 pbx_substitute_variables_helper(dummy, value->expression, subst_buf, sizeof(subst_buf) - 1);
00257 escaped = sqlite3_mprintf("%q", subst_buf);
00258 ast_str_append(&value_string, 0, "%s'%s'", ast_str_strlen(value_string) ? "," : "", escaped);
00259 sqlite3_free(escaped);
00260 }
00261 sql = sqlite3_mprintf("INSERT INTO %q (%s) VALUES (%s)", table, columns, ast_str_buffer(value_string));
00262 ast_debug(1, "About to log: %s\n", sql);
00263 ast_channel_release(dummy);
00264 ast_free(value_string);
00265 }
00266
00267
00268 for (count = 0; count < 5; count++) {
00269 res = sqlite3_exec(db, sql, NULL, NULL, &error);
00270 if (res != SQLITE_BUSY && res != SQLITE_LOCKED) {
00271 break;
00272 }
00273 usleep(200);
00274 }
00275
00276 if (error) {
00277 ast_log(LOG_ERROR, "%s. SQL: %s.\n", error, sql);
00278 sqlite3_free(error);
00279 }
00280
00281 if (sql) {
00282 sqlite3_free(sql);
00283 }
00284
00285 ast_mutex_unlock(&lock);
00286
00287 return res;
00288 }
00289
00290 static int unload_module(void)
00291 {
00292 ast_cdr_unregister(name);
00293
00294 free_config(0);
00295
00296 return 0;
00297 }
00298
00299 static int load_module(void)
00300 {
00301 char *error;
00302 char filename[PATH_MAX];
00303 int res;
00304 char *sql;
00305
00306 if (load_config(0)) {
00307 return AST_MODULE_LOAD_DECLINE;
00308 }
00309
00310
00311 snprintf(filename, sizeof(filename), "%s/master.db", ast_config_AST_LOG_DIR);
00312 res = sqlite3_open(filename, &db);
00313 if (res != SQLITE_OK) {
00314 ast_log(LOG_ERROR, "Could not open database %s.\n", filename);
00315 free_config(0);
00316 return AST_MODULE_LOAD_DECLINE;
00317 }
00318
00319
00320 sql = sqlite3_mprintf("SELECT COUNT(AcctId) FROM %q;", table);
00321 res = sqlite3_exec(db, sql, NULL, NULL, NULL);
00322 sqlite3_free(sql);
00323 if (res != SQLITE_OK) {
00324
00325 sql = sqlite3_mprintf("CREATE TABLE %q (AcctId INTEGER PRIMARY KEY, %s)", table, columns);
00326 res = sqlite3_exec(db, sql, NULL, NULL, &error);
00327 sqlite3_free(sql);
00328 if (res != SQLITE_OK) {
00329 ast_log(LOG_WARNING, "Unable to create table '%s': %s.\n", table, error);
00330 sqlite3_free(error);
00331 free_config(0);
00332 return AST_MODULE_LOAD_DECLINE;
00333 }
00334 }
00335
00336 res = ast_cdr_register(name, desc, write_cdr);
00337 if (res) {
00338 ast_log(LOG_ERROR, "Unable to register custom SQLite3 CDR handling\n");
00339 free_config(0);
00340 return AST_MODULE_LOAD_DECLINE;
00341 }
00342
00343 return AST_MODULE_LOAD_SUCCESS;
00344 }
00345
00346 static int reload(void)
00347 {
00348 int res = 0;
00349
00350 ast_mutex_lock(&lock);
00351 res = load_config(1);
00352 ast_mutex_unlock(&lock);
00353
00354 return res;
00355 }
00356
00357 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SQLite3 Custom CDR Module",
00358 .load = load_module,
00359 .unload = unload_module,
00360 .reload = reload,
00361 .load_pri = AST_MODPRI_CDR_DRIVER,
00362 );