logger.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. /*
  2. * Asterisk Logger
  3. *
  4. * Mark Spencer <markster@marko.net>
  5. *
  6. * Copyright(C)1999, Linux Support Services, Inc.
  7. *
  8. * Distributed under the terms of the GNU General Public License (GPL) Version 2
  9. *
  10. * Logging routines
  11. *
  12. */
  13. #include <signal.h>
  14. #include <stdarg.h>
  15. #include <stdio.h>
  16. #include <unistd.h>
  17. #include <time.h>
  18. #include <asterisk/lock.h>
  19. #include <asterisk/options.h>
  20. #include <asterisk/channel.h>
  21. #include <asterisk/config.h>
  22. #include <asterisk/term.h>
  23. #include <asterisk/cli.h>
  24. #include <asterisk/utils.h>
  25. #include <string.h>
  26. #include <stdlib.h>
  27. #include <errno.h>
  28. #include <sys/stat.h>
  29. #include "asterisk.h"
  30. #include "astconf.h"
  31. #define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
  32. from <syslog.h> which is included by logger.h */
  33. #include <syslog.h>
  34. static int syslog_level_map[] = {
  35. LOG_DEBUG,
  36. LOG_INFO, /* arbitrary equivalent of LOG_EVENT */
  37. LOG_NOTICE,
  38. LOG_WARNING,
  39. LOG_ERR,
  40. LOG_DEBUG
  41. };
  42. #define SYSLOG_NLEVELS 6
  43. #include <asterisk/logger.h>
  44. #define MAX_MSG_QUEUE 200
  45. #if defined(__linux__) && defined (__NR_gettid)
  46. #include <asm/unistd.h>
  47. #define GETTID() syscall(__NR_gettid)
  48. #else
  49. #define GETTID() getpid()
  50. #endif
  51. static char dateformat[256] = "%b %e %T"; /* Original Asterisk Format */
  52. AST_MUTEX_DEFINE_STATIC(msglist_lock);
  53. AST_MUTEX_DEFINE_STATIC(loglock);
  54. static int pending_logger_reload = 0;
  55. static struct msglist {
  56. char *msg;
  57. struct msglist *next;
  58. } *list = NULL, *last = NULL;
  59. static char hostname[MAXHOSTNAMELEN];
  60. struct logchannel {
  61. int logmask;
  62. int facility; /* syslog */
  63. int syslog; /* syslog flag */
  64. int console; /* console logging */
  65. FILE *fileptr; /* logfile logging */
  66. char filename[256];
  67. struct logchannel *next;
  68. };
  69. static struct logchannel *logchannels = NULL;
  70. static int msgcnt = 0;
  71. static FILE *eventlog = NULL;
  72. static char *levels[] = {
  73. "DEBUG",
  74. "EVENT",
  75. "NOTICE",
  76. "WARNING",
  77. "ERROR",
  78. "VERBOSE"
  79. };
  80. static int colors[] = {
  81. COLOR_BRGREEN,
  82. COLOR_BRBLUE,
  83. COLOR_YELLOW,
  84. COLOR_BRRED,
  85. COLOR_RED,
  86. COLOR_GREEN
  87. };
  88. static int make_components(char *s, int lineno)
  89. {
  90. char *w;
  91. int res = 0;
  92. char *stringp=NULL;
  93. stringp=s;
  94. w = strsep(&stringp, ",");
  95. while(w) {
  96. while(*w && (*w < 33))
  97. w++;
  98. if (!strcasecmp(w, "error"))
  99. res |= (1 << __LOG_ERROR);
  100. else if (!strcasecmp(w, "warning"))
  101. res |= (1 << __LOG_WARNING);
  102. else if (!strcasecmp(w, "notice"))
  103. res |= (1 << __LOG_NOTICE);
  104. else if (!strcasecmp(w, "event"))
  105. res |= (1 << __LOG_EVENT);
  106. else if (!strcasecmp(w, "debug"))
  107. res |= (1 << __LOG_DEBUG);
  108. else if (!strcasecmp(w, "verbose"))
  109. res |= (1 << __LOG_VERBOSE);
  110. else {
  111. fprintf(stderr, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w, lineno);
  112. }
  113. w = strsep(&stringp, ",");
  114. }
  115. return res;
  116. }
  117. static struct logchannel *make_logchannel(char *channel, char *components, int lineno)
  118. {
  119. struct logchannel *chan;
  120. char *facility;
  121. CODE *cptr;
  122. if (ast_strlen_zero(channel))
  123. return NULL;
  124. chan = malloc(sizeof(struct logchannel));
  125. if (chan) {
  126. memset(chan, 0, sizeof(struct logchannel));
  127. if (!strcasecmp(channel, "console")) {
  128. chan->console = 1;
  129. } else if (!strncasecmp(channel, "syslog", 6)) {
  130. /*
  131. * syntax is:
  132. * syslog.facility => level,level,level
  133. */
  134. facility = strchr(channel, '.');
  135. if(!facility++ || !facility) {
  136. facility = "local0";
  137. }
  138. /*
  139. * Walk through the list of facilitynames (defined in sys/syslog.h)
  140. * to see if we can find the one we have been given
  141. */
  142. chan->facility = -1;
  143. cptr = facilitynames;
  144. while (cptr->c_name) {
  145. if (!strcasecmp(facility, cptr->c_name)) {
  146. chan->facility = cptr->c_val;
  147. break;
  148. }
  149. cptr++;
  150. }
  151. if (0 > chan->facility) {
  152. fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
  153. free(chan);
  154. return NULL;
  155. }
  156. chan->syslog = 1;
  157. openlog("asterisk", LOG_PID, chan->facility);
  158. } else {
  159. if (channel[0] == '/') {
  160. if(!ast_strlen_zero(hostname)) {
  161. snprintf(chan->filename, sizeof(chan->filename) - 1,"%s.%s", channel, hostname);
  162. } else {
  163. strncpy(chan->filename, channel, sizeof(chan->filename) - 1);
  164. }
  165. }
  166. if(!ast_strlen_zero(hostname)) {
  167. snprintf(chan->filename, sizeof(chan->filename), "%s/%s.%s",(char *)ast_config_AST_LOG_DIR, channel, hostname);
  168. } else {
  169. snprintf(chan->filename, sizeof(chan->filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, channel);
  170. }
  171. chan->fileptr = fopen(chan->filename, "a");
  172. if (!chan->fileptr) {
  173. /* Can't log here, since we're called with a lock */
  174. fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno));
  175. }
  176. }
  177. chan->logmask = make_components(components, lineno);
  178. }
  179. return chan;
  180. }
  181. static void init_logger_chain(void)
  182. {
  183. struct logchannel *chan, *cur;
  184. struct ast_config *cfg;
  185. struct ast_variable *var;
  186. char *s;
  187. /* delete our list of log channels */
  188. ast_mutex_lock(&loglock);
  189. chan = logchannels;
  190. while (chan) {
  191. cur = chan->next;
  192. free(chan);
  193. chan = cur;
  194. }
  195. logchannels = NULL;
  196. ast_mutex_unlock(&loglock);
  197. /* close syslog */
  198. closelog();
  199. cfg = ast_load("logger.conf");
  200. /* If no config file, we're fine */
  201. if (!cfg)
  202. return;
  203. ast_mutex_lock(&loglock);
  204. if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
  205. if(ast_true(s)) {
  206. if(gethostname(hostname, sizeof(hostname)-1)) {
  207. strncpy(hostname, "unknown", sizeof(hostname)-1);
  208. ast_log(LOG_WARNING, "What box has no hostname???\n");
  209. }
  210. } else
  211. hostname[0] = '\0';
  212. } else
  213. hostname[0] = '\0';
  214. if ((s = ast_variable_retrieve(cfg, "general", "dateformat"))) {
  215. strncpy(dateformat, s, sizeof(dateformat) - 1);
  216. } else
  217. strncpy(dateformat, "%b %e %T", sizeof(dateformat) - 1);
  218. var = ast_variable_browse(cfg, "logfiles");
  219. while(var) {
  220. chan = make_logchannel(var->name, var->value, var->lineno);
  221. if (chan) {
  222. chan->next = logchannels;
  223. logchannels = chan;
  224. }
  225. var = var->next;
  226. }
  227. ast_destroy(cfg);
  228. ast_mutex_unlock(&loglock);
  229. }
  230. static FILE *qlog = NULL;
  231. AST_MUTEX_DEFINE_STATIC(qloglock);
  232. void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
  233. {
  234. va_list ap;
  235. ast_mutex_lock(&qloglock);
  236. if (qlog) {
  237. va_start(ap, fmt);
  238. fprintf(qlog, "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
  239. vfprintf(qlog, fmt, ap);
  240. fprintf(qlog, "\n");
  241. va_end(ap);
  242. fflush(qlog);
  243. }
  244. ast_mutex_unlock(&qloglock);
  245. }
  246. static void queue_log_init(void)
  247. {
  248. char filename[256];
  249. int reloaded = 0;
  250. ast_mutex_lock(&qloglock);
  251. if (qlog) {
  252. reloaded = 1;
  253. fclose(qlog);
  254. qlog = NULL;
  255. }
  256. snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_LOG_DIR, "queue_log");
  257. qlog = fopen(filename, "a");
  258. ast_mutex_unlock(&qloglock);
  259. if (reloaded)
  260. ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
  261. else
  262. ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
  263. }
  264. int reload_logger(int rotate)
  265. {
  266. char old[AST_CONFIG_MAX_PATH] = "";
  267. char new[AST_CONFIG_MAX_PATH];
  268. struct logchannel *f;
  269. FILE *myf;
  270. int x;
  271. ast_mutex_lock(&loglock);
  272. if (eventlog)
  273. fclose(eventlog);
  274. else
  275. rotate = 0;
  276. eventlog = NULL;
  277. mkdir((char *)ast_config_AST_LOG_DIR, 0755);
  278. snprintf(old, sizeof(old), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
  279. if(rotate) {
  280. for(x=0;;x++) {
  281. snprintf(new, sizeof(new), "%s/%s.%d", (char *)ast_config_AST_LOG_DIR, EVENTLOG,x);
  282. myf = fopen((char *)new, "r");
  283. if(myf)
  284. fclose(myf);
  285. else
  286. break;
  287. }
  288. /* do it */
  289. if (rename(old,new))
  290. fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
  291. }
  292. eventlog = fopen(old, "a");
  293. f = logchannels;
  294. while(f) {
  295. if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
  296. fclose(f->fileptr);
  297. f->fileptr = NULL;
  298. if(rotate) {
  299. strncpy(old, f->filename, sizeof(old) - 1);
  300. for(x=0;;x++) {
  301. snprintf(new, sizeof(new), "%s.%d", f->filename, x);
  302. myf = fopen((char *)new, "r");
  303. if (myf) {
  304. fclose(myf);
  305. } else {
  306. break;
  307. }
  308. }
  309. /* do it */
  310. if (rename(old,new))
  311. fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
  312. }
  313. }
  314. f = f->next;
  315. }
  316. ast_mutex_unlock(&loglock);
  317. pending_logger_reload = 0;
  318. queue_log_init();
  319. if (eventlog) {
  320. init_logger_chain();
  321. ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
  322. if (option_verbose)
  323. ast_verbose("Asterisk Event Logger restarted\n");
  324. return 0;
  325. } else
  326. ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
  327. init_logger_chain();
  328. return -1;
  329. }
  330. static int handle_logger_reload(int fd, int argc, char *argv[])
  331. {
  332. if(reload_logger(0))
  333. {
  334. ast_cli(fd, "Failed to reloadthe logger\n");
  335. return RESULT_FAILURE;
  336. }
  337. else
  338. return RESULT_SUCCESS;
  339. }
  340. static int handle_logger_rotate(int fd, int argc, char *argv[])
  341. {
  342. if(reload_logger(1))
  343. {
  344. ast_cli(fd, "Failed to reloadthe logger\n");
  345. return RESULT_FAILURE;
  346. }
  347. else
  348. return RESULT_SUCCESS;
  349. }
  350. static struct verb {
  351. void (*verboser)(const char *string, int opos, int replacelast, int complete);
  352. struct verb *next;
  353. } *verboser = NULL;
  354. static char logger_reload_help[] =
  355. "Usage: logger reload\n"
  356. " Reloads the logger subsystem state. Use after restarting syslogd(8)\n";
  357. static char logger_rotate_help[] =
  358. "Usage: logger rotate\n"
  359. " Rotates and Reopens the log files.\n";
  360. static struct ast_cli_entry reload_logger_cli =
  361. { { "logger", "reload", NULL },
  362. handle_logger_reload, "Reopens the log files",
  363. logger_reload_help };
  364. static struct ast_cli_entry rotate_logger_cli =
  365. { { "logger", "rotate", NULL },
  366. handle_logger_rotate, "Rotates and reopens the log files",
  367. logger_rotate_help };
  368. static int handle_SIGXFSZ(int sig)
  369. {
  370. /* Indicate need to reload */
  371. pending_logger_reload = 1;
  372. return 0;
  373. }
  374. int init_logger(void)
  375. {
  376. char tmp[256];
  377. /* auto rotate if sig SIGXFSZ comes a-knockin */
  378. (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
  379. /* register the relaod logger cli command */
  380. ast_cli_register(&reload_logger_cli);
  381. ast_cli_register(&rotate_logger_cli);
  382. /* initialize queue logger */
  383. queue_log_init();
  384. /* create the eventlog */
  385. mkdir((char *)ast_config_AST_LOG_DIR, 0755);
  386. snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
  387. eventlog = fopen((char *)tmp, "a");
  388. if (eventlog) {
  389. init_logger_chain();
  390. ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
  391. if (option_verbose)
  392. ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp);
  393. return 0;
  394. } else
  395. ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
  396. /* create log channels */
  397. init_logger_chain();
  398. return -1;
  399. }
  400. void close_logger(void)
  401. {
  402. struct msglist *m, *tmp;
  403. ast_mutex_lock(&msglist_lock);
  404. m = list;
  405. while(m) {
  406. if (m->msg) {
  407. free(m->msg);
  408. }
  409. tmp = m->next;
  410. free(m);
  411. m = tmp;
  412. }
  413. list = last = NULL;
  414. msgcnt = 0;
  415. ast_mutex_unlock(&msglist_lock);
  416. return;
  417. }
  418. static void strip_coloring(char *str)
  419. {
  420. char *src, *dest, *end;
  421. if (!str)
  422. return;
  423. /* find the first potential escape sequence in the string */
  424. src = strchr(str, '\033');
  425. if (!src)
  426. return;
  427. dest = src;
  428. while (*src) {
  429. /* at the top of this loop, *src will always be an ESC character */
  430. if ((src[1] == '[') && ((end = strchr(src + 2, 'm'))))
  431. src = end + 1;
  432. else
  433. *dest++ = *src++;
  434. /* copy characters, checking for ESC as we go */
  435. while (*src && (*src != '\033'))
  436. *dest++ = *src++;
  437. }
  438. *dest = '\0';
  439. }
  440. static void ast_log_vsyslog(int level, const char *file, int line, const char *function, const char *fmt, va_list args)
  441. {
  442. char buf[BUFSIZ];
  443. char *s;
  444. if (level >= SYSLOG_NLEVELS) {
  445. /* we are locked here, so cannot ast_log() */
  446. fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
  447. return;
  448. }
  449. if (level == __LOG_VERBOSE) {
  450. snprintf(buf, sizeof(buf), "VERBOSE[%ld]: ", (long)GETTID());
  451. level = __LOG_DEBUG;
  452. } else {
  453. snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: ",
  454. levels[level], (long)GETTID(), file, line, function);
  455. }
  456. s = buf + strlen(buf);
  457. vsnprintf(s, sizeof(buf) - strlen(buf), fmt, args);
  458. strip_coloring(s);
  459. syslog(syslog_level_map[level], "%s", buf);
  460. }
  461. /*
  462. * send log messages to syslog and/or the console
  463. */
  464. void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
  465. {
  466. struct logchannel *chan;
  467. char buf[BUFSIZ];
  468. time_t t;
  469. struct tm tm;
  470. char date[256];
  471. va_list ap;
  472. if (!option_verbose && !option_debug && (level == __LOG_DEBUG)) {
  473. return;
  474. }
  475. /* begin critical section */
  476. ast_mutex_lock(&loglock);
  477. time(&t);
  478. localtime_r(&t, &tm);
  479. strftime(date, sizeof(date), dateformat, &tm);
  480. if (level == __LOG_EVENT) {
  481. va_start(ap, fmt);
  482. fprintf(eventlog, "%s asterisk[%d]: ", date, getpid());
  483. vfprintf(eventlog, fmt, ap);
  484. fflush(eventlog);
  485. va_end(ap);
  486. ast_mutex_unlock(&loglock);
  487. return;
  488. }
  489. if (logchannels) {
  490. chan = logchannels;
  491. while(chan) {
  492. if (chan->syslog && (chan->logmask & (1 << level))) {
  493. va_start(ap, fmt);
  494. ast_log_vsyslog(level, file, line, function, fmt, ap);
  495. va_end(ap);
  496. } else if ((chan->logmask & (1 << level)) && (chan->console)) {
  497. char linestr[128];
  498. char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
  499. if (level != __LOG_VERBOSE) {
  500. sprintf(linestr, "%d", line);
  501. snprintf(buf, sizeof(buf), "%s %s[%ld]: %s:%s %s: ",
  502. date,
  503. term_color(tmp1, levels[level], colors[level], 0, sizeof(tmp1)),
  504. (long)GETTID(),
  505. term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
  506. term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
  507. term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
  508. ast_console_puts(buf);
  509. va_start(ap, fmt);
  510. vsnprintf(buf, sizeof(buf), fmt, ap);
  511. va_end(ap);
  512. ast_console_puts(buf);
  513. }
  514. } else if ((chan->logmask & (1 << level)) && (chan->fileptr)) {
  515. snprintf(buf, sizeof(buf), "%s %s[%ld]: ", date,
  516. levels[level], (long)GETTID());
  517. fprintf(chan->fileptr, buf);
  518. va_start(ap, fmt);
  519. vsnprintf(buf, sizeof(buf), fmt, ap);
  520. strip_coloring(buf);
  521. va_end(ap);
  522. fputs(buf, chan->fileptr);
  523. fflush(chan->fileptr);
  524. }
  525. chan = chan->next;
  526. }
  527. } else {
  528. /*
  529. * we don't have the logger chain configured yet,
  530. * so just log to stdout
  531. */
  532. if (level != __LOG_VERBOSE) {
  533. va_start(ap, fmt);
  534. vsnprintf(buf, sizeof(buf), fmt, ap);
  535. va_end(ap);
  536. fputs(buf, stdout);
  537. }
  538. }
  539. ast_mutex_unlock(&loglock);
  540. /* end critical section */
  541. if (pending_logger_reload) {
  542. reload_logger(1);
  543. ast_log(LOG_EVENT,"Rotated Logs Per SIGXFSZ\n");
  544. if (option_verbose)
  545. ast_verbose("Rotated Logs Per SIGXFSZ\n");
  546. }
  547. }
  548. extern void ast_verbose(const char *fmt, ...)
  549. {
  550. static char stuff[4096];
  551. static int pos = 0, opos;
  552. static int replacelast = 0, complete;
  553. struct msglist *m;
  554. struct verb *v;
  555. va_list ap;
  556. va_start(ap, fmt);
  557. ast_mutex_lock(&msglist_lock);
  558. vsnprintf(stuff + pos, sizeof(stuff) - pos, fmt, ap);
  559. opos = pos;
  560. pos = strlen(stuff);
  561. if (fmt[strlen(fmt)-1] == '\n')
  562. complete = 1;
  563. else
  564. complete=0;
  565. if (complete) {
  566. if (msgcnt < MAX_MSG_QUEUE) {
  567. /* Allocate new structure */
  568. m = malloc(sizeof(struct msglist));
  569. msgcnt++;
  570. } else {
  571. /* Recycle the oldest entry */
  572. m = list;
  573. list = list->next;
  574. free(m->msg);
  575. }
  576. if (m) {
  577. m->msg = strdup(stuff);
  578. if (m->msg) {
  579. if (last)
  580. last->next = m;
  581. else
  582. list = m;
  583. m->next = NULL;
  584. last = m;
  585. } else {
  586. msgcnt--;
  587. ast_log(LOG_ERROR, "Out of memory\n");
  588. free(m);
  589. }
  590. }
  591. }
  592. if (verboser) {
  593. v = verboser;
  594. while(v) {
  595. v->verboser(stuff, opos, replacelast, complete);
  596. v = v->next;
  597. }
  598. } /* else
  599. fprintf(stdout, stuff + opos); */
  600. ast_log(LOG_VERBOSE, "%s", stuff);
  601. if (fmt[strlen(fmt)-1] != '\n')
  602. replacelast = 1;
  603. else
  604. replacelast = pos = 0;
  605. va_end(ap);
  606. ast_mutex_unlock(&msglist_lock);
  607. }
  608. int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
  609. {
  610. struct msglist *m;
  611. ast_mutex_lock(&msglist_lock);
  612. m = list;
  613. while(m) {
  614. /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
  615. v(m->msg, 0, 0, 1);
  616. m = m->next;
  617. }
  618. ast_mutex_unlock(&msglist_lock);
  619. return 0;
  620. }
  621. int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
  622. {
  623. struct msglist *m;
  624. struct verb *tmp;
  625. /* XXX Should be more flexible here, taking > 1 verboser XXX */
  626. if ((tmp = malloc(sizeof (struct verb)))) {
  627. tmp->verboser = v;
  628. ast_mutex_lock(&msglist_lock);
  629. tmp->next = verboser;
  630. verboser = tmp;
  631. m = list;
  632. while(m) {
  633. /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
  634. v(m->msg, 0, 0, 1);
  635. m = m->next;
  636. }
  637. ast_mutex_unlock(&msglist_lock);
  638. return 0;
  639. }
  640. return -1;
  641. }
  642. int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
  643. {
  644. int res = -1;
  645. struct verb *tmp, *tmpl=NULL;
  646. ast_mutex_lock(&msglist_lock);
  647. tmp = verboser;
  648. while(tmp) {
  649. if (tmp->verboser == v) {
  650. if (tmpl)
  651. tmpl->next = tmp->next;
  652. else
  653. verboser = tmp->next;
  654. free(tmp);
  655. break;
  656. }
  657. tmpl = tmp;
  658. tmp = tmp->next;
  659. }
  660. if (tmp)
  661. res = 0;
  662. ast_mutex_unlock(&msglist_lock);
  663. return res;
  664. }