1 /* $OpenBSD: savecore.c,v 1.48 2009/10/27 23:59:34 deraadt Exp $ */ 2 /* $NetBSD: savecore.c,v 1.26 1996/03/18 21:16:05 leo Exp $ */ 3 4 /*- 5 * Copyright (c) 1986, 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/stat.h> 35 #include <sys/mount.h> 36 #include <sys/syslog.h> 37 #include <sys/types.h> 38 #include <sys/time.h> 39 #include <sys/resource.h> 40 41 #include <dirent.h> 42 #include <errno.h> 43 #include <fcntl.h> 44 #include <nlist.h> 45 #include <paths.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <tzfile.h> 50 #include <unistd.h> 51 #include <limits.h> 52 #include <zlib.h> 53 #include <kvm.h> 54 #include <vis.h> 55 56 extern FILE *zopen(const char *fname, const char *mode, int bits); 57 58 #define KREAD(kd, addr, p)\ 59 (kvm_read(kd, addr, (char *)(p), sizeof(*(p))) != sizeof(*(p))) 60 61 struct nlist current_nl[] = { /* Namelist for currently running system. */ 62 #define X_DUMPDEV 0 63 { "_dumpdev" }, 64 #define X_DUMPLO 1 65 { "_dumplo" }, 66 #define X_TIME 2 67 { "_time_second" }, 68 #define X_DUMPSIZE 3 69 { "_dumpsize" }, 70 #define X_VERSION 4 71 { "_version" }, 72 #define X_PANICSTR 5 73 { "_panicstr" }, 74 #define X_DUMPMAG 6 75 { "_dumpmag" }, 76 { NULL }, 77 }; 78 int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; 79 int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; 80 81 struct nlist dump_nl[] = { /* Name list for dumped system. */ 82 { "_dumpdev" }, /* Entries MUST be the same as */ 83 { "_dumplo" }, /* those in current_nl[]. */ 84 { "_time_second" }, 85 { "_dumpsize" }, 86 { "_version" }, 87 { "_panicstr" }, 88 { "_dumpmag" }, 89 { NULL }, 90 }; 91 92 /* Types match kernel declarations. */ 93 long dumplo; /* where dump starts on dumpdev (in blocks) */ 94 off_t dumpoff; /* where dump starts on dumpdev (in bytes) */ 95 u_long dumpmag; /* magic number in dump */ 96 int dumppages; /* amount of memory dumped (in pages) */ 97 u_long dumpsize; /* amount of memory dumped */ 98 99 char *kernel; 100 char *dirn; /* directory to save dumps in */ 101 char *ddname; /* name of dump device */ 102 dev_t dumpdev; /* dump device */ 103 int dumpfd; /* read/write descriptor on block dev */ 104 kvm_t *kd_dump; /* kvm descriptor on block dev */ 105 time_t now; /* current date */ 106 char panic_mesg[1024]; 107 int panicstr; 108 char vers[1024]; 109 110 int clear, zcompress, force, verbose; /* flags */ 111 112 void check_kmem(void); 113 int check_space(void); 114 void clear_dump(void); 115 int Create(char *, int); 116 int dump_exists(void); 117 char *find_dev(dev_t, int); 118 int get_crashtime(void); 119 void kmem_setup(void); 120 void Lseek(int, off_t, int); 121 int Open(char *, int rw); 122 char *rawname(char *s); 123 void save_core(void); 124 void usage(void); 125 126 int 127 main(int argc, char *argv[]) 128 { 129 struct rlimit rl; 130 int ch; 131 132 openlog("savecore", LOG_PERROR, LOG_DAEMON); 133 134 /* Increase our data size to the max if we can. */ 135 if (getrlimit(RLIMIT_DATA, &rl) == 0) { 136 rl.rlim_cur = rl.rlim_max; 137 if (setrlimit(RLIMIT_DATA, &rl) < 0) 138 syslog(LOG_WARNING, "can't set rlimit data size: %m"); 139 } 140 141 while ((ch = getopt(argc, argv, "cdfN:vz")) != -1) 142 switch(ch) { 143 case 'c': 144 clear = 1; 145 break; 146 case 'd': /* Not documented. */ 147 case 'v': 148 verbose = 1; 149 break; 150 case 'f': 151 force = 1; 152 break; 153 case 'N': 154 kernel = optarg; 155 break; 156 case 'z': 157 zcompress = 1; 158 break; 159 case '?': 160 default: 161 usage(); 162 } 163 argc -= optind; 164 argv += optind; 165 166 if (!clear) { 167 if (argc != 1 && argc != 2) 168 usage(); 169 dirn = argv[0]; 170 } 171 if (argc == 2) 172 kernel = argv[1]; 173 174 (void)time(&now); 175 kmem_setup(); 176 177 if (clear) { 178 clear_dump(); 179 return (0); 180 } 181 182 if (!dump_exists() && !force) 183 return (1); 184 185 check_kmem(); 186 187 if (panicstr) 188 syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg); 189 else 190 syslog(LOG_ALERT, "reboot"); 191 192 if ((!get_crashtime() || !check_space()) && !force) 193 return (1); 194 195 save_core(); 196 197 clear_dump(); 198 return (0); 199 } 200 201 char *dump_sys; 202 203 void 204 kmem_setup(void) 205 { 206 kvm_t *kd_kern; 207 char errbuf[_POSIX2_LINE_MAX]; 208 int i, hdrsz; 209 210 /* 211 * Some names we need for the currently running system, others for 212 * the system that was running when the dump was made. The values 213 * obtained from the current system are used to look for things in 214 * /dev/kmem that cannot be found in the dump_sys namelist, but are 215 * presumed to be the same (since the disk partitions are probably 216 * the same!) 217 */ 218 kd_kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); 219 if (kd_kern == NULL) { 220 syslog(LOG_ERR, "%s: kvm_openfiles: %s", _PATH_UNIX, errbuf); 221 exit(1); 222 } 223 if (kvm_nlist(kd_kern, current_nl) == -1) 224 syslog(LOG_ERR, "%s: kvm_nlist: %s", _PATH_UNIX, 225 kvm_geterr(kd_kern)); 226 227 for (i = 0; cursyms[i] != -1; i++) 228 if (current_nl[cursyms[i]].n_value == 0) { 229 syslog(LOG_ERR, "%s: %s not in namelist", 230 _PATH_UNIX, current_nl[cursyms[i]].n_name); 231 exit(1); 232 } 233 234 (void)KREAD(kd_kern, current_nl[X_DUMPDEV].n_value, &dumpdev); 235 if (dumpdev == NODEV) { 236 syslog(LOG_WARNING, "no core dump (no dumpdev)"); 237 exit(1); 238 } 239 (void)KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &dumplo); 240 dumpoff = (off_t)dumplo * DEV_BSIZE; 241 if (verbose) 242 (void)printf("dumpoff = %lld (%ld * %d)\n", 243 (long long)dumpoff, dumplo, DEV_BSIZE); 244 (void) KREAD(kd_kern, current_nl[X_DUMPMAG].n_value, &dumpmag); 245 246 if (kernel == NULL) { 247 if (kvm_read(kd_kern, current_nl[X_VERSION].n_value, 248 vers, sizeof(vers)) == -1) { 249 syslog(LOG_ERR, "%s: kvm_read: version misread", _PATH_UNIX); 250 exit(1); 251 } 252 vers[sizeof(vers) - 1] = '\0'; 253 } 254 255 ddname = find_dev(dumpdev, S_IFBLK); 256 dumpfd = Open(ddname, O_RDWR); 257 258 dump_sys = kernel ? kernel : _PATH_UNIX; 259 kd_dump = kvm_openfiles(kernel, ddname, NULL, O_RDWR, errbuf); 260 if (kd_dump == NULL) { 261 syslog(LOG_ERR, "%s: kvm_openfiles: %s", dump_sys, errbuf); 262 exit(1); 263 } 264 265 if (kvm_nlist(kd_dump, dump_nl) == -1) 266 syslog(LOG_ERR, "%s: kvm_nlist: %s", dump_sys, 267 kvm_geterr(kd_dump)); 268 269 for (i = 0; dumpsyms[i] != -1; i++) 270 if (dump_nl[dumpsyms[i]].n_value == 0) { 271 syslog(LOG_ERR, "%s: %s not in namelist", 272 dump_sys, dump_nl[dumpsyms[i]].n_name); 273 exit(1); 274 } 275 hdrsz = kvm_dump_mkheader(kd_dump, dumpoff); 276 if (hdrsz == -1) { 277 if(verbose) 278 syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", dump_sys, 279 kvm_geterr(kd_dump)); 280 syslog(LOG_WARNING, "no core dump"); 281 exit(1); 282 } 283 dumpoff += hdrsz; 284 kvm_close(kd_kern); 285 } 286 287 void 288 check_kmem(void) 289 { 290 char *cp; 291 int panicloc; 292 char core_vers[1024]; 293 294 if (kvm_read(kd_dump, dump_nl[X_VERSION].n_value, core_vers, 295 sizeof(core_vers)) != sizeof(core_vers)) { 296 syslog(LOG_ERR, "%s: kvm_read: version misread", dump_sys); 297 exit(1); 298 } 299 core_vers[sizeof(core_vers) - 1] = '\0'; 300 301 if (strcmp(vers, core_vers) && kernel == 0) { 302 vers[strcspn(vers, "\n")] = '\0'; 303 core_vers[strcspn(core_vers, "\n")] = '\0'; 304 305 syslog(LOG_WARNING, 306 "warning: %s version mismatch:\n\t%s\nand\t%s\n", 307 _PATH_UNIX, vers, core_vers); 308 } 309 310 (void)KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr); 311 if (panicstr) { 312 char c, visout[5]; 313 size_t vislen; 314 315 cp = panic_mesg; 316 panicloc = panicstr; 317 for (;;) { 318 if (KREAD(kd_dump, panicloc, &c) != 0 || c == '\0') 319 break; 320 panicloc++; 321 322 vis(visout, c, VIS_SAFE|VIS_NOSLASH, 0); 323 vislen = strlen(visout); 324 if (cp - panic_mesg + vislen >= sizeof(panic_mesg)) 325 break; 326 strlcat(cp, visout, 327 panic_mesg + sizeof panic_mesg - cp); 328 cp += strlen(cp); 329 } 330 } 331 } 332 333 int 334 dump_exists(void) 335 { 336 u_long newdumpmag; 337 338 (void)KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag); 339 340 /* Read the dump size. */ 341 (void)KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumppages); 342 dumpsize = (u_long)dumppages * getpagesize(); 343 344 /* 345 * Return zero if core dump doesn't seem to be there, and note 346 * it for syslog. This check and return happens after the dump size 347 * is read, so dumpsize is whether or not the core is valid (for -f). 348 */ 349 if (newdumpmag != dumpmag) { 350 if (verbose) 351 syslog(LOG_WARNING, 352 "magic number mismatch (%lx != %lx)", 353 newdumpmag, dumpmag); 354 syslog(LOG_WARNING, "no core dump"); 355 return (0); 356 } 357 return (1); 358 } 359 360 void 361 clear_dump(void) 362 { 363 if (kvm_dump_inval(kd_dump) == -1) 364 syslog(LOG_ERR, "%s: kvm_clear_dump: %s", ddname, 365 kvm_geterr(kd_dump)); 366 367 } 368 369 char buf[1024 * 1024]; 370 371 void 372 save_core(void) 373 { 374 FILE *fp; 375 int bounds, ifd, nr, nw, ofd = -1; 376 char *rawp, path[MAXPATHLEN]; 377 mode_t um; 378 379 um = umask(S_IRWXG|S_IRWXO); 380 381 /* 382 * Get the current number and update the bounds file. Do the update 383 * now, because may fail later and don't want to overwrite anything. 384 */ 385 (void)snprintf(path, sizeof(path), "%s/bounds", dirn); 386 if ((fp = fopen(path, "r")) == NULL) 387 goto err1; 388 if (fgets(buf, sizeof(buf), fp) == NULL) { 389 if (ferror(fp)) 390 err1: syslog(LOG_WARNING, "%s: %s", path, strerror(errno)); 391 bounds = 0; 392 } else 393 bounds = atoi(buf); 394 if (fp != NULL) 395 (void)fclose(fp); 396 if ((fp = fopen(path, "w")) == NULL) 397 syslog(LOG_ERR, "%s: %m", path); 398 else { 399 (void)fprintf(fp, "%d\n", bounds + 1); 400 (void)fclose(fp); 401 } 402 403 /* Create the core file. */ 404 (void)snprintf(path, sizeof(path), "%s%s.%d.core%s", 405 dirn, _PATH_UNIX, bounds, zcompress ? ".Z" : ""); 406 if (zcompress) { 407 if ((fp = zopen(path, "w", 0)) == NULL) { 408 syslog(LOG_ERR, "%s: %s", path, strerror(errno)); 409 exit(1); 410 } 411 } else { 412 ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 413 fp = fdopen(ofd, "w"); 414 if (fp == NULL) { 415 syslog(LOG_ERR, "%s: fdopen: %s", path, strerror(errno)); 416 exit(1); 417 } 418 } 419 420 /* Open the raw device. */ 421 rawp = rawname(ddname); 422 if ((ifd = open(rawp, O_RDONLY)) == -1) { 423 syslog(LOG_WARNING, "%s: %m; using block device", rawp); 424 ifd = dumpfd; 425 } 426 427 /* Seek to the start of the core. */ 428 Lseek(ifd, dumpoff, SEEK_SET); 429 430 if (kvm_dump_wrtheader(kd_dump, fp, dumpsize) == -1) { 431 syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path, 432 kvm_geterr(kd_dump)); 433 exit(1); 434 } 435 436 /* Copy the core file. */ 437 syslog(LOG_NOTICE, "writing %score to %s", 438 zcompress ? "compressed " : "", path); 439 for (; dumpsize != 0; dumpsize -= nr) { 440 (void)printf("%8luK\r", dumpsize / 1024); 441 (void)fflush(stdout); 442 nr = read(ifd, buf, MIN(dumpsize, sizeof(buf))); 443 if (nr <= 0) { 444 if (nr == 0) 445 syslog(LOG_WARNING, 446 "WARNING: EOF on dump device"); 447 else 448 syslog(LOG_ERR, "%s: %m", rawp); 449 goto err2; 450 } 451 nw = fwrite(buf, 1, nr, fp); 452 if (nw != nr) { 453 syslog(LOG_ERR, "%s: %s", 454 path, strerror(nw == 0 ? EIO : errno)); 455 err2: syslog(LOG_WARNING, 456 "WARNING: core may be incomplete"); 457 (void)printf("\n"); 458 exit(1); 459 } 460 } 461 (void)close(ifd); 462 (void)fclose(fp); 463 464 /* Copy the kernel. */ 465 ifd = Open(kernel ? kernel : _PATH_UNIX, O_RDONLY); 466 (void)snprintf(path, sizeof(path), "%s%s.%d%s", 467 dirn, _PATH_UNIX, bounds, zcompress ? ".Z" : ""); 468 if (zcompress) { 469 if ((fp = zopen(path, "w", 0)) == NULL) { 470 syslog(LOG_ERR, "%s: %s", path, strerror(errno)); 471 exit(1); 472 } 473 } else 474 ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 475 syslog(LOG_NOTICE, "writing %skernel to %s", 476 zcompress ? "compressed " : "", path); 477 while ((nr = read(ifd, buf, sizeof(buf))) > 0) { 478 if (zcompress) 479 nw = fwrite(buf, 1, nr, fp); 480 else 481 nw = write(ofd, buf, nr); 482 if (nw != nr) { 483 syslog(LOG_ERR, "%s: %s", 484 path, strerror(nw == 0 ? EIO : errno)); 485 syslog(LOG_WARNING, 486 "WARNING: kernel may be incomplete"); 487 exit(1); 488 } 489 } 490 if (nr < 0) { 491 syslog(LOG_ERR, "%s: %s", 492 kernel ? kernel : _PATH_UNIX, strerror(errno)); 493 syslog(LOG_WARNING, 494 "WARNING: kernel may be incomplete"); 495 exit(1); 496 } 497 if (zcompress) 498 (void)fclose(fp); 499 else 500 (void)close(ofd); 501 (void)umask(um); 502 } 503 504 char * 505 find_dev(dev_t dev, int type) 506 { 507 DIR *dfd; 508 struct dirent *dir; 509 struct stat sb; 510 char *dp, devname[MAXPATHLEN]; 511 512 if ((dfd = opendir(_PATH_DEV)) == NULL) { 513 syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno)); 514 exit(1); 515 } 516 (void)strlcpy(devname, _PATH_DEV, sizeof devname); 517 while ((dir = readdir(dfd))) { 518 (void)strlcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name, 519 sizeof devname - (sizeof(_PATH_DEV) - 1)); 520 if (lstat(devname, &sb)) { 521 syslog(LOG_ERR, "%s: %s", devname, strerror(errno)); 522 continue; 523 } 524 if ((sb.st_mode & S_IFMT) != type) 525 continue; 526 if (dev == sb.st_rdev) { 527 closedir(dfd); 528 if ((dp = strdup(devname)) == NULL) { 529 syslog(LOG_ERR, "%s", strerror(errno)); 530 exit(1); 531 } 532 return (dp); 533 } 534 } 535 closedir(dfd); 536 syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev)); 537 exit(1); 538 } 539 540 char * 541 rawname(char *s) 542 { 543 char *sl, name[MAXPATHLEN]; 544 545 if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') { 546 syslog(LOG_ERR, 547 "can't make raw dump device name from %s", s); 548 return (s); 549 } 550 (void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s, sl + 1); 551 if ((sl = strdup(name)) == NULL) { 552 syslog(LOG_ERR, "%s", strerror(errno)); 553 exit(1); 554 } 555 return (sl); 556 } 557 558 int 559 get_crashtime(void) 560 { 561 time_t dumptime; /* Time the dump was taken. */ 562 563 (void)KREAD(kd_dump, dump_nl[X_TIME].n_value, &dumptime); 564 if (dumptime == 0) { 565 if (verbose) 566 syslog(LOG_ERR, "dump time is zero"); 567 return (0); 568 } 569 (void)printf("savecore: system went down at %s", ctime(&dumptime)); 570 #define LEEWAY (7 * SECSPERDAY) 571 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { 572 (void)printf("dump time is unreasonable\n"); 573 return (0); 574 } 575 return (1); 576 } 577 578 int 579 check_space(void) 580 { 581 FILE *fp; 582 char *tkernel; 583 off_t minfree, spacefree, kernelsize, needed; 584 struct stat st; 585 struct statfs fsbuf; 586 char buf[100], path[MAXPATHLEN]; 587 int fd; 588 589 tkernel = kernel ? kernel : _PATH_UNIX; 590 if (stat(tkernel, &st) < 0) { 591 syslog(LOG_ERR, "%s: %m", tkernel); 592 exit(1); 593 } 594 kernelsize = st.st_blocks * S_BLKSIZE; 595 if ((fd = open(dirn, O_RDONLY, 0)) < 0 || fstatfs(fd, &fsbuf) < 0) { 596 syslog(LOG_ERR, "%s: %m", dirn); 597 exit(1); 598 } 599 close(fd); 600 spacefree = ((off_t)fsbuf.f_bavail * fsbuf.f_bsize) / 1024; 601 602 (void)snprintf(path, sizeof(path), "%s/minfree", dirn); 603 if ((fp = fopen(path, "r")) == NULL) 604 minfree = 0; 605 else { 606 if (fgets(buf, sizeof(buf), fp) == NULL) 607 minfree = 0; 608 else 609 minfree = atoi(buf); 610 (void)fclose(fp); 611 } 612 613 needed = (dumpsize + kernelsize) / 1024; 614 if (minfree > 0 && spacefree - needed < minfree) { 615 syslog(LOG_WARNING, 616 "no dump, not enough free space on device"); 617 return (0); 618 } 619 if (spacefree - needed < minfree) 620 syslog(LOG_WARNING, 621 "dump performed, but free space threshold crossed"); 622 return (1); 623 } 624 625 int 626 Open(char *name, int rw) 627 { 628 int fd; 629 630 if ((fd = open(name, rw, 0)) < 0) { 631 syslog(LOG_ERR, "%s: %m", name); 632 exit(1); 633 } 634 return (fd); 635 } 636 637 void 638 Lseek(int fd, off_t off, int flag) 639 { 640 off_t ret; 641 642 ret = lseek(fd, off, flag); 643 if (ret == -1) { 644 syslog(LOG_ERR, "lseek: %m"); 645 exit(1); 646 } 647 } 648 649 int 650 Create(char *file, int mode) 651 { 652 int fd; 653 654 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode); 655 if (fd < 0) { 656 syslog(LOG_ERR, "%s: %m", file); 657 exit(1); 658 } 659 return (fd); 660 } 661 662 void 663 usage(void) 664 { 665 extern char *__progname; 666 fprintf(stderr, "usage: %s [-cfvz] [-N system] directory\n", 667 __progname); 668 exit(1); 669 } 670