savecore.c revision 1.90
1/* $NetBSD: savecore.c,v 1.90 2020/04/03 19:09:43 maxv Exp $ */ 2 3/*- 4 * Copyright (c) 1986, 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#ifndef lint 34__COPYRIGHT("@(#) Copyright (c) 1986, 1992, 1993\ 35 The Regents of the University of California. All rights reserved."); 36#endif /* not lint */ 37 38#ifndef lint 39#if 0 40static char sccsid[] = "@(#)savecore.c 8.5 (Berkeley) 4/28/95"; 41#else 42__RCSID("$NetBSD: savecore.c,v 1.90 2020/04/03 19:09:43 maxv Exp $"); 43#endif 44#endif /* not lint */ 45 46#define _KSYMS_PRIVATE 47 48#include <stdbool.h> 49 50#include <sys/param.h> 51#include <sys/mount.h> 52#include <sys/msgbuf.h> 53#include <sys/syslog.h> 54#include <sys/time.h> 55#include <sys/ksyms.h> 56 57#include <dirent.h> 58#include <errno.h> 59#include <fcntl.h> 60#include <nlist.h> 61#include <paths.h> 62#include <stddef.h> 63#include <stdio.h> 64#include <stdlib.h> 65#include <string.h> 66#include <time.h> 67#include <tzfile.h> 68#include <unistd.h> 69#include <util.h> 70#include <limits.h> 71#include <stdarg.h> 72#include <kvm.h> 73 74extern FILE *zopen(const char *fname, const char *mode); 75 76/* 77 * Note that KREAD_LOGWARN and KREAD_ERR take a variable name, not 78 * pointer to it, unlike KREAD() itself. 79 */ 80#define KREAD(kd, addr, p)\ 81 (kvm_read(kd, addr, (char *)(p), sizeof(*(p))) != sizeof(*(p))) 82#define KREAD_ERR(kd, addr, p, err) \ 83do { \ 84 if (KREAD(kd, addr, &(p)) != 0) { \ 85 err; \ 86 } \ 87} while (0) 88#define KREAD_LOGWARN(kd, addr, p, err) \ 89do { \ 90 if (KREAD(kd, addr, &(p)) != 0) { \ 91 syslog(LOG_WARNING, "%s:%d: kvm_read " #p ": %s", \ 92 __func__, __LINE__, kvm_geterr(kd)); \ 93 err; \ 94 } \ 95} while (0) 96 97static struct nlist current_nl[] = { /* Namelist for currently running system. */ 98#define X_DUMPDEV 0 99 { .n_name = "_dumpdev" }, 100#define X_DUMPLO 1 101 { .n_name = "_dumplo" }, 102#define X_TIME_SECOND 2 103 { .n_name = "_time_second" }, 104#define X_TIME 3 105 { .n_name = "_time" }, 106#define X_DUMPSIZE 4 107 { .n_name = "_dumpsize" }, 108#define X_VERSION 5 109 { .n_name = "_version" }, 110#define X_DUMPMAG 6 111 { .n_name = "_dumpmag" }, 112#define X_PANICSTR 7 113 { .n_name = "_panicstr" }, 114#define X_PANICSTART 8 115 { .n_name = "_panicstart" }, 116#define X_PANICEND 9 117 { .n_name = "_panicend" }, 118#define X_MSGBUF 10 119 { .n_name = "_msgbufp" }, 120#define X_DUMPCDEV 11 121 { .n_name = "_dumpcdev" }, 122#define X_SYMSZ 12 123 { .n_name = "_ksyms_symsz" }, 124#define X_STRSZ 13 125 { .n_name = "_ksyms_strsz" }, 126#define X_KHDR 14 127 { .n_name = "_ksyms_hdr" }, 128#define X_SYMTABS 15 129 { .n_name = "_ksyms_symtabs" }, 130 { .n_name = NULL }, 131}; 132static int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, X_DUMPCDEV, -1 }; 133static int dumpsyms[] = { X_TIME_SECOND, X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, 134 X_DUMPMAG, X_SYMSZ, X_STRSZ, X_KHDR, X_SYMTABS, -1 }; 135 136static struct nlist dump_nl[] = { /* Name list for dumped system. */ 137 { .n_name = "_dumpdev" }, /* Entries MUST be the same as */ 138 { .n_name = "_dumplo" }, /* those in current_nl[]. */ 139 { .n_name = "_time_second" }, 140 { .n_name = "_time" }, 141 { .n_name = "_dumpsize" }, 142 { .n_name = "_version" }, 143 { .n_name = "_dumpmag" }, 144 { .n_name = "_panicstr" }, 145 { .n_name = "_panicstart" }, 146 { .n_name = "_panicend" }, 147 { .n_name = "_msgbufp" }, 148 { .n_name = "_dumpcdev" }, 149 { .n_name = "_ksyms_symsz" }, 150 { .n_name = "_ksyms_strsz" }, 151 { .n_name = "_ksyms_hdr" }, 152 { .n_name = "_ksyms_symtabs" }, 153 { .n_name = NULL }, 154}; 155 156/* Types match kernel declarations. */ 157static off_t dumplo; /* where dump starts on dumpdev */ 158static u_int32_t dumpmag; /* magic number in dump */ 159static int dumpsize; /* amount of memory dumped */ 160static off_t dumpbytes; /* in bytes */ 161 162static const char *kernel; /* name of used kernel */ 163static const char *dirname; /* directory to save dumps in */ 164static char *ddname; /* name of dump device */ 165static dev_t dumpdev; /* dump device */ 166static dev_t dumpcdev = NODEV; /* dump device (char equivalent) */ 167static int dumpfd; /* read/write descriptor on dev */ 168static kvm_t *kd_dump; /* kvm descriptor on dev */ 169static time_t now; /* current date */ 170static char panic_mesg[1024]; 171static long panicstr; 172static char vers[1024]; 173static char gzmode[3]; 174 175static void check_kmem(void); 176static int check_space(void); 177static void clear_dump(void); 178static int Create(char *, int); 179static int dump_exists(int); 180static char *find_dev(dev_t, mode_t); 181static int get_crashtime(void); 182static void kmem_setup(int); 183static void Lseek(int, off_t, int); 184static int Open(const char *, int rw); 185static void save_core(int); 186__dead static void usage(const char *fmt, ...) __printflike(1, 2); 187 188int 189main(int argc, char *argv[]) 190{ 191 int ch, level, testonly, compress, force, clear, verbose; 192 char *ep; 193 194 kernel = NULL; 195 level = 1; /* default to fastest gzip compression */ 196 force = 0; 197 clear = 0; 198 testonly = 0; 199 verbose = 0; 200 compress = 0; 201 gzmode[0] = 'w'; 202 203 openlog("savecore", LOG_PERROR, LOG_DAEMON); 204 205 while ((ch = getopt(argc, argv, "cdfnN:vzZ:")) != -1) 206 switch(ch) { 207 case 'c': 208 clear = 1; 209 break; 210 case 'd': /* Not documented. */ 211 case 'v': 212 verbose = 1; 213 break; 214 case 'f': 215 force = 1; 216 break; 217 case 'n': 218 testonly = 1; 219 break; 220 case 'N': 221 kernel = optarg; 222 break; 223 case 'z': 224 compress = 1; 225 break; 226 case 'Z': 227 level = (int)strtol(optarg, &ep, 10); 228 if (level < 0 || level > 9) 229 usage("Invalid compression `%s'", optarg); 230 break; 231 case '?': 232 usage("Missing argument for flag `%c'", optopt); 233 default: 234 usage("Unknown flag `%c'", ch); 235 } 236 argc -= optind; 237 argv += optind; 238 239 if (argc != 0) 240 dirname = argv[0]; 241 else 242 dirname = "/var/crash"; 243 244 gzmode[1] = level + '0'; 245 246 (void)time(&now); 247 kmem_setup(verbose); 248 249 if (clear && !testonly) { 250 clear_dump(); 251 exit(0); 252 } 253 254 if (!dump_exists(verbose) && !force) 255 exit(1); 256 257 if (testonly) 258 /* If -n was passed and there was a dump, exit at level 0 */ 259 exit(0); 260 261 check_kmem(); 262 263 if (panicstr) 264 syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg); 265 else 266 syslog(LOG_ALERT, "reboot"); 267 268 if ((!get_crashtime() || !check_space()) && !force) 269 exit(1); 270 271 save_core(compress); 272 273 clear_dump(); 274 exit(0); 275} 276 277static void 278read_string(kvm_t *kd, u_long kva, char *buf, size_t size) 279{ 280 size_t i; 281 282 for (i = 0; i < size - 1; i++) { 283 (void)kvm_read(kd, kva + i, buf + i, 1); 284 if (buf[i] == '\0') 285 return; 286 } 287 288 buf[size - 1] = '\0'; 289} 290 291static void 292kmem_setup(int verbose) 293{ 294 long l_dumplo; 295 kvm_t *kd_kern; 296 char errbuf[_POSIX2_LINE_MAX]; 297 int i, hdrsz; 298 299 /* 300 * Some names we need for the currently running system, others for 301 * the system that was running when the dump was made. The values 302 * obtained from the current system are used to look for things in 303 * /dev/kmem that cannot be found in the kernel namelist, but are 304 * presumed to be the same (since the disk partitions are probably 305 * the same!) 306 */ 307 kd_kern = kvm_openfiles(kernel, NULL, NULL, O_RDONLY, errbuf); 308 if (kd_kern == NULL) { 309 syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf); 310 exit(1); 311 } 312 if (kvm_nlist(kd_kern, current_nl) == -1) 313 syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel, 314 kvm_geterr(kd_kern)); 315 316 for (i = 0; cursyms[i] != -1; i++) { 317 if (current_nl[cursyms[i]].n_value != 0) 318 continue; 319 switch (cursyms[i]) { 320 case X_TIME_SECOND: 321 case X_TIME: 322 case X_DUMPCDEV: 323 break; 324 default: 325 syslog(LOG_ERR, "%s: %s not in namelist", 326 kernel, current_nl[cursyms[i]].n_name); 327 exit(1); 328 } 329 } 330 331 KREAD_LOGWARN(kd_kern, current_nl[X_DUMPDEV].n_value, dumpdev, exit(1)); 332 if (dumpdev == NODEV) { 333 syslog(LOG_WARNING, "no core dump (no dumpdev)"); 334 exit(1); 335 } 336 KREAD_LOGWARN(kd_kern, current_nl[X_DUMPLO].n_value, l_dumplo, exit(1)); 337 if (l_dumplo == -1) { 338 syslog(LOG_WARNING, "no core dump (invalid dumplo)"); 339 exit(1); 340 } 341 dumplo = DEV_BSIZE * (off_t) l_dumplo; 342 343 if (verbose) 344 (void)printf("dumplo = %lld (%ld * %ld)\n", 345 (long long)dumplo, (long)(dumplo / DEV_BSIZE), (long)DEV_BSIZE); 346 KREAD_LOGWARN(kd_kern, current_nl[X_DUMPMAG].n_value, dumpmag, exit(1)); 347 348 read_string(kd_kern, current_nl[X_VERSION].n_value, vers, 349 sizeof(vers)); 350 351 if (current_nl[X_DUMPCDEV].n_value != 0) { 352 KREAD_LOGWARN(kd_kern, current_nl[X_DUMPCDEV].n_value, dumpcdev, 353 exit(1)); 354 ddname = find_dev(dumpcdev, S_IFCHR); 355 } else 356 ddname = find_dev(dumpdev, S_IFBLK); 357 if (strncmp(ddname, "/dev/cons", 8) == 0 || 358 strncmp(ddname, "/dev/tty", 7) == 0 || 359 strncmp(ddname, "/dev/pty", 7) == 0 || 360 strncmp(ddname, "/dev/pts", 7) == 0) { 361 syslog(LOG_ERR, "dumpdev %s is tty; override kernel", ddname); 362 exit(1); 363 } 364 dumpfd = Open(ddname, O_RDWR); 365 366 kd_dump = kvm_openfiles(kernel, ddname, NULL, O_RDWR, errbuf); 367 if (kd_dump == NULL) { 368 syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf); 369 exit(1); 370 } 371 372 if (kvm_nlist(kd_dump, dump_nl) == -1) 373 syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel, 374 kvm_geterr(kd_dump)); 375 376 for (i = 0; dumpsyms[i] != -1; i++) 377 if (dump_nl[dumpsyms[i]].n_value == 0 && 378 dumpsyms[i] != X_TIME_SECOND && 379 dumpsyms[i] != X_TIME) { 380 syslog(LOG_ERR, "%s: %s not in namelist", 381 kernel, dump_nl[dumpsyms[i]].n_name); 382 exit(1); 383 } 384 hdrsz = kvm_dump_mkheader(kd_dump, dumplo); 385 386 /* 387 * If 'hdrsz' == 0, kvm_dump_mkheader() failed on the magic-number 388 * checks, ergo no dump is present... 389 */ 390 if (hdrsz == 0) { 391 syslog(LOG_WARNING, "no core dump"); 392 exit(1); 393 } 394 if (hdrsz == -1) { 395 syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", kernel, 396 kvm_geterr(kd_dump)); 397 exit(1); 398 } 399 dumplo += hdrsz; 400 kvm_close(kd_kern); 401} 402 403static void 404check_kmem(void) 405{ 406 char *cp, *bufdata; 407 struct kern_msgbuf msgbuf, *bufp; 408 long panicloc, panicstart, panicend; 409 char core_vers[1024]; 410 411 read_string(kd_dump, dump_nl[X_VERSION].n_value, core_vers, 412 sizeof(core_vers)); 413 414 if (strcmp(vers, core_vers) != 0) 415 syslog(LOG_WARNING, 416 "warning: %s version mismatch:\n\t%s\nand\t%s\n", 417 kvm_getkernelname(kd_dump), vers, core_vers); 418 419 panicstart = panicend = 0; 420 KREAD_LOGWARN(kd_dump, dump_nl[X_PANICSTART].n_value, panicstart, 421 goto nomsguf); 422 KREAD_LOGWARN(kd_dump, dump_nl[X_PANICEND].n_value, panicend, 423 goto nomsguf); 424 425 if (panicstart != 0 && panicend != 0) { 426 KREAD_LOGWARN(kd_dump, dump_nl[X_MSGBUF].n_value, bufp, 427 goto nomsguf); 428 /* Reads msg_bufs[1], but doesn't matter. */ 429 KREAD_LOGWARN(kd_dump, (long)bufp, msgbuf, 430 goto nomsguf); 431 if (msgbuf.msg_magic != MSG_MAGIC) { 432 syslog(LOG_WARNING, "msgbuf magic incorrect (%lx != %lx)", 433 msgbuf.msg_magic, (long)MSG_MAGIC); 434 goto nomsguf; 435 } 436 bufdata = malloc(msgbuf.msg_bufs); 437 if (bufdata == NULL) { 438 syslog(LOG_WARNING, "couldn't allocate space for msgbuf data"); 439 goto nomsguf; 440 } 441 if (kvm_read(kd_dump, (long)&bufp->msg_bufc, bufdata, 442 msgbuf.msg_bufs) != msgbuf.msg_bufs) { 443 syslog(LOG_WARNING, "kvm_read dmesg buffer: %s", 444 kvm_geterr(kd_dump)); 445 free(bufdata); 446 goto nomsguf; 447 } 448 cp = panic_mesg; 449 while (panicstart != panicend && cp < &panic_mesg[sizeof(panic_mesg)-1]) { 450 *cp++ = bufdata[panicstart]; 451 panicstart++; 452 if (panicstart >= msgbuf.msg_bufs) 453 panicstart = 0; 454 } 455 /* Don't end in a new-line */ 456 cp = &panic_mesg[strlen(panic_mesg)] - 1; 457 if (*cp == '\n') 458 *cp = '\0'; 459 panic_mesg[sizeof(panic_mesg) - 1] = '\0'; 460 free(bufdata); 461 462 panicstr = 1; /* anything not zero */ 463 return; 464 } 465nomsguf: 466 KREAD_LOGWARN(kd_dump, dump_nl[X_PANICSTR].n_value, panicstr, 467 return); 468 if (panicstr) { 469 cp = panic_mesg; 470 panicloc = panicstr; 471 do { 472 if (KREAD(kd_dump, panicloc, cp) != 0) { 473 syslog(LOG_WARNING, "kvm_read msgbuf: %s", 474 kvm_geterr(kd_dump)); 475 break; 476 } 477 panicloc++; 478 } while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)-1]); 479 panic_mesg[sizeof(panic_mesg) - 1] = '\0'; 480 } 481} 482 483static int 484dump_exists(int verbose) 485{ 486 u_int32_t newdumpmag; 487 488 /* Read the dump magic and size. */ 489 KREAD_ERR(kd_dump, dump_nl[X_DUMPMAG].n_value, newdumpmag, return 0); 490 KREAD_ERR(kd_dump, dump_nl[X_DUMPSIZE].n_value, dumpsize, return 0); 491 492 dumpbytes = (off_t)dumpsize * getpagesize(); 493 494 /* 495 * Return zero if core dump doesn't seem to be there, and note 496 * it for syslog. This check and return happens after the dump size 497 * is read, so dumpsize is whether or not the core is valid (for -f). 498 */ 499 if (newdumpmag != dumpmag) { 500 if (verbose) 501 syslog(LOG_WARNING, "magic number mismatch " 502 "(%#x != %#x)", newdumpmag, dumpmag); 503 syslog(LOG_WARNING, "no core dump"); 504 return (0); 505 } 506 return (1); 507} 508 509static void 510clear_dump(void) 511{ 512 if (kvm_dump_inval(kd_dump) == -1) 513 syslog(LOG_ERR, "%s: kvm_dump_inval: %s", ddname, 514 kvm_geterr(kd_dump)); 515 516} 517 518static char buf[1024 * 1024]; 519 520static void 521save_kernel(FILE *fp, char *path) 522{ 523 int nw, nr, ifd; 524 525 ifd = Open(kernel, O_RDONLY); 526 while ((nr = read(ifd, buf, sizeof(buf))) > 0) { 527 nw = fwrite(buf, 1, nr, fp); 528 if (nw != nr) { 529 syslog(LOG_ERR, "%s: %s", 530 path, strerror(nw == 0 ? EIO : errno)); 531 syslog(LOG_WARNING, 532 "WARNING: kernel may be incomplete"); 533 exit(1); 534 } 535 } 536 if (nr < 0) { 537 syslog(LOG_ERR, "%s: %m", kernel); 538 syslog(LOG_WARNING, "WARNING: kernel may be incomplete"); 539 exit(1); 540 } 541} 542 543static int 544ksymsget(u_long addr, void *ptr, size_t size) 545{ 546 547 if ((size_t)kvm_read(kd_dump, addr, ptr, size) != size) { 548 syslog(LOG_WARNING, "kvm_read ksyms: %s", kvm_geterr(kd_dump)); 549 return 1; 550 } 551 return 0; 552} 553 554static int 555save_ksyms(FILE *fp, char *path) 556{ 557 struct ksyms_hdr khdr; 558 int nw, symsz, strsz; 559 TAILQ_HEAD(, ksyms_symtab) symtabs; 560 struct ksyms_symtab st, *stptr; 561 void *p; 562 563 /* Get basic info and ELF headers, check if ksyms was on. */ 564 if (ksymsget(dump_nl[X_KHDR].n_value, &khdr, sizeof(khdr))) 565 return 1; 566 if (ksymsget(dump_nl[X_SYMSZ].n_value, &symsz, sizeof(symsz))) 567 return 1; 568 if (ksymsget(dump_nl[X_STRSZ].n_value, &strsz, sizeof(strsz))) 569 return 1; 570 if (symsz == 0 || strsz == 0) 571 return 1; 572 573 /* Update the ELF section headers for symbols/strings. */ 574 khdr.kh_shdr[SYMTAB].sh_size = symsz; 575 khdr.kh_shdr[SYMTAB].sh_info = symsz / sizeof(Elf_Sym); 576 khdr.kh_shdr[STRTAB].sh_offset = symsz + 577 khdr.kh_shdr[SYMTAB].sh_offset; 578 khdr.kh_shdr[STRTAB].sh_size = strsz; 579 580 /* Write out the ELF headers. */ 581 nw = fwrite(&khdr, 1, sizeof(khdr), fp); 582 if (nw != sizeof(khdr)) { 583 syslog(LOG_ERR, "%s: %s", 584 path, strerror(nw == 0 ? EIO : errno)); 585 syslog(LOG_WARNING, 586 "WARNING: kernel may be incomplete"); 587 exit(1); 588 } 589 590 /* Dump symbol table. */ 591 if (ksymsget(dump_nl[X_SYMTABS].n_value, &symtabs, sizeof(symtabs))) 592 return 1; 593 stptr = TAILQ_FIRST(&symtabs); 594 while (stptr != NULL) { 595 if (ksymsget((u_long)stptr, &st, sizeof(st))) 596 return 1; 597 stptr = TAILQ_NEXT(&st, sd_queue); 598 if ((p = malloc(st.sd_symsize)) == NULL) 599 return 1; 600 if (ksymsget((u_long)st.sd_symstart, p, st.sd_symsize)) { 601 free(p); 602 return 1; 603 } 604 nw = fwrite(p, 1, st.sd_symsize, fp); 605 free(p); 606 if (nw != st.sd_symsize) { 607 syslog(LOG_ERR, "%s: %s", 608 path, strerror(nw == 0 ? EIO : errno)); 609 syslog(LOG_WARNING, 610 "WARNING: kernel may be incomplete"); 611 exit(1); 612 } 613 } 614 615 /* Dump string table. */ 616 if (ksymsget(dump_nl[X_SYMTABS].n_value, &symtabs, sizeof(symtabs))) 617 return 1; 618 stptr = TAILQ_FIRST(&symtabs); 619 while (stptr != NULL) { 620 if (ksymsget((u_long)stptr, &st, sizeof(st))) 621 return 1; 622 stptr = TAILQ_NEXT(&st, sd_queue); 623 if ((p = malloc(st.sd_symsize)) == NULL) 624 return 1; 625 if (ksymsget((u_long)st.sd_strstart, p, st.sd_strsize)) { 626 free(p); 627 return 1; 628 } 629 nw = fwrite(p, 1, st.sd_strsize, fp); 630 free(p); 631 if (nw != st.sd_strsize) { 632 syslog(LOG_ERR, "%s: %s", 633 path, strerror(nw == 0 ? EIO : errno)); 634 syslog(LOG_WARNING, 635 "WARNING: kernel may be incomplete"); 636 exit(1); 637 } 638 } 639 640 return 0; 641} 642 643static void 644save_core(int compress) 645{ 646 FILE *fp; 647 int bounds, ifd, nr, nw, ofd, tryksyms; 648 char path[MAXPATHLEN], rbuf[MAXPATHLEN]; 649 const char *rawp; 650 651 ofd = -1; 652 /* 653 * Get the current number and update the bounds file. Do the update 654 * now, because may fail later and don't want to overwrite anything. 655 */ 656 umask(066); 657 (void)snprintf(path, sizeof(path), "%s/bounds", dirname); 658 if ((fp = fopen(path, "r")) == NULL) 659 goto err1; 660 if (fgets(buf, sizeof(buf), fp) == NULL) { 661 if (ferror(fp)) 662err1: syslog(LOG_WARNING, "%s: %m", path); 663 bounds = 0; 664 } else 665 bounds = atoi(buf); 666 if (fp != NULL) 667 (void)fclose(fp); 668 if ((fp = fopen(path, "w")) == NULL) 669 syslog(LOG_ERR, "%s: %m", path); 670 else { 671 (void)fprintf(fp, "%d\n", bounds + 1); 672 (void)fclose(fp); 673 } 674 675 /* Create the core file. */ 676 (void)snprintf(path, sizeof(path), "%s/netbsd.%d.core%s", 677 dirname, bounds, compress ? ".gz" : ""); 678 if (compress) { 679 if ((fp = zopen(path, gzmode)) == NULL) { 680 syslog(LOG_ERR, "%s: %m", path); 681 exit(1); 682 } 683 } else { 684 ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 685 fp = fdopen(ofd, "w"); 686 if (fp == NULL) { 687 syslog(LOG_ERR, "%s: fdopen: %m", path); 688 exit(1); 689 } 690 } 691 692 if (dumpcdev == NODEV) { 693 /* Open the raw device. */ 694 rawp = getdiskrawname(rbuf, sizeof(rbuf), ddname); 695 if (rawp == NULL) { 696 syslog(LOG_WARNING, "%s: %m; can't convert to raw", 697 ddname); 698 rawp = ddname; 699 } 700 if ((ifd = open(rawp, O_RDONLY)) == -1) { 701 syslog(LOG_WARNING, "%s: %m; using block device", 702 rawp); 703 ifd = dumpfd; 704 } 705 } else { 706 rawp = ddname; 707 ifd = dumpfd; 708 } 709 710 /* Seek to the start of the core. */ 711 Lseek(ifd, dumplo, SEEK_SET); 712 713 if (kvm_dump_wrtheader(kd_dump, fp, (int32_t)dumpbytes) == -1) { 714 syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path, 715 kvm_geterr(kd_dump)); 716 exit(1); 717 } 718 719 /* Copy the core file. */ 720 syslog(LOG_NOTICE, "writing %score to %s", 721 compress ? "compressed " : "", path); 722 for (; dumpbytes > (off_t)0; dumpbytes -= (off_t)nr) { 723 char nbuf[7]; 724 humanize_number(nbuf, 7, dumpbytes, "", HN_AUTOSCALE, 0); 725 (void)printf("%7s\r", nbuf); 726 (void)fflush(stdout); 727 nr = read(ifd, buf, MIN(dumpbytes, (off_t)sizeof(buf))); 728 if (nr <= 0) { 729 if (nr == 0) 730 syslog(LOG_WARNING, 731 "WARNING: EOF on dump device"); 732 else 733 syslog(LOG_ERR, "%s: %m", rawp); 734 goto err2; 735 } 736 nw = fwrite(buf, 1, nr, fp); 737 if (nw != nr) { 738 syslog(LOG_ERR, "%s: %s", 739 path, strerror(nw == 0 ? EIO : errno)); 740err2: syslog(LOG_WARNING, 741 "WARNING: core may be incomplete"); 742 (void)printf("\n"); 743 exit(1); 744 } 745 } 746 if (dumpcdev == NODEV) 747 (void)close(ifd); 748 (void)fclose(fp); 749 750 /* Create a kernel. */ 751 (void)snprintf(path, sizeof(path), "%s/netbsd.%d%s", 752 dirname, bounds, compress ? ".gz" : ""); 753 syslog(LOG_NOTICE, "writing %skernel to %s", 754 compress ? "compressed " : "", path); 755 for (tryksyms = 1;; tryksyms = 0) { 756 if (compress) { 757 if ((fp = zopen(path, gzmode)) == NULL) { 758 syslog(LOG_ERR, "%s: %m", path); 759 exit(1); 760 } 761 } else { 762 ofd = Create(path, S_IRUSR | S_IWUSR); 763 fp = fdopen(ofd, "w"); 764 if (fp == NULL) { 765 syslog(LOG_ERR, "fdopen: %m"); 766 exit(1); 767 } 768 } 769 if (tryksyms) { 770 if (!save_ksyms(fp, path)) 771 break; 772 (void)fclose(fp); 773 unlink(path); 774 } else { 775 save_kernel(fp, path); 776 break; 777 } 778 } 779 (void)fclose(fp); 780 781 /* 782 * For development systems where the crash occurs during boot 783 * to multiuser. 784 */ 785 sync(); 786 sleep(1); 787 sync(); 788 sleep(1); 789} 790 791static char * 792find_dev(dev_t dev, mode_t type) 793{ 794 DIR *dfd; 795 struct dirent *dir; 796 struct stat sb; 797 char *dp, device[MAXPATHLEN + 1], *p; 798 size_t l; 799 800 if ((dfd = opendir(_PATH_DEV)) == NULL) { 801 syslog(LOG_ERR, "%s: %m", _PATH_DEV); 802 exit(1); 803 } 804 strlcpy(device, _PATH_DEV, sizeof(device)); 805 p = &device[strlen(device)]; 806 l = sizeof(device) - strlen(device); 807 while ((dir = readdir(dfd))) { 808 strlcpy(p, dir->d_name, l); 809 if (lstat(device, &sb)) { 810 syslog(LOG_ERR, "%s: %m", device); 811 continue; 812 } 813 if ((sb.st_mode & S_IFMT) != type) 814 continue; 815 if (dev == sb.st_rdev) { 816 closedir(dfd); 817 if ((dp = strdup(device)) == NULL) { 818 syslog(LOG_ERR, "%m"); 819 exit(1); 820 } 821 return (dp); 822 } 823 } 824 closedir(dfd); 825 syslog(LOG_ERR, "can't find device %lld/%lld", 826 (long long)major(dev), (long long)minor(dev)); 827 exit(1); 828} 829 830static int 831get_crashtime(void) 832{ 833 time_t dumptime; /* Time the dump was taken. */ 834 struct timeval dtime; 835 836 if (KREAD(kd_dump, dump_nl[X_TIME_SECOND].n_value, &dumptime) != 0) { 837 if (KREAD(kd_dump, dump_nl[X_TIME].n_value, &dtime) != 0) { 838 syslog(LOG_WARNING, "kvm_read dumptime: %s (and _time_second " 839 "is not defined also)", kvm_geterr(kd_dump)); 840 return (0); 841 } 842 dumptime = dtime.tv_sec; 843 } 844 if (dumptime == 0) { 845 syslog(LOG_WARNING, "dump time is zero"); 846 return (0); 847 } 848 syslog(LOG_INFO, "system went down at %s", ctime(&dumptime)); 849#define LEEWAY (60 * SECSPERDAY) 850 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { 851 syslog(LOG_WARNING, "dump time is unreasonable"); 852 return (0); 853 } 854 return (1); 855} 856 857static int 858check_space(void) 859{ 860 FILE *fp; 861 off_t minfree, spacefree, kernelsize, needed; 862 struct stat st; 863 struct statvfs fsbuf; 864 char mbuf[100], path[MAXPATHLEN]; 865 866 /* XXX assume a reasonable default, unless we find a kernel. */ 867 kernelsize = 20 * 1024 * 1024; 868 if (!stat(kernel, &st)) kernelsize = st.st_blocks * S_BLKSIZE; 869 if (statvfs(dirname, &fsbuf) < 0) { 870 syslog(LOG_ERR, "%s: %m", dirname); 871 exit(1); 872 } 873 spacefree = fsbuf.f_bavail; 874 spacefree *= fsbuf.f_frsize; 875 spacefree /= 1024; 876 877 (void)snprintf(path, sizeof(path), "%s/minfree", dirname); 878 if ((fp = fopen(path, "r")) == NULL) 879 minfree = 0; 880 else { 881 if (fgets(mbuf, sizeof(mbuf), fp) == NULL) 882 minfree = 0; 883 else 884 minfree = atoi(mbuf); 885 (void)fclose(fp); 886 } 887 888 needed = (dumpbytes + kernelsize) / 1024; 889 if (minfree > 0 && spacefree - needed < minfree) { 890 syslog(LOG_WARNING, 891 "no dump, not enough free space in %s", dirname); 892 return (0); 893 } 894 if (spacefree - needed < minfree) 895 syslog(LOG_WARNING, 896 "dump performed, but free space threshold crossed"); 897 return (1); 898} 899 900static int 901Open(const char *name, int rw) 902{ 903 int fd; 904 905 if ((fd = open(name, rw, 0)) < 0) { 906 syslog(LOG_ERR, "%s: %m", name); 907 exit(1); 908 } 909 return (fd); 910} 911 912static void 913Lseek(int fd, off_t off, int flag) 914{ 915 off_t ret; 916 917 ret = lseek(fd, off, flag); 918 if (ret == -1) { 919 syslog(LOG_ERR, "lseek: %m"); 920 exit(1); 921 } 922} 923 924static int 925Create(char *file, int mode) 926{ 927 int fd; 928 929 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode); 930 if (fd < 0) { 931 syslog(LOG_ERR, "%s: %m", file); 932 exit(1); 933 } 934 return (fd); 935} 936 937static void 938usage(const char *fmt, ...) 939{ 940 va_list ap; 941 va_start(ap, fmt); 942 (void)vsyslog(LOG_ERR, fmt, ap); 943 va_end(ap); 944 (void)syslog(LOG_ERR, 945 "Usage: %s [-cfnvz] [-N system] [-Z level] [directory]", 946 getprogname()); 947 exit(1); 948} 949