cdr_odbc.c 12 KB

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