Totally revamp thread debugging to support locating and removing deadlocks
[asterisk/asterisk.git] / cdr.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Call Detail Record API 
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
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 <asterisk/lock.h>
18 #include <asterisk/channel.h>
19 #include <asterisk/cdr.h>
20 #include <asterisk/logger.h>
21 #include <asterisk/callerid.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <pthread.h>
26
27 int ast_default_amaflags = AST_CDR_DOCUMENTATION;
28 char ast_default_accountcode[20] = "";
29
30 static ast_mutex_t cdrlock = AST_MUTEX_INITIALIZER;
31
32 static struct ast_cdr_beitem {
33         char name[20];
34         char desc[80];
35         ast_cdrbe be;
36         struct ast_cdr_beitem *next;
37 } *bes = NULL;
38
39 /*
40  * We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
41  * through our fingers somehow.  If someone allocates a CDR, it must be completely handled normally
42  * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
43  * isn't properly generated and posted.
44  */
45
46 int ast_cdr_register(char *name, char *desc, ast_cdrbe be)
47 {
48         struct ast_cdr_beitem *i;
49         if (!name)
50                 return -1;
51         if (!be) {
52                 ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
53                 return -1;
54         }
55         ast_mutex_lock(&cdrlock);
56         i = bes;
57         while(i) {
58                 if (!strcasecmp(name, i->name))
59                         break;
60                 i = i->next;
61         }
62         ast_mutex_unlock(&cdrlock);
63         if (i) {
64                 ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
65                 return -1;
66         }
67         i = malloc(sizeof(struct ast_cdr_beitem));
68         if (!i)         
69                 return -1;
70         memset(i, 0, sizeof(struct ast_cdr_beitem));
71         strncpy(i->name, name, sizeof(i->name) - 1);
72         strncpy(i->desc, desc, sizeof(i->desc) - 1);
73         i->be = be;
74         ast_mutex_lock(&cdrlock);
75         i->next = bes;
76         bes = i;
77         ast_mutex_unlock(&cdrlock);
78         return 0;
79 }
80
81 void ast_cdr_unregister(char *name)
82 {
83         struct ast_cdr_beitem *i, *prev = NULL;
84         ast_mutex_lock(&cdrlock);
85         i = bes;
86         while(i) {
87                 if (!strcasecmp(name, i->name)) {
88                         if (prev)
89                                 prev->next = i->next;
90                         else
91                                 bes = i->next;
92                         break;
93                 }
94                 i = i->next;
95         }
96         ast_mutex_unlock(&cdrlock);
97         if (i) 
98                 free(i);
99 }
100
101 void ast_cdr_free(struct ast_cdr *cdr)
102 {
103         char *chan; 
104         if (cdr) {
105                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
106                 if (!cdr->posted)
107                         ast_log(LOG_WARNING, "CDR on channel '%s' not posted\n", chan);
108                 if (!cdr->end.tv_sec && !cdr->end.tv_usec)
109                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
110                 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
111                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
112                 free(cdr);
113         }
114 }
115
116 struct ast_cdr *ast_cdr_alloc(void)
117 {
118         struct ast_cdr *cdr;
119         cdr = malloc(sizeof(struct ast_cdr));
120         if (cdr) {
121                 memset(cdr, 0, sizeof(struct ast_cdr));
122         }
123         return cdr;
124 }
125
126 void ast_cdr_start(struct ast_cdr *cdr)
127 {
128         char *chan; 
129         if (cdr) {
130                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
131                 if (cdr->posted)
132                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
133                 if (cdr->start.tv_sec || cdr->start.tv_usec)
134                         ast_log(LOG_WARNING, "CDR on channel '%s' already started\n", chan);
135                 gettimeofday(&cdr->start, NULL);
136         }
137 }
138
139 void ast_cdr_answer(struct ast_cdr *cdr)
140 {
141         char *chan; 
142         if (cdr) {
143                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
144                 if (cdr->posted)
145                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
146                 if (cdr->disposition < AST_CDR_ANSWERED)
147                         cdr->disposition = AST_CDR_ANSWERED;
148                 if (!cdr->answer.tv_sec && !cdr->answer.tv_usec) {
149                         gettimeofday(&cdr->answer, NULL);
150                 }
151         }
152 }
153
154 void ast_cdr_busy(struct ast_cdr *cdr)
155 {
156         char *chan; 
157         if (cdr) {
158                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
159                 if (cdr->posted)
160                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
161                 if (cdr->disposition < AST_CDR_BUSY)
162                         cdr->disposition = AST_CDR_BUSY;
163         }
164 }
165
166 void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann)
167 {
168         char *chan; 
169         if (cdr) {
170                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
171                 if (cdr->posted)
172                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
173                 strncpy(cdr->dstchannel, chann, sizeof(cdr->dstchannel) - 1);
174         }
175 }
176
177 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
178 {
179         char *chan; 
180         if (cdr) {
181                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
182                 if (cdr->posted)
183                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
184                 if (!app)
185                         app = "";
186                 strncpy(cdr->lastapp, app, sizeof(cdr->lastapp) - 1);
187                 if (!data)
188                         data = "";
189                 strncpy(cdr->lastdata, data, sizeof(cdr->lastdata) - 1);
190         }
191 }
192
193 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
194 {
195         char tmp[AST_MAX_EXTENSION] = "";
196         char *num, *name;
197         if (cdr) {
198                 /* Grab source from ANI or normal Caller*ID */
199                 if (c->ani)
200                         strncpy(tmp, c->ani, sizeof(tmp) - 1);
201                 else if (c->callerid)
202                         strncpy(tmp, c->callerid, sizeof(tmp) - 1);
203                 if (c->callerid)
204                         strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
205                 name = NULL;
206                 num = NULL;
207                 ast_callerid_parse(tmp, &name, &num);
208                 if (num) {
209                         ast_shrink_phone_number(num);
210                         strncpy(cdr->src, num, sizeof(cdr->src) - 1);
211                 }
212         }
213         return 0;
214 }
215
216 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
217 {
218         char *chan;
219         char *num, *name;
220         char tmp[AST_MAX_EXTENSION] = "";
221         if (cdr) {
222                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
223                 if (strlen(cdr->channel)) 
224                         ast_log(LOG_WARNING, "CDR already initialized on '%s'\n", chan); 
225                 strncpy(cdr->channel, c->name, sizeof(cdr->channel) - 1);
226                 /* Grab source from ANI or normal Caller*ID */
227                 if (c->ani)
228                         strncpy(tmp, c->ani, sizeof(tmp) - 1);
229                 else if (c->callerid)
230                         strncpy(tmp, c->callerid, sizeof(tmp) - 1);
231                 if (c->callerid)
232                         strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
233                 name = NULL;
234                 num = NULL;
235                 ast_callerid_parse(tmp, &name, &num);
236                 if (num) {
237                         ast_shrink_phone_number(num);
238                         strncpy(cdr->src, num, sizeof(cdr->src) - 1);
239                 }
240                 
241                 if (c->_state == AST_STATE_UP)
242                         cdr->disposition = AST_CDR_ANSWERED;
243                 else
244                         cdr->disposition = AST_CDR_NOANSWER;
245                 if (c->amaflags)
246                         cdr->amaflags = c->amaflags;
247                 else
248                         cdr->amaflags = ast_default_amaflags;
249                 strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
250                 /* Destination information */
251                 strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
252                 strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
253                 /* Unique call identifier */
254                 strncpy(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid) - 1);
255         }
256         return 0;
257 }
258
259 void ast_cdr_end(struct ast_cdr *cdr)
260 {
261         char *chan;
262         if (cdr) {
263                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
264                 if (cdr->posted)
265                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
266                 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
267                         ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", chan);
268                 if (!cdr->end.tv_sec && !cdr->end.tv_usec) 
269                         gettimeofday(&cdr->end, NULL);
270         }
271 }
272
273 char *ast_cdr_disp2str(int disposition)
274 {
275         switch (disposition) {
276         case AST_CDR_NOANSWER:
277                 return "NO ANSWER";
278         case AST_CDR_BUSY:
279                 return "BUSY";          
280         case AST_CDR_ANSWERED:
281                 return "ANSWERED";
282         default:
283                 return "UNKNOWN";
284         }
285 }
286
287 char *ast_cdr_flags2str(int flag)
288 {
289         switch(flag) {
290         case AST_CDR_OMIT:
291                 return "OMIT";
292         case AST_CDR_BILLING:
293                 return "BILLING";
294         case AST_CDR_DOCUMENTATION:
295                 return "DOCUMENTATION";
296         }
297         return "Unknown";
298 }
299
300 int ast_cdr_setaccount(struct ast_channel *chan, char *account)
301 {
302         struct ast_cdr *cdr = chan->cdr;
303
304         strncpy(chan->accountcode, account, sizeof(chan->accountcode) - 1);
305         if (cdr)
306                 strncpy(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode) - 1);
307         return 0;
308 }
309
310 int ast_cdr_update(struct ast_channel *c)
311 {
312         struct ast_cdr *cdr = c->cdr;
313         char *name, *num;
314         char tmp[AST_MAX_EXTENSION] = "";
315         /* Grab source from ANI or normal Caller*ID */
316         if (c->ani)
317                 strncpy(tmp, c->ani, sizeof(tmp) - 1);
318         else if (c->callerid)
319                 strncpy(tmp, c->callerid, sizeof(tmp) - 1);
320         if (c->callerid)
321                 strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1);
322         else
323                 strcpy(cdr->clid, "");
324         name = NULL;
325         num = NULL;
326         ast_callerid_parse(tmp, &name, &num);
327         if (num) {
328                 ast_shrink_phone_number(num);
329                 strncpy(cdr->src, num, sizeof(cdr->src) - 1);
330         }
331         /* Copy account code et-al */   
332         strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1);
333         /* Destination information */
334         strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1);
335         strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1);
336         return 0;
337 }
338
339 int ast_cdr_amaflags2int(char *flag)
340 {
341         if (!strcasecmp(flag, "default"))
342                 return 0;
343         if (!strcasecmp(flag, "omit"))
344                 return AST_CDR_OMIT;
345         if (!strcasecmp(flag, "billing"))
346                 return AST_CDR_BILLING;
347         if (!strcasecmp(flag, "documentation"))
348                 return AST_CDR_DOCUMENTATION;
349         return -1;
350 }
351
352 void ast_cdr_post(struct ast_cdr *cdr)
353 {
354         char *chan;
355         struct ast_cdr_beitem *i;
356         if (cdr) {
357                 chan = strlen(cdr->channel) ? cdr->channel : "<unknown>";
358                 if (cdr->posted)
359                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
360                 if (!cdr->end.tv_sec && !cdr->end.tv_usec)
361                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
362                 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
363                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
364                 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec + (cdr->end.tv_usec - cdr->start.tv_usec) / 1000000;
365                 if (cdr->answer.tv_sec || cdr->answer.tv_usec) {
366                         cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec + (cdr->end.tv_usec - cdr->answer.tv_usec) / 1000000;
367                 } else
368                         cdr->billsec = 0;
369                 cdr->posted = 1;
370                 ast_mutex_lock(&cdrlock);
371                 i = bes;
372                 while(i) {
373                         i->be(cdr);
374                         i = i->next;
375                 }
376                 ast_mutex_unlock(&cdrlock);
377         }
378 }
379
380 void ast_cdr_reset(struct ast_cdr *cdr, int post)
381 {
382         /* Post if requested */
383         if (post) {
384                 ast_cdr_end(cdr);
385                 ast_cdr_post(cdr);
386         }
387         /* Reset to initial state */
388         cdr->posted = 0;
389         memset(&cdr->start, 0, sizeof(cdr->start));
390         memset(&cdr->end, 0, sizeof(cdr->end));
391         memset(&cdr->answer, 0, sizeof(cdr->answer));
392         cdr->billsec = 0;
393         cdr->duration = 0;
394         ast_cdr_start(cdr);
395         cdr->disposition = AST_CDR_NOANSWER;
396 }