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