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