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
41 int debuglevel = 0;
42 FILE *debugfp = NULL;
43 static int prio_idx_inititialized = 0;
44
45 static 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
56 static int use_syslog = 1;
57 static int no_debuglog = 0;
58 static int syslog_level_adjust = 0;
59
60 static 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
74 static const char *prio_name_idx[16];
75
76 static void
set_prio_idx_init()77 set_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
92 void
debug_set_debugfp(FILE * fp)93 debug_set_debugfp(FILE *fp)
94 {
95 debugfp = fp;
96 }
97
98 void
debug_use_syslog(int b)99 debug_use_syslog(int b)
100 {
101 if (b)
102 use_syslog = 1;
103 else
104 use_syslog = 0;
105 }
106
107 void
debug_set_no_debuglog(int no_debuglog0)108 debug_set_no_debuglog(int no_debuglog0)
109 {
110 if (no_debuglog0)
111 no_debuglog = 1;
112 else
113 no_debuglog = 0;
114 }
115
116 FILE *
debug_get_debugfp()117 debug_get_debugfp()
118 {
119 return debugfp;
120 }
121
122 #define DL(p) ((p) >> 24 & 0xff)
123 int
vlog_printf(uint32_t prio,const char * format,va_list ap)124 vlog_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];
192 copy_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
224 int
log_printf(int prio,const char * fmt,...)225 log_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
237 void
debug_set_syslog_level_adjust(int adjust)238 debug_set_syslog_level_adjust(int adjust)
239 {
240 syslog_level_adjust = adjust;
241 }
242
243 int
debug_get_syslog_level_adjust(void)244 debug_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 */
257 void
show_hd(FILE * file,const u_char * buf,int len)258 show_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