pjdlog.c revision 210875
1/*-
2 * Copyright (c) 2009-2010 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sbin/hastd/pjdlog.c 210875 2010-08-05 18:26:38Z pjd $");
32
33#include <assert.h>
34#include <errno.h>
35#include <stdarg.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <syslog.h>
40
41#include "pjdlog.h"
42
43static int pjdlog_mode = PJDLOG_MODE_STD;
44static int pjdlog_debug_level = 0;
45static char pjdlog_prefix[128];
46
47/*
48 * Configure where the logs should go.
49 * By default they are send to stdout/stderr, but after going into background
50 * (eg. by calling daemon(3)) application is responsible for changing mode to
51 * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
52 */
53void
54pjdlog_mode_set(int mode)
55{
56
57	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
58
59	pjdlog_mode = mode;
60}
61
62/*
63 * Return current mode.
64 */
65int
66pjdlog_mode_get(void)
67{
68
69	return (pjdlog_mode);
70}
71
72/*
73 * Set debug level. All the logs above the level specified here will be
74 * ignored.
75 */
76void
77pjdlog_debug_set(int level)
78{
79
80	assert(level >= 0);
81
82	pjdlog_debug_level = level;
83}
84
85/*
86 * Return current debug level.
87 */
88int
89pjdlog_debug_get(void)
90{
91
92	return (pjdlog_debug_level);
93}
94
95/*
96 * Set prefix that will be used before each log.
97 * Setting prefix to NULL will remove it.
98 */
99void
100pjdlog_prefix_set(const char *fmt, ...)
101{
102	va_list ap;
103
104	va_start(ap, fmt);
105	pjdlog_prefix_setv(fmt, ap);
106	va_end(ap);
107}
108
109/*
110 * Set prefix that will be used before each log.
111 * Setting prefix to NULL will remove it.
112 */
113void
114pjdlog_prefix_setv(const char *fmt, va_list ap)
115{
116
117	assert(fmt != NULL);
118
119	vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap);
120}
121
122/*
123 * Convert log level into string.
124 */
125static const char *
126pjdlog_level_string(int loglevel)
127{
128
129	switch (loglevel) {
130	case LOG_EMERG:
131		return ("EMERG");
132	case LOG_ALERT:
133		return ("ALERT");
134	case LOG_CRIT:
135		return ("CRIT");
136	case LOG_ERR:
137		return ("ERROR");
138	case LOG_WARNING:
139		return ("WARNING");
140	case LOG_NOTICE:
141		return ("NOTICE");
142	case LOG_INFO:
143		return ("INFO");
144	case LOG_DEBUG:
145		return ("DEBUG");
146	}
147	assert(!"Invalid log level.");
148	abort();	/* XXX: gcc */
149}
150
151/*
152 * Common log routine.
153 */
154void
155pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...)
156{
157	va_list ap;
158
159	va_start(ap, fmt);
160	pjdlogv_common(loglevel, debuglevel, error, fmt, ap);
161	va_end(ap);
162}
163
164/*
165 * Common log routine, which can handle regular log level as well as debug
166 * level. We decide here where to send the logs (stdout/stderr or syslog).
167 */
168void
169pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt,
170    va_list ap)
171{
172
173	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
174	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
175	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
176	    loglevel == LOG_INFO || loglevel == LOG_DEBUG);
177	assert(loglevel != LOG_DEBUG || debuglevel > 0);
178	assert(error >= -1);
179
180	/* Ignore debug above configured level. */
181	if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
182		return;
183
184	switch (pjdlog_mode) {
185	case PJDLOG_MODE_STD:
186	    {
187		FILE *out;
188
189		/*
190		 * We send errors and warning to stderr and the rest to stdout.
191		 */
192		switch (loglevel) {
193		case LOG_EMERG:
194		case LOG_ALERT:
195		case LOG_CRIT:
196		case LOG_ERR:
197		case LOG_WARNING:
198			out = stderr;
199			break;
200		case LOG_NOTICE:
201		case LOG_INFO:
202		case LOG_DEBUG:
203			out = stdout;
204			break;
205		default:
206			assert(!"Invalid loglevel.");
207			abort();	/* XXX: gcc */
208		}
209
210		fprintf(out, "[%s]", pjdlog_level_string(loglevel));
211		/* Attach debuglevel if this is debug log. */
212		if (loglevel == LOG_DEBUG)
213			fprintf(out, "[%d]", debuglevel);
214		fprintf(out, " ");
215		fprintf(out, "%s", pjdlog_prefix);
216		vfprintf(out, fmt, ap);
217		if (error != -1)
218			fprintf(out, ": %s.", strerror(error));
219		fprintf(out, "\n");
220		break;
221	    }
222	case PJDLOG_MODE_SYSLOG:
223	    {
224		char log[1024];
225		int len;
226
227		len = snprintf(log, sizeof(log), "%s", pjdlog_prefix);
228		if ((size_t)len < sizeof(log))
229			len += vsnprintf(log + len, sizeof(log) - len, fmt, ap);
230		if (error != -1 && (size_t)len < sizeof(log)) {
231			(void)snprintf(log + len, sizeof(log) - len, ": %s.",
232			    strerror(error));
233		}
234		syslog(loglevel, "%s", log);
235		break;
236	    }
237	default:
238		assert(!"Invalid mode.");
239	}
240}
241
242/*
243 * Regular logs.
244 */
245void
246pjdlogv(int loglevel, const char *fmt, va_list ap)
247{
248
249	/* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */
250	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
251	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
252	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
253	    loglevel == LOG_INFO);
254
255	pjdlogv_common(loglevel, 0, -1, fmt, ap);
256}
257
258/*
259 * Regular logs.
260 */
261void
262pjdlog(int loglevel, const char *fmt, ...)
263{
264	va_list ap;
265
266	va_start(ap, fmt);
267	pjdlogv(loglevel, fmt, ap);
268	va_end(ap);
269}
270
271/*
272 * Debug logs.
273 */
274void
275pjdlogv_debug(int debuglevel, const char *fmt, va_list ap)
276{
277
278	pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap);
279}
280
281/*
282 * Debug logs.
283 */
284void
285pjdlog_debug(int debuglevel, const char *fmt, ...)
286{
287	va_list ap;
288
289	va_start(ap, fmt);
290	pjdlogv_debug(debuglevel, fmt, ap);
291	va_end(ap);
292}
293
294/*
295 * Error logs with errno logging.
296 */
297void
298pjdlogv_errno(int loglevel, const char *fmt, va_list ap)
299{
300
301	pjdlogv_common(loglevel, 0, errno, fmt, ap);
302}
303
304/*
305 * Error logs with errno logging.
306 */
307void
308pjdlog_errno(int loglevel, const char *fmt, ...)
309{
310	va_list ap;
311
312	va_start(ap, fmt);
313	pjdlogv_errno(loglevel, fmt, ap);
314	va_end(ap);
315}
316
317/*
318 * Log error, errno and exit.
319 */
320void
321pjdlogv_exit(int exitcode, const char *fmt, va_list ap)
322{
323
324	pjdlogv_errno(LOG_ERR, fmt, ap);
325	exit(exitcode);
326	/* NOTREACHED */
327}
328
329/*
330 * Log error, errno and exit.
331 */
332void
333pjdlog_exit(int exitcode, const char *fmt, ...)
334{
335	va_list ap;
336
337	va_start(ap, fmt);
338	pjdlogv_exit(exitcode, fmt, ap);
339	/* NOTREACHED */
340	va_end(ap);
341}
342
343/*
344 * Log error and exit.
345 */
346void
347pjdlogv_exitx(int exitcode, const char *fmt, va_list ap)
348{
349
350	pjdlogv(LOG_ERR, fmt, ap);
351	exit(exitcode);
352	/* NOTREACHED */
353}
354
355/*
356 * Log error and exit.
357 */
358void
359pjdlog_exitx(int exitcode, const char *fmt, ...)
360{
361	va_list ap;
362
363	va_start(ap, fmt);
364	pjdlogv_exitx(exitcode, fmt, ap);
365	/* NOTREACHED */
366	va_end(ap);
367}
368
369/*
370 * Log assertion and exit.
371 */
372void
373pjdlog_verify(const char *func, const char *file, int line,
374    const char *failedexpr)
375{
376
377	if (func == NULL) {
378		pjdlog_critical("Assertion failed: (%s), file %s, line %d.",
379		    failedexpr, file, line);
380	} else {
381		pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.",
382		    failedexpr, func, file, line);
383	}
384	abort();
385        /* NOTREACHED */
386}
387
388