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