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