cdr_odbc.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /*
  2. * Asterisk -- A telephony toolkit for Linux.
  3. *
  4. * ODBC CDR Backend
  5. *
  6. * Brian K. West <brian@bkw.org>
  7. *
  8. * This program is free software, distributed under the terms of
  9. * the GNU General Public License.
  10. *
  11. * Copyright (c) 2003 Digium, Inc.
  12. *
  13. */
  14. #include <sys/types.h>
  15. #include <asterisk/config.h>
  16. #include <asterisk/options.h>
  17. #include <asterisk/channel.h>
  18. #include <asterisk/cdr.h>
  19. #include <asterisk/module.h>
  20. #include <asterisk/logger.h>
  21. #include "../asterisk.h"
  22. #include <stdio.h>
  23. #include <string.h>
  24. #include <stdlib.h>
  25. #include <unistd.h>
  26. #include <time.h>
  27. #include <sql.h>
  28. #include <sqlext.h>
  29. #include <sqltypes.h>
  30. #define DATE_FORMAT "%Y-%m-%d %T"
  31. static char *desc = "ODBC CDR Backend";
  32. static char *name = "ODBC";
  33. static char *config = "cdr_odbc.conf";
  34. static char *dsn = NULL, *username = NULL, *password = NULL, *loguniqueid = NULL;
  35. static int dsn_alloc = 0, username_alloc = 0, password_alloc = 0, loguniqueid_alloc = 0;
  36. static int connected = 0;
  37. static ast_mutex_t odbc_lock = AST_MUTEX_INITIALIZER;
  38. static int odbc_do_query(void);
  39. static int odbc_init(void);
  40. static SQLHENV ODBC_env = SQL_NULL_HANDLE; /* global ODBC Environment */
  41. static SQLHDBC ODBC_con; /* global ODBC Connection Handle */
  42. static SQLHSTMT ODBC_stmt; /* global ODBC Statement Handle */
  43. static int odbc_log(struct ast_cdr *cdr)
  44. {
  45. long int ODBC_err;
  46. short int ODBC_mlen;
  47. int ODBC_res;
  48. char ODBC_msg[200], ODBC_stat[10];
  49. char sqlcmd[2048], timestr[128];
  50. int res = 0;
  51. struct tm tm;
  52. struct timeval tv;
  53. time_t t;
  54. gettimeofday(&tv,NULL);
  55. t = tv.tv_sec;
  56. localtime_r(&t,&tm);
  57. ast_mutex_lock(&odbc_lock);
  58. strftime(timestr,128,DATE_FORMAT,&tm);
  59. memset(sqlcmd,0,2048);
  60. if((loguniqueid != NULL) && ((strcmp(loguniqueid, "1") == 0) || (strcmp(loguniqueid, "yes") == 0)))
  61. {
  62. sprintf(sqlcmd,"INSERT INTO cdr "
  63. "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,"
  64. "lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) "
  65. "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
  66. }
  67. else
  68. {
  69. sprintf(sqlcmd,"INSERT INTO cdr "
  70. "(calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,"
  71. "duration,billsec,disposition,amaflags,accountcode) "
  72. "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
  73. }
  74. if(!connected)
  75. {
  76. res = odbc_init();
  77. if(res < 0)
  78. {
  79. connected = 0;
  80. ast_mutex_unlock(&odbc_lock);
  81. return 0;
  82. }
  83. }
  84. ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, ODBC_con, &ODBC_stmt);
  85. if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
  86. {
  87. if(option_verbose > 3)
  88. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Failure in AllocStatement %d\n", ODBC_res);
  89. SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen);
  90. SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
  91. connected = 0;
  92. ast_mutex_unlock(&odbc_lock);
  93. return 0;
  94. }
  95. /* We really should only have to do this once. But for some
  96. strange reason if I don't it blows holes in memory like
  97. like a shotgun. So we just do this so its safe. */
  98. ODBC_res = SQLPrepare(ODBC_stmt, sqlcmd, SQL_NTS);
  99. if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
  100. {
  101. if(option_verbose > 3)
  102. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error in PREPARE %d\n", ODBC_res);
  103. SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen);
  104. SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
  105. connected = 0;
  106. ast_mutex_unlock(&odbc_lock);
  107. return 0;
  108. }
  109. SQLBindParameter(ODBC_stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, &timestr, 0, NULL);
  110. SQLBindParameter(ODBC_stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, cdr->clid, 0, NULL);
  111. SQLBindParameter(ODBC_stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, cdr->src, 0, NULL);
  112. SQLBindParameter(ODBC_stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, cdr->dst, 0, NULL);
  113. SQLBindParameter(ODBC_stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, cdr->dcontext, 0, NULL);
  114. SQLBindParameter(ODBC_stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, cdr->channel, 0, NULL);
  115. SQLBindParameter(ODBC_stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, cdr->dstchannel, 0, NULL);
  116. SQLBindParameter(ODBC_stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, cdr->lastapp, 0, NULL);
  117. SQLBindParameter(ODBC_stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, cdr->lastdata, 0, NULL);
  118. SQLBindParameter(ODBC_stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->duration, 0, NULL);
  119. SQLBindParameter(ODBC_stmt, 11, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
  120. SQLBindParameter(ODBC_stmt, 12, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->disposition, 0, NULL);
  121. SQLBindParameter(ODBC_stmt, 13, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL);
  122. SQLBindParameter(ODBC_stmt, 14, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, cdr->accountcode, 0, NULL);
  123. if((loguniqueid != NULL) && ((strcmp(loguniqueid, "1") == 0) || (strcmp(loguniqueid, "yes") == 0)))
  124. {
  125. SQLBindParameter(ODBC_stmt, 15, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, cdr->uniqueid, 0, NULL);
  126. SQLBindParameter(ODBC_stmt, 16, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, cdr->userfield, 0, NULL);
  127. }
  128. if(connected)
  129. {
  130. res = odbc_do_query();
  131. if(res < 0)
  132. {
  133. if(option_verbose > 3)
  134. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n");
  135. res = odbc_init();
  136. if(option_verbose > 3)
  137. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Reconnecting to dsn %s\n", dsn);
  138. if(res < 0)
  139. {
  140. if(option_verbose > 3)
  141. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: %s has gone away!\n", dsn);
  142. connected = 0;
  143. }
  144. else
  145. {
  146. if(option_verbose > 3)
  147. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Trying Query again!\n");
  148. res = odbc_do_query();
  149. if(res < 0)
  150. {
  151. if(option_verbose > 3)
  152. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n");
  153. }
  154. }
  155. }
  156. }
  157. else
  158. {
  159. if(option_verbose > 3)
  160. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query FAILED Call not logged!\n");
  161. }
  162. ast_mutex_unlock(&odbc_lock);
  163. return 0;
  164. }
  165. char *description(void)
  166. {
  167. return desc;
  168. }
  169. static int odbc_unload_module(void)
  170. {
  171. ast_mutex_lock(&odbc_lock);
  172. if (connected)
  173. {
  174. if(option_verbose > 3)
  175. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Disconnecting from %s\n", dsn);
  176. SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
  177. SQLDisconnect(ODBC_con);
  178. SQLFreeHandle(SQL_HANDLE_DBC, ODBC_con);
  179. SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
  180. connected = 0;
  181. }
  182. if (dsn && dsn_alloc)
  183. {
  184. if(option_verbose > 3)
  185. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free dsn\n");
  186. free(dsn);
  187. dsn = NULL;
  188. dsn_alloc = 0;
  189. }
  190. if (username && username_alloc)
  191. {
  192. if(option_verbose > 3)
  193. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free username\n");
  194. free(username);
  195. username = NULL;
  196. username_alloc = 0;
  197. }
  198. if (password && password_alloc)
  199. {
  200. if(option_verbose > 3)
  201. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free password\n");
  202. free(password);
  203. password = NULL;
  204. password_alloc = 0;
  205. }
  206. if (loguniqueid && loguniqueid_alloc)
  207. {
  208. if(option_verbose > 3)
  209. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: free loguniqueid\n");
  210. free(loguniqueid);
  211. loguniqueid = NULL;
  212. loguniqueid_alloc = 0;
  213. }
  214. ast_cdr_unregister(name);
  215. ast_mutex_unlock(&odbc_lock);
  216. return 0;
  217. }
  218. static int odbc_load_module(void)
  219. {
  220. int res = 0;
  221. struct ast_config *cfg;
  222. struct ast_variable *var;
  223. char *tmp;
  224. ast_mutex_lock(&odbc_lock);
  225. cfg = ast_load(config);
  226. if (!cfg)
  227. {
  228. ast_log(LOG_WARNING, "cdr_odbc: Unable to load config for ODBC CDR's: %s\n", config);
  229. goto out;
  230. }
  231. var = ast_variable_browse(cfg, "global");
  232. if (!var) {
  233. /* nothing configured */
  234. goto out;
  235. }
  236. tmp = ast_variable_retrieve(cfg,"global","dsn");
  237. if (tmp)
  238. {
  239. dsn = malloc(strlen(tmp) + 1);
  240. if (dsn != NULL)
  241. {
  242. dsn_alloc = 1;
  243. strcpy(dsn,tmp);
  244. }
  245. else
  246. {
  247. ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
  248. return -1;
  249. }
  250. }
  251. else
  252. {
  253. ast_log(LOG_WARNING,"cdr_odbc: dsn not specified. Assuming asteriskdb\n");
  254. dsn = "asteriskdb";
  255. }
  256. tmp = ast_variable_retrieve(cfg,"global","username");
  257. if (tmp)
  258. {
  259. username = malloc(strlen(tmp) + 1);
  260. if (username != NULL)
  261. {
  262. username_alloc = 1;
  263. strcpy(username,tmp);
  264. }
  265. else
  266. {
  267. ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
  268. return -1;
  269. }
  270. }
  271. else
  272. {
  273. ast_log(LOG_WARNING,"cdr_odbc: username not specified. Assuming root\n");
  274. username = "root";
  275. }
  276. tmp = ast_variable_retrieve(cfg,"global","password");
  277. if (tmp)
  278. {
  279. password = malloc(strlen(tmp) + 1);
  280. if (password != NULL)
  281. {
  282. password_alloc = 1;
  283. strcpy(password,tmp);
  284. }
  285. else
  286. {
  287. ast_log(LOG_ERROR,"cdr_odbc: Out of memory error.\n");
  288. return -1;
  289. }
  290. }
  291. else
  292. {
  293. ast_log(LOG_WARNING,"cdr_odbc: database password not specified. Assuming blank\n");
  294. password = "";
  295. }
  296. tmp = ast_variable_retrieve(cfg,"global","loguniqueid");
  297. if (tmp)
  298. {
  299. loguniqueid = malloc(strlen(tmp) + 1);
  300. if (loguniqueid != NULL)
  301. {
  302. strcpy(loguniqueid,tmp);
  303. loguniqueid_alloc = 1;
  304. ast_log(LOG_NOTICE,"cdr_odbc: Logging uniqueid\n");
  305. }
  306. else
  307. {
  308. ast_log(LOG_ERROR,"cdr_odbc: Not logging uniqueid\n");
  309. loguniqueid_alloc = 1;
  310. loguniqueid = NULL;
  311. }
  312. }
  313. else
  314. {
  315. ast_log(LOG_WARNING,"cdr_odbc: Not logging uniqueid\n");
  316. loguniqueid = NULL;
  317. }
  318. ast_destroy(cfg);
  319. if(option_verbose > 3)
  320. {
  321. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: dsn is %s\n",dsn);
  322. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: username is %s\n",username);
  323. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: password is [secret]\n");
  324. }
  325. res = odbc_init();
  326. if(res < 0)
  327. {
  328. ast_log(LOG_ERROR, "cdr_odbc: Unable to connect to datasource: %s\n", dsn);
  329. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Unable to connect to datasource: %s\n", dsn);
  330. }
  331. res = ast_cdr_register(name, desc, odbc_log);
  332. if (res)
  333. {
  334. ast_log(LOG_ERROR, "cdr_odbc: Unable to register ODBC CDR handling\n");
  335. }
  336. out:
  337. ast_mutex_unlock(&odbc_lock);
  338. return res;
  339. }
  340. static int odbc_do_query(void)
  341. {
  342. long int ODBC_err;
  343. int ODBC_res;
  344. short int ODBC_mlen;
  345. char ODBC_msg[200], ODBC_stat[10];
  346. ODBC_res = SQLExecute(ODBC_stmt);
  347. if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
  348. {
  349. if(option_verbose > 3)
  350. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error in Query %d\n", ODBC_res);
  351. SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen);
  352. SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt);
  353. connected = 0;
  354. return -1;
  355. }
  356. else
  357. {
  358. if(option_verbose > 3)
  359. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Query Successful!\n");
  360. connected = 1;
  361. }
  362. return 0;
  363. }
  364. static int odbc_init(void)
  365. {
  366. long int ODBC_err;
  367. short int ODBC_mlen;
  368. int ODBC_res;
  369. char ODBC_msg[200], ODBC_stat[10];
  370. if ( ODBC_env == SQL_NULL_HANDLE || connected == 0 )
  371. {
  372. ODBC_res = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &ODBC_env);
  373. if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
  374. {
  375. if(option_verbose > 3)
  376. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error AllocHandle\n");
  377. connected = 0;
  378. return -1;
  379. }
  380. ODBC_res = SQLSetEnvAttr(ODBC_env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
  381. if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
  382. {
  383. if(option_verbose > 3)
  384. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error SetEnv\n");
  385. SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
  386. connected = 0;
  387. return -1;
  388. }
  389. ODBC_res = SQLAllocHandle(SQL_HANDLE_DBC, ODBC_env, &ODBC_con);
  390. if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
  391. {
  392. if(option_verbose > 3)
  393. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error AllocHDB %d\n", ODBC_res);
  394. SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
  395. connected = 0;
  396. return -1;
  397. }
  398. SQLSetConnectAttr(ODBC_con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)10, 0);
  399. }
  400. ODBC_res = SQLConnect(ODBC_con, (SQLCHAR*)dsn, SQL_NTS, (SQLCHAR*)username, SQL_NTS, (SQLCHAR*)password, SQL_NTS);
  401. if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO))
  402. {
  403. if(option_verbose > 3)
  404. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Error SQLConnect %d\n", ODBC_res);
  405. SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen);
  406. SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env);
  407. connected = 0;
  408. return -1;
  409. }
  410. else
  411. {
  412. if(option_verbose > 3)
  413. ast_verbose( VERBOSE_PREFIX_4 "cdr_odbc: Connected to %s\n", dsn);
  414. connected = 1;
  415. }
  416. return 0;
  417. }
  418. int load_module(void)
  419. {
  420. return odbc_load_module();
  421. }
  422. int unload_module(void)
  423. {
  424. return odbc_unload_module();
  425. }
  426. int reload(void)
  427. {
  428. odbc_unload_module();
  429. return odbc_load_module();
  430. }
  431. int usecount(void)
  432. {
  433. return connected;
  434. }
  435. char *key()
  436. {
  437. return ASTERISK_GPL_KEY;
  438. }