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