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