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