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