xref: /freebsd/sbin/hastd/pjdlog.c (revision 891b8ed4672a213bbe6f3f10522eeadb34d01b76)
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/socket.h>
35 #include <netinet/in.h>
36 
37 #include <assert.h>
38 #include <errno.h>
39 #include <libutil.h>
40 #include <printf.h>
41 #include <stdarg.h>
42 #include <stdint.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <syslog.h>
47 
48 #include "pjdlog.h"
49 
50 #define	PJDLOG_NEVER_INITIALIZED	0
51 #define	PJDLOG_NOT_INITIALIZED		1
52 #define	PJDLOG_INITIALIZED		2
53 
54 static int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED;
55 static int pjdlog_mode, pjdlog_debug_level;
56 static char pjdlog_prefix[128];
57 
58 static int
59 pjdlog_printf_arginfo_humanized_number(const struct printf_info *pi __unused,
60     size_t n, int *argt)
61 {
62 
63 	assert(n >= 1);
64 	argt[0] = PA_INT | PA_FLAG_INTMAX;
65 	return (1);
66 }
67 
68 static int
69 pjdlog_printf_render_humanized_number(struct __printf_io *io,
70     const struct printf_info *pi, const void * const *arg)
71 {
72 	char buf[5];
73 	intmax_t num;
74 	int ret;
75 
76 	num = *(const intmax_t *)arg[0];
77 	humanize_number(buf, sizeof(buf), (int64_t)num, "", HN_AUTOSCALE,
78 	    HN_NOSPACE | HN_DECIMAL);
79 	ret = __printf_out(io, pi, buf, strlen(buf));
80 	__printf_flush(io);
81 	return (ret);
82 }
83 
84 static int
85 pjdlog_printf_arginfo_sockaddr(const struct printf_info *pi __unused,
86     size_t n, int *argt)
87 {
88 
89 	assert(n >= 1);
90 	argt[0] = PA_POINTER;
91 	return (1);
92 }
93 
94 static int
95 pjdlog_printf_render_sockaddr(struct __printf_io *io,
96     const struct printf_info *pi, const void * const *arg)
97 {
98 	const struct sockaddr_storage *ss;
99 	char buf[64];
100 	int ret;
101 
102 	ss = *(const struct sockaddr_storage * const *)arg[0];
103 	switch (ss->ss_family) {
104 	case AF_INET:
105 	    {
106 		const struct sockaddr_in *sin;
107 		in_addr_t ip;
108 		unsigned int port;
109 
110 		sin = (const struct sockaddr_in *)ss;
111 		ip = ntohl(sin->sin_addr.s_addr);
112 		port = ntohs(sin->sin_port);
113 
114 		snprintf(buf, sizeof(buf), "%u.%u.%u.%u:%u",
115 		    ((ip >> 24) & 0xff), ((ip >> 16) & 0xff),
116 		    ((ip >> 8) & 0xff), (ip & 0xff), port);
117 		break;
118 	    }
119 	default:
120 		snprintf(buf, sizeof(buf), "[unsupported family %u]",
121 		    (unsigned int)ss->ss_family);
122 		break;
123 	}
124 	ret = __printf_out(io, pi, buf, strlen(buf));
125 	__printf_flush(io);
126 	return (ret);
127 }
128 
129 void
130 pjdlog_init(int mode)
131 {
132 
133 	assert(pjdlog_initialized == PJDLOG_NEVER_INITIALIZED ||
134 	    pjdlog_initialized == PJDLOG_NOT_INITIALIZED);
135 	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
136 
137 	if (pjdlog_initialized == PJDLOG_NEVER_INITIALIZED) {
138 		__use_xprintf = 1;
139 		register_printf_render_std("T");
140 		register_printf_render('N',
141 		    pjdlog_printf_render_humanized_number,
142 		    pjdlog_printf_arginfo_humanized_number);
143 		register_printf_render('S',
144 		    pjdlog_printf_render_sockaddr,
145 		    pjdlog_printf_arginfo_sockaddr);
146 	}
147 
148 	if (mode == PJDLOG_MODE_SYSLOG)
149 		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
150 	pjdlog_mode = mode;
151 	pjdlog_debug_level = 0;
152 	bzero(pjdlog_prefix, sizeof(pjdlog_prefix));
153 
154 	pjdlog_initialized = PJDLOG_INITIALIZED;
155 }
156 
157 void
158 pjdlog_fini(void)
159 {
160 
161 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
162 
163 	if (pjdlog_mode == PJDLOG_MODE_SYSLOG)
164 		closelog();
165 
166 	pjdlog_initialized = PJDLOG_NOT_INITIALIZED;
167 }
168 
169 /*
170  * Configure where the logs should go.
171  * By default they are send to stdout/stderr, but after going into background
172  * (eg. by calling daemon(3)) application is responsible for changing mode to
173  * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
174  */
175 void
176 pjdlog_mode_set(int mode)
177 {
178 
179 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
180 	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
181 
182 	if (pjdlog_mode == mode)
183 		return;
184 
185 	if (mode == PJDLOG_MODE_SYSLOG)
186 		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
187 	else /* if (mode == PJDLOG_MODE_STD) */
188 		closelog();
189 
190 	pjdlog_mode = mode;
191 }
192 
193 /*
194  * Return current mode.
195  */
196 int
197 pjdlog_mode_get(void)
198 {
199 
200 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
201 
202 	return (pjdlog_mode);
203 }
204 
205 /*
206  * Set debug level. All the logs above the level specified here will be
207  * ignored.
208  */
209 void
210 pjdlog_debug_set(int level)
211 {
212 
213 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
214 	assert(level >= 0);
215 
216 	pjdlog_debug_level = level;
217 }
218 
219 /*
220  * Return current debug level.
221  */
222 int
223 pjdlog_debug_get(void)
224 {
225 
226 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
227 
228 	return (pjdlog_debug_level);
229 }
230 
231 /*
232  * Set prefix that will be used before each log.
233  * Setting prefix to NULL will remove it.
234  */
235 void
236 pjdlog_prefix_set(const char *fmt, ...)
237 {
238 	va_list ap;
239 
240 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
241 
242 	va_start(ap, fmt);
243 	pjdlogv_prefix_set(fmt, ap);
244 	va_end(ap);
245 }
246 
247 /*
248  * Set prefix that will be used before each log.
249  * Setting prefix to NULL will remove it.
250  */
251 void
252 pjdlogv_prefix_set(const char *fmt, va_list ap)
253 {
254 
255 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
256 	assert(fmt != NULL);
257 
258 	vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap);
259 }
260 
261 /*
262  * Convert log level into string.
263  */
264 static const char *
265 pjdlog_level_string(int loglevel)
266 {
267 
268 	switch (loglevel) {
269 	case LOG_EMERG:
270 		return ("EMERG");
271 	case LOG_ALERT:
272 		return ("ALERT");
273 	case LOG_CRIT:
274 		return ("CRIT");
275 	case LOG_ERR:
276 		return ("ERROR");
277 	case LOG_WARNING:
278 		return ("WARNING");
279 	case LOG_NOTICE:
280 		return ("NOTICE");
281 	case LOG_INFO:
282 		return ("INFO");
283 	case LOG_DEBUG:
284 		return ("DEBUG");
285 	}
286 	assert(!"Invalid log level.");
287 	abort();	/* XXX: gcc */
288 }
289 
290 /*
291  * Common log routine.
292  */
293 void
294 pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...)
295 {
296 	va_list ap;
297 
298 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
299 
300 	va_start(ap, fmt);
301 	pjdlogv_common(loglevel, debuglevel, error, fmt, ap);
302 	va_end(ap);
303 }
304 
305 /*
306  * Common log routine, which can handle regular log level as well as debug
307  * level. We decide here where to send the logs (stdout/stderr or syslog).
308  */
309 void
310 pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt,
311     va_list ap)
312 {
313 
314 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
315 	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
316 	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
317 	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
318 	    loglevel == LOG_INFO || loglevel == LOG_DEBUG);
319 	assert(loglevel != LOG_DEBUG || debuglevel > 0);
320 	assert(error >= -1);
321 
322 	/* Ignore debug above configured level. */
323 	if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
324 		return;
325 
326 	switch (pjdlog_mode) {
327 	case PJDLOG_MODE_STD:
328 	    {
329 		FILE *out;
330 
331 		/*
332 		 * We send errors and warning to stderr and the rest to stdout.
333 		 */
334 		switch (loglevel) {
335 		case LOG_EMERG:
336 		case LOG_ALERT:
337 		case LOG_CRIT:
338 		case LOG_ERR:
339 		case LOG_WARNING:
340 			out = stderr;
341 			break;
342 		case LOG_NOTICE:
343 		case LOG_INFO:
344 		case LOG_DEBUG:
345 			out = stdout;
346 			break;
347 		default:
348 			assert(!"Invalid loglevel.");
349 			abort();	/* XXX: gcc */
350 		}
351 
352 		fprintf(out, "[%s]", pjdlog_level_string(loglevel));
353 		/* Attach debuglevel if this is debug log. */
354 		if (loglevel == LOG_DEBUG)
355 			fprintf(out, "[%d]", debuglevel);
356 		fprintf(out, " %s", pjdlog_prefix);
357 		vfprintf(out, fmt, ap);
358 		if (error != -1)
359 			fprintf(out, ": %s.", strerror(error));
360 		fprintf(out, "\n");
361 		fflush(out);
362 		break;
363 	    }
364 	case PJDLOG_MODE_SYSLOG:
365 	    {
366 		char log[1024];
367 		int len;
368 
369 		len = snprintf(log, sizeof(log), "%s", pjdlog_prefix);
370 		if ((size_t)len < sizeof(log))
371 			len += vsnprintf(log + len, sizeof(log) - len, fmt, ap);
372 		if (error != -1 && (size_t)len < sizeof(log)) {
373 			(void)snprintf(log + len, sizeof(log) - len, ": %s.",
374 			    strerror(error));
375 		}
376 		syslog(loglevel, "%s", log);
377 		break;
378 	    }
379 	default:
380 		assert(!"Invalid mode.");
381 	}
382 }
383 
384 /*
385  * Regular logs.
386  */
387 void
388 pjdlogv(int loglevel, const char *fmt, va_list ap)
389 {
390 
391 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
392 
393 	/* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */
394 	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
395 	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
396 	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
397 	    loglevel == LOG_INFO);
398 
399 	pjdlogv_common(loglevel, 0, -1, fmt, ap);
400 }
401 
402 /*
403  * Regular logs.
404  */
405 void
406 pjdlog(int loglevel, const char *fmt, ...)
407 {
408 	va_list ap;
409 
410 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
411 
412 	va_start(ap, fmt);
413 	pjdlogv(loglevel, fmt, ap);
414 	va_end(ap);
415 }
416 
417 /*
418  * Debug logs.
419  */
420 void
421 pjdlogv_debug(int debuglevel, const char *fmt, va_list ap)
422 {
423 
424 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
425 
426 	pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap);
427 }
428 
429 /*
430  * Debug logs.
431  */
432 void
433 pjdlog_debug(int debuglevel, const char *fmt, ...)
434 {
435 	va_list ap;
436 
437 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
438 
439 	va_start(ap, fmt);
440 	pjdlogv_debug(debuglevel, fmt, ap);
441 	va_end(ap);
442 }
443 
444 /*
445  * Error logs with errno logging.
446  */
447 void
448 pjdlogv_errno(int loglevel, const char *fmt, va_list ap)
449 {
450 
451 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
452 
453 	pjdlogv_common(loglevel, 0, errno, fmt, ap);
454 }
455 
456 /*
457  * Error logs with errno logging.
458  */
459 void
460 pjdlog_errno(int loglevel, const char *fmt, ...)
461 {
462 	va_list ap;
463 
464 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
465 
466 	va_start(ap, fmt);
467 	pjdlogv_errno(loglevel, fmt, ap);
468 	va_end(ap);
469 }
470 
471 /*
472  * Log error, errno and exit.
473  */
474 void
475 pjdlogv_exit(int exitcode, const char *fmt, va_list ap)
476 {
477 
478 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
479 
480 	pjdlogv_errno(LOG_ERR, fmt, ap);
481 	exit(exitcode);
482 	/* NOTREACHED */
483 }
484 
485 /*
486  * Log error, errno and exit.
487  */
488 void
489 pjdlog_exit(int exitcode, const char *fmt, ...)
490 {
491 	va_list ap;
492 
493 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
494 
495 	va_start(ap, fmt);
496 	pjdlogv_exit(exitcode, fmt, ap);
497 	/* NOTREACHED */
498 	va_end(ap);
499 }
500 
501 /*
502  * Log error and exit.
503  */
504 void
505 pjdlogv_exitx(int exitcode, const char *fmt, va_list ap)
506 {
507 
508 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
509 
510 	pjdlogv(LOG_ERR, fmt, ap);
511 	exit(exitcode);
512 	/* NOTREACHED */
513 }
514 
515 /*
516  * Log error and exit.
517  */
518 void
519 pjdlog_exitx(int exitcode, const char *fmt, ...)
520 {
521 	va_list ap;
522 
523 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
524 
525 	va_start(ap, fmt);
526 	pjdlogv_exitx(exitcode, fmt, ap);
527 	/* NOTREACHED */
528 	va_end(ap);
529 }
530 
531 /*
532  * Log failure message and exit.
533  */
534 void
535 pjdlog_abort(const char *func, const char *file, int line,
536     const char *failedexpr, const char *fmt, ...)
537 {
538 	va_list ap;
539 
540 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
541 
542 	/*
543 	 * When there is no message we pass __func__ as 'fmt'.
544 	 * It would be cleaner to pass NULL or "", but gcc generates a warning
545 	 * for both of those.
546 	 */
547 	if (fmt != func) {
548 		va_start(ap, fmt);
549 		pjdlogv_critical(fmt, ap);
550 		va_end(ap);
551 	}
552 	if (failedexpr == NULL) {
553 		if (func == NULL) {
554 			pjdlog_critical("Aborted at file %s, line %d.", file,
555 			    line);
556 		} else {
557 			pjdlog_critical("Aborted at function %s, file %s, line %d.",
558 			    func, file, line);
559 		}
560 	} else {
561 		if (func == NULL) {
562 			pjdlog_critical("Assertion failed: (%s), file %s, line %d.",
563 			    failedexpr, file, line);
564 		} else {
565 			pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.",
566 			    failedexpr, func, file, line);
567 		}
568 	}
569 	abort();
570 }
571