Asterisk - The Open Source Telephony Project  21.4.1
cel_custom.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2009, Digium, Inc.
5  *
6  * Steve Murphy <murf@digium.com>
7  * much borrowed from cdr code (cdr_custom.c), author Mark Spencer
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19 
20 /*! \file
21  *
22  * \brief Custom Comma Separated Value CEL records.
23  *
24  * \author Steve Murphy <murf@digium.com>
25  * Logs in LOG_DIR/cel_custom
26  * \ingroup cel_drivers
27  */
28 
29 /*** MODULEINFO
30  <support_level>core</support_level>
31  ***/
32 
33 #include "asterisk.h"
34 
35 #include "asterisk/paths.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/cel.h"
38 #include "asterisk/module.h"
39 #include "asterisk/config.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/utils.h"
42 #include "asterisk/lock.h"
43 #include "asterisk/threadstorage.h"
44 #include "asterisk/strings.h"
45 
46 #define CUSTOM_LOG_DIR "/cel_custom"
47 #define CONFIG "cel_custom.conf"
48 
49 AST_THREADSTORAGE(custom_buf);
50 
51 static const char name[] = "cel-custom";
52 
53 struct cel_config {
55  AST_STRING_FIELD(filename);
56  AST_STRING_FIELD(format);
57  );
58  ast_mutex_t lock;
59  AST_RWLIST_ENTRY(cel_config) list;
60 };
61 
62 #define CUSTOM_BACKEND_NAME "CEL Custom CSV Logging"
63 
65 
66 static void free_config(void)
67 {
68  struct cel_config *sink;
69 
70  while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
71  ast_mutex_destroy(&sink->lock);
73  ast_free(sink);
74  }
75 }
76 
77 static int load_config(void)
78 {
79  struct ast_config *cfg;
80  struct ast_variable *var;
81  struct ast_flags config_flags = { 0 };
82  int mappings = 0;
83  int res = 0;
84 
85  cfg = ast_config_load(CONFIG, config_flags);
86  if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
87  ast_log(LOG_ERROR, "Unable to load " CONFIG ". Not logging CEL to custom CSVs.\n");
88  return -1;
89  }
90 
91  if (!(var = ast_variable_browse(cfg, "mappings"))) {
92  ast_log(LOG_NOTICE, "No mappings found in " CONFIG ". Not logging CEL to custom CSVs.\n");
93  }
94 
95  while (var) {
96  if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) {
97  struct cel_config *sink = ast_calloc_with_stringfields(1, struct cel_config, 1024);
98 
99  if (!sink) {
100  ast_log(LOG_ERROR, "Unable to allocate memory for configuration settings.\n");
101  res = -2;
102  break;
103  }
104 
105  ast_string_field_build(sink, format, "%s\n", var->value);
106  ast_string_field_build(sink, filename, "%s/%s/%s", ast_config_AST_LOG_DIR, name, var->name);
107  ast_mutex_init(&sink->lock);
108 
109  ast_verb(3, "Added CEL CSV mapping for '%s'.\n", sink->filename);
110  mappings += 1;
111  AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
112  } else {
113  ast_log(LOG_NOTICE, "Mapping must have both a filename and a format at line %d\n", var->lineno);
114  }
115  var = var->next;
116  }
117  ast_config_destroy(cfg);
118 
119  ast_verb(1, "Added CEL CSV mapping for %d files.\n", mappings);
120 
121  return res;
122 }
123 
124 static void custom_log(struct ast_event *event)
125 {
126  struct ast_channel *dummy;
127  struct ast_str *str;
128  struct cel_config *config;
129 
130  /* Batching saves memory management here. Otherwise, it's the same as doing an allocation and free each time. */
131  if (!(str = ast_str_thread_get(&custom_buf, 16))) {
132  return;
133  }
134 
136  if (!dummy) {
137  ast_log(LOG_ERROR, "Unable to fabricate channel from CEL event.\n");
138  return;
139  }
140 
142 
143  AST_LIST_TRAVERSE(&sinks, config, list) {
144  FILE *out;
145 
146  ast_str_substitute_variables(&str, 0, dummy, config->format);
147 
148  /* Even though we have a lock on the list, we could be being chased by
149  another thread and this lock ensures that we won't step on anyone's
150  toes. Once each CEL backend gets it's own thread, this lock can be
151  removed. */
152  ast_mutex_lock(&config->lock);
153 
154  /* Because of the absolutely unconditional need for the
155  highest reliability possible in writing billing records,
156  we open write and close the log file each time */
157  if ((out = fopen(config->filename, "a"))) {
158  fputs(ast_str_buffer(str), out);
159  fflush(out); /* be particularly anal here */
160  fclose(out);
161  } else {
162  ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", config->filename, strerror(errno));
163  }
164 
165  ast_mutex_unlock(&config->lock);
166  }
167 
169 
170  ast_channel_unref(dummy);
171 }
172 
173 static int unload_module(void)
174 {
175 
176  if (AST_RWLIST_WRLOCK(&sinks)) {
177  ast_log(LOG_ERROR, "Unable to lock sink list. Unload failed.\n");
178  return -1;
179  }
180 
181  free_config();
183  ast_cel_backend_unregister(CUSTOM_BACKEND_NAME);
184  return 0;
185 }
186 
187 static enum ast_module_load_result load_module(void)
188 {
189  if (AST_RWLIST_WRLOCK(&sinks)) {
190  ast_log(LOG_ERROR, "Unable to lock sink list. Load failed.\n");
192  }
193 
194  load_config();
196 
197  if (ast_cel_backend_register(CUSTOM_BACKEND_NAME, custom_log)) {
198  free_config();
200  }
202 }
203 
204 static int reload(void)
205 {
206  if (AST_RWLIST_WRLOCK(&sinks)) {
207  ast_log(LOG_ERROR, "Unable to lock sink list. Load failed.\n");
209  }
210 
211  free_config();
212  load_config();
215 }
216 
217 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable Comma Separated Values CEL Backend",
218  .support_level = AST_MODULE_SUPPORT_CORE,
219  .load = load_module,
220  .unload = unload_module,
221  .reload = reload,
222  .load_pri = AST_MODPRI_CDR_DRIVER,
223  .requires = "cel",
224 );
struct ast_variable * next
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
ast_module_load_result
Definition: module.h:68
Main Channel structure associated with a channel.
An event.
Definition: event.c:81
A container that holds all config-related information.
Definition: cel_custom.c:53
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
String manipulation functions.
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
Call Event Logging API.
int ast_cel_backend_register(const char *name, ast_cel_backend_cb backend_callback)
Register a CEL backend.
Definition: cel.c:1781
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
Structure for variables, used for configurations and for channel variables.
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
#define ast_calloc_with_stringfields(n, type, size)
Allocate a structure with embedded stringfields in a single allocation.
Definition: stringfields.h:432
Definition: astman.c:222
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
Definitions to aid in the use of thread local storage.
Utility functions.
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
Definition: linkedlists.h:333
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
General Asterisk PBX channel definitions.
Asterisk file paths, configured in asterisk.conf.
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
Core PBX routines and definitions.
int ast_cel_backend_unregister(const char *name)
Unregister a CEL backend.
Definition: cel.c:1769
Support for dynamic strings.
Definition: strings.h:623
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
struct ast_channel * ast_cel_fabricate_channel_from_event(const struct ast_event *event)
Create a fake channel from data in a CEL event.
Definition: cel.c:662
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:555
Structure used to handle boolean flags.
Definition: utils.h:199
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:909
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
Structure for mutex and tracking information.
Definition: lock.h:135