pjdlog.c revision 219385
161452Sdfr/*-
261452Sdfr * Copyright (c) 2009-2010 The FreeBSD Foundation
361452Sdfr * Copyright (c) 2011 Pawel Jakub Dawidek <pjd@FreeBSD.org>
461452Sdfr * All rights reserved.
561452Sdfr *
661452Sdfr * This software was developed by Pawel Jakub Dawidek under sponsorship from
761452Sdfr * the FreeBSD Foundation.
861452Sdfr *
961452Sdfr * Redistribution and use in source and binary forms, with or without
1061452Sdfr * modification, are permitted provided that the following conditions
1161452Sdfr * are met:
1261452Sdfr * 1. Redistributions of source code must retain the above copyright
1361452Sdfr *    notice, this list of conditions and the following disclaimer.
1461452Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1561452Sdfr *    notice, this list of conditions and the following disclaimer in the
1661452Sdfr *    documentation and/or other materials provided with the distribution.
1761452Sdfr *
1861452Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
1961452Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2061452Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2161452Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2261452Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2361452Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2461452Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2561452Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2661452Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27116192Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28116192Sobrien * SUCH DAMAGE.
29116192Sobrien */
3061452Sdfr
3161452Sdfr#include <sys/cdefs.h>
3261452Sdfr__FBSDID("$FreeBSD: head/sbin/hastd/pjdlog.c 219385 2011-03-07 19:54:51Z pjd $");
3361452Sdfr
34129878Sphk#include <sys/socket.h>
3561452Sdfr#include <netinet/in.h>
3661452Sdfr
3776827Salfred#include <assert.h>
3879339Sjhb#include <errno.h>
3961452Sdfr#include <libutil.h>
40173573Sjhb#include <printf.h>
41173573Sjhb#include <stdarg.h>
42119288Simp#include <stdint.h>
43119288Simp#include <stdio.h>
4461452Sdfr#include <stdlib.h>
4561452Sdfr#include <string.h>
4661452Sdfr#include <syslog.h>
4761452Sdfr
4861452Sdfr#include "pjdlog.h"
4961452Sdfr
5061452Sdfr#define	PJDLOG_NEVER_INITIALIZED	0
5161452Sdfr#define	PJDLOG_NOT_INITIALIZED		1
5261452Sdfr#define	PJDLOG_INITIALIZED		2
5361452Sdfr
5461452Sdfrstatic int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED;
5561452Sdfrstatic int pjdlog_mode, pjdlog_debug_level;
5661452Sdfrstatic char pjdlog_prefix[128];
5761452Sdfr
5861452Sdfrstatic int
5961452Sdfrpjdlog_printf_arginfo_humanized_number(const struct printf_info *pi __unused,
6061452Sdfr    size_t n, int *argt)
6161452Sdfr{
6261452Sdfr
6361452Sdfr	assert(n >= 1);
6461452Sdfr	argt[0] = PA_INT | PA_FLAG_INTMAX;
6561452Sdfr	return (1);
66142646Scognet}
67142646Scognet
6861452Sdfrstatic int
6961452Sdfrpjdlog_printf_render_humanized_number(struct __printf_io *io,
70139431Sanholt    const struct printf_info *pi, const void * const *arg)
71139431Sanholt{
72244926Santoine	char buf[5];
7361452Sdfr	intmax_t num;
7461452Sdfr	int ret;
7561452Sdfr
7661452Sdfr	num = *(const intmax_t *)arg[0];
7761452Sdfr	humanize_number(buf, sizeof(buf), (int64_t)num, "", HN_AUTOSCALE,
7861452Sdfr	    HN_NOSPACE | HN_DECIMAL);
7961452Sdfr	ret = __printf_out(io, pi, buf, strlen(buf));
8061452Sdfr	__printf_flush(io);
8161452Sdfr	return (ret);
82241885Seadler}
83241885Seadler
8461452Sdfrstatic int
8561452Sdfrpjdlog_printf_arginfo_sockaddr(const struct printf_info *pi __unused,
8661452Sdfr    size_t n, int *argt)
87142398Simp{
8861452Sdfr
8961452Sdfr	assert(n >= 1);
9061452Sdfr	argt[0] = PA_POINTER;
9161452Sdfr	return (1);
9261452Sdfr}
9361452Sdfr
9461452Sdfrstatic int
9561452Sdfrpjdlog_printf_render_sockaddr(struct __printf_io *io,
9661452Sdfr    const struct printf_info *pi, const void * const *arg)
9761452Sdfr{
9861452Sdfr	const struct sockaddr_storage *ss;
99134098Sanholt	char buf[64];
10061452Sdfr	int ret;
10161452Sdfr
10261452Sdfr	ss = *(const struct sockaddr_storage * const *)arg[0];
10361452Sdfr	switch (ss->ss_family) {
10461452Sdfr	case AF_INET:
10561452Sdfr	    {
106122513Sanholt		const struct sockaddr_in *sin;
107122513Sanholt		in_addr_t ip;
108122513Sanholt		unsigned int port;
109122513Sanholt
11061452Sdfr		sin = (const struct sockaddr_in *)ss;
11161452Sdfr		ip = ntohl(sin->sin_addr.s_addr);
11261452Sdfr		port = ntohs(sin->sin_port);
11361452Sdfr
11461452Sdfr		snprintf(buf, sizeof(buf), "%u.%u.%u.%u:%u",
11561452Sdfr		    ((ip >> 24) & 0xff), ((ip >> 16) & 0xff),
11661452Sdfr		    ((ip >> 8) & 0xff), (ip & 0xff), port);
11761452Sdfr		break;
11861452Sdfr	    }
11961452Sdfr	default:
12061452Sdfr		snprintf(buf, sizeof(buf), "[unsupported family %u]",
12161452Sdfr		    (unsigned int)ss->ss_family);
12261452Sdfr		break;
12361452Sdfr	}
12461452Sdfr	ret = __printf_out(io, pi, buf, strlen(buf));
12561452Sdfr	__printf_flush(io);
12661452Sdfr	return (ret);
12761452Sdfr}
128134098Sanholt
129134098Sanholtvoid
130134099Sanholtpjdlog_init(int mode)
13161452Sdfr{
13261452Sdfr
13361452Sdfr	assert(pjdlog_initialized == PJDLOG_NEVER_INITIALIZED ||
13461452Sdfr	    pjdlog_initialized == PJDLOG_NOT_INITIALIZED);
13561452Sdfr	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
13661452Sdfr
13761452Sdfr	if (pjdlog_initialized == PJDLOG_NEVER_INITIALIZED) {
13861452Sdfr		__use_xprintf = 1;
13961452Sdfr		register_printf_render_std("T");
14061452Sdfr		register_printf_render('N',
14161452Sdfr		    pjdlog_printf_render_humanized_number,
142134098Sanholt		    pjdlog_printf_arginfo_humanized_number);
14361452Sdfr		register_printf_render('S',
144173203Sjhb		    pjdlog_printf_render_sockaddr,
14561452Sdfr		    pjdlog_printf_arginfo_sockaddr);
14661452Sdfr	}
14761452Sdfr
14861452Sdfr	if (mode == PJDLOG_MODE_SYSLOG)
14961452Sdfr		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
15061452Sdfr	pjdlog_mode = mode;
151134098Sanholt	pjdlog_debug_level = 0;
152134099Sanholt	bzero(pjdlog_prefix, sizeof(pjdlog_prefix));
15361452Sdfr
15461452Sdfr	pjdlog_initialized = PJDLOG_INITIALIZED;
155173203Sjhb}
15661452Sdfr
15761452Sdfrvoid
15861452Sdfrpjdlog_fini(void)
15961452Sdfr{
16061452Sdfr
16161452Sdfr	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
16261452Sdfr
16361452Sdfr	if (pjdlog_mode == PJDLOG_MODE_SYSLOG)
16461452Sdfr		closelog();
16561452Sdfr
16661452Sdfr	pjdlog_initialized = PJDLOG_NOT_INITIALIZED;
16761452Sdfr}
16861452Sdfr
16961452Sdfr/*
17061452Sdfr * Configure where the logs should go.
17161452Sdfr * By default they are send to stdout/stderr, but after going into background
17261452Sdfr * (eg. by calling daemon(3)) application is responsible for changing mode to
17361452Sdfr * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
17461452Sdfr */
17561452Sdfrvoid
17661452Sdfrpjdlog_mode_set(int mode)
17761452Sdfr{
17861452Sdfr
17961452Sdfr	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
18061452Sdfr	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
18161452Sdfr
18261452Sdfr	if (pjdlog_mode == mode)
183134099Sanholt		return;
18461452Sdfr
18561452Sdfr	if (mode == PJDLOG_MODE_SYSLOG)
18661452Sdfr		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
18761452Sdfr	else /* if (mode == PJDLOG_MODE_STD) */
18861452Sdfr		closelog();
18961452Sdfr
19061452Sdfr	pjdlog_mode = mode;
19161452Sdfr}
19261452Sdfr
193134098Sanholt/*
19461452Sdfr * Return current mode.
19561452Sdfr */
19661452Sdfrint
19761452Sdfrpjdlog_mode_get(void)
19861452Sdfr{
19961452Sdfr
20061452Sdfr	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
201134098Sanholt
202134099Sanholt	return (pjdlog_mode);
20361452Sdfr}
20461452Sdfr
20561452Sdfr/*
20661452Sdfr * Set debug level. All the logs above the level specified here will be
207194017Savg * ignored.
20861452Sdfr */
20961452Sdfrvoid
21061452Sdfrpjdlog_debug_set(int level)
211194017Savg{
21261452Sdfr
21361452Sdfr	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
21461452Sdfr	assert(level >= 0);
21561452Sdfr
21661452Sdfr	pjdlog_debug_level = level;
21761452Sdfr}
21861452Sdfr
219194017Savg/*
22061452Sdfr * Return current debug level.
22161452Sdfr */
22261452Sdfrint
223194017Savgpjdlog_debug_get(void)
22461452Sdfr{
22561452Sdfr
22661452Sdfr	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
22761452Sdfr
22861452Sdfr	return (pjdlog_debug_level);
22961452Sdfr}
23061452Sdfr
23161452Sdfr/*
23261452Sdfr * Set prefix that will be used before each log.
23361452Sdfr * Setting prefix to NULL will remove it.
23461452Sdfr */
23561452Sdfrvoid
23661452Sdfrpjdlog_prefix_set(const char *fmt, ...)
23761452Sdfr{
23861452Sdfr	va_list ap;
23961452Sdfr
24061452Sdfr	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
24161452Sdfr
24261452Sdfr	va_start(ap, fmt);
24361452Sdfr	pjdlogv_prefix_set(fmt, ap);
24461452Sdfr	va_end(ap);
24561452Sdfr}
24661452Sdfr
24761452Sdfr/*
24861452Sdfr * Set prefix that will be used before each log.
24961452Sdfr * Setting prefix to NULL will remove it.
25061452Sdfr */
25161452Sdfrvoid
25261452Sdfrpjdlogv_prefix_set(const char *fmt, va_list ap)
25361452Sdfr{
25461452Sdfr
25561452Sdfr	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
25661452Sdfr	assert(fmt != NULL);
25761452Sdfr
25861452Sdfr	vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap);
25961452Sdfr}
26061452Sdfr
26161452Sdfr/*
26261452Sdfr * Convert log level into string.
26361452Sdfr */
26461452Sdfrstatic const char *
26561452Sdfrpjdlog_level_string(int loglevel)
26661452Sdfr{
26761452Sdfr
26861452Sdfr	switch (loglevel) {
269153572Sjhb	case LOG_EMERG:
270113506Smdodd		return ("EMERG");
271113506Smdodd	case LOG_ALERT:
272		return ("ALERT");
273	case LOG_CRIT:
274		return ("CRIT");
275	case LOG_ERR:
276		return ("ERROR");
277	case LOG_WARNING:
278		return ("WARNING");
279	case LOG_NOTICE:
280		return ("NOTICE");
281	case LOG_INFO:
282		return ("INFO");
283	case LOG_DEBUG:
284		return ("DEBUG");
285	}
286	assert(!"Invalid log level.");
287	abort();	/* XXX: gcc */
288}
289
290/*
291 * Common log routine.
292 */
293void
294pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...)
295{
296	va_list ap;
297
298	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
299
300	va_start(ap, fmt);
301	pjdlogv_common(loglevel, debuglevel, error, fmt, ap);
302	va_end(ap);
303}
304
305/*
306 * Common log routine, which can handle regular log level as well as debug
307 * level. We decide here where to send the logs (stdout/stderr or syslog).
308 */
309void
310pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt,
311    va_list ap)
312{
313
314	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
315	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
316	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
317	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
318	    loglevel == LOG_INFO || loglevel == LOG_DEBUG);
319	assert(loglevel != LOG_DEBUG || debuglevel > 0);
320	assert(error >= -1);
321
322	/* Ignore debug above configured level. */
323	if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
324		return;
325
326	switch (pjdlog_mode) {
327	case PJDLOG_MODE_STD:
328	    {
329		FILE *out;
330
331		/*
332		 * We send errors and warning to stderr and the rest to stdout.
333		 */
334		switch (loglevel) {
335		case LOG_EMERG:
336		case LOG_ALERT:
337		case LOG_CRIT:
338		case LOG_ERR:
339		case LOG_WARNING:
340			out = stderr;
341			break;
342		case LOG_NOTICE:
343		case LOG_INFO:
344		case LOG_DEBUG:
345			out = stdout;
346			break;
347		default:
348			assert(!"Invalid loglevel.");
349			abort();	/* XXX: gcc */
350		}
351
352		fprintf(out, "[%s]", pjdlog_level_string(loglevel));
353		/* Attach debuglevel if this is debug log. */
354		if (loglevel == LOG_DEBUG)
355			fprintf(out, "[%d]", debuglevel);
356		fprintf(out, " %s", pjdlog_prefix);
357		vfprintf(out, fmt, ap);
358		if (error != -1)
359			fprintf(out, ": %s.", strerror(error));
360		fprintf(out, "\n");
361		fflush(out);
362		break;
363	    }
364	case PJDLOG_MODE_SYSLOG:
365	    {
366		char log[1024];
367		int len;
368
369		len = snprintf(log, sizeof(log), "%s", pjdlog_prefix);
370		if ((size_t)len < sizeof(log))
371			len += vsnprintf(log + len, sizeof(log) - len, fmt, ap);
372		if (error != -1 && (size_t)len < sizeof(log)) {
373			(void)snprintf(log + len, sizeof(log) - len, ": %s.",
374			    strerror(error));
375		}
376		syslog(loglevel, "%s", log);
377		break;
378	    }
379	default:
380		assert(!"Invalid mode.");
381	}
382}
383
384/*
385 * Regular logs.
386 */
387void
388pjdlogv(int loglevel, const char *fmt, va_list ap)
389{
390
391	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
392
393	/* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */
394	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
395	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
396	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
397	    loglevel == LOG_INFO);
398
399	pjdlogv_common(loglevel, 0, -1, fmt, ap);
400}
401
402/*
403 * Regular logs.
404 */
405void
406pjdlog(int loglevel, const char *fmt, ...)
407{
408	va_list ap;
409
410	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
411
412	va_start(ap, fmt);
413	pjdlogv(loglevel, fmt, ap);
414	va_end(ap);
415}
416
417/*
418 * Debug logs.
419 */
420void
421pjdlogv_debug(int debuglevel, const char *fmt, va_list ap)
422{
423
424	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
425
426	pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap);
427}
428
429/*
430 * Debug logs.
431 */
432void
433pjdlog_debug(int debuglevel, const char *fmt, ...)
434{
435	va_list ap;
436
437	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
438
439	va_start(ap, fmt);
440	pjdlogv_debug(debuglevel, fmt, ap);
441	va_end(ap);
442}
443
444/*
445 * Error logs with errno logging.
446 */
447void
448pjdlogv_errno(int loglevel, const char *fmt, va_list ap)
449{
450
451	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
452
453	pjdlogv_common(loglevel, 0, errno, fmt, ap);
454}
455
456/*
457 * Error logs with errno logging.
458 */
459void
460pjdlog_errno(int loglevel, const char *fmt, ...)
461{
462	va_list ap;
463
464	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
465
466	va_start(ap, fmt);
467	pjdlogv_errno(loglevel, fmt, ap);
468	va_end(ap);
469}
470
471/*
472 * Log error, errno and exit.
473 */
474void
475pjdlogv_exit(int exitcode, const char *fmt, va_list ap)
476{
477
478	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
479
480	pjdlogv_errno(LOG_ERR, fmt, ap);
481	exit(exitcode);
482	/* NOTREACHED */
483}
484
485/*
486 * Log error, errno and exit.
487 */
488void
489pjdlog_exit(int exitcode, const char *fmt, ...)
490{
491	va_list ap;
492
493	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
494
495	va_start(ap, fmt);
496	pjdlogv_exit(exitcode, fmt, ap);
497	/* NOTREACHED */
498	va_end(ap);
499}
500
501/*
502 * Log error and exit.
503 */
504void
505pjdlogv_exitx(int exitcode, const char *fmt, va_list ap)
506{
507
508	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
509
510	pjdlogv(LOG_ERR, fmt, ap);
511	exit(exitcode);
512	/* NOTREACHED */
513}
514
515/*
516 * Log error and exit.
517 */
518void
519pjdlog_exitx(int exitcode, const char *fmt, ...)
520{
521	va_list ap;
522
523	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
524
525	va_start(ap, fmt);
526	pjdlogv_exitx(exitcode, fmt, ap);
527	/* NOTREACHED */
528	va_end(ap);
529}
530
531/*
532 * Log failure message and exit.
533 */
534void
535pjdlog_abort(const char *func, const char *file, int line,
536    const char *failedexpr, const char *fmt, ...)
537{
538	va_list ap;
539
540	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
541
542	/*
543	 * When there is no message we pass __func__ as 'fmt'.
544	 * It would be cleaner to pass NULL or "", but gcc generates a warning
545	 * for both of those.
546	 */
547	if (fmt != func) {
548		va_start(ap, fmt);
549		pjdlogv_critical(fmt, ap);
550		va_end(ap);
551	}
552	if (failedexpr == NULL) {
553		if (func == NULL) {
554			pjdlog_critical("Aborted at file %s, line %d.", file,
555			    line);
556		} else {
557			pjdlog_critical("Aborted at function %s, file %s, line %d.",
558			    func, file, line);
559		}
560	} else {
561		if (func == NULL) {
562			pjdlog_critical("Assertion failed: (%s), file %s, line %d.",
563			    failedexpr, file, line);
564		} else {
565			pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.",
566			    failedexpr, func, file, line);
567		}
568	}
569	abort();
570}
571