Asterisk - The Open Source Telephony Project GIT-master-80b953f
Loading...
Searching...
No Matches
loggers.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2026, Sangoma Technologies Corporation
5 *
6 * George Joseph <gjoseph@sangoma.com>
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 * \author George Joseph <gjoseph@sangoma.com>
22 *
23 * \brief Common log entrypoint from the cdr/cel modules
24 *
25 */
26
27#include "cdrel.h"
28#include "asterisk/pbx.h"
29#include "asterisk/vector.h"
30
31/*
32 * We can save some time and ast_str memory allocation work by allocating a single
33 * thread-local buffer and re-using it.
34 */
36
37/*!
38 * \internal
39 * \brief Free an ast_value object
40 *
41 * ... and if the data type is "string", free it as well.
42 *
43 * \param data
44 */
45static void free_value(void *data)
46{
47 struct cdrel_value *val = data;
48 if (val->data_type == cdrel_type_string) {
49 ast_free(val->values.string);
50 val->values.string = NULL;
51 }
53}
54
55/*!
56 * \internal
57 * \brief Free a vector of cdrel_values
58 *
59 * \param data
60 */
61static void free_value_vector(void *data)
62{
63 struct cdrel_values *values = data;
65 AST_VECTOR_PTR_FREE(values);
66}
67
68/*!
69 * \internal
70 * \brief Log a legacy record to a file.
71 *
72 * The file legacy format specifies one long string with dialplan functions. We have no idea
73 * what the separator is so we need to pass the entire string to ast_str_substitute_variables.
74 * This is where the cycles are spent. We then write the result directly to the backend
75 * file bypassing all of the advanced processing.
76 *
77 * \param config The configuration object.
78 * \param data The data to write. May be an ast_cdr or an ast_event.
79 * \retval 0 on success
80 * \retval -1 on failure
81 */
82static int log_legacy_dsv_record(struct cdrel_config *config, void *data)
83{
84 struct ast_channel *dummy = data;
85 struct ast_str *str;
86
87 if (!(str = ast_str_thread_get(&custom_buf, 1024))) {
88 return -1;
89 }
91
93
95}
96
97/*!
98 * \internal
99 * \brief Log a legacy record to a database.
100 *
101 * Unlike the file backends, the legacy database backend configs always use commas
102 * as field separators but they all still use dialplan functions so we need still
103 * need to do evaluation and substitution. Since we know the separator however,
104 * we can iterate over the individual fields.
105 *
106 * \param config The configuration object.
107 * \param data The data to write. May be an ast_cdr or an ast_event.
108 * \retval 0 on success
109 * \retval -1 on failure
110 */
111static int log_legacy_database_record(struct cdrel_config *config, void *data)
112{
113 struct ast_channel *dummy = data;
114 int ix = 0;
115 int res = 0;
116 char subst_buf[2048];
117 size_t field_count = AST_VECTOR_SIZE(&config->fields);
118 RAII_VAR(struct cdrel_values *, values, ast_calloc(1, sizeof(*values)), free_value_vector);
119
120 if (!values) {
121 return -1;
122 }
123
124 res = AST_VECTOR_INIT(values, field_count);
125 if (res != 0) {
126 return -1;
127 }
128
129 if (config->db == NULL) {
130 return -1;
131 }
132
133 for (ix = 0; ix < AST_VECTOR_SIZE(&config->fields); ix++) {
134 struct cdrel_field *field = AST_VECTOR_GET(&config->fields, ix);
135 struct cdrel_value *output_value = ast_calloc(1, sizeof(*output_value));
136
137 if (!output_value) {
138 return -1;
139 }
140 output_value->mallocd = 1;
141
142 pbx_substitute_variables_helper(dummy, field->data, subst_buf, sizeof(subst_buf) - 1);
143 output_value->data_type = cdrel_type_string;
144
145 output_value->field_name = field->name;
146 output_value->values.string = ast_strdup(ast_strip_quoted(subst_buf, "'\"", "'\""));
147 if (!output_value->values.string) {
148 return -1;
149 }
150
151 res = AST_VECTOR_APPEND(values, output_value);
152 if (res != 0) {
153 ast_free(output_value);
154 return -1;
155 }
156 }
157
159}
160
161/*!
162 * \internal
163 * \brief Log an advanced record
164 *
165 * For the file advanced formats, we know what the field separator is so we
166 * iterate over them and accumulate the results in a vector of cdrel_values.
167 * No dialplan function evaluation needed.
168 *
169 * \param config The configuration object.
170 * \param data The data to log. May be an ast_cdr or an ast_event.
171 * \retval 0 on success
172 * \retval -1 on failure
173 */
174static int log_advanced_record(struct cdrel_config *config, void *data)
175{
176 int ix = 0;
177 int res = 0;
178 size_t field_count = AST_VECTOR_SIZE(&config->fields);
180
181 if (!values) {
182 return -1;
183 }
184
185 res = AST_VECTOR_INIT(values, field_count);
186 if (res != 0) {
187 return -1;
188 }
189
190 for (ix = 0; ix < AST_VECTOR_SIZE(&config->fields); ix++) {
191 struct cdrel_field *field = AST_VECTOR_GET(&config->fields, ix);
192 struct cdrel_value input_value = { 0, };
193 struct cdrel_value *output_value = ast_calloc(1, sizeof(*output_value));
194
195 if (!output_value) {
196 return -1;
197 }
198 output_value->mallocd = 1;
199
200 /*
201 * Get a field from a CDR structure or CEL event into an cdrel_value.
202 */
203 res = cdrel_field_getters[config->record_type][field->input_data_type](data, config, field, &input_value);
204 if (res != 0) {
205 ast_free(output_value);
206 return -1;
207 }
208
209 /*
210 * Set the output data type to the type we want to see in the output.
211 */
212 output_value->data_type = field->output_data_type;
213
214 /*
215 * Now call the formatter based on the INPUT data type.
216 */
217 res = cdrel_field_formatters[input_value.data_type](config, field, &input_value, output_value);
218 if (res != 0) {
219 ast_free(output_value);
220 return -1;
221 }
222
223 res = AST_VECTOR_APPEND(values, output_value);
224 if (res != 0) {
225 ast_free(output_value);
226 return -1;
227 }
228 }
229 return cdrel_backend_writers[config->format_type](config, values);
230}
231
232/*
233 * These callbacks are only used in this file so there's no need to
234 * make them available to the rest of the module.
235 */
236typedef int (*cdrel_logger_cb)(struct cdrel_config *config, void *data);
237
248
249/*!
250 * \internal
251 * \brief Main logging entrypoint from the individual modules.
252 *
253 * This is the entrypoint from the individual cdr and cel modules.
254 * "data" will either be an ast_cdr or ast_event structure but we
255 * don't actually care at this point.
256 *
257 * For legacy configs, we need to create a dummy channel so we'll
258 * do that if/when we hit the first one and we'll reuse it for all
259 * further legacy configs. If we fail to get a channel, we'll skip
260 * all further configs.
261 *
262 * \warning This function MUST be called with the module's config_lock
263 * held for reading to prevent reloads from happening while we're logging.
264 *
265 * \param configs The calling module's vector of configuration objects.
266 * \param data The data to write. May be an ast_cdr or an ast_event.
267 * \retval 0 on success
268 * \retval <0 on failure. The magnitude indicates how many configs failed.
269 */
270int cdrel_logger(struct cdrel_configs *configs, void *data)
271{
272 struct ast_channel *dummy = NULL;
273 int ix = 0;
274 int skip_legacy = 0;
275 int res = 0;
276
277 for(ix = 0; ix < AST_VECTOR_SIZE(configs); ix++) {
279 void *chan_or_data = NULL;
280
281 if (config->config_type == cdrel_config_legacy) {
282 if (skip_legacy) {
283 continue;
284 }
285 if (!dummy) {
286 dummy = config->dummy_channel_alloc(config, data);
287 if (!dummy) {
288 ast_log(LOG_ERROR, "Unable to fabricate channel from CEL event for '%s'\n",
289 config->output_filename);
290 skip_legacy = 1;
291 res--;
292 continue;
293 }
294 }
295 chan_or_data = dummy;
296 } else {
297 chan_or_data = data;
298 }
299 res += logger_callbacks[config->backend_type][config->config_type](config, chan_or_data);
300 }
301
302 if (dummy) {
304 }
305 return res;
306}
307
const char * str
Definition app_jack.c:150
#define ast_free(a)
Definition astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
#define ast_log
Definition astobj2.c:42
Private header for res_cdrel_custom.
int write_record_to_database(struct cdrel_config *config, struct cdrel_values *values)
Definition writers.c:169
int write_record_to_file(struct cdrel_config *config, struct ast_str *record)
Definition writers.c:50
@ cdrel_type_string
Definition cdrel.h:73
cdrel_field_formatter cdrel_field_formatters[cdrel_data_type_end]
@ cdrel_config_type_end
Definition cdrel.h:57
@ cdrel_config_legacy
Definition cdrel.h:55
@ cdrel_config_advanced
Definition cdrel.h:56
cdrel_backend_writer cdrel_backend_writers[cdrel_format_type_end]
cdrel_field_getter cdrel_field_getters[cdrel_record_type_end][cdrel_data_type_end]
static const char config[]
static void dummy(char *unused,...)
#define ast_channel_unref(c)
Decrease channel reference count.
Definition channel.h:3018
#define LOG_ERROR
static void free_value_vector(void *data)
Definition loggers.c:61
static int log_advanced_record(struct cdrel_config *config, void *data)
Definition loggers.c:174
static const cdrel_logger_cb logger_callbacks[cdrel_backend_type_end][cdrel_config_type_end]
Definition loggers.c:238
int cdrel_logger(struct cdrel_configs *configs, void *data)
Log a record. The module's logging_cb must call this.
Definition loggers.c:270
static void free_value(void *data)
Definition loggers.c:45
static int log_legacy_database_record(struct cdrel_config *config, void *data)
Definition loggers.c:111
int(* cdrel_logger_cb)(struct cdrel_config *config, void *data)
Definition loggers.c:236
static int log_legacy_dsv_record(struct cdrel_config *config, void *data)
Definition loggers.c:82
Core PBX routines and definitions.
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
Definition ael_main.c:211
@ cdrel_backend_db
@ cdrel_backend_text
@ cdrel_backend_type_end
#define NULL
Definition resample.c:96
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition strings.h:693
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:1852
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
Main Channel structure associated with a channel.
const char * data
Support for dynamic strings.
Definition strings.h:623
char data[1]
Definition cdrel.h:277
enum cdrel_data_type input_data_type
Definition cdrel.h:274
char * name
Definition cdrel.h:273
enum cdrel_data_type output_data_type
Definition cdrel.h:275
union cdrel_value::@454 values
enum cdrel_data_type data_type
Definition cdrel.h:175
char * field_name
Definition cdrel.h:174
int mallocd
Definition cdrel.h:176
char * string
Definition cdrel.h:178
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:981
Vector container support.
#define AST_VECTOR_RESET(vec, cleanup)
Reset vector.
Definition vector.h:636
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition vector.h:620
#define AST_VECTOR_PTR_FREE(vec)
Deallocates this vector pointer.
Definition vector.h:200
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition vector.h:124
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition vector.h:267
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition vector.h:691