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