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