1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2009-2010 The FreeBSD Foundation
5 * Copyright (c) 2011 Pawel Jakub Dawidek <pjd@FreeBSD.org>
6 * All rights reserved.
7 *
8 * This software was developed by Pawel Jakub Dawidek under sponsorship from
9 * the FreeBSD Foundation.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <netinet/in.h>
36#include <arpa/inet.h>
37
38#include <assert.h>
39#include <errno.h>
40#include <libutil.h>
41#include <printf.h>
42#include <stdarg.h>
43#include <stdint.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <syslog.h>
48
49#include "pjdlog.h"
50
51#define	PJDLOG_NEVER_INITIALIZED	0
52#define	PJDLOG_NOT_INITIALIZED		1
53#define	PJDLOG_INITIALIZED		2
54
55static int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED;
56static int pjdlog_mode, pjdlog_debug_level;
57static char pjdlog_prefix[128];
58
59static int
60pjdlog_printf_arginfo_humanized_number(const struct printf_info *pi __unused,
61    size_t n, int *argt)
62{
63
64	assert(n >= 1);
65	argt[0] = PA_INT | PA_FLAG_INTMAX;
66	return (1);
67}
68
69static int
70pjdlog_printf_render_humanized_number(struct __printf_io *io,
71    const struct printf_info *pi, const void * const *arg)
72{
73	char buf[5];
74	intmax_t num;
75	int ret;
76
77	num = *(const intmax_t *)arg[0];
78	humanize_number(buf, sizeof(buf), (int64_t)num, "", HN_AUTOSCALE,
79	    HN_NOSPACE | HN_DECIMAL);
80	ret = __printf_out(io, pi, buf, strlen(buf));
81	__printf_flush(io);
82	return (ret);
83}
84
85static int
86pjdlog_printf_arginfo_sockaddr(const struct printf_info *pi __unused,
87    size_t n, int *argt)
88{
89
90	assert(n >= 1);
91	argt[0] = PA_POINTER;
92	return (1);
93}
94
95static int
96pjdlog_printf_render_sockaddr(struct __printf_io *io,
97    const struct printf_info *pi, const void * const *arg)
98{
99	const struct sockaddr_storage *ss;
100	char buf[64];
101	int ret;
102
103	ss = *(const struct sockaddr_storage * const *)arg[0];
104	switch (ss->ss_family) {
105	case AF_INET:
106	    {
107		char addr[INET_ADDRSTRLEN];
108		const struct sockaddr_in *sin;
109		unsigned int port;
110
111		sin = (const struct sockaddr_in *)ss;
112		port = ntohs(sin->sin_port);
113		if (inet_ntop(ss->ss_family, &sin->sin_addr, addr,
114		    sizeof(addr)) == NULL) {
115			PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.",
116			    strerror(errno));
117		}
118		snprintf(buf, sizeof(buf), "%s:%u", addr, port);
119		break;
120	    }
121	case AF_INET6:
122	    {
123		char addr[INET6_ADDRSTRLEN];
124		const struct sockaddr_in6 *sin;
125		unsigned int port;
126
127		sin = (const struct sockaddr_in6 *)ss;
128		port = ntohs(sin->sin6_port);
129		if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr,
130		    sizeof(addr)) == NULL) {
131			PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.",
132			    strerror(errno));
133		}
134		snprintf(buf, sizeof(buf), "[%s]:%u", addr, port);
135		break;
136	    }
137	default:
138		snprintf(buf, sizeof(buf), "[unsupported family %hhu]",
139		    ss->ss_family);
140		break;
141	}
142	ret = __printf_out(io, pi, buf, strlen(buf));
143	__printf_flush(io);
144	return (ret);
145}
146
147void
148pjdlog_init(int mode)
149{
150	int saved_errno;
151
152	assert(pjdlog_initialized == PJDLOG_NEVER_INITIALIZED ||
153	    pjdlog_initialized == PJDLOG_NOT_INITIALIZED);
154	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
155
156	saved_errno = errno;
157
158	if (pjdlog_initialized == PJDLOG_NEVER_INITIALIZED) {
159		__use_xprintf = 1;
160		register_printf_render_std("T");
161		register_printf_render('N',
162		    pjdlog_printf_render_humanized_number,
163		    pjdlog_printf_arginfo_humanized_number);
164		register_printf_render('S',
165		    pjdlog_printf_render_sockaddr,
166		    pjdlog_printf_arginfo_sockaddr);
167	}
168
169	if (mode == PJDLOG_MODE_SYSLOG)
170		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
171	pjdlog_mode = mode;
172	pjdlog_debug_level = 0;
173	bzero(pjdlog_prefix, sizeof(pjdlog_prefix));
174
175	pjdlog_initialized = PJDLOG_INITIALIZED;
176
177	errno = saved_errno;
178}
179
180void
181pjdlog_fini(void)
182{
183	int saved_errno;
184
185	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
186
187	saved_errno = errno;
188
189	if (pjdlog_mode == PJDLOG_MODE_SYSLOG)
190		closelog();
191
192	pjdlog_initialized = PJDLOG_NOT_INITIALIZED;
193
194	errno = saved_errno;
195}
196
197/*
198 * Configure where the logs should go.
199 * By default they are send to stdout/stderr, but after going into background
200 * (eg. by calling daemon(3)) application is responsible for changing mode to
201 * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
202 */
203void
204pjdlog_mode_set(int mode)
205{
206	int saved_errno;
207
208	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
209	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
210
211	if (pjdlog_mode == mode)
212		return;
213
214	saved_errno = errno;
215
216	if (mode == PJDLOG_MODE_SYSLOG)
217		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
218	else /* if (mode == PJDLOG_MODE_STD) */
219		closelog();
220
221	pjdlog_mode = mode;
222
223	errno = saved_errno;
224}
225
226/*
227 * Return current mode.
228 */
229int
230pjdlog_mode_get(void)
231{
232
233	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
234
235	return (pjdlog_mode);
236}
237
238/*
239 * Set debug level. All the logs above the level specified here will be
240 * ignored.
241 */
242void
243pjdlog_debug_set(int level)
244{
245
246	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
247	assert(level >= 0);
248
249	pjdlog_debug_level = level;
250}
251
252/*
253 * Return current debug level.
254 */
255int
256pjdlog_debug_get(void)
257{
258
259	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
260
261	return (pjdlog_debug_level);
262}
263
264/*
265 * Set prefix that will be used before each log.
266 * Setting prefix to NULL will remove it.
267 */
268void
269pjdlog_prefix_set(const char *fmt, ...)
270{
271	va_list ap;
272
273	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
274
275	va_start(ap, fmt);
276	pjdlogv_prefix_set(fmt, ap);
277	va_end(ap);
278}
279
280/*
281 * Set prefix that will be used before each log.
282 * Setting prefix to NULL will remove it.
283 */
284void
285pjdlogv_prefix_set(const char *fmt, va_list ap)
286{
287	int saved_errno;
288
289	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
290	assert(fmt != NULL);
291
292	saved_errno = errno;
293
294	vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap);
295
296	errno = saved_errno;
297}
298
299/*
300 * Convert log level into string.
301 */
302static const char *
303pjdlog_level_string(int loglevel)
304{
305
306	switch (loglevel) {
307	case LOG_EMERG:
308		return ("EMERG");
309	case LOG_ALERT:
310		return ("ALERT");
311	case LOG_CRIT:
312		return ("CRIT");
313	case LOG_ERR:
314		return ("ERROR");
315	case LOG_WARNING:
316		return ("WARNING");
317	case LOG_NOTICE:
318		return ("NOTICE");
319	case LOG_INFO:
320		return ("INFO");
321	case LOG_DEBUG:
322		return ("DEBUG");
323	}
324	assert(!"Invalid log level.");
325	abort();	/* XXX: gcc */
326}
327
328/*
329 * Common log routine.
330 */
331void
332pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...)
333{
334	va_list ap;
335
336	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
337
338	va_start(ap, fmt);
339	pjdlogv_common(loglevel, debuglevel, error, fmt, ap);
340	va_end(ap);
341}
342
343/*
344 * Common log routine, which can handle regular log level as well as debug
345 * level. We decide here where to send the logs (stdout/stderr or syslog).
346 */
347void
348pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt,
349    va_list ap)
350{
351	int saved_errno;
352
353	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
354	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
355	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
356	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
357	    loglevel == LOG_INFO || loglevel == LOG_DEBUG);
358	assert(loglevel != LOG_DEBUG || debuglevel > 0);
359	assert(error >= -1);
360
361	/* Ignore debug above configured level. */
362	if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
363		return;
364
365	saved_errno = errno;
366
367	switch (pjdlog_mode) {
368	case PJDLOG_MODE_STD:
369	    {
370		FILE *out;
371
372		/*
373		 * We send errors and warning to stderr and the rest to stdout.
374		 */
375		switch (loglevel) {
376		case LOG_EMERG:
377		case LOG_ALERT:
378		case LOG_CRIT:
379		case LOG_ERR:
380		case LOG_WARNING:
381			out = stderr;
382			break;
383		case LOG_NOTICE:
384		case LOG_INFO:
385		case LOG_DEBUG:
386			out = stdout;
387			break;
388		default:
389			assert(!"Invalid loglevel.");
390			abort();	/* XXX: gcc */
391		}
392
393		fprintf(out, "[%s]", pjdlog_level_string(loglevel));
394		/* Attach debuglevel if this is debug log. */
395		if (loglevel == LOG_DEBUG)
396			fprintf(out, "[%d]", debuglevel);
397		fprintf(out, " %s", pjdlog_prefix);
398		vfprintf(out, fmt, ap);
399		if (error != -1)
400			fprintf(out, ": %s.", strerror(error));
401		fprintf(out, "\n");
402		fflush(out);
403		break;
404	    }
405	case PJDLOG_MODE_SYSLOG:
406	    {
407		char log[1024];
408		int len;
409
410		len = snprintf(log, sizeof(log), "%s", pjdlog_prefix);
411		if ((size_t)len < sizeof(log))
412			len += vsnprintf(log + len, sizeof(log) - len, fmt, ap);
413		if (error != -1 && (size_t)len < sizeof(log)) {
414			(void)snprintf(log + len, sizeof(log) - len, ": %s.",
415			    strerror(error));
416		}
417		syslog(loglevel, "%s", log);
418		break;
419	    }
420	default:
421		assert(!"Invalid mode.");
422	}
423
424	errno = saved_errno;
425}
426
427/*
428 * Regular logs.
429 */
430void
431pjdlogv(int loglevel, const char *fmt, va_list ap)
432{
433
434	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
435
436	/* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */
437	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
438	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
439	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
440	    loglevel == LOG_INFO);
441
442	pjdlogv_common(loglevel, 0, -1, fmt, ap);
443}
444
445/*
446 * Regular logs.
447 */
448void
449pjdlog(int loglevel, const char *fmt, ...)
450{
451	va_list ap;
452
453	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
454
455	va_start(ap, fmt);
456	pjdlogv(loglevel, fmt, ap);
457	va_end(ap);
458}
459
460/*
461 * Debug logs.
462 */
463void
464pjdlogv_debug(int debuglevel, const char *fmt, va_list ap)
465{
466
467	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
468
469	pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap);
470}
471
472/*
473 * Debug logs.
474 */
475void
476pjdlog_debug(int debuglevel, const char *fmt, ...)
477{
478	va_list ap;
479
480	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
481
482	va_start(ap, fmt);
483	pjdlogv_debug(debuglevel, fmt, ap);
484	va_end(ap);
485}
486
487/*
488 * Error logs with errno logging.
489 */
490void
491pjdlogv_errno(int loglevel, const char *fmt, va_list ap)
492{
493
494	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
495
496	pjdlogv_common(loglevel, 0, errno, fmt, ap);
497}
498
499/*
500 * Error logs with errno logging.
501 */
502void
503pjdlog_errno(int loglevel, const char *fmt, ...)
504{
505	va_list ap;
506
507	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
508
509	va_start(ap, fmt);
510	pjdlogv_errno(loglevel, fmt, ap);
511	va_end(ap);
512}
513
514/*
515 * Log error, errno and exit.
516 */
517void
518pjdlogv_exit(int exitcode, const char *fmt, va_list ap)
519{
520
521	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
522
523	pjdlogv_errno(LOG_ERR, fmt, ap);
524	exit(exitcode);
525	/* NOTREACHED */
526}
527
528/*
529 * Log error, errno and exit.
530 */
531void
532pjdlog_exit(int exitcode, const char *fmt, ...)
533{
534	va_list ap;
535
536	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
537
538	va_start(ap, fmt);
539	pjdlogv_exit(exitcode, fmt, ap);
540	/* NOTREACHED */
541	va_end(ap);
542}
543
544/*
545 * Log error and exit.
546 */
547void
548pjdlogv_exitx(int exitcode, const char *fmt, va_list ap)
549{
550
551	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
552
553	pjdlogv(LOG_ERR, fmt, ap);
554	exit(exitcode);
555	/* NOTREACHED */
556}
557
558/*
559 * Log error and exit.
560 */
561void
562pjdlog_exitx(int exitcode, const char *fmt, ...)
563{
564	va_list ap;
565
566	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
567
568	va_start(ap, fmt);
569	pjdlogv_exitx(exitcode, fmt, ap);
570	/* NOTREACHED */
571	va_end(ap);
572}
573
574/*
575 * Log failure message and exit.
576 */
577void
578pjdlog_abort(const char *func, const char *file, int line,
579    const char *failedexpr, const char *fmt, ...)
580{
581	va_list ap;
582
583	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
584
585	/*
586	 * When there is no message we pass __func__ as 'fmt'.
587	 * It would be cleaner to pass NULL or "", but gcc generates a warning
588	 * for both of those.
589	 */
590	if (fmt != func) {
591		va_start(ap, fmt);
592		pjdlogv_critical(fmt, ap);
593		va_end(ap);
594	}
595	if (failedexpr == NULL) {
596		if (func == NULL) {
597			pjdlog_critical("Aborted at file %s, line %d.", file,
598			    line);
599		} else {
600			pjdlog_critical("Aborted at function %s, file %s, line %d.",
601			    func, file, line);
602		}
603	} else {
604		if (func == NULL) {
605			pjdlog_critical("Assertion failed: (%s), file %s, line %d.",
606			    failedexpr, file, line);
607		} else {
608			pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.",
609			    failedexpr, func, file, line);
610		}
611	}
612	abort();
613}
614