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.
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
36 <depend>sqlite3</depend>
41 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
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];
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
)
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");
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
);
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
);
95 while ((col
= strsep(&cols
, ","))) {
97 escaped
= sqlite3_mprintf("%q", col
);
99 ast_log(LOG_ERROR
, "Out of memory creating entry for column '%s' in table '%s.'\n", col
, table
);
100 ast_free(column_string
);
104 if (!column_string
->used
)
105 ast_str_set(&column_string
, 0, "%s", escaped
);
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
);
116 ast_free(column_string
);
122 static int load_values_config(const char *tmp
)
126 struct values
*value
= NULL
;
128 if (ast_strlen_zero(tmp
)) {
129 ast_log(LOG_WARNING
, "Values not specified. Module not loaded.\n");
132 if (!(vals
= ast_strdup(tmp
))) {
133 ast_log(LOG_ERROR
, "Out of memory creating temporary buffer for value '%s'\n", tmp
);
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);
141 ast_log(LOG_ERROR
, "Out of memory creating entry for value '%s'\n", val
);
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
);
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
;
161 if (!(cfg
= ast_config_load(config_file
, config_flags
))) {
163 ast_log(LOG_WARNING
, "Failed to reload configuration file.\n");
165 ast_log(LOG_WARNING
, "Failed to load configuration file. Module not activated.\n");
167 } else if (cfg
== CONFIG_STATUS_FILEUNCHANGED
)
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
);
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
));
187 ast_log(LOG_WARNING
, "Table name not specified. Assuming cdr.\n");
188 strcpy(table
, "cdr");
192 tmp
= ast_variable_retrieve(cfg
, "master", "columns");
193 if (load_column_config(tmp
)) {
194 ast_mutex_unlock(&lock
);
195 ast_config_destroy(cfg
);
201 tmp
= ast_variable_retrieve(cfg
, "master", "values");
202 if (load_values_config(tmp
)) {
203 ast_mutex_unlock(&lock
);
204 ast_config_destroy(cfg
);
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
);
217 static int free_config(void)
219 struct values
*value
;
221 ast_mutex_lock(&lock
);
233 while ((value
= AST_LIST_REMOVE_HEAD(&sql_values
, list
)))
236 ast_mutex_unlock(&lock
);
241 static int sqlite3_log(struct ast_cdr
*cdr
)
246 struct ast_channel dummy
= { 0, };
249 { /* Make it obvious that only sql should be used outside of this block */
251 char subst_buf
[2048];
252 struct values
*value
;
253 struct ast_str
*value_string
= ast_str_create(1024);
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
);
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
)
281 ast_log(LOG_ERROR
, "%s. SQL: %s.\n", error
, sql
);
288 ast_mutex_unlock(&lock
);
293 static int unload_module(void)
297 ast_cdr_unregister(name
);
302 static int load_module(void)
305 char filename
[PATH_MAX
];
309 if (!load_config(0)) {
310 res
= ast_cdr_register(name
, desc
, sqlite3_log
);
312 ast_log(LOG_ERROR
, "Unable to register custom SQLite3 CDR handling\n");
314 return AST_MODULE_LOAD_DECLINE
;
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
);
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
);
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
);
337 if (res
!= SQLITE_OK
) {
338 ast_log(LOG_WARNING
, "Unable to create table '%s': %s.\n", table
, error
);
341 return AST_MODULE_LOAD_DECLINE
;
348 static int reload(void)
350 return load_config(1);
353 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "SQLite3 Custom CDR Module",
355 .unload
= unload_module
,