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