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