cdr_csv.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * Asterisk -- A telephony toolkit for Linux.
  3. *
  4. * Comma Separated Value CDR records.
  5. *
  6. * Copyright (C) 1999, Mark Spencer
  7. *
  8. * Mark Spencer <markster@linux-support.net>
  9. *
  10. * This program is free software, distributed under the terms of
  11. * the GNU General Public License.
  12. *
  13. * Includes code and algorithms from the Zapata library.
  14. *
  15. */
  16. #include <sys/types.h>
  17. #include <asterisk/channel.h>
  18. #include <asterisk/cdr.h>
  19. #include <asterisk/module.h>
  20. #include <asterisk/logger.h>
  21. #include <asterisk/utils.h>
  22. #include "../asterisk.h"
  23. #include "../astconf.h"
  24. #define CSV_LOG_DIR "/cdr-csv"
  25. #define CSV_MASTER "/Master.csv"
  26. #define DATE_FORMAT "%Y-%m-%d %T"
  27. /* #define CSV_LOGUNIQUEID 1 */
  28. /* #define CSV_LOGUSERFIELD 1 */
  29. #include <stdio.h>
  30. #include <string.h>
  31. #include <errno.h>
  32. #include <stdlib.h>
  33. #include <unistd.h>
  34. #include <time.h>
  35. /* The values are as follows:
  36. "accountcode", // accountcode is the account name of detail records, Master.csv contains all records
  37. // Detail records are configured on a channel basis, IAX and SIP are determined by user
  38. // Zap is determined by channel in zaptel.conf
  39. "source",
  40. "destination",
  41. "destination context",
  42. "callerid",
  43. "channel",
  44. "destination channel", (if applicable)
  45. "last application", // Last application run on the channel
  46. "last app argument", // argument to the last channel
  47. "start time",
  48. "answer time",
  49. "end time",
  50. duration, // Duration is the whole length that the entire call lasted. ie. call rx'd to hangup
  51. // "end time" minus "start time"
  52. billable seconds, // the duration that a call was up after other end answered which will be <= to duration
  53. // "end time" minus "answer time"
  54. "disposition", // ANSWERED, NO ANSWER, BUSY
  55. "amaflags", // DOCUMENTATION, BILL, IGNORE etc, specified on a per channel basis like accountcode.
  56. "uniqueid", // unique call identifier
  57. "userfield" // user field set via SetCDRUserField
  58. */
  59. static char *desc = "Comma Separated Values CDR Backend";
  60. static char *name = "csv";
  61. static FILE *mf = NULL;
  62. static int append_string(char *buf, char *s, size_t bufsize)
  63. {
  64. int pos = strlen(buf);
  65. int spos = 0;
  66. int error = 0;
  67. if (pos >= bufsize - 4)
  68. return -1;
  69. buf[pos++] = '\"';
  70. error = -1;
  71. while(pos < bufsize - 3) {
  72. if (!s[spos]) {
  73. error = 0;
  74. break;
  75. }
  76. if (s[spos] == '\"')
  77. buf[pos++] = '\"';
  78. buf[pos++] = s[spos];
  79. spos++;
  80. }
  81. buf[pos++] = '\"';
  82. buf[pos++] = ',';
  83. buf[pos++] = '\0';
  84. return error;
  85. }
  86. static int append_int(char *buf, int s, size_t bufsize)
  87. {
  88. char tmp[32];
  89. int pos = strlen(buf);
  90. snprintf(tmp, sizeof(tmp), "%d", s);
  91. if (pos + strlen(tmp) > bufsize - 3)
  92. return -1;
  93. strncat(buf, tmp, bufsize - strlen(buf) - 1);
  94. pos = strlen(buf);
  95. buf[pos++] = ',';
  96. buf[pos++] = '\0';
  97. return 0;
  98. }
  99. static int append_date(char *buf, struct timeval tv, size_t bufsize)
  100. {
  101. char tmp[80] = "";
  102. struct tm tm;
  103. time_t t;
  104. t = tv.tv_sec;
  105. if (strlen(buf) > bufsize - 3)
  106. return -1;
  107. if (!tv.tv_sec && !tv.tv_usec) {
  108. strncat(buf, ",", bufsize - strlen(buf) - 1);
  109. return 0;
  110. }
  111. localtime_r(&t,&tm);
  112. strftime(tmp, sizeof(tmp), DATE_FORMAT, &tm);
  113. return append_string(buf, tmp, bufsize);
  114. }
  115. static int build_csv_record(char *buf, size_t bufsize, struct ast_cdr *cdr)
  116. {
  117. buf[0] = '\0';
  118. /* Account code */
  119. append_string(buf, cdr->accountcode, bufsize);
  120. /* Source */
  121. append_string(buf, cdr->src, bufsize);
  122. /* Destination */
  123. append_string(buf, cdr->dst, bufsize);
  124. /* Destination context */
  125. append_string(buf, cdr->dcontext, bufsize);
  126. /* Caller*ID */
  127. append_string(buf, cdr->clid, bufsize);
  128. /* Channel */
  129. append_string(buf, cdr->channel, bufsize);
  130. /* Destination Channel */
  131. append_string(buf, cdr->dstchannel, bufsize);
  132. /* Last Application */
  133. append_string(buf, cdr->lastapp, bufsize);
  134. /* Last Data */
  135. append_string(buf, cdr->lastdata, bufsize);
  136. /* Start Time */
  137. append_date(buf, cdr->start, bufsize);
  138. /* Answer Time */
  139. append_date(buf, cdr->answer, bufsize);
  140. /* End Time */
  141. append_date(buf, cdr->end, bufsize);
  142. /* Duration */
  143. append_int(buf, cdr->duration, bufsize);
  144. /* Billable seconds */
  145. append_int(buf, cdr->billsec, bufsize);
  146. /* Disposition */
  147. append_string(buf, ast_cdr_disp2str(cdr->disposition), bufsize);
  148. /* AMA Flags */
  149. append_string(buf, ast_cdr_flags2str(cdr->amaflags), bufsize);
  150. #ifdef CSV_LOGUNIQUEID
  151. /* Unique ID */
  152. append_string(buf, cdr->uniqueid, bufsize);
  153. #endif
  154. #ifdef CSV_LOGUSERFIELD
  155. /* append the user field */
  156. append_string(buf, cdr->userfield,bufsize);
  157. #endif
  158. /* If we hit the end of our buffer, log an error */
  159. if (strlen(buf) < bufsize - 5) {
  160. /* Trim off trailing comma */
  161. buf[strlen(buf) - 1] = '\0';
  162. strncat(buf, "\n", bufsize - strlen(buf) - 1);
  163. return 0;
  164. }
  165. return -1;
  166. }
  167. static int writefile(char *s, char *acc)
  168. {
  169. char tmp[AST_CONFIG_MAX_PATH];
  170. FILE *f;
  171. if (strchr(acc, '/') || (acc[0] == '.')) {
  172. ast_log(LOG_WARNING, "Account code '%s' insecure for writing file\n", acc);
  173. return -1;
  174. }
  175. snprintf(tmp, sizeof(tmp), "%s/%s/%s.csv", (char *)ast_config_AST_LOG_DIR,CSV_LOG_DIR, acc);
  176. f = fopen(tmp, "a");
  177. if (!f)
  178. return -1;
  179. fputs(s, f);
  180. fflush(f);
  181. fclose(f);
  182. return 0;
  183. }
  184. static int csv_log(struct ast_cdr *cdr)
  185. {
  186. /* Make sure we have a big enough buf */
  187. char buf[1024];
  188. char csvmaster[AST_CONFIG_MAX_PATH];
  189. snprintf(csvmaster, sizeof(csvmaster),"%s/%s/%s", ast_config_AST_LOG_DIR, CSV_LOG_DIR, CSV_MASTER);
  190. #if 0
  191. printf("[CDR] %s ('%s' -> '%s') Dur: %ds Bill: %ds Disp: %s Flags: %s Account: [%s]\n", cdr->channel, cdr->src, cdr->dst, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), cdr->accountcode);
  192. #endif
  193. if (build_csv_record(buf, sizeof(buf), cdr)) {
  194. ast_log(LOG_WARNING, "Unable to create CSV record in %d bytes. CDR not recorded!\n", (int)sizeof(buf));
  195. } else {
  196. /* because of the absolutely unconditional need for the
  197. highest reliability possible in writing billing records,
  198. we open write and close the log file each time */
  199. mf = fopen(csvmaster, "a");
  200. if (!mf) {
  201. ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", csvmaster, strerror(errno));
  202. }
  203. if (mf) {
  204. fputs(buf, mf);
  205. fflush(mf); /* be particularly anal here */
  206. fclose(mf);
  207. mf = NULL;
  208. }
  209. if (!ast_strlen_zero(cdr->accountcode)) {
  210. if (writefile(buf, cdr->accountcode))
  211. ast_log(LOG_WARNING, "Unable to write CSV record to account file '%s' : %s\n", cdr->accountcode, strerror(errno));
  212. }
  213. }
  214. return 0;
  215. }
  216. char *description(void)
  217. {
  218. return desc;
  219. }
  220. int unload_module(void)
  221. {
  222. if (mf)
  223. fclose(mf);
  224. ast_cdr_unregister(name);
  225. return 0;
  226. }
  227. int load_module(void)
  228. {
  229. int res;
  230. res = ast_cdr_register(name, desc, csv_log);
  231. if (res) {
  232. ast_log(LOG_ERROR, "Unable to register CSV CDR handling\n");
  233. if (mf)
  234. fclose(mf);
  235. }
  236. return res;
  237. }
  238. int reload(void)
  239. {
  240. return 0;
  241. }
  242. int usecount(void)
  243. {
  244. return 0;
  245. }
  246. char *key()
  247. {
  248. return ASTERISK_GPL_KEY;
  249. }