Wed Jan 8 2020 09:49:42

Asterisk developer's documentation


cdr_sqlite3_custom.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2007, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com> and others.
7  *
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.
13  *
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.
17  */
18 
19 /*!
20  * \file
21  * \brief Custom SQLite3 CDR records.
22  *
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>
27  *
28  *
29  * \arg See also \ref AstCDR
30  *
31  *
32  * \ingroup cdr_drivers
33  */
34 
35 /*** MODULEINFO
36  <depend>sqlite3</depend>
37  <support_level>extended</support_level>
38  ***/
39 
40 #include "asterisk.h"
41 
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416336 $")
43 
44 #include <sqlite3.h>
45 
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"
54 #include "asterisk/app.h"
55 
57 
58 static const char config_file[] = "cdr_sqlite3_custom.conf";
59 
60 static const char desc[] = "Customizable SQLite3 CDR Backend";
61 static const char name[] = "cdr_sqlite3_custom";
62 static sqlite3 *db = NULL;
63 
64 static char table[80];
65 static char *columns;
66 
67 struct values {
69  char expression[1];
70 };
71 
73 
74 static void free_config(int reload);
75 
76 static int load_column_config(const char *tmp)
77 {
78  char *col = NULL;
79  char *cols = NULL, *save = NULL;
80  char *escaped = NULL;
81  struct ast_str *column_string = NULL;
82 
83  if (ast_strlen_zero(tmp)) {
84  ast_log(LOG_WARNING, "Column names not specified. Module not loaded.\n");
85  return -1;
86  }
87  if (!(column_string = ast_str_create(1024))) {
88  ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
89  return -1;
90  }
91  if (!(save = cols = ast_strdup(tmp))) {
92  ast_log(LOG_ERROR, "Out of memory creating temporary buffer for column list for table '%s.'\n", table);
93  ast_free(column_string);
94  return -1;
95  }
96  while ((col = strsep(&cols, ","))) {
97  col = ast_strip(col);
98  escaped = sqlite3_mprintf("%q", col);
99  if (!escaped) {
100  ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s.'\n", col, table);
101  ast_free(column_string);
102  ast_free(save);
103  return -1;
104  }
105  ast_str_append(&column_string, 0, "%s%s", ast_str_strlen(column_string) ? "," : "", escaped);
106  sqlite3_free(escaped);
107  }
108  if (!(columns = ast_strdup(ast_str_buffer(column_string)))) {
109  ast_log(LOG_ERROR, "Out of memory copying columns string for table '%s.'\n", table);
110  ast_free(column_string);
111  ast_free(save);
112  return -1;
113  }
114  ast_free(column_string);
115  ast_free(save);
116 
117  return 0;
118 }
119 
120 static int load_values_config(const char *tmp)
121 {
122  char *vals = NULL, *save = NULL;
123  struct values *value = NULL;
124  int i;
126  AST_APP_ARG(ues)[200]; /* More than 200 columns in this CDR? Yeah, right... */
127  );
128 
129  if (ast_strlen_zero(tmp)) {
130  ast_log(LOG_WARNING, "Values not specified. Module not loaded.\n");
131  return -1;
132  }
133  if (!(save = vals = ast_strdup(tmp))) {
134  ast_log(LOG_ERROR, "Out of memory creating temporary buffer for value '%s'\n", tmp);
135  return -1;
136  }
137  AST_STANDARD_RAW_ARGS(val, vals);
138  for (i = 0; i < val.argc; i++) {
139  /* Strip the single quotes off if they are there */
140  char *v = ast_strip_quoted(val.ues[i], "'", "'");
141  value = ast_calloc(sizeof(char), sizeof(*value) + strlen(v));
142  if (!value) {
143  ast_log(LOG_ERROR, "Out of memory creating entry for value '%s'\n", v);
144  ast_free(save);
145  return -1;
146  }
147  strcpy(value->expression, v); /* SAFE */
149  }
150  ast_free(save);
151 
152  return 0;
153 }
154 
155 static int load_config(int reload)
156 {
157  struct ast_config *cfg;
158  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
159  struct ast_variable *mappingvar;
160  const char *tmp;
161 
162  if ((cfg = ast_config_load(config_file, config_flags)) == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
163  ast_log(LOG_WARNING, "Failed to %sload configuration file. %s\n", reload ? "re" : "", reload ? "" : "Module not activated.");
164  return -1;
165  } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
166  return 0;
167  }
168 
169  if (reload) {
170  free_config(1);
171  }
172 
173  if (!(mappingvar = ast_variable_browse(cfg, "master"))) {
174  /* Nothing configured */
175  ast_config_destroy(cfg);
176  return -1;
177  }
178 
179  /* Mapping must have a table name */
180  if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "master", "table"))) {
181  ast_copy_string(table, tmp, sizeof(table));
182  } else {
183  ast_log(LOG_WARNING, "Table name not specified. Assuming cdr.\n");
184  strcpy(table, "cdr");
185  }
186 
187  /* Columns */
188  if (load_column_config(ast_variable_retrieve(cfg, "master", "columns"))) {
189  ast_config_destroy(cfg);
190  free_config(0);
191  return -1;
192  }
193 
194  /* Values */
195  if (load_values_config(ast_variable_retrieve(cfg, "master", "values"))) {
196  ast_config_destroy(cfg);
197  free_config(0);
198  return -1;
199  }
200 
201  ast_verb(3, "cdr_sqlite3_custom: Logging CDR records to table '%s' in 'master.db'\n", table);
202 
203  ast_config_destroy(cfg);
204 
205  return 0;
206 }
207 
208 static void free_config(int reload)
209 {
210  struct values *value;
211 
212  if (!reload && db) {
213  sqlite3_close(db);
214  db = NULL;
215  }
216 
217  if (columns) {
218  ast_free(columns);
219  columns = NULL;
220  }
221 
222  while ((value = AST_LIST_REMOVE_HEAD(&sql_values, list))) {
223  ast_free(value);
224  }
225 }
226 
227 static int write_cdr(struct ast_cdr *cdr)
228 {
229  int res = 0;
230  char *error = NULL;
231  char *sql = NULL;
232 
233  if (db == NULL) {
234  /* Should not have loaded, but be failsafe. */
235  return 0;
236  }
237 
239 
240  { /* Make it obvious that only sql should be used outside of this block */
241  char *escaped;
242  char subst_buf[2048];
243  struct values *value;
244  struct ast_channel *dummy;
245  struct ast_str *value_string = ast_str_create(1024);
246 
247  dummy = ast_dummy_channel_alloc();
248  if (!dummy) {
249  ast_log(LOG_ERROR, "Unable to allocate channel for variable subsitution.\n");
250  ast_free(value_string);
252  return 0;
253  }
254  dummy->cdr = ast_cdr_dup(cdr);
255  AST_LIST_TRAVERSE(&sql_values, value, list) {
256  pbx_substitute_variables_helper(dummy, value->expression, subst_buf, sizeof(subst_buf) - 1);
257  escaped = sqlite3_mprintf("%q", subst_buf);
258  ast_str_append(&value_string, 0, "%s'%s'", ast_str_strlen(value_string) ? "," : "", escaped);
259  sqlite3_free(escaped);
260  }
261  sql = sqlite3_mprintf("INSERT INTO %q (%s) VALUES (%s)", table, columns, ast_str_buffer(value_string));
262  ast_debug(1, "About to log: %s\n", sql);
263  ast_channel_unref(dummy);
264  ast_free(value_string);
265  }
266 
267  if (sqlite3_exec(db, sql, NULL, NULL, &error) != SQLITE_OK) {
268  ast_log(LOG_ERROR, "%s. SQL: %s.\n", error, sql);
269  sqlite3_free(error);
270  }
271 
272  if (sql) {
273  sqlite3_free(sql);
274  }
275 
277 
278  return res;
279 }
280 
281 static int unload_module(void)
282 {
283  ast_cdr_unregister(name);
284 
285  free_config(0);
286 
287  return 0;
288 }
289 
290 static int load_module(void)
291 {
292  char *error;
293  char filename[PATH_MAX];
294  int res;
295  char *sql;
296 
297  if (load_config(0)) {
299  }
300 
301  /* is the database there? */
302  snprintf(filename, sizeof(filename), "%s/master.db", ast_config_AST_LOG_DIR);
303  res = sqlite3_open(filename, &db);
304  if (res != SQLITE_OK) {
305  ast_log(LOG_ERROR, "Could not open database %s.\n", filename);
306  free_config(0);
308  }
309  sqlite3_busy_timeout(db, 1000);
310  /* is the table there? */
311  sql = sqlite3_mprintf("SELECT COUNT(AcctId) FROM %q;", table);
312  res = sqlite3_exec(db, sql, NULL, NULL, NULL);
313  sqlite3_free(sql);
314  if (res != SQLITE_OK) {
315  /* We don't use %q for the column list here since we already escaped when building it */
316  sql = sqlite3_mprintf("CREATE TABLE %q (AcctId INTEGER PRIMARY KEY, %s)", table, columns);
317  res = sqlite3_exec(db, sql, NULL, NULL, &error);
318  sqlite3_free(sql);
319  if (res != SQLITE_OK) {
320  ast_log(LOG_WARNING, "Unable to create table '%s': %s.\n", table, error);
321  sqlite3_free(error);
322  free_config(0);
324  }
325  }
326 
327  res = ast_cdr_register(name, desc, write_cdr);
328  if (res) {
329  ast_log(LOG_ERROR, "Unable to register custom SQLite3 CDR handling\n");
330  free_config(0);
332  }
333 
335 }
336 
337 static int reload(void)
338 {
339  int res = 0;
340 
342  res = load_config(1);
344 
345  return res;
346 }
347 
348 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SQLite3 Custom CDR Module",
349  .load = load_module,
350  .unload = unload_module,
351  .reload = reload,
352  .load_pri = AST_MODPRI_CDR_DRIVER,
353 );
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
Definition: pbx.c:4676
Main Channel structure associated with a channel.
Definition: channel.h:742
Asterisk main include file. File version handling, generic pbx functions.
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
Definition: config.c:625
char * strsep(char **str, const char *delims)
static char * columns
#define ast_strdup(a)
Definition: astmm.h:109
Definition: ast_expr2.c:325
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2502
struct values::@75 list
static sqlite3 * db
#define LOG_WARNING
Definition: logger.h:144
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
Definition: config.c:597
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
static void dummy(char *unused,...)
Definition: chan_unistim.c:188
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
static int load_values_config(const char *tmp)
struct ast_cdr * ast_cdr_dup(struct ast_cdr *cdr)
Duplicate a record.
Definition: cdr.c:213
Configuration File Parser.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:900
struct ast_str * ast_str_create(size_t init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:420
#define ast_mutex_lock(a)
Definition: lock.h:155
struct ast_cdr * cdr
Definition: channel.h:766
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
int value
Definition: syslog.c:39
static int load_module(void)
#define ast_verb(level,...)
Definition: logger.h:243
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
Utility functions.
#define CONFIG_STATUS_FILEMISSING
Definition: config.h:50
Call Detail Record API.
char * ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
Strip leading/trailing whitespace and quotes from a string.
Definition: utils.c:1431
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR handling engine.
Definition: cdr.c:130
General Asterisk PBX channel definitions.
#define unload_module
Definition: agent.c:75
Asterisk file paths, configured in asterisk.conf.
static void free_config(int reload)
#define ast_config_load(filename, flags)
Load a config file.
Definition: config.h:170
char expression[1]
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
ast_mutex_t lock
Definition: app_meetme.c:964
static const char config_file[]
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:155
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
Core PBX routines and definitions.
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:290
static int reload(void)
Definition: app_amd.c:497
Responsible for call detail data.
Definition: cdr.h:82
static char table[80]
#define LOG_ERROR
Definition: logger.h:155
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
static int load_config(void)
const char * ast_config_AST_LOG_DIR
Definition: asterisk.c:263
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
static const char desc[]
static const char name[]
#define ast_free(a)
Definition: astmm.h:97
Structure used to handle boolean flags.
Definition: utils.h:200
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:471
Standard Command Line Interface.
#define ast_calloc(a, b)
Definition: astmm.h:82
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
static int load_column_config(const char *tmp)
#define AST_STANDARD_RAW_ARGS(args, parse)
Definition: app.h:606
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
struct ast_channel * ast_dummy_channel_alloc(void)
Create a fake channel structure.
Definition: channel.c:1391
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
Asterisk module definitions.
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:526
void ast_cdr_unregister(const char *name)
Unregister a CDR handling engine.
Definition: cdr.c:165
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
#define CONFIG_STATUS_FILEUNCHANGED
Definition: config.h:51
#define ast_mutex_unlock(a)
Definition: lock.h:156