Add "logger reload" CLI (bug #345)
[asterisk/asterisk.git] / logger.c
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
14 #include <stdarg.h>
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <time.h>
18 #include <asterisk/lock.h>
19 #include <asterisk/logger.h>
20 #include <asterisk/options.h>
21 #include <asterisk/channel.h>
22 #include <asterisk/config.h>
23 #include <asterisk/term.h>
24 #include <asterisk/cli.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <pthread.h>
29 #include <sys/stat.h>
30 #include "asterisk.h"
31 #include "astconf.h"
32
33 #define MAX_MSG_QUEUE 200
34
35 static ast_mutex_t msglist_lock = AST_MUTEX_INITIALIZER;
36 static ast_mutex_t loglock = AST_MUTEX_INITIALIZER;
37
38 static struct msglist {
39         char *msg;
40         struct msglist *next;
41 } *list = NULL, *last = NULL;
42
43 struct logfile {
44         char fn[256];
45         int logflags;
46         FILE *f;
47         struct logfile *next;
48 };
49
50 static struct logfile *logfiles = NULL;
51
52 static int msgcnt = 0;
53
54 static FILE *eventlog = NULL;
55
56 static char *levels[] = {
57         "DEBUG",
58         "EVENT",
59         "NOTICE",
60         "WARNING",
61         "ERROR"
62 };
63
64 static int colors[] = {
65         COLOR_BRGREEN,
66         COLOR_BRBLUE,
67         COLOR_YELLOW,
68         COLOR_BRRED,
69         COLOR_RED
70 };
71
72 static int make_components(char *s, int lineno)
73 {
74         char *w;
75         int res = 0;
76         char *stringp=NULL;
77         stringp=s;
78         w = strsep(&stringp, ",");
79         while(w) {
80                 while(*w && (*w < 33))
81                         w++;
82                 if (!strcasecmp(w, "debug"))
83                         res |= (1 << 0);
84                 else if (!strcasecmp(w, "notice"))
85                         res |= (1 << 2);
86                 else if (!strcasecmp(w, "warning"))
87                         res |= (1 << 3);
88                 else if (!strcasecmp(w, "error"))
89                         res |= (1 << 4);
90                 else {
91                         fprintf(stderr, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w, lineno);
92                 }
93                 w = strsep(&stringp, ",");
94         }
95         return res;
96 }
97
98 static struct logfile *make_logfile(char *fn, char *components, int lineno)
99 {
100         struct logfile *f;
101         char tmp[256];
102         if (!strlen(fn))
103                 return NULL;
104         f = malloc(sizeof(struct logfile));
105         if (f) {
106                 memset(f, 0, sizeof(f));
107                 strncpy(f->fn, fn, sizeof(f->fn) - 1);
108                 if (!strcasecmp(fn, "ignore")) {
109                         f->f = NULL;
110                 } else if (!strcasecmp(fn, "console")) {
111                         f->f = stdout;
112                 } else {
113                         if (fn[0] == '/') 
114                                 strncpy(tmp, fn, sizeof(tmp) - 1);
115                         else
116                                 snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, fn);
117                         f->f = fopen(tmp, "a");
118                         if (!f->f) {
119                                 /* Can't log here, since we're called with a lock */
120                                 fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", tmp, strerror(errno));
121                         }
122                 }
123                 f->logflags = make_components(components, lineno);
124                 
125         }
126         return f;
127 }
128
129 static void init_logger_chain(void)
130 {
131         struct logfile *f, *cur;
132         struct ast_config *cfg;
133         struct ast_variable *var;
134
135         ast_mutex_lock(&loglock);
136
137         /* Free anything that is here */
138         f = logfiles;
139         while(f) {
140                 cur = f->next;
141                 if (f->f && (f->f != stdout) && (f->f != stderr))
142                         fclose(f->f);
143                 free(f);
144                 f = cur;
145         }
146
147         logfiles = NULL;
148
149         ast_mutex_unlock(&loglock);
150         cfg = ast_load("logger.conf");
151         ast_mutex_lock(&loglock);
152         
153         /* If no config file, we're fine */
154         if (!cfg) {
155                 ast_mutex_unlock(&loglock);
156                 return;
157         }
158         var = ast_variable_browse(cfg, "logfiles");
159         while(var) {
160                 f = make_logfile(var->name, var->value, var->lineno);
161                 if (f) {
162                         f->next = logfiles;
163                         logfiles = f;
164                 }
165                 var = var->next;
166         }
167         if (!logfiles) {
168                 /* Gotta have at least one.  We'll make a NULL one */
169                 logfiles = make_logfile("ignore", "", -1);
170         }
171         ast_destroy(cfg);
172         ast_mutex_unlock(&loglock);
173         
174
175 }
176
177 int reload_logger(void)
178 {
179         char tmp[AST_CONFIG_MAX_PATH];
180         ast_mutex_lock(&loglock);
181         if (eventlog)
182                 fclose(eventlog);
183         mkdir((char *)ast_config_AST_LOG_DIR, 0755);
184         snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
185         eventlog = fopen((char *)tmp, "a");
186         ast_mutex_unlock(&loglock);
187
188         if (eventlog) {
189                 init_logger_chain();
190                 ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
191                 if (option_verbose)
192                         ast_verbose("Asterisk Event Logger restarted\n");
193                 return 0;
194         } else 
195                 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
196         init_logger_chain();
197         return -1;
198 }
199
200 static int handle_logger_reload(int fd, int argc, char *argv[])
201 {
202         if(reload_logger())
203         {
204                 ast_cli(fd, "Failed to reloadthe logger\n");
205                 return RESULT_FAILURE;
206         }
207         else
208                 return RESULT_SUCCESS;
209 }
210
211 static struct verb {
212         void (*verboser)(const char *string, int opos, int replacelast, int complete);
213         struct verb *next;
214 } *verboser = NULL;
215
216
217 static char logger_reload_help[] =
218 "Usage: logger reload\n"
219 "       Reopens the log files.  Use after a rotating the log files\n";
220
221 static struct ast_cli_entry reload_logger_cli = 
222         { { "logger", "reload", NULL }, 
223         handle_logger_reload, "Reopens the log files",
224         logger_reload_help };
225
226
227 int init_logger(void)
228 {
229         char tmp[AST_CONFIG_MAX_PATH];
230
231         /* register the relaod logger cli command */
232         ast_cli_register(&reload_logger_cli);
233         
234         mkdir((char *)ast_config_AST_LOG_DIR, 0755);
235         snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_LOG_DIR, EVENTLOG);
236         eventlog = fopen((char *)tmp, "a");
237         if (eventlog) {
238                 init_logger_chain();
239                 ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
240                 if (option_verbose)
241                         ast_verbose("Asterisk Event Logger Started %s\n",(char *)tmp);
242                 return 0;
243         } else 
244                 ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
245         init_logger_chain();
246         return -1;
247 }
248
249
250 extern void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
251 {
252         char date[256];
253         char tmp[80];
254         char tmp2[80];
255         char tmp3[80];
256         char tmp4[80];
257         char linestr[80];
258         time_t t;
259         struct tm tm;
260         struct logfile *f;
261
262         va_list ap;
263         if (!option_verbose && !option_debug && (!level)) {
264                 return;
265         }
266         ast_mutex_lock(&loglock);
267         if (level == 1 /* Event */) {
268                 time(&t);
269                 localtime_r(&t,&tm);
270                 if (&tm) {
271                         /* Log events into the event log file, with a different format */
272                         strftime(date, sizeof(date), "%b %e %T", &tm);
273                         fprintf(eventlog, "%s asterisk[%d]: ", date, getpid());
274                         va_start(ap, fmt);
275                         vfprintf(eventlog, fmt, ap);
276                         va_end(ap);
277                         fflush(eventlog);
278                 } else
279                         /** Cannot use ast_log() from locked section of ast_log()!
280                             ast_log(LOG_WARNING, "Unable to retrieve local time?\n"); **/
281                         fprintf(stderr, "ast_log: Unable to retrieve local time for %ld?\n", (long)t);
282         } else {
283                 if (logfiles) {
284                         f = logfiles;
285                         while(f) {
286                                 if (f->logflags & (1 << level) && f->f) {
287                                         if ((f->f != stdout) && (f->f != stderr)) {
288                                                 time(&t);
289                                                 localtime_r(&t,&tm);
290                                                 strftime(date, sizeof(date), "%b %e %T", &tm);
291                                                 fprintf(f->f, "%s %s[%ld]: File %s, Line %d (%s): ", date, levels[level], (long)pthread_self(), file, line, function);
292                                         } else {
293                                                 sprintf(linestr, "%d", line);
294                                                 fprintf(f->f, "%s[%ld]: File %s, Line %s (%s): ",
295                                                                                                                                 term_color(tmp, levels[level], colors[level], 0, sizeof(tmp)),
296                                                                                                                                 (long)pthread_self(),
297                                                                                                                                 term_color(tmp2, file, COLOR_BRWHITE, 0, sizeof(tmp2)),
298                                                                                                                                 term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
299                                                                                                                                 term_color(tmp4, function, COLOR_BRWHITE, 0, sizeof(tmp4)));
300                                         }
301                                         va_start(ap, fmt);
302                                         vfprintf(f->f, fmt, ap);
303                                         va_end(ap);
304                                         fflush(f->f);
305                                 }
306                                 f = f->next;
307                         }
308                 } else {
309                         fprintf(stdout, "%s[%ld]: File %s, Line %d (%s): ", levels[level], (long)pthread_self(), file, line, function);
310                         va_start(ap, fmt);
311                         vfprintf(stdout, fmt, ap);
312                         va_end(ap);
313                         fflush(stdout);
314                 }
315         }
316         ast_mutex_unlock(&loglock);
317 }
318
319 extern void ast_verbose(const char *fmt, ...)
320 {
321         static char stuff[4096];
322         static int pos = 0, opos;
323         static int replacelast = 0, complete;
324         struct msglist *m;
325         struct verb *v;
326         va_list ap;
327         va_start(ap, fmt);
328         ast_mutex_lock(&msglist_lock);
329         vsnprintf(stuff + pos, sizeof(stuff) - pos, fmt, ap);
330         opos = pos;
331         pos = strlen(stuff);
332         if (fmt[strlen(fmt)-1] == '\n') 
333                 complete = 1;
334         else
335                 complete=0;
336         if (complete) {
337                 if (msgcnt < MAX_MSG_QUEUE) {
338                         /* Allocate new structure */
339                         m = malloc(sizeof(struct msglist));
340                         msgcnt++;
341                 } else {
342                         /* Recycle the oldest entry */
343                         m = list;
344                         list = list->next;
345                         free(m->msg);
346                 }
347                 if (m) {
348                         m->msg = strdup(stuff);
349                         if (m->msg) {
350                                 if (last)
351                                         last->next = m;
352                                 else
353                                         list = m;
354                                 m->next = NULL;
355                                 last = m;
356                         } else {
357                                 msgcnt--;
358                                 ast_log(LOG_DEBUG, "Out of memory\n");
359                                 free(m);
360                         }
361                 }
362         }
363         if (verboser) {
364                 v = verboser;
365                 while(v) {
366                         v->verboser(stuff, opos, replacelast, complete);
367                         v = v->next;
368                 }
369         } /* else
370                 fprintf(stdout, stuff + opos); */
371
372         if (fmt[strlen(fmt)-1] != '\n') 
373                 replacelast = 1;
374         else 
375                 replacelast = pos = 0;
376         va_end(ap);
377         ast_mutex_unlock(&msglist_lock);
378 }
379
380 int ast_verbose_dmesg(void (*v)(const char *string, int opos, int replacelast, int complete))
381 {
382         struct msglist *m;
383         m = list;
384         ast_mutex_lock(&msglist_lock);
385         while(m) {
386                 /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
387                 v(m->msg, 0, 0, 1);
388                 m = m->next;
389         }
390         ast_mutex_unlock(&msglist_lock);
391         return 0;
392 }
393
394 int ast_register_verbose(void (*v)(const char *string, int opos, int replacelast, int complete)) 
395 {
396         struct msglist *m;
397         struct verb *tmp;
398         /* XXX Should be more flexible here, taking > 1 verboser XXX */
399         if ((tmp = malloc(sizeof (struct verb)))) {
400                 tmp->verboser = v;
401                 ast_mutex_lock(&msglist_lock);
402                 tmp->next = verboser;
403                 verboser = tmp;
404                 m = list;
405                 while(m) {
406                         /* Send all the existing entries that we have queued (i.e. they're likely to have missed) */
407                         v(m->msg, 0, 0, 1);
408                         m = m->next;
409                 }
410                 ast_mutex_unlock(&msglist_lock);
411                 return 0;
412         }
413         return -1;
414 }
415
416 int ast_unregister_verbose(void (*v)(const char *string, int opos, int replacelast, int complete))
417 {
418         int res = -1;
419         struct verb *tmp, *tmpl=NULL;
420         ast_mutex_lock(&msglist_lock);
421         tmp = verboser;
422         while(tmp) {
423                 if (tmp->verboser == v) {
424                         if (tmpl)
425                                 tmpl->next = tmp->next;
426                         else
427                                 verboser = tmp->next;
428                         free(tmp);
429                         break;
430                 }
431                 tmpl = tmp;
432                 tmp = tmp->next;
433         }
434         if (tmp)
435                 res = 0;
436         ast_mutex_unlock(&msglist_lock);
437         return res;
438 }