pjdlog.c revision 217965
179455Sobrien/*-
279455Sobrien * Copyright (c) 2009-2010 The FreeBSD Foundation
379455Sobrien * Copyright (c) 2011 Pawel Jakub Dawidek <pjd@FreeBSD.org>
479455Sobrien * All rights reserved.
579455Sobrien *
679455Sobrien * This software was developed by Pawel Jakub Dawidek under sponsorship from
779455Sobrien * the FreeBSD Foundation.
879455Sobrien *
979455Sobrien * Redistribution and use in source and binary forms, with or without
1079455Sobrien * modification, are permitted provided that the following conditions
1179455Sobrien * are met:
1279455Sobrien * 1. Redistributions of source code must retain the above copyright
1379455Sobrien *    notice, this list of conditions and the following disclaimer.
1479455Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1579455Sobrien *    notice, this list of conditions and the following disclaimer in the
1679455Sobrien *    documentation and/or other materials provided with the distribution.
1779455Sobrien *
1879455Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
1979455Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2079455Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2179455Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2279455Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2379455Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2479455Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2579455Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2679455Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2779455Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2879455Sobrien * SUCH DAMAGE.
2979455Sobrien */
3079455Sobrien
3179455Sobrien#include <sys/cdefs.h>
3279455Sobrien__FBSDID("$FreeBSD: head/sbin/hastd/pjdlog.c 217965 2011-01-27 19:24:07Z pjd $");
3379455Sobrien
3479455Sobrien#include <assert.h>
3579455Sobrien#include <errno.h>
3679455Sobrien#include <stdarg.h>
3779455Sobrien#include <stdbool.h>
3879455Sobrien#include <stdio.h>
3979455Sobrien#include <stdlib.h>
4079455Sobrien#include <string.h>
4179455Sobrien#include <syslog.h>
4279455Sobrien
4379455Sobrien#include "pjdlog.h"
4479455Sobrien
4579455Sobrienstatic bool pjdlog_initialized = false;
4679455Sobrienstatic int pjdlog_mode = PJDLOG_MODE_STD;
4779455Sobrienstatic int pjdlog_debug_level = 0;
4879455Sobrienstatic char pjdlog_prefix[128];
49123883Sbde
5079455Sobrienvoid
5179455Sobrienpjdlog_init(int mode)
5279455Sobrien{
5379455Sobrien
5479455Sobrien	assert(!pjdlog_initialized);
5579455Sobrien	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
5679455Sobrien
5779455Sobrien	if (mode == PJDLOG_MODE_SYSLOG)
5892839Simp		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
5979455Sobrien	pjdlog_mode = mode;
6079455Sobrien
61123883Sbde	pjdlog_initialized = true;
62123883Sbde}
63123883Sbde
64123883Sbdevoid
65123883Sbdepjdlog_fini(void)
66123883Sbde{
67123883Sbde
68102231Strhodes	assert(pjdlog_initialized);
6979455Sobrien
7092839Simp	if (pjdlog_mode == PJDLOG_MODE_SYSLOG)
7179455Sobrien		closelog();
7279455Sobrien
7379455Sobrien	pjdlog_initialized = false;
7479455Sobrien}
7579455Sobrien
7679455Sobrien/*
7779455Sobrien * Configure where the logs should go.
7879455Sobrien * By default they are send to stdout/stderr, but after going into background
7979455Sobrien * (eg. by calling daemon(3)) application is responsible for changing mode to
8079455Sobrien * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
8179455Sobrien */
82102231Strhodesvoid
8379455Sobrienpjdlog_mode_set(int mode)
8479455Sobrien{
8579455Sobrien
8679455Sobrien	assert(pjdlog_initialized);
8779455Sobrien	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
8892839Simp
8979455Sobrien	if (pjdlog_mode == mode)
9079455Sobrien		return;
9179455Sobrien
9279455Sobrien	if (mode == PJDLOG_MODE_SYSLOG)
9392839Simp		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
9479455Sobrien	else /* if (mode == PJDLOG_MODE_STD) */
9579455Sobrien		closelog();
9679455Sobrien
9779455Sobrien	pjdlog_mode = mode;
9879455Sobrien}
9992839Simp
10079455Sobrien/*
10179455Sobrien * Return current mode.
10279455Sobrien */
10379455Sobrienint
10479455Sobrienpjdlog_mode_get(void)
10592839Simp{
10679455Sobrien
10779455Sobrien	assert(pjdlog_initialized);
10879455Sobrien
10979455Sobrien	return (pjdlog_mode);
11092839Simp}
11179455Sobrien
11279455Sobrien/*
11379455Sobrien * Set debug level. All the logs above the level specified here will be
11479455Sobrien * ignored.
11592839Simp */
11679455Sobrienvoid
11779455Sobrienpjdlog_debug_set(int level)
11879455Sobrien{
11979455Sobrien
12092839Simp	assert(pjdlog_initialized);
12192839Simp	assert(level >= 0);
12292839Simp
12379455Sobrien	pjdlog_debug_level = level;
12479455Sobrien}
12579455Sobrien
12679455Sobrien/*
12779455Sobrien * Return current debug level.
12879455Sobrien */
12979455Sobrienint
13092839Simppjdlog_debug_get(void)
13179455Sobrien{
13279455Sobrien
13379455Sobrien	assert(pjdlog_initialized);
13492839Simp
13592839Simp	return (pjdlog_debug_level);
13679455Sobrien}
13779455Sobrien
13879455Sobrien/*
13979455Sobrien * Set prefix that will be used before each log.
14079455Sobrien * Setting prefix to NULL will remove it.
14179455Sobrien */
14279455Sobrienvoid
14392839Simppjdlog_prefix_set(const char *fmt, ...)
14479455Sobrien{
14579455Sobrien	va_list ap;
14679455Sobrien
14779455Sobrien	assert(pjdlog_initialized);
14892839Simp
14979455Sobrien	va_start(ap, fmt);
15079455Sobrien	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)
444{
445
446	if (func == NULL) {
447		pjdlog_critical("Assertion failed: (%s), file %s, line %d.",
448		    failedexpr, file, line);
449	} else {
450		pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.",
451		    failedexpr, func, file, line);
452	}
453	abort();
454}
455