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