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