pjdlog.c revision 225736
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: stable/9/sbin/hastd/pjdlog.c 222087 2011-05-18 22:43:56Z 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
152	assert(pjdlog_initialized == PJDLOG_NEVER_INITIALIZED ||
153	    pjdlog_initialized == PJDLOG_NOT_INITIALIZED);
154	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
155
156	if (pjdlog_initialized == PJDLOG_NEVER_INITIALIZED) {
157		__use_xprintf = 1;
158		register_printf_render_std("T");
159		register_printf_render('N',
160		    pjdlog_printf_render_humanized_number,
161		    pjdlog_printf_arginfo_humanized_number);
162		register_printf_render('S',
163		    pjdlog_printf_render_sockaddr,
164		    pjdlog_printf_arginfo_sockaddr);
165	}
166
167	if (mode == PJDLOG_MODE_SYSLOG)
168		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
169	pjdlog_mode = mode;
170	pjdlog_debug_level = 0;
171	bzero(pjdlog_prefix, sizeof(pjdlog_prefix));
172
173	pjdlog_initialized = PJDLOG_INITIALIZED;
174}
175
176void
177pjdlog_fini(void)
178{
179
180	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
181
182	if (pjdlog_mode == PJDLOG_MODE_SYSLOG)
183		closelog();
184
185	pjdlog_initialized = PJDLOG_NOT_INITIALIZED;
186}
187
188/*
189 * Configure where the logs should go.
190 * By default they are send to stdout/stderr, but after going into background
191 * (eg. by calling daemon(3)) application is responsible for changing mode to
192 * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
193 */
194void
195pjdlog_mode_set(int mode)
196{
197
198	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
199	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
200
201	if (pjdlog_mode == mode)
202		return;
203
204	if (mode == PJDLOG_MODE_SYSLOG)
205		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
206	else /* if (mode == PJDLOG_MODE_STD) */
207		closelog();
208
209	pjdlog_mode = mode;
210}
211
212/*
213 * Return current mode.
214 */
215int
216pjdlog_mode_get(void)
217{
218
219	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
220
221	return (pjdlog_mode);
222}
223
224/*
225 * Set debug level. All the logs above the level specified here will be
226 * ignored.
227 */
228void
229pjdlog_debug_set(int level)
230{
231
232	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
233	assert(level >= 0);
234
235	pjdlog_debug_level = level;
236}
237
238/*
239 * Return current debug level.
240 */
241int
242pjdlog_debug_get(void)
243{
244
245	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
246
247	return (pjdlog_debug_level);
248}
249
250/*
251 * Set prefix that will be used before each log.
252 * Setting prefix to NULL will remove it.
253 */
254void
255pjdlog_prefix_set(const char *fmt, ...)
256{
257	va_list ap;
258
259	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
260
261	va_start(ap, fmt);
262	pjdlogv_prefix_set(fmt, ap);
263	va_end(ap);
264}
265
266/*
267 * Set prefix that will be used before each log.
268 * Setting prefix to NULL will remove it.
269 */
270void
271pjdlogv_prefix_set(const char *fmt, va_list ap)
272{
273
274	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
275	assert(fmt != NULL);
276
277	vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap);
278}
279
280/*
281 * Convert log level into string.
282 */
283static const char *
284pjdlog_level_string(int loglevel)
285{
286
287	switch (loglevel) {
288	case LOG_EMERG:
289		return ("EMERG");
290	case LOG_ALERT:
291		return ("ALERT");
292	case LOG_CRIT:
293		return ("CRIT");
294	case LOG_ERR:
295		return ("ERROR");
296	case LOG_WARNING:
297		return ("WARNING");
298	case LOG_NOTICE:
299		return ("NOTICE");
300	case LOG_INFO:
301		return ("INFO");
302	case LOG_DEBUG:
303		return ("DEBUG");
304	}
305	assert(!"Invalid log level.");
306	abort();	/* XXX: gcc */
307}
308
309/*
310 * Common log routine.
311 */
312void
313pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...)
314{
315	va_list ap;
316
317	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
318
319	va_start(ap, fmt);
320	pjdlogv_common(loglevel, debuglevel, error, fmt, ap);
321	va_end(ap);
322}
323
324/*
325 * Common log routine, which can handle regular log level as well as debug
326 * level. We decide here where to send the logs (stdout/stderr or syslog).
327 */
328void
329pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt,
330    va_list ap)
331{
332
333	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
334	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
335	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
336	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
337	    loglevel == LOG_INFO || loglevel == LOG_DEBUG);
338	assert(loglevel != LOG_DEBUG || debuglevel > 0);
339	assert(error >= -1);
340
341	/* Ignore debug above configured level. */
342	if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
343		return;
344
345	switch (pjdlog_mode) {
346	case PJDLOG_MODE_STD:
347	    {
348		FILE *out;
349
350		/*
351		 * We send errors and warning to stderr and the rest to stdout.
352		 */
353		switch (loglevel) {
354		case LOG_EMERG:
355		case LOG_ALERT:
356		case LOG_CRIT:
357		case LOG_ERR:
358		case LOG_WARNING:
359			out = stderr;
360			break;
361		case LOG_NOTICE:
362		case LOG_INFO:
363		case LOG_DEBUG:
364			out = stdout;
365			break;
366		default:
367			assert(!"Invalid loglevel.");
368			abort();	/* XXX: gcc */
369		}
370
371		fprintf(out, "[%s]", pjdlog_level_string(loglevel));
372		/* Attach debuglevel if this is debug log. */
373		if (loglevel == LOG_DEBUG)
374			fprintf(out, "[%d]", debuglevel);
375		fprintf(out, " %s", pjdlog_prefix);
376		vfprintf(out, fmt, ap);
377		if (error != -1)
378			fprintf(out, ": %s.", strerror(error));
379		fprintf(out, "\n");
380		fflush(out);
381		break;
382	    }
383	case PJDLOG_MODE_SYSLOG:
384	    {
385		char log[1024];
386		int len;
387
388		len = snprintf(log, sizeof(log), "%s", pjdlog_prefix);
389		if ((size_t)len < sizeof(log))
390			len += vsnprintf(log + len, sizeof(log) - len, fmt, ap);
391		if (error != -1 && (size_t)len < sizeof(log)) {
392			(void)snprintf(log + len, sizeof(log) - len, ": %s.",
393			    strerror(error));
394		}
395		syslog(loglevel, "%s", log);
396		break;
397	    }
398	default:
399		assert(!"Invalid mode.");
400	}
401}
402
403/*
404 * Regular logs.
405 */
406void
407pjdlogv(int loglevel, const char *fmt, va_list ap)
408{
409
410	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
411
412	/* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */
413	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
414	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
415	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
416	    loglevel == LOG_INFO);
417
418	pjdlogv_common(loglevel, 0, -1, fmt, ap);
419}
420
421/*
422 * Regular logs.
423 */
424void
425pjdlog(int loglevel, const char *fmt, ...)
426{
427	va_list ap;
428
429	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
430
431	va_start(ap, fmt);
432	pjdlogv(loglevel, fmt, ap);
433	va_end(ap);
434}
435
436/*
437 * Debug logs.
438 */
439void
440pjdlogv_debug(int debuglevel, const char *fmt, va_list ap)
441{
442
443	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
444
445	pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap);
446}
447
448/*
449 * Debug logs.
450 */
451void
452pjdlog_debug(int debuglevel, const char *fmt, ...)
453{
454	va_list ap;
455
456	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
457
458	va_start(ap, fmt);
459	pjdlogv_debug(debuglevel, fmt, ap);
460	va_end(ap);
461}
462
463/*
464 * Error logs with errno logging.
465 */
466void
467pjdlogv_errno(int loglevel, const char *fmt, va_list ap)
468{
469
470	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
471
472	pjdlogv_common(loglevel, 0, errno, fmt, ap);
473}
474
475/*
476 * Error logs with errno logging.
477 */
478void
479pjdlog_errno(int loglevel, const char *fmt, ...)
480{
481	va_list ap;
482
483	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
484
485	va_start(ap, fmt);
486	pjdlogv_errno(loglevel, fmt, ap);
487	va_end(ap);
488}
489
490/*
491 * Log error, errno and exit.
492 */
493void
494pjdlogv_exit(int exitcode, const char *fmt, va_list ap)
495{
496
497	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
498
499	pjdlogv_errno(LOG_ERR, fmt, ap);
500	exit(exitcode);
501	/* NOTREACHED */
502}
503
504/*
505 * Log error, errno and exit.
506 */
507void
508pjdlog_exit(int exitcode, const char *fmt, ...)
509{
510	va_list ap;
511
512	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
513
514	va_start(ap, fmt);
515	pjdlogv_exit(exitcode, fmt, ap);
516	/* NOTREACHED */
517	va_end(ap);
518}
519
520/*
521 * Log error and exit.
522 */
523void
524pjdlogv_exitx(int exitcode, const char *fmt, va_list ap)
525{
526
527	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
528
529	pjdlogv(LOG_ERR, fmt, ap);
530	exit(exitcode);
531	/* NOTREACHED */
532}
533
534/*
535 * Log error and exit.
536 */
537void
538pjdlog_exitx(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_exitx(exitcode, fmt, ap);
546	/* NOTREACHED */
547	va_end(ap);
548}
549
550/*
551 * Log failure message and exit.
552 */
553void
554pjdlog_abort(const char *func, const char *file, int line,
555    const char *failedexpr, const char *fmt, ...)
556{
557	va_list ap;
558
559	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
560
561	/*
562	 * When there is no message we pass __func__ as 'fmt'.
563	 * It would be cleaner to pass NULL or "", but gcc generates a warning
564	 * for both of those.
565	 */
566	if (fmt != func) {
567		va_start(ap, fmt);
568		pjdlogv_critical(fmt, ap);
569		va_end(ap);
570	}
571	if (failedexpr == NULL) {
572		if (func == NULL) {
573			pjdlog_critical("Aborted at file %s, line %d.", file,
574			    line);
575		} else {
576			pjdlog_critical("Aborted at function %s, file %s, line %d.",
577			    func, file, line);
578		}
579	} else {
580		if (func == NULL) {
581			pjdlog_critical("Assertion failed: (%s), file %s, line %d.",
582			    failedexpr, file, line);
583		} else {
584			pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.",
585			    failedexpr, func, file, line);
586		}
587	}
588	abort();
589}
590