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