header ordering fixes for FreeBSD (pending a global merge into asterisk.h) (bug ...
[asterisk/asterisk.git] / cdr.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Call Detail Record API 
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 <unistd.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include <signal.h>
22
23 #include "asterisk.h"
24
25 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
26
27 #include "asterisk/lock.h"
28 #include "asterisk/channel.h"
29 #include "asterisk/cdr.h"
30 #include "asterisk/logger.h"
31 #include "asterisk/callerid.h"
32 #include "asterisk/causes.h"
33 #include "asterisk/options.h"
34 #include "asterisk/linkedlists.h"
35 #include "asterisk/utils.h"
36 #include "asterisk/sched.h"
37 #include "asterisk/config.h"
38 #include "asterisk/cli.h"
39 #include "asterisk/module.h"
40
41 int ast_default_amaflags = AST_CDR_DOCUMENTATION;
42 char ast_default_accountcode[AST_MAX_ACCOUNT_CODE] = "";
43
44 struct ast_cdr_beitem {
45         char name[20];
46         char desc[80];
47         ast_cdrbe be;
48         AST_LIST_ENTRY(ast_cdr_beitem) list;
49 };
50
51 static AST_LIST_HEAD_STATIC(be_list, ast_cdr_beitem);
52
53 struct ast_cdr_batch_item {
54         struct ast_cdr *cdr;
55         struct ast_cdr_batch_item *next;
56 };
57
58 static struct ast_cdr_batch {
59         int size;
60         struct ast_cdr_batch_item *head;
61         struct ast_cdr_batch_item *tail;
62 } *batch = NULL;
63
64 static struct sched_context *sched;
65 static int cdr_sched = -1;
66 static pthread_t cdr_thread = AST_PTHREADT_NULL;
67
68 #define BATCH_SIZE_DEFAULT 100
69 #define BATCH_TIME_DEFAULT 300
70 #define BATCH_SCHEDULER_ONLY_DEFAULT 0
71 #define BATCH_SAFE_SHUTDOWN_DEFAULT 1
72
73 static int enabled;
74 static int batchmode;
75 static int batchsize;
76 static int batchtime;
77 static int batchscheduleronly;
78 static int batchsafeshutdown;
79
80 AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);
81
82 /* these are used to wake up the CDR thread when there's work to do */
83 AST_MUTEX_DEFINE_STATIC(cdr_pending_lock);
84 pthread_cond_t cdr_pending_cond;
85
86 /*
87  * We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
88  * through our fingers somehow.  If someone allocates a CDR, it must be completely handled normally
89  * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
90  * isn't properly generated and posted.
91  */
92
93 int ast_cdr_register(char *name, char *desc, ast_cdrbe be)
94 {
95         struct ast_cdr_beitem *i;
96
97         if (!name)
98                 return -1;
99         if (!be) {
100                 ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
101                 return -1;
102         }
103
104         AST_LIST_LOCK(&be_list);
105         AST_LIST_TRAVERSE(&be_list, i, list) {
106                 if (!strcasecmp(name, i->name))
107                         break;
108         }
109         AST_LIST_UNLOCK(&be_list);
110
111         if (i) {
112                 ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
113                 return -1;
114         }
115
116         i = malloc(sizeof(*i));
117         if (!i)         
118                 return -1;
119
120         memset(i, 0, sizeof(*i));
121         i->be = be;
122         ast_copy_string(i->name, name, sizeof(i->name));
123         ast_copy_string(i->desc, desc, sizeof(i->desc));
124
125         AST_LIST_LOCK(&be_list);
126         AST_LIST_INSERT_HEAD(&be_list, i, list);
127         AST_LIST_UNLOCK(&be_list);
128
129         return 0;
130 }
131
132 void ast_cdr_unregister(char *name)
133 {
134         struct ast_cdr_beitem *i = NULL;
135
136         AST_LIST_LOCK(&be_list);
137         AST_LIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) {
138                 if (!strcasecmp(name, i->name)) {
139                         AST_LIST_REMOVE_CURRENT(&be_list, list);
140                         if (option_verbose > 1)
141                                 ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s' CDR backend\n", name);
142                         free(i);
143                         break;
144                 }
145         }
146         AST_LIST_TRAVERSE_SAFE_END;
147         AST_LIST_UNLOCK(&be_list);
148 }
149
150 static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur) 
151 {
152         struct ast_var_t *variables;
153         struct varshead *headp;
154
155         if (!name || ast_strlen_zero(name))
156                 return NULL;
157
158         while (cdr) {
159                 headp = &cdr->varshead;
160                 AST_LIST_TRAVERSE(headp, variables, entries) {
161                         if (!strcasecmp(name, ast_var_name(variables)))
162                                 return ast_var_value(variables);
163                 }
164                 if (!recur)
165                         break;
166                 cdr = cdr->next;
167         }
168
169         return NULL;
170 }
171
172 void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur) 
173 {
174         struct tm tm;
175         time_t t;
176         const char *fmt = "%Y-%m-%d %T";
177         const char *varbuf;
178
179         *ret = NULL;
180         /* special vars (the ones from the struct ast_cdr when requested by name) 
181            I'd almost say we should convert all the stringed vals to vars */
182
183         if (!strcasecmp(name, "clid"))
184                 ast_copy_string(workspace, cdr->clid, workspacelen);
185         else if (!strcasecmp(name, "src"))
186                 ast_copy_string(workspace, cdr->src, workspacelen);
187         else if (!strcasecmp(name, "dst"))
188                 ast_copy_string(workspace, cdr->dst, workspacelen);
189         else if (!strcasecmp(name, "dcontext"))
190                 ast_copy_string(workspace, cdr->dcontext, workspacelen);
191         else if (!strcasecmp(name, "channel"))
192                 ast_copy_string(workspace, cdr->channel, workspacelen);
193         else if (!strcasecmp(name, "dstchannel"))
194                 ast_copy_string(workspace, cdr->dstchannel, workspacelen);
195         else if (!strcasecmp(name, "lastapp"))
196                 ast_copy_string(workspace, cdr->lastapp, workspacelen);
197         else if (!strcasecmp(name, "lastdata"))
198                 ast_copy_string(workspace, cdr->lastdata, workspacelen);
199         else if (!strcasecmp(name, "start")) {
200                 t = cdr->start.tv_sec;
201                 if (t) {
202                         localtime_r(&t, &tm);
203                         strftime(workspace, workspacelen, fmt, &tm);
204                 }
205         } else if (!strcasecmp(name, "answer")) {
206                 t = cdr->start.tv_sec;
207                 if (t) {
208                         localtime_r(&t, &tm);
209                         strftime(workspace, workspacelen, fmt, &tm);
210                 }
211         } else if (!strcasecmp(name, "end")) {
212                 t = cdr->start.tv_sec;
213                 if (t) {
214                         localtime_r(&t, &tm);
215                         strftime(workspace, workspacelen, fmt, &tm);
216                 }
217         } else if (!strcasecmp(name, "duration"))
218                 snprintf(workspace, workspacelen, "%d", cdr->duration);
219         else if (!strcasecmp(name, "billsec"))
220                 snprintf(workspace, workspacelen, "%d", cdr->billsec);
221         else if (!strcasecmp(name, "disposition"))
222                 ast_copy_string(workspace, ast_cdr_disp2str(cdr->disposition), workspacelen);
223         else if (!strcasecmp(name, "amaflags"))
224                 ast_copy_string(workspace, ast_cdr_flags2str(cdr->amaflags), workspacelen);
225         else if (!strcasecmp(name, "accountcode"))
226                 ast_copy_string(workspace, cdr->accountcode, workspacelen);
227         else if (!strcasecmp(name, "uniqueid"))
228                 ast_copy_string(workspace, cdr->uniqueid, workspacelen);
229         else if (!strcasecmp(name, "userfield"))
230                 ast_copy_string(workspace, cdr->userfield, workspacelen);
231         else if ((varbuf = ast_cdr_getvar_internal(cdr, name, recur)))
232                 ast_copy_string(workspace, varbuf, workspacelen);
233
234         if (!ast_strlen_zero(workspace))
235                 *ret = workspace;
236 }
237
238 int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur) 
239 {
240         struct ast_var_t *newvariable;
241         struct varshead *headp;
242         const char *read_only[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
243                                     "lastapp", "lastdata", "start", "answer", "end", "duration",
244                                     "billsec", "disposition", "amaflags", "accountcode", "uniqueid",
245                                     "userfield", NULL };
246         int x;
247         
248         for(x = 0; read_only[x]; x++) {
249                 if (!strcasecmp(name, read_only[x])) {
250                         ast_log(LOG_ERROR, "Attempt to set a read-only variable!.\n");
251                         return -1;
252                 }
253         }
254
255         if (!cdr) {
256                 ast_log(LOG_ERROR, "Attempt to set a variable on a nonexistent CDR record.\n");
257                 return -1;
258         }
259
260         while (cdr) {
261                 headp = &cdr->varshead;
262                 AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
263                         if (!strcasecmp(ast_var_name(newvariable), name)) {
264                                 /* there is already such a variable, delete it */
265                                 AST_LIST_REMOVE_CURRENT(headp, entries);
266                                 ast_var_delete(newvariable);
267                                 break;
268                         }
269                 }
270                 AST_LIST_TRAVERSE_SAFE_END;
271
272                 if (value) {
273                         newvariable = ast_var_assign(name, value);
274                         AST_LIST_INSERT_HEAD(headp, newvariable, entries);
275                 }
276
277                 if (!recur) {
278                         break;
279                 }
280
281                 cdr = cdr->next;
282         }
283
284         return 0;
285 }
286
287 int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr)
288 {
289         struct ast_var_t *variables, *newvariable = NULL;
290         struct varshead *headpa, *headpb;
291         char *var, *val;
292         int x = 0;
293
294         headpa = &from_cdr->varshead;
295         headpb = &to_cdr->varshead;
296
297         AST_LIST_TRAVERSE(headpa,variables,entries) {
298                 if (variables &&
299                     (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
300                     !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
301                         newvariable = ast_var_assign(var, val);
302                         AST_LIST_INSERT_HEAD(headpb, newvariable, entries);
303                         x++;
304                 }
305         }
306
307         return x;
308 }
309
310 #define CDR_CLEN 18
311 int ast_cdr_serialize_variables(struct ast_cdr *cdr, char *buf, size_t size, char delim, char sep, int recur) 
312 {
313         struct ast_var_t *variables;
314         struct varshead *headp;
315         char *var=NULL ,*val=NULL;
316         char *tmp = NULL;
317         char workspace[256];
318         int workspacelen;
319         int total = 0, x = 0, i = 0;
320         const char *cdrcols[CDR_CLEN] = { 
321                 "clid",
322                 "src",
323                 "dst",
324                 "dcontext",
325                 "channel",
326                 "dstchannel",
327                 "lastapp",
328                 "lastdata",
329                 "start",
330                 "answer",
331                 "end",
332                 "duration",
333                 "billsec",
334                 "disposition",
335                 "amaflags",
336                 "accountcode",
337                 "uniqueid",
338                 "userfield"
339         };
340
341
342         memset(buf,0,size);
343         while (cdr) {
344                 x++;
345                 if (x > 1) {
346                         strncat(buf, "\n", size);
347                 }
348                 headp = &cdr->varshead;
349                 AST_LIST_TRAVERSE(headp,variables,entries) {
350                         if (cdr && variables &&
351                             (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
352                             !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
353                                 snprintf(buf + strlen(buf), size - strlen(buf), "level %d: %s%c%s%c", x, var, delim, val, sep);
354                                 if (strlen(buf) >= size) {
355                                         ast_log(LOG_ERROR,"Data Buffer Size Exceeded!\n");
356                                         break;
357                                 }
358                                 total++;
359                         } else 
360                                 break;
361                 }
362                 for (i = 0 ; i < CDR_CLEN; i++) {
363                         workspacelen = sizeof(workspace);
364                         ast_cdr_getvar(cdr, cdrcols[i], &tmp, workspace, workspacelen, 0);
365                         if (!tmp)
366                                 continue;
367                         
368                         snprintf(buf + strlen(buf), size - strlen(buf), "level %d: %s%c%s%c", x, cdrcols[i], delim, tmp, sep);
369                         if (strlen(buf) >= size) {
370                                 ast_log(LOG_ERROR,"Data Buffer Size Exceeded!\n");
371                                 break;
372                         }
373                         total++;
374                 }
375
376                 if (!recur) {
377                         break;
378                 }
379
380                 cdr = cdr->next;
381         }
382
383         return total;
384 }
385
386
387 void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
388 {
389         struct varshead *headp;
390         struct ast_var_t *vardata;
391
392         /* clear variables */
393         while (cdr) {
394                 headp = &cdr->varshead;
395                 while (!AST_LIST_EMPTY(headp)) {
396                         vardata = AST_LIST_REMOVE_HEAD(headp, entries);
397                         ast_var_delete(vardata);
398                 }
399
400                 if (!recur) {
401                         break;
402                 }
403
404                 cdr = cdr->next;
405         }
406 }
407
408 void ast_cdr_free(struct ast_cdr *cdr)
409 {
410         char *chan;
411         struct ast_cdr *next; 
412
413         while (cdr) {
414                 next = cdr->next;
415                 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
416                 if (!ast_test_flag(cdr, AST_CDR_FLAG_POSTED) && !ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
417                         ast_log(LOG_WARNING, "CDR on channel '%s' not posted\n", chan);
418                 if (!cdr->end.tv_sec && !cdr->end.tv_usec)
419                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
420                 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
421                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
422
423                 ast_cdr_free_vars(cdr, 0);
424                 free(cdr);
425                 cdr = next;
426         }
427 }
428
429 struct ast_cdr *ast_cdr_alloc(void)
430 {
431         struct ast_cdr *cdr;
432
433         cdr = malloc(sizeof(*cdr));
434         if (cdr)
435                 memset(cdr, 0, sizeof(*cdr));
436
437         return cdr;
438 }
439
440 void ast_cdr_start(struct ast_cdr *cdr)
441 {
442         char *chan; 
443
444         while (cdr) {
445                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
446                         chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
447                         if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
448                                 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
449                         if (cdr->start.tv_sec || cdr->start.tv_usec)
450                                 ast_log(LOG_WARNING, "CDR on channel '%s' already started\n", chan);
451                         gettimeofday(&cdr->start, NULL);
452                 }
453                 cdr = cdr->next;
454         }
455 }
456
457 void ast_cdr_answer(struct ast_cdr *cdr)
458 {
459         char *chan; 
460
461         while (cdr) {
462                 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
463                 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
464                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
465                 if (cdr->disposition < AST_CDR_ANSWERED)
466                         cdr->disposition = AST_CDR_ANSWERED;
467                 if (!cdr->answer.tv_sec && !cdr->answer.tv_usec) {
468                         gettimeofday(&cdr->answer, NULL);
469                 }
470                 cdr = cdr->next;
471         }
472 }
473
474 void ast_cdr_busy(struct ast_cdr *cdr)
475 {
476         char *chan; 
477
478         while (cdr) {
479                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
480                         chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
481                         if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
482                                 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
483                         if (cdr->disposition < AST_CDR_BUSY)
484                                 cdr->disposition = AST_CDR_BUSY;
485                 }
486                 cdr = cdr->next;
487         }
488 }
489
490 void ast_cdr_failed(struct ast_cdr *cdr)
491 {
492         char *chan; 
493
494         while (cdr) {
495                 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
496                 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
497                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
498                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
499                         cdr->disposition = AST_CDR_FAILED;
500                 cdr = cdr->next;
501         }
502 }
503
504 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
505 {
506         int res = 0;
507
508         while (cdr) {
509                 switch(cause) {
510                 case AST_CAUSE_BUSY:
511                         ast_cdr_busy(cdr);
512                         break;
513                 case AST_CAUSE_FAILURE:
514                         ast_cdr_failed(cdr);
515                         break;
516                 case AST_CAUSE_NORMAL:
517                         break;
518                 case AST_CAUSE_NOTDEFINED:
519                         res = -1;
520                         break;
521                 default:
522                         res = -1;
523                         ast_log(LOG_WARNING, "Cause not handled\n");
524                 }
525                 cdr = cdr->next;
526         }
527         return res;
528 }
529
530 void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann)
531 {
532         char *chan; 
533
534         while (cdr) {
535                 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
536                 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
537                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
538                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
539                         ast_copy_string(cdr->dstchannel, chann, sizeof(cdr->dstchannel));
540                 cdr = cdr->next;
541         }
542 }
543
544 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
545 {
546         char *chan; 
547
548         while (cdr) {
549                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
550                         chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
551                         if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
552                                 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
553                         if (!app)
554                                 app = "";
555                         ast_copy_string(cdr->lastapp, app, sizeof(cdr->lastapp));
556                         if (!data)
557                                 data = "";
558                         ast_copy_string(cdr->lastdata, data, sizeof(cdr->lastdata));
559                 }
560                 cdr = cdr->next;
561         }
562 }
563
564 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
565 {
566         char tmp[AST_MAX_EXTENSION] = "";
567         char *num;
568
569         while (cdr) {
570                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
571                         /* Grab source from ANI or normal Caller*ID */
572                         num = c->cid.cid_ani ? c->cid.cid_ani : c->cid.cid_num;
573                         
574                         if (c->cid.cid_name && num)
575                                 snprintf(tmp, sizeof(tmp), "\"%s\" <%s>", c->cid.cid_name, num);
576                         else if (c->cid.cid_name)
577                                 strncpy(tmp, c->cid.cid_name, sizeof(tmp) - 1);
578                         else if (num)
579                                 strncpy(tmp, num, sizeof(tmp) - 1);
580                         ast_copy_string(cdr->clid, tmp, sizeof(cdr->clid));
581                         ast_copy_string(cdr->src, num ? num : "", sizeof(cdr->src));
582                 }
583                 cdr = cdr->next;
584         }
585
586         return 0;
587 }
588
589
590 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
591 {
592         char *chan;
593         char *num;
594         char tmp[AST_MAX_EXTENSION] = "";
595
596         while (cdr) {
597                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
598                         chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
599                         if (!ast_strlen_zero(cdr->channel)) 
600                                 ast_log(LOG_WARNING, "CDR already initialized on '%s'\n", chan); 
601                         ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
602                         /* Grab source from ANI or normal Caller*ID */
603                         num = c->cid.cid_ani ? c->cid.cid_ani : c->cid.cid_num;
604                         
605                         if (c->cid.cid_name && num)
606                                 snprintf(tmp, sizeof(tmp), "\"%s\" <%s>", c->cid.cid_name, num);
607                         else if (c->cid.cid_name)
608                                 strncpy(tmp, c->cid.cid_name, sizeof(tmp) - 1);
609                         else if (num)
610                                 strncpy(tmp, num, sizeof(tmp) - 1);
611                         ast_copy_string(cdr->clid, tmp, sizeof(cdr->clid));
612                         ast_copy_string(cdr->src, num ? num : "", sizeof(cdr->src));
613
614                         cdr->disposition = (c->_state == AST_STATE_UP) ?  AST_CDR_ANSWERED : AST_CDR_NOANSWER;
615                         cdr->amaflags = c->amaflags ? c->amaflags :  ast_default_amaflags;
616                         ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
617                         /* Destination information */
618                         ast_copy_string(cdr->dst, c->exten, sizeof(cdr->dst));
619                         ast_copy_string(cdr->dcontext, c->context, sizeof(cdr->dcontext));
620                         /* Unique call identifier */
621                         ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
622                 }
623                 cdr = cdr->next;
624         }
625         return 0;
626 }
627
628 void ast_cdr_end(struct ast_cdr *cdr)
629 {
630         char *chan;
631
632         while (cdr) {
633                 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
634                 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
635                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
636                 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
637                         ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", chan);
638                 if (!cdr->end.tv_sec && !cdr->end.tv_usec) 
639                         gettimeofday(&cdr->end, NULL);
640                 cdr = cdr->next;
641         }
642 }
643
644 char *ast_cdr_disp2str(int disposition)
645 {
646         switch (disposition) {
647         case AST_CDR_NOANSWER:
648                 return "NO ANSWER";
649         case AST_CDR_FAILED:
650                 return "FAILED";                
651         case AST_CDR_BUSY:
652                 return "BUSY";          
653         case AST_CDR_ANSWERED:
654                 return "ANSWERED";
655         }
656         return "UNKNOWN";
657 }
658
659 char *ast_cdr_flags2str(int flag)
660 {
661         switch(flag) {
662         case AST_CDR_OMIT:
663                 return "OMIT";
664         case AST_CDR_BILLING:
665                 return "BILLING";
666         case AST_CDR_DOCUMENTATION:
667                 return "DOCUMENTATION";
668         }
669         return "Unknown";
670 }
671
672 int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
673 {
674         struct ast_cdr *cdr = chan->cdr;
675
676         ast_copy_string(chan->accountcode, account, sizeof(chan->accountcode));
677         while (cdr) {
678                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
679                         ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode));
680                 cdr = cdr->next;
681         }
682         return 0;
683 }
684
685 int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
686 {
687         struct ast_cdr *cdr = chan->cdr;
688         int newflag;
689
690         newflag = ast_cdr_amaflags2int(flag);
691         if (newflag)
692                 cdr->amaflags = newflag;
693
694         return 0;
695 }
696
697 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
698 {
699         struct ast_cdr *cdr = chan->cdr;
700
701         while (cdr) {
702                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) 
703                         ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield));
704                 cdr = cdr->next;
705         }
706
707         return 0;
708 }
709
710 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
711 {
712         struct ast_cdr *cdr = chan->cdr;
713
714         while (cdr) {
715                 int len = strlen(cdr->userfield);
716
717                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
718                         strncpy(cdr->userfield+len, userfield, sizeof(cdr->userfield) - len - 1);
719
720                 cdr = cdr->next;
721         }
722
723         return 0;
724 }
725
726 int ast_cdr_update(struct ast_channel *c)
727 {
728         struct ast_cdr *cdr = c->cdr;
729         char *num;
730         char tmp[AST_MAX_EXTENSION] = "";
731
732         while (cdr) {
733                 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
734                         num = c->cid.cid_ani ? c->cid.cid_ani : c->cid.cid_num;
735                         
736                         if (c->cid.cid_name && num)
737                                 snprintf(tmp, sizeof(tmp), "\"%s\" <%s>", c->cid.cid_name, num);
738                         else if (c->cid.cid_name)
739                                 strncpy(tmp, c->cid.cid_name, sizeof(tmp) - 1);
740                         else if (num)
741                                 strncpy(tmp, num, sizeof(tmp) - 1);
742                         ast_copy_string(cdr->clid, tmp, sizeof(cdr->clid));
743                         ast_copy_string(cdr->src, num ? num : "", sizeof(cdr->src));
744
745                         /* Copy account code et-al */   
746                         ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
747                         /* Destination information */
748                         ast_copy_string(cdr->dst, (ast_strlen_zero(c->macroexten)) ? c->exten : c->macroexten, sizeof(cdr->dst));
749                         ast_copy_string(cdr->dcontext, (ast_strlen_zero(c->macrocontext)) ? c->context : c->macrocontext, sizeof(cdr->dcontext));
750                 }
751                 cdr = cdr->next;
752         }
753
754         return 0;
755 }
756
757 int ast_cdr_amaflags2int(const char *flag)
758 {
759         if (!strcasecmp(flag, "default"))
760                 return 0;
761         if (!strcasecmp(flag, "omit"))
762                 return AST_CDR_OMIT;
763         if (!strcasecmp(flag, "billing"))
764                 return AST_CDR_BILLING;
765         if (!strcasecmp(flag, "documentation"))
766                 return AST_CDR_DOCUMENTATION;
767         return -1;
768 }
769
770 static void post_cdr(struct ast_cdr *cdr)
771 {
772         char *chan;
773         struct ast_cdr_beitem *i;
774
775         while (cdr) {
776                 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
777                 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
778                         ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
779                 if (!cdr->end.tv_sec && !cdr->end.tv_usec)
780                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
781                 if (!cdr->start.tv_sec && !cdr->start.tv_usec)
782                         ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
783                 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec + (cdr->end.tv_usec - cdr->start.tv_usec) / 1000000;
784                 if (cdr->answer.tv_sec || cdr->answer.tv_usec)
785                         cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec + (cdr->end.tv_usec - cdr->answer.tv_usec) / 1000000;
786                 else
787                         cdr->billsec = 0;
788                 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
789                 AST_LIST_LOCK(&be_list);
790                 AST_LIST_TRAVERSE(&be_list, i, list) {
791                         i->be(cdr);
792                 }
793                 AST_LIST_UNLOCK(&be_list);
794                 cdr = cdr->next;
795         }
796 }
797
798 void ast_cdr_reset(struct ast_cdr *cdr, int flags)
799 {
800         struct ast_flags tmp = {flags};
801         struct ast_cdr *dup;
802
803         while (cdr) {
804                 /* Detach if post is requested */
805                 if (ast_test_flag(&tmp, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
806                         if (ast_test_flag(&tmp, AST_CDR_FLAG_POSTED)) {
807                                 ast_cdr_end(cdr);
808                                 dup = ast_cdr_alloc();
809                                 memcpy(dup, cdr, sizeof(*dup));
810                                 ast_cdr_detach(dup);
811                                 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
812                         }
813
814                         /* clear variables */
815                         if (!ast_test_flag(&tmp, AST_CDR_FLAG_KEEP_VARS)) {
816                                 ast_cdr_free_vars(cdr, 0);
817                         }
818
819                         /* Reset to initial state */
820                         ast_clear_flag(cdr, AST_FLAGS_ALL);     
821                         memset(&cdr->start, 0, sizeof(cdr->start));
822                         memset(&cdr->end, 0, sizeof(cdr->end));
823                         memset(&cdr->answer, 0, sizeof(cdr->answer));
824                         cdr->billsec = 0;
825                         cdr->duration = 0;
826                         ast_cdr_start(cdr);
827                         cdr->disposition = AST_CDR_NOANSWER;
828                 }
829                         
830                 cdr = cdr->next;
831         }
832 }
833
834 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr) 
835 {
836         struct ast_cdr *ret;
837
838         if (cdr) {
839                 ret = cdr;
840
841                 while (cdr->next)
842                         cdr = cdr->next;
843                 cdr->next = newcdr;
844         } else {
845                 ret = newcdr;
846         }
847
848         return ret;
849 }
850
851 /* Don't call without cdr_batch_lock */
852 static void reset_batch(void)
853 {
854         batch->size = 0;
855         batch->head = NULL;
856         batch->tail = NULL;
857 }
858
859 /* Don't call without cdr_batch_lock */
860 static int init_batch(void)
861 {
862         /* This is the single meta-batch used to keep track of all CDRs during the entire life of the program */
863         batch = malloc(sizeof(*batch));
864         if (!batch) {
865                 ast_log(LOG_WARNING, "CDR: out of memory while trying to handle batched records, data will most likely be lost\n");
866                 return -1;
867         }
868
869         reset_batch();
870
871         return 0;
872 }
873
874 static void *do_batch_backend_process(void *data)
875 {
876         struct ast_cdr_batch_item *processeditem;
877         struct ast_cdr_batch_item *batchitem = data;
878
879         /* Push each CDR into storage mechanism(s) and free all the memory */
880         while (batchitem) {
881                 post_cdr(batchitem->cdr);
882                 ast_cdr_free(batchitem->cdr);
883                 processeditem = batchitem;
884                 batchitem = batchitem->next;
885                 free(processeditem);
886         }
887
888         return NULL;
889 }
890
891 void ast_cdr_submit_batch(int shutdown)
892 {
893         struct ast_cdr_batch_item *oldbatchitems = NULL;
894         pthread_attr_t attr;
895         pthread_t batch_post_thread = AST_PTHREADT_NULL;
896
897         /* if there's no batch, or no CDRs in the batch, then there's nothing to do */
898         if (!batch || !batch->head)
899                 return;
900
901         /* move the old CDRs aside, and prepare a new CDR batch */
902         ast_mutex_lock(&cdr_batch_lock);
903         oldbatchitems = batch->head;
904         reset_batch();
905         ast_mutex_unlock(&cdr_batch_lock);
906
907         /* if configured, spawn a new thread to post these CDRs,
908            also try to save as much as possible if we are shutting down safely */
909         if (batchscheduleronly || shutdown) {
910                 if (option_debug)
911                         ast_log(LOG_DEBUG, "CDR single-threaded batch processing begins now\n");
912                 do_batch_backend_process(oldbatchitems);
913         } else {
914                 pthread_attr_init(&attr);
915                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
916                 if (ast_pthread_create(&batch_post_thread, &attr, do_batch_backend_process, oldbatchitems)) {
917                         ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
918                         do_batch_backend_process(oldbatchitems);
919                 } else {
920                         if (option_debug)
921                                 ast_log(LOG_DEBUG, "CDR multi-threaded batch processing begins now\n");
922                 }
923         }
924 }
925
926 static int submit_scheduled_batch(void *data)
927 {
928         ast_cdr_submit_batch(0);
929         /* manually reschedule from this point in time */
930         cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
931         /* returning zero so the scheduler does not automatically reschedule */
932         return 0;
933 }
934
935 static void submit_unscheduled_batch(void)
936 {
937         /* this is okay since we are not being called from within the scheduler */
938         if (cdr_sched > -1)
939                 ast_sched_del(sched, cdr_sched);
940         /* schedule the submission to occur ASAP (1 ms) */
941         cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
942         /* signal the do_cdr thread to wakeup early and do some work (that lazy thread ;) */
943         pthread_mutex_lock(&cdr_pending_lock);
944         pthread_cond_signal(&cdr_pending_cond);
945         pthread_mutex_unlock(&cdr_pending_lock);
946 }
947
948 void ast_cdr_detach(struct ast_cdr *cdr)
949 {
950         struct ast_cdr_batch_item *newtail;
951         int curr;
952
953         /* maybe they disabled CDR stuff completely, so just drop it */
954         if (!enabled) {
955                 if (option_debug)
956                         ast_log(LOG_DEBUG, "Dropping CDR !\n");
957                 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
958                 ast_cdr_free(cdr);
959                 return;
960         }
961
962         /* post stuff immediately if we are not in batch mode, this is legacy behaviour */
963         if (!batchmode) {
964                 post_cdr(cdr);
965                 ast_cdr_free(cdr);
966                 return;
967         }
968
969         /* otherwise, each CDR gets put into a batch list (at the end) */
970         if (option_debug)
971                 ast_log(LOG_DEBUG, "CDR detaching from this thread\n");
972
973         /* we'll need a new tail for every CDR */
974         newtail = malloc(sizeof(*newtail));
975         if (!newtail) {
976                 ast_log(LOG_WARNING, "CDR: out of memory while trying to detach, will try in this thread instead\n");
977                 post_cdr(cdr);
978                 ast_cdr_free(cdr);
979                 return;
980         }
981         memset(newtail, 0, sizeof(*newtail));
982
983         /* don't traverse a whole list (just keep track of the tail) */
984         ast_mutex_lock(&cdr_batch_lock);
985         if (!batch)
986                 init_batch();
987         if (!batch->head) {
988                 /* new batch is empty, so point the head at the new tail */
989                 batch->head = newtail;
990         } else {
991                 /* already got a batch with something in it, so just append a new tail */
992                 batch->tail->next = newtail;
993         }
994         newtail->cdr = cdr;
995         batch->tail = newtail;
996         curr = batch->size++;
997         ast_mutex_unlock(&cdr_batch_lock);
998
999         /* if we have enough stuff to post, then do it */
1000         if (curr >= (batchsize - 1))
1001                 submit_unscheduled_batch();
1002 }
1003
1004 static void *do_cdr(void *data)
1005 {
1006         struct timeval now;
1007         struct timespec timeout;
1008         int schedms;
1009         int numevents = 0;
1010
1011         for(;;) {
1012                 gettimeofday(&now, NULL);
1013                 schedms = ast_sched_wait(sched);
1014                 /* this shouldn't happen, but provide a 1 second default just in case */
1015                 if (schedms <= 0)
1016                         schedms = 1000;
1017                 timeout.tv_sec = now.tv_sec + (schedms / 1000);
1018                 timeout.tv_nsec = (now.tv_usec * 1000) + ((schedms % 1000) * 1000);
1019                 /* prevent stuff from clobbering cdr_pending_cond, then wait on signals sent to it until the timeout expires */
1020                 pthread_mutex_lock(&cdr_pending_lock);
1021                 pthread_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
1022                 numevents = ast_sched_runq(sched);
1023                 pthread_mutex_unlock(&cdr_pending_lock);
1024                 if (option_debug > 1)
1025                         ast_log(LOG_DEBUG, "Processed %d scheduled CDR batches from the run queue\n", numevents);
1026         }
1027
1028         return NULL;
1029 }
1030
1031 static int handle_cli_status(int fd, int argc, char *argv[])
1032 {
1033         struct ast_cdr_beitem *beitem=NULL;
1034         int cnt=0;
1035         long nextbatchtime=0;
1036
1037         if (argc > 2)
1038                 return RESULT_SHOWUSAGE;
1039
1040         ast_cli(fd, "CDR logging: %s\n", enabled ? "enabled" : "disabled");
1041         ast_cli(fd, "CDR mode: %s\n", batchmode ? "batch" : "simple");
1042         if (enabled) {
1043                 if (batchmode) {
1044                         if (batch)
1045                                 cnt = batch->size;
1046                         if (cdr_sched > -1)
1047                                 nextbatchtime = ast_sched_when(sched, cdr_sched);
1048                         ast_cli(fd, "CDR safe shut down: %s\n", batchsafeshutdown ? "enabled" : "disabled");
1049                         ast_cli(fd, "CDR batch threading model: %s\n", batchscheduleronly ? "scheduler only" : "scheduler plus separate threads");
1050                         ast_cli(fd, "CDR current batch size: %d record(s)\n", cnt);
1051                         ast_cli(fd, "CDR maximum batch size: %d record(s)\n", batchsize);
1052                         ast_cli(fd, "CDR maximum batch time: %d second(s)\n", batchtime);
1053                         ast_cli(fd, "CDR next scheduled batch processing time: %ld second(s)\n", nextbatchtime);
1054                 }
1055                 AST_LIST_LOCK(&be_list);
1056                 AST_LIST_TRAVERSE(&be_list, beitem, list) {
1057                         ast_cli(fd, "CDR registered backend: %s\n", beitem->name);
1058                 }
1059                 AST_LIST_UNLOCK(&be_list);
1060         }
1061
1062         return 0;
1063 }
1064
1065 static int handle_cli_submit(int fd, int argc, char *argv[])
1066 {
1067         if (argc > 2)
1068                 return RESULT_SHOWUSAGE;
1069
1070         submit_unscheduled_batch();
1071         ast_cli(fd, "Submitted CDRs to backend engines for processing.  This may take a while.\n");
1072
1073         return 0;
1074 }
1075
1076 static struct ast_cli_entry cli_submit = {
1077         .cmda = { "cdr", "submit", NULL },
1078         .handler = handle_cli_submit,
1079         .summary = "Posts all pending batched CDR data",
1080         .usage =
1081         "Usage: cdr submit\n"
1082         "       Posts all pending batched CDR data to the configured CDR backend engine modules.\n"
1083 };
1084
1085 static struct ast_cli_entry cli_status = {
1086         .cmda = { "cdr", "status", NULL },
1087         .handler = handle_cli_status,
1088         .summary = "Display the CDR status",
1089         .usage =
1090         "Usage: cdr status\n"
1091         "       Displays the Call Detail Record engine system status.\n"
1092 };
1093
1094 static int do_reload(void)
1095 {
1096         struct ast_config *config;
1097         const char *enabled_value;
1098         const char *batched_value;
1099         const char *scheduleronly_value;
1100         const char *batchsafeshutdown_value;
1101         const char *size_value;
1102         const char *time_value;
1103         int cfg_size;
1104         int cfg_time;
1105         int was_enabled;
1106         int was_batchmode;
1107         int res=0;
1108         pthread_attr_t attr;
1109
1110         ast_mutex_lock(&cdr_batch_lock);
1111
1112         batchsize = BATCH_SIZE_DEFAULT;
1113         batchtime = BATCH_TIME_DEFAULT;
1114         batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
1115         batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
1116         was_enabled = enabled;
1117         was_batchmode = batchmode;
1118         enabled = 1;
1119         batchmode = 0;
1120
1121         /* don't run the next scheduled CDR posting while reloading */
1122         if (cdr_sched > -1)
1123                 ast_sched_del(sched, cdr_sched);
1124
1125         if ((config = ast_config_load("cdr.conf"))) {
1126                 if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
1127                         enabled = ast_true(enabled_value);
1128                 }
1129                 if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
1130                         batchmode = ast_true(batched_value);
1131                 }
1132                 if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
1133                         batchscheduleronly = ast_true(scheduleronly_value);
1134                 }
1135                 if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
1136                         batchsafeshutdown = ast_true(batchsafeshutdown_value);
1137                 }
1138                 if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
1139                         if (sscanf(size_value, "%d", &cfg_size) < 1)
1140                                 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
1141                         else if (size_value < 0)
1142                                 ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
1143                         else
1144                                 batchsize = cfg_size;
1145                 }
1146                 if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
1147                         if (sscanf(time_value, "%d", &cfg_time) < 1)
1148                                 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
1149                         else if (time_value < 0)
1150                                 ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
1151                         else
1152                                 batchtime = cfg_time;
1153                 }
1154         }
1155
1156         if (enabled && !batchmode) {
1157                 ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
1158         } else if (enabled && batchmode) {
1159                 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
1160                 ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
1161         } else {
1162                 ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
1163         }
1164
1165         /* if this reload enabled the CDR batch mode, create the background thread
1166            if it does not exist */
1167         if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
1168                 pthread_cond_init(&cdr_pending_cond, NULL);
1169                 pthread_attr_init(&attr);
1170                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1171                 if (ast_pthread_create(&cdr_thread, &attr, do_cdr, NULL) < 0) {
1172                         ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
1173                         ast_sched_del(sched, cdr_sched);
1174                 } else {
1175                         ast_cli_register(&cli_submit);
1176                         ast_register_atexit(ast_cdr_engine_term);
1177                         res = 0;
1178                 }
1179         /* if this reload disabled the CDR and/or batch mode and there is a background thread,
1180            kill it */
1181         } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
1182                 /* wake up the thread so it will exit */
1183                 pthread_cancel(cdr_thread);
1184                 pthread_kill(cdr_thread, SIGURG);
1185                 pthread_join(cdr_thread, NULL);
1186                 cdr_thread = AST_PTHREADT_NULL;
1187                 pthread_cond_destroy(&cdr_pending_cond);
1188                 ast_cli_unregister(&cli_submit);
1189                 ast_unregister_atexit(ast_cdr_engine_term);
1190                 res = 0;
1191                 /* if leaving batch mode, then post the CDRs in the batch,
1192                    and don't reschedule, since we are stopping CDR logging */
1193                 if (!batchmode && was_batchmode) {
1194                         ast_cdr_engine_term();
1195                 }
1196         } else {
1197                 res = 0;
1198         }
1199
1200         ast_mutex_unlock(&cdr_batch_lock);
1201         ast_config_destroy(config);
1202
1203         return res;
1204 }
1205
1206 int ast_cdr_engine_init(void)
1207 {
1208         int res;
1209
1210         sched = sched_context_create();
1211         if (!sched) {
1212                 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
1213                 return -1;
1214         }
1215
1216         ast_cli_register(&cli_status);
1217
1218         res = do_reload();
1219         if (res) {
1220                 ast_mutex_lock(&cdr_batch_lock);
1221                 res = init_batch();
1222                 ast_mutex_unlock(&cdr_batch_lock);
1223         }
1224
1225         return res;
1226 }
1227
1228 /* This actually gets called a couple of times at shutdown.  Once, before we start
1229    hanging up channels, and then again, after the channel hangup timeout expires */
1230 void ast_cdr_engine_term(void)
1231 {
1232         ast_cdr_submit_batch(batchsafeshutdown);
1233 }
1234
1235 void ast_cdr_engine_reload(void)
1236 {
1237         do_reload();
1238 }
1239