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