Asterisk - The Open Source Telephony Project GIT-master-7e7a603
cdr_csv.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 1999 - 2005, Digium, Inc.
5 *
6 * Mark Spencer <markster@digium.com>
7 *
8 * Includes code and algorithms from the Zapata library.
9 *
10 * See http://www.asterisk.org for more information about
11 * the Asterisk project. Please do not directly contact
12 * any of the maintainers of this project for assistance;
13 * the project provides a web site, mailing lists and IRC
14 * channels for your use.
15 *
16 * This program is free software, distributed under the terms of
17 * the GNU General Public License Version 2. See the LICENSE file
18 * at the top of the source tree.
19 */
20
21/*!
22 * \file
23 * \brief Comma Separated Value CDR records.
24 *
25 * \author Mark Spencer <markster@digium.com>
26 *
27 * \arg See also \ref AstCDR
28 * \ingroup cdr_drivers
29 */
30
31/*! \li \ref cdr_csv.c uses the configuration file \ref cdr.conf
32 * \addtogroup configuration_file Configuration Files
33 */
34
35/*** MODULEINFO
36 <support_level>extended</support_level>
37 ***/
38
39#include "asterisk.h"
40
41#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
42#include "asterisk/config.h"
43#include "asterisk/channel.h"
44#include "asterisk/cdr.h"
45#include "asterisk/module.h"
46#include "asterisk/utils.h"
47#include "asterisk/lock.h"
48
49#define CSV_LOG_DIR "/cdr-csv"
50#define CSV_MASTER "/Master.csv"
51
52#define DATE_FORMAT "%Y-%m-%d %T"
53
54static int usegmtime = 0;
55static int accountlogs = 1;
56static int loguniqueid = 0;
57static int loguserfield = 0;
58static int loaded = 0;
59static int newcdrcolumns = 0;
60static const char config[] = "cdr.conf";
62
63/* #define CSV_LOGUNIQUEID 1 */
64/* #define CSV_LOGUSERFIELD 1 */
65
66/*----------------------------------------------------
67 The values are as follows:
68
69
70 "accountcode", accountcode is the account name of detail records, Master.csv contains all records *
71 Detail records are configured on a channel basis, IAX and SIP are determined by user *
72 DAHDI is determined by channel in dahdi.conf
73 "source",
74 "destination",
75 "destination context",
76 "callerid",
77 "channel",
78 "destination channel", (if applicable)
79 "last application", Last application run on the channel
80 "last app argument", argument to the last channel
81 "start time",
82 "answer time",
83 "end time",
84 duration, Duration is the whole length that the entire call lasted. ie. call rx'd to hangup
85 "end time" minus "start time"
86 billable seconds, the duration that a call was up after other end answered which will be <= to duration
87 "end time" minus "answer time"
88 "disposition", ANSWERED, NO ANSWER, BUSY
89 "amaflags", DOCUMENTATION, BILL, IGNORE etc, specified on a per channel basis like accountcode.
90 "uniqueid", unique call identifier
91 "userfield" user field set via SetCDRUserField
92----------------------------------------------------------*/
93
94static char *name = "csv";
95
97
98static int load_config(int reload)
99{
100 struct ast_config *cfg;
101 struct ast_variable *v;
102 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
103
104 if (!(cfg = ast_config_load(config, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
105 ast_log(LOG_WARNING, "unable to load config: %s\n", config);
106 return 0;
107 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
108 return 1;
109 }
110
111 accountlogs = 1;
112 usegmtime = 0;
113 loguniqueid = 0;
114 loguserfield = 0;
115 newcdrcolumns = 0;
116
117 if (!(v = ast_variable_browse(cfg, "csv"))) {
119 return 0;
120 }
121
122 /* compute the location of the csv master file */
124 snprintf(file_csv_master, sizeof(file_csv_master),
127
128 for (; v; v = v->next) {
129 if (!strcasecmp(v->name, "usegmtime")) {
131 } else if (!strcasecmp(v->name, "accountlogs")) {
132 /* Turn on/off separate files per accountcode. Default is on (as before) */
134 } else if (!strcasecmp(v->name, "loguniqueid")) {
136 } else if (!strcasecmp(v->name, "loguserfield")) {
138 } else if (!strcasecmp(v->name, "newcdrcolumns")) {
140 }
141
142 }
144 return 1;
145}
146
147static int append_string(char *buf, const char *s, size_t bufsize)
148{
149 int pos = strlen(buf), spos = 0, error = -1;
150
151 if (pos >= bufsize - 4)
152 return -1;
153
154 buf[pos++] = '\"';
155
156 while(pos < bufsize - 3) {
157 if (!s[spos]) {
158 error = 0;
159 break;
160 }
161 if (s[spos] == '\"')
162 buf[pos++] = '\"';
163 buf[pos++] = s[spos];
164 spos++;
165 }
166
167 buf[pos++] = '\"';
168 buf[pos++] = ',';
169 buf[pos++] = '\0';
170
171 return error;
172}
173
174static int append_int(char *buf, int s, size_t bufsize)
175{
176 char tmp[32];
177 int pos = strlen(buf);
178
179 snprintf(tmp, sizeof(tmp), "%d", s);
180
181 if (pos + strlen(tmp) > bufsize - 3)
182 return -1;
183
184 strncat(buf, tmp, bufsize - strlen(buf) - 1);
185 pos = strlen(buf);
186 buf[pos++] = ',';
187 buf[pos++] = '\0';
188
189 return 0;
190}
191
192static int append_date(char *buf, struct timeval when, size_t bufsize)
193{
194 char tmp[80] = "";
195 struct ast_tm tm;
196
197 if (strlen(buf) > bufsize - 3)
198 return -1;
199
200 if (ast_tvzero(when)) {
201 strncat(buf, ",", bufsize - strlen(buf) - 1);
202 return 0;
203 }
204
205 ast_localtime(&when, &tm, usegmtime ? "GMT" : NULL);
206 ast_strftime(tmp, sizeof(tmp), DATE_FORMAT, &tm);
207
208 return append_string(buf, tmp, bufsize);
209}
210
211static int build_csv_record(char *buf, size_t bufsize, struct ast_cdr *cdr)
212{
213
214 buf[0] = '\0';
215 /* Account code */
216 append_string(buf, cdr->accountcode, bufsize);
217 /* Source */
218 append_string(buf, cdr->src, bufsize);
219 /* Destination */
220 append_string(buf, cdr->dst, bufsize);
221 /* Destination context */
222 append_string(buf, cdr->dcontext, bufsize);
223 /* Caller*ID */
224 append_string(buf, cdr->clid, bufsize);
225 /* Channel */
226 append_string(buf, cdr->channel, bufsize);
227 /* Destination Channel */
228 append_string(buf, cdr->dstchannel, bufsize);
229 /* Last Application */
230 append_string(buf, cdr->lastapp, bufsize);
231 /* Last Data */
232 append_string(buf, cdr->lastdata, bufsize);
233 /* Start Time */
234 append_date(buf, cdr->start, bufsize);
235 /* Answer Time */
236 append_date(buf, cdr->answer, bufsize);
237 /* End Time */
238 append_date(buf, cdr->end, bufsize);
239 /* Duration */
240 append_int(buf, cdr->duration, bufsize);
241 /* Billable seconds */
242 append_int(buf, cdr->billsec, bufsize);
243 /* Disposition */
245 /* AMA Flags */
247 /* Unique ID */
248 if (loguniqueid)
249 append_string(buf, cdr->uniqueid, bufsize);
250 /* append the user field */
251 if(loguserfield)
252 append_string(buf, cdr->userfield, bufsize);
253 if (newcdrcolumns) {
254 append_string(buf, cdr->peeraccount, bufsize);
255 append_string(buf, cdr->linkedid, bufsize);
256 append_int(buf, cdr->sequence, bufsize);
257 }
258 /* If we hit the end of our buffer, log an error */
259 if (strlen(buf) < bufsize - 5) {
260 /* Trim off trailing comma */
261 buf[strlen(buf) - 1] = '\0';
262 strncat(buf, "\n", bufsize - strlen(buf) - 1);
263 return 0;
264 }
265 return -1;
266}
267
268static int writefile(char *s, char *file_path)
269{
270 FILE *f;
271 /* because of the absolutely unconditional need for the
272 highest reliability possible in writing billing records,
273 we open write and close the log file each time */
274 if (!(f = fopen(file_path, "a"))) {
275 ast_log(LOG_ERROR, "Unable to open file %s : %s\n", file_path, strerror(errno));
276 return -1;
277 }
278 fputs(s, f);
279 fflush(f); /* be particularly anal here */
280 fclose(f);
281
282 return 0;
283}
284
285
286static int writefile_account(char *s, char *acc)
287{
288 char file_account[PATH_MAX];
289 if (strchr(acc, '/') || (acc[0] == '.')) {
290 ast_log(LOG_WARNING, "Account code '%s' insecure for writing file\n", acc);
291 return -1;
292 }
293 snprintf(file_account, sizeof(file_account), "%s/%s/%s.csv", ast_config_AST_LOG_DIR,CSV_LOG_DIR, acc);
294 return writefile(s, file_account);
295}
296
297static int csv_log(struct ast_cdr *cdr)
298{
299 /* Make sure we have a big enough buf */
300 char buf[1024];
301 if (build_csv_record(buf, sizeof(buf), cdr)) {
302 ast_log(LOG_WARNING, "Unable to create CSV record in %d bytes. CDR not recorded!\n", (int)sizeof(buf));
303 return 0;
304 }
305
308 ast_log(LOG_WARNING, "Unable to write CSV record to master '%s' : %s\n", file_csv_master, strerror(errno));
309
312 ast_log(LOG_WARNING, "Unable to write CSV record to account file '%s' : %s\n", cdr->accountcode, strerror(errno));
313 }
315 return 0;
316}
317
318static int unload_module(void)
319{
321 return -1;
322 }
323
324 loaded = 0;
325 return 0;
326}
327
328static int load_module(void)
329{
330 int res;
331
332 if (!load_config(0)) {
334 }
335
337 ast_log(LOG_ERROR, "Unable to register CSV CDR handling\n");
338 } else {
339 loaded = 1;
340 }
341 return res;
342}
343
344static int reload(void)
345{
346 if (load_config(1)) {
347 loaded = 1;
348 } else {
349 loaded = 0;
350 ast_log(LOG_WARNING, "No [csv] section in cdr.conf. Unregistering backend.\n");
352 }
353
354 return 0;
355}
356
357AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Comma Separated Values CDR Backend",
358 .support_level = AST_MODULE_SUPPORT_EXTENDED,
359 .load = load_module,
360 .unload = unload_module,
361 .reload = reload,
362 .load_pri = AST_MODPRI_CDR_DRIVER,
363 .requires = "cdr",
Asterisk main include file. File version handling, generic pbx functions.
#define PATH_MAX
Definition: asterisk.h:40
#define ast_log
Definition: astobj2.c:42
static int tmp()
Definition: bt_open.c:389
Call Detail Record API.
int ast_cdr_unregister(const char *name)
Unregister a CDR handling engine.
Definition: cdr.c:3050
const char * ast_cdr_disp2str(int disposition)
Disposition to a string.
Definition: cdr.c:3492
int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR handling engine.
Definition: cdr.c:3005
static int append_date(char *buf, struct timeval when, size_t bufsize)
Definition: cdr_csv.c:192
static int loguserfield
Definition: cdr_csv.c:57
#define CSV_MASTER
Definition: cdr_csv.c:50
static int csv_log(struct ast_cdr *cdr)
Definition: cdr_csv.c:297
static int loaded
Definition: cdr_csv.c:58
static char * name
Definition: cdr_csv.c:94
static int writefile_account(char *s, char *acc)
Definition: cdr_csv.c:286
static int loguniqueid
Definition: cdr_csv.c:56
static int writefile(char *s, char *file_path)
Definition: cdr_csv.c:268
static int accountlogs
Definition: cdr_csv.c:55
static ast_mutex_t f_lock
Definition: cdr_csv.c:96
static int append_int(char *buf, int s, size_t bufsize)
Definition: cdr_csv.c:174
#define CSV_LOG_DIR
Definition: cdr_csv.c:49
static const char config[]
Definition: cdr_csv.c:60
static int newcdrcolumns
Definition: cdr_csv.c:59
static int append_string(char *buf, const char *s, size_t bufsize)
Definition: cdr_csv.c:147
static int usegmtime
Definition: cdr_csv.c:54
static int load_module(void)
Definition: cdr_csv.c:328
static char file_csv_master[PATH_MAX]
Definition: cdr_csv.c:61
static int unload_module(void)
Definition: cdr_csv.c:318
static int load_config(int reload)
Definition: cdr_csv.c:98
static int reload(void)
Definition: cdr_csv.c:344
#define DATE_FORMAT
Definition: cdr_csv.c:52
static int build_csv_record(char *buf, size_t bufsize, struct ast_cdr *cdr)
Definition: cdr_csv.c:211
General Asterisk PBX channel definitions.
const char * ast_channel_amaflags2string(enum ama_flags flags)
Convert the enum representation of an AMA flag to a string representation.
Definition: channel.c:4373
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
@ CONFIG_FLAG_FILEUNCHANGED
#define LOG_ERROR
#define LOG_WARNING
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
Asterisk locking-related definitions:
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define ast_mutex_lock(a)
Definition: lock.h:189
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:520
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CDR_DRIVER
Definition: module.h:331
@ AST_MODULE_SUPPORT_EXTENDED
Definition: module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_LOG_DIR
Definition: options.c:159
#define NULL
Definition: resample.c:96
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Responsible for call detail data.
Definition: cdr.h:279
char dstchannel[AST_MAX_EXTENSION]
Definition: cdr.h:291
long int disposition
Definition: cdr.h:307
char lastdata[AST_MAX_EXTENSION]
Definition: cdr.h:295
char linkedid[AST_MAX_UNIQUEID]
Definition: cdr.h:319
char userfield[AST_MAX_USER_FIELD]
Definition: cdr.h:321
long int billsec
Definition: cdr.h:305
struct timeval answer
Definition: cdr.h:299
char channel[AST_MAX_EXTENSION]
Definition: cdr.h:289
char peeraccount[AST_MAX_ACCOUNT_CODE]
Definition: cdr.h:313
long int duration
Definition: cdr.h:303
long int amaflags
Definition: cdr.h:309
char src[AST_MAX_EXTENSION]
Definition: cdr.h:283
char dst[AST_MAX_EXTENSION]
Definition: cdr.h:285
char clid[AST_MAX_EXTENSION]
Definition: cdr.h:281
char uniqueid[AST_MAX_UNIQUEID]
Definition: cdr.h:317
int sequence
Definition: cdr.h:323
struct timeval start
Definition: cdr.h:297
char accountcode[AST_MAX_ACCOUNT_CODE]
Definition: cdr.h:311
char lastapp[AST_MAX_EXTENSION]
Definition: cdr.h:293
char dcontext[AST_MAX_EXTENSION]
Definition: cdr.h:287
struct timeval end
Definition: cdr.h:301
Structure used to handle boolean flags.
Definition: utils.h:199
const char * description
Definition: module.h:352
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:117
int error(const char *format,...)
Definition: utils/frame.c:999
Utility functions.