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