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