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