]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sbin/hastd/pjdlog.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / sbin / hastd / pjdlog.c
1 /*-
2  * Copyright (c) 2009-2010 The FreeBSD Foundation
3  * Copyright (c) 2011 Pawel Jakub Dawidek <pjd@FreeBSD.org>
4  * All rights reserved.
5  *
6  * This software was developed by Pawel Jakub Dawidek under sponsorship from
7  * the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38
39 #include <assert.h>
40 #include <errno.h>
41 #include <libutil.h>
42 #include <printf.h>
43 #include <stdarg.h>
44 #include <stdint.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <syslog.h>
49
50 #include "pjdlog.h"
51
52 #define PJDLOG_NEVER_INITIALIZED        0
53 #define PJDLOG_NOT_INITIALIZED          1
54 #define PJDLOG_INITIALIZED              2
55
56 static int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED;
57 static int pjdlog_mode, pjdlog_debug_level;
58 static char pjdlog_prefix[128];
59
60 static int
61 pjdlog_printf_arginfo_humanized_number(const struct printf_info *pi __unused,
62     size_t n, int *argt)
63 {
64
65         assert(n >= 1);
66         argt[0] = PA_INT | PA_FLAG_INTMAX;
67         return (1);
68 }
69
70 static int
71 pjdlog_printf_render_humanized_number(struct __printf_io *io,
72     const struct printf_info *pi, const void * const *arg)
73 {
74         char buf[5];
75         intmax_t num;
76         int ret;
77
78         num = *(const intmax_t *)arg[0];
79         humanize_number(buf, sizeof(buf), (int64_t)num, "", HN_AUTOSCALE,
80             HN_NOSPACE | HN_DECIMAL);
81         ret = __printf_out(io, pi, buf, strlen(buf));
82         __printf_flush(io);
83         return (ret);
84 }
85
86 static int
87 pjdlog_printf_arginfo_sockaddr(const struct printf_info *pi __unused,
88     size_t n, int *argt)
89 {
90
91         assert(n >= 1);
92         argt[0] = PA_POINTER;
93         return (1);
94 }
95
96 static int
97 pjdlog_printf_render_sockaddr(struct __printf_io *io,
98     const struct printf_info *pi, const void * const *arg)
99 {
100         const struct sockaddr_storage *ss;
101         char buf[64];
102         int ret;
103
104         ss = *(const struct sockaddr_storage * const *)arg[0];
105         switch (ss->ss_family) {
106         case AF_INET:
107             {
108                 char addr[INET_ADDRSTRLEN];
109                 const struct sockaddr_in *sin;
110                 unsigned int port;
111
112                 sin = (const struct sockaddr_in *)ss;
113                 port = ntohs(sin->sin_port);
114                 if (inet_ntop(ss->ss_family, &sin->sin_addr, addr,
115                     sizeof(addr)) == NULL) {
116                         PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.",
117                             strerror(errno));
118                 }
119                 snprintf(buf, sizeof(buf), "%s:%u", addr, port);
120                 break;
121             }
122         case AF_INET6:
123             {
124                 char addr[INET6_ADDRSTRLEN];
125                 const struct sockaddr_in6 *sin;
126                 unsigned int port;
127
128                 sin = (const struct sockaddr_in6 *)ss;
129                 port = ntohs(sin->sin6_port);
130                 if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr,
131                     sizeof(addr)) == NULL) {
132                         PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.",
133                             strerror(errno));
134                 }
135                 snprintf(buf, sizeof(buf), "[%s]:%u", addr, port);
136                 break;
137             }
138         default:
139                 snprintf(buf, sizeof(buf), "[unsupported family %hhu]",
140                     ss->ss_family);
141                 break;
142         }
143         ret = __printf_out(io, pi, buf, strlen(buf));
144         __printf_flush(io);
145         return (ret);
146 }
147
148 void
149 pjdlog_init(int mode)
150 {
151
152         assert(pjdlog_initialized == PJDLOG_NEVER_INITIALIZED ||
153             pjdlog_initialized == PJDLOG_NOT_INITIALIZED);
154         assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
155
156         if (pjdlog_initialized == PJDLOG_NEVER_INITIALIZED) {
157                 __use_xprintf = 1;
158                 register_printf_render_std("T");
159                 register_printf_render('N',
160                     pjdlog_printf_render_humanized_number,
161                     pjdlog_printf_arginfo_humanized_number);
162                 register_printf_render('S',
163                     pjdlog_printf_render_sockaddr,
164                     pjdlog_printf_arginfo_sockaddr);
165         }
166
167         if (mode == PJDLOG_MODE_SYSLOG)
168                 openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
169         pjdlog_mode = mode;
170         pjdlog_debug_level = 0;
171         bzero(pjdlog_prefix, sizeof(pjdlog_prefix));
172
173         pjdlog_initialized = PJDLOG_INITIALIZED;
174 }
175
176 void
177 pjdlog_fini(void)
178 {
179
180         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
181
182         if (pjdlog_mode == PJDLOG_MODE_SYSLOG)
183                 closelog();
184
185         pjdlog_initialized = PJDLOG_NOT_INITIALIZED;
186 }
187
188 /*
189  * Configure where the logs should go.
190  * By default they are send to stdout/stderr, but after going into background
191  * (eg. by calling daemon(3)) application is responsible for changing mode to
192  * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
193  */
194 void
195 pjdlog_mode_set(int mode)
196 {
197
198         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
199         assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
200
201         if (pjdlog_mode == mode)
202                 return;
203
204         if (mode == PJDLOG_MODE_SYSLOG)
205                 openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
206         else /* if (mode == PJDLOG_MODE_STD) */
207                 closelog();
208
209         pjdlog_mode = mode;
210 }
211
212 /*
213  * Return current mode.
214  */
215 int
216 pjdlog_mode_get(void)
217 {
218
219         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
220
221         return (pjdlog_mode);
222 }
223
224 /*
225  * Set debug level. All the logs above the level specified here will be
226  * ignored.
227  */
228 void
229 pjdlog_debug_set(int level)
230 {
231
232         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
233         assert(level >= 0);
234
235         pjdlog_debug_level = level;
236 }
237
238 /*
239  * Return current debug level.
240  */
241 int
242 pjdlog_debug_get(void)
243 {
244
245         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
246
247         return (pjdlog_debug_level);
248 }
249
250 /*
251  * Set prefix that will be used before each log.
252  * Setting prefix to NULL will remove it.
253  */
254 void
255 pjdlog_prefix_set(const char *fmt, ...)
256 {
257         va_list ap;
258
259         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
260
261         va_start(ap, fmt);
262         pjdlogv_prefix_set(fmt, ap);
263         va_end(ap);
264 }
265
266 /*
267  * Set prefix that will be used before each log.
268  * Setting prefix to NULL will remove it.
269  */
270 void
271 pjdlogv_prefix_set(const char *fmt, va_list ap)
272 {
273
274         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
275         assert(fmt != NULL);
276
277         vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap);
278 }
279
280 /*
281  * Convert log level into string.
282  */
283 static const char *
284 pjdlog_level_string(int loglevel)
285 {
286
287         switch (loglevel) {
288         case LOG_EMERG:
289                 return ("EMERG");
290         case LOG_ALERT:
291                 return ("ALERT");
292         case LOG_CRIT:
293                 return ("CRIT");
294         case LOG_ERR:
295                 return ("ERROR");
296         case LOG_WARNING:
297                 return ("WARNING");
298         case LOG_NOTICE:
299                 return ("NOTICE");
300         case LOG_INFO:
301                 return ("INFO");
302         case LOG_DEBUG:
303                 return ("DEBUG");
304         }
305         assert(!"Invalid log level.");
306         abort();        /* XXX: gcc */
307 }
308
309 /*
310  * Common log routine.
311  */
312 void
313 pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...)
314 {
315         va_list ap;
316
317         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
318
319         va_start(ap, fmt);
320         pjdlogv_common(loglevel, debuglevel, error, fmt, ap);
321         va_end(ap);
322 }
323
324 /*
325  * Common log routine, which can handle regular log level as well as debug
326  * level. We decide here where to send the logs (stdout/stderr or syslog).
327  */
328 void
329 pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt,
330     va_list ap)
331 {
332
333         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
334         assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
335             loglevel == LOG_CRIT || loglevel == LOG_ERR ||
336             loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
337             loglevel == LOG_INFO || loglevel == LOG_DEBUG);
338         assert(loglevel != LOG_DEBUG || debuglevel > 0);
339         assert(error >= -1);
340
341         /* Ignore debug above configured level. */
342         if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
343                 return;
344
345         switch (pjdlog_mode) {
346         case PJDLOG_MODE_STD:
347             {
348                 FILE *out;
349
350                 /*
351                  * We send errors and warning to stderr and the rest to stdout.
352                  */
353                 switch (loglevel) {
354                 case LOG_EMERG:
355                 case LOG_ALERT:
356                 case LOG_CRIT:
357                 case LOG_ERR:
358                 case LOG_WARNING:
359                         out = stderr;
360                         break;
361                 case LOG_NOTICE:
362                 case LOG_INFO:
363                 case LOG_DEBUG:
364                         out = stdout;
365                         break;
366                 default:
367                         assert(!"Invalid loglevel.");
368                         abort();        /* XXX: gcc */
369                 }
370
371                 fprintf(out, "[%s]", pjdlog_level_string(loglevel));
372                 /* Attach debuglevel if this is debug log. */
373                 if (loglevel == LOG_DEBUG)
374                         fprintf(out, "[%d]", debuglevel);
375                 fprintf(out, " %s", pjdlog_prefix);
376                 vfprintf(out, fmt, ap);
377                 if (error != -1)
378                         fprintf(out, ": %s.", strerror(error));
379                 fprintf(out, "\n");
380                 fflush(out);
381                 break;
382             }
383         case PJDLOG_MODE_SYSLOG:
384             {
385                 char log[1024];
386                 int len;
387
388                 len = snprintf(log, sizeof(log), "%s", pjdlog_prefix);
389                 if ((size_t)len < sizeof(log))
390                         len += vsnprintf(log + len, sizeof(log) - len, fmt, ap);
391                 if (error != -1 && (size_t)len < sizeof(log)) {
392                         (void)snprintf(log + len, sizeof(log) - len, ": %s.",
393                             strerror(error));
394                 }
395                 syslog(loglevel, "%s", log);
396                 break;
397             }
398         default:
399                 assert(!"Invalid mode.");
400         }
401 }
402
403 /*
404  * Regular logs.
405  */
406 void
407 pjdlogv(int loglevel, const char *fmt, va_list ap)
408 {
409
410         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
411
412         /* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */
413         assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
414             loglevel == LOG_CRIT || loglevel == LOG_ERR ||
415             loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
416             loglevel == LOG_INFO);
417
418         pjdlogv_common(loglevel, 0, -1, fmt, ap);
419 }
420
421 /*
422  * Regular logs.
423  */
424 void
425 pjdlog(int loglevel, const char *fmt, ...)
426 {
427         va_list ap;
428
429         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
430
431         va_start(ap, fmt);
432         pjdlogv(loglevel, fmt, ap);
433         va_end(ap);
434 }
435
436 /*
437  * Debug logs.
438  */
439 void
440 pjdlogv_debug(int debuglevel, const char *fmt, va_list ap)
441 {
442
443         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
444
445         pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap);
446 }
447
448 /*
449  * Debug logs.
450  */
451 void
452 pjdlog_debug(int debuglevel, const char *fmt, ...)
453 {
454         va_list ap;
455
456         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
457
458         va_start(ap, fmt);
459         pjdlogv_debug(debuglevel, fmt, ap);
460         va_end(ap);
461 }
462
463 /*
464  * Error logs with errno logging.
465  */
466 void
467 pjdlogv_errno(int loglevel, const char *fmt, va_list ap)
468 {
469
470         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
471
472         pjdlogv_common(loglevel, 0, errno, fmt, ap);
473 }
474
475 /*
476  * Error logs with errno logging.
477  */
478 void
479 pjdlog_errno(int loglevel, const char *fmt, ...)
480 {
481         va_list ap;
482
483         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
484
485         va_start(ap, fmt);
486         pjdlogv_errno(loglevel, fmt, ap);
487         va_end(ap);
488 }
489
490 /*
491  * Log error, errno and exit.
492  */
493 void
494 pjdlogv_exit(int exitcode, const char *fmt, va_list ap)
495 {
496
497         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
498
499         pjdlogv_errno(LOG_ERR, fmt, ap);
500         exit(exitcode);
501         /* NOTREACHED */
502 }
503
504 /*
505  * Log error, errno and exit.
506  */
507 void
508 pjdlog_exit(int exitcode, const char *fmt, ...)
509 {
510         va_list ap;
511
512         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
513
514         va_start(ap, fmt);
515         pjdlogv_exit(exitcode, fmt, ap);
516         /* NOTREACHED */
517         va_end(ap);
518 }
519
520 /*
521  * Log error and exit.
522  */
523 void
524 pjdlogv_exitx(int exitcode, const char *fmt, va_list ap)
525 {
526
527         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
528
529         pjdlogv(LOG_ERR, fmt, ap);
530         exit(exitcode);
531         /* NOTREACHED */
532 }
533
534 /*
535  * Log error and exit.
536  */
537 void
538 pjdlog_exitx(int exitcode, const char *fmt, ...)
539 {
540         va_list ap;
541
542         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
543
544         va_start(ap, fmt);
545         pjdlogv_exitx(exitcode, fmt, ap);
546         /* NOTREACHED */
547         va_end(ap);
548 }
549
550 /*
551  * Log failure message and exit.
552  */
553 void
554 pjdlog_abort(const char *func, const char *file, int line,
555     const char *failedexpr, const char *fmt, ...)
556 {
557         va_list ap;
558
559         assert(pjdlog_initialized == PJDLOG_INITIALIZED);
560
561         /*
562          * When there is no message we pass __func__ as 'fmt'.
563          * It would be cleaner to pass NULL or "", but gcc generates a warning
564          * for both of those.
565          */
566         if (fmt != func) {
567                 va_start(ap, fmt);
568                 pjdlogv_critical(fmt, ap);
569                 va_end(ap);
570         }
571         if (failedexpr == NULL) {
572                 if (func == NULL) {
573                         pjdlog_critical("Aborted at file %s, line %d.", file,
574                             line);
575                 } else {
576                         pjdlog_critical("Aborted at function %s, file %s, line %d.",
577                             func, file, line);
578                 }
579         } else {
580                 if (func == NULL) {
581                         pjdlog_critical("Assertion failed: (%s), file %s, line %d.",
582                             failedexpr, file, line);
583                 } else {
584                         pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.",
585                             failedexpr, func, file, line);
586                 }
587         }
588         abort();
589 }