pjdlog.c revision 293161
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
56static int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED;
57static int pjdlog_mode, pjdlog_debug_level;
58static char pjdlog_prefix[128];
59
60#ifdef __FreeBSD__
61static int
62pjdlog_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
71static int
72pjdlog_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
87static int
88pjdlog_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
97static int
98pjdlog_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
150void
151pjdlog_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
185void
186pjdlog_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 */
208void
209pjdlog_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 */
234int
235pjdlog_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 */
247void
248pjdlog_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 */
260int
261pjdlog_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 */
273void
274pjdlog_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 */
289void
290pjdlogv_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 */
307static const char *
308pjdlog_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 */
336void
337pjdlog_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 */
352void
353pjdlogv_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 */
436void
437pjdlogv(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 */
454void
455pjdlog(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 */
469void
470pjdlogv_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 */
481void
482pjdlog_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 */
496void
497pjdlogv_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 */
508void
509pjdlog_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 */
523void
524pjdlogv_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 */
537void
538pjdlog_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 */
553void
554pjdlogv_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 */
567void
568pjdlog_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 */
583void
584pjdlog_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