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