xref: /freebsd/sbin/hastd/pjdlog.c (revision b2db760808f74bb53c232900091c9da801ebbfcc)
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 
62 /*
63  * Return current mode.
64  */
65 int
66 pjdlog_mode_get(void)
67 {
68 
69 	return (pjdlog_mode);
70 }
71 
72 /*
73  * Set debug level. All the logs above the level specified here will be
74  * ignored.
75  */
76 void
77 pjdlog_debug_set(int level)
78 {
79 
80 	assert(level >= 0);
81 
82 	pjdlog_debug_level = level;
83 }
84 
85 /*
86  * Return current debug level.
87  */
88 int
89 pjdlog_debug_get(void)
90 {
91 
92 	return (pjdlog_debug_level);
93 }
94 
95 /*
96  * Set prefix that will be used before each log.
97  * Setting prefix to NULL will remove it.
98  */
99 void
100 pjdlog_prefix_set(const char *fmt, ...)
101 {
102 	va_list ap;
103 
104 	va_start(ap, fmt);
105 	pjdlog_prefix_setv(fmt, ap);
106 	va_end(ap);
107 }
108 
109 /*
110  * Set prefix that will be used before each log.
111  * Setting prefix to NULL will remove it.
112  */
113 void
114 pjdlog_prefix_setv(const char *fmt, va_list ap)
115 {
116 
117 	assert(fmt != NULL);
118 
119 	vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap);
120 }
121 
122 /*
123  * Convert log level into string.
124  */
125 static const char *
126 pjdlog_level_string(int loglevel)
127 {
128 
129 	switch (loglevel) {
130 	case LOG_EMERG:
131 		return ("EMERG");
132 	case LOG_ALERT:
133 		return ("ALERT");
134 	case LOG_CRIT:
135 		return ("CRIT");
136 	case LOG_ERR:
137 		return ("ERROR");
138 	case LOG_WARNING:
139 		return ("WARNING");
140 	case LOG_NOTICE:
141 		return ("NOTICE");
142 	case LOG_INFO:
143 		return ("INFO");
144 	case LOG_DEBUG:
145 		return ("DEBUG");
146 	}
147 	assert(!"Invalid log level.");
148 	abort();	/* XXX: gcc */
149 }
150 
151 /*
152  * Common log routine.
153  */
154 void
155 pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...)
156 {
157 	va_list ap;
158 
159 	va_start(ap, fmt);
160 	pjdlogv_common(loglevel, debuglevel, error, fmt, ap);
161 	va_end(ap);
162 }
163 
164 /*
165  * Common log routine, which can handle regular log level as well as debug
166  * level. We decide here where to send the logs (stdout/stderr or syslog).
167  */
168 void
169 pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt,
170     va_list ap)
171 {
172 
173 	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
174 	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
175 	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
176 	    loglevel == LOG_INFO || loglevel == LOG_DEBUG);
177 	assert(loglevel != LOG_DEBUG || debuglevel > 0);
178 	assert(error >= -1);
179 
180 	/* Ignore debug above configured level. */
181 	if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
182 		return;
183 
184 	switch (pjdlog_mode) {
185 	case PJDLOG_MODE_STD:
186 	    {
187 		FILE *out;
188 
189 		/*
190 		 * We send errors and warning to stderr and the rest to stdout.
191 		 */
192 		switch (loglevel) {
193 		case LOG_EMERG:
194 		case LOG_ALERT:
195 		case LOG_CRIT:
196 		case LOG_ERR:
197 		case LOG_WARNING:
198 			out = stderr;
199 			break;
200 		case LOG_NOTICE:
201 		case LOG_INFO:
202 		case LOG_DEBUG:
203 			out = stdout;
204 			break;
205 		default:
206 			assert(!"Invalid loglevel.");
207 			abort();	/* XXX: gcc */
208 		}
209 
210 		fprintf(out, "[%s]", pjdlog_level_string(loglevel));
211 		/* Attach debuglevel if this is debug log. */
212 		if (loglevel == LOG_DEBUG)
213 			fprintf(out, "[%d]", debuglevel);
214 		fprintf(out, " ");
215 		fprintf(out, "%s", pjdlog_prefix);
216 		vfprintf(out, fmt, ap);
217 		if (error != -1)
218 			fprintf(out, ": %s.", strerror(error));
219 		fprintf(out, "\n");
220 		break;
221 	    }
222 	case PJDLOG_MODE_SYSLOG:
223 	    {
224 		char log[1024];
225 		int len;
226 
227 		len = snprintf(log, sizeof(log), "%s", pjdlog_prefix);
228 		if ((size_t)len < sizeof(log))
229 			len += vsnprintf(log + len, sizeof(log) - len, fmt, ap);
230 		if (error != -1 && (size_t)len < sizeof(log)) {
231 			(void)snprintf(log + len, sizeof(log) - len, ": %s.",
232 			    strerror(error));
233 		}
234 		syslog(loglevel, "%s", log);
235 		break;
236 	    }
237 	default:
238 		assert(!"Invalid mode.");
239 	}
240 }
241 
242 /*
243  * Regular logs.
244  */
245 void
246 pjdlogv(int loglevel, const char *fmt, va_list ap)
247 {
248 
249 	/* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */
250 	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
251 	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
252 	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
253 	    loglevel == LOG_INFO);
254 
255 	pjdlogv_common(loglevel, 0, -1, fmt, ap);
256 }
257 
258 /*
259  * Regular logs.
260  */
261 void
262 pjdlog(int loglevel, const char *fmt, ...)
263 {
264 	va_list ap;
265 
266 	va_start(ap, fmt);
267 	pjdlogv(loglevel, fmt, ap);
268 	va_end(ap);
269 }
270 
271 /*
272  * Debug logs.
273  */
274 void
275 pjdlogv_debug(int debuglevel, const char *fmt, va_list ap)
276 {
277 
278 	pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap);
279 }
280 
281 /*
282  * Debug logs.
283  */
284 void
285 pjdlog_debug(int debuglevel, const char *fmt, ...)
286 {
287 	va_list ap;
288 
289 	va_start(ap, fmt);
290 	pjdlogv_debug(debuglevel, fmt, ap);
291 	va_end(ap);
292 }
293 
294 /*
295  * Error logs with errno logging.
296  */
297 void
298 pjdlogv_errno(int loglevel, const char *fmt, va_list ap)
299 {
300 
301 	pjdlogv_common(loglevel, 0, errno, fmt, ap);
302 }
303 
304 /*
305  * Error logs with errno logging.
306  */
307 void
308 pjdlog_errno(int loglevel, const char *fmt, ...)
309 {
310 	va_list ap;
311 
312 	va_start(ap, fmt);
313 	pjdlogv_errno(loglevel, fmt, ap);
314 	va_end(ap);
315 }
316 
317 /*
318  * Log error, errno and exit.
319  */
320 void
321 pjdlogv_exit(int exitcode, const char *fmt, va_list ap)
322 {
323 
324 	pjdlogv_errno(LOG_ERR, fmt, ap);
325 	exit(exitcode);
326 	/* NOTREACHED */
327 }
328 
329 /*
330  * Log error, errno and exit.
331  */
332 void
333 pjdlog_exit(int exitcode, const char *fmt, ...)
334 {
335 	va_list ap;
336 
337 	va_start(ap, fmt);
338 	pjdlogv_exit(exitcode, fmt, ap);
339 	/* NOTREACHED */
340 	va_end(ap);
341 }
342 
343 /*
344  * Log error and exit.
345  */
346 void
347 pjdlogv_exitx(int exitcode, const char *fmt, va_list ap)
348 {
349 
350 	pjdlogv(LOG_ERR, fmt, ap);
351 	exit(exitcode);
352 	/* NOTREACHED */
353 }
354 
355 /*
356  * Log error and exit.
357  */
358 void
359 pjdlog_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 
369 /*
370  * Log assertion and exit.
371  */
372 void
373 pjdlog_verify(const char *func, const char *file, int line,
374     const char *failedexpr)
375 {
376 
377 	if (func == NULL) {
378 		pjdlog_critical("Assertion failed: (%s), file %s, line %d.",
379 		    failedexpr, file, line);
380 	} else {
381 		pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.",
382 		    failedexpr, func, file, line);
383 	}
384 	abort();
385         /* NOTREACHED */
386 }
387 
388