1/*	$OpenBSD: debugutil.c,v 1.7 2024/02/26 08:25:51 yasuoka Exp $ */
2/*-
3 * Copyright (c) 2009 Internet Initiative Japan Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27#include <sys/types.h>
28#include <stdio.h>
29#include <errno.h>
30#include <string.h>
31#include <syslog.h>
32#include <stdarg.h>
33#include <stdlib.h>
34#include <time.h>
35
36#include "debugutil.h"
37
38#define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
39#define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
40
41int debuglevel = 0;
42FILE *debugfp = NULL;
43static int prio_idx_inititialized = 0;
44
45static void  set_prio_idx_init(void);
46
47#ifndef countof
48#define countof(x)	(sizeof((x)) / sizeof((x)[0]))
49#endif
50#define VAL_NAME(x)	{ (x), #x}
51
52#ifndef LOG_PRI
53#define LOG_PRI(p)	((p) & LOG_PRIMASK)
54#endif
55
56static int use_syslog = 1;
57static int no_debuglog = 0;
58static int syslog_level_adjust = 0;
59
60static struct {
61	int prio;
62	const char *name;
63} prio_name[] = {
64	VAL_NAME(LOG_EMERG),
65	VAL_NAME(LOG_ALERT),
66	VAL_NAME(LOG_CRIT),
67	VAL_NAME(LOG_ERR),
68	VAL_NAME(LOG_WARNING),
69	VAL_NAME(LOG_NOTICE),
70	VAL_NAME(LOG_INFO),
71	VAL_NAME(LOG_DEBUG)
72};
73
74static const char *prio_name_idx[16];
75
76static void
77set_prio_idx_init()
78{
79	int i;
80
81	if (prio_idx_inititialized)
82		return;
83	for (i = 0; i < (int)countof(prio_name); i++) {
84		ASSERT(prio_name[i].prio < countof(prio_name_idx));
85		if (prio_name[i].prio >= (int)countof(prio_name_idx))
86		    continue;
87		prio_name_idx[prio_name[i].prio] = &prio_name[i].name[4];
88	}
89	prio_idx_inititialized = 1;
90}
91
92void
93debug_set_debugfp(FILE *fp)
94{
95	debugfp = fp;
96}
97
98void
99debug_use_syslog(int b)
100{
101	if (b)
102		use_syslog = 1;
103	else
104		use_syslog = 0;
105}
106
107void
108debug_set_no_debuglog(int no_debuglog0)
109{
110	if (no_debuglog0)
111		no_debuglog = 1;
112	else
113		no_debuglog = 0;
114}
115
116FILE *
117debug_get_debugfp()
118{
119	return debugfp;
120}
121
122#define	DL(p)		((p) >> 24 & 0xff)
123int
124vlog_printf(uint32_t prio, const char *format, va_list ap)
125{
126	int status = 0, i, fmtoff = 0, state = 0, fmtlen, saved_errno, level;
127	char fmt[8192];
128	struct tm *lt;
129	time_t now;
130
131	ASSERT(format != NULL);
132	ASSERT(format[0] != '\0');
133	if (DL(prio) > 0 && debuglevel < (int)DL(prio))
134		return -1;
135	if (no_debuglog &&  LOG_PRI(prio) >= LOG_DEBUG)
136		return -1;
137
138	if (!prio_idx_inititialized)
139		set_prio_idx_init();
140	if (use_syslog && DL(prio) == 0) {
141		level = LOG_PRI(prio) + syslog_level_adjust;
142		if (!no_debuglog || level < LOG_DEBUG) {
143			level = MINIMUM(LOG_DEBUG, level);
144			level = MAXIMUM(LOG_EMERG, level);
145			level |= (prio & LOG_FACMASK);
146			vsyslog(level, format, ap);
147		}
148	}
149
150	if (debugfp == NULL)
151		return -1;
152
153	time(&now);
154	lt = localtime(&now);
155
156	fmtlen = strlen(format);
157	for (i = 0; i < fmtlen; i++) {
158		/* 2 chars in this block and 2 chars after this block */
159		if (sizeof(fmt) - fmtoff < 4)
160			break;
161		switch(state) {
162		case 0:
163			switch(format[i]) {
164			case '%':
165				state = 1;
166				goto copy_loop;
167			case '\n':
168				fmt[fmtoff++] = '\n';
169				fmt[fmtoff++] = '\t';
170				goto copy_loop;
171			}
172			break;
173		case 1:
174			switch(format[i]) {
175			default:
176			case '%':
177				fmt[fmtoff++] = '%';
178				state = 0;
179				break;
180			case 'm':
181				fmt[fmtoff] = '\0';
182				saved_errno = errno;
183				/* -1 is to reserve for '\n' */
184				strlcat(fmt, strerror(errno), sizeof(fmt) - 1);
185				errno = saved_errno;
186				fmtoff = strlen(fmt);
187				state = 0;
188				goto copy_loop;
189			}
190		}
191		fmt[fmtoff++] = format[i];
192copy_loop:
193		continue;
194	}
195	/* remove trailing TAB */
196	if (fmtoff > 0 && fmt[fmtoff - 1] == '\t')
197		fmtoff--;
198	/* append new line char */
199	if (fmtoff == 0 || fmt[fmtoff-1] != '\n')
200		fmt[fmtoff++] = '\n';
201
202	fmt[fmtoff] = '\0';
203
204	ASSERT(0 <= LOG_PRI(prio)
205	    && LOG_PRI(prio) < countof(prio_name_idx)
206	    && prio_name_idx[LOG_PRI(prio)] != NULL);
207	ftell(debugfp);
208	fprintf(debugfp,
209	    "%04d-%02d-%02d %02d:%02d:%02d:%s: "
210	    , lt->tm_year + 1900
211	    , lt->tm_mon + 1
212	    , lt->tm_mday
213	    , lt->tm_hour
214	    , lt->tm_min
215	    , lt->tm_sec
216	    , (prio & 0xff000000) ? "DEBUG" : prio_name_idx[LOG_PRI(prio)]
217	);
218	status = vfprintf(debugfp, fmt, ap);
219	fflush(debugfp);
220
221	return status;
222}
223
224int
225log_printf(int prio, const char *fmt, ...)
226{
227	int status;
228	va_list ap;
229
230	va_start(ap, fmt);
231	status = vlog_printf((uint32_t)prio, fmt, ap);
232	va_end(ap);
233
234	return status;
235}
236
237void
238debug_set_syslog_level_adjust(int adjust)
239{
240	syslog_level_adjust = adjust;
241}
242
243int
244debug_get_syslog_level_adjust(void)
245{
246	return syslog_level_adjust;
247}
248
249
250/*
251 * show_hd -
252 *	print hexadecimal/ascii dump for debug
253 *
254 * usage:
255 *  show_hd(stderr, buf, sizeof(buf));
256 */
257void
258show_hd(FILE *file, const u_char *buf, int len)
259{
260	int i, o = 0;
261	int hd_cnt = 0;
262	char linebuf[80];
263	char asciibuf[17];
264
265	memset(asciibuf, ' ', sizeof(asciibuf));
266	asciibuf[sizeof(asciibuf)-1] = '\0';
267
268	for (i = 0; i < len; i++) {
269		if (0x20 <= *(buf+i)  && *(buf+i) <= 0x7e)
270			asciibuf[hd_cnt % 16] = *(buf+i);
271		else
272			asciibuf[hd_cnt % 16] = '.';
273
274		switch (hd_cnt % 16) {
275		case 0:
276			o += snprintf(linebuf + o, sizeof(linebuf) - o,
277			    "%04x  %02x", hd_cnt,
278			    (unsigned char)*(buf+i));
279			break;
280		case 15:
281			o += snprintf(linebuf + o, sizeof(linebuf) - o,
282			    "%02x", (unsigned char)*(buf+i));
283			if (file)
284				fprintf(file, "\t%-47s  |%s|\n", linebuf,
285				    asciibuf);
286			else
287				syslog(LOG_ERR, "%-47s  |%s|\n", linebuf,
288				    asciibuf);
289			memset(asciibuf, ' ', sizeof(asciibuf));
290			asciibuf[sizeof(asciibuf)-1] = '\0';
291			o = 0;
292			break;
293		case 8:
294			o += snprintf(linebuf + o, sizeof(linebuf) - o,
295			    "- %02x", (unsigned char)*(buf+i));
296			break;
297		default:
298			if (hd_cnt % 2 == 1)
299				o += snprintf(linebuf + o, sizeof(linebuf) - o,
300				    "%02x ", (unsigned char)*(buf+i));
301			else
302				o += snprintf(linebuf + o, sizeof(linebuf) - o,
303				    "%02x", (unsigned char)*(buf+i));
304			break;
305		}
306		hd_cnt++;
307	}
308	if (hd_cnt > 0 && (hd_cnt % 16) != 0) {
309		if (file)
310			fprintf(file, "\t%-47s  |%s|\n", linebuf, asciibuf);
311		else
312			syslog(LOG_ERR, "%-47s  |%s|\n", linebuf, asciibuf);
313	}
314	if (file)
315		fflush(file);
316}
317