Minor formatting change to test a mantis change ...
[asterisk-bristuff.git] / cdr / cdr_sqlite3_custom.c
blob9352e1d4d435cc41ff6b0bbdef52e4e09bf85881
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2007, Digium, Inc.
6 * Mark Spencer <markster@digium.com> and others.
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief Custom SQLite3 CDR records.
23 * \author Adapted by Alejandro Rios <alejandro.rios@avatar.com.co> and
24 * Russell Bryant <russell@digium.com> from
25 * cdr_mysql_custom by Edward Eastman <ed@dm3.co.uk>,
26 * and cdr_sqlite by Holger Schurig <hs4233@mail.mn-solutions.de>
29 * \arg See also \ref AstCDR
32 * \ingroup cdr_drivers
35 /*** MODULEINFO
36 <depend>sqlite3</depend>
37 ***/
39 #include "asterisk.h"
41 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
43 #include <time.h>
44 #include <sqlite3.h>
46 #include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
47 #include "asterisk/channel.h"
48 #include "asterisk/cdr.h"
49 #include "asterisk/module.h"
50 #include "asterisk/config.h"
51 #include "asterisk/pbx.h"
52 #include "asterisk/utils.h"
53 #include "asterisk/cli.h"
55 AST_MUTEX_DEFINE_STATIC(lock);
57 static const char config_file[] = "cdr_sqlite3_custom.conf";
59 static char *desc = "Customizable SQLite3 CDR Backend";
60 static char *name = "cdr_sqlite3_custom";
61 static sqlite3 *db = NULL;
63 static char table[80];
64 static char *columns;
66 struct values {
67 char *expression;
68 AST_LIST_ENTRY(values) list;
71 static AST_LIST_HEAD_STATIC(sql_values, values);
73 static int free_config(void);
75 static int load_column_config(const char *tmp)
77 char *col = NULL;
78 char *cols = NULL;
79 char *escaped = NULL;
80 struct ast_str *column_string = NULL;
82 if (ast_strlen_zero(tmp)) {
83 ast_log(LOG_WARNING, "Column names not specified. Module not loaded.\n");
84 return -1;
86 if (!(column_string = ast_str_create(1024))) {
87 ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
88 return -1;
90 if (!(cols = ast_strdup(tmp))) {
91 ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
92 ast_free(column_string);
93 return -1;
95 while ((col = strsep(&cols, ","))) {
96 col = ast_strip(col);
97 escaped = sqlite3_mprintf("%q", col);
98 if (!escaped) {
99 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s.'\n", col, table);
100 ast_free(column_string);
101 ast_free(cols);
102 return -1;
104 if (!column_string->used)
105 ast_str_set(&column_string, 0, "%s", escaped);
106 else
107 ast_str_append(&column_string, 0, ",%s", escaped);
108 sqlite3_free(escaped);
110 if (!(columns = ast_strdup(column_string->str))) {
111 ast_log(LOG_ERROR, "Out of memory copying columns string for table '%s.'\n", table);
112 ast_free(column_string);
113 ast_free(cols);
114 return -1;
116 ast_free(column_string);
117 ast_free(cols);
119 return 0;
122 static int load_values_config(const char *tmp)
124 char *val = NULL;
125 char *vals = NULL;
126 struct values *value = NULL;
128 if (ast_strlen_zero(tmp)) {
129 ast_log(LOG_WARNING, "Values not specified. Module not loaded.\n");
130 return -1;
132 if (!(vals = ast_strdup(tmp))) {
133 ast_log(LOG_ERROR, "Out of memory creating temporary buffer for value '%s'\n", tmp);
134 return -1;
136 while ((val = strsep(&vals, ","))) {
137 /* Strip the single quotes off if they are there */
138 val = ast_strip_quoted(val, "'", "'");
139 value = ast_calloc(sizeof(char), sizeof(*value) + strlen(val) + 1);
140 if (!value) {
141 ast_log(LOG_ERROR, "Out of memory creating entry for value '%s'\n", val);
142 ast_free(vals);
143 return -1;
145 value->expression = (char *) value + sizeof(*value);
146 ast_copy_string(value->expression, val, strlen(val) + 1);
147 AST_LIST_INSERT_TAIL(&sql_values, value, list);
149 ast_free(vals);
151 return 0;
154 static int load_config(int reload)
156 struct ast_config *cfg;
157 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
158 struct ast_variable *mappingvar;
159 const char *tmp;
161 if (!(cfg = ast_config_load(config_file, config_flags))) {
162 if (reload)
163 ast_log(LOG_WARNING, "Failed to reload configuration file.\n");
164 else
165 ast_log(LOG_WARNING, "Failed to load configuration file. Module not activated.\n");
166 return -1;
167 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
168 return 0;
170 if (reload)
171 free_config();
173 ast_mutex_lock(&lock);
175 if (!(mappingvar = ast_variable_browse(cfg, "master"))) {
176 /* Nothing configured */
177 ast_mutex_unlock(&lock);
178 ast_config_destroy(cfg);
179 return 0;
182 /* Mapping must have a table name */
183 tmp = ast_variable_retrieve(cfg, "master", "table");
184 if (!ast_strlen_zero(tmp))
185 ast_copy_string(table, tmp, sizeof(table));
186 else {
187 ast_log(LOG_WARNING, "Table name not specified. Assuming cdr.\n");
188 strcpy(table, "cdr");
191 /* Columns */
192 tmp = ast_variable_retrieve(cfg, "master", "columns");
193 if (load_column_config(tmp)) {
194 ast_mutex_unlock(&lock);
195 ast_config_destroy(cfg);
196 free_config();
197 return -1;
200 /* Values */
201 tmp = ast_variable_retrieve(cfg, "master", "values");
202 if (load_values_config(tmp)) {
203 ast_mutex_unlock(&lock);
204 ast_config_destroy(cfg);
205 free_config();
206 return -1;
209 ast_verb(3, "cdr_sqlite3_custom: Logging CDR records to table '%s' in 'master.db'\n", table);
211 ast_mutex_unlock(&lock);
212 ast_config_destroy(cfg);
214 return 0;
217 static int free_config(void)
219 struct values *value;
221 ast_mutex_lock(&lock);
223 if (db) {
224 sqlite3_close(db);
225 db = NULL;
228 if (columns) {
229 ast_free(columns);
230 columns = NULL;
233 while ((value = AST_LIST_REMOVE_HEAD(&sql_values, list)))
234 ast_free(value);
236 ast_mutex_unlock(&lock);
238 return 0;
241 static int sqlite3_log(struct ast_cdr *cdr)
243 int res = 0;
244 char *error = NULL;
245 char *sql = NULL;
246 struct ast_channel dummy = { 0, };
247 int count = 0;
249 { /* Make it obvious that only sql should be used outside of this block */
250 char *escaped;
251 char subst_buf[2048];
252 struct values *value;
253 struct ast_str *value_string = ast_str_create(1024);
254 dummy.cdr = cdr;
255 AST_LIST_TRAVERSE(&sql_values, value, list) {
256 memset(subst_buf, 0, sizeof(subst_buf));
257 pbx_substitute_variables_helper(&dummy, value->expression, subst_buf, sizeof(subst_buf) - 1);
258 escaped = sqlite3_mprintf("%q", subst_buf);
259 if (!value_string->used)
260 ast_str_append(&value_string, 0, "'%s'", escaped);
261 else
262 ast_str_append(&value_string, 0, ",'%s'", escaped);
263 sqlite3_free(escaped);
265 sql = sqlite3_mprintf("INSERT INTO %q (%s) VALUES (%s)", table, columns, value_string->str);
266 ast_debug(1, "About to log: %s\n", sql);
267 ast_free(value_string);
270 ast_mutex_lock(&lock);
272 /* XXX This seems awful arbitrary... */
273 for (count = 0; count < 5; count++) {
274 res = sqlite3_exec(db, sql, NULL, NULL, &error);
275 if (res != SQLITE_BUSY && res != SQLITE_LOCKED)
276 break;
277 usleep(200);
280 if (error) {
281 ast_log(LOG_ERROR, "%s. SQL: %s.\n", error, sql);
282 sqlite3_free(error);
285 if (sql)
286 sqlite3_free(sql);
288 ast_mutex_unlock(&lock);
290 return res;
293 static int unload_module(void)
295 free_config();
297 ast_cdr_unregister(name);
299 return 0;
302 static int load_module(void)
304 char *error;
305 char filename[PATH_MAX];
306 int res;
307 char *sql;
309 if (!load_config(0)) {
310 res = ast_cdr_register(name, desc, sqlite3_log);
311 if (res) {
312 ast_log(LOG_ERROR, "Unable to register custom SQLite3 CDR handling\n");
313 free_config();
314 return AST_MODULE_LOAD_DECLINE;
316 } else
317 return AST_MODULE_LOAD_DECLINE;
319 /* is the database there? */
320 snprintf(filename, sizeof(filename), "%s/master.db", ast_config_AST_LOG_DIR);
321 res = sqlite3_open(filename, &db);
322 if (res != SQLITE_OK) {
323 ast_log(LOG_ERROR, "Could not open database %s.\n", filename);
324 free_config();
325 return AST_MODULE_LOAD_DECLINE;
328 /* is the table there? */
329 sql = sqlite3_mprintf("SELECT COUNT(AcctId) FROM %q;", table);
330 res = sqlite3_exec(db, sql, NULL, NULL, NULL);
331 sqlite3_free(sql);
332 if (res != SQLITE_OK) {
333 /* We don't use %q for the column list here since we already escaped when building it */
334 sql = sqlite3_mprintf("CREATE TABLE %q (AcctId INTEGER PRIMARY KEY, %s)", table, columns);
335 res = sqlite3_exec(db, sql, NULL, NULL, &error);
336 sqlite3_free(sql);
337 if (res != SQLITE_OK) {
338 ast_log(LOG_WARNING, "Unable to create table '%s': %s.\n", table, error);
339 sqlite3_free(error);
340 free_config();
341 return AST_MODULE_LOAD_DECLINE;
345 return 0;
348 static int reload(void)
350 return load_config(1);
353 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "SQLite3 Custom CDR Module",
354 .load = load_module,
355 .unload = unload_module,
356 .reload = reload,