Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

cdr.c

Go to the documentation of this file.
00001 /* 00002 * Asterisk -- A telephony toolkit for Linux. 00003 * 00004 * Call Detail Record API 00005 * 00006 * Copyright (C) 1999, Mark Spencer 00007 * 00008 * Mark Spencer <markster@linux-support.net> 00009 * 00010 * This program is free software, distributed under the terms of 00011 * the GNU General Public License. 00012 * 00013 * Includes code and algorithms from the Zapata library. 00014 * 00015 */ 00016 00017 #include <asterisk/lock.h> 00018 #include <asterisk/channel.h> 00019 #include <asterisk/cdr.h> 00020 #include <asterisk/logger.h> 00021 #include <asterisk/callerid.h> 00022 #include <asterisk/causes.h> 00023 #include <asterisk/options.h> 00024 #include <asterisk/utils.h> 00025 #include <unistd.h> 00026 #include <stdlib.h> 00027 #include <string.h> 00028 #include <pthread.h> 00029 00030 int ast_default_amaflags = AST_CDR_DOCUMENTATION; 00031 char ast_default_accountcode[20] = ""; 00032 00033 AST_MUTEX_DEFINE_STATIC(cdrlock); 00034 00035 static struct ast_cdr_beitem { 00036 char name[20]; 00037 char desc[80]; 00038 ast_cdrbe be; 00039 struct ast_cdr_beitem *next; 00040 } *bes = NULL; 00041 00042 /* 00043 * We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip 00044 * through our fingers somehow. If someone allocates a CDR, it must be completely handled normally 00045 * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR 00046 * isn't properly generated and posted. 00047 */ 00048 00049 int ast_cdr_register(char *name, char *desc, ast_cdrbe be) 00050 { 00051 struct ast_cdr_beitem *i; 00052 if (!name) 00053 return -1; 00054 if (!be) { 00055 ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name); 00056 return -1; 00057 } 00058 ast_mutex_lock(&cdrlock); 00059 i = bes; 00060 while(i) { 00061 if (!strcasecmp(name, i->name)) 00062 break; 00063 i = i->next; 00064 } 00065 ast_mutex_unlock(&cdrlock); 00066 if (i) { 00067 ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name); 00068 return -1; 00069 } 00070 i = malloc(sizeof(struct ast_cdr_beitem)); 00071 if (!i) 00072 return -1; 00073 memset(i, 0, sizeof(struct ast_cdr_beitem)); 00074 strncpy(i->name, name, sizeof(i->name) - 1); 00075 strncpy(i->desc, desc, sizeof(i->desc) - 1); 00076 i->be = be; 00077 ast_mutex_lock(&cdrlock); 00078 i->next = bes; 00079 bes = i; 00080 ast_mutex_unlock(&cdrlock); 00081 return 0; 00082 } 00083 00084 void ast_cdr_unregister(char *name) 00085 { 00086 struct ast_cdr_beitem *i, *prev = NULL; 00087 ast_mutex_lock(&cdrlock); 00088 i = bes; 00089 while(i) { 00090 if (!strcasecmp(name, i->name)) { 00091 if (prev) 00092 prev->next = i->next; 00093 else 00094 bes = i->next; 00095 break; 00096 } 00097 i = i->next; 00098 } 00099 if (option_verbose > 1) 00100 ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s' CDR backend\n", name); 00101 ast_mutex_unlock(&cdrlock); 00102 if (i) 00103 free(i); 00104 } 00105 00106 void ast_cdr_free(struct ast_cdr *cdr) 00107 { 00108 char *chan; 00109 if (cdr) { 00110 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>"; 00111 if (!cdr->posted) 00112 ast_log(LOG_WARNING, "CDR on channel '%s' not posted\n", chan); 00113 if (!cdr->end.tv_sec && !cdr->end.tv_usec) 00114 ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan); 00115 if (!cdr->start.tv_sec && !cdr->start.tv_usec) 00116 ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan); 00117 free(cdr); 00118 } 00119 } 00120 00121 struct ast_cdr *ast_cdr_alloc(void) 00122 { 00123 struct ast_cdr *cdr; 00124 cdr = malloc(sizeof(struct ast_cdr)); 00125 if (cdr) { 00126 memset(cdr, 0, sizeof(struct ast_cdr)); 00127 } 00128 return cdr; 00129 } 00130 00131 void ast_cdr_start(struct ast_cdr *cdr) 00132 { 00133 char *chan; 00134 if (cdr) { 00135 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>"; 00136 if (cdr->posted) 00137 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan); 00138 if (cdr->start.tv_sec || cdr->start.tv_usec) 00139 ast_log(LOG_WARNING, "CDR on channel '%s' already started\n", chan); 00140 gettimeofday(&cdr->start, NULL); 00141 } 00142 } 00143 00144 void ast_cdr_answer(struct ast_cdr *cdr) 00145 { 00146 char *chan; 00147 if (cdr) { 00148 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>"; 00149 if (cdr->posted) 00150 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan); 00151 if (cdr->disposition < AST_CDR_ANSWERED) 00152 cdr->disposition = AST_CDR_ANSWERED; 00153 if (!cdr->answer.tv_sec && !cdr->answer.tv_usec) { 00154 gettimeofday(&cdr->answer, NULL); 00155 } 00156 } 00157 } 00158 00159 void ast_cdr_busy(struct ast_cdr *cdr) 00160 { 00161 char *chan; 00162 if (cdr) { 00163 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>"; 00164 if (cdr->posted) 00165 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan); 00166 if (cdr->disposition < AST_CDR_BUSY) 00167 cdr->disposition = AST_CDR_BUSY; 00168 } 00169 } 00170 00171 void ast_cdr_failed(struct ast_cdr *cdr) 00172 { 00173 char *chan; 00174 if (cdr) { 00175 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>"; 00176 if (cdr->posted) 00177 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan); 00178 cdr->disposition = AST_CDR_FAILED; 00179 } 00180 } 00181 00182 int ast_cdr_disposition(struct ast_cdr *cdr, int cause) 00183 { 00184 int res = 0; 00185 if (cdr) { 00186 switch(cause) { 00187 case AST_CAUSE_BUSY: 00188 ast_cdr_busy(cdr); 00189 break; 00190 case AST_CAUSE_FAILURE: 00191 ast_cdr_failed(cdr); 00192 break; 00193 case AST_CAUSE_NORMAL: 00194 break; 00195 case AST_CAUSE_NOTDEFINED: 00196 res = -1; 00197 break; 00198 default: 00199 res = -1; 00200 ast_log(LOG_WARNING, "We don't handle that cause yet\n"); 00201 } 00202 } 00203 return res; 00204 } 00205 00206 void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann) 00207 { 00208 char *chan; 00209 if (cdr) { 00210 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>"; 00211 if (cdr->posted) 00212 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan); 00213 strncpy(cdr->dstchannel, chann, sizeof(cdr->dstchannel) - 1); 00214 } 00215 } 00216 00217 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data) 00218 { 00219 char *chan; 00220 if (cdr) { 00221 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>"; 00222 if (cdr->posted) 00223 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan); 00224 if (!app) 00225 app = ""; 00226 strncpy(cdr->lastapp, app, sizeof(cdr->lastapp) - 1); 00227 if (!data) 00228 data = ""; 00229 strncpy(cdr->lastdata, data, sizeof(cdr->lastdata) - 1); 00230 } 00231 } 00232 00233 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c) 00234 { 00235 char tmp[AST_MAX_EXTENSION] = ""; 00236 char *num, *name; 00237 if (cdr) { 00238 /* Grab source from ANI or normal Caller*ID */ 00239 if (c->ani) 00240 strncpy(tmp, c->ani, sizeof(tmp) - 1); 00241 else if (c->callerid) 00242 strncpy(tmp, c->callerid, sizeof(tmp) - 1); 00243 if (c->callerid) 00244 strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1); 00245 name = NULL; 00246 num = NULL; 00247 ast_callerid_parse(tmp, &name, &num); 00248 if (num) { 00249 ast_shrink_phone_number(num); 00250 strncpy(cdr->src, num, sizeof(cdr->src) - 1); 00251 } 00252 } 00253 return 0; 00254 } 00255 00256 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c) 00257 { 00258 char *chan; 00259 char *num, *name; 00260 char tmp[AST_MAX_EXTENSION] = ""; 00261 if (cdr) { 00262 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>"; 00263 if (!ast_strlen_zero(cdr->channel)) 00264 ast_log(LOG_WARNING, "CDR already initialized on '%s'\n", chan); 00265 strncpy(cdr->channel, c->name, sizeof(cdr->channel) - 1); 00266 /* Grab source from ANI or normal Caller*ID */ 00267 if (c->ani) 00268 strncpy(tmp, c->ani, sizeof(tmp) - 1); 00269 else if (c->callerid) 00270 strncpy(tmp, c->callerid, sizeof(tmp) - 1); 00271 if (c->callerid) 00272 strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1); 00273 name = NULL; 00274 num = NULL; 00275 ast_callerid_parse(tmp, &name, &num); 00276 if (num) { 00277 ast_shrink_phone_number(num); 00278 strncpy(cdr->src, num, sizeof(cdr->src) - 1); 00279 } 00280 00281 if (c->_state == AST_STATE_UP) 00282 cdr->disposition = AST_CDR_ANSWERED; 00283 else 00284 cdr->disposition = AST_CDR_NOANSWER; 00285 if (c->amaflags) 00286 cdr->amaflags = c->amaflags; 00287 else 00288 cdr->amaflags = ast_default_amaflags; 00289 strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1); 00290 /* Destination information */ 00291 strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1); 00292 strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1); 00293 /* Unique call identifier */ 00294 strncpy(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid) - 1); 00295 } 00296 return 0; 00297 } 00298 00299 void ast_cdr_end(struct ast_cdr *cdr) 00300 { 00301 char *chan; 00302 if (cdr) { 00303 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>"; 00304 if (cdr->posted) 00305 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan); 00306 if (!cdr->start.tv_sec && !cdr->start.tv_usec) 00307 ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", chan); 00308 if (!cdr->end.tv_sec && !cdr->end.tv_usec) 00309 gettimeofday(&cdr->end, NULL); 00310 } 00311 } 00312 00313 char *ast_cdr_disp2str(int disposition) 00314 { 00315 switch (disposition) { 00316 case AST_CDR_NOANSWER: 00317 return "NO ANSWER"; 00318 case AST_CDR_FAILED: 00319 return "FAILED"; 00320 case AST_CDR_BUSY: 00321 return "BUSY"; 00322 case AST_CDR_ANSWERED: 00323 return "ANSWERED"; 00324 default: 00325 return "UNKNOWN"; 00326 } 00327 } 00328 00329 char *ast_cdr_flags2str(int flag) 00330 { 00331 switch(flag) { 00332 case AST_CDR_OMIT: 00333 return "OMIT"; 00334 case AST_CDR_BILLING: 00335 return "BILLING"; 00336 case AST_CDR_DOCUMENTATION: 00337 return "DOCUMENTATION"; 00338 } 00339 return "Unknown"; 00340 } 00341 00342 int ast_cdr_setaccount(struct ast_channel *chan, char *account) 00343 { 00344 struct ast_cdr *cdr = chan->cdr; 00345 00346 strncpy(chan->accountcode, account, sizeof(chan->accountcode) - 1); 00347 if (cdr) 00348 strncpy(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode) - 1); 00349 return 0; 00350 } 00351 00352 int ast_cdr_setuserfield(struct ast_channel *chan, char *userfield) 00353 { 00354 struct ast_cdr *cdr = chan->cdr; 00355 00356 if (cdr) 00357 strncpy(cdr->userfield, userfield, sizeof(cdr->userfield) - 1); 00358 return 0; 00359 } 00360 00361 int ast_cdr_appenduserfield(struct ast_channel *chan, char *userfield) 00362 { 00363 struct ast_cdr *cdr = chan->cdr; 00364 00365 if (cdr) 00366 { 00367 int len = strlen(cdr->userfield); 00368 strncpy(cdr->userfield+len, userfield, sizeof(cdr->userfield) - len - 1); 00369 } 00370 return 0; 00371 } 00372 00373 int ast_cdr_update(struct ast_channel *c) 00374 { 00375 struct ast_cdr *cdr = c->cdr; 00376 char *name, *num; 00377 char tmp[AST_MAX_EXTENSION] = ""; 00378 /* Grab source from ANI or normal Caller*ID */ 00379 if (cdr) { 00380 if (c->ani) 00381 strncpy(tmp, c->ani, sizeof(tmp) - 1); 00382 else if (c->callerid && !ast_strlen_zero(c->callerid)) 00383 strncpy(tmp, c->callerid, sizeof(tmp) - 1); 00384 if (c->callerid && !ast_strlen_zero(c->callerid)) 00385 strncpy(cdr->clid, c->callerid, sizeof(cdr->clid) - 1); 00386 else 00387 strcpy(cdr->clid, ""); 00388 name = NULL; 00389 num = NULL; 00390 ast_callerid_parse(tmp, &name, &num); 00391 if (num) { 00392 ast_shrink_phone_number(num); 00393 strncpy(cdr->src, num, sizeof(cdr->src) - 1); 00394 } 00395 /* Copy account code et-al */ 00396 strncpy(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode) - 1); 00397 /* Destination information */ 00398 strncpy(cdr->dst, c->exten, sizeof(cdr->dst) - 1); 00399 strncpy(cdr->dcontext, c->context, sizeof(cdr->dcontext) - 1); 00400 } 00401 return 0; 00402 } 00403 00404 int ast_cdr_amaflags2int(char *flag) 00405 { 00406 if (!strcasecmp(flag, "default")) 00407 return 0; 00408 if (!strcasecmp(flag, "omit")) 00409 return AST_CDR_OMIT; 00410 if (!strcasecmp(flag, "billing")) 00411 return AST_CDR_BILLING; 00412 if (!strcasecmp(flag, "documentation")) 00413 return AST_CDR_DOCUMENTATION; 00414 return -1; 00415 } 00416 00417 void ast_cdr_post(struct ast_cdr *cdr) 00418 { 00419 char *chan; 00420 struct ast_cdr_beitem *i; 00421 if (cdr) { 00422 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>"; 00423 if (cdr->posted) 00424 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan); 00425 if (!cdr->end.tv_sec && !cdr->end.tv_usec) 00426 ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan); 00427 if (!cdr->start.tv_sec && !cdr->start.tv_usec) 00428 ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan); 00429 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec + (cdr->end.tv_usec - cdr->start.tv_usec) / 1000000; 00430 if (cdr->answer.tv_sec || cdr->answer.tv_usec) { 00431 cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec + (cdr->end.tv_usec - cdr->answer.tv_usec) / 1000000; 00432 } else 00433 cdr->billsec = 0; 00434 cdr->posted = 1; 00435 ast_mutex_lock(&cdrlock); 00436 i = bes; 00437 while(i) { 00438 i->be(cdr); 00439 i = i->next; 00440 } 00441 ast_mutex_unlock(&cdrlock); 00442 } 00443 } 00444 00445 void ast_cdr_reset(struct ast_cdr *cdr, int post) 00446 { 00447 if (cdr) { 00448 /* Post if requested */ 00449 if (post) { 00450 ast_cdr_end(cdr); 00451 ast_cdr_post(cdr); 00452 } 00453 /* Reset to initial state */ 00454 cdr->posted = 0; 00455 memset(&cdr->start, 0, sizeof(cdr->start)); 00456 memset(&cdr->end, 0, sizeof(cdr->end)); 00457 memset(&cdr->answer, 0, sizeof(cdr->answer)); 00458 cdr->billsec = 0; 00459 cdr->duration = 0; 00460 ast_cdr_start(cdr); 00461 cdr->disposition = AST_CDR_NOANSWER; 00462 } 00463 }

Generated on Sat Jun 12 16:40:57 2004 for Asterisk by doxygen 1.3.7