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