1 /*
2 **  OSSP l2 - Flexible Logging
3 **  Copyright (c) 2001-2005 Cable & Wireless <http://www.cw.com/>
4 **  Copyright (c) 2001-2005 The OSSP Project <http://www.ossp.org/>
5 **  Copyright (c) 2001-2005 Ralf S. Engelschall <rse@engelschall.com>
6 **
7 **  This file is part of OSSP l2, a flexible logging library which
8 **  can be found at http://www.ossp.org/pkg/lib/l2/.
9 **
10 **  Permission to use, copy, modify, and distribute this software for
11 **  any purpose with or without fee is hereby granted, provided that
12 **  the above copyright notice and this permission notice appear in all
13 **  copies.
14 **
15 **  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
16 **  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 **  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 **  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
19 **  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 **  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 **  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
22 **  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 **  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 **  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 **  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 **  SUCH DAMAGE.
27 **
28 **  l2_channel.c: channel object
29 **
30 */
31 
32 #include "l2_p.h"
33 
34 /*
35  * A channel is the central object for a logging stream. It is
36  * implemented by a framework (the code implemented here) which provides
37  * the channel API and which is equal for all channels and a particular
38  * handler which implements the characteristics specific to a particular
39  * channel class. The lifecycle of a channel is illustrated in the
40  * following figure:
41  *
42  * -----BEGIN EMBEDDED OBJECT-----
43  * Content-editor: xfig %s
44  * Content-encoding: gzip:9 base64
45  * Content-type: application/fig
46  * Content-viewer: xfig %s
47  * Description: L2 Channel Lifecycle
48  * Filename: l2_ch_lifecycle.fig
49  * Last-modified: 2000-09-28/14:40
50  * Name: l2_ch_lifecycle
51  * Version: eo/1.0
52  * H4sIAGdztDsCA62WTW/bMAyGz/WvENBzM5KyPnwusGFAdtp1l8BVWgOZUyTuhv77
53  * kfKHnC4t5CZJ7JAO3kcUKUq5/fr9m9IrKtab9uFYb55DcR/aLhyKH6E7NHWxDh17
54  * ShUIsAIofjbt4y4Ud1QgASgqQGlSt3V8Fai0AoV8gTJyI76xLD6Tb35iPSpybNlS
55  * PsknV5po5WC0BZZRWSlL8km+tlYsa3MwJfAP6Kdokl8iltHKwjiwJ5jJL52DbIxB
56  * Z+aY5BvSVT7GEieSzISZfGN9Fa0cjAVj55iZz7XPxxg6mdTMNz5/Ug55ncwwyXdU
57  * mnyMPS148p1bUHCPAPPcJN+jLOrM3GjUOMckX2PV16ygiJDL9Zi7Qa4GR36i4kYM
58  * XMW6yZ1LJP160y+iofqUy4SeKW01zCtaMT2XBjSRUrdFJr1h0rmAZg3qhnV0cUAT
59  * KTVcfoYsjOmN1jLxbPWJdZV6T2GkTstPb2pOh3ilek+kNN3LmWO6UuflZB0/YvYk
60  * B+eYXpi4PM4YYs80g3XK/Gh1JHG0UC/p3OFIhRkhiuXNJ3aaDr0/tKExbraWqtFX
61  * g1qsiyue8qDxWq0ykdI+l5/g2eE87rCX79XGj8cK6CGgMh0gyH8qYPrPoTCeGzx0
62  * Jft6yUHU+3bbPL4cwi8AzFAazU+XKXlJlHL6O64uec3KQ9h0OQPK1uJRZNob9RCO
63  * 3WH/mjGc5kC1HXX759Bmiixas0jkZHIgBy9wrH8PTRe+bHcvx6dMrTUOPqOVYA1x
64  * sFhJQnf7Y8hUOVfCOZV/v3h+3N88W7tmG7rm9ztCXPGE/Gw4IuAgLUd67M4V3f8/
65  * nPS/M9IprN/UXfMnR2b8MA4Rl6Np3wh9EtJM6OWRlkGrfrtUa1L3T5u2DTu15qnW
66  * r/Wup/wDvlAM/PkMAAA=
67  * -----END EMBEDDED OBJECT-----
68  */
69 
70 /* create channel */
l2_channel_create(l2_channel_t ** chp,l2_env_t * env,const char * name)71 l2_result_t l2_channel_create(l2_channel_t **chp, l2_env_t *env, const char *name)
72 {
73     l2_channel_t *ch;
74     l2_handler_t *h;
75     int i;
76 
77     /* argument sanity check */
78     if (env == NULL || name == NULL)
79         return L2_ERR_ARG;
80 
81     /* lookup channel handler */
82     h = NULL;
83     for (i = 0; i < L2_MAX_HANDLERS && env->handlers[i] != NULL; i++) {
84         if (strcmp(env->handlers[i]->name, name) == 0) {
85             h = env->handlers[i];
86             break;
87         }
88     }
89     if (h == NULL)
90         return L2_ERR_CH;
91 
92     /* allocate channel structure */
93     if ((ch = (l2_channel_t *)malloc(sizeof(l2_channel_t))) == NULL)
94         return L2_ERR_SYS;
95 
96     /* initialize channel structure */
97     ch->env = env;
98     ch->state = L2_CHSTATE_CREATED;
99     ch->parent = NULL;
100     ch->sibling = NULL;
101     ch->child = NULL;
102     memset(&ch->context, 0, sizeof(l2_context_t));
103     memcpy(&ch->handler, h, sizeof(l2_handler_t));
104     ch->levelmask = env->levelmask;
105     ch->flushmask = env->flushmask;
106 
107     /* (optionally) perform create operation in handler */
108     if (ch->handler.create != NULL) {
109         if (ch->handler.create(&ch->context, ch) != L2_OK) {
110             free(ch);
111             return L2_ERR_SYS;
112         }
113     }
114 
115     /* pass object to caller */
116     (*chp) = ch;
117 
118     return L2_OK;
119 }
120 
121 /* link channels */
l2_channel_link(l2_channel_t * ch0,l2_link_t id,l2_channel_t * ch,...)122 l2_result_t l2_channel_link(l2_channel_t *ch0, l2_link_t id, l2_channel_t *ch, ...)
123 {
124     l2_channel_t *chT;
125     l2_channel_t *chN;
126     va_list ap;
127 
128     /* argument sanity check */
129     if (ch0 == NULL || ch == NULL)
130         return L2_ERR_ARG;
131 
132     /* perform either child or sibling linking operation(s) */
133     if (id == L2_LINK_CHILD) {
134         /* make sure child parents are filters only */
135         if (ch0->handler.type != L2_CHANNEL_FILTER)
136             return L2_ERR_USE;
137         va_start(ap, ch);
138         chT = ch;
139         do {
140             chN = (l2_channel_t *)va_arg(ap, l2_channel_t *);
141             if (chN != NULL && chT->handler.type != L2_CHANNEL_FILTER)
142                 return L2_ERR_USE;
143         } while ((chT = chN) != NULL);
144         va_end(ap);
145 
146         /* perform link operation(s) */
147         va_start(ap, ch);
148         do {
149             ch->parent  = ch0;
150             if (ch0->child == NULL)
151                 ch0->child = ch;
152             else {
153                 chT = ch0->child;
154                 while (chT->sibling != NULL)
155                     chT = chT->sibling;
156                 chT->sibling = ch;
157             }
158             ch0 = ch;
159             ch = (l2_channel_t *)va_arg(ap, l2_channel_t *);
160         } while (ch != NULL);
161         va_end(ap);
162     }
163     else if (id == L2_LINK_SIBLING) {
164         /* perform link operation(s) */
165         va_start(ap, ch);
166         do {
167             ch0->sibling = ch;
168             ch->parent   = ch0->parent;
169             ch0 = ch;
170             ch = (l2_channel_t *)va_arg(ap, l2_channel_t *);
171         } while (ch != NULL);
172         va_end(ap);
173     }
174 
175     return L2_OK;
176 }
177 
178 /* unlink channels */
l2_channel_unlink(l2_channel_t * ch)179 l2_result_t l2_channel_unlink(l2_channel_t *ch)
180 {
181     l2_channel_t *chS;
182     l2_channel_t *chP;
183 
184     /* argument sanity check */
185     if (ch == NULL)
186         return L2_ERR_ARG;
187 
188     /* make sure channel is in state "created" */
189     if (ch->state != L2_CHSTATE_CREATED)
190         return L2_ERR_USE;
191 
192     /* make sure channel has no childs */
193     if (ch->child != NULL)
194         return L2_ERR_USE;
195 
196     /* unlink the channel */
197     chP = ch->parent;
198     ch->parent = NULL;
199     if (chP != NULL) {
200         if (chP->child == ch)
201             chP->child = ch->sibling;
202         else {
203             chS = chP->child;
204             while (chS->sibling != ch)
205                 chS = chS->sibling;
206             chS->sibling = ch->sibling;
207         }
208     }
209 
210     return L2_OK;
211 }
212 
213 /* return upstream channel */
l2_channel_upstream(l2_channel_t * ch,l2_channel_t ** chU)214 l2_result_t l2_channel_upstream(l2_channel_t *ch, l2_channel_t **chU)
215 {
216     /* argument sanity check */
217     if (ch == NULL || chU == NULL)
218         return L2_ERR_ARG;
219 
220     /* determine parent/upstream channel */
221     *chU = ch->parent;
222 
223     return (*chU != NULL ? L2_OK : L2_ERR_CH);
224 }
225 
226 /* return (subsequent) downstream channel(s) */
l2_channel_downstream(l2_channel_t * ch,l2_channel_t ** chD)227 l2_result_t l2_channel_downstream(l2_channel_t *ch, l2_channel_t **chD)
228 {
229     /* argument sanity check */
230     if (ch == NULL || chD == NULL)
231         return L2_ERR_ARG;
232 
233     /* determine (next) downstream/child channel */
234     if (*chD == NULL)
235         *chD = ch->child;
236     else
237         *chD = (*chD)->sibling;
238 
239     return (*chD != NULL ? L2_OK : L2_ERR_CH);
240 }
241 
242 /* return channel type */
l2_channel_type(l2_channel_t * ch,l2_chtype_t * type)243 l2_result_t l2_channel_type(l2_channel_t *ch, l2_chtype_t *type)
244 {
245     /* argument sanity check */
246     if (ch == NULL || type == NULL)
247         return L2_ERR_ARG;
248 
249     /* return type */
250     (*type) = ch->handler.type;
251 
252     return L2_OK;
253 }
254 
255 /* set channel level masks */
l2_channel_levels(l2_channel_t * ch,unsigned int levelmask,unsigned int flushmask)256 l2_result_t l2_channel_levels(l2_channel_t *ch, unsigned int levelmask, unsigned int flushmask)
257 {
258     /* argument sanity check */
259     if (ch == NULL)
260         return L2_ERR_ARG;
261 
262     /* override global level mask */
263     ch->levelmask = levelmask;
264     ch->flushmask = flushmask;
265 
266     return L2_OK;
267 }
268 
269 /* configure channel */
l2_channel_configure(l2_channel_t * ch,const char * fmt,...)270 l2_result_t l2_channel_configure(l2_channel_t *ch, const char *fmt, ...)
271 {
272     l2_result_t rv;
273     va_list ap;
274 
275     /* argument sanity check */
276     if (ch == NULL || fmt == NULL)
277         return L2_ERR_ARG;
278 
279     /* make sure the channel is in state "created" */
280     if (ch->state != L2_CHSTATE_CREATED)
281         return L2_ERR_USE;
282 
283     /* pass operation to handler */
284     rv = L2_OK;
285     va_start(ap, fmt);
286     if (ch->handler.configure != NULL)
287         rv = ch->handler.configure(&ch->context, ch, fmt, &ap);
288     va_end(ap);
289 
290     return rv;
291 }
292 
293 /* open channel */
l2_channel_open(l2_channel_t * ch)294 l2_result_t l2_channel_open(l2_channel_t *ch)
295 {
296     l2_result_t rv;
297     l2_result_t rvD;
298     l2_channel_t *chD;
299 
300     /* argument sanity check */
301     if (ch == NULL)
302         return L2_ERR_ARG;
303 
304     /* make sure channel is in state "created" */
305     if (ch->state != L2_CHSTATE_CREATED)
306         return L2_ERR_USE;
307 
308     /* perform operation */
309     if (ch->handler.open != NULL)
310         rv = ch->handler.open(&ch->context, ch);
311     else
312         rv = L2_OK_PASS;
313 
314     /* optionally pass operation downstream */
315     if (rv == L2_OK_PASS) {
316         rv = L2_OK;
317         chD = NULL;
318         while (l2_channel_downstream(ch, &chD) == L2_OK)
319             if ((rvD = l2_channel_open(chD)) != L2_OK)
320                 rv = rvD;
321         if (rv != L2_OK) {
322             chD = NULL;
323             while (l2_channel_downstream(ch, &chD) == L2_OK)
324                 l2_channel_close(chD);
325         }
326     }
327 
328     /* mark channel as opened */
329     if (rv == L2_OK)
330         ch->state = L2_CHSTATE_OPENED;
331 
332     return rv;
333 }
334 
335 /* write to channel */
l2_channel_write(l2_channel_t * ch,l2_level_t level,const char * buf,size_t bufsize)336 l2_result_t l2_channel_write(l2_channel_t *ch, l2_level_t level, const char *buf, size_t bufsize)
337 {
338     int l, j;
339     l2_result_t rv;
340     l2_result_t rvD;
341     l2_channel_t *chD;
342 
343     /* argument sanity check */
344     if (ch == NULL || level == 0 || buf == NULL)
345         return L2_ERR_ARG;
346 
347     /* make sure channel is in state "opened" */
348     if (ch->state != L2_CHSTATE_OPENED)
349         return L2_ERR_USE;
350 
351     /* make sure only a single level is specified */
352     for (l = level, j = 0; l != 0; l = (l >> 1))
353         if (l & 0x1)
354             j++;
355     if (j != 1)
356         return L2_ERR_ARG;
357 
358     /* check whether level mask already stops processing */
359     if (!(ch->levelmask & level))
360         return L2_OK;
361 
362     /* short circuiting */
363     if (bufsize == 0)
364         return L2_OK;
365 
366     /* perform operation */
367     if (ch->handler.write != NULL)
368         rv = ch->handler.write(&ch->context, ch, level, buf, bufsize);
369     else
370         rv = L2_OK_PASS;
371 
372     /* optionally pass operation downstream */
373     if (rv == L2_OK_PASS) {
374         rv = L2_OK;
375         chD = NULL;
376         while (l2_channel_downstream(ch, &chD) == L2_OK)
377             if ((rvD = l2_channel_write(chD, level, buf, bufsize)) != L2_OK)
378                 rv = rvD;
379     }
380 
381     return rv;
382 }
383 
384 /* flush channel (stack) */
l2_channel_flush(l2_channel_t * ch)385 l2_result_t l2_channel_flush(l2_channel_t *ch)
386 {
387     l2_result_t rv;
388     l2_result_t rvD;
389     l2_channel_t *chD;
390 
391     /* argument sanity check */
392     if (ch == NULL)
393         return L2_ERR_ARG;
394 
395     /* make sure channel is in state "opened" */
396     if (ch->state != L2_CHSTATE_OPENED)
397         return L2_ERR_USE;
398 
399     /* perform operation */
400     if (ch->handler.flush != NULL)
401         rv = ch->handler.flush(&ch->context, ch);
402     else
403         rv = L2_OK_PASS;
404 
405     /* optionally pass operation downstream */
406     if (rv == L2_OK_PASS) {
407         rv = L2_OK;
408         chD = NULL;
409         while (l2_channel_downstream(ch, &chD) == L2_OK)
410             if ((rvD = l2_channel_flush(chD)) != L2_OK)
411                 rv = rvD;
412     }
413 
414     return rv;
415 }
416 
417 /* close channel (stack) */
l2_channel_close(l2_channel_t * ch)418 l2_result_t l2_channel_close(l2_channel_t *ch)
419 {
420     l2_result_t rv;
421     l2_result_t rvD;
422     l2_channel_t *chD;
423 
424     /* argument sanity check */
425     if (ch == NULL)
426         return L2_ERR_ARG;
427 
428     /* make sure channel is in state "opened" */
429     if (ch->state != L2_CHSTATE_OPENED)
430         return L2_ERR_USE;
431 
432     /* perform operation */
433     if (ch->handler.close != NULL)
434         rv = ch->handler.close(&ch->context, ch);
435     else
436         rv = L2_OK_PASS;
437 
438     /* optionally pass operation downstream */
439     if (rv == L2_OK_PASS) {
440         rv = L2_OK;
441         chD = NULL;
442         while (l2_channel_downstream(ch, &chD) == L2_OK)
443             if ((rvD = l2_channel_close(chD)) != L2_OK)
444                 rv = rvD;
445     }
446 
447     /* mark channel as closed */
448     if (rv == L2_OK)
449         ch->state = L2_CHSTATE_CREATED;
450 
451     return rv;
452 }
453 
454 /* destroy channel */
l2_channel_destroy(l2_channel_t * ch)455 l2_result_t l2_channel_destroy(l2_channel_t *ch)
456 {
457     l2_result_t rv;
458     l2_result_t rvD;   /* downstream */
459     l2_channel_t *chD;
460     l2_result_t rvL;   /* lookahead */
461     l2_channel_t *chL;
462 
463     /* argument sanity check */
464     if (ch == NULL)
465         return L2_ERR_ARG;
466 
467     /* make sure channel is in state "opened" */
468     if (ch->state == L2_CHSTATE_OPENED)
469         if ((rv = l2_channel_close(ch)) != L2_OK)
470             return rv;
471 
472     /* perform operation */
473     if (ch->handler.destroy != NULL)
474         rv = ch->handler.destroy(&ch->context, ch);
475     else
476         rv = L2_OK_PASS;
477 
478     /* optionally pass operation downstream */
479     if (rv == L2_OK_PASS) {
480         rv = L2_OK;
481         chD = NULL;
482         if (l2_channel_downstream(ch, &chD) == L2_OK) {
483             chL = chD;
484             do {
485                 rvL = l2_channel_downstream(ch, &chL);
486                 if ((rvD = l2_channel_destroy(chD)) != L2_OK)
487                     rv = rvD;
488                 if (rvL == L2_OK)
489                     chD = chL;
490             } while ((rv == L2_OK) && (rvL == L2_OK));
491         }
492     }
493 
494     /* free channel structure */
495     if (rv == L2_OK)
496         free(ch);
497 
498     return rv;
499 }
500 
501 /* log a message to channel */
l2_channel_log(l2_channel_t * ch,l2_level_t level,const char * fmt,...)502 l2_result_t l2_channel_log(l2_channel_t *ch, l2_level_t level, const char *fmt, ...)
503 {
504     va_list ap;
505     l2_result_t rv;
506 
507     /* pass-through to va_list-based variant */
508     va_start(ap, fmt);
509     rv = l2_channel_vlog(ch, level, fmt, &ap);
510     va_end(ap);
511 
512     return rv;
513 }
514 
515 /* indirect callback function from l2_channel_vlog for flushing */
l2_channel_vlog_flush(l2_util_format_t * vfmt)516 static int l2_channel_vlog_flush(l2_util_format_t *vfmt)
517 {
518     /* we do no format buffer flushing */
519     return -1;
520 }
521 
522 /* indirect callback function from l2_channel_vlog for formatting */
l2_channel_vlog_format(l2_util_format_t * vfmt,char * cPrefix,char * cPad,char ** cppOut,size_t * npOutLen,char * cpBuf,int nBufLenMax,char * cpParam,char cId,va_list * apArgs)523 static void l2_channel_vlog_format(
524     l2_util_format_t *vfmt,
525     char *cPrefix, char *cPad, char **cppOut, size_t *npOutLen,
526     char *cpBuf, int nBufLenMax, char *cpParam, char cId, va_list *apArgs)
527 {
528     l2_env_t *env = (l2_env_t *)(vfmt->data[0].vp);
529     l2_result_t rv;
530     int i;
531 
532     /* init formatting result */
533     *cPrefix = '\0';
534     *cPad = ' ';
535     *cppOut = NULL;
536     *npOutLen = 0;
537 
538     /* iterate over all configured L2 formatters */
539     for (i = 0; i < L2_MAX_FORMATTERS && env->formatters[i].cb != NULL; i++) {
540         if (env->formatters[i].id == cId) {
541             rv = env->formatters[i].cb(env->formatters[i].ctx, cId, cpParam,
542                                        cpBuf, nBufLenMax, npOutLen, apArgs);
543             vfmt->data[1].i = (int)rv;
544             if (rv == L2_OK) {
545                 *cppOut = cpBuf;
546                 break;
547             }
548         }
549     }
550     return;
551 }
552 
553 /* log a message to channel (va_list-variant) */
l2_channel_vlog(l2_channel_t * ch,l2_level_t level,const char * fmt,va_list * ap)554 l2_result_t l2_channel_vlog(l2_channel_t *ch, l2_level_t level, const char *fmt, va_list *ap)
555 {
556     int l, j;
557     size_t len;
558     l2_result_t rv;
559     l2_util_format_t vfmt;
560     l2_env_t *env;
561 
562     /* argument sanity check */
563     if (ch == NULL || level == 0 || fmt == NULL)
564         return L2_ERR_ARG;
565 
566     /* make sure only a single level is specified */
567     for (l = level, j = 0; l != 0; l = (l >> 1))
568         if (l & 0x1)
569             j++;
570     if (j != 1)
571         return L2_ERR_ARG;
572 
573     /* check whether level mask already stops processing */
574     if (!(ch->levelmask & level))
575         return L2_OK;
576 
577     /* format message */
578     env = ch->env;
579     vfmt.curpos = env->message;
580     vfmt.endpos = env->message + L2_MAX_MSGSIZE;
581     vfmt.data[0].vp = env;
582     vfmt.data[1].i  = L2_ERR_FMT;
583     vfmt.flush  = l2_channel_vlog_flush;
584     vfmt.format = l2_channel_vlog_format;
585     len = l2_util_format(&vfmt, fmt, ap);
586 
587     /* check for formatting error including buffer overrun */
588     if (len == -1)
589         return (l2_result_t)(vfmt.data[1].i);
590 
591     /* check for formatting led to completely empty message */
592     if (len == 0)
593         return L2_ERR_FMT;
594 
595     /* check for formatting led to newline-only message */
596     if (len == 1 && env->message[len] == '\n')
597         return L2_ERR_FMT;
598 
599     /* make sure a trailing newline exists; L2_MSG_BUFSIZE has room for CR/LF */
600     if (env->message[len-1] != '\n')
601         env->message[len++] = '\n';
602 
603     /* make sure a trailing NUL exists; L2_MSG_BUFSIZE has room for NUL  */
604     env->message[len] = '\0';
605 
606     /* write message to channel */
607     rv = L2_OK;
608     if ((rv = l2_channel_write(ch, level, env->message, len)) != L2_OK)
609         return rv;
610     if (ch->flushmask & level)
611         l2_channel_flush(ch);
612 
613     return rv;
614 }
615 
616 /* return environment object */
l2_channel_env(l2_channel_t * ch,l2_env_t ** env)617 l2_result_t l2_channel_env(l2_channel_t *ch, l2_env_t **env)
618 {
619     if (ch == NULL || env == NULL)
620         return L2_ERR_ARG;
621     *env = ch->env;
622     return L2_OK;
623 }
624 
625