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