Add custom CDR (bug #3595)
[asterisk/asterisk.git] / cdr / cdr_custom.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Comma Separated Value CDR records.
5  * 
6  * Copyright (C) 1999 - 2005, Digium, inc 
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License.
12  *
13  * Includes code and algorithms from the Zapata library.
14  *
15  */
16
17 #include <sys/types.h>
18 #include <asterisk/channel.h>
19 #include <asterisk/cdr.h>
20 #include <asterisk/module.h>
21 #include <asterisk/config.h>
22 #include <asterisk/channel_pvt.h>
23 #include <asterisk/pbx.h>
24 #include <asterisk/logger.h>
25 #include <asterisk/utils.h>
26 #include "../asterisk.h"
27 #include "../astconf.h"
28
29 #define CUSTOM_LOG_DIR "/cdr_custom"
30
31 #define DATE_FORMAT "%Y-%m-%d %T"
32
33 #include <stdio.h>
34 #include <string.h>
35 #include <errno.h>
36
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <time.h>
40
41 AST_MUTEX_DEFINE_STATIC(lock);
42
43 static char *desc = "Customizable Comma Separated Values CDR Backend";
44
45 static char *name = "cdr-custom";
46
47 static FILE *mf = NULL;
48
49 static char master[AST_CONFIG_MAX_PATH];
50 static char format[1024]="";
51
52 static int load_config(int reload) 
53 {
54         struct ast_config *cfg;
55         struct ast_variable *var;
56         int res = -1;
57
58         strcpy(format, "");
59         strcpy(master, "");
60         if((cfg = ast_config_load("cdr_custom.conf"))) {
61                 var = ast_variable_browse(cfg, "mappings");
62                 while(var) {
63                         ast_mutex_lock(&lock);
64                         if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) {
65                                 strncpy(format, var->value, sizeof(format) - 2);
66                                 strcat(format,"\n");
67                                 snprintf(master, sizeof(master),"%s/%s/%s", ast_config_AST_LOG_DIR, name, var->name);
68                                 ast_mutex_unlock(&lock);
69                         } else
70                                 ast_log(LOG_NOTICE, "Mapping must have both filename and format at line %d\n", var->lineno);
71                         if (var->next)
72                                 ast_log(LOG_NOTICE, "Sorry, only one mapping is supported at this time, mapping '%s' will be ignored at line %d.\n", var->next->name, var->next->lineno); 
73                         var = var->next;
74                 }
75                 ast_config_destroy(cfg);
76         }
77         
78         return res;
79 }
80
81
82
83 static int custom_log(struct ast_cdr *cdr)
84 {
85         /* Make sure we have a big enough buf */
86         char buf[2048];
87         struct ast_channel dummy;
88
89         /* Abort if no master file is specified */
90         if (ast_strlen_zero(master))
91                 return 0;
92
93         memset(buf, 0 , sizeof(buf));
94         /* Quite possibly the first use of a static struct ast_channel, we need it so the var funcs will work */
95         memset(&dummy, 0, sizeof(dummy));
96         dummy.cdr = cdr;
97         pbx_substitute_variables_helper(&dummy, format, buf, sizeof(buf) - 1);
98
99         /* because of the absolutely unconditional need for the
100            highest reliability possible in writing billing records,
101            we open write and close the log file each time */
102         mf = fopen(master, "a");
103         if (!mf) {
104                 ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", master, strerror(errno));
105         }
106         if (mf) {
107                 fputs(buf, mf);
108                 fflush(mf); /* be particularly anal here */
109                 fclose(mf);
110                 mf = NULL;
111         }
112         return 0;
113 }
114
115 char *description(void)
116 {
117         return desc;
118 }
119
120 int unload_module(void)
121 {
122         if (mf)
123                 fclose(mf);
124         ast_cdr_unregister(name);
125         return 0;
126 }
127
128 int load_module(void)
129 {
130         int res;
131
132         res = load_config(0);
133         if (!res)
134                 res = ast_cdr_register(name, desc, custom_log);
135         if (res) {
136                 ast_log(LOG_ERROR, "Unable to register custom CDR handling\n");
137                 res = 0;
138                 if (mf)
139                         fclose(mf);
140         }
141         return res;
142 }
143
144 int reload(void)
145 {
146         return load_config(1);
147 }
148
149 int usecount(void)
150 {
151         return 0;
152 }
153
154 char *key()
155 {
156         return ASTERISK_GPL_KEY;
157 }