pjdlog.c revision 217962
1/*-
2 * Copyright (c) 2009-2011 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 217962 2011-01-27 19:15:25Z 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_NDELAY, 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	pjdlogv_prefix_set(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
117pjdlogv_prefix_set(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, " %s", pjdlog_prefix);
218		vfprintf(out, fmt, ap);
219		if (error != -1)
220			fprintf(out, ": %s.", strerror(error));
221		fprintf(out, "\n");
222		fflush(out);
223		break;
224	    }
225	case PJDLOG_MODE_SYSLOG:
226	    {
227		char log[1024];
228		int len;
229
230		len = snprintf(log, sizeof(log), "%s", pjdlog_prefix);
231		if ((size_t)len < sizeof(log))
232			len += vsnprintf(log + len, sizeof(log) - len, fmt, ap);
233		if (error != -1 && (size_t)len < sizeof(log)) {
234			(void)snprintf(log + len, sizeof(log) - len, ": %s.",
235			    strerror(error));
236		}
237		syslog(loglevel, "%s", log);
238		break;
239	    }
240	default:
241		assert(!"Invalid mode.");
242	}
243}
244
245/*
246 * Regular logs.
247 */
248void
249pjdlogv(int loglevel, const char *fmt, va_list ap)
250{
251
252	/* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */
253	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
254	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
255	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
256	    loglevel == LOG_INFO);
257
258	pjdlogv_common(loglevel, 0, -1, fmt, ap);
259}
260
261/*
262 * Regular logs.
263 */
264void
265pjdlog(int loglevel, const char *fmt, ...)
266{
267	va_list ap;
268
269	va_start(ap, fmt);
270	pjdlogv(loglevel, fmt, ap);
271	va_end(ap);
272}
273
274/*
275 * Debug logs.
276 */
277void
278pjdlogv_debug(int debuglevel, const char *fmt, va_list ap)
279{
280
281	pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap);
282}
283
284/*
285 * Debug logs.
286 */
287void
288pjdlog_debug(int debuglevel, const char *fmt, ...)
289{
290	va_list ap;
291
292	va_start(ap, fmt);
293	pjdlogv_debug(debuglevel, fmt, ap);
294	va_end(ap);
295}
296
297/*
298 * Error logs with errno logging.
299 */
300void
301pjdlogv_errno(int loglevel, const char *fmt, va_list ap)
302{
303
304	pjdlogv_common(loglevel, 0, errno, fmt, ap);
305}
306
307/*
308 * Error logs with errno logging.
309 */
310void
311pjdlog_errno(int loglevel, const char *fmt, ...)
312{
313	va_list ap;
314
315	va_start(ap, fmt);
316	pjdlogv_errno(loglevel, fmt, ap);
317	va_end(ap);
318}
319
320/*
321 * Log error, errno and exit.
322 */
323void
324pjdlogv_exit(int exitcode, const char *fmt, va_list ap)
325{
326
327	pjdlogv_errno(LOG_ERR, fmt, ap);
328	exit(exitcode);
329	/* NOTREACHED */
330}
331
332/*
333 * Log error, errno and exit.
334 */
335void
336pjdlog_exit(int exitcode, const char *fmt, ...)
337{
338	va_list ap;
339
340	va_start(ap, fmt);
341	pjdlogv_exit(exitcode, fmt, ap);
342	/* NOTREACHED */
343	va_end(ap);
344}
345
346/*
347 * Log error and exit.
348 */
349void
350pjdlogv_exitx(int exitcode, const char *fmt, va_list ap)
351{
352
353	pjdlogv(LOG_ERR, fmt, ap);
354	exit(exitcode);
355	/* NOTREACHED */
356}
357
358/*
359 * Log error and exit.
360 */
361void
362pjdlog_exitx(int exitcode, const char *fmt, ...)
363{
364	va_list ap;
365
366	va_start(ap, fmt);
367	pjdlogv_exitx(exitcode, fmt, ap);
368	/* NOTREACHED */
369	va_end(ap);
370}
371
372/*
373 * Log assertion and exit.
374 */
375void
376pjdlog_verify(const char *func, const char *file, int line,
377    const char *failedexpr)
378{
379
380	if (func == NULL) {
381		pjdlog_critical("Assertion failed: (%s), file %s, line %d.",
382		    failedexpr, file, line);
383	} else {
384		pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.",
385		    failedexpr, func, file, line);
386	}
387	abort();
388}
389