/* ** L2 - OSSP Logging Library ** Copyright (c) 2001 The OSSP Project (http://www.ossp.org/) ** Copyright (c) 2001 Cable & Wireless Deutschland (http://www.cw.com/de/) ** ** This file is part of OSSP L2, a flexible logging library which ** can be found at http://www.ossp.org/pkg/l2/. ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** l2_channel.c: channel handling ** */ #include "l2.h" #include "l2_p.h" #include /* * A channel is the central object for a logging stream. It is * implemented by a framework (the code implemented here) which provides * the channel API and which is equal for all channels and a particular * handler which implements the characteristics specific to a particular * channel class. The lifecycle of a channel is illustrated in the * following figure: * * -----BEGIN EMBEDDED OBJECT----- * Content-editor: xfig %s * Content-encoding: gzip:9 base64 * Content-type: application/fig * Content-viewer: xfig %s * Description: L2 Channel Lifecycle * Filename: l2_ch_lifecycle.fig * Last-modified: 2000-09-28/14:40 * Name: l2_ch_lifecycle * Version: eo/1.0 * H4sIAIsvszsCA62WTW/bMAyGz/WvENBzM5KyPnwusGFAdtp1l8BVWgOZUyTuhv77 * kfKHnC4t5CZJ7JAO3kcUKUq5/fr9m9IrKtab9uFYb55DcR/aLhyKH6E7NHWxDh17 * ShUIsAIofjbt4y4Ud1QgASgqQGlSt3V8Fai0AoV8gTJyI76xLD6Tb35iPSpybNlS * PsknV5po5WC0BZZRWSlL8km+tlYsa3MwJfAP6Kdokl8iltHKwjiwJ5jJL52DbIxB * Z+aY5BvSVT7GEieSzISZfGN9Fa0cjAVj55iZz7XPxxg6mdTMNz5/Ug55ncwwyXdU * mnyMPS148p1bUHCPAPPcJN+jLOrM3GjUOMckX2PV16ygiJDL9Zi7Qa4GR36i4kYM * XMW6yZ1LJP160y+iofqUy4SeKW01zCtaMT2XBjSRUrdFJr1h0rmAZg3qhnV0cUAT * KTVcfoYsjOmN1jLxbPWJdZV6T2GkTstPb2pOh3ilek+kNN3LmWO6UuflZB0/YvYk * B+eYXpi4PM4YYs80g3XK/Gh1JHG0UC/p3OFIhRkhiuXNJ3aaDr0/tKExbraWqtFX * g1qsiyue8qDxWq0ykdI+l5/g2eE87rCX79XGj8cK6CGgMh0gyH8qYPrPoTCeGzx0 * Jft6yUHU+3bbPL4cwi8AzFAazU+XKXlJlHL6O64uec3KQ9h0OQPK1uJRZNob9RCO * 3WH/mjGc5kC1HXX759Bmiixas0jkZHIgBy9wrH8PTRe+bHcvx6dMrTUOPqOVYA1x * sFhJQnf7Y8hUOVfCOZV/v3h+3N88W7tmG7rm9ztCXPGE/Gw4IuAgLUd67M4V3f8/ * nPS/M9IprN/UXfMnR2b8MA4Rl6Np3wh9EtJM6OWRlkGrfrtUa1L3T5u2DTu15qnW * r/Wup/wDvlAM/PkMAAA= * -----END EMBEDDED OBJECT----- */ /* create channel */ l2_channel_t *l2_channel_create(l2_handler_t *h) { l2_channel_t *ch; /* argument sanity check */ if (h == NULL) return NULL; /* allocate channel structure */ if ((ch = (l2_channel_t *)malloc(sizeof(l2_channel_t))) == NULL) return NULL; /* initialize channel structure */ ch->state = L2_CHSTATE_CREATED; ch->downstream = NULL; memset(&ch->context, 0, sizeof(l2_context_t)); memcpy(&ch->handler, h, sizeof(l2_handler_t)); ch->rvErrorInfo = L2_OK; ch->szErrorInfo[0] = '\0'; ch->szError[0] = '\0'; /* (optionally) perform create operation in handler */ if (ch->handler.create != NULL) { if (ch->handler.create(&ch->context, ch) != L2_OK) { free(ch); return NULL; } } return ch; } /* configure channel */ l2_result_t l2_channel_configure(l2_channel_t *ch, const char *fmt, ...) { l2_result_t rv; va_list ap; /* argument sanity check */ if (ch == NULL || fmt == NULL) return L2_ERR_ARG; /* make sure the channel is in state "created" */ if (ch->state != L2_CHSTATE_CREATED) return L2_ERR_USE; /* pass operation to handler */ rv = L2_OK; va_start(ap, fmt); if (ch->handler.configure != NULL) rv = ch->handler.configure(&ch->context, ch, fmt, ap); va_end(ap); return rv; } /* open channel (stack) */ l2_result_t l2_channel_open(l2_channel_t *ch) { l2_result_t rv; /* argument sanity check */ if (ch == NULL) return L2_ERR_ARG; /* make sure channel is in state "created" */ if (ch->state != L2_CHSTATE_CREATED) return L2_ERR_USE; /* skip empty open handlers on channel stack */ while (ch != NULL && ch->handler.open == NULL) { ch->state = L2_CHSTATE_OPENED; ch = ch->downstream; } if (ch == NULL) return L2_ERR_USE; /* pass operation to handler */ rv = ch->handler.open(&ch->context, ch); if (rv == L2_OK) ch->state = L2_CHSTATE_OPENED; return rv; } /* write to channel (stack) */ l2_result_t l2_channel_write(l2_channel_t *ch, l2_level_t level, const char *buf, size_t bufsize) { l2_result_t rv; /* argument sanity check */ if (ch == NULL || buf == NULL) return L2_ERR_ARG; /* make sure channel is in state "opened" */ if (ch->state != L2_CHSTATE_OPENED) return L2_ERR_USE; /* short circuiting */ if (bufsize == 0) return L2_OK; /* walk to next available write handler */ while (ch != NULL && ch->handler.write == NULL) ch = ch->downstream; if (ch == NULL) return L2_ERR_USE; /* pass operation to handler */ rv = ch->handler.write(&ch->context, ch, level, buf, bufsize); return rv; } /* flush channel (stack) */ l2_result_t l2_channel_flush(l2_channel_t *ch) { l2_result_t rv; /* argument sanity check */ if (ch == NULL) return L2_ERR_ARG; /* make sure channel is in state "opened" */ if (ch->state != L2_CHSTATE_OPENED) return L2_ERR_USE; /* walk to next available flush handler */ while (ch != NULL && ch->handler.flush == NULL) ch = ch->downstream; if (ch == NULL) return L2_ERR_USE; /* pass operation to handler */ rv = ch->handler.flush(&ch->context, ch); return rv; } /* close channel (stack) */ l2_result_t l2_channel_close(l2_channel_t *ch) { l2_result_t rv; /* argument sanity check */ if (ch == NULL) return L2_ERR_ARG; /* make sure channel is in state "opened" */ if (ch->state != L2_CHSTATE_OPENED) return L2_ERR_USE; /* walk to next available close handler */ while (ch != NULL && ch->handler.close == NULL) { ch->state = L2_CHSTATE_CREATED; ch = ch->downstream; } if (ch == NULL) return L2_ERR_USE; /* pass operation to handler */ rv = ch->handler.close(&ch->context, ch); if (rv == L2_OK) ch->state = L2_CHSTATE_CREATED; return rv; } /* destroy channel */ l2_result_t l2_channel_destroy(l2_channel_t *ch) { l2_result_t rv; /* argument sanity check */ if (ch == NULL) return L2_ERR_ARG; /* make sure channel is in state "opened" */ if (ch->state == L2_CHSTATE_OPENED) if ((rv = l2_channel_close(ch)) != L2_OK) return rv; /* walk to next available destroy handler */ while (ch != NULL && ch->handler.destroy == NULL) ch = ch->downstream; /* pass operation to handler */ if (ch != NULL) rv = ch->handler.destroy(&ch->context, ch); else rv = L2_OK; /* free channel structure */ free(ch); return rv; } l2_result_t l2_channel_errorinfo(l2_channel_t *ch, l2_result_t rv, const char *fmt, ...) { va_list ap; /* argument sanity check */ if (ch == NULL || rv == L2_OK || fmt == NULL) return L2_ERR_ARG; /* remember error information */ va_start(ap, fmt); l2_util_vsprintf(ch->szErrorInfo, sizeof(ch->szErrorInfo), fmt, ap); ch->rvErrorInfo = rv; va_end(ap); return L2_OK; } char *l2_channel_strerror(l2_channel_t *ch, l2_result_t rv) { char *sz; char *cpBuf; int nBuf; int n; /* argument sanity check */ if (ch == NULL) return NULL; /* start at begin of buffer */ cpBuf = ch->szError; nBuf = sizeof(ch->szError); /* translate result value into corresponding string */ if (rv == L2_OK) sz = "everything ok"; else if (rv == L2_ERR_ARG) sz = "invalid argument"; else if (rv == L2_ERR_USE) sz = "invalid use"; else if (rv == L2_ERR_MEM) sz = "no more memory available"; else if (rv == L2_ERR_SYS) sz = "operating system error"; else if (rv == L2_ERR_IO) sz = "input/output error"; else if (rv == L2_ERR_FMT) sz = "formatting error"; else if (rv == L2_ERR_INT) sz = "internal error"; else sz = "unknown error"; n = l2_util_sprintf(cpBuf, nBuf, "%s", sz); cpBuf += n; nBuf -= n; /* optionally annotate with error information */ if (rv == ch->rvErrorInfo && ch->szErrorInfo[0] != '\0') { n = l2_util_sprintf(cpBuf, nBuf, "; %s", ch->szErrorInfo); cpBuf += n; nBuf -= n; } /* optionally annotate with operating system error information */ if (rv == L2_ERR_SYS) { n = l2_util_sprintf(cpBuf, nBuf, "; %s (%d)", strerror(errno), errno); cpBuf += n; nBuf -= n; } /* return pointer to internal buffer */ return ch->szError; } /* stack channel on top of another channel */ l2_result_t l2_channel_stack(l2_channel_t *ch, l2_channel_t *chTop) { /* argument sanity check */ if (ch == NULL || chTop == NULL) return L2_ERR_ARG; /* make sure both channels are in state "created" */ if ( ch->state != L2_CHSTATE_CREATED || chTop->state != L2_CHSTATE_CREATED) return L2_ERR_USE; /* make sure top channel is a filter channel */ if (chTop->handler.type != L2_CHANNEL_FILTER) return L2_ERR_USE; /* stack the channels */ chTop->downstream = ch; return L2_OK; } /* return downstream channel */ l2_channel_t *l2_channel_downstream(l2_channel_t *ch) { /* argument sanity check */ if (ch == NULL) return NULL; return ch->downstream; } /* return channel type */ l2_chtype_t l2_channel_type(l2_channel_t *ch) { /* argument sanity check */ if (ch == NULL) return L2_CHANNEL_FILTER; /* FIXME */ return ch->handler.type; }