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