xref: /freebsd/sbin/hastd/pjdlog.c (revision 5f4c09dd85bff675e0ca63c55ea3c517e0fddfcc)
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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