xref: /freebsd/sbin/hastd/pjdlog.c (revision a3cf0ef5a295c885c895fabfd56470c0d1db322d)
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$");
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 
43 static int pjdlog_mode = PJDLOG_MODE_STD;
44 static int pjdlog_debug_level = 0;
45 static 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  */
53 void
54 pjdlog_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  */
68 int
69 pjdlog_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  */
79 void
80 pjdlog_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  */
91 int
92 pjdlog_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  */
102 void
103 pjdlog_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  */
116 void
117 pjdlog_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  */
128 static const char *
129 pjdlog_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  */
157 void
158 pjdlog_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  */
171 void
172 pjdlogv_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  */
248 void
249 pjdlogv(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  */
264 void
265 pjdlog(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  */
277 void
278 pjdlogv_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  */
287 void
288 pjdlog_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  */
300 void
301 pjdlogv_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  */
310 void
311 pjdlog_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  */
323 void
324 pjdlogv_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  */
335 void
336 pjdlog_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  */
349 void
350 pjdlogv_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  */
361 void
362 pjdlog_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  */
375 void
376 pjdlog_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         /* NOTREACHED */
389 }
390 
391