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_ch_buffer.c: buffering channel implementation
29 */
30 
31 #include "l2.h"
32 #include "l2_p.h"     /* for TRACE macro              */
33 
34 #include <string.h>
35 #include <unistd.h>   /* for alarm(3)                 */
36 #include <signal.h>   /* for sigaction(2) and SIGALRM */
37 
38 #ifdef HAVE_SYS_TIME_H
39 #include <sys/time.h> /* for setitimer(2)             */
40 #endif
41 
42 #define L2_BUFFER_TIMER ITIMER_REAL /* calls to [s|g]etitimer() and alarm() */
43 
44 /* declare private channel configuration */
45 typedef struct {
46     char       *buf;
47     int         bufpos;
48     int         bufsize;
49     long        bufinterval;
50     struct      sigaction sigalrm;
51 #if defined(HAVE_SETITIMER) && defined(HAVE_SYS_TIME_H)
52     struct      itimerval valprev;
53 #endif
54     int         levelflush;
55     l2_level_t  level;
56 } l2_ch_buffer_t;
57 
58 /* Sets the VIRTUAL timer to preconfigured value in cfg */
set_alarm(l2_ch_buffer_t * cfg)59 static int set_alarm(l2_ch_buffer_t *cfg)
60 {
61 #if defined(HAVE_SETITIMER) && defined(HAVE_SYS_TIME_H)
62     struct itimerval valtest, valnew;
63 
64     /* initialize auto vars before using them */
65     memset(&valnew, 0, sizeof(valnew));
66 
67     valnew.it_interval.tv_sec = cfg->bufinterval;
68     valnew.it_interval.tv_usec = 0;
69     valnew.it_value.tv_sec = cfg->bufinterval;
70     valnew.it_value.tv_usec = 0;
71     if  ((getitimer(L2_BUFFER_TIMER, &valtest) == 0) &&
72         ((valtest.it_value.tv_sec | valtest.it_value.tv_usec |
73         valtest.it_interval.tv_sec | valtest.it_interval.tv_usec) == 0))
74         return setitimer(L2_BUFFER_TIMER, &valnew, &cfg->valprev);
75     else {
76         cfg->bufinterval = -1L; /* mark this timer as broken */
77         assert(FALSE);
78         return 1; /* to make the compiler happy */
79     }
80 #else
81     unsigned int uiAlarmed = 0;
82 
83     assert(uiAlarmed = alarm(cfg->bufinterval));
84     if (uiAlarmed) {            /* check if SIGALRM is occupied          */
85         alarm(uiAlarmed);       /* ...if so, then hack in the old value  */
86         cfg->bufinterval = -1L; /* ...mark this timer as broken          */
87     }
88     return 0;
89 #endif
90 }
91 
92 /* Resets the VIRTUAL timer to preconfigured value in cfg */
reset_alarm(l2_ch_buffer_t * cfg)93 static int reset_alarm(l2_ch_buffer_t *cfg)
94 {
95 #if defined(HAVE_SETITIMER) && defined(HAVE_SYS_TIME_H)
96     struct itimerval valnew;
97 
98     /* initialize auto vars before using them */
99     memset(&valnew, 0, sizeof(valnew));
100 
101     valnew.it_interval.tv_sec = cfg->bufinterval;
102     valnew.it_interval.tv_usec = 0;
103     valnew.it_value.tv_sec = cfg->bufinterval;
104     valnew.it_value.tv_usec = 0;
105     return setitimer(L2_BUFFER_TIMER, &valnew, 0);
106 #else
107     alarm(cfg->bufinterval);
108     return 0;
109 #endif
110 }
111 
catchsignal(int sig,...)112 static void catchsignal(int sig, ...)
113 {
114     va_list ap;
115     static  l2_channel_t   *ch  = NULL;
116     static  l2_ch_buffer_t *cfg = NULL;
117 
118     if (sig == 0) {
119         va_start(ap, sig);
120         ch  = va_arg(ap, l2_channel_t   *); /* init the handler just like */
121         cfg = va_arg(ap, l2_ch_buffer_t *); /* Thomas Lotterer does       */
122         va_end(ap);
123     }
124     else if (sig == SIGALRM) {
125         TRACE("SIGALRM caught");
126         l2_channel_flush(ch);
127         reset_alarm(cfg); /* alarm(3) doesn't auto-reset like setitime(2) */
128     }
129 }
130 
131 /* create channel */
hook_create(l2_context_t * ctx,l2_channel_t * ch)132 static l2_result_t hook_create(l2_context_t *ctx, l2_channel_t *ch)
133 {
134     l2_ch_buffer_t *cfg;
135 
136     /* allocate private channel configuration */
137     if ((cfg = (l2_ch_buffer_t *)malloc(sizeof(l2_ch_buffer_t))) == NULL)
138         return L2_ERR_MEM;
139 
140     /* initialize configuration with reasonable defaults */
141     cfg->buf         = NULL;
142     cfg->bufpos      = 0;
143     cfg->bufsize     = 4096;
144     cfg->bufinterval = 0;
145     cfg->levelflush  = 0;
146     cfg->level       = L2_LEVEL_NONE;
147     memset(&cfg->sigalrm, 0, sizeof(cfg->sigalrm));
148 #if defined(HAVE_SETITIMER) && defined(HAVE_SYS_TIME_H)
149     memset(&cfg->valprev, 0, sizeof(cfg->valprev));
150 #endif
151 
152     /* link private channel configuration into channel context */
153     ctx->vp = cfg;
154 
155     return L2_OK;
156 }
157 
158 /* configure channel */
hook_configure(l2_context_t * ctx,l2_channel_t * ch,const char * fmt,va_list * ap)159 static l2_result_t hook_configure(l2_context_t *ctx, l2_channel_t *ch, const char *fmt, va_list *ap)
160 {
161     l2_ch_buffer_t *cfg = (l2_ch_buffer_t *)ctx->vp;
162     l2_param_t pa[4];
163     l2_result_t rv;
164     l2_env_t *env;
165 
166     /* feed and call generic parameter parsing engine */
167     L2_PARAM_SET(pa[0], size,       INT, &cfg->bufsize);
168     L2_PARAM_SET(pa[1], interval,   INT, &cfg->bufinterval);
169     L2_PARAM_SET(pa[2], levelflush, INT, &cfg->levelflush);
170     L2_PARAM_END(pa[3]);
171     l2_channel_env(ch, &env);
172     rv = l2_util_setparams(env, pa, fmt, ap);
173     if (cfg->bufinterval == -1L) /* -1 is reserved by L2 */
174         return L2_ERR_ARG;       /* set_alarm() uses it  */
175 
176     if (cfg->bufsize < 0)
177         return L2_ERR_ARG;
178 
179     return rv;
180 }
181 
182 /* open channel */
hook_open(l2_context_t * ctx,l2_channel_t * ch)183 static l2_result_t hook_open(l2_context_t *ctx, l2_channel_t *ch)
184 {
185     l2_ch_buffer_t *cfg = (l2_ch_buffer_t *)ctx->vp;
186     struct sigaction locact;
187 
188     if ((cfg->bufinterval != 0) && (cfg->bufinterval != -1L)) {
189         /* initialize auto vars before using them */
190         memset(&locact, 0, sizeof(locact));
191 
192         locact.sa_handler = (void(*)(int))catchsignal;
193         sigemptyset(&locact.sa_mask);
194         locact.sa_flags = 0;
195 
196         catchsignal(0, ch, (l2_ch_buffer_t *)ctx->vp);
197         /* save old signal context before replacing with our own */
198         if (sigaction(SIGALRM, &locact, &cfg->sigalrm) < 0)
199             return L2_ERR_SYS;
200 
201         if (set_alarm(cfg))      /* this is our own L2 set_alarm */
202             return L2_ERR_SYS;
203     }
204 
205     /* open channel buffer */
206     if (cfg->bufsize > 0) {
207         if ((cfg->buf = malloc(cfg->bufsize)) == NULL)
208             return L2_ERR_MEM;
209         cfg->bufpos = 0;
210     }
211 
212     return L2_OK_PASS;
213 }
214 
215 /* write to channel */
hook_write(l2_context_t * ctx,l2_channel_t * ch,l2_level_t level,const char * buf,size_t buf_size)216 static l2_result_t hook_write(l2_context_t *ctx, l2_channel_t *ch,
217                               l2_level_t level, const char *buf, size_t buf_size)
218 {
219     l2_ch_buffer_t *cfg = (l2_ch_buffer_t *)ctx->vp;
220     l2_channel_t *downstream;
221     l2_result_t rv;
222 
223     if (buf_size > (cfg->bufsize - cfg->bufpos)) {
224         /* flush buffer if necessary */
225         if (cfg->bufpos > 0) {
226             downstream = NULL;
227             while ((rv = l2_channel_downstream(ch, &downstream)) == L2_OK)
228                 if ((rv = l2_channel_write(downstream, cfg->level, cfg->buf, cfg->bufpos)) != L2_OK)
229                     return rv;
230             cfg->bufpos = 0;
231             cfg->level  = L2_LEVEL_NONE;
232         }
233         /* pass through immediately to downstream if still too large */
234         if (buf_size > cfg->bufsize) {
235             downstream = NULL;
236             while ((rv = l2_channel_downstream(ch, &downstream)) == L2_OK)
237                 if ((rv = l2_channel_write(downstream, level, buf, buf_size)) != L2_OK)
238                     return rv;
239             return L2_OK;
240         }
241     }
242 
243     /* flush if incoming message level differs from those already in buffer */
244     if (   (cfg->levelflush)        /* if different levels force a flush    */
245         && (cfg->bufpos > 0)        /* and there is something in the buffer */
246         && (cfg->level != L2_LEVEL_NONE) /* and a remembered level is known */
247         && (level != cfg->level)         /* and the levels really differ    */)
248     {
249         downstream = NULL;
250         while (l2_channel_downstream(ch, &downstream) == L2_OK)
251             if ((rv = l2_channel_write(downstream, cfg->level, cfg->buf, cfg->bufpos)) != L2_OK)
252                 return rv;
253         cfg->bufpos = 0;
254         cfg->level  = L2_LEVEL_NONE;
255     }
256 
257     /* finally write incoming message to channel buffer */
258     memcpy(cfg->buf+cfg->bufpos, buf, buf_size);
259     cfg->bufpos += buf_size;
260     cfg->level = level;
261 
262     return L2_OK;
263 }
264 
265 /* flush channel */
hook_flush(l2_context_t * ctx,l2_channel_t * ch)266 static l2_result_t hook_flush(l2_context_t *ctx, l2_channel_t *ch)
267 {
268     l2_ch_buffer_t *cfg = (l2_ch_buffer_t *)ctx->vp;
269     l2_channel_t *downstream;
270     l2_result_t rv;
271 
272     /* write the buffer contents downstream */
273 TRACE("l2_ch_buffer hook_flush called\n");
274     if (cfg->bufpos > 0) {
275         downstream = NULL;
276         while (l2_channel_downstream(ch, &downstream) == L2_OK)
277             if ((rv = l2_channel_write(downstream, cfg->level, cfg->buf, cfg->bufpos)) != L2_OK)
278                 return rv;
279         cfg->bufpos = 0;
280         cfg->level  = L2_LEVEL_NONE; /* reset this->context->level */
281     }
282 
283     /* reset the flush alarm timer to synchronize the buffer */
284     if ((cfg->bufinterval != 0) && (cfg->bufinterval != -1L))
285         if (reset_alarm(cfg))
286             return L2_ERR_SYS;
287 
288     return L2_OK_PASS;
289 }
290 
291 /* close channel */
hook_close(l2_context_t * ctx,l2_channel_t * ch)292 static l2_result_t hook_close(l2_context_t *ctx, l2_channel_t *ch)
293 {
294     l2_ch_buffer_t *cfg = (l2_ch_buffer_t *)ctx->vp;
295     l2_channel_t *downstream;
296     l2_result_t rv;
297 
298     if ((cfg->bufinterval != 0) && (cfg->bufinterval != -1L)) {
299 #if defined(HAVE_SETITIMER) && defined(HAVE_SYS_TIME_H)
300         if (setitimer(L2_BUFFER_TIMER, &cfg->valprev, 0)) /* restore timer */
301             return L2_ERR_SYS;
302 #else
303         alarm(0);
304 #endif
305         /* restore previous signal context if previously saved & replaced  */
306         if (&cfg->sigalrm.sa_handler)
307             if (sigaction(SIGALRM, &cfg->sigalrm, 0) < 0)
308                 rv = L2_ERR_SYS;
309     }
310 
311     /* write pending data before closing down */
312     if (cfg->bufpos > 0) {
313         downstream = NULL;
314         while (l2_channel_downstream(ch, &downstream) == L2_OK)
315             if ((rv = l2_channel_write(downstream, cfg->level, cfg->buf, cfg->bufpos)) != L2_OK)
316                 return rv;
317         cfg->bufpos = 0;
318         cfg->level  = L2_LEVEL_NONE; /* reset this->context->level */
319     }
320 
321     /* close channel buffer */
322     if (cfg->buf != NULL) {
323         free(cfg->buf);
324         cfg->buf = NULL;
325     }
326 
327     return L2_OK_PASS;
328 }
329 
330 /* destroy channel */
hook_destroy(l2_context_t * ctx,l2_channel_t * ch)331 static l2_result_t hook_destroy(l2_context_t *ctx, l2_channel_t *ch)
332 {
333     l2_ch_buffer_t *cfg = (l2_ch_buffer_t *)ctx->vp;
334 
335     /* destroy channel configuration */
336     if (cfg->buf != NULL)
337         free(cfg->buf);
338     free(cfg);
339 
340     return L2_OK_PASS;
341 }
342 
343 /* exported channel handler structure */
344 l2_handler_t l2_handler_buffer = {
345     "buffer",
346     L2_CHANNEL_FILTER,
347     hook_create,
348     hook_configure,
349     hook_open,
350     hook_write,
351     hook_flush,
352     hook_close,
353     hook_destroy
354 };
355 
356