2 #include "ucw/clists.h"
3 #include "ucw/simple-lists.h"
16 #include "ucw/logstream.h"
18 /* forward declaration */
19 static int ls_fdfile_handler(struct log_stream* ls, const char *m, u32 cat);
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,
28 .substreams.head.next = (cnode *) &ls_default_log.substreams.head,
29 .substreams.head.prev = (cnode *) &ls_default_log.substreams.head,
32 /* user de/allocated program/process name for use in the logsystem (for LSFMT_TITLE) */
33 char *ls_title = NULL;
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
40 /* Flag indicating initialization of the module */
41 static int ls_initialized = 0;
43 /* The growing array of pointers to log_streams. */
44 static struct lsbuf_t ls_streams;
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;
51 /* the first never-used index in ls_streams.ptr */
52 int ls_streams_after = 0;
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)
60 if (ls_initialized) return;
62 /* create the grow array */
63 ls_streams.ptr = NULL;
65 lsbuf_set_size(&ls_streams, LS_INIT_STREAMS);
68 memset(ls_streams.ptr, 0, sizeof(struct log_stream*) * (ls_streams.len));
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);
78 ls_add_substream(ls, (struct log_stream *) &ls_default_log);
81 ls_msg(L_DEBUG, "logstream module initialized.");
84 /* close all open streams, un-initialize the module, free all memory,
85 * use only ls_default_log */
86 void ls_close_all(void)
90 if (!ls_initialized) return;
92 for (i=0; i<ls_streams_after; i++)
94 if (ls_streams.ptr[i]->regnum>=0)
95 ls_close(ls_streams.ptr[i]);
96 xfree(ls_streams.ptr[i]);
99 /* set to the default state */
100 lsbuf_done(&ls_streams);
106 /* add a new substream, malloc()-ate a new simp_node */
107 void ls_add_substream(struct log_stream *where, struct log_stream *what)
112 simp_node *n = xmalloc(sizeof(simp_node));
114 clist_add_tail(&(where->substreams), (cnode*)n);
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)
126 CLIST_FOR_EACH_DELSAFE(simp_node *, i, where->substreams, tmp)
129 clist_remove((cnode*)i);
136 /* Return a pointer to a new stream with no handler and an empty substream list. */
137 struct log_stream *ls_new(void)
139 struct log_stream *l;
142 /* initialize the array if not initialized already */
143 if (unlikely(ls_initialized==0))
146 /* there is no closed stream -- allocate a new one */
147 if (ls_streams_free==-1)
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;
157 ASSERT(ls_streams_free>=0);
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);
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)
178 /* xfree() all the simp_nodes from substreams */
179 CLIST_FOR_EACH_DELSAFE(simp_node *, i, ls->substreams, tmp)
181 clist_remove((cnode*)i);
185 /* close and remember the stream */
188 ls->idata = ls_streams_free;
189 ls_streams_free = LS_GET_STRNUM(ls->regnum);
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)
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) )
203 return (struct log_stream *)&ls_default_log;
206 return ls_streams.ptr[n];
209 /* The proposed alternative to original vmsg() */
210 void ls_vmsg(unsigned int cat, const char *fmt, va_list args)
220 int level=LS_GET_LEVEL(cat);
221 struct log_stream *ls=ls_bynum(cat);
223 /* Check the stream existence */
226 ls_msg((LS_INTERNAL_MASK&cat)|L_WARN, "No log_stream with number %d! Logging to the default log.",
228 ls=(struct log_stream *)&ls_default_log;
232 if (!(cat&LSFLAG_SIGHANDLER))
234 /* CAVEAT: These calls are not safe in signal handlers. */
235 gettimeofday(&tv, NULL);
236 if (localtime_r(&tv.tv_sec, &tm))
240 /* generate time strings */
243 strftime(stime, 24, "%Y-%m-%d %H:%M:%S", &tm);
244 snprintf(sutime, 12, ".%06d", (int)tv.tv_usec);
248 snprintf(stime, 24, "\?\?\?\?-\?\?-\?\? \?\?:\?\?:\?\?");
249 snprintf(sutime, 12, ".\?\?\?\?\?\?");
252 /* generate the message string */
253 va_copy(args2, args);
254 /* WARN: this may be C99 specefic */
255 len = vsnprintf(NULL, 0, fmt, args2);
257 buf = xmalloc(len+2);
258 vsnprintf(buf, len+1, fmt, args);
260 /* remove non-printable characters and newlines */
264 if (*p < 0x20 && *p != '\t')
269 /* pass the message to the log_stream */
270 if(ls_passmsg(0, ls, stime, sutime, buf, cat))
272 /* error (such as looping) occured */
273 ls_passmsg(0, (struct log_stream *)&ls_default_log, stime, sutime, buf, cat);
279 /* The proposed alternative to original msg() */
280 void ls_msg(unsigned int cat, const char *fmt, ...)
285 ls_vmsg(cat, fmt, args);
289 /* The proposed alternative to original die() */
290 void ls_die(const char *fmt, ...)
295 ls_vmsg(L_FATAL, fmt, args);
300 #ifdef DEBUG_DIE_BY_ABORT
307 /* process a message (string) */
308 /* depth prevents undetected looping */
309 /* returns 1 in case of loop detection or other fatal error
311 int ls_passmsg(int depth, struct log_stream *ls, const char *stime, const char *sutime, const char *m, u32 cat)
315 /* Check recursion depth */
316 if( depth > LS_MAX_DEPTH )
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) );
323 /* Filter by level and filter hook */
324 if(!( (1<<LS_GET_LEVEL(cat)) & ls->levels )) return 0;
326 if( ls->filter(ls, m, cat) != 0 ) return 0;
328 /* pass message to substreams */
329 CLIST_FOR_EACH(simp_node *, s, ls->substreams)
331 if (ls_passmsg(depth+1, (struct log_stream*)(s->p), stime, sutime, m, cat))
335 /* Prepare for handler */
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);
345 /* Level (2 chars) */
346 if(ls->msgfmt & LSFMT_LEVEL)
348 *p++=LS_LEVEL_LETTER(LS_GET_LEVEL(cat));
352 /* Time (|stime| + |sutime| + 1 chars) */
353 if(ls->msgfmt & LSFMT_TIME)
355 char *q = (char *)stime;
359 if(ls->msgfmt & LSFMT_USEC)
368 /* process name, PID ( |ls_title| + 6 + (|PID|<=10) chars ) */
369 if((ls->msgfmt & LSFMT_TITLE) && ls_title)
371 if(ls->msgfmt & LSFMT_PID)
372 p += sprintf(p, "[%s (%d)] ", ls_title, getpid());
374 p += sprintf(p, "[%s] ", ls_title);
378 if(ls->msgfmt & LSFMT_PID)
379 p += sprintf(p, "[%d] ", getpid());
382 /* log_stream name ( |ls->name| + 4 chars ) */
383 if(ls->msgfmt & LSFMT_LOGNAME)
386 p += sprintf(p, "<%s> ", ls->name);
388 p += sprintf(p, "<?> ");
391 /* finish the string and call the handler ( |m| + 1 chars ) */
399 ls->handler(ls, buf, cat);
407 /********** log types */
410 /**** standard (filedes) files */
412 /* destructor for standard files */
413 static void ls_fdfile_close(struct log_stream *ls)
421 /* handler for standard files */
422 static int ls_fdfile_handler(struct log_stream* ls, const char *m, u32 cat)
425 int r = write(ls->idata, m, len);
426 /* TODO: check the errors here? */
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)
436 struct log_stream *ls=ls_new();
438 ls->msgfmt=LSFMT_DEFAULT;
439 ls->handler=ls_fdfile_handler;
443 /* open() a file (append mode) */
444 /* initialize with the default formatting */
445 struct log_stream *ls_file_new(const char *path)
447 struct log_stream *ls;
448 int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0666);
451 ls_msg(L_ERROR, "Opening logfile '%s' failed: %m.", path);
455 ls->name = xstrdup(path);
457 ls->msgfmt = LSFMT_DEFAULT;
458 ls->handler = ls_fdfile_handler;
459 ls->close = ls_fdfile_close;
466 /* destructor for syslog logs */
467 static void ls_syslog_close(struct log_stream *ls)
474 /* convert severity level to syslog constants */
475 static int ls_syslog_convert_level(int level)
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;
491 /* simple syslog write handler */
492 static int ls_syslog_handler(struct log_stream *ls, const char *m, u32 flags)
498 prio = ls_syslog_convert_level(LS_GET_LEVEL(flags)) | (ls->idata);
500 syslog(prio | (ls->idata), "%s: %s", ls->name, m);
502 syslog(prio | (ls->idata), "%s", m);
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)
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;