xref: /netbsd-src/sbin/dmesg/dmesg.c (revision 422de7d163b835424a2fc84b6ffb030839998dd3)
1 /*	$NetBSD: dmesg.c,v 1.51 2022/08/06 10:22:22 rin Exp $	*/
2 /*-
3  * Copyright (c) 1991, 1993
4  *	The Regents of the University of California.  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  * 3. Neither the name of the University nor the names of its contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 #ifndef lint
33 __COPYRIGHT("@(#) Copyright (c) 1991, 1993\
34  The Regents of the University of California.  All rights reserved.");
35 #endif /* not lint */
36 
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)dmesg.c	8.1 (Berkeley) 6/5/93";
40 #else
41 __RCSID("$NetBSD: dmesg.c,v 1.51 2022/08/06 10:22:22 rin Exp $");
42 #endif
43 #endif /* not lint */
44 
45 #include <sys/param.h>
46 #include <sys/msgbuf.h>
47 #include <sys/sysctl.h>
48 
49 #include <err.h>
50 #include <ctype.h>
51 #include <fcntl.h>
52 #include <time.h>
53 #include <kvm.h>
54 #include <nlist.h>
55 #include <stdio.h>
56 #include <stddef.h>
57 #include <stdlib.h>
58 #include <unistd.h>
59 #include <vis.h>
60 
61 #ifndef SMALL
62 #include <langinfo.h>
63 #include <locale.h>
64 
65 static struct nlist nl[] = {
66 #define	X_MSGBUF	0
67 	{ .n_name = "_msgbufp" },
68 	{ .n_name = NULL },
69 };
70 
71 static const char *radix;
72 
73 __dead static void	usage(void);
74 
75 #define	KREAD(addr, var) \
76 	kvm_read(kd, addr, &var, sizeof(var)) != sizeof(var)
77 
78 static const char *
fmtydhmsf(char * b,size_t l,intmax_t t,long nsec,int ht)79 fmtydhmsf(char *b, size_t l, intmax_t t, long nsec, int ht)
80 {
81 	intmax_t s, m, h;
82 	int z;
83 	int prec;
84 	size_t o;
85 
86 	s = t % 60;
87 	t /= 60;
88 
89 	m = t % 60;
90 	t /= 60;
91 
92 	h = t;
93 
94 	z = 0;
95 	o = 0;
96 
97 #define APPENDFMT(fmt, ...)  \
98     do { \
99 	    z = snprintf(b + o, l - o, fmt, __VA_ARGS__); \
100 	    if (z == -1) \
101 		    return b; \
102 	    o += (size_t)z; \
103 	    if (o >= l) \
104 		    return b; \
105     } while (/*CONSTCOND*/0)
106 
107 #define APPEND(a) \
108     do if (a) \
109 	APPENDFMT("%jd%c", a, toupper((unsigned char)__STRING(a)[0])); \
110     while (/*CONSTCOND*/0)
111 #define APPENDS(a, pr, ms) \
112     APPENDFMT("%jd%s%.*ld%c", a, radix, pr, ms, \
113 	toupper((unsigned char)__STRING(a)[0]))
114 
115 	APPENDFMT("%s", "P");
116 	APPENDFMT("%s", "T");
117 	APPEND(h);
118 	APPEND(m);
119 	if (nsec)
120 		nsec = (nsec + 500000) / 1000000;	/* now milliseconds */
121 	prec = 3;
122 	if (nsec && ht == 2) {
123 		while (prec > 0 && (nsec % 10) == 0)
124 			--prec, nsec /= 10;
125 	}
126 	if (nsec || ht > 2)
127 		APPENDS(s, prec, nsec);
128 	else
129 		APPEND(s);
130 	return b;
131 }
132 
133 static void
pnsec(long nsec,long fsec,int scale)134 pnsec(long nsec, long fsec, int scale)
135 {
136 	if (scale > 6)
137 		printf("%6.6ld", (nsec + 499) / 1000);
138 	else
139 		printf("%*.*ld%.*s", scale, scale, fsec, 6 - scale, "000000");
140 }
141 #endif
142 
143 int
main(int argc,char * argv[])144 main(int argc, char *argv[])
145 {
146 	struct kern_msgbuf cur;
147 	int ch, newl, log, i;
148 	size_t size;
149 	char *p, *bufdata;
150 	char buf[5];
151 #ifndef SMALL
152 	size_t tstamp;
153 	char tbuf[64];
154 	char *memf, *nlistf;
155 	struct timespec boottime;
156 	struct timespec lasttime;
157 	intmax_t sec;
158 	long nsec, fsec;
159 	int scale;
160 	int deltas, quiet, humantime;
161 	bool frac, postts;
162 
163 	static const int bmib[] = { CTL_KERN, KERN_BOOTTIME };
164 	size = sizeof(boottime);
165 
166 	(void)setlocale(LC_ALL, "");
167 	radix = nl_langinfo(RADIXCHAR);
168 	if (radix == NULL)
169 		radix = ".";	/* could also select "," */
170 
171 	boottime.tv_sec = 0;
172 	boottime.tv_nsec = 0;
173 	lasttime.tv_sec = 0;
174 	lasttime.tv_nsec = 0;
175 	deltas = quiet = humantime = 0;
176 
177 	(void)sysctl(bmib, 2, &boottime, &size, NULL, 0);
178 
179 	memf = nlistf = NULL;
180 	while ((ch = getopt(argc, argv, "dM:N:tT")) != -1)
181 		switch(ch) {
182 		case 'd':
183 			deltas = 1;
184 			break;
185 		case 'M':
186 			memf = optarg;
187 			break;
188 		case 'N':
189 			nlistf = optarg;
190 			break;
191 		case 't':
192 			quiet = 1;
193 			break;
194 		case 'T':
195 			humantime++;
196 			break;
197 		case '?':
198 		default:
199 			usage();
200 		}
201 	argc -= optind;
202 	argv += optind;
203 	if (quiet && humantime)
204 		err(EXIT_FAILURE, "-t cannot be used with -T");
205 
206 	if (memf == NULL) {
207 #endif
208 		static const int mmib[2] = { CTL_KERN, KERN_MSGBUF };
209 
210 		if (sysctl(mmib, 2, NULL, &size, NULL, 0) == -1 ||
211 		    (bufdata = malloc(size)) == NULL ||
212 		    sysctl(mmib, 2, bufdata, &size, NULL, 0) == -1)
213 			err(1, "can't get msgbuf");
214 
215 		/* make a dummy struct msgbuf for the display logic */
216 		cur.msg_bufx = 0;
217 		cur.msg_bufs = size;
218 #ifndef SMALL
219 	} else {
220 		kvm_t *kd;
221 		struct kern_msgbuf *bufp;
222 
223 		/*
224 		 * Read in message buffer header and data, and do sanity
225 		 * checks.
226 		 */
227 		kd = kvm_open(nlistf, memf, NULL, O_RDONLY, "dmesg");
228 		if (kd == NULL)
229 			exit (1);
230 		if (kvm_nlist(kd, nl) == -1)
231 			errx(1, "kvm_nlist: %s", kvm_geterr(kd));
232 		if (nl[X_MSGBUF].n_type == 0)
233 			errx(1, "%s: msgbufp not found", nlistf ? nlistf :
234 			    "namelist");
235 		if (KREAD(nl[X_MSGBUF].n_value, bufp))
236 			errx(1, "kvm_read: %s (0x%lx)", kvm_geterr(kd),
237 			    nl[X_MSGBUF].n_value);
238 		if (kvm_read(kd, (long)bufp, &cur,
239 		    offsetof(struct kern_msgbuf, msg_bufc)) !=
240 		    offsetof(struct kern_msgbuf, msg_bufc))
241 			errx(1, "kvm_read: %s (0x%lx)", kvm_geterr(kd),
242 			    (unsigned long)bufp);
243 		if (cur.msg_magic != MSG_MAGIC)
244 			errx(1, "magic number incorrect");
245 		bufdata = malloc(cur.msg_bufs);
246 		if (bufdata == NULL)
247 			errx(1, "couldn't allocate space for buffer data");
248 		if (kvm_read(kd, (long)&bufp->msg_bufc, bufdata,
249 		    cur.msg_bufs) != cur.msg_bufs)
250 			errx(1, "kvm_read: %s", kvm_geterr(kd));
251 		kvm_close(kd);
252 		if (cur.msg_bufx >= cur.msg_bufs)
253 			cur.msg_bufx = 0;
254 	}
255 #endif
256 
257 	/*
258 	 * The message buffer is circular; start at the write pointer
259 	 * (which points the oldest character), and go to the write
260 	 * pointer - 1 (which points the newest character).  I.e, loop
261 	 * over cur.msg_bufs times.  Unused area is skipped since it
262 	 * contains nul.
263 	 */
264 #ifndef SMALL
265 	frac = false;
266 	postts = false;
267 	tstamp = 0;
268 	scale = 0;
269 #endif
270 	for (newl = 1, log = i = 0, p = bufdata + cur.msg_bufx;
271 	    i < cur.msg_bufs; i++, p++) {
272 
273 #ifndef SMALL
274 		if (p == bufdata + cur.msg_bufs)
275 			p = bufdata;
276 #define ADDC(c)								\
277     do {								\
278 	if (tstamp < sizeof(tbuf) - 1)					\
279 		tbuf[tstamp++] = (c);					\
280 	else {								\
281 		/* Cannot be a timestamp. */				\
282 		tstamp = 0;						\
283 		tbuf[sizeof(tbuf) - 1] = '\0';				\
284 		goto not_tstamp;					\
285 	}								\
286 	if (frac)							\
287 		scale++;						\
288     } while (0)
289 #define	PRTBUF()							\
290     for (char *_p = tbuf; *_p != '\0'; _p++) {				\
291 	(void)vis(buf, *_p, VIS_NOSLASH, 0);				\
292 	if (buf[1] == 0)						\
293 		(void)putchar(buf[0]);					\
294 	else								\
295 		(void)printf("%s", buf);				\
296     }
297 #endif
298 		ch = *p;
299 		if (ch == '\0')
300 			continue;
301 		/* Skip "\n<.*>" syslog sequences. */
302 		/* Gather timestamp sequences */
303 		if (newl) {
304 #ifndef SMALL
305 			int j;
306 #endif
307 
308 			switch (ch) {
309 #ifndef SMALL
310 			case '[':
311 				frac = false;
312 				scale = 0;
313 				ADDC(ch);
314 				continue;
315 #endif
316 			case '<':
317 				log = 1;
318 				continue;
319 			case '>':
320 				log = 0;
321 				continue;
322 #ifndef SMALL
323 			case ']':
324 				if (tstamp == 0)
325 					goto prchar;
326 				frac = false;
327 				ADDC(ch);
328 				ADDC('\0');
329 				tstamp = 0;
330 				sec = fsec = 0;
331 				switch (sscanf(tbuf, "[%jd.%ld]", &sec, &fsec)){
332 				case 0:
333  not_tstamp:				/* not a timestamp */
334 					PRTBUF();
335 					continue;
336 				case 1:
337 					fsec = 0; /* XXX PRTBUF()? */
338 					break;
339 				case 2:
340 					break;
341 				case EOF:
342 				default:
343 					/* Help */
344 					continue;
345 				}
346 				postts = true;
347 
348 				for (nsec = fsec, j = 9 - scale; --j >= 0; )
349 					nsec *= 10;
350 				if (!quiet || deltas)
351 					printf("[");
352 				if (humantime == 1) {
353 					time_t t;
354 					struct tm tm;
355 
356 					t = boottime.tv_sec + sec;
357 					if (nsec + boottime.tv_nsec >=
358 					    ( 1L		/* 1 second */
359 						 * 1000L	/* ms */
360 						 * 1000L	/* us */
361 						 * 1000L	/* ns */ ))
362 							t++;
363 
364 					if (localtime_r(&t, &tm) != NULL) {
365 						strftime(tbuf, sizeof(tbuf),
366 						    "%a %b %e %H:%M:%S %Z %Y",
367 						    &tm);
368 						printf("%s", tbuf);
369 					}
370 				} else if (humantime > 1) {
371 					const char *fp = fmtydhmsf(tbuf,
372 					    sizeof(tbuf), sec, fsec, humantime);
373 					if (fp) {
374 						printf("%s", fp);
375 					}
376 				} else if (!quiet) {
377 					printf(" %5jd%s", sec, radix);
378 					pnsec(nsec, fsec, scale);
379 				}
380 				if (deltas) {
381 					struct timespec nt = { sec, nsec };
382 					struct timespec dt;
383 
384 					timespecsub(&nt, &lasttime, &dt);
385 					if (humantime || !quiet)
386 						printf(" ");
387 					printf("<% 4jd%s%6.6ld>",
388 					    (intmax_t)dt.tv_sec, radix,
389 					    (dt.tv_nsec+499) / 1000);
390 					lasttime = nt;
391 				}
392 				if (!quiet || deltas)
393 					printf("] ");
394 				continue;
395 #endif
396 			case ' ':
397 #ifndef SMALL
398 				if (!tstamp && postts) {
399 					postts = false;
400 					continue;
401 				}
402 #endif
403 				/*FALLTHROUGH*/
404 			default:
405 #ifndef SMALL
406 				if (tstamp) {
407 					ADDC(ch);
408 					if (ch == '.')
409 						frac = true;
410 					continue;
411 				}
412  prchar:
413 #endif
414 				if (log)
415 					continue;
416 				break;
417 			}
418 		}
419 		newl = ch == '\n';
420 		(void)vis(buf, ch, VIS_NOSLASH, 0);
421 #ifndef SMALL
422 		if (buf[1] == 0)
423 			(void)putchar(buf[0]);
424 		else
425 #endif
426 			(void)printf("%s", buf);
427 	}
428 #ifndef SMALL
429 	/* non-terminated [.*] */
430 	if (tstamp) {
431 		ADDC('\0');
432 		PRTBUF();
433 	}
434 #endif
435 	if (!newl)
436 		(void)putchar('\n');
437 	return EXIT_SUCCESS;
438 }
439 
440 #ifndef SMALL
441 static void
usage(void)442 usage(void)
443 {
444 
445 	(void)fprintf(stderr, "Usage: %s [-dTt] [-M core] [-N system]\n",
446 		getprogname());
447 	exit(EXIT_FAILURE);
448 }
449 #endif
450