pjdlog.c revision 218040
1166124Srafan/*-
2184989Srafan * Copyright (c) 2009-2010 The FreeBSD Foundation
3166124Srafan * Copyright (c) 2011 Pawel Jakub Dawidek <pjd@FreeBSD.org>
4166124Srafan * All rights reserved.
5166124Srafan *
6166124Srafan * This software was developed by Pawel Jakub Dawidek under sponsorship from
7166124Srafan * the FreeBSD Foundation.
8166124Srafan *
9166124Srafan * Redistribution and use in source and binary forms, with or without
10166124Srafan * modification, are permitted provided that the following conditions
11166124Srafan * are met:
12166124Srafan * 1. Redistributions of source code must retain the above copyright
13166124Srafan *    notice, this list of conditions and the following disclaimer.
14166124Srafan * 2. Redistributions in binary form must reproduce the above copyright
15166124Srafan *    notice, this list of conditions and the following disclaimer in the
16166124Srafan *    documentation and/or other materials provided with the distribution.
17166124Srafan *
18166124Srafan * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19166124Srafan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20166124Srafan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21166124Srafan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22166124Srafan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23166124Srafan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24166124Srafan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25166124Srafan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26166124Srafan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27166124Srafan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28184989Srafan * SUCH DAMAGE.
29166124Srafan */
3050276Speter
3150276Speter#include <sys/cdefs.h>
3250276Speter__FBSDID("$FreeBSD: head/sbin/hastd/pjdlog.c 218040 2011-01-28 21:36:01Z pjd $");
3350276Speter
3450276Speter#include <assert.h>
35166124Srafan#include <errno.h>
3650276Speter#include <stdarg.h>
37166124Srafan#include <stdbool.h>
3850276Speter#include <stdio.h>
3950276Speter#include <stdlib.h>
4050276Speter#include <string.h>
41166124Srafan#include <syslog.h>
42166124Srafan
43166124Srafan#include "pjdlog.h"
4450276Speter
45166124Srafanstatic bool pjdlog_initialized = false;
4650276Speterstatic int pjdlog_mode, pjdlog_debug_level;
4750276Speterstatic char pjdlog_prefix[128];
48166124Srafan
4950276Spetervoid
5050276Speterpjdlog_init(int mode)
5150276Speter{
5250276Speter
5350276Speter	assert(!pjdlog_initialized);
54166124Srafan	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
55166124Srafan
56166124Srafan	if (mode == PJDLOG_MODE_SYSLOG)
5750276Speter		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
58166124Srafan	pjdlog_mode = mode;
59166124Srafan	pjdlog_debug_level = 0;
6050276Speter	bzero(pjdlog_prefix, sizeof(pjdlog_prefix));
61166124Srafan
62166124Srafan	pjdlog_initialized = true;
63166124Srafan}
64184989Srafan
65184989Srafanvoid
66184989Srafanpjdlog_fini(void)
67184989Srafan{
68184989Srafan
69184989Srafan	assert(pjdlog_initialized);
70184989Srafan
71184989Srafan	if (pjdlog_mode == PJDLOG_MODE_SYSLOG)
72184989Srafan		closelog();
73184989Srafan
74184989Srafan	pjdlog_initialized = false;
7550276Speter}
7650276Speter
77166124Srafan/*
78166124Srafan * Configure where the logs should go.
79166124Srafan * By default they are send to stdout/stderr, but after going into background
80166124Srafan * (eg. by calling daemon(3)) application is responsible for changing mode to
81166124Srafan * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
82166124Srafan */
83166124Srafanvoid
8450276Speterpjdlog_mode_set(int mode)
85166124Srafan{
86174993Srafan
87174993Srafan	assert(pjdlog_initialized);
88174993Srafan	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
89174993Srafan
90174993Srafan	if (pjdlog_mode == mode)
91174993Srafan		return;
9250276Speter
93166124Srafan	if (mode == PJDLOG_MODE_SYSLOG)
94166124Srafan		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
95166124Srafan	else /* if (mode == PJDLOG_MODE_STD) */
96166124Srafan		closelog();
9750276Speter
98174993Srafan	pjdlog_mode = mode;
9950276Speter}
10050276Speter
101166124Srafan/*
102166124Srafan * Return current mode.
10350276Speter */
10450276Speterint
10550276Speterpjdlog_mode_get(void)
106166124Srafan{
10750276Speter
108166124Srafan	assert(pjdlog_initialized);
10950276Speter
11050276Speter	return (pjdlog_mode);
11150276Speter}
11250276Speter
11350276Speter/*
11450276Speter * Set debug level. All the logs above the level specified here will be
11550276Speter * ignored.
11650276Speter */
117174993Srafanvoid
11850276Speterpjdlog_debug_set(int level)
11950276Speter{
12050276Speter
12150276Speter	assert(pjdlog_initialized);
12250276Speter	assert(level >= 0);
12350276Speter
124166124Srafan	pjdlog_debug_level = level;
12550276Speter}
12650276Speter
12750276Speter/*
12850276Speter * Return current debug level.
12950276Speter */
13050276Speterint
13150276Speterpjdlog_debug_get(void)
13250276Speter{
13350276Speter
13450276Speter	assert(pjdlog_initialized);
13550276Speter
13650276Speter	return (pjdlog_debug_level);
13750276Speter}
13850276Speter
13950276Speter/*
14050276Speter * Set prefix that will be used before each log.
14150276Speter * Setting prefix to NULL will remove it.
14250276Speter */
14350276Spetervoid
14450276Speterpjdlog_prefix_set(const char *fmt, ...)
14550276Speter{
14650276Speter	va_list ap;
14750276Speter
14850276Speter	assert(pjdlog_initialized);
14950276Speter
15050276Speter	va_start(ap, fmt);
15150276Speter	pjdlogv_prefix_set(fmt, ap);
15250276Speter	va_end(ap);
15350276Speter}
15450276Speter
15550276Speter/*
15650276Speter * Set prefix that will be used before each log.
15750276Speter * Setting prefix to NULL will remove it.
15850276Speter */
15950276Spetervoid
16050276Speterpjdlogv_prefix_set(const char *fmt, va_list ap)
16150276Speter{
16250276Speter
16350276Speter	assert(pjdlog_initialized);
16450276Speter	assert(fmt != NULL);
16550276Speter
16650276Speter	vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap);
16750276Speter}
16850276Speter
16950276Speter/*
17050276Speter * Convert log level into string.
17150276Speter */
17250276Speterstatic const char *
17350276Speterpjdlog_level_string(int loglevel)
17450276Speter{
17550276Speter
17650276Speter	switch (loglevel) {
17750276Speter	case LOG_EMERG:
17850276Speter		return ("EMERG");
17950276Speter	case LOG_ALERT:
18050276Speter		return ("ALERT");
18150276Speter	case LOG_CRIT:
18250276Speter		return ("CRIT");
18350276Speter	case LOG_ERR:
18450276Speter		return ("ERROR");
18550276Speter	case LOG_WARNING:
18650276Speter		return ("WARNING");
18750276Speter	case LOG_NOTICE:
18850276Speter		return ("NOTICE");
18950276Speter	case LOG_INFO:
19050276Speter		return ("INFO");
19150276Speter	case LOG_DEBUG:
19250276Speter		return ("DEBUG");
19350276Speter	}
19450276Speter	assert(!"Invalid log level.");
19550276Speter	abort();	/* XXX: gcc */
19650276Speter}
19750276Speter
19850276Speter/*
19950276Speter * Common log routine.
20050276Speter */
20150276Spetervoid
20250276Speterpjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...)
20350276Speter{
20450276Speter	va_list ap;
20550276Speter
20650276Speter	assert(pjdlog_initialized);
20750276Speter
20850276Speter	va_start(ap, fmt);
20950276Speter	pjdlogv_common(loglevel, debuglevel, error, fmt, ap);
21050276Speter	va_end(ap);
21150276Speter}
212166124Srafan
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