xref: /netbsd-src/sbin/dmesg/dmesg.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: dmesg.c,v 1.45 2020/01/01 00:24:52 kre 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.45 2020/01/01 00:24:52 kre 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 *
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
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
144 main(int argc, char *argv[])
145 {
146 	struct kern_msgbuf cur;
147 	int ch, newl, log, i;
148 	size_t tstamp, size;
149 	char *p, *bufdata;
150 	char buf[5];
151 #ifndef SMALL
152 	char tbuf[64];
153 	char *memf, *nlistf;
154 	struct timespec boottime;
155 	struct timespec lasttime;
156 	intmax_t sec;
157 	long nsec, fsec;
158 	int scale;
159 	int deltas, quiet, humantime;
160 	bool frac, postts;
161 
162 	static const int bmib[] = { CTL_KERN, KERN_BOOTTIME };
163 	size = sizeof(boottime);
164 
165 	(void)setlocale(LC_ALL, "");
166 	radix = nl_langinfo(RADIXCHAR);
167 	if (radix == NULL)
168 		radix = ".";	/* could also select "," */
169 
170 	boottime.tv_sec = 0;
171 	boottime.tv_nsec = 0;
172 	lasttime.tv_sec = 0;
173 	lasttime.tv_nsec = 0;
174 	deltas = quiet = humantime = 0;
175 
176 	(void)sysctl(bmib, 2, &boottime, &size, NULL, 0);
177 
178 	memf = nlistf = NULL;
179 	while ((ch = getopt(argc, argv, "dM:N:tT")) != -1)
180 		switch(ch) {
181 		case 'd':
182 			deltas = 1;
183 			break;
184 		case 'M':
185 			memf = optarg;
186 			break;
187 		case 'N':
188 			nlistf = optarg;
189 			break;
190 		case 't':
191 			quiet = 1;
192 			break;
193 		case 'T':
194 			humantime++;
195 			break;
196 		case '?':
197 		default:
198 			usage();
199 		}
200 	argc -= optind;
201 	argv += optind;
202 	if (quiet && humantime)
203 		err(EXIT_FAILURE, "-t cannot be used with -T");
204 
205 	if (memf == NULL) {
206 #endif
207 		static const int mmib[2] = { CTL_KERN, KERN_MSGBUF };
208 
209 		if (sysctl(mmib, 2, NULL, &size, NULL, 0) == -1 ||
210 		    (bufdata = malloc(size)) == NULL ||
211 		    sysctl(mmib, 2, bufdata, &size, NULL, 0) == -1)
212 			err(1, "can't get msgbuf");
213 
214 		/* make a dummy struct msgbuf for the display logic */
215 		cur.msg_bufx = 0;
216 		cur.msg_bufs = size;
217 #ifndef SMALL
218 	} else {
219 		kvm_t *kd;
220 		struct kern_msgbuf *bufp;
221 
222 		/*
223 		 * Read in message buffer header and data, and do sanity
224 		 * checks.
225 		 */
226 		kd = kvm_open(nlistf, memf, NULL, O_RDONLY, "dmesg");
227 		if (kd == NULL)
228 			exit (1);
229 		if (kvm_nlist(kd, nl) == -1)
230 			errx(1, "kvm_nlist: %s", kvm_geterr(kd));
231 		if (nl[X_MSGBUF].n_type == 0)
232 			errx(1, "%s: msgbufp not found", nlistf ? nlistf :
233 			    "namelist");
234 		if (KREAD(nl[X_MSGBUF].n_value, bufp))
235 			errx(1, "kvm_read: %s (0x%lx)", kvm_geterr(kd),
236 			    nl[X_MSGBUF].n_value);
237 		if (kvm_read(kd, (long)bufp, &cur,
238 		    offsetof(struct kern_msgbuf, msg_bufc)) !=
239 		    offsetof(struct kern_msgbuf, msg_bufc))
240 			errx(1, "kvm_read: %s (0x%lx)", kvm_geterr(kd),
241 			    (unsigned long)bufp);
242 		if (cur.msg_magic != MSG_MAGIC)
243 			errx(1, "magic number incorrect");
244 		bufdata = malloc(cur.msg_bufs);
245 		if (bufdata == NULL)
246 			errx(1, "couldn't allocate space for buffer data");
247 		if (kvm_read(kd, (long)&bufp->msg_bufc, bufdata,
248 		    cur.msg_bufs) != cur.msg_bufs)
249 			errx(1, "kvm_read: %s", kvm_geterr(kd));
250 		kvm_close(kd);
251 		if (cur.msg_bufx >= cur.msg_bufs)
252 			cur.msg_bufx = 0;
253 	}
254 #endif
255 
256 	/*
257 	 * The message buffer is circular; start at the write pointer
258 	 * (which points the oldest character), and go to the write
259 	 * pointer - 1 (which points the newest character).  I.e, loop
260 	 * over cur.msg_bufs times.  Unused area is skipped since it
261 	 * contains nul.
262 	 */
263 #ifndef SMALL
264 	frac = false;
265 	postts = false;
266 	scale = 0;
267 #endif
268 	for (tstamp = 0, newl = 1, log = i = 0, p = bufdata + cur.msg_bufx;
269 	    i < cur.msg_bufs; i++, p++) {
270 
271 #ifndef SMALL
272 		if (p == bufdata + cur.msg_bufs)
273 			p = bufdata;
274 #define ADDC(c)				\
275     do {				\
276 	if (tstamp < sizeof(tbuf) - 1)	\
277 		tbuf[tstamp++] = (c);	\
278 	if (frac)			\
279 		scale++;		\
280     } while (/*CONSTCOND*/0)
281 #else
282 #define ADDC(c)
283 #endif
284 		ch = *p;
285 		if (ch == '\0')
286 			continue;
287 		/* Skip "\n<.*>" syslog sequences. */
288 		/* Gather timestamp sequences */
289 		if (newl) {
290 #ifndef SMALL
291 			int j;
292 #endif
293 
294 			switch (ch) {
295 #ifndef SMALL
296 			case '[':
297 				frac = false;
298 				scale = 0;
299 				ADDC(ch);
300 				continue;
301 #endif
302 			case '<':
303 				log = 1;
304 				continue;
305 			case '>':
306 				log = 0;
307 				continue;
308 #ifndef SMALL
309 			case ']':
310 				frac = false;
311 				ADDC(ch);
312 				ADDC('\0');
313 				tstamp = 0;
314 				postts = true;
315 				sec = fsec = 0;
316 				switch (sscanf(tbuf, "[%jd.%ld]", &sec, &fsec)){
317 				case EOF:
318 				case 0:
319 					/*???*/
320 					continue;
321 				case 1:
322 					fsec = 0;
323 					break;
324 				case 2:
325 					break;
326 				default:
327 					/* Help */
328 					continue;
329 				}
330 
331 				for (nsec = fsec, j = 9 - scale; --j >= 0; )
332 					nsec *= 10;
333 				if (!quiet || deltas)
334 					printf("[");
335 				if (humantime == 1) {
336 					time_t t;
337 					struct tm tm;
338 
339 					t = boottime.tv_sec + sec;
340 					if (nsec + boottime.tv_nsec >=
341 					    ( 1L		/* 1 second */
342 						 * 1000L	/* ms */
343 						 * 1000L	/* us */
344 						 * 1000L	/* ns */ ))
345 							t++;
346 
347 					if (localtime_r(&t, &tm) != NULL) {
348 						strftime(tbuf, sizeof(tbuf),
349 						    "%a %b %e %H:%M:%S %Z %Y",
350 						    &tm);
351 						printf("%s", tbuf);
352 					}
353 				} else if (humantime > 1) {
354 					const char *fp = fmtydhmsf(tbuf,
355 					    sizeof(tbuf), sec, fsec, humantime);
356 					if (fp) {
357 						printf("%s", fp);
358 					}
359 				} else if (!quiet) {
360 					printf(" %5jd%s", sec, radix);
361 					pnsec(nsec, fsec, scale);
362 				}
363 				if (deltas) {
364 					struct timespec nt = { sec, nsec };
365 					struct timespec dt;
366 
367 					timespecsub(&nt, &lasttime, &dt);
368 					if (humantime || !quiet)
369 						printf(" ");
370 					printf("<% 4jd%s%6.6ld>",
371 					    (intmax_t)dt.tv_sec, radix,
372 					    (dt.tv_nsec+499) / 1000);
373 					lasttime = nt;
374 				}
375 				if (!quiet || deltas)
376 					printf("] ");
377 				continue;
378 #endif
379 			case ' ':
380 #ifndef SMALL
381 				if (!tstamp && postts) {
382 					postts = false;
383 #else
384 				if (!tstamp) {
385 #endif
386 					continue;
387 				}
388 				/*FALLTHROUGH*/
389 			default:
390 #ifndef SMALL
391 				if (tstamp) {
392 				    ADDC(ch);
393 				    if (ch == '.')
394 					frac = true;
395 				    continue;
396 				}
397 #endif
398 				if (log)
399 					continue;
400 				break;
401 			}
402 		}
403 		newl = ch == '\n';
404 		(void)vis(buf, ch, VIS_NOSLASH, 0);
405 #ifndef SMALL
406 		if (buf[1] == 0)
407 			(void)putchar(buf[0]);
408 		else
409 #endif
410 			(void)printf("%s", buf);
411 	}
412 	if (!newl)
413 		(void)putchar('\n');
414 	return EXIT_SUCCESS;
415 }
416 
417 #ifndef SMALL
418 static void
419 usage(void)
420 {
421 
422 	(void)fprintf(stderr, "Usage: %s [-dTt] [-M core] [-N system]\n",
423 		getprogname());
424 	exit(EXIT_FAILURE);
425 }
426 #endif
427