pjdlog.c revision 212052
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 212052 2010-08-31 12:05:13Z 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	if (mode == PJDLOG_MODE_SYSLOG)
62		openlog(NULL, LOG_PID, LOG_DAEMON);
63}
64
65/*
66 * Return current mode.
67 */
68int
69pjdlog_mode_get(void)
70{
71
72	return (pjdlog_mode);
73}
74
75/*
76 * Set debug level. All the logs above the level specified here will be
77 * ignored.
78 */
79void
80pjdlog_debug_set(int level)
81{
82
83	assert(level >= 0);
84
85	pjdlog_debug_level = level;
86}
87
88/*
89 * Return current debug level.
90 */
91int
92pjdlog_debug_get(void)
93{
94
95	return (pjdlog_debug_level);
96}
97
98/*
99 * Set prefix that will be used before each log.
100 * Setting prefix to NULL will remove it.
101 */
102void
103pjdlog_prefix_set(const char *fmt, ...)
104{
105	va_list ap;
106
107	va_start(ap, fmt);
108	pjdlog_prefix_setv(fmt, ap);
109	va_end(ap);
110}
111
112/*
113 * Set prefix that will be used before each log.
114 * Setting prefix to NULL will remove it.
115 */
116void
117pjdlog_prefix_setv(const char *fmt, va_list ap)
118{
119
120	assert(fmt != NULL);
121
122	vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap);
123}
124
125/*
126 * Convert log level into string.
127 */
128static const char *
129pjdlog_level_string(int loglevel)
130{
131
132	switch (loglevel) {
133	case LOG_EMERG:
134		return ("EMERG");
135	case LOG_ALERT:
136		return ("ALERT");
137	case LOG_CRIT:
138		return ("CRIT");
139	case LOG_ERR:
140		return ("ERROR");
141	case LOG_WARNING:
142		return ("WARNING");
143	case LOG_NOTICE:
144		return ("NOTICE");
145	case LOG_INFO:
146		return ("INFO");
147	case LOG_DEBUG:
148		return ("DEBUG");
149	}
150	assert(!"Invalid log level.");
151	abort();	/* XXX: gcc */
152}
153
154/*
155 * Common log routine.
156 */
157void
158pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...)
159{
160	va_list ap;
161
162	va_start(ap, fmt);
163	pjdlogv_common(loglevel, debuglevel, error, fmt, ap);
164	va_end(ap);
165}
166
167/*
168 * Common log routine, which can handle regular log level as well as debug
169 * level. We decide here where to send the logs (stdout/stderr or syslog).
170 */
171void
172pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt,
173    va_list ap)
174{
175
176	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
177	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
178	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
179	    loglevel == LOG_INFO || loglevel == LOG_DEBUG);
180	assert(loglevel != LOG_DEBUG || debuglevel > 0);
181	assert(error >= -1);
182
183	/* Ignore debug above configured level. */
184	if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
185		return;
186
187	switch (pjdlog_mode) {
188	case PJDLOG_MODE_STD:
189	    {
190		FILE *out;
191
192		/*
193		 * We send errors and warning to stderr and the rest to stdout.
194		 */
195		switch (loglevel) {
196		case LOG_EMERG:
197		case LOG_ALERT:
198		case LOG_CRIT:
199		case LOG_ERR:
200		case LOG_WARNING:
201			out = stderr;
202			break;
203		case LOG_NOTICE:
204		case LOG_INFO:
205		case LOG_DEBUG:
206			out = stdout;
207			break;
208		default:
209			assert(!"Invalid loglevel.");
210			abort();	/* XXX: gcc */
211		}
212
213		fprintf(out, "[%s]", pjdlog_level_string(loglevel));
214		/* Attach debuglevel if this is debug log. */
215		if (loglevel == LOG_DEBUG)
216			fprintf(out, "[%d]", debuglevel);
217		fprintf(out, " ");
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        /* NOTREACHED */
390}
391
392