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