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