pjdlog.c revision 219369
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 219369 2011-03-07 10:33:52Z pjd $");
33
34#include <assert.h>
35#include <errno.h>
36#include <stdarg.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <syslog.h>
41
42#include "pjdlog.h"
43
44#define	PJDLOG_NEVER_INITIALIZED	0
45#define	PJDLOG_NOT_INITIALIZED		1
46#define	PJDLOG_INITIALIZED		2
47
48static int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED;
49static int pjdlog_mode, pjdlog_debug_level;
50static char pjdlog_prefix[128];
51
52void
53pjdlog_init(int mode)
54{
55
56	assert(pjdlog_initialized == PJDLOG_NEVER_INITIALIZED ||
57	    pjdlog_initialized == PJDLOG_NOT_INITIALIZED);
58	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
59
60	if (mode == PJDLOG_MODE_SYSLOG)
61		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
62	pjdlog_mode = mode;
63	pjdlog_debug_level = 0;
64	bzero(pjdlog_prefix, sizeof(pjdlog_prefix));
65
66	pjdlog_initialized = PJDLOG_INITIALIZED;
67}
68
69void
70pjdlog_fini(void)
71{
72
73	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
74
75	if (pjdlog_mode == PJDLOG_MODE_SYSLOG)
76		closelog();
77
78	pjdlog_initialized = PJDLOG_NOT_INITIALIZED;
79}
80
81/*
82 * Configure where the logs should go.
83 * By default they are send to stdout/stderr, but after going into background
84 * (eg. by calling daemon(3)) application is responsible for changing mode to
85 * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
86 */
87void
88pjdlog_mode_set(int mode)
89{
90
91	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
92	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
93
94	if (pjdlog_mode == mode)
95		return;
96
97	if (mode == PJDLOG_MODE_SYSLOG)
98		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
99	else /* if (mode == PJDLOG_MODE_STD) */
100		closelog();
101
102	pjdlog_mode = mode;
103}
104
105/*
106 * Return current mode.
107 */
108int
109pjdlog_mode_get(void)
110{
111
112	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
113
114	return (pjdlog_mode);
115}
116
117/*
118 * Set debug level. All the logs above the level specified here will be
119 * ignored.
120 */
121void
122pjdlog_debug_set(int level)
123{
124
125	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
126	assert(level >= 0);
127
128	pjdlog_debug_level = level;
129}
130
131/*
132 * Return current debug level.
133 */
134int
135pjdlog_debug_get(void)
136{
137
138	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
139
140	return (pjdlog_debug_level);
141}
142
143/*
144 * Set prefix that will be used before each log.
145 * Setting prefix to NULL will remove it.
146 */
147void
148pjdlog_prefix_set(const char *fmt, ...)
149{
150	va_list ap;
151
152	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
153
154	va_start(ap, fmt);
155	pjdlogv_prefix_set(fmt, ap);
156	va_end(ap);
157}
158
159/*
160 * Set prefix that will be used before each log.
161 * Setting prefix to NULL will remove it.
162 */
163void
164pjdlogv_prefix_set(const char *fmt, va_list ap)
165{
166
167	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
168	assert(fmt != NULL);
169
170	vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap);
171}
172
173/*
174 * Convert log level into string.
175 */
176static const char *
177pjdlog_level_string(int loglevel)
178{
179
180	switch (loglevel) {
181	case LOG_EMERG:
182		return ("EMERG");
183	case LOG_ALERT:
184		return ("ALERT");
185	case LOG_CRIT:
186		return ("CRIT");
187	case LOG_ERR:
188		return ("ERROR");
189	case LOG_WARNING:
190		return ("WARNING");
191	case LOG_NOTICE:
192		return ("NOTICE");
193	case LOG_INFO:
194		return ("INFO");
195	case LOG_DEBUG:
196		return ("DEBUG");
197	}
198	assert(!"Invalid log level.");
199	abort();	/* XXX: gcc */
200}
201
202/*
203 * Common log routine.
204 */
205void
206pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...)
207{
208	va_list ap;
209
210	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
211
212	va_start(ap, fmt);
213	pjdlogv_common(loglevel, debuglevel, error, fmt, ap);
214	va_end(ap);
215}
216
217/*
218 * Common log routine, which can handle regular log level as well as debug
219 * level. We decide here where to send the logs (stdout/stderr or syslog).
220 */
221void
222pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt,
223    va_list ap)
224{
225
226	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
227	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
228	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
229	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
230	    loglevel == LOG_INFO || loglevel == LOG_DEBUG);
231	assert(loglevel != LOG_DEBUG || debuglevel > 0);
232	assert(error >= -1);
233
234	/* Ignore debug above configured level. */
235	if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
236		return;
237
238	switch (pjdlog_mode) {
239	case PJDLOG_MODE_STD:
240	    {
241		FILE *out;
242
243		/*
244		 * We send errors and warning to stderr and the rest to stdout.
245		 */
246		switch (loglevel) {
247		case LOG_EMERG:
248		case LOG_ALERT:
249		case LOG_CRIT:
250		case LOG_ERR:
251		case LOG_WARNING:
252			out = stderr;
253			break;
254		case LOG_NOTICE:
255		case LOG_INFO:
256		case LOG_DEBUG:
257			out = stdout;
258			break;
259		default:
260			assert(!"Invalid loglevel.");
261			abort();	/* XXX: gcc */
262		}
263
264		fprintf(out, "[%s]", pjdlog_level_string(loglevel));
265		/* Attach debuglevel if this is debug log. */
266		if (loglevel == LOG_DEBUG)
267			fprintf(out, "[%d]", debuglevel);
268		fprintf(out, " %s", pjdlog_prefix);
269		vfprintf(out, fmt, ap);
270		if (error != -1)
271			fprintf(out, ": %s.", strerror(error));
272		fprintf(out, "\n");
273		fflush(out);
274		break;
275	    }
276	case PJDLOG_MODE_SYSLOG:
277	    {
278		char log[1024];
279		int len;
280
281		len = snprintf(log, sizeof(log), "%s", pjdlog_prefix);
282		if ((size_t)len < sizeof(log))
283			len += vsnprintf(log + len, sizeof(log) - len, fmt, ap);
284		if (error != -1 && (size_t)len < sizeof(log)) {
285			(void)snprintf(log + len, sizeof(log) - len, ": %s.",
286			    strerror(error));
287		}
288		syslog(loglevel, "%s", log);
289		break;
290	    }
291	default:
292		assert(!"Invalid mode.");
293	}
294}
295
296/*
297 * Regular logs.
298 */
299void
300pjdlogv(int loglevel, const char *fmt, va_list ap)
301{
302
303	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
304
305	/* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */
306	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
307	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
308	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
309	    loglevel == LOG_INFO);
310
311	pjdlogv_common(loglevel, 0, -1, fmt, ap);
312}
313
314/*
315 * Regular logs.
316 */
317void
318pjdlog(int loglevel, const char *fmt, ...)
319{
320	va_list ap;
321
322	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
323
324	va_start(ap, fmt);
325	pjdlogv(loglevel, fmt, ap);
326	va_end(ap);
327}
328
329/*
330 * Debug logs.
331 */
332void
333pjdlogv_debug(int debuglevel, const char *fmt, va_list ap)
334{
335
336	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
337
338	pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap);
339}
340
341/*
342 * Debug logs.
343 */
344void
345pjdlog_debug(int debuglevel, const char *fmt, ...)
346{
347	va_list ap;
348
349	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
350
351	va_start(ap, fmt);
352	pjdlogv_debug(debuglevel, fmt, ap);
353	va_end(ap);
354}
355
356/*
357 * Error logs with errno logging.
358 */
359void
360pjdlogv_errno(int loglevel, const char *fmt, va_list ap)
361{
362
363	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
364
365	pjdlogv_common(loglevel, 0, errno, fmt, ap);
366}
367
368/*
369 * Error logs with errno logging.
370 */
371void
372pjdlog_errno(int loglevel, const char *fmt, ...)
373{
374	va_list ap;
375
376	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
377
378	va_start(ap, fmt);
379	pjdlogv_errno(loglevel, fmt, ap);
380	va_end(ap);
381}
382
383/*
384 * Log error, errno and exit.
385 */
386void
387pjdlogv_exit(int exitcode, const char *fmt, va_list ap)
388{
389
390	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
391
392	pjdlogv_errno(LOG_ERR, fmt, ap);
393	exit(exitcode);
394	/* NOTREACHED */
395}
396
397/*
398 * Log error, errno and exit.
399 */
400void
401pjdlog_exit(int exitcode, const char *fmt, ...)
402{
403	va_list ap;
404
405	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
406
407	va_start(ap, fmt);
408	pjdlogv_exit(exitcode, fmt, ap);
409	/* NOTREACHED */
410	va_end(ap);
411}
412
413/*
414 * Log error and exit.
415 */
416void
417pjdlogv_exitx(int exitcode, const char *fmt, va_list ap)
418{
419
420	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
421
422	pjdlogv(LOG_ERR, fmt, ap);
423	exit(exitcode);
424	/* NOTREACHED */
425}
426
427/*
428 * Log error and exit.
429 */
430void
431pjdlog_exitx(int exitcode, const char *fmt, ...)
432{
433	va_list ap;
434
435	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
436
437	va_start(ap, fmt);
438	pjdlogv_exitx(exitcode, fmt, ap);
439	/* NOTREACHED */
440	va_end(ap);
441}
442
443/*
444 * Log failure message and exit.
445 */
446void
447pjdlog_abort(const char *func, const char *file, int line,
448    const char *failedexpr, const char *fmt, ...)
449{
450	va_list ap;
451
452	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
453
454	/*
455	 * When there is no message we pass __func__ as 'fmt'.
456	 * It would be cleaner to pass NULL or "", but gcc generates a warning
457	 * for both of those.
458	 */
459	if (fmt != func) {
460		va_start(ap, fmt);
461		pjdlogv_critical(fmt, ap);
462		va_end(ap);
463	}
464	if (failedexpr == NULL) {
465		if (func == NULL) {
466			pjdlog_critical("Aborted at file %s, line %d.", file,
467			    line);
468		} else {
469			pjdlog_critical("Aborted at function %s, file %s, line %d.",
470			    func, file, line);
471		}
472	} else {
473		if (func == NULL) {
474			pjdlog_critical("Assertion failed: (%s), file %s, line %d.",
475			    failedexpr, file, line);
476		} else {
477			pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.",
478			    failedexpr, func, file, line);
479		}
480	}
481	abort();
482}
483