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