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