]> mj.ucw.cz Git - libucw.git/blob - ucw/logstream.c
Logging: Fixed a typo.
[libucw.git] / ucw / logstream.c
1 #include "ucw/lib.h"
2 #include "ucw/clists.h"
3 #include "ucw/simple-lists.h"
4
5 #include <syslog.h>
6 #include <string.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <errno.h>
10 #include <unistd.h>
11 #include <sys/time.h>
12 #include <sys/stat.h>
13 #include <time.h>
14 #include <alloca.h>
15 #include <fcntl.h>
16 #include "ucw/logstream.h"
17
18 /* forward declaration */
19 static int ls_fdfile_handler(struct log_stream* ls, const char *m, u32 cat);
20
21 /* the deafult logger */
22 const struct log_stream ls_default_log={
23   .name = "fd2", .idata = 2, .pdata = NULL, .regnum = 0,
24   .handler = ls_fdfile_handler,
25   .levels = LS_ALL_LEVELS,
26   .msgfmt = LSFMT_DEFAULT,
27   // empty clist
28   .substreams.head.next = (cnode *) &ls_default_log.substreams.head,
29   .substreams.head.prev = (cnode *) &ls_default_log.substreams.head,
30 };
31
32 /* user de/allocated program/process name for use in the logsystem (for LSFMT_TITLE) */
33 char *ls_title = NULL;
34
35 /* Define an array (growing buffer) for pointers to log_streams. */
36 #define GBUF_TYPE struct log_stream*
37 #define GBUF_PREFIX(x) lsbuf_##x
38 #include "ucw/gbuf.h"
39
40 /* Flag indicating initialization of the module */
41 static int ls_initialized = 0;
42
43 /* The growing array of pointers to log_streams. */
44 static struct lsbuf_t ls_streams;
45
46 /* The head of the list of freed log_streams indexes in ls_streams.ptr (-1 if none free).
47  * Freed positions in ls_streams.ptr are connected into a linked list in the following way:
48  * ls_streams.ptr[ls_streams_free].idata is the index of next freed position (or -1) */
49 int ls_streams_free = -1;
50
51 /* the first never-used index in ls_streams.ptr */
52 int ls_streams_after = 0;
53
54 /* Initialize the logstream module.
55  * It is not neccessary to call this explicitely as it is called by
56  * the first ls_new()  (for backward compatibility and ease of use). */
57 static void ls_init_module(void)
58 {
59   unsigned int i;
60   if (ls_initialized) return;
61
62   /* create the grow array */
63   ls_streams.ptr = NULL;
64   ls_streams.len = 0;
65   lsbuf_set_size(&ls_streams, LS_INIT_STREAMS);
66
67   /* bzero */
68   memset(ls_streams.ptr, 0, sizeof(struct log_stream*) * (ls_streams.len));
69   ls_streams_free = -1;
70
71   ls_initialized = 1;
72
73   /* init the default stream (0) as forwarder to fd2 */
74   struct log_stream *ls = ls_new();
75   ASSERT(ls == ls_streams.ptr[0]);
76   ASSERT(ls->regnum == 0);
77   ls->name = "default";
78   ls_add_substream(ls, (struct log_stream *) &ls_default_log);
79
80   /* log this */
81   ls_msg(L_DEBUG, "logstream module initialized.");
82 }
83
84 /* close all open streams, un-initialize the module, free all memory,
85  * use only ls_default_log */
86 void ls_close_all(void)
87 {
88   int i;
89
90   if (!ls_initialized) return;
91
92   for (i=0; i<ls_streams_after; i++)
93   {
94     if (ls_streams.ptr[i]->regnum>=0)
95       ls_close(ls_streams.ptr[i]);
96     xfree(ls_streams.ptr[i]);
97   }
98
99   /* set to the default state */
100   lsbuf_done(&ls_streams);
101   ls_streams_after=0;
102   ls_streams_free=-1;
103   ls_initialized = 0;
104 }
105
106 /* add a new substream, malloc()-ate a new simp_node */
107 void ls_add_substream(struct log_stream *where, struct log_stream *what)
108 {
109   ASSERT(where);
110   ASSERT(what);
111
112   simp_node *n = xmalloc(sizeof(simp_node));
113   n->p = what;
114   clist_add_tail(&(where->substreams), (cnode*)n);
115 }
116
117 /* remove all occurences of a substream, free() the simp_node */
118 /* return number of deleted entries */
119 int ls_rm_substream(struct log_stream *where, struct log_stream *what)
120 {
121   void *tmp;
122   int cnt=0;
123   ASSERT(where);
124   ASSERT(what);
125
126   CLIST_FOR_EACH_DELSAFE(simp_node *, i, where->substreams, tmp)
127     if (i->p == what)
128     {
129       clist_remove((cnode*)i);
130       xfree(i);
131       cnt++;
132     }
133   return cnt;
134 }
135
136 /* Return a pointer to a new stream with no handler and an empty substream list. */
137 struct log_stream *ls_new(void)
138 {
139   struct log_stream *l;
140   int index;
141
142   /* initialize the array if not initialized already */
143   if (unlikely(ls_initialized==0))
144     ls_init_module();
145
146   /* there is no closed stream -- allocate a new one */
147   if (ls_streams_free==-1)
148   {
149     /* check the size of the pointer array */
150     lsbuf_grow(&ls_streams, ls_streams_after+1);
151     ls_streams_free = ls_streams_after++;
152     ls_streams.ptr[ls_streams_free] = xmalloc(sizeof(struct log_stream));
153     ls_streams.ptr[ls_streams_free]->idata = -1;
154     ls_streams.ptr[ls_streams_free]->regnum = -1;
155   }
156
157   ASSERT(ls_streams_free>=0);
158
159   /* initialize the stream */
160   index = ls_streams_free;
161   l = ls_streams.ptr[index];
162   ls_streams_free = l->idata;
163   memset(l, 0, sizeof(struct log_stream));
164   l->levels = LS_ALL_LEVELS;
165   l->regnum = LS_SET_STRNUM(index);
166   l->substreams.head.next = &(l->substreams.head);
167   l->substreams.head.prev = &(l->substreams.head);
168   return l;
169 }
170
171 /* Close and remember given log_stream */
172 /* does not affect substreams, but frees the .substreams list */
173 void ls_close(struct log_stream *ls)
174 {
175   void *tmp;
176   ASSERT(ls);
177
178   /* xfree() all the simp_nodes from substreams */
179   CLIST_FOR_EACH_DELSAFE(simp_node *, i, ls->substreams, tmp)
180   {
181     clist_remove((cnode*)i);
182     xfree(i);
183   }
184
185   /* close and remember the stream */
186   if (ls->close!=NULL)
187     ls->close(ls);
188   ls->idata = ls_streams_free;
189   ls_streams_free = LS_GET_STRNUM(ls->regnum);
190   ls->regnum = -1;
191 }
192
193 /* get a stream by its LS_SET_STRNUM() */
194 /* returns NULL for free/invalid numbers */
195 /* defaults to ls_default_stream when stream number 0 closed */
196 struct log_stream *ls_bynum(int num)
197 {
198   /* get the real number */
199   int n = LS_GET_STRNUM(num);
200   if ((n<0) || (n>=ls_streams_after) || (ls_streams.ptr[n]->regnum==-1) )
201   {
202     if (n==0)
203       return (struct log_stream *)&ls_default_log;
204     else return NULL;
205   }
206   return ls_streams.ptr[n];
207 }
208
209 /* The proposed alternative to original vmsg() */
210 void ls_vmsg(unsigned int cat, const char *fmt, va_list args)
211 {
212   struct timeval tv;
213   int have_tm = 0;
214   struct tm tm;
215   va_list args2;
216   char stime[24];
217   char sutime[12];
218   char *buf,*p;
219   int len;
220   int level=LS_GET_LEVEL(cat);
221   struct log_stream *ls=ls_bynum(cat);
222
223   /* Check the stream existence */
224   if(!ls)
225   {
226     ls_msg((LS_INTERNAL_MASK&cat)|L_WARN, "No log_stream with number %d! Logging to the default log.",
227       LS_GET_STRNUM(cat));
228     ls=(struct log_stream *)&ls_default_log;
229   }
230
231   /* get the time */
232   if (!(cat&LSFLAG_SIGHANDLER))
233   {
234     /* CAVEAT: These calls are not safe in signal handlers. */
235     gettimeofday(&tv, NULL);
236     if (localtime_r(&tv.tv_sec, &tm))
237       have_tm = 1;
238   }
239
240   /* generate time strings */
241   if (have_tm)
242   {
243     strftime(stime, 24, "%Y-%m-%d %H:%M:%S", &tm);
244     snprintf(sutime, 12, ".%06d", (int)tv.tv_usec);
245   }
246   else
247   {
248     snprintf(stime, 24, "\?\?\?\?-\?\?-\?\? \?\?:\?\?:\?\?");
249     snprintf(sutime, 12, ".\?\?\?\?\?\?");
250   }
251
252   /* generate the message string */
253   va_copy(args2, args);
254   /* WARN: this may be C99 specefic */
255   len = vsnprintf(NULL, 0, fmt, args2);
256   va_end(args2);
257   buf = xmalloc(len+2);
258   vsnprintf(buf, len+1, fmt, args);
259
260   /* remove non-printable characters and newlines */
261   p=buf;
262   while (*p)
263   {
264     if (*p < 0x20 && *p != '\t')
265       *p = 0x7f;
266     p++;
267   }
268
269   /* pass the message to the log_stream */
270   if(ls_passmsg(0, ls, stime, sutime, buf, cat))
271   {
272     /* error (such as looping) occured */
273     ls_passmsg(0, (struct log_stream *)&ls_default_log, stime, sutime, buf, cat);
274   }
275
276   xfree(buf);
277 }
278
279 /* The proposed alternative to original msg() */
280 void ls_msg(unsigned int cat, const char *fmt, ...)
281 {
282   va_list args;
283
284   va_start(args, fmt);
285   ls_vmsg(cat, fmt, args);
286   va_end(args);
287 }
288
289 /* The proposed alternative to original die() */
290 void ls_die(const char *fmt, ...)
291 {
292   va_list args;
293
294   va_start(args, fmt);
295   ls_vmsg(L_FATAL, fmt, args);
296   va_end(args);
297 ///// why this?
298 //  if (log_die_hook)
299 //    log_die_hook();
300 #ifdef DEBUG_DIE_BY_ABORT
301   abort();
302 #else
303   exit(1);
304 #endif
305 }
306
307 /* process a message (string) */
308 /* depth prevents undetected looping */
309 /* returns 1 in case of loop detection or other fatal error
310  *         0 otherwise */
311 int ls_passmsg(int depth, struct log_stream *ls, const char *stime, const char *sutime, const char *m, u32 cat)
312 {
313   ASSERT(ls);
314
315   /* Check recursion depth */
316   if( depth > LS_MAX_DEPTH )
317   {
318     ls_passmsg(0, (struct log_stream *)&ls_default_log, stime, sutime,
319       "Loop in the log_stream system detected.", L_ERROR | (cat&LS_INTERNAL_MASK) );
320     return 1;
321   }
322
323   /* Filter by level and filter hook */
324   if(!( (1<<LS_GET_LEVEL(cat)) & ls->levels )) return 0;
325   if( ls->filter )
326     if( ls->filter(ls, m, cat) != 0 ) return 0;
327
328   /* pass message to substreams */
329   CLIST_FOR_EACH(simp_node *, s, ls->substreams)
330   {
331     if (ls_passmsg(depth+1, (struct log_stream*)(s->p), stime, sutime, m, cat))
332       return 1;
333   }
334
335   /* Prepare for handler */
336   if(ls->handler)
337   {
338     int len = strlen(m) + strlen(stime) + strlen(sutime) + 32;
339       /* SHOULD be enough for all information, but beware */
340     if (ls_title)  len += strlen(ls_title);
341     if (ls->name)  len += strlen(ls->name);
342     char *buf=xmalloc(len);
343     char *p=buf;
344
345     /* Level (2 chars) */
346     if(ls->msgfmt & LSFMT_LEVEL)
347     {
348       *p++=LS_LEVEL_LETTER(LS_GET_LEVEL(cat));
349       *p++=' ';
350     }
351
352     /* Time (|stime| + |sutime| + 1 chars) */
353     if(ls->msgfmt & LSFMT_TIME)
354     {
355       char *q = (char *)stime;
356
357       while(*q)
358         *p++=*q++;
359       if(ls->msgfmt & LSFMT_USEC)
360       {
361         q = (char *)sutime;
362         while(*q)
363           *p++=*q++;
364       }
365       *p++=' ';
366     }
367
368     /* process name, PID ( |ls_title| + 6 + (|PID|<=10) chars ) */
369     if((ls->msgfmt & LSFMT_TITLE) && ls_title)
370     {
371       if(ls->msgfmt & LSFMT_PID)
372         p += sprintf(p, "[%s (%d)] ", ls_title, getpid());
373       else
374         p += sprintf(p, "[%s] ", ls_title);
375     }
376     else
377     {
378       if(ls->msgfmt & LSFMT_PID)
379         p += sprintf(p, "[%d] ", getpid());
380     }
381
382     /* log_stream name ( |ls->name| + 4 chars ) */
383     if(ls->msgfmt & LSFMT_LOGNAME)
384     {
385       if(ls->name)
386         p += sprintf(p, "<%s> ", ls->name);
387       else
388         p += sprintf(p, "<?> ");
389     }
390
391     /* finish the string and call the handler ( |m| + 1 chars ) */
392     {
393       char *q = (char *)m;
394
395       while(*q)
396         *p++=*q++;
397       *p++ = '\n';
398       *p++ = '\0';
399       ls->handler(ls, buf, cat);
400     }
401     xfree(buf);
402   }
403   return 0;
404 }
405
406
407 /********** log types */
408
409
410 /**** standard (filedes) files */
411
412 /* destructor for standard files */
413 static void ls_fdfile_close(struct log_stream *ls)
414 {
415   ASSERT(ls);
416   close(ls->idata);
417   if(ls->name)
418     xfree(ls->name);
419 }
420
421 /* handler for standard files */
422 static int ls_fdfile_handler(struct log_stream* ls, const char *m, u32 cat)
423 {
424   int len = strlen(m);
425   int r = write(ls->idata, m, len);
426   /* TODO: check the errors here? */
427   if (r!=len)
428     return errno;
429   return 0;
430 }
431
432 /* assign log to a file descriptor */
433 /* initialize with the default formatting, does NOT close the descriptor */
434 struct log_stream *ls_fdfile_new(int fd)
435 {
436   struct log_stream *ls=ls_new();
437   ls->idata=fd;
438   ls->msgfmt=LSFMT_DEFAULT;
439   ls->handler=ls_fdfile_handler;
440   return ls;
441 }
442
443 /* open() a file (append mode) */
444 /* initialize with the default formatting */
445 struct log_stream *ls_file_new(const char *path)
446 {
447   struct log_stream *ls;
448   int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0666);
449   if (fd<0)
450   {
451     ls_msg(L_ERROR, "Opening logfile '%s' failed: %m.", path);
452     return NULL;
453   }
454   ls = ls_new();
455   ls->name = xstrdup(path);
456   ls->idata = fd;
457   ls->msgfmt = LSFMT_DEFAULT;
458   ls->handler = ls_fdfile_handler;
459   ls->close = ls_fdfile_close;
460   return ls;
461 }
462
463
464 /**** syslog */
465
466 /* destructor for syslog logs */
467 static void ls_syslog_close(struct log_stream *ls)
468 {
469   ASSERT(ls);
470   if(ls->name)
471     xfree(ls->name);
472 }
473
474 /* convert severity level to syslog constants */
475 static int ls_syslog_convert_level(int level)
476 {
477   switch(level)
478   {
479     case L_DEBUG: return LOG_DEBUG;
480     case L_INFO: return LOG_INFO;
481     case L_INFO_R: return LOG_INFO;
482     case L_WARN: return LOG_WARNING;
483     case L_WARN_R: return LOG_WARNING;
484     case L_ERROR: return LOG_ERR;
485     case L_ERROR_R: return LOG_ERR;
486     case L_FATAL: return LOG_CRIT;
487     default: return LOG_NOTICE;
488   }
489 }
490
491 /* simple syslog write handler */
492 static int ls_syslog_handler(struct log_stream *ls, const char *m, u32 flags)
493 {
494   int prio;
495   ASSERT(ls);
496   ASSERT(m);
497
498   prio = ls_syslog_convert_level(LS_GET_LEVEL(flags)) | (ls->idata);
499   if (ls->name)
500     syslog(prio | (ls->idata), "%s: %s", ls->name, m);
501   else
502     syslog(prio | (ls->idata), "%s", m);
503   return 0;
504 }
505
506 /* assign log to a syslog facility */
507 /* initialize with no formatting (syslog adds these inforamtion) */
508 /* name is optional prefix (NULL for none) */
509 struct log_stream *ls_syslog_new(int facility, const char *name)
510 {
511   struct log_stream *ls=ls_new();
512   if (name) ls->name = xstrdup(name);
513   ls->idata = facility;
514   ls->msgfmt = LSFMT_NONE;
515   ls->handler = ls_syslog_handler;
516   ls->close = ls_syslog_close;
517   return ls;
518 }