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