FreeTDS CDR logger. More...
#include "asterisk.h"
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/cdr.h"
#include "asterisk/module.h"
#include <sqlfront.h>
#include <sybdb.h>
Go to the source code of this file.
Data Structures | |
struct | cdr_tds_config |
Defines | |
#define | DATE_FORMAT "%Y/%m/%d %T" |
Functions | |
static char * | anti_injection (const char *, int) |
AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,"FreeTDS CDR Backend",.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CDR_DRIVER,) | |
AST_MUTEX_DEFINE_STATIC (tds_lock) | |
static int | execute_and_consume (DBPROCESS *dbproc, const char *fmt,...) |
static void | get_date (char *, size_t len, struct timeval) |
static int | load_module (void) |
static int | mssql_connect (void) |
static int | mssql_disconnect (void) |
static int | reload (void) |
static int | tds_error_handler (DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr) |
static int | tds_load_module (int reload) |
static int | tds_log (struct ast_cdr *cdr) |
static int | tds_message_handler (DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line) |
static int | tds_unload_module (void) |
static int | unload_module (void) |
Variables | |
static const char | config [] = "cdr_tds.conf" |
static const char | name [] = "FreeTDS (MSSQL)" |
static struct cdr_tds_config * | settings |
FreeTDS CDR logger.
See also
Definition in file cdr_tds.c.
#define DATE_FORMAT "%Y/%m/%d %T" |
* * Table Structure for `cdr` * * Created on: 05/20/2004 16:16 * Last changed on: 07/27/2004 20:01 CREATE TABLE [dbo].[cdr] ( [accountcode] [varchar] (20) NULL , [src] [varchar] (80) NULL , [dst] [varchar] (80) NULL , [dcontext] [varchar] (80) NULL , [clid] [varchar] (80) NULL , [channel] [varchar] (80) NULL , [dstchannel] [varchar] (80) NULL , [lastapp] [varchar] (80) NULL , [lastdata] [varchar] (80) NULL , [start] [datetime] NULL , [answer] [datetime] NULL , [end] [datetime] NULL , [duration] [int] NULL , [billsec] [int] NULL , [disposition] [varchar] (20) NULL , [amaflags] [varchar] (16) NULL , [uniqueid] [varchar] (32) NULL , [userfield] [varchar] (256) NULL ) ON [PRIMARY]
static char * anti_injection | ( | const char * | str, | |
int | len | |||
) | [static] |
Definition at line 304 of file cdr_tds.c.
References ast_calloc, ast_log(), and LOG_ERROR.
Referenced by tds_log().
00305 { 00306 /* Reference to http://www.nextgenss.com/papers/advanced_sql_injection.pdf */ 00307 char *buf; 00308 char *buf_ptr, *srh_ptr; 00309 char *known_bad[] = {"select", "insert", "update", "delete", "drop", ";", "--", "\0"}; 00310 int idx; 00311 00312 if (!(buf = ast_calloc(1, len + 1))) { 00313 ast_log(LOG_ERROR, "Out of memory\n"); 00314 return NULL; 00315 } 00316 00317 buf_ptr = buf; 00318 00319 /* Escape single quotes */ 00320 for (; *str && strlen(buf) < len; str++) { 00321 if (*str == '\'') { 00322 *buf_ptr++ = '\''; 00323 } 00324 *buf_ptr++ = *str; 00325 } 00326 *buf_ptr = '\0'; 00327 00328 /* Erase known bad input */ 00329 for (idx = 0; *known_bad[idx]; idx++) { 00330 while ((srh_ptr = strcasestr(buf, known_bad[idx]))) { 00331 memmove(srh_ptr, srh_ptr + strlen(known_bad[idx]), strlen(srh_ptr + strlen(known_bad[idx])) + 1); 00332 } 00333 } 00334 00335 return buf; 00336 }
AST_MODULE_INFO | ( | ASTERISK_GPL_KEY | , | |
AST_MODFLAG_LOAD_ORDER | , | |||
"FreeTDS CDR Backend" | , | |||
. | load = load_module , |
|||
. | unload = unload_module , |
|||
. | reload = reload , |
|||
. | load_pri = AST_MODPRI_CDR_DRIVER | |||
) |
AST_MUTEX_DEFINE_STATIC | ( | tds_lock | ) |
static int execute_and_consume | ( | DBPROCESS * | dbproc, | |
const char * | fmt, | |||
... | ||||
) | [static] |
Definition at line 350 of file cdr_tds.c.
References ast_free, and ast_vasprintf.
Referenced by mssql_connect().
00351 { 00352 va_list ap; 00353 char *buffer; 00354 00355 va_start(ap, fmt); 00356 if (ast_vasprintf(&buffer, fmt, ap) < 0) { 00357 va_end(ap); 00358 return 1; 00359 } 00360 va_end(ap); 00361 00362 if (dbfcmd(dbproc, buffer) == FAIL) { 00363 ast_free(buffer); 00364 return 1; 00365 } 00366 00367 ast_free(buffer); 00368 00369 if (dbsqlexec(dbproc) == FAIL) { 00370 return 1; 00371 } 00372 00373 /* Consume the result set (we don't really care about the result, though) */ 00374 while (dbresults(dbproc) != NO_MORE_RESULTS) { 00375 while (dbnextrow(dbproc) != NO_MORE_ROWS); 00376 } 00377 00378 return 0; 00379 }
static void get_date | ( | char * | dateField, | |
size_t | len, | |||
struct timeval | when | |||
) | [static] |
Definition at line 338 of file cdr_tds.c.
References ast_copy_string(), ast_localtime(), ast_strftime(), ast_tvzero(), and DATE_FORMAT.
Referenced by tds_log().
00339 { 00340 /* To make sure we have date variable if not insert null to SQL */ 00341 if (!ast_tvzero(when)) { 00342 struct ast_tm tm; 00343 ast_localtime(&when, &tm, NULL); 00344 ast_strftime(dateField, len, "'" DATE_FORMAT "'", &tm); 00345 } else { 00346 ast_copy_string(dateField, "null", len); 00347 } 00348 }
static int load_module | ( | void | ) | [static] |
Definition at line 597 of file cdr_tds.c.
References ast_calloc_with_stringfields, ast_cdr_register(), ast_free, ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_string_field_free_memory, dbinit(), LOG_ERROR, tds_error_handler(), tds_load_module(), tds_log(), and tds_message_handler().
00598 { 00599 if (dbinit() == FAIL) { 00600 ast_log(LOG_ERROR, "Failed to initialize FreeTDS db-lib\n"); 00601 return AST_MODULE_LOAD_DECLINE; 00602 } 00603 00604 dberrhandle(tds_error_handler); 00605 dbmsghandle(tds_message_handler); 00606 00607 settings = ast_calloc_with_stringfields(1, struct cdr_tds_config, 256); 00608 00609 if (!settings) { 00610 dbexit(); 00611 return AST_MODULE_LOAD_DECLINE; 00612 } 00613 00614 if (!tds_load_module(0)) { 00615 ast_string_field_free_memory(settings); 00616 ast_free(settings); 00617 settings = NULL; 00618 dbexit(); 00619 return AST_MODULE_LOAD_DECLINE; 00620 } 00621 00622 ast_cdr_register(name, ast_module_info->description, tds_log); 00623 00624 return AST_MODULE_LOAD_SUCCESS; 00625 }
static int mssql_connect | ( | void | ) | [static] |
Definition at line 393 of file cdr_tds.c.
References ast_log(), cdr_tds_config::connected, cdr_tds_config::dbproc, execute_and_consume(), cdr_tds_config::has_userfield, LOG_ERROR, and LOG_NOTICE.
Referenced by tds_load_module(), and tds_log().
00394 { 00395 LOGINREC *login; 00396 00397 if ((login = dblogin()) == NULL) { 00398 ast_log(LOG_ERROR, "Unable to allocate login structure for db-lib\n"); 00399 return -1; 00400 } 00401 00402 DBSETLAPP(login, "TSQL"); 00403 DBSETLUSER(login, (char *) settings->username); 00404 DBSETLPWD(login, (char *) settings->password); 00405 DBSETLCHARSET(login, (char *) settings->charset); 00406 DBSETLNATLANG(login, (char *) settings->language); 00407 00408 if ((settings->dbproc = dbopen(login, (char *) settings->hostname)) == NULL) { 00409 ast_log(LOG_ERROR, "Unable to connect to %s\n", settings->hostname); 00410 dbloginfree(login); 00411 return -1; 00412 } 00413 00414 dbloginfree(login); 00415 00416 if (dbuse(settings->dbproc, (char *) settings->database) == FAIL) { 00417 ast_log(LOG_ERROR, "Unable to select database %s\n", settings->database); 00418 goto failed; 00419 } 00420 00421 if (execute_and_consume(settings->dbproc, "SELECT 1 FROM [%s] WHERE 1 = 0", settings->table)) { 00422 ast_log(LOG_ERROR, "Unable to find table '%s'\n", settings->table); 00423 goto failed; 00424 } 00425 00426 /* Check to see if we have a userfield column in the table */ 00427 if (execute_and_consume(settings->dbproc, "SELECT userfield FROM [%s] WHERE 1 = 0", settings->table)) { 00428 ast_log(LOG_NOTICE, "Unable to find 'userfield' column in table '%s'\n", settings->table); 00429 settings->has_userfield = 0; 00430 } else { 00431 settings->has_userfield = 1; 00432 } 00433 00434 settings->connected = 1; 00435 00436 return 0; 00437 00438 failed: 00439 dbclose(settings->dbproc); 00440 settings->dbproc = NULL; 00441 return -1; 00442 }
static int mssql_disconnect | ( | void | ) | [static] |
Definition at line 381 of file cdr_tds.c.
References cdr_tds_config::connected, and cdr_tds_config::dbproc.
Referenced by tds_load_module(), tds_log(), and tds_unload_module().
static int reload | ( | void | ) | [static] |
Definition at line 592 of file cdr_tds.c.
References tds_load_module().
00593 { 00594 return tds_load_module(1); 00595 }
static int tds_error_handler | ( | DBPROCESS * | dbproc, | |
int | severity, | |||
int | dberr, | |||
int | oserr, | |||
char * | dberrstr, | |||
char * | oserrstr | |||
) | [static] |
Definition at line 462 of file cdr_tds.c.
References ast_log(), and LOG_ERROR.
Referenced by load_module().
static int tds_load_module | ( | int | reload | ) | [static] |
Definition at line 481 of file cdr_tds.c.
References ast_config_destroy(), ast_config_load, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_string_field_init, ast_string_field_set, ast_true(), ast_variable_browse(), ast_variable_retrieve(), charset, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, hostname, language, LOG_ERROR, LOG_NOTICE, mssql_connect(), mssql_disconnect(), and table.
Referenced by load_module(), and reload().
00482 { 00483 struct ast_config *cfg; 00484 const char *ptr = NULL; 00485 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 00486 00487 cfg = ast_config_load(config, config_flags); 00488 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) { 00489 ast_log(LOG_NOTICE, "Unable to load TDS config for CDRs: %s\n", config); 00490 return 0; 00491 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) 00492 return 0; 00493 00494 if (!ast_variable_browse(cfg, "global")) { 00495 /* nothing configured */ 00496 ast_config_destroy(cfg); 00497 return 0; 00498 } 00499 00500 ast_mutex_lock(&tds_lock); 00501 00502 /* Clear out any existing settings */ 00503 ast_string_field_init(settings, 0); 00504 00505 /* 'connection' is the new preferred configuration option */ 00506 ptr = ast_variable_retrieve(cfg, "global", "connection"); 00507 if (ptr) { 00508 ast_string_field_set(settings, hostname, ptr); 00509 } else { 00510 /* But we keep 'hostname' for backwards compatibility */ 00511 ptr = ast_variable_retrieve(cfg, "global", "hostname"); 00512 if (ptr) { 00513 ast_string_field_set(settings, hostname, ptr); 00514 } else { 00515 ast_log(LOG_ERROR, "Failed to connect: Database server connection not specified.\n"); 00516 goto failed; 00517 } 00518 } 00519 00520 ptr = ast_variable_retrieve(cfg, "global", "dbname"); 00521 if (ptr) { 00522 ast_string_field_set(settings, database, ptr); 00523 } else { 00524 ast_log(LOG_ERROR, "Failed to connect: Database dbname not specified.\n"); 00525 goto failed; 00526 } 00527 00528 ptr = ast_variable_retrieve(cfg, "global", "user"); 00529 if (ptr) { 00530 ast_string_field_set(settings, username, ptr); 00531 } else { 00532 ast_log(LOG_ERROR, "Failed to connect: Database dbuser not specified.\n"); 00533 goto failed; 00534 } 00535 00536 ptr = ast_variable_retrieve(cfg, "global", "password"); 00537 if (ptr) { 00538 ast_string_field_set(settings, password, ptr); 00539 } else { 00540 ast_log(LOG_ERROR, "Failed to connect: Database password not specified.\n"); 00541 goto failed; 00542 } 00543 00544 ptr = ast_variable_retrieve(cfg, "global", "charset"); 00545 if (ptr) { 00546 ast_string_field_set(settings, charset, ptr); 00547 } else { 00548 ast_string_field_set(settings, charset, "iso_1"); 00549 } 00550 00551 ptr = ast_variable_retrieve(cfg, "global", "language"); 00552 if (ptr) { 00553 ast_string_field_set(settings, language, ptr); 00554 } else { 00555 ast_string_field_set(settings, language, "us_english"); 00556 } 00557 00558 ptr = ast_variable_retrieve(cfg, "global", "table"); 00559 if (ptr) { 00560 ast_string_field_set(settings, table, ptr); 00561 } else { 00562 ast_log(LOG_NOTICE, "Table name not specified, using 'cdr' by default.\n"); 00563 ast_string_field_set(settings, table, "cdr"); 00564 } 00565 00566 ptr = ast_variable_retrieve(cfg, "global", "hrtime"); 00567 if (ptr && ast_true(ptr)) { 00568 ast_string_field_set(settings, hrtime, ptr); 00569 } else { 00570 ast_log(LOG_NOTICE, "High Resolution Time not found, using integers for billsec and duration fields by default.\n"); 00571 } 00572 00573 mssql_disconnect(); 00574 00575 if (mssql_connect()) { 00576 /* We failed to connect (mssql_connect takes care of logging it) */ 00577 goto failed; 00578 } 00579 00580 ast_mutex_unlock(&tds_lock); 00581 ast_config_destroy(cfg); 00582 00583 return 1; 00584 00585 failed: 00586 ast_mutex_unlock(&tds_lock); 00587 ast_config_destroy(cfg); 00588 00589 return 0; 00590 }
static int tds_log | ( | struct ast_cdr * | cdr | ) | [static] |
Definition at line 111 of file cdr_tds.c.
References accountcode, anti_injection(), ast_cdr_disp2str(), ast_cdr_flags2str(), ast_free, ast_log(), AST_MAX_USER_FIELD, ast_mutex_lock, ast_mutex_unlock, ast_tvdiff_us(), ast_tvzero(), cdr_tds_config::connected, cdr_tds_config::dbproc, get_date(), cdr_tds_config::has_userfield, LOG_ERROR, LOG_NOTICE, mssql_connect(), and mssql_disconnect().
Referenced by load_module().
00112 { 00113 char start[80], answer[80], end[80]; 00114 char *accountcode, *src, *dst, *dcontext, *clid, *channel, *dstchannel, *lastapp, *lastdata, *uniqueid, *userfield = NULL; 00115 RETCODE erc; 00116 int res = -1; 00117 int attempt = 1; 00118 00119 accountcode = anti_injection(cdr->accountcode, 20); 00120 src = anti_injection(cdr->src, 80); 00121 dst = anti_injection(cdr->dst, 80); 00122 dcontext = anti_injection(cdr->dcontext, 80); 00123 clid = anti_injection(cdr->clid, 80); 00124 channel = anti_injection(cdr->channel, 80); 00125 dstchannel = anti_injection(cdr->dstchannel, 80); 00126 lastapp = anti_injection(cdr->lastapp, 80); 00127 lastdata = anti_injection(cdr->lastdata, 80); 00128 uniqueid = anti_injection(cdr->uniqueid, 32); 00129 00130 get_date(start, sizeof(start), cdr->start); 00131 get_date(answer, sizeof(answer), cdr->answer); 00132 get_date(end, sizeof(end), cdr->end); 00133 00134 ast_mutex_lock(&tds_lock); 00135 00136 if (settings->has_userfield) { 00137 userfield = anti_injection(cdr->userfield, AST_MAX_USER_FIELD); 00138 } 00139 00140 retry: 00141 /* Ensure that we are connected */ 00142 if (!settings->connected) { 00143 ast_log(LOG_NOTICE, "Attempting to reconnect to %s (Attempt %d)\n", settings->hostname, attempt); 00144 if (mssql_connect()) { 00145 /* Connect failed */ 00146 if (attempt++ < 3) { 00147 goto retry; 00148 } 00149 goto done; 00150 } 00151 } 00152 00153 if (settings->has_userfield) { 00154 if (settings->hrtime) { 00155 double hrbillsec = 0.0; 00156 double hrduration; 00157 00158 if (!ast_tvzero(cdr->answer)) { 00159 hrbillsec = (double)(ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0); 00160 } 00161 hrduration = (double)(ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0); 00162 00163 erc = dbfcmd(settings->dbproc, 00164 "INSERT INTO %s " 00165 "(" 00166 "accountcode, src, dst, dcontext, clid, channel, " 00167 "dstchannel, lastapp, lastdata, start, answer, [end], duration, " 00168 "billsec, disposition, amaflags, uniqueid, userfield" 00169 ") " 00170 "VALUES " 00171 "(" 00172 "'%s', '%s', '%s', '%s', '%s', '%s', " 00173 "'%s', '%s', '%s', %s, %s, %s, %lf, " 00174 "%lf, '%s', '%s', '%s', '%s'" 00175 ")", 00176 settings->table, 00177 accountcode, src, dst, dcontext, clid, channel, 00178 dstchannel, lastapp, lastdata, start, answer, end, hrduration, 00179 hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid, 00180 userfield 00181 ); 00182 } else { 00183 erc = dbfcmd(settings->dbproc, 00184 "INSERT INTO %s " 00185 "(" 00186 "accountcode, src, dst, dcontext, clid, channel, " 00187 "dstchannel, lastapp, lastdata, start, answer, [end], duration, " 00188 "billsec, disposition, amaflags, uniqueid, userfield" 00189 ") " 00190 "VALUES " 00191 "(" 00192 "'%s', '%s', '%s', '%s', '%s', '%s', " 00193 "'%s', '%s', '%s', %s, %s, %s, %ld, " 00194 "%ld, '%s', '%s', '%s', '%s'" 00195 ")", 00196 settings->table, 00197 accountcode, src, dst, dcontext, clid, channel, 00198 dstchannel, lastapp, lastdata, start, answer, end, cdr->duration, 00199 cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid, 00200 userfield 00201 ); 00202 } 00203 } else { 00204 if (settings->hrtime) { 00205 double hrbillsec = 0.0; 00206 double hrduration; 00207 00208 if (!ast_tvzero(cdr->answer)) { 00209 hrbillsec = (double)(ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0); 00210 } 00211 hrduration = (double)(ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0); 00212 00213 erc = dbfcmd(settings->dbproc, 00214 "INSERT INTO %s " 00215 "(" 00216 "accountcode, src, dst, dcontext, clid, channel, " 00217 "dstchannel, lastapp, lastdata, start, answer, [end], duration, " 00218 "billsec, disposition, amaflags, uniqueid" 00219 ") " 00220 "VALUES " 00221 "(" 00222 "'%s', '%s', '%s', '%s', '%s', '%s', " 00223 "'%s', '%s', '%s', %s, %s, %s, %lf, " 00224 "%lf, '%s', '%s', '%s'" 00225 ")", 00226 settings->table, 00227 accountcode, src, dst, dcontext, clid, channel, 00228 dstchannel, lastapp, lastdata, start, answer, end, hrduration, 00229 hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid 00230 ); 00231 } else { 00232 erc = dbfcmd(settings->dbproc, 00233 "INSERT INTO %s " 00234 "(" 00235 "accountcode, src, dst, dcontext, clid, channel, " 00236 "dstchannel, lastapp, lastdata, start, answer, [end], duration, " 00237 "billsec, disposition, amaflags, uniqueid" 00238 ") " 00239 "VALUES " 00240 "(" 00241 "'%s', '%s', '%s', '%s', '%s', '%s', " 00242 "'%s', '%s', '%s', %s, %s, %s, %ld, " 00243 "%ld, '%s', '%s', '%s'" 00244 ")", 00245 settings->table, 00246 accountcode, src, dst, dcontext, clid, channel, 00247 dstchannel, lastapp, lastdata, start, answer, end, cdr->duration, 00248 cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid 00249 ); 00250 } 00251 } 00252 00253 if (erc == FAIL) { 00254 if (attempt++ < 3) { 00255 ast_log(LOG_NOTICE, "Failed to build INSERT statement, retrying...\n"); 00256 mssql_disconnect(); 00257 goto retry; 00258 } else { 00259 ast_log(LOG_ERROR, "Failed to build INSERT statement, no CDR was logged.\n"); 00260 goto done; 00261 } 00262 } 00263 00264 if (dbsqlexec(settings->dbproc) == FAIL) { 00265 if (attempt++ < 3) { 00266 ast_log(LOG_NOTICE, "Failed to execute INSERT statement, retrying...\n"); 00267 mssql_disconnect(); 00268 goto retry; 00269 } else { 00270 ast_log(LOG_ERROR, "Failed to execute INSERT statement, no CDR was logged.\n"); 00271 goto done; 00272 } 00273 } 00274 00275 /* Consume any results we might get back (this is more of a sanity check than 00276 * anything else, since an INSERT shouldn't return results). */ 00277 while (dbresults(settings->dbproc) != NO_MORE_RESULTS) { 00278 while (dbnextrow(settings->dbproc) != NO_MORE_ROWS); 00279 } 00280 00281 res = 0; 00282 00283 done: 00284 ast_mutex_unlock(&tds_lock); 00285 00286 ast_free(accountcode); 00287 ast_free(src); 00288 ast_free(dst); 00289 ast_free(dcontext); 00290 ast_free(clid); 00291 ast_free(channel); 00292 ast_free(dstchannel); 00293 ast_free(lastapp); 00294 ast_free(lastdata); 00295 ast_free(uniqueid); 00296 00297 if (userfield) { 00298 ast_free(userfield); 00299 } 00300 00301 return res; 00302 }
static int tds_message_handler | ( | DBPROCESS * | dbproc, | |
DBINT | msgno, | |||
int | msgstate, | |||
int | severity, | |||
char * | msgtext, | |||
char * | srvname, | |||
char * | procname, | |||
int | line | |||
) | [static] |
Definition at line 473 of file cdr_tds.c.
References ast_debug, ast_log(), and LOG_NOTICE.
Referenced by load_module().
00474 { 00475 ast_debug(1, "Msg %d, Level %d, State %d, Line %d\n", msgno, severity, msgstate, line); 00476 ast_log(LOG_NOTICE, "%s\n", msgtext); 00477 00478 return 0; 00479 }
static int tds_unload_module | ( | void | ) | [static] |
Definition at line 444 of file cdr_tds.c.
References ast_cdr_unregister(), ast_free, ast_mutex_lock, ast_mutex_unlock, ast_string_field_free_memory, and mssql_disconnect().
Referenced by unload_module().
00445 { 00446 if (settings) { 00447 ast_mutex_lock(&tds_lock); 00448 mssql_disconnect(); 00449 ast_mutex_unlock(&tds_lock); 00450 00451 ast_string_field_free_memory(settings); 00452 ast_free(settings); 00453 } 00454 00455 ast_cdr_unregister(name); 00456 00457 dbexit(); 00458 00459 return 0; 00460 }
static int unload_module | ( | void | ) | [static] |
Definition at line 627 of file cdr_tds.c.
References tds_unload_module().
00628 { 00629 return tds_unload_module(); 00630 }
struct cdr_tds_config* settings [static] |