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 * 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 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 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