Wed Jan 8 2020 09:49:42

Asterisk developer's documentation


cdr.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Call Detail Record API
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \note Includes code and algorithms from the Zapata library.
26  *
27  * \note We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
28  * through our fingers somehow. If someone allocates a CDR, it must be completely handled normally
29  * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
30  * isn't properly generated and posted.
31  */
32 
33 
34 /*** MODULEINFO
35  <support_level>core</support_level>
36  ***/
37 
38 #include "asterisk.h"
39 
40 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 396279 $")
41 
42 #include <signal.h>
43 
44 #include "asterisk/lock.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/cdr.h"
47 #include "asterisk/callerid.h"
48 #include "asterisk/manager.h"
49 #include "asterisk/causes.h"
50 #include "asterisk/linkedlists.h"
51 #include "asterisk/utils.h"
52 #include "asterisk/sched.h"
53 #include "asterisk/config.h"
54 #include "asterisk/cli.h"
55 #include "asterisk/stringfields.h"
56 #include "asterisk/data.h"
57 
58 /*! Default AMA flag for billing records (CDR's) */
61 
63  char name[20];
64  char desc[80];
67 };
68 
70 
72  struct ast_cdr *cdr;
73  struct ast_cdr_batch_item *next;
74 };
75 
76 static struct ast_cdr_batch {
77  int size;
80 } *batch = NULL;
81 
82 
83 static int cdr_sequence = 0;
84 
85 static int cdr_seq_inc(struct ast_cdr *cdr);
86 
87 static struct sched_context *sched;
88 static int cdr_sched = -1;
89 static pthread_t cdr_thread = AST_PTHREADT_NULL;
90 
91 static int enabled;
92 static const int ENABLED_DEFAULT = 1;
93 
94 static int batchmode;
95 static const int BATCHMODE_DEFAULT = 0;
96 
97 static int unanswered;
98 static const int UNANSWERED_DEFAULT = 0;
99 
100 static int batchsize;
101 static const int BATCH_SIZE_DEFAULT = 100;
102 
103 static int batchtime;
104 static const int BATCH_TIME_DEFAULT = 300;
105 
107 static const int BATCH_SCHEDULER_ONLY_DEFAULT = 0;
108 
109 static int batchsafeshutdown;
110 static const int BATCH_SAFE_SHUTDOWN_DEFAULT = 1;
111 
113 
115 
116 /* these are used to wake up the CDR thread when there's work to do */
119 
121 {
122  return enabled;
123 }
124 
125 /*!
126  * \brief Register a CDR driver. Each registered CDR driver generates a CDR
127  * \retval 0 on success.
128  * \retval -1 on error
129  */
130 int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
131 {
132  struct ast_cdr_beitem *i = NULL;
133 
134  if (!name)
135  return -1;
136 
137  if (!be) {
138  ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
139  return -1;
140  }
141 
143  AST_RWLIST_TRAVERSE(&be_list, i, list) {
144  if (!strcasecmp(name, i->name)) {
145  ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
147  return -1;
148  }
149  }
150 
151  if (!(i = ast_calloc(1, sizeof(*i))))
152  return -1;
153 
154  i->be = be;
155  ast_copy_string(i->name, name, sizeof(i->name));
156  ast_copy_string(i->desc, desc, sizeof(i->desc));
157 
158  AST_RWLIST_INSERT_HEAD(&be_list, i, list);
160 
161  return 0;
162 }
163 
164 /*! unregister a CDR driver */
165 void ast_cdr_unregister(const char *name)
166 {
167  struct ast_cdr_beitem *i = NULL;
168 
171  if (!strcasecmp(name, i->name)) {
173  break;
174  }
175  }
178 
179  if (i) {
180  ast_verb(2, "Unregistered '%s' CDR backend\n", name);
181  ast_free(i);
182  }
183 }
184 
186 {
187  return unanswered;
188 }
189 
190 struct ast_cdr *ast_cdr_dup_unique(struct ast_cdr *cdr)
191 {
192  struct ast_cdr *newcdr = ast_cdr_dup(cdr);
193  if (!newcdr)
194  return NULL;
195 
196  cdr_seq_inc(newcdr);
197  return newcdr;
198 }
199 
201 {
202  struct ast_cdr *newcdr = ast_cdr_dup(cdr);
203  if (!newcdr)
204  return NULL;
205 
206  cdr_seq_inc(cdr);
207  return newcdr;
208 }
209 
210 /*! Duplicate a CDR record
211  \returns Pointer to new CDR record
212 */
213 struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr)
214 {
215  struct ast_cdr *newcdr;
216 
217  if (!cdr) /* don't die if we get a null cdr pointer */
218  return NULL;
219  newcdr = ast_cdr_alloc();
220  if (!newcdr)
221  return NULL;
222 
223  memcpy(newcdr, cdr, sizeof(*newcdr));
224  /* The varshead is unusable, volatile even, after the memcpy so we take care of that here */
225  memset(&newcdr->varshead, 0, sizeof(newcdr->varshead));
226  ast_cdr_copy_vars(newcdr, cdr);
227  newcdr->next = NULL;
228 
229  return newcdr;
230 }
231 
232 static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur)
233 {
234  if (ast_strlen_zero(name))
235  return NULL;
236 
237  for (; cdr; cdr = recur ? cdr->next : NULL) {
238  struct ast_var_t *variables;
239  struct varshead *headp = &cdr->varshead;
240  AST_LIST_TRAVERSE(headp, variables, entries) {
241  if (!strcasecmp(name, ast_var_name(variables)))
242  return ast_var_value(variables);
243  }
244  }
245 
246  return NULL;
247 }
248 
249 static void cdr_get_tv(struct timeval when, const char *fmt, char *buf, int bufsize)
250 {
251  if (fmt == NULL) { /* raw mode */
252  snprintf(buf, bufsize, "%ld.%06ld", (long)when.tv_sec, (long)when.tv_usec);
253  } else {
254  if (when.tv_sec) {
255  struct ast_tm tm;
256 
257  ast_localtime(&when, &tm, NULL);
258  ast_strftime(buf, bufsize, fmt, &tm);
259  }
260  }
261 }
262 
263 /*! CDR channel variable retrieval */
264 void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw)
265 {
266  const char *fmt = "%Y-%m-%d %T";
267  const char *varbuf;
268 
269  if (!cdr) /* don't die if the cdr is null */
270  return;
271 
272  *ret = NULL;
273  /* special vars (the ones from the struct ast_cdr when requested by name)
274  I'd almost say we should convert all the stringed vals to vars */
275 
276  if (!strcasecmp(name, "clid"))
277  ast_copy_string(workspace, cdr->clid, workspacelen);
278  else if (!strcasecmp(name, "src"))
279  ast_copy_string(workspace, cdr->src, workspacelen);
280  else if (!strcasecmp(name, "dst"))
281  ast_copy_string(workspace, cdr->dst, workspacelen);
282  else if (!strcasecmp(name, "dcontext"))
283  ast_copy_string(workspace, cdr->dcontext, workspacelen);
284  else if (!strcasecmp(name, "channel"))
285  ast_copy_string(workspace, cdr->channel, workspacelen);
286  else if (!strcasecmp(name, "dstchannel"))
287  ast_copy_string(workspace, cdr->dstchannel, workspacelen);
288  else if (!strcasecmp(name, "lastapp"))
289  ast_copy_string(workspace, cdr->lastapp, workspacelen);
290  else if (!strcasecmp(name, "lastdata"))
291  ast_copy_string(workspace, cdr->lastdata, workspacelen);
292  else if (!strcasecmp(name, "start"))
293  cdr_get_tv(cdr->start, raw ? NULL : fmt, workspace, workspacelen);
294  else if (!strcasecmp(name, "answer"))
295  cdr_get_tv(cdr->answer, raw ? NULL : fmt, workspace, workspacelen);
296  else if (!strcasecmp(name, "end"))
297  cdr_get_tv(cdr->end, raw ? NULL : fmt, workspace, workspacelen);
298  else if (!strcasecmp(name, "duration")) {
299  snprintf(workspace, workspacelen, "%ld", cdr->end.tv_sec != 0 ? cdr->duration : (long)ast_tvdiff_ms(ast_tvnow(), cdr->start) / 1000);
300  } else if (!strcasecmp(name, "billsec")) {
301  snprintf(workspace, workspacelen, "%ld", (cdr->billsec || !ast_tvzero(cdr->end) || ast_tvzero(cdr->answer)) ? cdr->billsec : (long)ast_tvdiff_ms(ast_tvnow(), cdr->answer) / 1000);
302  } else if (!strcasecmp(name, "disposition")) {
303  if (raw) {
304  snprintf(workspace, workspacelen, "%ld", cdr->disposition);
305  } else {
306  ast_copy_string(workspace, ast_cdr_disp2str(cdr->disposition), workspacelen);
307  }
308  } else if (!strcasecmp(name, "amaflags")) {
309  if (raw) {
310  snprintf(workspace, workspacelen, "%ld", cdr->amaflags);
311  } else {
312  ast_copy_string(workspace, ast_cdr_flags2str(cdr->amaflags), workspacelen);
313  }
314  } else if (!strcasecmp(name, "accountcode"))
315  ast_copy_string(workspace, cdr->accountcode, workspacelen);
316  else if (!strcasecmp(name, "peeraccount"))
317  ast_copy_string(workspace, cdr->peeraccount, workspacelen);
318  else if (!strcasecmp(name, "uniqueid"))
319  ast_copy_string(workspace, cdr->uniqueid, workspacelen);
320  else if (!strcasecmp(name, "linkedid"))
321  ast_copy_string(workspace, cdr->linkedid, workspacelen);
322  else if (!strcasecmp(name, "userfield"))
323  ast_copy_string(workspace, cdr->userfield, workspacelen);
324  else if (!strcasecmp(name, "sequence"))
325  snprintf(workspace, workspacelen, "%d", cdr->sequence);
326  else if ((varbuf = ast_cdr_getvar_internal(cdr, name, recur)))
327  ast_copy_string(workspace, varbuf, workspacelen);
328  else
329  workspace[0] = '\0';
330 
331  if (!ast_strlen_zero(workspace))
332  *ret = workspace;
333 }
334 
335 /* readonly cdr variables */
336 static const char * const cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
337  "lastapp", "lastdata", "start", "answer", "end", "duration",
338  "billsec", "disposition", "amaflags", "accountcode", "uniqueid", "linkedid",
339  "userfield", "sequence", NULL };
340 /*! Set a CDR channel variable
341  \note You can't set the CDR variables that belong to the actual CDR record, like "billsec".
342 */
343 int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur)
344 {
345  struct ast_var_t *newvariable;
346  struct varshead *headp;
347  int x;
348 
349  for (x = 0; cdr_readonly_vars[x]; x++) {
350  if (!strcasecmp(name, cdr_readonly_vars[x])) {
351  ast_log(LOG_ERROR, "Attempt to set the '%s' read-only variable!.\n", name);
352  return -1;
353  }
354  }
355 
356  if (!cdr) {
357  ast_log(LOG_ERROR, "Attempt to set a variable on a nonexistent CDR record.\n");
358  return -1;
359  }
360 
361  for (; cdr; cdr = recur ? cdr->next : NULL) {
363  continue;
364  headp = &cdr->varshead;
365  AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
366  if (!strcasecmp(ast_var_name(newvariable), name)) {
367  /* there is already such a variable, delete it */
368  AST_LIST_REMOVE_CURRENT(entries);
369  ast_var_delete(newvariable);
370  break;
371  }
372  }
374 
375  if (value && (newvariable = ast_var_assign(name, value))) {
376  AST_LIST_INSERT_HEAD(headp, newvariable, entries);
377  }
378  }
379 
380  return 0;
381 }
382 
383 int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr)
384 {
385  struct ast_var_t *variables, *newvariable = NULL;
386  struct varshead *headpa, *headpb;
387  const char *var, *val;
388  int x = 0;
389 
390  if (!to_cdr || !from_cdr) /* don't die if one of the pointers is null */
391  return 0;
392 
393  headpa = &from_cdr->varshead;
394  headpb = &to_cdr->varshead;
395 
396  AST_LIST_TRAVERSE(headpa,variables,entries) {
397  if (variables &&
398  (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
399  !ast_strlen_zero(var) && !ast_strlen_zero(val) &&
400  (newvariable = ast_var_assign(var, val))) {
401  AST_LIST_INSERT_HEAD(headpb, newvariable, entries);
402  x++;
403  }
404  }
405 
406  return x;
407 }
408 
409 int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char delim, char sep, int recur)
410 {
411  struct ast_var_t *variables;
412  const char *var;
413  char *tmp;
414  char workspace[256];
415  int total = 0, x = 0, i;
416 
417  ast_str_reset(*buf);
418 
419  for (; cdr; cdr = recur ? cdr->next : NULL) {
420  if (++x > 1)
421  ast_str_append(buf, 0, "\n");
422 
423  AST_LIST_TRAVERSE(&cdr->varshead, variables, entries) {
424  if (!(var = ast_var_name(variables))) {
425  continue;
426  }
427 
428  if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, var, delim, S_OR(ast_var_value(variables), ""), sep) < 0) {
429  ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
430  break;
431  }
432 
433  total++;
434  }
435 
436  for (i = 0; cdr_readonly_vars[i]; i++) {
437  workspace[0] = 0; /* null out the workspace, because the cdr_get_tv() won't write anything if time is NULL, so you get old vals */
438  ast_cdr_getvar(cdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
439  if (!tmp)
440  continue;
441 
442  if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, cdr_readonly_vars[i], delim, tmp, sep) < 0) {
443  ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
444  break;
445  } else
446  total++;
447  }
448  }
449 
450  return total;
451 }
452 
453 
454 void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
455 {
456 
457  /* clear variables */
458  for (; cdr; cdr = recur ? cdr->next : NULL) {
459  struct ast_var_t *vardata;
460  struct varshead *headp = &cdr->varshead;
461  while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
462  ast_var_delete(vardata);
463  }
464 }
465 
466 /*! \brief print a warning if cdr already posted */
467 static void check_post(struct ast_cdr *cdr)
468 {
469  if (!cdr)
470  return;
472  ast_log(LOG_NOTICE, "CDR on channel '%s' already posted\n", S_OR(cdr->channel, "<unknown>"));
473 }
474 
475 void ast_cdr_free(struct ast_cdr *cdr)
476 {
477 
478  while (cdr) {
479  struct ast_cdr *next = cdr->next;
480 
481  ast_cdr_free_vars(cdr, 0);
482  ast_free(cdr);
483  cdr = next;
484  }
485 }
486 
487 /*! \brief the same as a cdr_free call, only with no checks; just get rid of it */
488 void ast_cdr_discard(struct ast_cdr *cdr)
489 {
490  while (cdr) {
491  struct ast_cdr *next = cdr->next;
492 
493  ast_cdr_free_vars(cdr, 0);
494  ast_free(cdr);
495  cdr = next;
496  }
497 }
498 
499 struct ast_cdr *ast_cdr_alloc(void)
500 {
501  struct ast_cdr *x;
502  x = ast_calloc(1, sizeof(*x));
503  if (!x)
504  ast_log(LOG_ERROR,"Allocation Failure for a CDR!\n");
505  return x;
506 }
507 
508 static void cdr_merge_vars(struct ast_cdr *to, struct ast_cdr *from)
509 {
510  struct ast_var_t *variablesfrom,*variablesto;
511  struct varshead *headpfrom = &to->varshead;
512  struct varshead *headpto = &from->varshead;
513  AST_LIST_TRAVERSE_SAFE_BEGIN(headpfrom, variablesfrom, entries) {
514  /* for every var in from, stick it in to */
515  const char *fromvarname, *fromvarval;
516  const char *tovarname = NULL, *tovarval = NULL;
517  fromvarname = ast_var_name(variablesfrom);
518  fromvarval = ast_var_value(variablesfrom);
519  tovarname = 0;
520 
521  /* now, quick see if that var is in the 'to' cdr already */
522  AST_LIST_TRAVERSE(headpto, variablesto, entries) {
523 
524  /* now, quick see if that var is in the 'to' cdr already */
525  if ( strcasecmp(fromvarname, ast_var_name(variablesto)) == 0 ) {
526  tovarname = ast_var_name(variablesto);
527  tovarval = ast_var_value(variablesto);
528  break;
529  }
530  }
531  if (tovarname && strcasecmp(fromvarval,tovarval) != 0) { /* this message here to see how irritating the userbase finds it */
532  ast_log(LOG_NOTICE, "Merging CDR's: variable %s value %s dropped in favor of value %s\n", tovarname, fromvarval, tovarval);
533  continue;
534  } else if (tovarname && strcasecmp(fromvarval,tovarval) == 0) /* if they are the same, the job is done */
535  continue;
536 
537  /* rip this var out of the from cdr, and stick it in the to cdr */
538  AST_LIST_MOVE_CURRENT(headpto, entries);
539  }
541 }
542 
543 void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
544 {
545  struct ast_cdr *zcdr;
546  struct ast_cdr *lto = NULL;
547  struct ast_cdr *lfrom = NULL;
548  int discard_from = 0;
549 
550  if (!to || !from)
551  return;
552 
553  /* don't merge into locked CDR's -- it's bad business */
555  zcdr = to; /* safety valve? */
556  while (to->next) {
557  lto = to;
558  to = to->next;
559  }
560 
562  ast_log(LOG_WARNING, "Merging into locked CDR... no choice.\n");
563  to = zcdr; /* safety-- if all there are is locked CDR's, then.... ?? */
564  lto = NULL;
565  }
566  }
567 
568  if (ast_test_flag(from, AST_CDR_FLAG_LOCKED)) {
569  struct ast_cdr *llfrom = NULL;
570  discard_from = 1;
571  if (lto) {
572  /* insert the from stuff after lto */
573  lto->next = from;
574  lfrom = from;
575  while (lfrom && lfrom->next) {
576  if (!lfrom->next->next)
577  llfrom = lfrom;
578  lfrom = lfrom->next;
579  }
580  /* rip off the last entry and put a copy of the to at the end */
581  if (llfrom) {
582  llfrom->next = to;
583  }
584  from = lfrom;
585  } else {
586  /* save copy of the current *to cdr */
587  struct ast_cdr tcdr;
588  memcpy(&tcdr, to, sizeof(tcdr));
589  /* copy in the locked from cdr */
590  memcpy(to, from, sizeof(*to));
591  lfrom = from;
592  while (lfrom && lfrom->next) {
593  if (!lfrom->next->next)
594  llfrom = lfrom;
595  lfrom = lfrom->next;
596  }
597  from->next = NULL;
598  /* rip off the last entry and put a copy of the to at the end */
599  if (llfrom == from) {
600  to = to->next = ast_cdr_dup(&tcdr);
601  } else if (llfrom) {
602  to = llfrom->next = ast_cdr_dup(&tcdr);
603  }
604  from = lfrom;
605  }
606  }
607 
608  if (!ast_tvzero(from->start)) {
609  if (!ast_tvzero(to->start)) {
610  if (ast_tvcmp(to->start, from->start) > 0 ) {
611  to->start = from->start; /* use the earliest time */
612  from->start = ast_tv(0,0); /* we actively "steal" these values */
613  }
614  /* else nothing to do */
615  } else {
616  to->start = from->start;
617  from->start = ast_tv(0,0); /* we actively "steal" these values */
618  }
619  }
620  if (!ast_tvzero(from->answer)) {
621  if (!ast_tvzero(to->answer)) {
622  if (ast_tvcmp(to->answer, from->answer) > 0 ) {
623  to->answer = from->answer; /* use the earliest time */
624  from->answer = ast_tv(0,0); /* we actively "steal" these values */
625  }
626  /* we got the earliest answer time, so we'll settle for that? */
627  } else {
628  to->answer = from->answer;
629  from->answer = ast_tv(0,0); /* we actively "steal" these values */
630  }
631  }
632  if (!ast_tvzero(from->end)) {
633  if (!ast_tvzero(to->end)) {
634  if (ast_tvcmp(to->end, from->end) < 0 ) {
635  to->end = from->end; /* use the latest time */
636  from->end = ast_tv(0,0); /* we actively "steal" these values */
637  to->duration = to->end.tv_sec - to->start.tv_sec; /* don't forget to update the duration, billsec, when we set end */
638  to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
639  }
640  /* else, nothing to do */
641  } else {
642  to->end = from->end;
643  from->end = ast_tv(0,0); /* we actively "steal" these values */
644  to->duration = to->end.tv_sec - to->start.tv_sec;
645  to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
646  }
647  }
648  if (to->disposition < from->disposition) {
649  to->disposition = from->disposition;
651  }
652  if (ast_strlen_zero(to->lastapp) && !ast_strlen_zero(from->lastapp)) {
653  ast_copy_string(to->lastapp, from->lastapp, sizeof(to->lastapp));
654  from->lastapp[0] = 0; /* theft */
655  }
656  if (ast_strlen_zero(to->lastdata) && !ast_strlen_zero(from->lastdata)) {
657  ast_copy_string(to->lastdata, from->lastdata, sizeof(to->lastdata));
658  from->lastdata[0] = 0; /* theft */
659  }
660  if (ast_strlen_zero(to->dcontext) && !ast_strlen_zero(from->dcontext)) {
661  ast_copy_string(to->dcontext, from->dcontext, sizeof(to->dcontext));
662  from->dcontext[0] = 0; /* theft */
663  }
665  ast_copy_string(to->dstchannel, from->dstchannel, sizeof(to->dstchannel));
666  from->dstchannel[0] = 0; /* theft */
667  }
668  if (!ast_strlen_zero(from->channel) && (ast_strlen_zero(to->channel) || !strncasecmp(from->channel, "Agent/", 6))) {
669  ast_copy_string(to->channel, from->channel, sizeof(to->channel));
670  from->channel[0] = 0; /* theft */
671  }
672  if (ast_strlen_zero(to->src) && !ast_strlen_zero(from->src)) {
673  ast_copy_string(to->src, from->src, sizeof(to->src));
674  from->src[0] = 0; /* theft */
675  }
676  if (ast_strlen_zero(to->clid) && !ast_strlen_zero(from->clid)) {
677  ast_copy_string(to->clid, from->clid, sizeof(to->clid));
678  from->clid[0] = 0; /* theft */
679  }
680  if (ast_strlen_zero(to->dst) && !ast_strlen_zero(from->dst)) {
681  ast_copy_string(to->dst, from->dst, sizeof(to->dst));
682  from->dst[0] = 0; /* theft */
683  }
684  if (!to->amaflags)
686  if (!from->amaflags)
687  from->amaflags = AST_CDR_DOCUMENTATION; /* make sure both amaflags are set to something (DOC is default) */
689  to->amaflags = from->amaflags;
690  }
692  ast_copy_string(to->accountcode, from->accountcode, sizeof(to->accountcode));
693  }
695  ast_copy_string(to->peeraccount, from->peeraccount, sizeof(to->peeraccount));
696  }
698  ast_copy_string(to->userfield, from->userfield, sizeof(to->userfield));
699  }
700  /* flags, varsead, ? */
701  cdr_merge_vars(from, to);
702 
713 
714  /* last, but not least, we need to merge any forked CDRs to the 'to' cdr */
715  while (from->next) {
716  /* just rip 'em off the 'from' and insert them on the 'to' */
717  zcdr = from->next;
718  from->next = zcdr->next;
719  zcdr->next = NULL;
720  /* zcdr is now ripped from the current list; */
721  ast_cdr_append(to, zcdr);
722  }
723  if (discard_from)
724  ast_cdr_discard(from);
725 }
726 
727 void ast_cdr_start(struct ast_cdr *cdr)
728 {
729  for (; cdr; cdr = cdr->next) {
730  if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
731  check_post(cdr);
732  cdr->start = ast_tvnow();
733  }
734  }
735 }
736 
737 void ast_cdr_answer(struct ast_cdr *cdr)
738 {
739 
740  for (; cdr; cdr = cdr->next) {
742  continue;
744  continue;
745  check_post(cdr);
746  if (cdr->disposition < AST_CDR_ANSWERED)
748  if (ast_tvzero(cdr->answer))
749  cdr->answer = ast_tvnow();
750  }
751 }
752 
753 void ast_cdr_busy(struct ast_cdr *cdr)
754 {
755 
756  for (; cdr; cdr = cdr->next) {
757  if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
758  check_post(cdr);
759  cdr->disposition = AST_CDR_BUSY;
760  }
761  }
762 }
763 
764 void ast_cdr_failed(struct ast_cdr *cdr)
765 {
766  for (; cdr; cdr = cdr->next) {
767  check_post(cdr);
768  if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
769  check_post(cdr);
770  if (cdr->disposition < AST_CDR_FAILED)
772  }
773  }
774 }
775 
776 void ast_cdr_noanswer(struct ast_cdr *cdr)
777 {
778  while (cdr) {
779  if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
780  check_post(cdr);
782  }
783  cdr = cdr->next;
784  }
785 }
786 
787 /* everywhere ast_cdr_disposition is called, it will call ast_cdr_failed()
788  if ast_cdr_disposition returns a non-zero value */
789 
790 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
791 {
792  int res = 0;
793 
794  for (; cdr; cdr = cdr->next) {
795  switch (cause) { /* handle all the non failure, busy cases, return 0 not to set disposition,
796  return -1 to set disposition to FAILED */
797  case AST_CAUSE_BUSY:
798  ast_cdr_busy(cdr);
799  break;
800  case AST_CAUSE_NO_ANSWER:
801  ast_cdr_noanswer(cdr);
802  break;
803  case AST_CAUSE_NORMAL:
804  break;
805  default:
806  res = -1;
807  }
808  }
809  return res;
810 }
811 
812 void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chann)
813 {
814  for (; cdr; cdr = cdr->next) {
815  if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
816  check_post(cdr);
817  ast_copy_string(cdr->dstchannel, chann, sizeof(cdr->dstchannel));
818  }
819  }
820 }
821 
822 void ast_cdr_setapp(struct ast_cdr *cdr, const char *app, const char *data)
823 {
824 
825  for (; cdr; cdr = cdr->next) {
826  if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
827  check_post(cdr);
828  ast_copy_string(cdr->lastapp, S_OR(app, ""), sizeof(cdr->lastapp));
829  ast_copy_string(cdr->lastdata, S_OR(data, ""), sizeof(cdr->lastdata));
830  }
831  }
832 }
833 
834 void ast_cdr_setanswer(struct ast_cdr *cdr, struct timeval t)
835 {
836 
837  for (; cdr; cdr = cdr->next) {
839  continue;
841  continue;
842  check_post(cdr);
843  cdr->answer = t;
844  }
845 }
846 
847 void ast_cdr_setdisposition(struct ast_cdr *cdr, long int disposition)
848 {
849 
850  for (; cdr; cdr = cdr->next) {
852  continue;
853  check_post(cdr);
854  cdr->disposition = disposition;
855  }
856 }
857 
858 /* set cid info for one record */
859 static void set_one_cid(struct ast_cdr *cdr, struct ast_channel *c)
860 {
861  const char *num;
862 
863  if (!cdr) {
864  return;
865  }
866 
867  /* Grab source from ANI or normal Caller*ID */
868  num = S_COR(c->caller.ani.number.valid, c->caller.ani.number.str,
869  S_COR(c->caller.id.number.valid, c->caller.id.number.str, NULL));
870  ast_callerid_merge(cdr->clid, sizeof(cdr->clid),
871  S_COR(c->caller.id.name.valid, c->caller.id.name.str, NULL), num, "");
872  ast_copy_string(cdr->src, S_OR(num, ""), sizeof(cdr->src));
873  ast_cdr_setvar(cdr, "dnid", S_OR(c->dialed.number.str, ""), 0);
874 
875  if (c->caller.id.subaddress.valid) {
876  ast_cdr_setvar(cdr, "callingsubaddr", S_OR(c->caller.id.subaddress.str, ""), 0);
877  }
878  if (c->dialed.subaddress.valid) {
879  ast_cdr_setvar(cdr, "calledsubaddr", S_OR(c->dialed.subaddress.str, ""), 0);
880  }
881 }
882 
883 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
884 {
885  for (; cdr; cdr = cdr->next) {
887  set_one_cid(cdr, c);
888  }
889  return 0;
890 }
891 
892 static int cdr_seq_inc(struct ast_cdr *cdr)
893 {
894  return (cdr->sequence = ast_atomic_fetchadd_int(&cdr_sequence, +1));
895 }
896 
897 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
898 {
899  for ( ; cdr ; cdr = cdr->next) {
900  if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
901  ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
902  set_one_cid(cdr, c);
903  cdr_seq_inc(cdr);
904 
907  ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
908  ast_copy_string(cdr->peeraccount, c->peeraccount, sizeof(cdr->peeraccount));
909  /* Destination information */
910  ast_copy_string(cdr->dst, S_OR(c->macroexten,c->exten), sizeof(cdr->dst));
911  ast_copy_string(cdr->dcontext, S_OR(c->macrocontext,c->context), sizeof(cdr->dcontext));
912  /* Unique call identifier */
913  ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
914  /* Linked call identifier */
915  ast_copy_string(cdr->linkedid, c->linkedid, sizeof(cdr->linkedid));
916  }
917  }
918  return 0;
919 }
920 
921 /* Three routines were "fixed" via 10668, and later shown that
922  users were depending on this behavior. ast_cdr_end,
923  ast_cdr_setvar and ast_cdr_answer are the three routines.
924  While most of the other routines would not touch
925  LOCKED cdr's, these three routines were designed to
926  operate on locked CDR's as a matter of course.
927  I now appreciate how this plays with the ForkCDR app,
928  which forms these cdr chains in the first place.
929  cdr_end is pretty key: all cdrs created are closed
930  together. They only vary by start time. Arithmetically,
931  users can calculate the subintervals they wish to track. */
932 
933 void ast_cdr_end(struct ast_cdr *cdr)
934 {
935  for ( ; cdr ; cdr = cdr->next) {
937  continue;
938  check_post(cdr);
939  if (ast_tvzero(cdr->end))
940  cdr->end = ast_tvnow();
941  if (ast_tvzero(cdr->start)) {
942  ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", S_OR(cdr->channel, "<unknown>"));
944  } else
945  cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec;
946  if (ast_tvzero(cdr->answer)) {
947  if (cdr->disposition == AST_CDR_ANSWERED) {
948  ast_log(LOG_WARNING, "CDR on channel '%s' has no answer time but is 'ANSWERED'\n", S_OR(cdr->channel, "<unknown>"));
950  }
951  } else {
952  cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec;
954  cdr->billsec += cdr->end.tv_usec > cdr->answer.tv_usec ? 1 : 0;
955  }
956  }
957 }
958 
960 {
961  switch (disposition) {
962  case AST_CDR_NULL:
963  return "NO ANSWER"; /* by default, for backward compatibility */
964  case AST_CDR_NOANSWER:
965  return "NO ANSWER";
966  case AST_CDR_FAILED:
967  return "FAILED";
968  case AST_CDR_BUSY:
969  return "BUSY";
970  case AST_CDR_ANSWERED:
971  return "ANSWERED";
972  }
973  return "UNKNOWN";
974 }
975 
976 /*! Converts AMA flag to printable string */
977 char *ast_cdr_flags2str(int flag)
978 {
979  switch (flag) {
980  case AST_CDR_OMIT:
981  return "OMIT";
982  case AST_CDR_BILLING:
983  return "BILLING";
985  return "DOCUMENTATION";
986  }
987  return "Unknown";
988 }
989 
990 int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
991 {
992  struct ast_cdr *cdr = chan->cdr;
993  const char *old_acct = "";
994 
995  if (!ast_strlen_zero(chan->accountcode)) {
996  old_acct = ast_strdupa(chan->accountcode);
997  }
998 
999  ast_string_field_set(chan, accountcode, account);
1000  for ( ; cdr ; cdr = cdr->next) {
1001  if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
1002  ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode));
1003  }
1004  }
1005 
1006  ast_manager_event(chan, EVENT_FLAG_CALL, "NewAccountCode",
1007  "Channel: %s\r\n"
1008  "Uniqueid: %s\r\n"
1009  "AccountCode: %s\r\n"
1010  "OldAccountCode: %s\r\n",
1011  chan->name, chan->uniqueid, chan->accountcode, old_acct);
1012 
1013  return 0;
1014 }
1015 
1016 int ast_cdr_setpeeraccount(struct ast_channel *chan, const char *account)
1017 {
1018  struct ast_cdr *cdr = chan->cdr;
1019  const char *old_acct = "";
1020 
1021  if (!ast_strlen_zero(chan->peeraccount)) {
1022  old_acct = ast_strdupa(chan->peeraccount);
1023  }
1024 
1025  ast_string_field_set(chan, peeraccount, account);
1026  for ( ; cdr ; cdr = cdr->next) {
1027  if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
1028  ast_copy_string(cdr->peeraccount, chan->peeraccount, sizeof(cdr->peeraccount));
1029  }
1030  }
1031 
1032  ast_manager_event(chan, EVENT_FLAG_CALL, "NewPeerAccount",
1033  "Channel: %s\r\n"
1034  "Uniqueid: %s\r\n"
1035  "PeerAccount: %s\r\n"
1036  "OldPeerAccount: %s\r\n",
1037  chan->name, chan->uniqueid, chan->peeraccount, old_acct);
1038 
1039  return 0;
1040 }
1041 
1042 int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
1043 {
1044  struct ast_cdr *cdr;
1045  int newflag = ast_cdr_amaflags2int(flag);
1046  if (newflag) {
1047  for (cdr = chan->cdr; cdr; cdr = cdr->next) {
1048  if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
1049  cdr->amaflags = newflag;
1050  }
1051  }
1052  }
1053 
1054  return 0;
1055 }
1056 
1057 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
1058 {
1059  struct ast_cdr *cdr = chan->cdr;
1060 
1061  for ( ; cdr ; cdr = cdr->next) {
1063  ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield));
1064  }
1065 
1066  return 0;
1067 }
1068 
1069 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
1070 {
1071  struct ast_cdr *cdr = chan->cdr;
1072 
1073  for ( ; cdr ; cdr = cdr->next) {
1074  int len = strlen(cdr->userfield);
1075 
1077  ast_copy_string(cdr->userfield + len, userfield, sizeof(cdr->userfield) - len);
1078  }
1079 
1080  return 0;
1081 }
1082 
1084 {
1085  struct ast_cdr *cdr = c->cdr;
1086 
1087  for ( ; cdr ; cdr = cdr->next) {
1088  if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
1089  set_one_cid(cdr, c);
1090 
1091  /* Copy account code et-al */
1092  ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
1093  ast_copy_string(cdr->peeraccount, c->peeraccount, sizeof(cdr->peeraccount));
1094  ast_copy_string(cdr->linkedid, c->linkedid, sizeof(cdr->linkedid));
1095 
1096  /* Destination information */ /* XXX privilege macro* ? */
1097  ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst));
1098  ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext));
1099  }
1100  }
1101 
1102  return 0;
1103 }
1104 
1105 int ast_cdr_amaflags2int(const char *flag)
1106 {
1107  if (!strcasecmp(flag, "default"))
1108  return 0;
1109  if (!strcasecmp(flag, "omit"))
1110  return AST_CDR_OMIT;
1111  if (!strcasecmp(flag, "billing"))
1112  return AST_CDR_BILLING;
1113  if (!strcasecmp(flag, "documentation"))
1114  return AST_CDR_DOCUMENTATION;
1115  return -1;
1116 }
1117 
1118 static void post_cdr(struct ast_cdr *cdr)
1119 {
1120  struct ast_cdr_beitem *i;
1121 
1122  for ( ; cdr ; cdr = cdr->next) {
1124  /* For people, who don't want to see unanswered single-channel events */
1126  continue;
1127  }
1128 
1129  /* don't post CDRs that are for dialed channels unless those
1130  * channels were originated from asterisk (pbx_spool, manager,
1131  * cli) */
1134  continue;
1135  }
1136 
1137  check_post(cdr);
1140  continue;
1142  AST_RWLIST_TRAVERSE(&be_list, i, list) {
1143  i->be(cdr);
1144  }
1146  }
1147 }
1148 
1149 void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
1150 {
1151  struct ast_cdr *duplicate;
1152  struct ast_flags flags = { 0 };
1153 
1154  if (_flags)
1155  ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
1156 
1157  for ( ; cdr ; cdr = cdr->next) {
1158  /* Detach if post is requested */
1160  if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
1161  ast_cdr_end(cdr);
1162  if ((duplicate = ast_cdr_dup_unique_swap(cdr))) {
1163  ast_cdr_detach(duplicate);
1164  }
1166  }
1167 
1168  /* enable CDR only */
1169  if (ast_test_flag(&flags, AST_CDR_FLAG_POST_ENABLE)) {
1171  continue;
1172  }
1173 
1174  /* clear variables */
1175  if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
1176  ast_cdr_free_vars(cdr, 0);
1177  }
1178 
1179  /* Reset to initial state */
1181  memset(&cdr->start, 0, sizeof(cdr->start));
1182  memset(&cdr->end, 0, sizeof(cdr->end));
1183  memset(&cdr->answer, 0, sizeof(cdr->answer));
1184  cdr->billsec = 0;
1185  cdr->duration = 0;
1186  ast_cdr_start(cdr);
1188  }
1189  }
1190 }
1191 
1192 void ast_cdr_specialized_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
1193 {
1194  struct ast_flags flags = { 0 };
1195 
1196  if (_flags)
1197  ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
1198 
1199  /* Reset to initial state */
1200  if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED)) { /* But do NOT lose the NoCDR() setting */
1203  } else {
1205  }
1206 
1207  memset(&cdr->start, 0, sizeof(cdr->start));
1208  memset(&cdr->end, 0, sizeof(cdr->end));
1209  memset(&cdr->answer, 0, sizeof(cdr->answer));
1210  cdr->billsec = 0;
1211  cdr->duration = 0;
1212  ast_cdr_start(cdr);
1213  cdr->disposition = AST_CDR_NULL;
1214 }
1215 
1216 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
1217 {
1218  struct ast_cdr *ret;
1219 
1220  if (cdr) {
1221  ret = cdr;
1222 
1223  while (cdr->next)
1224  cdr = cdr->next;
1225  cdr->next = newcdr;
1226  } else {
1227  ret = newcdr;
1228  }
1229 
1230  return ret;
1231 }
1232 
1233 /*! \note Don't call without cdr_batch_lock */
1234 static void reset_batch(void)
1235 {
1236  batch->size = 0;
1237  batch->head = NULL;
1238  batch->tail = NULL;
1239 }
1240 
1241 /*! \note Don't call without cdr_batch_lock */
1242 static int init_batch(void)
1243 {
1244  /* This is the single meta-batch used to keep track of all CDRs during the entire life of the program */
1245  if (!(batch = ast_malloc(sizeof(*batch))))
1246  return -1;
1247 
1248  reset_batch();
1249 
1250  return 0;
1251 }
1252 
1253 static void *do_batch_backend_process(void *data)
1254 {
1255  struct ast_cdr_batch_item *processeditem;
1256  struct ast_cdr_batch_item *batchitem = data;
1257 
1258  /* Push each CDR into storage mechanism(s) and free all the memory */
1259  while (batchitem) {
1260  post_cdr(batchitem->cdr);
1261  ast_cdr_free(batchitem->cdr);
1262  processeditem = batchitem;
1263  batchitem = batchitem->next;
1264  ast_free(processeditem);
1265  }
1266 
1267  return NULL;
1268 }
1269 
1270 void ast_cdr_submit_batch(int do_shutdown)
1271 {
1272  struct ast_cdr_batch_item *oldbatchitems = NULL;
1273  pthread_t batch_post_thread = AST_PTHREADT_NULL;
1274 
1275  /* if there's no batch, or no CDRs in the batch, then there's nothing to do */
1276  if (!batch || !batch->head)
1277  return;
1278 
1279  /* move the old CDRs aside, and prepare a new CDR batch */
1281  oldbatchitems = batch->head;
1282  reset_batch();
1284 
1285  /* if configured, spawn a new thread to post these CDRs,
1286  also try to save as much as possible if we are shutting down safely */
1287  if (batchscheduleronly || do_shutdown) {
1288  ast_debug(1, "CDR single-threaded batch processing begins now\n");
1289  do_batch_backend_process(oldbatchitems);
1290  } else {
1291  if (ast_pthread_create_detached_background(&batch_post_thread, NULL, do_batch_backend_process, oldbatchitems)) {
1292  ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
1293  do_batch_backend_process(oldbatchitems);
1294  } else {
1295  ast_debug(1, "CDR multi-threaded batch processing begins now\n");
1296  }
1297  }
1298 }
1299 
1300 static int submit_scheduled_batch(const void *data)
1301 {
1303  /* manually reschedule from this point in time */
1307  /* returning zero so the scheduler does not automatically reschedule */
1308  return 0;
1309 }
1310 
1311 /*! Do not hold the batch lock while calling this function */
1312 static void submit_unscheduled_batch(void)
1313 {
1314  /* Prevent two deletes from happening at the same time */
1316  /* this is okay since we are not being called from within the scheduler */
1318  /* schedule the submission to occur ASAP (1 ms) */
1321 
1322  /* signal the do_cdr thread to wakeup early and do some work (that lazy thread ;) */
1326 }
1327 
1329 {
1330  struct ast_cdr_batch_item *newtail;
1331  int curr;
1332  int submit_batch = 0;
1333 
1334  if (!cdr)
1335  return;
1336 
1337  /* maybe they disabled CDR stuff completely, so just drop it */
1338  if (!enabled) {
1339  ast_debug(1, "Dropping CDR !\n");
1341  ast_cdr_free(cdr);
1342  return;
1343  }
1344 
1345  /* post stuff immediately if we are not in batch mode, this is legacy behaviour */
1346  if (!batchmode) {
1347  post_cdr(cdr);
1348  ast_cdr_free(cdr);
1349  return;
1350  }
1351 
1352  /* otherwise, each CDR gets put into a batch list (at the end) */
1353  ast_debug(1, "CDR detaching from this thread\n");
1354 
1355  /* we'll need a new tail for every CDR */
1356  if (!(newtail = ast_calloc(1, sizeof(*newtail)))) {
1357  post_cdr(cdr);
1358  ast_cdr_free(cdr);
1359  return;
1360  }
1361 
1362  /* don't traverse a whole list (just keep track of the tail) */
1364  if (!batch)
1365  init_batch();
1366  if (!batch->head) {
1367  /* new batch is empty, so point the head at the new tail */
1368  batch->head = newtail;
1369  } else {
1370  /* already got a batch with something in it, so just append a new tail */
1371  batch->tail->next = newtail;
1372  }
1373  newtail->cdr = cdr;
1374  batch->tail = newtail;
1375  curr = batch->size++;
1376 
1377  /* if we have enough stuff to post, then do it */
1378  if (curr >= (batchsize - 1)) {
1379  submit_batch = 1;
1380  }
1382 
1383  /* Don't call submit_unscheduled_batch with the cdr_batch_lock held */
1384  if (submit_batch) {
1386  }
1387 }
1388 
1389 static void *do_cdr(void *data)
1390 {
1391  struct timespec timeout;
1392  int schedms;
1393  int numevents = 0;
1394 
1395  for (;;) {
1396  struct timeval now;
1397  schedms = ast_sched_wait(sched);
1398  /* this shouldn't happen, but provide a 1 second default just in case */
1399  if (schedms <= 0)
1400  schedms = 1000;
1401  now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000));
1402  timeout.tv_sec = now.tv_sec;
1403  timeout.tv_nsec = now.tv_usec * 1000;
1404  /* prevent stuff from clobbering cdr_pending_cond, then wait on signals sent to it until the timeout expires */
1407  numevents = ast_sched_runq(sched);
1409  ast_debug(2, "Processed %d scheduled CDR batches from the run queue\n", numevents);
1410  }
1411 
1412  return NULL;
1413 }
1414 
1415 static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1416 {
1417  struct ast_cdr_beitem *beitem=NULL;
1418  int cnt=0;
1419  long nextbatchtime=0;
1420 
1421  switch (cmd) {
1422  case CLI_INIT:
1423  e->command = "cdr show status";
1424  e->usage =
1425  "Usage: cdr show status\n"
1426  " Displays the Call Detail Record engine system status.\n";
1427  return NULL;
1428  case CLI_GENERATE:
1429  return NULL;
1430  }
1431 
1432  if (a->argc > 3)
1433  return CLI_SHOWUSAGE;
1434 
1435  ast_cli(a->fd, "\n");
1436  ast_cli(a->fd, "Call Detail Record (CDR) settings\n");
1437  ast_cli(a->fd, "----------------------------------\n");
1438  ast_cli(a->fd, " Logging: %s\n", enabled ? "Enabled" : "Disabled");
1439  ast_cli(a->fd, " Mode: %s\n", batchmode ? "Batch" : "Simple");
1440  if (enabled) {
1441  ast_cli(a->fd, " Log unanswered calls: %s\n\n", unanswered ? "Yes" : "No");
1442  if (batchmode) {
1443  ast_cli(a->fd, "* Batch Mode Settings\n");
1444  ast_cli(a->fd, " -------------------\n");
1445  if (batch)
1446  cnt = batch->size;
1447  if (cdr_sched > -1)
1448  nextbatchtime = ast_sched_when(sched, cdr_sched);
1449  ast_cli(a->fd, " Safe shutdown: %s\n", batchsafeshutdown ? "Enabled" : "Disabled");
1450  ast_cli(a->fd, " Threading model: %s\n", batchscheduleronly ? "Scheduler only" : "Scheduler plus separate threads");
1451  ast_cli(a->fd, " Current batch size: %d record%s\n", cnt, ESS(cnt));
1452  ast_cli(a->fd, " Maximum batch size: %d record%s\n", batchsize, ESS(batchsize));
1453  ast_cli(a->fd, " Maximum batch time: %d second%s\n", batchtime, ESS(batchtime));
1454  ast_cli(a->fd, " Next batch processing time: %ld second%s\n\n", nextbatchtime, ESS(nextbatchtime));
1455  }
1456  ast_cli(a->fd, "* Registered Backends\n");
1457  ast_cli(a->fd, " -------------------\n");
1459  if (AST_RWLIST_EMPTY(&be_list)) {
1460  ast_cli(a->fd, " (none)\n");
1461  } else {
1462  AST_RWLIST_TRAVERSE(&be_list, beitem, list) {
1463  ast_cli(a->fd, " %s\n", beitem->name);
1464  }
1465  }
1467  ast_cli(a->fd, "\n");
1468  }
1469 
1470  return CLI_SUCCESS;
1471 }
1472 
1473 static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1474 {
1475  switch (cmd) {
1476  case CLI_INIT:
1477  e->command = "cdr submit";
1478  e->usage =
1479  "Usage: cdr submit\n"
1480  " Posts all pending batched CDR data to the configured CDR backend engine modules.\n";
1481  return NULL;
1482  case CLI_GENERATE:
1483  return NULL;
1484  }
1485  if (a->argc > 2)
1486  return CLI_SHOWUSAGE;
1487 
1489  ast_cli(a->fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
1490 
1491  return CLI_SUCCESS;
1492 }
1493 
1494 static struct ast_cli_entry cli_submit = AST_CLI_DEFINE(handle_cli_submit, "Posts all pending batched CDR data");
1495 static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CDR status");
1496 
1497 static void do_reload(int reload)
1498 {
1499  struct ast_config *config;
1500  const char *enabled_value;
1501  const char *unanswered_value;
1502  const char *batched_value;
1503  const char *scheduleronly_value;
1504  const char *batchsafeshutdown_value;
1505  const char *size_value;
1506  const char *time_value;
1507  const char *end_before_h_value;
1508  const char *initiatedseconds_value;
1509  int cfg_size;
1510  int cfg_time;
1511  int was_enabled;
1512  int was_batchmode;
1513  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1514 
1515  if ((config = ast_config_load2("cdr.conf", "cdr", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
1516  return;
1517  }
1518 
1520 
1521  was_enabled = enabled;
1522  was_batchmode = batchmode;
1523 
1531 
1532  if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
1534  return;
1535  }
1536 
1537  /* don't run the next scheduled CDR posting while reloading */
1541 
1542  if (config) {
1543  if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
1544  enabled = ast_true(enabled_value);
1545  }
1546  if ((unanswered_value = ast_variable_retrieve(config, "general", "unanswered"))) {
1547  unanswered = ast_true(unanswered_value);
1548  }
1549  if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
1550  batchmode = ast_true(batched_value);
1551  }
1552  if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
1553  batchscheduleronly = ast_true(scheduleronly_value);
1554  }
1555  if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
1556  batchsafeshutdown = ast_true(batchsafeshutdown_value);
1557  }
1558  if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
1559  if (sscanf(size_value, "%30d", &cfg_size) < 1)
1560  ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
1561  else if (cfg_size < 0)
1562  ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
1563  else
1564  batchsize = cfg_size;
1565  }
1566  if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
1567  if (sscanf(time_value, "%30d", &cfg_time) < 1)
1568  ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
1569  else if (cfg_time < 0)
1570  ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
1571  else
1572  batchtime = cfg_time;
1573  }
1574  if ((end_before_h_value = ast_variable_retrieve(config, "general", "endbeforehexten")))
1576  if ((initiatedseconds_value = ast_variable_retrieve(config, "general", "initiatedseconds")))
1578  }
1579 
1580  if (enabled && !batchmode) {
1581  ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
1582  } else if (enabled && batchmode) {
1586  ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
1587  } else {
1588  ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
1589  }
1590 
1591  /* if this reload enabled the CDR batch mode, create the background thread
1592  if it does not exist */
1593  if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
1595  if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
1596  ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
1600  } else {
1603  }
1604  /* if this reload disabled the CDR and/or batch mode and there is a background thread,
1605  kill it */
1606  } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
1607  /* wake up the thread so it will exit */
1608  pthread_cancel(cdr_thread);
1609  pthread_kill(cdr_thread, SIGURG);
1610  pthread_join(cdr_thread, NULL);
1615  /* if leaving batch mode, then post the CDRs in the batch,
1616  and don't reschedule, since we are stopping CDR logging */
1617  if (!batchmode && was_batchmode) {
1619  }
1620  }
1621 
1623  ast_config_destroy(config);
1624  manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: CDR\r\nMessage: CDR subsystem reload requested\r\n");
1625 }
1626 
1627 static void cdr_engine_shutdown(void)
1628 {
1629  if (cdr_thread != AST_PTHREADT_NULL) {
1630  /* wake up the thread so it will exit */
1631  pthread_cancel(cdr_thread);
1632  pthread_kill(cdr_thread, SIGURG);
1633  pthread_join(cdr_thread, NULL);
1636  }
1638 
1641  sched = NULL;
1642  ast_free(batch);
1643  batch = NULL;
1644 }
1645 
1647 {
1649  if (!sched) {
1650  ast_log(LOG_ERROR, "Unable to create schedule context.\n");
1651  return -1;
1652  }
1653 
1655  do_reload(0);
1657 
1658  return 0;
1659 }
1660 
1661 /* \note This actually gets called a couple of times at shutdown. Once, before we start
1662  hanging up channels, and then again, after the channel hangup timeout expires */
1664 {
1666 }
1667 
1669 {
1670  do_reload(1);
1671  return 0;
1672 }
1673 
1674 int ast_cdr_data_add_structure(struct ast_data *tree, struct ast_cdr *cdr, int recur)
1675 {
1676  struct ast_cdr *tmpcdr;
1677  struct ast_data *level;
1678  struct ast_var_t *variables;
1679  const char *var, *val;
1680  int x = 1, i;
1681  char workspace[256];
1682  char *tmp;
1683 
1684  if (!cdr) {
1685  return -1;
1686  }
1687 
1688  for (tmpcdr = cdr; tmpcdr; tmpcdr = (recur ? tmpcdr->next : NULL)) {
1689  level = ast_data_add_node(tree, "level");
1690  if (!level) {
1691  continue;
1692  }
1693 
1694  ast_data_add_int(level, "level_number", x);
1695 
1696  AST_LIST_TRAVERSE(&tmpcdr->varshead, variables, entries) {
1697  if (variables && (var = ast_var_name(variables)) &&
1698  (val = ast_var_value(variables)) && !ast_strlen_zero(var)
1699  && !ast_strlen_zero(val)) {
1700  ast_data_add_str(level, var, val);
1701  } else {
1702  break;
1703  }
1704  }
1705 
1706  for (i = 0; cdr_readonly_vars[i]; i++) {
1707  workspace[0] = 0; /* null out the workspace, because the cdr_get_tv() won't write anything if time is NULL, so you get old vals */
1708  ast_cdr_getvar(tmpcdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
1709  if (!tmp) {
1710  continue;
1711  }
1712  ast_data_add_str(level, cdr_readonly_vars[i], tmp);
1713  }
1714 
1715  x++;
1716  }
1717 
1718  return 0;
1719 }
1720 
static void set_one_cid(struct ast_cdr *cdr, struct ast_channel *c)
Definition: cdr.c:859
int ast_cdr_setamaflags(struct ast_channel *chan, const char *amaflags)
Set AMA flags for channel.
Definition: cdr.c:1042
const ast_string_field peeraccount
Definition: channel.h:787
int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
Save the result of the call based on the AST_CAUSE_*.
Definition: cdr.c:790
void ast_cdr_submit_batch(int shutdown)
Spawns (possibly) a new thread to submit a batch of CDRs to the backend engines.
Definition: cdr.c:1270
static int batchsize
Definition: cdr.c:100
int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur)
Definition: cdr.c:343
static char accountcode[AST_MAX_ACCOUNT_CODE]
Definition: chan_iax2.c:383
int ast_cdr_isset_unanswered(void)
Definition: cdr.c:185
Main Channel structure associated with a channel.
Definition: channel.h:742
char accountcode[AST_MAX_ACCOUNT_CODE]
Definition: cdr.h:114
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
void ast_cdr_failed(struct ast_cdr *cdr)
Fail a call.
Definition: cdr.c:764
char * str
Subscriber phone number (Malloced)
Definition: channel.h:241
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
Definition: config.c:625
static const char config[]
Definition: cdr_csv.c:57
The data tree to be returned by the callbacks and managed by functions local to this file...
Definition: data.c:85
char * str
Subscriber phone number (Malloced)
Definition: channel.h:336
struct ast_party_caller caller
Channel Caller ID information.
Definition: channel.h:804
static void reset_batch(void)
Definition: cdr.c:1234
static void * do_batch_backend_process(void *data)
Definition: cdr.c:1253
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
Definition: linkedlists.h:332
const ast_string_field uniqueid
Definition: channel.h:787
static const int UNANSWERED_DEFAULT
Definition: cdr.c:98
static struct ast_cdr_batch * batch
int ast_cli_register(struct ast_cli_entry *e)
Registers a command or an array of commands.
Definition: cli.c:2159
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
Definition: ast_expr2.c:325
char dstchannel[AST_MAX_EXTENSION]
Definition: cdr.h:94
void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *flags)
Reset the detail record, optionally posting it first.
Definition: cdr.c:1149
static void * do_cdr(void *data)
Definition: cdr.c:1389
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_test_flag(p, flag)
Definition: utils.h:63
ast_cdrbe be
Definition: cdr.c:65
void ast_cdr_end(struct ast_cdr *cdr)
End a call.
Definition: cdr.c:933
static int batchmode
Definition: cdr.c:94
const char * ast_var_value(const struct ast_var_t *var)
Definition: chanvars.c:89
struct ast_party_name name
Subscriber name.
Definition: channel.h:290
int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, const void *data) attribute_warn_unused_result
Adds a scheduled event Schedule an event to take place at some point in the future. callback will be called with data as the argument, when milliseconds into the future (approximately) If callback returns 0, no further events will be re-scheduled.
Definition: sched.c:446
struct ast_var_t * ast_var_assign(const char *name, const char *value)
Definition: chanvars.c:41
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
char context[AST_MAX_CONTEXT]
Definition: channel.h:868
const char * ast_var_name(const struct ast_var_t *var)
Definition: chanvars.c:69
int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
Append to CDR user field for channel (stored in CDR)
Definition: cdr.c:1069
long int billsec
Definition: cdr.h:108
#define ast_set_flag(p, flag)
Definition: utils.h:70
int ast_cli_unregister(struct ast_cli_entry *e)
Unregisters a command or an array of commands.
Definition: cli.c:2153
descriptor for a cli entry.
Definition: cli.h:165
const int argc
Definition: cli.h:154
static void post_cdr(struct ast_cdr *cdr)
Definition: cdr.c:1118
#define LOG_WARNING
Definition: logger.h:144
static int batchsafeshutdown
Definition: cdr.c:109
struct ast_cdr * next
Definition: cdr.h:132
static int cdr_seq_inc(struct ast_cdr *cdr)
Definition: cdr.c:892
char dcontext[AST_MAX_EXTENSION]
Definition: cdr.h:90
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
Set account code, will generate AMI event.
Definition: cdr.c:990
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1570
Data retrieval API.
static int batchscheduleronly
Definition: cdr.c:106
static void do_reload(int reload)
Definition: cdr.c:1497
#define var
Definition: ast_expr2f.c:606
static char * handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: cdr.c:1415
#define AST_MAX_ACCOUNT_CODE
Definition: cdr.h:73
struct ast_cdr * ast_cdr_dup(struct ast_cdr *cdr)
Duplicate a record.
Definition: cdr.c:213
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:100
char uniqueid[150]
Definition: cdr.h:121
static ast_mutex_t cdr_sched_lock
Definition: cdr.c:112
static void cdr_engine_shutdown(void)
Definition: cdr.c:1627
void ast_cdr_setdisposition(struct ast_cdr *cdr, long int disposition)
Set the disposition for a call.
Definition: cdr.c:847
Definition: sched.c:57
#define EVENT_FLAG_CALL
Definition: manager.h:72
Definition: cli.h:146
Configuration File Parser.
int sequence
Definition: cdr.h:127
char * str
Subscriber name (Malloced)
Definition: channel.h:214
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:900
#define ast_cond_init(cond, attr)
Definition: lock.h:167
unsigned char valid
TRUE if the subaddress information is valid/present.
Definition: channel.h:278
static int submit_scheduled_batch(const void *data)
Definition: cdr.c:1300
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:142
static const int BATCH_TIME_DEFAULT
Definition: cdr.c:104
#define ast_mutex_lock(a)
Definition: lock.h:155
struct ast_cdr * cdr
Definition: channel.h:766
struct ast_cdr_batch_item * head
Definition: cdr.c:78
static const int ENABLED_DEFAULT
Definition: cdr.c:92
#define ast_copy_flags(dest, src, flagz)
Definition: utils.h:84
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:90
static const int BATCH_SCHEDULER_ONLY_DEFAULT
Definition: cdr.c:107
char * str
Malloced subaddress string.
Definition: channel.h:263
char * ast_cdr_flags2str(int flags)
Definition: cdr.c:977
int value
Definition: syslog.c:39
void ast_cli(int fd, const char *fmt,...)
Definition: cli.c:105
int ast_default_amaflags
Definition: cdr.c:59
const ast_string_field linkedid
Definition: channel.h:787
static const char * ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur)
Definition: cdr.c:232
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:600
#define ast_cond_signal(cond)
Definition: lock.h:169
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
Definition: config.c:2499
void ast_var_delete(struct ast_var_t *var)
Definition: chanvars.c:63
#define ast_pthread_create_detached_background(a, b, c, d)
Definition: utils.h:431
#define ast_verb(level,...)
Definition: logger.h:243
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return * the previous value of *p. This can be used to handle reference co...
Definition: lock.h:603
String fields in structures.
Utility functions.
int(* ast_cdrbe)(struct ast_cdr *cdr)
CDR backend callback.
Definition: cdr.h:148
pthread_cond_t ast_cond_t
Definition: lock.h:144
int ast_cdr_data_add_structure(struct ast_data *tree, struct ast_cdr *cdr, int recur)
Definition: cdr.c:1674
#define ast_manager_event(chan, category, event, contents,...)
Definition: manager.h:221
int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
Set CDR user field for channel (stored in CDR)
Definition: cdr.c:1057
struct ast_cdr_batch_item * tail
Definition: cdr.c:79
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:426
#define CONFIG_STATUS_FILEMISSING
Definition: config.h:50
struct varshead varshead
Definition: cdr.h:130
void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw)
Definition: cdr.c:264
Call Detail Record API.
struct ast_party_id id
Caller party ID.
Definition: channel.h:370
int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *chan)
Initialize based on a channel.
Definition: cdr.c:883
char lastdata[AST_MAX_EXTENSION]
Definition: cdr.h:98
char desc[80]
Definition: cdr.c:64
struct ast_cdr * ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
Definition: cdr.c:1216
long int amaflags
Definition: cdr.h:112
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
#define AST_RWLIST_INSERT_HEAD
Definition: linkedlists.h:703
#define EVENT_FLAG_SYSTEM
Definition: manager.h:71
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR handling engine.
Definition: cdr.c:130
static const char app[]
Definition: app_adsiprog.c:49
char * ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
Definition: callerid.c:1074
struct ast_party_id ani
Automatic Number Identification (ANI)
Definition: channel.h:377
General Asterisk PBX channel definitions.
struct ast_party_dialed::@155 number
Dialed/Called number.
#define AST_SCHED_DEL(sched, id)
a loop construct to ensure that the scheduled task get deleted. The idea is that if we loop attemptin...
Definition: sched.h:51
void ast_unregister_atexit(void(*func)(void))
Unregister a function registered with ast_register_atexit().
Definition: asterisk.c:1008
const int fd
Definition: cli.h:153
#define AST_PTHREADT_NULL
Definition: lock.h:65
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
struct ast_data * ast_data_add_node(struct ast_data *root, const char *childname)
Add a container child.
Definition: data.c:2317
struct ast_cdr * cdr
Definition: cdr.c:72
char linkedid[32]
Definition: cdr.h:123
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:493
Scheduler Routines (derived from cheops)
void ast_cdr_answer(struct ast_cdr *cdr)
Answer a call.
Definition: cdr.c:737
char ast_default_accountcode[AST_MAX_ACCOUNT_CODE]
Definition: cdr.c:60
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:554
static ast_mutex_t cdr_pending_lock
Definition: cdr.c:117
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:83
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
Definition: asterisk.c:998
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
Definition: time.h:191
void ast_cdr_setanswer(struct ast_cdr *cdr, struct timeval t)
Set the answer time for a call.
Definition: cdr.c:834
#define AST_RWLIST_REMOVE_CURRENT
Definition: linkedlists.h:565
static const char *const cdr_readonly_vars[]
Definition: cdr.c:336
static void cdr_get_tv(struct timeval when, const char *fmt, char *buf, int bufsize)
Definition: cdr.c:249
char dst[AST_MAX_EXTENSION]
Definition: cdr.h:88
static int init_batch(void)
Definition: cdr.c:1242
A set of macros to manage forward-linked lists.
char channel[AST_MAX_EXTENSION]
Definition: cdr.h:92
static const int BATCH_SAFE_SHUTDOWN_DEFAULT
Definition: cdr.c:110
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
#define AST_CAUSE_NO_ANSWER
Definition: causes.h:108
#define AST_LIST_MOVE_CURRENT(newhead, field)
Definition: linkedlists.h:567
struct ast_cdr * ast_cdr_dup_unique_swap(struct ast_cdr *cdr)
Duplicate a record and increment the sequence number of the old record.
Definition: cdr.c:200
struct ast_cdr_batch_item * next
Definition: cdr.c:73
int ast_cdr_update(struct ast_channel *chan)
Update CDR on a channel.
Definition: cdr.c:1083
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:542
static struct sched_context * sched
Definition: chan_gtalk.c:227
struct ast_party_dialed dialed
Dialed/Called information.
Definition: channel.h:797
#define AST_CAUSE_NORMAL
Definition: causes.h:150
int ast_cdr_engine_reload(void)
Reload the configuration file cdr.conf and start/stop CDR scheduling thread.
Definition: cdr.c:1668
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
static int reload(void)
Definition: app_amd.c:497
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
static void check_post(struct ast_cdr *cdr)
print a warning if cdr already posted
Definition: cdr.c:467
#define AST_RWLIST_EMPTY
Definition: linkedlists.h:451
struct timeval answer
Definition: cdr.h:102
struct ast_party_subaddress subaddress
Subscriber subaddress.
Definition: channel.h:294
void ast_cdr_engine_term(void)
Definition: cdr.c:1663
Responsible for call detail data.
Definition: cdr.h:82
char lastapp[AST_MAX_EXTENSION]
Definition: cdr.h:96
int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char delim, char sep, int recur)
Definition: cdr.c:409
void ast_cdr_start(struct ast_cdr *cdr)
Start a call.
Definition: cdr.c:727
#define LOG_ERROR
Definition: logger.h:155
int ast_tvcmp(struct timeval _a, struct timeval _b)
Compres two struct timeval instances returning -1, 0, 1 if the first arg is smaller, equal or greater to the second.
Definition: time.h:120
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is &quot;true&quot;. This function checks to see whether a string passed to it is an indication of an &quot;true&quot; value. It checks to see if the string is &quot;yes&quot;, &quot;true&quot;, &quot;y&quot;, &quot;t&quot;, &quot;on&quot; or &quot;1&quot;.
Definition: utils.c:1533
void sched_context_destroy(struct sched_context *c)
destroys a schedule context Destroys (free&#39;s) the given sched_context structure
Definition: sched.c:267
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
int size
Definition: cdr.c:77
#define CLI_SHOWUSAGE
Definition: cli.h:44
long ast_sched_when(struct sched_context *con, int id)
Returns the number of seconds before an event takes place.
Definition: sched.c:664
static int batchtime
Definition: cdr.c:103
static ast_cond_t cdr_pending_cond
Definition: cdr.c:118
static ast_mutex_t cdr_batch_lock
Definition: cdr.c:114
static const int BATCH_SIZE_DEFAULT
Definition: cdr.c:101
static int cdr_sched
Definition: cdr.c:88
static int cdr_sequence
Definition: cdr.c:83
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int ast_cdr_amaflags2int(const char *flag)
Convert a string to a detail record AMA flag.
Definition: cdr.c:1105
void ast_cdr_detach(struct ast_cdr *cdr)
Detaches the detail record for posting (and freeing) either now or at a later time in bulk with other...
Definition: cdr.c:1328
enum ast_channel_state _state
Definition: channel.h:839
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: utils.c:1587
const ast_string_field name
Definition: channel.h:787
void ast_cdr_specialized_reset(struct ast_cdr *cdr, struct ast_flags *flags)
Definition: cdr.c:1192
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define ast_cond_destroy(cond)
Definition: lock.h:168
#define LOG_NOTICE
Definition: logger.h:133
void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
Move the non-null data from the &quot;from&quot; cdr to the &quot;to&quot; cdr.
Definition: cdr.c:543
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:696
struct timeval start
Definition: cdr.h:100
#define ESS(x)
Definition: cli.h:58
struct ast_var_t::@158 entries
#define ast_free(a)
Definition: astmm.h:97
char * command
Definition: cli.h:180
char macrocontext[AST_MAX_CONTEXT]
Definition: channel.h:870
#define AST_FLAGS_ALL
Definition: utils.h:196
int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr)
Definition: cdr.c:383
long int duration
Definition: cdr.h:106
void ast_cdr_setapp(struct ast_cdr *cdr, const char *app, const char *data)
Set the last executed application.
Definition: cdr.c:822
int ast_cdr_setpeeraccount(struct ast_channel *chan, const char *account)
Set the peer account.
Definition: cdr.c:1016
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2351
struct ast_party_subaddress subaddress
Dialed/Called subaddress.
Definition: channel.h:341
Structure used to handle boolean flags.
Definition: utils.h:200
#define ast_clear_flag(p, flag)
Definition: utils.h:77
static int unanswered
Definition: cdr.c:97
#define AST_RWLIST_ENTRY
Definition: linkedlists.h:414
int ast_sched_runq(struct sched_context *con)
Runs the queue.
Definition: sched.c:600
static const int BATCHMODE_DEFAULT
Definition: cdr.c:95
const char * usage
Definition: cli.h:171
char src[AST_MAX_EXTENSION]
Definition: cdr.h:86
char peeraccount[AST_MAX_ACCOUNT_CODE]
Definition: cdr.h:116
char macroexten[AST_MAX_EXTENSION]
Definition: channel.h:871
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:436
#define CLI_SUCCESS
Definition: cli.h:43
struct ast_cdr * ast_cdr_dup_unique(struct ast_cdr *cdr)
Duplicate a record and increment the sequence number.
Definition: cdr.c:190
void ast_cdr_discard(struct ast_cdr *cdr)
Discard and free a CDR record.
Definition: cdr.c:488
static struct ast_cli_entry cli_status
Definition: cdr.c:1495
struct ast_flags ast_options
Definition: asterisk.c:178
struct timeval end
Definition: cdr.h:104
struct sched_context * sched_context_create(void)
New schedule context.
Definition: sched.c:246
char name[20]
Definition: cdr.c:63
Standard Command Line Interface.
#define ast_calloc(a, b)
Definition: astmm.h:82
static void cdr_merge_vars(struct ast_cdr *to, struct ast_cdr *from)
Definition: cdr.c:508
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:77
struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec)
Returns a timeval from sec, usec.
Definition: time.h:179
int ast_cdr_engine_init(void)
Load the configuration file cdr.conf and possibly start the CDR scheduling thread.
Definition: cdr.c:1646
void ast_cdr_noanswer(struct ast_cdr *cdr)
A call wasn&#39;t answered.
Definition: cdr.c:776
int ast_sched_wait(struct sched_context *con) attribute_warn_unused_result
Determines number of seconds until the next outstanding event to take place Determine the number of s...
Definition: sched.c:334
#define AST_CAUSE_BUSY
Definition: causes.h:148
void ast_cdr_free(struct ast_cdr *cdr)
Free a CDR record.
Definition: cdr.c:475
const ast_string_field accountcode
Definition: channel.h:787
Internal Asterisk hangup causes.
static int total
Definition: res_adsi.c:967
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
long int disposition
Definition: cdr.h:110
int check_cdr_enabled(void)
Return TRUE if CDR subsystem is enabled.
Definition: cdr.c:120
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:229
char clid[AST_MAX_EXTENSION]
Definition: cdr.h:84
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
static int enabled
Definition: cdr.c:91
static void submit_unscheduled_batch(void)
Definition: cdr.c:1312
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
Definition: manager.h:219
#define ast_malloc(a)
Definition: astmm.h:91
struct ast_data * ast_data_add_str(struct ast_data *root, const char *childname, const char *string)
Add a string node type.
Definition: data.c:2401
static struct ast_cli_entry cli_submit
Definition: cdr.c:1494
int amaflags
Definition: channel.h:843
static char * handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: cdr.c:1473
struct ast_data * ast_data_add_int(struct ast_data *root, const char *childname, int value)
Add an integer node type.
Definition: data.c:2322
char * ast_cdr_disp2str(int disposition)
Disposition to a string.
Definition: cdr.c:959
void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chan)
Set the destination channel, if there was one.
Definition: cdr.c:812
void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
Definition: cdr.c:454
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:247
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:172
int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *chan)
Initialize based on a channel.
Definition: cdr.c:897
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:602
struct ast_cdr * ast_cdr_alloc(void)
Allocate a CDR record.
Definition: cdr.c:499
char exten[AST_MAX_EXTENSION]
Definition: channel.h:869
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:526
static pthread_t cdr_thread
Definition: cdr.c:89
Definition: cdr.c:69
void ast_cdr_unregister(const char *name)
Unregister a CDR handling engine.
Definition: cdr.c:165
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
char userfield[AST_MAX_USER_FIELD]
Definition: cdr.h:125
#define CONFIG_STATUS_FILEUNCHANGED
Definition: config.h:51
#define ast_mutex_unlock(a)
Definition: lock.h:156
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:344
void ast_cdr_busy(struct ast_cdr *cdr)
Busy a call.
Definition: cdr.c:753
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:292