savecore.c revision 1.53
1/* $NetBSD: savecore.c,v 1.53 2001/09/12 02:58:29 lukem 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.53 2001/09/12 02:58:29 lukem 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. */ 118long dumplo; /* where dump starts on dumpdev */ 119int 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 gzmode[0] = 'w'; 162 163 openlog("savecore", LOG_PERROR, LOG_DAEMON); 164 165 while ((ch = getopt(argc, argv, "cdfN:vzZ:")) != -1) 166 switch(ch) { 167 case 'c': 168 clear = 1; 169 break; 170 case 'd': /* Not documented. */ 171 case 'v': 172 verbose = 1; 173 break; 174 case 'f': 175 force = 1; 176 break; 177 case 'N': 178 kernel = optarg; 179 break; 180 case 'z': 181 compress = 1; 182 break; 183 case 'Z': 184 level = (int)strtol(optarg, &ep, 10); 185 if (level < 0 || level > 9) { 186 (void)syslog(LOG_ERR, "invalid compression %s", 187 optarg); 188 usage(); 189 } 190 gzmode[1] = level + '0'; 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 if (!clear) 203 dirname = argv[0]; 204 205 if (kernel == NULL) { 206 kernel = getbootfile(); 207 } 208 209 (void)time(&now); 210 kmem_setup(); 211 212 if (clear) { 213 clear_dump(); 214 exit(0); 215 } 216 217 if (!dump_exists() && !force) 218 exit(1); 219 220 check_kmem(); 221 222 if (panicstr) 223 syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg); 224 else 225 syslog(LOG_ALERT, "reboot"); 226 227 if ((!get_crashtime() || !check_space()) && !force) 228 exit(1); 229 230 save_core(); 231 232 clear_dump(); 233 exit(0); 234} 235 236void 237kmem_setup(void) 238{ 239 kvm_t *kd_kern; 240 char errbuf[_POSIX2_LINE_MAX]; 241 int i, hdrsz; 242 243 /* 244 * Some names we need for the currently running system, others for 245 * the system that was running when the dump was made. The values 246 * obtained from the current system are used to look for things in 247 * /dev/kmem that cannot be found in the kernel namelist, but are 248 * presumed to be the same (since the disk partitions are probably 249 * the same!) 250 */ 251 kd_kern = kvm_openfiles(kernel, NULL, NULL, O_RDONLY, errbuf); 252 if (kd_kern == NULL) { 253 syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf); 254 exit(1); 255 } 256 if (kvm_nlist(kd_kern, current_nl) == -1) 257 syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel, 258 kvm_geterr(kd_kern)); 259 260 for (i = 0; cursyms[i] != -1; i++) 261 if (current_nl[cursyms[i]].n_value == 0) { 262 syslog(LOG_ERR, "%s: %s not in namelist", 263 kernel, current_nl[cursyms[i]].n_name); 264 exit(1); 265 } 266 267 if (KREAD(kd_kern, current_nl[X_DUMPDEV].n_value, &dumpdev) != 0) { 268 if (verbose) 269 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern)); 270 exit(1); 271 } 272 if (dumpdev == NODEV) { 273 syslog(LOG_WARNING, "no core dump (no dumpdev)"); 274 exit(1); 275 } 276 if (KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &dumplo) != 0) { 277 if (verbose) 278 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern)); 279 exit(1); 280 } 281 if (dumplo == -1) { 282 syslog(LOG_WARNING, "no core dump (invalid dumplo)"); 283 exit(1); 284 } 285 dumplo *= DEV_BSIZE; 286 if (verbose) 287 (void)printf("dumplo = %ld (%ld * %ld)\n", 288 (long)dumplo, (long)(dumplo / DEV_BSIZE), (long)DEV_BSIZE); 289 if (KREAD(kd_kern, current_nl[X_DUMPMAG].n_value, &dumpmag) != 0) { 290 if (verbose) 291 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern)); 292 exit(1); 293 } 294 295 (void)kvm_read(kd_kern, current_nl[X_VERSION].n_value, vers, 296 sizeof(vers)); 297 vers[sizeof(vers) - 1] = '\0'; 298 299 ddname = find_dev(dumpdev, S_IFBLK); 300 dumpfd = Open(ddname, O_RDWR); 301 302 kd_dump = kvm_openfiles(kernel, ddname, NULL, O_RDWR, errbuf); 303 if (kd_dump == NULL) { 304 syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf); 305 exit(1); 306 } 307 308 if (kvm_nlist(kd_dump, dump_nl) == -1) 309 syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel, 310 kvm_geterr(kd_dump)); 311 312 for (i = 0; dumpsyms[i] != -1; i++) 313 if (dump_nl[dumpsyms[i]].n_value == 0) { 314 syslog(LOG_ERR, "%s: %s not in namelist", 315 kernel, dump_nl[dumpsyms[i]].n_name); 316 exit(1); 317 } 318 hdrsz = kvm_dump_mkheader(kd_dump, (off_t)dumplo); 319 320 /* 321 * If 'hdrsz' == 0, kvm_dump_mkheader() failed on the magic-number 322 * checks, ergo no dump is present... 323 */ 324 if (hdrsz == 0) { 325 syslog(LOG_WARNING, "no core dump"); 326 exit(1); 327 } 328 if (hdrsz == -1) { 329 syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", kernel, 330 kvm_geterr(kd_dump)); 331 exit(1); 332 } 333 dumplo += hdrsz; 334 kvm_close(kd_kern); 335} 336 337void 338check_kmem(void) 339{ 340 char *cp, *bufdata; 341 struct kern_msgbuf msgbuf, *bufp; 342 long panicloc, panicstart, panicend; 343 char core_vers[1024]; 344 345 (void)kvm_read(kd_dump, dump_nl[X_VERSION].n_value, core_vers, 346 sizeof(core_vers)); 347 core_vers[sizeof(core_vers) - 1] = '\0'; 348 349 if (strcmp(vers, core_vers) != 0) 350 syslog(LOG_WARNING, 351 "warning: %s version mismatch:\n\t%s\nand\t%s\n", 352 kernel, vers, core_vers); 353 354 panicstart = panicend = 0; 355 if (KREAD(kd_dump, dump_nl[X_PANICSTART].n_value, &panicstart) != 0) { 356 if (verbose) 357 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 358 goto nomsguf; 359 } 360 if (KREAD(kd_dump, dump_nl[X_PANICEND].n_value, &panicend) != 0) { 361 if (verbose) 362 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 363 goto nomsguf; 364 } 365 if (panicstart != 0 && panicend != 0) { 366 if (KREAD(kd_dump, dump_nl[X_MSGBUF].n_value, &bufp)) { 367 if (verbose) 368 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 369 goto nomsguf; 370 } 371 if (kvm_read(kd_dump, (long)bufp, &msgbuf, 372 offsetof(struct kern_msgbuf, msg_bufc)) != 373 offsetof(struct kern_msgbuf, msg_bufc)) { 374 if (verbose) 375 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 376 goto nomsguf; 377 } 378 if (msgbuf.msg_magic != MSG_MAGIC) { 379 if (verbose) 380 syslog(LOG_WARNING, "msgbuf magic incorrect"); 381 goto nomsguf; 382 } 383 bufdata = malloc(msgbuf.msg_bufs); 384 if (bufdata == NULL) { 385 if (verbose) 386 syslog(LOG_WARNING, "couldn't allocate space for msgbuf data"); 387 goto nomsguf; 388 } 389 if (kvm_read(kd_dump, (long)&bufp->msg_bufc, bufdata, 390 msgbuf.msg_bufs) != msgbuf.msg_bufs) { 391 if (verbose) 392 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 393 goto nomsguf; 394 } 395 cp = panic_mesg; 396 while (panicstart != panicend && cp < &panic_mesg[sizeof(panic_mesg)-1]) { 397 *cp++ = bufdata[panicstart]; 398 panicstart++; 399 if (panicstart >= msgbuf.msg_bufs) 400 panicstart = 0; 401 } 402 /* Don't end in a new-line */ 403 cp = &panic_mesg[strlen(panic_mesg)] - 1; 404 if (*cp == '\n') 405 *cp = '\0'; 406 panic_mesg[sizeof(panic_mesg) - 1] = '\0'; 407 408 panicstr = 1; /* anything not zero */ 409 return; 410 } 411nomsguf: 412 if (KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr) != 0) { 413 if (verbose) 414 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 415 return; 416 } 417 if (panicstr) { 418 cp = panic_mesg; 419 panicloc = panicstr; 420 do { 421 if (KREAD(kd_dump, panicloc, cp) != 0) { 422 if (verbose) 423 syslog(LOG_WARNING, "kvm_read: %s", 424 kvm_geterr(kd_dump)); 425 break; 426 } 427 panicloc++; 428 } while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)-1]); 429 panic_mesg[sizeof(panic_mesg) - 1] = '\0'; 430 } 431} 432 433int 434dump_exists(void) 435{ 436 int newdumpmag; 437 438 if (KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag) != 0) { 439 if (verbose) 440 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 441 return (0); 442 } 443 444 /* Read the dump size. */ 445 if (KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumpsize) != 0) { 446 if (verbose) 447 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 448 return (0); 449 } 450 dumpsize *= getpagesize(); 451 452 /* 453 * Return zero if core dump doesn't seem to be there, and note 454 * it for syslog. This check and return happens after the dump size 455 * is read, so dumpsize is whether or not the core is valid (for -f). 456 */ 457 if (newdumpmag != dumpmag) { 458 if (verbose) 459 syslog(LOG_WARNING, 460 "magic number mismatch (0x%x != 0x%x)", 461 newdumpmag, dumpmag); 462 syslog(LOG_WARNING, "no core dump"); 463 return (0); 464 } 465 return (1); 466} 467 468void 469clear_dump(void) 470{ 471 if (kvm_dump_inval(kd_dump) == -1) 472 syslog(LOG_ERR, "%s: kvm_clear_dump: %s", ddname, 473 kvm_geterr(kd_dump)); 474 475} 476 477char buf[1024 * 1024]; 478 479void 480save_core(void) 481{ 482 FILE *fp; 483 int bounds, ifd, nr, nw, ofd; 484 char *rawp, path[MAXPATHLEN]; 485 486 ofd = -1; 487 /* 488 * Get the current number and update the bounds file. Do the update 489 * now, because may fail later and don't want to overwrite anything. 490 */ 491 umask(066); 492 (void)snprintf(path, sizeof(path), "%s/bounds", dirname); 493 if ((fp = fopen(path, "r")) == NULL) 494 goto err1; 495 if (fgets(buf, sizeof(buf), fp) == NULL) { 496 if (ferror(fp)) 497err1: syslog(LOG_WARNING, "%s: %m", path); 498 bounds = 0; 499 } else 500 bounds = atoi(buf); 501 if (fp != NULL) 502 (void)fclose(fp); 503 if ((fp = fopen(path, "w")) == NULL) 504 syslog(LOG_ERR, "%s: %m", path); 505 else { 506 (void)fprintf(fp, "%d\n", bounds + 1); 507 (void)fclose(fp); 508 } 509 510 /* Create the core file. */ 511 (void)snprintf(path, sizeof(path), "%s/netbsd.%d.core%s", 512 dirname, bounds, compress ? ".gz" : ""); 513 if (compress) { 514 if ((fp = zopen(path, gzmode)) == NULL) { 515 syslog(LOG_ERR, "%s: %m", path); 516 exit(1); 517 } 518 } else { 519 ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 520 fp = fdopen(ofd, "w"); 521 if (fp == NULL) { 522 syslog(LOG_ERR, "%s: fdopen: %m", path); 523 exit(1); 524 } 525 } 526 527 /* Open the raw device. */ 528 rawp = rawname(ddname); 529 if ((ifd = open(rawp, O_RDONLY)) == -1) { 530 syslog(LOG_WARNING, "%s: %m; using block device", rawp); 531 ifd = dumpfd; 532 } 533 534 /* Seek to the start of the core. */ 535 Lseek(ifd, (off_t)dumplo, SEEK_SET); 536 537 if (kvm_dump_wrtheader(kd_dump, fp, dumpsize) == -1) { 538 syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path, 539 kvm_geterr(kd_dump)); 540 exit(1); 541 } 542 543 /* Copy the core file. */ 544 syslog(LOG_NOTICE, "writing %score to %s", 545 compress ? "compressed " : "", path); 546 for (; dumpsize > 0; dumpsize -= nr) { 547 (void)printf("%8dK\r", dumpsize / 1024); 548 (void)fflush(stdout); 549 nr = read(ifd, buf, MIN(dumpsize, sizeof(buf))); 550 if (nr <= 0) { 551 if (nr == 0) 552 syslog(LOG_WARNING, 553 "WARNING: EOF on dump device"); 554 else 555 syslog(LOG_ERR, "%s: %m", rawp); 556 goto err2; 557 } 558 nw = fwrite(buf, 1, nr, fp); 559 if (nw != nr) { 560 syslog(LOG_ERR, "%s: %s", 561 path, strerror(nw == 0 ? EIO : errno)); 562err2: syslog(LOG_WARNING, 563 "WARNING: core may be incomplete"); 564 (void)printf("\n"); 565 exit(1); 566 } 567 } 568 (void)close(ifd); 569 (void)fclose(fp); 570 571 /* Copy the kernel. */ 572 ifd = Open(kernel, O_RDONLY); 573 (void)snprintf(path, sizeof(path), "%s/netbsd.%d%s", 574 dirname, bounds, compress ? ".gz" : ""); 575 if (compress) { 576 if ((fp = zopen(path, gzmode)) == NULL) { 577 syslog(LOG_ERR, "%s: %m", path); 578 exit(1); 579 } 580 } else 581 ofd = Create(path, S_IRUSR | S_IWUSR); 582 syslog(LOG_NOTICE, "writing %skernel to %s", 583 compress ? "compressed " : "", path); 584 while ((nr = read(ifd, buf, sizeof(buf))) > 0) { 585 if (compress) 586 nw = fwrite(buf, 1, nr, fp); 587 else 588 nw = write(ofd, buf, nr); 589 if (nw != nr) { 590 syslog(LOG_ERR, "%s: %s", 591 path, strerror(nw == 0 ? EIO : errno)); 592 syslog(LOG_WARNING, 593 "WARNING: kernel may be incomplete"); 594 exit(1); 595 } 596 } 597 if (nr < 0) { 598 syslog(LOG_ERR, "%s: %m", kernel); 599 syslog(LOG_WARNING, "WARNING: kernel may be incomplete"); 600 exit(1); 601 } 602 if (compress) 603 (void)fclose(fp); 604 else 605 (void)close(ofd); 606} 607 608char * 609find_dev(dev_t dev, int type) 610{ 611 DIR *dfd; 612 struct dirent *dir; 613 struct stat sb; 614 char *dp, devname[MAXPATHLEN + 1]; 615 616 if ((dfd = opendir(_PATH_DEV)) == NULL) { 617 syslog(LOG_ERR, "%s: %m", _PATH_DEV); 618 exit(1); 619 } 620 (void)strcpy(devname, _PATH_DEV); 621 while ((dir = readdir(dfd))) { 622 (void)strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name); 623 if (lstat(devname, &sb)) { 624 syslog(LOG_ERR, "%s: %m", devname); 625 continue; 626 } 627 if ((sb.st_mode & S_IFMT) != type) 628 continue; 629 if (dev == sb.st_rdev) { 630 closedir(dfd); 631 if ((dp = strdup(devname)) == NULL) { 632 syslog(LOG_ERR, "%m"); 633 exit(1); 634 } 635 return (dp); 636 } 637 } 638 closedir(dfd); 639 syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev)); 640 exit(1); 641} 642 643char * 644rawname(char *s) 645{ 646 char *sl; 647 char name[MAXPATHLEN]; 648 649 if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') { 650 syslog(LOG_ERR, 651 "can't make raw dump device name from %s", s); 652 return (s); 653 } 654 (void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s, 655 sl + 1); 656 if ((sl = strdup(name)) == NULL) { 657 syslog(LOG_ERR, "%m"); 658 exit(1); 659 } 660 return (sl); 661} 662 663int 664get_crashtime(void) 665{ 666 struct timeval dtime; 667 time_t dumptime; /* Time the dump was taken. */ 668 669 if (KREAD(kd_dump, dump_nl[X_TIME].n_value, &dtime) != 0) { 670 if (verbose) 671 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 672 return (0); 673 } 674 dumptime = dtime.tv_sec; 675 if (dumptime == 0) { 676 if (verbose) 677 syslog(LOG_ERR, "dump time is zero"); 678 return (0); 679 } 680 (void)printf("savecore: system went down at %s", ctime(&dumptime)); 681#define LEEWAY (7 * SECSPERDAY) 682 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { 683 (void)printf("dump time is unreasonable\n"); 684 return (0); 685 } 686 return (1); 687} 688 689int 690check_space(void) 691{ 692 FILE *fp; 693 off_t minfree, spacefree, kernelsize, needed; 694 struct stat st; 695 struct statfs fsbuf; 696 char mbuf[100], path[MAXPATHLEN]; 697 698#ifdef __GNUC__ 699 (void) &minfree; 700#endif 701 702 if (stat(kernel, &st) < 0) { 703 syslog(LOG_ERR, "%s: %m", kernel); 704 exit(1); 705 } 706 kernelsize = st.st_blocks * S_BLKSIZE; 707 if (statfs(dirname, &fsbuf) < 0) { 708 syslog(LOG_ERR, "%s: %m", dirname); 709 exit(1); 710 } 711 spacefree = fsbuf.f_bavail; 712 spacefree *= fsbuf.f_bsize; 713 spacefree /= 1024; 714 715 (void)snprintf(path, sizeof(path), "%s/minfree", dirname); 716 if ((fp = fopen(path, "r")) == NULL) 717 minfree = 0; 718 else { 719 if (fgets(mbuf, sizeof(mbuf), fp) == NULL) 720 minfree = 0; 721 else 722 minfree = atoi(mbuf); 723 (void)fclose(fp); 724 } 725 726 needed = (dumpsize + kernelsize) / 1024; 727 if (minfree > 0 && spacefree - needed < minfree) { 728 syslog(LOG_WARNING, 729 "no dump, not enough free space in %s", dirname); 730 return (0); 731 } 732 if (spacefree - needed < minfree) 733 syslog(LOG_WARNING, 734 "dump performed, but free space threshold crossed"); 735 return (1); 736} 737 738int 739Open(const char *name, int rw) 740{ 741 int fd; 742 743 if ((fd = open(name, rw, 0)) < 0) { 744 syslog(LOG_ERR, "%s: %m", name); 745 exit(1); 746 } 747 return (fd); 748} 749 750void 751Lseek(int fd, off_t off, int flag) 752{ 753 off_t ret; 754 755 ret = lseek(fd, off, flag); 756 if (ret == -1) { 757 syslog(LOG_ERR, "lseek: %m"); 758 exit(1); 759 } 760} 761 762int 763Create(char *file, int mode) 764{ 765 int fd; 766 767 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode); 768 if (fd < 0) { 769 syslog(LOG_ERR, "%s: %m", file); 770 exit(1); 771 } 772 return (fd); 773} 774 775void 776Write(int fd, void *bp, int size) 777{ 778 int n; 779 780 if ((n = write(fd, bp, size)) < size) { 781 syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO)); 782 exit(1); 783 } 784} 785 786void 787usage(void) 788{ 789 (void)syslog(LOG_ERR, 790 "usage: savecore [-cfvz] [-N system] [-Z level] directory"); 791 exit(1); 792} 793