Fix extraneous include from MySQL
[asterisk/asterisk.git] / cdr / cdr_pgsql.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * PostgreSQL CDR logger 
5  *
6  * Matthew D. Hardeman <mhardemn@papersoft.com> 
7  * Adapted from the MySQL CDR logger originally by James Sharp 
8  *
9  * Modified September 2003
10  * Matthew D. Hardeman <mhardemn@papersoft.com>
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License.
14  *
15  */
16
17 #include <sys/types.h>
18 #include <asterisk/config.h>
19 #include <asterisk/options.h>
20 #include <asterisk/channel.h>
21 #include <asterisk/cdr.h>
22 #include <asterisk/module.h>
23 #include <asterisk/logger.h>
24 #include "../asterisk.h"
25
26 #include <stdio.h>
27 #include <string.h>
28
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <time.h>
32
33 #include <libpq-fe.h>
34
35 #define DATE_FORMAT "%Y-%m-%d %T"
36
37 static char *desc = "PostgreSQL CDR Backend";
38 static char *name = "pgsql";
39 static char *config = "cdr_pgsql.conf";
40 static char *pghostname = NULL, *pgdbname = NULL, *pgdbuser = NULL, *pgpassword = NULL, *pgdbsock = NULL, *pgdbport = NULL;
41 static int hostname_alloc = 0, dbname_alloc = 0, dbuser_alloc = 0, password_alloc = 0, dbsock_alloc = 0, dbport_alloc = 0;
42 static int connected = 0;
43
44 static ast_mutex_t pgsql_lock = AST_MUTEX_INITIALIZER;
45
46 PGconn          *conn;
47 PGresult        *result;
48
49 static int pgsql_log(struct ast_cdr *cdr)
50 {
51         struct tm tm;
52         struct timeval tv;
53         char sqlcmd[2048], timestr[128];
54         time_t t;
55
56         ast_mutex_lock(&pgsql_lock);
57
58         memset(sqlcmd,0,2048);
59
60         gettimeofday(&tv,NULL);
61         t = tv.tv_sec;
62         localtime_r(&t,&tm);
63         strftime(timestr,128,DATE_FORMAT,&tm);
64
65         if ((!connected) && pghostname && pgdbuser && pgpassword && pgdbname) {
66                 conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
67                 if (PQstatus(conn) != CONNECTION_BAD) {
68                         connected = 1;
69                 } else {
70                         ast_log(LOG_ERROR, "cdr_pgsql: cannot connect to database server %s.  Call will not be logged\n", pghostname);
71                 }
72         } else {
73                 /* Test to be sure we're still connected... */
74                 /* If we're connected, and connection is working, good. */
75                 /* Otherwise, attempt reconnect.  If it fails... sorry... */
76
77                 if (PQstatus(conn) == CONNECTION_OK) {
78                         connected = 1;
79                 } else {
80                         ast_log(LOG_ERROR, "cdr_pgsql: connection was lost... reattempting connection.");
81                         PQreset(conn);
82                         if (PQstatus(conn) == CONNECTION_OK) {
83                                 ast_log(LOG_ERROR, "cdr_pgsql: connection reestablished.");
84                                 connected = 1;
85                         } else {
86                                 ast_log(LOG_ERROR, "cdr_pgsql: unable to reconnect to database.");
87                                 connected = 0;
88                         }
89                 }
90
91         }
92
93         if (connected) {
94                 char *clid=NULL, *dcontext=NULL, *channel=NULL, *dstchannel=NULL, *lastapp=NULL, *lastdata=NULL;
95                 char *uniqueid=NULL;
96
97                 /* Maximum space needed would be if all characters needed to be escaped, plus a trailing NULL */
98                 if ((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL)
99                         PQescapeString(clid, cdr->clid, strlen(cdr->clid));
100                 if ((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL)
101                         PQescapeString(dcontext, cdr->dcontext, strlen(cdr->dcontext));
102                 if ((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL)
103                         PQescapeString(channel, cdr->channel, strlen(cdr->channel));
104                 if ((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL)
105                         PQescapeString(dstchannel, cdr->dstchannel, strlen(cdr->dstchannel));
106                 if ((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL)
107                         PQescapeString(lastapp, cdr->lastapp, strlen(cdr->lastapp));
108                 if ((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL)
109                         PQescapeString(lastdata, cdr->lastdata, strlen(cdr->lastdata));
110                 if ((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL)
111                         PQescapeString(uniqueid, cdr->uniqueid, strlen(cdr->uniqueid));
112
113                 /* Check for all alloca failures above at once */
114                 if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid)) {
115                         ast_log(LOG_ERROR, "cdr_pgsql:  Out of memory error (insert fails)\n");
116                         ast_mutex_unlock(&pgsql_lock);
117                         return -1;
118                 }
119
120                 ast_log(LOG_DEBUG,"cdr_pgsql: inserting a CDR record.\n");
121
122                 sprintf(sqlcmd,"INSERT INTO cdr (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s')",timestr,clid,cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata,cdr->duration,cdr->billsec,ast_cdr_disp2str(cdr->disposition),cdr->amaflags, cdr->accountcode, uniqueid);
123                 ast_log(LOG_DEBUG,"cdr_pgsql: SQL command as follows:  %s\n",sqlcmd);
124         
125                 result = PQexec(conn, sqlcmd);
126                 if ( PQresultStatus(result) != PGRES_COMMAND_OK) {
127                         ast_log(LOG_ERROR,"cdr_pgsql: Failed to insert call detail record into database.");
128                         ast_mutex_unlock(&pgsql_lock);
129                         return -1;
130                 }
131         }
132         ast_mutex_unlock(&pgsql_lock);
133         return 0;
134 }
135
136 char *description(void)
137 {
138         return desc;
139 }
140
141 static int my_unload_module(void)
142
143         PQfinish(conn);
144         connected = 0;
145         if (pghostname && hostname_alloc) {
146                 free(pghostname);
147                 pghostname = NULL;
148                 hostname_alloc = 0;
149         }
150         if (pgdbname && dbname_alloc) {
151                 free(pgdbname);
152                 pgdbname = NULL;
153                 dbname_alloc = 0;
154         }
155         if (pgdbuser && dbuser_alloc) {
156                 free(pgdbuser);
157                 pgdbuser = NULL;
158                 dbuser_alloc = 0;
159         }
160         if (pgdbsock && dbsock_alloc) {
161                 free(pgdbsock);
162                 pgdbsock = NULL;
163                 dbsock_alloc = 0;
164         }
165         if (pgpassword && password_alloc) {
166                 free(pgpassword);
167                 pgpassword = NULL;
168                 password_alloc = 0;
169         }
170         if (pgdbport && dbport_alloc) {
171                 free(pgdbport);
172                 pgdbport = NULL;
173                 dbport_alloc = 0;
174         }
175         ast_cdr_unregister(name);
176         return 0;
177 }
178
179 static int my_load_module(void)
180 {
181         int res;
182         struct ast_config *cfg;
183         struct ast_variable *var;
184         char *tmp;
185
186         cfg = ast_load(config);
187         if (!cfg) {
188                 ast_log(LOG_WARNING, "Unable to load config for PostgreSQL CDR's: %s\n", config);
189                 return 0;
190         }
191         
192         var = ast_variable_browse(cfg, "global");
193         if (!var) {
194                 /* nothing configured */
195                 return 0;
196         }
197
198         tmp = ast_variable_retrieve(cfg,"global","hostname");
199         if (tmp) {
200                 pghostname = malloc(strlen(tmp) + 1);
201                 if (pghostname != NULL) {
202                         hostname_alloc = 1;
203                         strcpy(pghostname,tmp);
204                 } else {
205                         ast_log(LOG_ERROR,"Out of memory error.\n");
206                         return -1;
207                 }
208         } else {
209                 ast_log(LOG_WARNING,"PostgreSQL server hostname not specified.  Assuming localhost\n");
210                 pghostname = "localhost";
211         }
212
213         tmp = ast_variable_retrieve(cfg,"global","dbname");
214         if (tmp) {
215                 pgdbname = malloc(strlen(tmp) + 1);
216                 if (pgdbname != NULL) {
217                         dbname_alloc = 1;
218                         strcpy(pgdbname,tmp);
219                 } else {
220                         ast_log(LOG_ERROR,"Out of memory error.\n");
221                         return -1;
222                 }
223         } else {
224                 ast_log(LOG_WARNING,"PostgreSQL database not specified.  Assuming asteriskcdrdb\n");
225                 pgdbname = "asteriskcdrdb";
226         }
227
228         tmp = ast_variable_retrieve(cfg,"global","user");
229         if (tmp) {
230                 pgdbuser = malloc(strlen(tmp) + 1);
231                 if (pgdbuser != NULL) {
232                         dbuser_alloc = 1;
233                         strcpy(pgdbuser,tmp);
234                 } else {
235                         ast_log(LOG_ERROR,"Out of memory error.\n");
236                         return -1;
237                 }
238         } else {
239                 ast_log(LOG_WARNING,"PostgreSQL database user not specified.  Assuming root\n");
240                 pgdbuser = "root";
241         }
242
243         tmp = ast_variable_retrieve(cfg,"global","password");
244         if (tmp) {
245                 pgpassword = malloc(strlen(tmp) + 1);
246                 if (pgpassword != NULL) {
247                         password_alloc = 1;
248                         strcpy(pgpassword,tmp);
249                 } else {
250                         ast_log(LOG_ERROR,"Out of memory error.\n");
251                         return -1;
252                 }
253         } else {
254                 ast_log(LOG_WARNING,"PostgreSQL database password not specified.  Assuming blank\n");
255                 pgpassword = "";
256         }
257
258         tmp = ast_variable_retrieve(cfg,"global","port");
259         if (tmp) {
260                 pgdbport = malloc(strlen(tmp) + 1);
261                 if (pgdbport != NULL) {
262                         dbport_alloc = 1;
263                         strcpy(pgdbport,tmp);
264                 } else {
265                         ast_log(LOG_ERROR,"Out of memory error.\n");
266                         return -1;
267                 }
268         } else {
269                 ast_log(LOG_WARNING,"PostgreSQL database port not specified.  Using default.\n");
270                 pgdbport = "5432";
271         }
272
273         ast_destroy(cfg);
274
275         ast_log(LOG_DEBUG,"cdr_pgsql: got hostname of %s\n",pghostname);
276         ast_log(LOG_DEBUG,"cdr_pgsql: got port of %s\n",pgdbport);
277         if (pgdbsock)
278                 ast_log(LOG_DEBUG,"cdr_pgsql: got sock file of %s\n",pgdbsock);
279         ast_log(LOG_DEBUG,"cdr_pgsql: got user of %s\n",pgdbuser);
280         ast_log(LOG_DEBUG,"cdr_pgsql: got dbname of %s\n",pgdbname);
281         ast_log(LOG_DEBUG,"cdr_pgsql: got password of %s\n",pgpassword);
282
283         conn = PQsetdbLogin(pghostname, pgdbport, NULL, NULL, pgdbname, pgdbuser, pgpassword);
284         if (PQstatus(conn) != CONNECTION_BAD) {
285                 ast_log(LOG_DEBUG,"Successfully connected to PostgreSQL database.\n");
286                 connected = 1;
287         } else {
288                 ast_log(LOG_ERROR, "cdr_pgsql: cannot connect to database server %s.  Call will not be logged\n", pghostname);
289                 connected = 0;
290         }
291
292         res = ast_cdr_register(name, desc, pgsql_log);
293         if (res) {
294                 ast_log(LOG_ERROR, "Unable to register PGSQL CDR handling\n");
295         }
296         return res;
297 }
298
299 int load_module(void)
300 {
301         return my_load_module();
302 }
303
304 int unload_module(void)
305 {
306         return my_unload_module();
307 }
308
309 int reload(void)
310 {
311         my_unload_module();
312         return my_load_module();
313 }
314
315 int usecount(void)
316 {
317         return connected;
318 }
319
320 char *key()
321 {
322         return ASTERISK_GPL_KEY;
323 }