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