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