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