1 /*
2 * Copyright (C) 2004, 2005 James Antill
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * email: james@and.org
19 */
20 /* Vectored logging APIs */
21
22 #define CONF_USE_HEXDUMP TRUE
23
24 #define _GNU_SOURCE 1
25
26 #include <vstr.h>
27
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <syslog.h>
31 #include <err.h>
32
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36
37 #include <sys/socket.h>
38 #include <arpa/inet.h>
39 #include <netinet/in.h>
40 #include <sys/un.h>
41
42 #include <limits.h>
43
44 #include <signal.h>
45
46
47 #define VLG_COMPILE_INLINE 0
48 #include "vlg.h"
49
50 /* FIXME: could possibly work on other OSes ? */
51 #ifdef __linux__
52 # include <sys/mount.h>
53 #endif
54
55 #ifdef MS_BIND
56 # define CONF_USE_MOUNT_BIND TRUE
57 # define BIND_MOUNT(x, y) mount(x, y, "", MS_BIND, "")
58 #else
59 # define BIND_MOUNT(x, y) -1 /* do nothing */
60 # define CONF_USE_MOUNT_BIND FALSE
61 #endif
62
63 #define CONF_USE_INTERNAL_SYSLOG TRUE
64
65 #define EX_UTILS_NO_FUNCS 1
66 #include "ex_utils.h"
67
68 /* how much memory should we preallocate so it's "unlikely" we'll get mem errors
69 * when writting a log entry */
70 #define VLG_MEM_PREALLOC (4 * 1024)
71
72 #ifndef FALSE
73 # define FALSE 0
74 #endif
75
76 #ifndef TRUE
77 # define TRUE 1
78 #endif
79
80 #if CONF_USE_HEXDUMP
81 # include "hexdump.h"
82 #else
83 # define ex_hexdump_reset() /* do nothing */
84 # define ex_hexdump_process(x1, x2, x3, x4, x5, x6, x7, x8, x9) FALSE
85 #endif
86
87 static Vstr_conf *vlg__conf = NULL;
88 static Vstr_conf *vlg__sig_conf = NULL;
89 static int vlg__done_syslog_init = FALSE;
90
91
vlg__syslog_con(Vlg * vlg,int alt)92 static int vlg__syslog_con(Vlg *vlg, int alt)
93 {
94 int type = alt ? SOCK_STREAM : SOCK_DGRAM;
95 int fd = -1;
96 const char *fname = _PATH_LOG;
97 size_t len = strlen(fname) + 1;
98 struct sockaddr_un tmp_sun;
99 struct sockaddr *sa = (struct sockaddr *)&tmp_sun;
100 socklen_t alloc_len = 0;
101
102 if (!CONF_USE_INTERNAL_SYSLOG)
103 goto conf_fail;
104 if (vlg->syslog_fd != -1)
105 return (TRUE);
106
107 tmp_sun.sun_path[0] = 0;
108 alloc_len = SUN_LEN(&tmp_sun) + len;
109 tmp_sun.sun_family = AF_LOCAL;
110 memcpy(tmp_sun.sun_path, fname, len);
111
112 if (vlg->syslog_stream)
113 type = alt ? SOCK_DGRAM : SOCK_STREAM;
114
115 if ((fd = socket(PF_LOCAL, type, 0)) == -1)
116 goto sock_fail;
117
118 if (fcntl(fd, F_SETFD, TRUE) == -1)
119 goto fcntl_fail;
120
121 if (connect(fd, sa, alloc_len) == -1)
122 goto connect_fail;
123
124 vlg->syslog_fd = fd;
125
126 return (TRUE);
127
128 connect_fail:
129 if (!alt)
130 return (vlg__syslog_con(vlg, TRUE));
131
132 fcntl_fail:
133 close(fd);
134 sock_fail:
135 conf_fail:
136 if (!vlg__done_syslog_init)
137 openlog(vlg->prog_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
138 vlg__done_syslog_init = TRUE;
139 return (FALSE);
140 }
141
vlg__syslog_close(Vlg * vlg)142 static void vlg__syslog_close(Vlg *vlg)
143 {
144 if (vlg->syslog_fd != -1)
145 close(vlg->syslog_fd);
146 vlg->syslog_fd = -1;
147 }
148
vlg__flush(Vlg * vlg,int type,int out_err)149 static void vlg__flush(Vlg *vlg, int type, int out_err)
150 {
151 Vstr_base *dlg = vlg->out_vstr;
152 time_t now = (*vlg->tm_get)();
153
154 ASSERT(vstr_export_chr(dlg, dlg->len) == '\n');
155
156 if (now != vlg->tm_time)
157 {
158 vlg->tm_time = now;
159 date_syslog(vlg->tm_time, vlg->tm_data, sizeof(vlg->tm_data));
160 }
161
162 if (vlg->daemon_mode)
163 {
164 if (!vlg__syslog_con(vlg, 0))
165 { /* ignoring borken syslog()'s that overflow here use a real OS */
166 const char *tmp = vstr_export_cstr_ptr(dlg, 1, dlg->len - 1);
167
168 if (!tmp)
169 errno = ENOMEM, err(EXIT_FAILURE, "vlog__flush");
170
171 syslog(type, "%s", tmp);
172 }
173 else
174 {
175 pid_t pid = getpid();
176 int fd = vlg->syslog_fd;
177 size_t beg_len = 0;
178
179 vstr_add_fmt(dlg, 0, "<%u>%s %s[%lu]: ", type | LOG_DAEMON,
180 vlg->tm_data, vlg->prog_name, (unsigned long)pid);
181
182 if (vlg->syslog_stream)
183 vstr_sub_buf(dlg, dlg->len, 1, "", 1);
184 else
185 vstr_sc_reduce(dlg, 1, dlg->len, 1); /* remove "\n" */
186
187 if (dlg->conf->malloc_bad)
188 errno = ENOMEM, err(EXIT_FAILURE, "vlog__flush");
189
190 beg_len = dlg->len;
191 while (dlg->len)
192 if (!vstr_sc_write_fd(dlg, 1, dlg->len, fd, NULL) && (errno != EAGAIN))
193 {
194 vlg__syslog_close(vlg);
195 if (beg_len != dlg->len) /* we have sent _some_ data, and it died */
196 break;
197 if (!vlg__syslog_con(vlg, 0))
198 err(EXIT_FAILURE, "vlg__syslog_con");
199 }
200 }
201
202 vstr_del(dlg, 1, dlg->len);
203 }
204 else
205 {
206 int fd = out_err ? STDERR_FILENO : STDOUT_FILENO;
207
208 if (vlg->log_prefix_console)
209 {
210 /* Note: we add the begining backwards, it's easier that way */
211 if ((type == LOG_WARNING) && !vstr_add_cstr_ptr(dlg, 0, "WARN: "))
212 errno = ENOMEM, err(EXIT_FAILURE, "warn");
213 if ((type == LOG_ALERT) && !vstr_add_cstr_ptr(dlg, 0, "ERR: "))
214 errno = ENOMEM, err(EXIT_FAILURE, "err");
215 if ((type == LOG_DEBUG) && !vstr_add_cstr_ptr(dlg, 0, "DEBUG: "))
216 errno = ENOMEM, err(EXIT_FAILURE, "vlog_vdbg");
217
218 if (!vlg->log_pid)
219 {
220 if (!vstr_add_cstr_ptr(dlg, 0, "]: "))
221 errno = ENOMEM, err(EXIT_FAILURE, "prefix");
222 }
223 else
224 {
225 pid_t pid = getpid();
226
227 if (!vstr_add_fmt(dlg, 0, "] %lu: ", (unsigned long)pid))
228 errno = ENOMEM, err(EXIT_FAILURE, "prefix");
229 }
230
231 if (!vstr_add_cstr_ptr(dlg, 0, vlg->tm_data) ||
232 !vstr_add_cstr_ptr(dlg, 0, "["))
233 errno = ENOMEM, err(EXIT_FAILURE, "prefix");
234 }
235
236 while (dlg->len)
237 if (!vstr_sc_write_fd(dlg, 1, dlg->len, fd, NULL) && (errno != EAGAIN))
238 err(EXIT_FAILURE, "vlg__flush");
239 }
240 }
241
vlg__add_chk_flush(Vlg * vlg,const char * fmt,va_list ap,int type,int out_err)242 static void vlg__add_chk_flush(Vlg *vlg, const char *fmt, va_list ap,
243 int type, int out_err)
244 {
245 Vstr_base *dlg = vlg->out_vstr;
246
247 if (!vstr_add_vfmt(dlg, dlg->len, fmt, ap))
248 {
249 if (dlg->conf->malloc_bad)
250 errno = ENOMEM, err(EXIT_FAILURE, "chk_flush");
251 return;
252 }
253
254 if (vstr_export_chr(dlg, dlg->len) == '\n')
255 vlg__flush(vlg, type, out_err);
256 }
257
258
259 /* because vlg goes away quickly it's likely we'll want to just use _BUF_PTR
260 for Vstr data to save the copying. So here is a helper. */
vlg__fmt__add_vstr_add_vstr(Vstr_base * base,size_t pos,Vstr_fmt_spec * spec)261 static int vlg__fmt__add_vstr_add_vstr(Vstr_base *base, size_t pos,
262 Vstr_fmt_spec *spec)
263 {
264 Vstr_base *sf = VSTR_FMT_CB_ARG_PTR(spec, 0);
265 size_t sf_pos = VSTR_FMT_CB_ARG_VAL(spec, size_t, 1);
266 size_t sf_len = VSTR_FMT_CB_ARG_VAL(spec, size_t, 2);
267 unsigned int sf_flags = VSTR_TYPE_ADD_BUF_PTR;
268
269 if (!vstr_sc_fmt_cb_beg(base, &pos, spec, &sf_len,
270 VSTR_FLAG_SC_FMT_CB_BEG_OBJ_STR))
271 return (FALSE);
272
273 if (!vstr_add_vstr(base, pos, sf, sf_pos, sf_len, sf_flags))
274 return (FALSE);
275
276 if (!vstr_sc_fmt_cb_end(base, pos, spec, sf_len))
277 return (FALSE);
278
279 return (TRUE);
280 }
281
vlg__fmt_add_vstr_add_vstr(Vstr_conf * conf,const char * name)282 static int vlg__fmt_add_vstr_add_vstr(Vstr_conf *conf, const char *name)
283 {
284 return (vstr_fmt_add(conf, name, vlg__fmt__add_vstr_add_vstr,
285 VSTR_TYPE_FMT_PTR_VOID,
286 VSTR_TYPE_FMT_SIZE_T,
287 VSTR_TYPE_FMT_SIZE_T,
288 VSTR_TYPE_FMT_END));
289 }
290
291 /* because you have to do (size_t)1 for varargs it's annoying when you want
292 the entire Vstr ... helper */
vlg__fmt__add_vstr_add_all_vstr(Vstr_base * base,size_t pos,Vstr_fmt_spec * spec)293 static int vlg__fmt__add_vstr_add_all_vstr(Vstr_base *base, size_t pos,
294 Vstr_fmt_spec *spec)
295 {
296 Vstr_base *sf = VSTR_FMT_CB_ARG_PTR(spec, 0);
297 size_t sf_pos = 1;
298 size_t sf_len = sf->len;
299 unsigned int sf_flags = VSTR_TYPE_ADD_BUF_PTR;
300
301 if (!vstr_sc_fmt_cb_beg(base, &pos, spec, &sf_len,
302 VSTR_FLAG_SC_FMT_CB_BEG_OBJ_STR))
303 return (FALSE);
304
305 if (!vstr_add_vstr(base, pos, sf, sf_pos, sf_len, sf_flags))
306 return (FALSE);
307
308 if (!vstr_sc_fmt_cb_end(base, pos, spec, sf_len))
309 return (FALSE);
310
311 return (TRUE);
312 }
313
vlg__fmt_add_vstr_add_all_vstr(Vstr_conf * conf,const char * name)314 static int vlg__fmt_add_vstr_add_all_vstr(Vstr_conf *conf, const char *name)
315 {
316 return (vstr_fmt_add(conf, name, vlg__fmt__add_vstr_add_all_vstr,
317 VSTR_TYPE_FMT_PTR_VOID,
318 VSTR_TYPE_FMT_END));
319 }
320
321 /* also a helper for printing sects --
322 * should probably have some in Vstr itself */
vlg__fmt__add_vstr_add_sect_vstr(Vstr_base * base,size_t pos,Vstr_fmt_spec * spec)323 static int vlg__fmt__add_vstr_add_sect_vstr(Vstr_base *base, size_t pos,
324 Vstr_fmt_spec *spec)
325 {
326 Vstr_base *sf = VSTR_FMT_CB_ARG_PTR(spec, 0);
327 Vstr_sects *sects = VSTR_FMT_CB_ARG_PTR(spec, 1);
328 unsigned int num = VSTR_FMT_CB_ARG_VAL(spec, unsigned int, 2);
329 size_t sf_pos = VSTR_SECTS_NUM(sects, num)->pos;
330 size_t sf_len = VSTR_SECTS_NUM(sects, num)->len;
331 unsigned int sf_flags = VSTR_TYPE_ADD_BUF_PTR;
332
333 if (!vstr_sc_fmt_cb_beg(base, &pos, spec, &sf_len,
334 VSTR_FLAG_SC_FMT_CB_BEG_OBJ_STR))
335 return (FALSE);
336
337 if (!vstr_add_vstr(base, pos, sf, sf_pos, sf_len, sf_flags))
338 return (FALSE);
339
340 if (!vstr_sc_fmt_cb_end(base, pos, spec, sf_len))
341 return (FALSE);
342
343 return (TRUE);
344 }
345
vlg__fmt_add_vstr_add_sect_vstr(Vstr_conf * conf,const char * name)346 static int vlg__fmt_add_vstr_add_sect_vstr(Vstr_conf *conf, const char *name)
347 {
348 return (vstr_fmt_add(conf, name, vlg__fmt__add_vstr_add_sect_vstr,
349 VSTR_TYPE_FMT_PTR_VOID,
350 VSTR_TYPE_FMT_PTR_VOID,
351 VSTR_TYPE_FMT_UINT,
352 VSTR_TYPE_FMT_END));
353 }
354
vlg__fmt__add_vstr_add_hexdump_vstr(Vstr_base * base,size_t pos,Vstr_fmt_spec * spec)355 static int vlg__fmt__add_vstr_add_hexdump_vstr(Vstr_base *base, size_t pos,
356 Vstr_fmt_spec *spec)
357 {
358 Vstr_base *sf = VSTR_FMT_CB_ARG_PTR(spec, 0);
359 size_t sf_pos = VSTR_FMT_CB_ARG_VAL(spec, size_t, 1);
360 size_t sf_len = VSTR_FMT_CB_ARG_VAL(spec, size_t, 2);
361 size_t orig_len = base->len;
362
363 ex_hexdump_reset();
364 ex_hexdump_process(base, pos, sf, sf_pos, sf_len, PRNT_NONE,
365 UINT_MAX, FALSE, TRUE);
366 if (base->conf->malloc_bad)
367 return (FALSE);
368
369 sf_len = base->len - orig_len;
370
371 if (!vstr_sc_fmt_cb_beg(base, &pos, spec, &sf_len,
372 VSTR_FLAG_SC_FMT_CB_BEG_OBJ_ATOM))
373 return (FALSE);
374
375 if (!vstr_sc_fmt_cb_end(base, pos, spec, sf_len))
376 return (FALSE);
377
378 return (TRUE);
379 }
380
vlg__fmt_add_vstr_add_hexdump_vstr(Vstr_conf * conf,const char * name)381 static int vlg__fmt_add_vstr_add_hexdump_vstr(Vstr_conf *conf, const char *name)
382 {
383 return (vstr_fmt_add(conf, name, vlg__fmt__add_vstr_add_hexdump_vstr,
384 VSTR_TYPE_FMT_PTR_VOID,
385 VSTR_TYPE_FMT_SIZE_T,
386 VSTR_TYPE_FMT_SIZE_T,
387 VSTR_TYPE_FMT_END));
388 }
389
390 /* also a helper for printing any network address */
vlg__fmt__add_vstr_add_sa(Vstr_base * base,size_t pos,Vstr_fmt_spec * spec)391 static int vlg__fmt__add_vstr_add_sa(Vstr_base *base, size_t pos,
392 Vstr_fmt_spec *spec)
393 {
394 struct sockaddr *sa = VSTR_FMT_CB_ARG_PTR(spec, 0);
395 size_t obj_len = 0;
396 char buf1[128 + 1];
397 char buf2[sizeof(short) * CHAR_BIT + 1];
398 const char *ptr1 = NULL;
399 size_t len1 = 0;
400 const char *ptr2 = NULL;
401 size_t len2 = 0;
402
403 assert(sizeof(buf1) >= INET_ADDRSTRLEN);
404 assert(sizeof(buf1) >= INET6_ADDRSTRLEN);
405
406 if (!sa)
407 {
408 ptr1 = "<none>";
409 len1 = strlen(ptr1);
410 }
411 else
412 switch (sa->sa_family)
413 {
414 case AF_INET:
415 {
416 struct sockaddr_in *sin4 = (void *)sa;
417 ptr1 = inet_ntop(AF_INET, &sin4->sin_addr, buf1, sizeof(buf1));
418 if (!ptr1) ptr1 = "<unknown>";
419 len1 = strlen(ptr1);
420 ptr2 = buf2;
421 len2 = vstr_sc_conv_num10_uint(buf2, sizeof(buf2), ntohs(sin4->sin_port));
422 }
423 break;
424
425 case AF_INET6:
426 {
427 struct sockaddr_in6 *sin6 = (void *)sa;
428 ptr1 = inet_ntop(AF_INET6, &sin6->sin6_addr, buf1, sizeof(buf1));
429 if (!ptr1) ptr1 = "<unknown>";
430 len1 = strlen(ptr1);
431 ptr2 = buf2;
432 len2 = vstr_sc_conv_num10_uint(buf2,sizeof(buf2), ntohs(sin6->sin6_port));
433 }
434 break;
435
436 case AF_LOCAL:
437 { /* struct sockaddr_un *sun = (void *)sa; */
438 struct sockaddr_un *sun = (void *)sa;
439 ptr1 = "local";
440 len1 = strlen(ptr1);
441 ptr2 = sun->sun_path;
442 len2 = strlen(ptr2);
443 }
444 break;
445
446 default: ASSERT_NOT_REACHED();
447 }
448
449 obj_len = len1 + !!len2 + len2;
450
451 if (!vstr_sc_fmt_cb_beg(base, &pos, spec, &obj_len,
452 VSTR_FLAG_SC_FMT_CB_BEG_OBJ_ATOM))
453 return (FALSE);
454 ASSERT(obj_len == (len1 + !!len2 + len2));
455
456 if (!vstr_add_buf(base, pos, ptr1, len1))
457 return (FALSE);
458 if (ptr2 && (!vstr_add_rep_chr(base, pos + len1, '@', 1) ||
459 !vstr_add_buf( base, pos + len1 + 1, ptr2, len2)))
460 return (FALSE);
461
462 if (!vstr_sc_fmt_cb_end(base, pos, spec, obj_len))
463 return (FALSE);
464
465 return (TRUE);
466 }
467
vlg__fmt_add_vstr_add_sa(Vstr_conf * conf,const char * name)468 static int vlg__fmt_add_vstr_add_sa(Vstr_conf *conf, const char *name)
469 {
470 return (vstr_fmt_add(conf, name, vlg__fmt__add_vstr_add_sa,
471 VSTR_TYPE_FMT_PTR_VOID,
472 VSTR_TYPE_FMT_END));
473 }
474
vlg_sc_fmt_add_all(Vstr_conf * conf)475 int vlg_sc_fmt_add_all(Vstr_conf *conf)
476 {
477 return (VSTR_SC_FMT_ADD(conf, vlg__fmt_add_vstr_add_vstr,
478 "<vstr", "p%zu%zu", ">") &&
479 VSTR_SC_FMT_ADD(conf, vlg__fmt_add_vstr_add_all_vstr,
480 "<vstr.all", "p", ">") &&
481 VSTR_SC_FMT_ADD(conf, vlg__fmt_add_vstr_add_hexdump_vstr,
482 "<vstr.hexdump", "p%zu%zu", ">") &&
483 VSTR_SC_FMT_ADD(conf, vlg__fmt_add_vstr_add_sect_vstr,
484 "<vstr.sect", "p%p%u", ">") &&
485 VSTR_SC_FMT_ADD(conf, vlg__fmt_add_vstr_add_sa,
486 "<sa", "p", ">"));
487 }
488
vlg_sc_bind_mount(const char * chroot_dir)489 void vlg_sc_bind_mount(const char *chroot_dir)
490 { /* make sure we can reconnect to syslog */
491 Vstr_base *tmp = NULL;
492 const char *src = "/dev/log";
493 const char *dst = NULL;
494 struct stat64 st_src[1];
495 struct stat64 st_dst[1];
496
497 if (!CONF_USE_MOUNT_BIND || !chroot_dir)
498 return;
499
500 if (!(tmp = vstr_make_base(NULL)))
501 errno = ENOMEM, err(EXIT_FAILURE, "bind-mount");
502
503 vstr_add_fmt(tmp, 0, "%s%s", chroot_dir, "/dev/log");
504 dst = vstr_export_cstr_ptr(tmp, 1, tmp->len);
505 if (tmp->conf->malloc_bad)
506 errno = ENOMEM, err(EXIT_FAILURE, "bind-mount");
507
508 if (stat64(src, st_src) == -1)
509 err(EXIT_FAILURE, "stat(%s)", src);
510 if (stat64(dst, st_dst) == -1)
511 err(EXIT_FAILURE, "stat(%s)", dst);
512
513 if ((st_src->st_ino != st_dst->st_ino) ||
514 (st_src->st_dev != st_dst->st_dev))
515 {
516 umount(dst); /* NOTE: You can't bind mount over a bind mount,
517 * so if syslog is restarted we need to try this */
518 if (BIND_MOUNT(src, dst) == -1)
519 err(EXIT_FAILURE, "bind-mount(%s, %s)", src, dst);
520 }
521
522 vstr_free_base(tmp);
523 }
524
vlg_init(void)525 void vlg_init(void)
526 {
527 unsigned int buf_sz = 0;
528
529 if (!(vlg__conf = vstr_make_conf()))
530 goto malloc_err_vstr_conf;
531 if (!(vlg__sig_conf = vstr_make_conf()))
532 goto malloc_err_vstr_conf;
533
534 if (!vstr_cntl_conf(vlg__conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '$') ||
535 !vstr_sc_fmt_add_all(vlg__conf) ||
536 !vlg_sc_fmt_add_all(vlg__conf) ||
537 FALSE)
538 goto malloc_err_vstr_fmt_all;
539 if (!vstr_cntl_conf(vlg__sig_conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '$') ||
540 !vstr_sc_fmt_add_all(vlg__sig_conf) ||
541 !vlg_sc_fmt_add_all(vlg__sig_conf) ||
542 FALSE)
543 goto malloc_err_vstr_fmt_all;
544
545 vstr_cntl_conf(vlg__conf, VSTR_CNTL_CONF_GET_NUM_BUF_SZ, &buf_sz);
546 vstr_cntl_conf(vlg__sig_conf, VSTR_CNTL_CONF_GET_NUM_BUF_SZ, &buf_sz);
547
548 /* don't bother with _NON nodes */
549 if (!vstr_make_spare_nodes(vlg__conf, VSTR_TYPE_NODE_BUF,
550 (VLG_MEM_PREALLOC / buf_sz) + 1) ||
551 !vstr_make_spare_nodes(vlg__conf, VSTR_TYPE_NODE_PTR,
552 (VLG_MEM_PREALLOC / buf_sz) + 1) ||
553 !vstr_make_spare_nodes(vlg__conf, VSTR_TYPE_NODE_REF,
554 (VLG_MEM_PREALLOC / buf_sz) + 1))
555 goto malloc_err_vstr_spare;
556 if (!vstr_make_spare_nodes(vlg__sig_conf, VSTR_TYPE_NODE_BUF,
557 (VLG_MEM_PREALLOC / buf_sz) + 1) ||
558 !vstr_make_spare_nodes(vlg__sig_conf, VSTR_TYPE_NODE_PTR,
559 (VLG_MEM_PREALLOC / buf_sz) + 1) ||
560 !vstr_make_spare_nodes(vlg__sig_conf, VSTR_TYPE_NODE_REF,
561 (VLG_MEM_PREALLOC / buf_sz) + 1))
562 goto malloc_err_vstr_spare;
563
564 return;
565
566 malloc_err_vstr_spare:
567 malloc_err_vstr_fmt_all:
568 vstr_free_conf(vlg__conf);
569 vstr_free_conf(vlg__sig_conf);
570 malloc_err_vstr_conf:
571 errno = ENOMEM; err(EXIT_FAILURE, "vlg_init");
572 }
573
vlg_exit(void)574 void vlg_exit(void)
575 {
576 if (vlg__done_syslog_init)
577 closelog();
578
579 vstr_free_conf(vlg__conf); vlg__conf = NULL;
580 vstr_free_conf(vlg__sig_conf); vlg__sig_conf = NULL;
581 }
582
vlg__tm_get(void)583 static time_t vlg__tm_get(void)
584 {
585 return time(NULL);
586 }
587
vlg_make(void)588 Vlg *vlg_make(void)
589 {
590 Vlg *vlg = malloc(sizeof(Vlg));
591
592 if (!vlg)
593 goto malloc_err_vlg;
594
595 if (!(vlg->out_vstr = vstr_make_base(vlg__conf)))
596 goto malloc_err_vstr_base;
597
598 if (!(vlg->sig_out_vstr = vstr_make_base(vlg__sig_conf)))
599 goto malloc_err_vstr_base;
600
601 vlg->prog_name = NULL;
602 vlg->syslog_fd = -1;
603
604 vlg->tm_time = -1;
605 vlg->tm_get = vlg__tm_get;
606
607 vlg->syslog_stream = FALSE;
608 vlg->log_pid = FALSE;
609 vlg->out_dbg = 0;
610 vlg->daemon_mode = FALSE;
611 vlg->log_prefix_console = TRUE;
612
613 return (vlg);
614
615 malloc_err_vstr_base:
616 free(vlg);
617 malloc_err_vlg:
618
619 return (NULL);
620 }
621
622 /* don't actually free ... this shouldn't happen until exit time anyway */
vlg_free(Vlg * vlg)623 void vlg_free(Vlg *vlg)
624 {
625 vstr_free_base(vlg->out_vstr); vlg->out_vstr = NULL;
626 vstr_free_base(vlg->sig_out_vstr); vlg->sig_out_vstr = NULL;
627 }
628
vlg_daemon(Vlg * vlg,const char * name)629 void vlg_daemon(Vlg *vlg, const char *name)
630 {
631 ASSERT(name);
632
633 vlg->prog_name = name;
634 vlg->log_pid = TRUE;
635 vlg->daemon_mode = TRUE;
636
637 vlg__syslog_con(vlg, 0);
638 }
639
vlg_debug(Vlg * vlg)640 void vlg_debug(Vlg *vlg)
641 {
642 if (vlg->out_dbg >= 3)
643 return;
644
645 ++vlg->out_dbg;
646 }
647
vlg_undbg(Vlg * vlg)648 void vlg_undbg(Vlg *vlg)
649 {
650 if (!vlg->out_dbg)
651 return;
652
653 --vlg->out_dbg;
654 }
655
vlg_pid_set(Vlg * vlg,int pid)656 int vlg_pid_set(Vlg *vlg, int pid)
657 {
658 int old = vlg->log_pid;
659
660 vlg->log_pid = !!pid;
661
662 return (old);
663 }
664
vlg_prefix_set(Vlg * vlg,int prefix)665 int vlg_prefix_set(Vlg *vlg, int prefix)
666 {
667 int old = vlg->log_prefix_console;
668
669 vlg->log_prefix_console = prefix;
670
671 return (old);
672 }
673
vlg_time_set(Vlg * vlg,time_t (* func)(void))674 void vlg_time_set(Vlg *vlg, time_t (*func)(void))
675 {
676 vlg->tm_get = func;
677 }
678
vlg_pid_file(Vlg * vlg,const char * pid_file)679 void vlg_pid_file(Vlg *vlg, const char *pid_file)
680 {
681 Vstr_base *out = vlg->out_vstr;
682
683 if (out->len)
684 vlg_err(vlg, EXIT_FAILURE, "Data in vlg for pid_file\n");
685
686 if (!vstr_add_fmt(out, out->len, "%lu", (unsigned long)getpid()))
687 {
688 vstr_del(out, 1, out->len);
689 vlg_err(vlg, EXIT_FAILURE, "vlg_pid_file: %m\n");
690 }
691
692 if (!vstr_sc_write_file(out, 1, out->len,
693 pid_file, O_WRONLY | O_CREAT | O_TRUNC, 0644, 0,NULL))
694 {
695 vstr_del(out, 1, out->len);
696 vlg_err(vlg, EXIT_FAILURE, "vlg_pid_file(%s): %m\n", pid_file);
697 }
698 }
699
700 /* ================== actual logging functions ================== */
701
702 /* ---------- va_list ---------- */
vlg_vabort(Vlg * vlg,const char * fmt,va_list ap)703 void vlg_vabort(Vlg *vlg, const char *fmt, va_list ap)
704 {
705 vlg__add_chk_flush(vlg, fmt, ap, LOG_ALERT, TRUE);
706 abort();
707 }
708
vlg_verr(Vlg * vlg,int exit_code,const char * fmt,va_list ap)709 void vlg_verr(Vlg *vlg, int exit_code, const char *fmt, va_list ap)
710 {
711 vlg__add_chk_flush(vlg, fmt, ap, LOG_ALERT, TRUE);
712 _exit(exit_code);
713 }
714
vlg_vwarn(Vlg * vlg,const char * fmt,va_list ap)715 void vlg_vwarn(Vlg *vlg, const char *fmt, va_list ap)
716 {
717 vlg__add_chk_flush(vlg, fmt, ap, LOG_WARNING, TRUE);
718 }
719
vlg_vinfo(Vlg * vlg,const char * fmt,va_list ap)720 void vlg_vinfo(Vlg *vlg, const char *fmt, va_list ap)
721 {
722 vlg__add_chk_flush(vlg, fmt, ap, LOG_NOTICE, FALSE);
723 }
724
vlg_vdbg1(Vlg * vlg,const char * fmt,va_list ap)725 void vlg_vdbg1(Vlg *vlg, const char *fmt, va_list ap)
726 {
727 if (vlg->out_dbg < 1)
728 return;
729
730 vlg__add_chk_flush(vlg, fmt, ap, LOG_DEBUG, TRUE);
731 }
732
vlg_vdbg2(Vlg * vlg,const char * fmt,va_list ap)733 void vlg_vdbg2(Vlg *vlg, const char *fmt, va_list ap)
734 {
735 if (vlg->out_dbg < 2)
736 return;
737
738 vlg__add_chk_flush(vlg, fmt, ap, LOG_DEBUG, TRUE);
739 }
740
vlg_vdbg3(Vlg * vlg,const char * fmt,va_list ap)741 void vlg_vdbg3(Vlg *vlg, const char *fmt, va_list ap)
742 {
743 if (vlg->out_dbg < 3)
744 return;
745
746 vlg__add_chk_flush(vlg, fmt, ap, LOG_DEBUG, TRUE);
747 }
748
749 /* ---------- ... ---------- */
vlg_abort(Vlg * vlg,const char * fmt,...)750 void vlg_abort(Vlg *vlg, const char *fmt, ... )
751 {
752 va_list ap;
753
754 va_start(ap, fmt);
755 vlg_vabort(vlg, fmt, ap);
756 va_end(ap);
757
758 ASSERT_NOT_REACHED();
759 }
760
vlg_err(Vlg * vlg,int exit_code,const char * fmt,...)761 void vlg_err(Vlg *vlg, int exit_code, const char *fmt, ... )
762 {
763 va_list ap;
764
765 va_start(ap, fmt);
766 vlg_verr(vlg, exit_code, fmt, ap);
767 va_end(ap);
768
769 ASSERT_NOT_REACHED();
770 }
771
vlg_warn(Vlg * vlg,const char * fmt,...)772 void vlg_warn(Vlg *vlg, const char *fmt, ... )
773 {
774 va_list ap;
775
776 va_start(ap, fmt);
777 vlg_vwarn(vlg, fmt, ap);
778 va_end(ap);
779 }
780
vlg_info(Vlg * vlg,const char * fmt,...)781 void vlg_info(Vlg *vlg, const char *fmt, ... )
782 {
783 va_list ap;
784
785 va_start(ap, fmt);
786 vlg_vinfo(vlg, fmt, ap);
787 va_end(ap);
788 }
789
vlg_dbg1(Vlg * vlg,const char * fmt,...)790 void vlg_dbg1(Vlg *vlg, const char *fmt, ... )
791 {
792 va_list ap;
793
794 va_start(ap, fmt);
795 vlg_vdbg1(vlg, fmt, ap);
796 va_end(ap);
797 }
798
vlg_dbg2(Vlg * vlg,const char * fmt,...)799 void vlg_dbg2(Vlg *vlg, const char *fmt, ... )
800 {
801 va_list ap;
802
803 va_start(ap, fmt);
804 vlg_vdbg2(vlg, fmt, ap);
805 va_end(ap);
806 }
807
vlg_dbg3(Vlg * vlg,const char * fmt,...)808 void vlg_dbg3(Vlg *vlg, const char *fmt, ... )
809 {
810 va_list ap;
811
812 va_start(ap, fmt);
813 vlg_vdbg3(vlg, fmt, ap);
814 va_end(ap);
815 }
816
817 /* ---------- signal ... ---------- */
818
819 static volatile sig_atomic_t vlg__in_signal = FALSE;
820
821 /* due to multiple signals hitting while we are inside vlg_*() we have
822 signal safe varients, that:
823
824 block all signals (apart from SEGV and ABRT)
825 do their thing
826 make sure they have flushed
827 restore signal mask
828
829 ...note that this does mean if we've crashed inside vlg, we are screwed
830 and just abort().
831 */
832
833 #define VLG__SIG_BLOCK_BEG() do { \
834 sigset_t oset; \
835 sigset_t nset; \
836 \
837 if (sigfillset(&nset) == -1) abort(); \
838 if (sigdelset(&nset, SIGSEGV) == -1) abort(); \
839 if (sigdelset(&nset, SIGABRT) == -1) abort(); \
840 if (sigprocmask(SIG_SETMASK, &nset, &oset) == -1) abort(); \
841 \
842 if (vlg__in_signal) abort(); \
843 else { \
844 Vstr_base *tmp = vlg->out_vstr; \
845 \
846 vlg__in_signal = TRUE; \
847 vlg->out_vstr = vlg->sig_out_vstr; \
848 vlg->sig_out_vstr = NULL; \
849 if (vlg->out_vstr->len) abort()
850
851 #define VLG__SIG_BLOCK_END() \
852 if (vlg->out_vstr->len) abort(); \
853 vlg->sig_out_vstr = vlg->out_vstr; \
854 vlg->out_vstr = tmp; \
855 vlg__in_signal = FALSE; \
856 } \
857 \
858 if (sigprocmask(SIG_SETMASK, &oset, NULL) == -1) abort(); \
859 } while (FALSE)
860
vlg_sig_abort(Vlg * vlg,const char * fmt,...)861 void vlg_sig_abort(Vlg *vlg, const char *fmt, ... )
862 {
863 va_list ap;
864
865 VLG__SIG_BLOCK_BEG();
866
867 va_start(ap, fmt);
868 vlg_vabort(vlg, fmt, ap);
869 va_end(ap);
870
871 VLG__SIG_BLOCK_END();
872
873 ASSERT_NOT_REACHED();
874 }
875
vlg_sig_err(Vlg * vlg,int exit_code,const char * fmt,...)876 void vlg_sig_err(Vlg *vlg, int exit_code, const char *fmt, ... )
877 {
878 va_list ap;
879
880 VLG__SIG_BLOCK_BEG();
881
882 va_start(ap, fmt);
883 vlg_verr(vlg, exit_code, fmt, ap);
884 va_end(ap);
885
886 VLG__SIG_BLOCK_END();
887
888 ASSERT_NOT_REACHED();
889 }
890
vlg_sig_warn(Vlg * vlg,const char * fmt,...)891 void vlg_sig_warn(Vlg *vlg, const char *fmt, ... )
892 {
893 va_list ap;
894
895 VLG__SIG_BLOCK_BEG();
896
897 va_start(ap, fmt);
898 vlg_vwarn(vlg, fmt, ap);
899 va_end(ap);
900
901 VLG__SIG_BLOCK_END();
902 }
903
vlg_sig_info(Vlg * vlg,const char * fmt,...)904 void vlg_sig_info(Vlg *vlg, const char *fmt, ... )
905 {
906 va_list ap;
907
908 VLG__SIG_BLOCK_BEG();
909
910 va_start(ap, fmt);
911 vlg_vinfo(vlg, fmt, ap);
912 va_end(ap);
913
914 VLG__SIG_BLOCK_END();
915 }
916
vlg_sig_dbg1(Vlg * vlg,const char * fmt,...)917 void vlg_sig_dbg1(Vlg *vlg, const char *fmt, ... )
918 {
919 va_list ap;
920
921 VLG__SIG_BLOCK_BEG();
922
923 va_start(ap, fmt);
924 vlg_vdbg1(vlg, fmt, ap);
925 va_end(ap);
926
927 VLG__SIG_BLOCK_END();
928 }
929
vlg_sig_dbg2(Vlg * vlg,const char * fmt,...)930 void vlg_sig_dbg2(Vlg *vlg, const char *fmt, ... )
931 {
932 va_list ap;
933
934 VLG__SIG_BLOCK_BEG();
935
936 va_start(ap, fmt);
937 vlg_vdbg2(vlg, fmt, ap);
938 va_end(ap);
939
940 VLG__SIG_BLOCK_END();
941 }
942
vlg_sig_dbg3(Vlg * vlg,const char * fmt,...)943 void vlg_sig_dbg3(Vlg *vlg, const char *fmt, ... )
944 {
945 va_list ap;
946
947 VLG__SIG_BLOCK_BEG();
948
949 va_start(ap, fmt);
950 vlg_vdbg3(vlg, fmt, ap);
951 va_end(ap);
952
953 VLG__SIG_BLOCK_END();
954 }
955
956