chiark / gitweb /
0981506cc696116a5223a5d783cdcd3d08e53d4e
[secnet.git] / log.c
1 #include "secnet.h"
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <time.h>
6 #include <errno.h>
7 #include <syslog.h>
8 #include "process.h"
9
10 bool_t secnet_is_daemon=False;
11 uint32_t message_level=M_WARNING|M_ERROR|M_SECURITY|M_FATAL;
12 struct log_if *system_log=NULL;
13
14 static void vMessage(uint32_t class, char *message, va_list args)
15 {
16     FILE *dest=stdout;
17 #define MESSAGE_BUFLEN 1023
18     static char buff[MESSAGE_BUFLEN+1]={0,};
19     uint32_t bp;
20     char *nlp;
21
22     if (secnet_is_daemon) {
23         /* Messages go to the system log interface */
24         bp=strlen(buff);
25         vsnprintf(buff+bp,MESSAGE_BUFLEN-bp,message,args);
26         /* Each line is sent separately */
27         while ((nlp=strchr(buff,'\n'))) {
28             *nlp=0;
29             log(system_log,class,buff);
30             memmove(buff,nlp+1,strlen(nlp+1)+1);
31         }
32     } else {
33         /* Messages go to stdout/stderr */
34         if (class & message_level) {
35             if (class&M_FATAL || class&M_ERROR || class&M_WARNING) {
36                 dest=stderr;
37             }
38             vfprintf(dest,message,args);
39         }
40     }
41 }  
42
43 void Message(uint32_t class, char *message, ...)
44 {
45     va_list ap;
46
47     va_start(ap,message);
48     vMessage(class,message,ap);
49     va_end(ap);
50 }
51
52 static void vfatal(int status, bool_t perror, char *message, va_list args)
53 {
54     int err;
55
56     err=errno;
57
58     enter_phase(PHASE_SHUTDOWN);
59     if (perror) {
60         Message(M_FATAL, "secnet fatal error: ");
61         vMessage(M_FATAL, message, args);
62         Message(M_FATAL, ": %s\n",strerror(err));
63     }
64     else {
65         Message(M_FATAL, "secnet fatal error: ");
66         vMessage(M_FATAL,message,args);
67     }
68     exit(status);
69 }
70
71 void fatal(char *message, ...)
72 {
73     va_list args;
74     va_start(args,message);
75     vfatal(current_phase,False,message,args);
76     va_end(args);
77 }
78
79 void fatal_status(int status, char *message, ...)
80 {
81     va_list args;
82     va_start(args,message);
83     vfatal(status,False,message,args);
84     va_end(args);
85 }
86
87 void fatal_perror(char *message, ...)
88 {
89     va_list args;
90     va_start(args,message);
91     vfatal(current_phase,True,message,args);
92     va_end(args);
93 }
94
95 void fatal_perror_status(int status, char *message, ...)
96 {
97     va_list args;
98     va_start(args,message);
99     vfatal(status,True,message,args);
100     va_end(args);
101 }
102
103 void cfgfatal(struct cloc loc, string_t facility, char *message, ...)
104 {
105     va_list args;
106
107     va_start(args,message);
108
109     enter_phase(PHASE_SHUTDOWN);
110
111     if (loc.file && loc.line) {
112         Message(M_FATAL, "config error (%s, %s:%d): ",facility,loc.file,
113                 loc.line);
114     } else if (!loc.file && loc.line) {
115         Message(M_FATAL, "config error (%s, line %d): ",facility,loc.line);
116     } else {
117         Message(M_FATAL, "config error (%s): ",facility);
118     }
119     
120     vMessage(M_FATAL,message,args);
121     va_end(args);
122     exit(current_phase);
123 }
124
125 /* Take a list of log closures and merge them */
126 struct loglist {
127     struct log_if *l;
128     struct loglist *next;
129 };
130
131 static void log_vmulti(void *sst, int class, char *message, va_list args)
132 {
133     struct loglist *st=sst, *i;
134
135     if (secnet_is_daemon) {
136         for (i=st; i; i=i->next) {
137             i->l->vlog(i->l->st,class,message,args);
138         }
139     } else {
140         vMessage(class,message,args);
141         Message(class,"\n");
142     }
143 }
144
145 static void log_multi(void *st, int priority, char *message, ...)
146 {
147     va_list ap;
148
149     va_start(ap,message);
150     log_vmulti(st,priority,message,ap);
151     va_end(ap);
152 }
153
154 struct log_if *init_log(list_t *ll)
155 {
156     int i=0;
157     item_t *item;
158     closure_t *cl;
159     struct loglist *l=NULL, *n;
160     struct log_if *r;
161
162     if (list_length(ll)==1) {
163         item=list_elem(ll,0);
164         cl=item->data.closure;
165         if (cl->type!=CL_LOG) {
166             cfgfatal(item->loc,"init_log","closure is not a logger");
167         }
168         return cl->interface;
169     }
170     while ((item=list_elem(ll,i++))) {
171         if (item->type!=t_closure) {
172             cfgfatal(item->loc,"init_log","item is not a closure");
173         }
174         cl=item->data.closure;
175         if (cl->type!=CL_LOG) {
176             cfgfatal(item->loc,"init_log","closure is not a logger");
177         }
178         n=safe_malloc(sizeof(*n),"init_log");
179         n->l=cl->interface;
180         n->next=l;
181         l=n;
182     }
183     if (!l) {
184         fatal("init_log: no log");
185     }
186     r=safe_malloc(sizeof(*r), "init_log");
187     r->st=l;
188     r->log=log_multi;
189     r->vlog=log_vmulti;
190     return r;
191 }
192
193 struct logfile {
194     closure_t cl;
195     struct log_if ops;
196     struct cloc loc;
197     string_t logfile;
198     uint32_t level;
199     FILE *f;
200 };
201
202 static string_t months[]={
203     "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
204
205 static void logfile_vlog(void *sst, int class, char *message, va_list args)
206 {
207     struct logfile *st=sst;
208     time_t t;
209     struct tm *tm;
210
211     if (secnet_is_daemon) {
212         if (class&st->level) {
213             t=time(NULL);
214             tm=localtime(&t);
215             fprintf(st->f,"%s %2d %02d:%02d:%02d ",
216                     months[tm->tm_mon],tm->tm_mday,tm->tm_hour,tm->tm_min,
217                     tm->tm_sec);
218             vfprintf(st->f,message,args);
219             fprintf(st->f,"\n");
220             fflush(st->f);
221         }
222     } else {
223         vMessage(class,message,args);
224         Message(class,"\n");
225     }
226 }
227
228 static void logfile_log(void *state, int priority, char *message, ...)
229 {
230     va_list ap;
231
232     va_start(ap,message);
233     logfile_vlog(state,priority,message,ap);
234     va_end(ap);
235 }
236
237 static void logfile_hup_notify(void *sst, int signum)
238 {
239     struct logfile *st=sst;
240     FILE *f;
241     f=fopen(st->logfile,"a");
242     if (!f) {
243         logfile_log(st,M_FATAL,"received SIGHUP, but could not reopen "
244                     "logfile: %s",strerror(errno));
245     } else {
246         fclose(st->f);
247         st->f=f;
248         logfile_log(st,M_INFO,"received SIGHUP");
249     }
250 }
251
252 static void logfile_phase_hook(void *sst, uint32_t new_phase)
253 {
254     struct logfile *st=sst;
255     FILE *f;
256
257     if (background) {
258         f=fopen(st->logfile,"a");
259         if (!f) fatal_perror("logfile (%s:%d): cannot open \"%s\"",
260                              st->loc.file,st->loc.line,st->logfile);
261         st->f=f;
262         request_signal_notification(SIGHUP, logfile_hup_notify,st);
263     }
264 }
265
266 static struct flagstr message_class_table[]={
267     { "debug-config", M_DEBUG_CONFIG },
268     { "debug-phase", M_DEBUG_PHASE },
269     { "debug", M_DEBUG },
270     { "all-debug", M_DEBUG|M_DEBUG_PHASE|M_DEBUG_CONFIG },
271     { "info", M_INFO },
272     { "notice", M_NOTICE },
273     { "warning", M_WARNING },
274     { "error", M_ERROR },
275     { "security", M_SECURITY },
276     { "fatal", M_FATAL },
277     { "default", M_WARNING|M_ERROR|M_SECURITY|M_FATAL },
278     { "verbose", M_INFO|M_NOTICE|M_WARNING|M_ERROR|M_SECURITY|M_FATAL },
279     { "quiet", M_FATAL },
280     { NULL, 0 }
281 };
282
283 static list_t *logfile_apply(closure_t *self, struct cloc loc, dict_t *context,
284                              list_t *args)
285 {
286     struct logfile *st;
287     item_t *item;
288     dict_t *dict;
289
290     /* We should defer opening the logfile until the getresources
291        phase.  We should defer writing into the logfile until after we
292        become a daemon. */
293     
294     st=safe_malloc(sizeof(*st),"logfile_apply");
295     st->cl.description="logfile";
296     st->cl.type=CL_LOG;
297     st->cl.apply=NULL;
298     st->cl.interface=&st->ops;
299     st->ops.st=st;
300     st->ops.log=logfile_log;
301     st->ops.vlog=logfile_vlog;
302     st->loc=loc;
303     st->f=stderr;
304
305     item=list_elem(args,0);
306     if (!item || item->type!=t_dict) {
307         cfgfatal(loc,"logfile","argument must be a dictionary\n");
308     }
309     dict=item->data.dict;
310
311     st->logfile=dict_read_string(dict,"filename",True,"logfile",loc);
312     st->level=string_list_to_word(dict_lookup(dict,"class"),
313                                        message_class_table,"logfile");
314
315     add_hook(PHASE_GETRESOURCES,logfile_phase_hook,st);
316
317     return new_closure(&st->cl);
318 }
319
320 struct syslog {
321     closure_t cl;
322     struct log_if ops;
323     string_t ident;
324     int facility;
325     bool_t open;
326 };
327
328 static int msgclass_to_syslogpriority(uint32_t m)
329 {
330     switch (m) {
331     case M_DEBUG_CONFIG: return LOG_DEBUG;
332     case M_DEBUG_PHASE: return LOG_DEBUG;
333     case M_DEBUG: return LOG_DEBUG;
334     case M_INFO: return LOG_INFO;
335     case M_NOTICE: return LOG_NOTICE;
336     case M_WARNING: return LOG_WARNING;
337     case M_ERROR: return LOG_ERR;
338     case M_SECURITY: return LOG_CRIT;
339     case M_FATAL: return LOG_EMERG;
340     default: return LOG_NOTICE;
341     }
342 }
343     
344 static void syslog_vlog(void *sst, int class, char *message,
345                          va_list args)
346 {
347     struct syslog *st=sst;
348
349     if (st->open)
350         vsyslog(msgclass_to_syslogpriority(class),message,args);
351     else {
352         vMessage(class,message,args);
353         Message(class,"\n");
354     }
355 }
356
357 static void syslog_log(void *sst, int priority, char *message, ...)
358 {
359     va_list ap;
360
361     va_start(ap,message);
362     syslog_vlog(sst,priority,message,ap);
363     va_end(ap);
364 }
365
366 static struct flagstr syslog_facility_table[]={
367     { "authpriv", LOG_AUTHPRIV },
368     { "cron", LOG_CRON },
369     { "daemon", LOG_DAEMON },
370     { "kern", LOG_KERN },
371     { "local0", LOG_LOCAL0 },
372     { "local1", LOG_LOCAL1 },
373     { "local2", LOG_LOCAL2 },
374     { "local3", LOG_LOCAL3 },
375     { "local4", LOG_LOCAL4 },
376     { "local5", LOG_LOCAL5 },
377     { "local6", LOG_LOCAL6 },
378     { "local7", LOG_LOCAL7 },
379     { "lpr", LOG_LPR },
380     { "mail", LOG_MAIL },
381     { "news", LOG_NEWS },
382     { "syslog", LOG_SYSLOG },
383     { "user", LOG_USER },
384     { "uucp", LOG_UUCP },
385     { NULL, 0 }
386 };
387
388 static void syslog_phase_hook(void *sst, uint32_t newphase)
389 {
390     struct syslog *st=sst;
391
392     if (background) {
393         openlog(st->ident,0,st->facility);
394         st->open=True;
395     }
396 }
397
398 static list_t *syslog_apply(closure_t *self, struct cloc loc, dict_t *context,
399                             list_t *args)
400 {
401     struct syslog *st;
402     dict_t *d;
403     item_t *item;
404     string_t facstr;
405
406     st=safe_malloc(sizeof(*st),"syslog_apply");
407     st->cl.description="syslog";
408     st->cl.type=CL_LOG;
409     st->cl.apply=NULL;
410     st->cl.interface=&st->ops;
411     st->ops.st=st;
412     st->ops.log=syslog_log;
413     st->ops.vlog=syslog_vlog;
414
415     item=list_elem(args,0);
416     if (!item || item->type!=t_dict)
417         cfgfatal(loc,"syslog","parameter must be a dictionary\n");
418     d=item->data.dict;
419
420     st->ident=dict_read_string(d, "ident", False, "syslog", loc);
421     facstr=dict_read_string(d, "facility", True, "syslog", loc);
422     st->facility=string_to_word(facstr,loc,
423                                 syslog_facility_table,"syslog");
424     st->open=False;
425     add_hook(PHASE_GETRESOURCES,syslog_phase_hook,st);
426
427     return new_closure(&st->cl);
428 }    
429
430 init_module log_module;
431 void log_module(dict_t *dict)
432 {
433     add_closure(dict,"logfile",logfile_apply);
434     add_closure(dict,"syslog",syslog_apply);
435 }