savecore.c revision 1.67
1/* $NetBSD: savecore.c,v 1.67 2006/06/07 20:56:19 kardel 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\n\ 35 The Regents of the University of California. All rights reserved.\n"); 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.67 2006/06/07 20:56:19 kardel Exp $"); 43#endif 44#endif /* not lint */ 45 46#include <sys/param.h> 47#include <sys/mount.h> 48#include <sys/msgbuf.h> 49#include <sys/syslog.h> 50#include <sys/time.h> 51 52#include <dirent.h> 53#include <errno.h> 54#include <fcntl.h> 55#include <nlist.h> 56#include <paths.h> 57#include <stddef.h> 58#include <stdio.h> 59#include <stdlib.h> 60#include <string.h> 61#include <time.h> 62#include <tzfile.h> 63#include <unistd.h> 64#include <util.h> 65#include <limits.h> 66#include <kvm.h> 67 68extern FILE *zopen(const char *fname, const char *mode); 69 70#define KREAD(kd, addr, p)\ 71 (kvm_read(kd, addr, (char *)(p), sizeof(*(p))) != sizeof(*(p))) 72 73struct nlist current_nl[] = { /* Namelist for currently running system. */ 74#define X_DUMPDEV 0 75 { "_dumpdev" }, 76#define X_DUMPLO 1 77 { "_dumplo" }, 78#define X_TIME_SECOND 2 79#ifdef __HAVE_TIMECOUNTER 80 { "_time_second" }, 81#else 82 { "_time" }, /* XXX uses same array slot as "X_TIME_SECOND" */ 83#endif 84#define X_DUMPSIZE 3 85 { "_dumpsize" }, 86#define X_VERSION 4 87 { "_version" }, 88#define X_DUMPMAG 5 89 { "_dumpmag" }, 90#define X_PANICSTR 6 91 { "_panicstr" }, 92#define X_PANICSTART 7 93 { "_panicstart" }, 94#define X_PANICEND 8 95 { "_panicend" }, 96#define X_MSGBUF 9 97 { "_msgbufp" }, 98 { NULL }, 99}; 100int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; 101int dumpsyms[] = { X_TIME_SECOND, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, 102 -1 }; 103 104struct nlist dump_nl[] = { /* Name list for dumped system. */ 105 { "_dumpdev" }, /* Entries MUST be the same as */ 106 { "_dumplo" }, /* those in current_nl[]. */ 107#ifdef __HAVE_TIMECOUNTER 108 { "_time_second" }, 109#else 110 { "_time" }, /* XXX uses same array slot as "X_TIME_SECOND" */ 111#endif 112 { "_dumpsize" }, 113 { "_version" }, 114 { "_dumpmag" }, 115 { "_panicstr" }, 116 { "_panicstart" }, 117 { "_panicend" }, 118 { "_msgbufp" }, 119 { NULL }, 120}; 121 122/* Types match kernel declarations. */ 123off_t dumplo; /* where dump starts on dumpdev */ 124u_int32_t dumpmag; /* magic number in dump */ 125int dumpsize; /* amount of memory dumped */ 126off_t dumpbytes; /* in bytes */ 127 128const char *kernel; /* name of used kernel */ 129char *dirname; /* directory to save dumps in */ 130char *ddname; /* name of dump device */ 131dev_t dumpdev; /* dump device */ 132int dumpfd; /* read/write descriptor on block dev */ 133kvm_t *kd_dump; /* kvm descriptor on block dev */ 134time_t now; /* current date */ 135char panic_mesg[1024]; 136long panicstr; 137char vers[1024]; 138char gzmode[3]; 139 140static int clear, compress, force, verbose; /* flags */ 141 142void check_kmem(void); 143int check_space(void); 144void clear_dump(void); 145int Create(char *, int); 146int dump_exists(void); 147char *find_dev(dev_t, int); 148int get_crashtime(void); 149void kmem_setup(void); 150void Lseek(int, off_t, int); 151int main(int, char *[]); 152int Open(const char *, int rw); 153char *rawname(char *s); 154void save_core(void); 155void usage(void); 156void Write(int, void *, int); 157 158int 159main(int argc, char *argv[]) 160{ 161 int ch, level, testonly; 162 char *ep; 163 164 dirname = NULL; 165 kernel = NULL; 166 level = 1; /* default to fastest gzip compression */ 167 testonly = 0; 168 gzmode[0] = 'w'; 169 170 openlog("savecore", LOG_PERROR, LOG_DAEMON); 171 172 while ((ch = getopt(argc, argv, "cdfnN:vzZ:")) != -1) 173 switch(ch) { 174 case 'c': 175 clear = 1; 176 break; 177 case 'd': /* Not documented. */ 178 case 'v': 179 verbose = 1; 180 break; 181 case 'f': 182 force = 1; 183 break; 184 case 'n': 185 testonly = 1; 186 break; 187 case 'N': 188 kernel = optarg; 189 break; 190 case 'z': 191 compress = 1; 192 break; 193 case 'Z': 194 level = (int)strtol(optarg, &ep, 10); 195 if (level < 0 || level > 9) { 196 (void)syslog(LOG_ERR, "invalid compression %s", 197 optarg); 198 usage(); 199 } 200 break; 201 case '?': 202 default: 203 usage(); 204 } 205 argc -= optind; 206 argv += optind; 207 208 if (argc != ((clear || testonly) ? 0 : 1)) 209 usage(); 210 211 gzmode[1] = level + '0'; 212 if (!clear) 213 dirname = argv[0]; 214 215 if (kernel == NULL) { 216 kernel = getbootfile(); 217 } 218 219 (void)time(&now); 220 kmem_setup(); 221 222 if (clear && !testonly) { 223 clear_dump(); 224 exit(0); 225 } 226 227 if (!dump_exists() && !force) 228 exit(1); 229 230 if (testonly) 231 /* If -n was passed and there was a dump, exit at level 0 */ 232 exit(0); 233 234 check_kmem(); 235 236 if (panicstr) 237 syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg); 238 else 239 syslog(LOG_ALERT, "reboot"); 240 241 if ((!get_crashtime() || !check_space()) && !force) 242 exit(1); 243 244 save_core(); 245 246 clear_dump(); 247 exit(0); 248} 249 250void 251kmem_setup(void) 252{ 253 kvm_t *kd_kern; 254 char errbuf[_POSIX2_LINE_MAX]; 255 int i, hdrsz; 256 257 /* 258 * Some names we need for the currently running system, others for 259 * the system that was running when the dump was made. The values 260 * obtained from the current system are used to look for things in 261 * /dev/kmem that cannot be found in the kernel namelist, but are 262 * presumed to be the same (since the disk partitions are probably 263 * the same!) 264 */ 265 kd_kern = kvm_openfiles(kernel, NULL, NULL, O_RDONLY, errbuf); 266 if (kd_kern == NULL) { 267 syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf); 268 exit(1); 269 } 270 if (kvm_nlist(kd_kern, current_nl) == -1) 271 syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel, 272 kvm_geterr(kd_kern)); 273 274 for (i = 0; cursyms[i] != -1; i++) 275 if (current_nl[cursyms[i]].n_value == 0) { 276 syslog(LOG_ERR, "%s: %s not in namelist", 277 kernel, current_nl[cursyms[i]].n_name); 278 exit(1); 279 } 280 281 if (KREAD(kd_kern, current_nl[X_DUMPDEV].n_value, &dumpdev) != 0) { 282 if (verbose) 283 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern)); 284 exit(1); 285 } 286 if (dumpdev == NODEV) { 287 syslog(LOG_WARNING, "no core dump (no dumpdev)"); 288 exit(1); 289 } 290 { 291 long l_dumplo; 292 293 if (KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &l_dumplo) != 0) { 294 if (verbose) 295 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern)); 296 exit(1); 297 } 298 if (l_dumplo == -1) { 299 syslog(LOG_WARNING, "no core dump (invalid dumplo)"); 300 exit(1); 301 } 302 dumplo = DEV_BSIZE * (off_t) l_dumplo; 303 } 304 305 if (verbose) 306 (void)printf("dumplo = %lld (%ld * %ld)\n", 307 (long long)dumplo, (long)(dumplo / DEV_BSIZE), (long)DEV_BSIZE); 308 if (KREAD(kd_kern, current_nl[X_DUMPMAG].n_value, &dumpmag) != 0) { 309 if (verbose) 310 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern)); 311 exit(1); 312 } 313 314 (void)kvm_read(kd_kern, current_nl[X_VERSION].n_value, vers, 315 sizeof(vers)); 316 vers[sizeof(vers) - 1] = '\0'; 317 318 ddname = find_dev(dumpdev, S_IFBLK); 319 dumpfd = Open(ddname, O_RDWR); 320 321 kd_dump = kvm_openfiles(kernel, ddname, NULL, O_RDWR, errbuf); 322 if (kd_dump == NULL) { 323 syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf); 324 exit(1); 325 } 326 327 if (kvm_nlist(kd_dump, dump_nl) == -1) 328 syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel, 329 kvm_geterr(kd_dump)); 330 331 for (i = 0; dumpsyms[i] != -1; i++) 332 if (dump_nl[dumpsyms[i]].n_value == 0) { 333 syslog(LOG_ERR, "%s: %s not in namelist", 334 kernel, dump_nl[dumpsyms[i]].n_name); 335 exit(1); 336 } 337 hdrsz = kvm_dump_mkheader(kd_dump, dumplo); 338 339 /* 340 * If 'hdrsz' == 0, kvm_dump_mkheader() failed on the magic-number 341 * checks, ergo no dump is present... 342 */ 343 if (hdrsz == 0) { 344 syslog(LOG_WARNING, "no core dump"); 345 exit(1); 346 } 347 if (hdrsz == -1) { 348 syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", kernel, 349 kvm_geterr(kd_dump)); 350 exit(1); 351 } 352 dumplo += hdrsz; 353 kvm_close(kd_kern); 354} 355 356void 357check_kmem(void) 358{ 359 char *cp, *bufdata; 360 struct kern_msgbuf msgbuf, *bufp; 361 long panicloc, panicstart, panicend; 362 char core_vers[1024]; 363 364 (void)kvm_read(kd_dump, dump_nl[X_VERSION].n_value, core_vers, 365 sizeof(core_vers)); 366 core_vers[sizeof(core_vers) - 1] = '\0'; 367 368 if (strcmp(vers, core_vers) != 0) 369 syslog(LOG_WARNING, 370 "warning: %s version mismatch:\n\t%s\nand\t%s\n", 371 kernel, vers, core_vers); 372 373 panicstart = panicend = 0; 374 if (KREAD(kd_dump, dump_nl[X_PANICSTART].n_value, &panicstart) != 0) { 375 if (verbose) 376 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 377 goto nomsguf; 378 } 379 if (KREAD(kd_dump, dump_nl[X_PANICEND].n_value, &panicend) != 0) { 380 if (verbose) 381 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 382 goto nomsguf; 383 } 384 if (panicstart != 0 && panicend != 0) { 385 if (KREAD(kd_dump, dump_nl[X_MSGBUF].n_value, &bufp)) { 386 if (verbose) 387 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 388 goto nomsguf; 389 } 390 if (kvm_read(kd_dump, (long)bufp, &msgbuf, 391 offsetof(struct kern_msgbuf, msg_bufc)) != 392 offsetof(struct kern_msgbuf, msg_bufc)) { 393 if (verbose) 394 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 395 goto nomsguf; 396 } 397 if (msgbuf.msg_magic != MSG_MAGIC) { 398 if (verbose) 399 syslog(LOG_WARNING, "msgbuf magic incorrect"); 400 goto nomsguf; 401 } 402 bufdata = malloc(msgbuf.msg_bufs); 403 if (bufdata == NULL) { 404 if (verbose) 405 syslog(LOG_WARNING, "couldn't allocate space for msgbuf data"); 406 goto nomsguf; 407 } 408 if (kvm_read(kd_dump, (long)&bufp->msg_bufc, bufdata, 409 msgbuf.msg_bufs) != msgbuf.msg_bufs) { 410 if (verbose) 411 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 412 free(bufdata); 413 goto nomsguf; 414 } 415 cp = panic_mesg; 416 while (panicstart != panicend && cp < &panic_mesg[sizeof(panic_mesg)-1]) { 417 *cp++ = bufdata[panicstart]; 418 panicstart++; 419 if (panicstart >= msgbuf.msg_bufs) 420 panicstart = 0; 421 } 422 /* Don't end in a new-line */ 423 cp = &panic_mesg[strlen(panic_mesg)] - 1; 424 if (*cp == '\n') 425 *cp = '\0'; 426 panic_mesg[sizeof(panic_mesg) - 1] = '\0'; 427 free(bufdata); 428 429 panicstr = 1; /* anything not zero */ 430 return; 431 } 432nomsguf: 433 if (KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr) != 0) { 434 if (verbose) 435 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 436 return; 437 } 438 if (panicstr) { 439 cp = panic_mesg; 440 panicloc = panicstr; 441 do { 442 if (KREAD(kd_dump, panicloc, cp) != 0) { 443 if (verbose) 444 syslog(LOG_WARNING, "kvm_read: %s", 445 kvm_geterr(kd_dump)); 446 break; 447 } 448 panicloc++; 449 } while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)-1]); 450 panic_mesg[sizeof(panic_mesg) - 1] = '\0'; 451 } 452} 453 454int 455dump_exists(void) 456{ 457 u_int32_t newdumpmag; 458 459 if (KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag) != 0) { 460 if (verbose) 461 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 462 return (0); 463 } 464 465 /* Read the dump size. */ 466 if (KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumpsize) != 0) { 467 if (verbose) 468 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 469 return (0); 470 } 471 dumpbytes = (off_t)dumpsize * getpagesize(); 472 473 /* 474 * Return zero if core dump doesn't seem to be there, and note 475 * it for syslog. This check and return happens after the dump size 476 * is read, so dumpsize is whether or not the core is valid (for -f). 477 */ 478 if (newdumpmag != dumpmag) { 479 if (verbose) 480 syslog(LOG_WARNING, 481 "magic number mismatch (0x%x != 0x%x)", 482 newdumpmag, dumpmag); 483 syslog(LOG_WARNING, "no core dump"); 484 return (0); 485 } 486 return (1); 487} 488 489void 490clear_dump(void) 491{ 492 if (kvm_dump_inval(kd_dump) == -1) 493 syslog(LOG_ERR, "%s: kvm_clear_dump: %s", ddname, 494 kvm_geterr(kd_dump)); 495 496} 497 498char buf[1024 * 1024]; 499 500void 501save_core(void) 502{ 503 FILE *fp; 504 int bounds, ifd, nr, nw, ofd; 505 char *rawp, path[MAXPATHLEN]; 506 507 ofd = -1; 508 /* 509 * Get the current number and update the bounds file. Do the update 510 * now, because may fail later and don't want to overwrite anything. 511 */ 512 umask(066); 513 (void)snprintf(path, sizeof(path), "%s/bounds", dirname); 514 if ((fp = fopen(path, "r")) == NULL) 515 goto err1; 516 if (fgets(buf, sizeof(buf), fp) == NULL) { 517 if (ferror(fp)) 518err1: syslog(LOG_WARNING, "%s: %m", path); 519 bounds = 0; 520 } else 521 bounds = atoi(buf); 522 if (fp != NULL) 523 (void)fclose(fp); 524 if ((fp = fopen(path, "w")) == NULL) 525 syslog(LOG_ERR, "%s: %m", path); 526 else { 527 (void)fprintf(fp, "%d\n", bounds + 1); 528 (void)fclose(fp); 529 } 530 531 /* Create the core file. */ 532 (void)snprintf(path, sizeof(path), "%s/netbsd.%d.core%s", 533 dirname, bounds, compress ? ".gz" : ""); 534 if (compress) { 535 if ((fp = zopen(path, gzmode)) == NULL) { 536 syslog(LOG_ERR, "%s: %m", path); 537 exit(1); 538 } 539 } else { 540 ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 541 fp = fdopen(ofd, "w"); 542 if (fp == NULL) { 543 syslog(LOG_ERR, "%s: fdopen: %m", path); 544 exit(1); 545 } 546 } 547 548 /* Open the raw device. */ 549 rawp = rawname(ddname); 550 if ((ifd = open(rawp, O_RDONLY)) == -1) { 551 syslog(LOG_WARNING, "%s: %m; using block device", rawp); 552 ifd = dumpfd; 553 } 554 555 /* Seek to the start of the core. */ 556 Lseek(ifd, dumplo, SEEK_SET); 557 558 if (kvm_dump_wrtheader(kd_dump, fp, (int32_t)dumpbytes) == -1) { 559 syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path, 560 kvm_geterr(kd_dump)); 561 exit(1); 562 } 563 564 /* Copy the core file. */ 565 syslog(LOG_NOTICE, "writing %score to %s", 566 compress ? "compressed " : "", path); 567 for (; dumpbytes > (off_t)0; dumpbytes -= (off_t)nr) { 568 char nbuf[7]; 569 humanize_number(nbuf, 7, dumpbytes, "", HN_AUTOSCALE, 0); 570 (void)printf("%7s\r", nbuf); 571 (void)fflush(stdout); 572 nr = read(ifd, buf, MIN(dumpbytes, sizeof(buf))); 573 if (nr <= 0) { 574 if (nr == 0) 575 syslog(LOG_WARNING, 576 "WARNING: EOF on dump device"); 577 else 578 syslog(LOG_ERR, "%s: %m", rawp); 579 goto err2; 580 } 581 nw = fwrite(buf, 1, nr, fp); 582 if (nw != nr) { 583 syslog(LOG_ERR, "%s: %s", 584 path, strerror(nw == 0 ? EIO : errno)); 585err2: syslog(LOG_WARNING, 586 "WARNING: core may be incomplete"); 587 (void)printf("\n"); 588 exit(1); 589 } 590 } 591 (void)close(ifd); 592 (void)fclose(fp); 593 594 /* Copy the kernel. */ 595 ifd = Open(kernel, O_RDONLY); 596 (void)snprintf(path, sizeof(path), "%s/netbsd.%d%s", 597 dirname, bounds, compress ? ".gz" : ""); 598 if (compress) { 599 if ((fp = zopen(path, gzmode)) == NULL) { 600 syslog(LOG_ERR, "%s: %m", path); 601 exit(1); 602 } 603 } else 604 ofd = Create(path, S_IRUSR | S_IWUSR); 605 syslog(LOG_NOTICE, "writing %skernel to %s", 606 compress ? "compressed " : "", path); 607 while ((nr = read(ifd, buf, sizeof(buf))) > 0) { 608 if (compress) 609 nw = fwrite(buf, 1, nr, fp); 610 else 611 nw = write(ofd, buf, nr); 612 if (nw != nr) { 613 syslog(LOG_ERR, "%s: %s", 614 path, strerror(nw == 0 ? EIO : errno)); 615 syslog(LOG_WARNING, 616 "WARNING: kernel may be incomplete"); 617 exit(1); 618 } 619 } 620 if (nr < 0) { 621 syslog(LOG_ERR, "%s: %m", kernel); 622 syslog(LOG_WARNING, "WARNING: kernel may be incomplete"); 623 exit(1); 624 } 625 if (compress) 626 (void)fclose(fp); 627 else 628 (void)close(ofd); 629} 630 631char * 632find_dev(dev_t dev, int type) 633{ 634 DIR *dfd; 635 struct dirent *dir; 636 struct stat sb; 637 char *dp, device[MAXPATHLEN + 1], *p; 638 size_t l; 639 640 if ((dfd = opendir(_PATH_DEV)) == NULL) { 641 syslog(LOG_ERR, "%s: %m", _PATH_DEV); 642 exit(1); 643 } 644 strlcpy(device, _PATH_DEV, sizeof(device)); 645 p = &device[strlen(device)]; 646 l = sizeof(device) - strlen(device); 647 while ((dir = readdir(dfd))) { 648 strlcpy(p, dir->d_name, l); 649 if (lstat(device, &sb)) { 650 syslog(LOG_ERR, "%s: %m", device); 651 continue; 652 } 653 if ((sb.st_mode & S_IFMT) != type) 654 continue; 655 if (dev == sb.st_rdev) { 656 closedir(dfd); 657 if ((dp = strdup(device)) == NULL) { 658 syslog(LOG_ERR, "%m"); 659 exit(1); 660 } 661 return (dp); 662 } 663 } 664 closedir(dfd); 665 syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev)); 666 exit(1); 667} 668 669char * 670rawname(char *s) 671{ 672 char *sl; 673 char name[MAXPATHLEN]; 674 675 if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') { 676 syslog(LOG_ERR, 677 "can't make raw dump device name from %s", s); 678 return (s); 679 } 680 (void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s, 681 sl + 1); 682 if ((sl = strdup(name)) == NULL) { 683 syslog(LOG_ERR, "%m"); 684 exit(1); 685 } 686 return (sl); 687} 688 689int 690get_crashtime(void) 691{ 692 time_t dumptime; /* Time the dump was taken. */ 693#ifndef __HAVE_TIMECOUNTER 694 struct timeval dtime; 695#endif 696 697#ifdef __HAVE_TIMECOUNTER 698 if (KREAD(kd_dump, dump_nl[X_TIME_SECOND].n_value, &dumptime) != 0) { 699 if (verbose) 700 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 701 return (0); 702 } 703#else 704 if (KREAD(kd_dump, dump_nl[X_TIME_SECOND].n_value, &dtime) != 0) { 705 if (verbose) 706 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 707 return (0); 708 } 709 dumptime = dtime.tv_sec; 710#endif 711 if (dumptime == 0) { 712 if (verbose) 713 syslog(LOG_ERR, "dump time is zero"); 714 return (0); 715 } 716 (void)printf("savecore: system went down at %s", ctime(&dumptime)); 717#define LEEWAY (60 * SECSPERDAY) 718 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { 719 (void)printf("dump time is unreasonable\n"); 720 return (0); 721 } 722 return (1); 723} 724 725int 726check_space(void) 727{ 728 FILE *fp; 729 off_t minfree, spacefree, kernelsize, needed; 730 struct stat st; 731 struct statvfs fsbuf; 732 char mbuf[100], path[MAXPATHLEN]; 733 734#ifdef __GNUC__ 735 (void) &minfree; 736#endif 737 738 if (stat(kernel, &st) < 0) { 739 syslog(LOG_ERR, "%s: %m", kernel); 740 exit(1); 741 } 742 kernelsize = st.st_blocks * S_BLKSIZE; 743 if (statvfs(dirname, &fsbuf) < 0) { 744 syslog(LOG_ERR, "%s: %m", dirname); 745 exit(1); 746 } 747 spacefree = fsbuf.f_bavail; 748 spacefree *= fsbuf.f_frsize; 749 spacefree /= 1024; 750 751 (void)snprintf(path, sizeof(path), "%s/minfree", dirname); 752 if ((fp = fopen(path, "r")) == NULL) 753 minfree = 0; 754 else { 755 if (fgets(mbuf, sizeof(mbuf), fp) == NULL) 756 minfree = 0; 757 else 758 minfree = atoi(mbuf); 759 (void)fclose(fp); 760 } 761 762 needed = (dumpbytes + kernelsize) / 1024; 763 if (minfree > 0 && spacefree - needed < minfree) { 764 syslog(LOG_WARNING, 765 "no dump, not enough free space in %s", dirname); 766 return (0); 767 } 768 if (spacefree - needed < minfree) 769 syslog(LOG_WARNING, 770 "dump performed, but free space threshold crossed"); 771 return (1); 772} 773 774int 775Open(const char *name, int rw) 776{ 777 int fd; 778 779 if ((fd = open(name, rw, 0)) < 0) { 780 syslog(LOG_ERR, "%s: %m", name); 781 exit(1); 782 } 783 return (fd); 784} 785 786void 787Lseek(int fd, off_t off, int flag) 788{ 789 off_t ret; 790 791 ret = lseek(fd, off, flag); 792 if (ret == -1) { 793 syslog(LOG_ERR, "lseek: %m"); 794 exit(1); 795 } 796} 797 798int 799Create(char *file, int mode) 800{ 801 int fd; 802 803 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode); 804 if (fd < 0) { 805 syslog(LOG_ERR, "%s: %m", file); 806 exit(1); 807 } 808 return (fd); 809} 810 811void 812Write(int fd, void *bp, int size) 813{ 814 int n; 815 816 if ((n = write(fd, bp, size)) < size) { 817 syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO)); 818 exit(1); 819 } 820} 821 822void 823usage(void) 824{ 825 (void)syslog(LOG_ERR, 826 "usage: savecore [-cfnvz] [-N system] [-Z level] directory"); 827 exit(1); 828} 829