savecore.c revision 1.62
1/* $NetBSD: savecore.c,v 1.62 2004/03/30 19:52:02 christos 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.62 2004/03/30 19:52:02 christos 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 2 79 { "_time" }, 80#define X_DUMPSIZE 3 81 { "_dumpsize" }, 82#define X_VERSION 4 83 { "_version" }, 84#define X_DUMPMAG 5 85 { "_dumpmag" }, 86#define X_PANICSTR 6 87 { "_panicstr" }, 88#define X_PANICSTART 7 89 { "_panicstart" }, 90#define X_PANICEND 8 91 { "_panicend" }, 92#define X_MSGBUF 9 93 { "_msgbufp" }, 94 { NULL }, 95}; 96int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; 97int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; 98 99struct nlist dump_nl[] = { /* Name list for dumped system. */ 100 { "_dumpdev" }, /* Entries MUST be the same as */ 101 { "_dumplo" }, /* those in current_nl[]. */ 102 { "_time" }, 103 { "_dumpsize" }, 104 { "_version" }, 105 { "_dumpmag" }, 106 { "_panicstr" }, 107 { "_panicstart" }, 108 { "_panicend" }, 109 { "_msgbufp" }, 110 { NULL }, 111}; 112 113/* Types match kernel declarations. */ 114off_t dumplo; /* where dump starts on dumpdev */ 115u_int32_t dumpmag; /* magic number in dump */ 116int dumpsize; /* amount of memory dumped */ 117 118const char *kernel; /* name of used kernel */ 119char *dirname; /* directory to save dumps in */ 120char *ddname; /* name of dump device */ 121dev_t dumpdev; /* dump device */ 122int dumpfd; /* read/write descriptor on block dev */ 123kvm_t *kd_dump; /* kvm descriptor on block dev */ 124time_t now; /* current date */ 125char panic_mesg[1024]; 126long panicstr; 127char vers[1024]; 128char gzmode[3]; 129 130static int clear, compress, force, verbose; /* flags */ 131 132void check_kmem(void); 133int check_space(void); 134void clear_dump(void); 135int Create(char *, int); 136int dump_exists(void); 137char *find_dev(dev_t, int); 138int get_crashtime(void); 139void kmem_setup(void); 140void Lseek(int, off_t, int); 141int main(int, char *[]); 142int Open(const char *, int rw); 143char *rawname(char *s); 144void save_core(void); 145void usage(void); 146void Write(int, void *, int); 147 148int 149main(int argc, char *argv[]) 150{ 151 int ch, level; 152 char *ep; 153 154 dirname = NULL; 155 kernel = NULL; 156 level = 1; /* default to fastest gzip compression */ 157 gzmode[0] = 'w'; 158 159 openlog("savecore", LOG_PERROR, LOG_DAEMON); 160 161 while ((ch = getopt(argc, argv, "cdfN:vzZ:")) != -1) 162 switch(ch) { 163 case 'c': 164 clear = 1; 165 break; 166 case 'd': /* Not documented. */ 167 case 'v': 168 verbose = 1; 169 break; 170 case 'f': 171 force = 1; 172 break; 173 case 'N': 174 kernel = optarg; 175 break; 176 case 'z': 177 compress = 1; 178 break; 179 case 'Z': 180 level = (int)strtol(optarg, &ep, 10); 181 if (level < 0 || level > 9) { 182 (void)syslog(LOG_ERR, "invalid compression %s", 183 optarg); 184 usage(); 185 } 186 break; 187 case '?': 188 default: 189 usage(); 190 } 191 argc -= optind; 192 argv += optind; 193 194 if (argc != (clear ? 0 : 1)) 195 usage(); 196 197 gzmode[1] = level + '0'; 198 if (!clear) 199 dirname = argv[0]; 200 201 if (kernel == NULL) { 202 kernel = getbootfile(); 203 } 204 205 (void)time(&now); 206 kmem_setup(); 207 208 if (clear) { 209 clear_dump(); 210 exit(0); 211 } 212 213 if (!dump_exists() && !force) 214 exit(1); 215 216 check_kmem(); 217 218 if (panicstr) 219 syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg); 220 else 221 syslog(LOG_ALERT, "reboot"); 222 223 if ((!get_crashtime() || !check_space()) && !force) 224 exit(1); 225 226 save_core(); 227 228 clear_dump(); 229 exit(0); 230} 231 232void 233kmem_setup(void) 234{ 235 kvm_t *kd_kern; 236 char errbuf[_POSIX2_LINE_MAX]; 237 int i, hdrsz; 238 239 /* 240 * Some names we need for the currently running system, others for 241 * the system that was running when the dump was made. The values 242 * obtained from the current system are used to look for things in 243 * /dev/kmem that cannot be found in the kernel namelist, but are 244 * presumed to be the same (since the disk partitions are probably 245 * the same!) 246 */ 247 kd_kern = kvm_openfiles(kernel, NULL, NULL, O_RDONLY, errbuf); 248 if (kd_kern == NULL) { 249 syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf); 250 exit(1); 251 } 252 if (kvm_nlist(kd_kern, current_nl) == -1) 253 syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel, 254 kvm_geterr(kd_kern)); 255 256 for (i = 0; cursyms[i] != -1; i++) 257 if (current_nl[cursyms[i]].n_value == 0) { 258 syslog(LOG_ERR, "%s: %s not in namelist", 259 kernel, current_nl[cursyms[i]].n_name); 260 exit(1); 261 } 262 263 if (KREAD(kd_kern, current_nl[X_DUMPDEV].n_value, &dumpdev) != 0) { 264 if (verbose) 265 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern)); 266 exit(1); 267 } 268 if (dumpdev == NODEV) { 269 syslog(LOG_WARNING, "no core dump (no dumpdev)"); 270 exit(1); 271 } 272 { 273 long l_dumplo; 274 275 if (KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &l_dumplo) != 0) { 276 if (verbose) 277 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern)); 278 exit(1); 279 } 280 if (l_dumplo == -1) { 281 syslog(LOG_WARNING, "no core dump (invalid dumplo)"); 282 exit(1); 283 } 284 dumplo = DEV_BSIZE * (off_t) l_dumplo; 285 } 286 287 if (verbose) 288 (void)printf("dumplo = %lld (%ld * %ld)\n", 289 (long 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, 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, 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 char nbuf[7]; 549 humanize_number(nbuf, 7, dumpsize, "", HN_AUTOSCALE, 0); 550 (void)printf("%7s\r", nbuf); 551 (void)fflush(stdout); 552 nr = read(ifd, buf, MIN(dumpsize, sizeof(buf))); 553 if (nr <= 0) { 554 if (nr == 0) 555 syslog(LOG_WARNING, 556 "WARNING: EOF on dump device"); 557 else 558 syslog(LOG_ERR, "%s: %m", rawp); 559 goto err2; 560 } 561 nw = fwrite(buf, 1, nr, fp); 562 if (nw != nr) { 563 syslog(LOG_ERR, "%s: %s", 564 path, strerror(nw == 0 ? EIO : errno)); 565err2: syslog(LOG_WARNING, 566 "WARNING: core may be incomplete"); 567 (void)printf("\n"); 568 exit(1); 569 } 570 } 571 (void)close(ifd); 572 (void)fclose(fp); 573 574 /* Copy the kernel. */ 575 ifd = Open(kernel, O_RDONLY); 576 (void)snprintf(path, sizeof(path), "%s/netbsd.%d%s", 577 dirname, bounds, compress ? ".gz" : ""); 578 if (compress) { 579 if ((fp = zopen(path, gzmode)) == NULL) { 580 syslog(LOG_ERR, "%s: %m", path); 581 exit(1); 582 } 583 } else 584 ofd = Create(path, S_IRUSR | S_IWUSR); 585 syslog(LOG_NOTICE, "writing %skernel to %s", 586 compress ? "compressed " : "", path); 587 while ((nr = read(ifd, buf, sizeof(buf))) > 0) { 588 if (compress) 589 nw = fwrite(buf, 1, nr, fp); 590 else 591 nw = write(ofd, buf, nr); 592 if (nw != nr) { 593 syslog(LOG_ERR, "%s: %s", 594 path, strerror(nw == 0 ? EIO : errno)); 595 syslog(LOG_WARNING, 596 "WARNING: kernel may be incomplete"); 597 exit(1); 598 } 599 } 600 if (nr < 0) { 601 syslog(LOG_ERR, "%s: %m", kernel); 602 syslog(LOG_WARNING, "WARNING: kernel may be incomplete"); 603 exit(1); 604 } 605 if (compress) 606 (void)fclose(fp); 607 else 608 (void)close(ofd); 609} 610 611char * 612find_dev(dev_t dev, int type) 613{ 614 DIR *dfd; 615 struct dirent *dir; 616 struct stat sb; 617 char *dp, device[MAXPATHLEN + 1], *p; 618 size_t l; 619 620 if ((dfd = opendir(_PATH_DEV)) == NULL) { 621 syslog(LOG_ERR, "%s: %m", _PATH_DEV); 622 exit(1); 623 } 624 strlcpy(device, _PATH_DEV, sizeof(device)); 625 p = &device[strlen(device)]; 626 l = sizeof(device) - strlen(device); 627 while ((dir = readdir(dfd))) { 628 strlcpy(p, dir->d_name, l); 629 if (lstat(device, &sb)) { 630 syslog(LOG_ERR, "%s: %m", device); 631 continue; 632 } 633 if ((sb.st_mode & S_IFMT) != type) 634 continue; 635 if (dev == sb.st_rdev) { 636 closedir(dfd); 637 if ((dp = strdup(device)) == NULL) { 638 syslog(LOG_ERR, "%m"); 639 exit(1); 640 } 641 return (dp); 642 } 643 } 644 closedir(dfd); 645 syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev)); 646 exit(1); 647} 648 649char * 650rawname(char *s) 651{ 652 char *sl; 653 char name[MAXPATHLEN]; 654 655 if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') { 656 syslog(LOG_ERR, 657 "can't make raw dump device name from %s", s); 658 return (s); 659 } 660 (void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s, 661 sl + 1); 662 if ((sl = strdup(name)) == NULL) { 663 syslog(LOG_ERR, "%m"); 664 exit(1); 665 } 666 return (sl); 667} 668 669int 670get_crashtime(void) 671{ 672 struct timeval dtime; 673 time_t dumptime; /* Time the dump was taken. */ 674 675 if (KREAD(kd_dump, dump_nl[X_TIME].n_value, &dtime) != 0) { 676 if (verbose) 677 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 678 return (0); 679 } 680 dumptime = dtime.tv_sec; 681 if (dumptime == 0) { 682 if (verbose) 683 syslog(LOG_ERR, "dump time is zero"); 684 return (0); 685 } 686 (void)printf("savecore: system went down at %s", ctime(&dumptime)); 687#define LEEWAY (60 * SECSPERDAY) 688 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { 689 (void)printf("dump time is unreasonable\n"); 690 return (0); 691 } 692 return (1); 693} 694 695int 696check_space(void) 697{ 698 FILE *fp; 699 off_t minfree, spacefree, kernelsize, needed; 700 struct stat st; 701 struct statfs fsbuf; 702 char mbuf[100], path[MAXPATHLEN]; 703 704#ifdef __GNUC__ 705 (void) &minfree; 706#endif 707 708 if (stat(kernel, &st) < 0) { 709 syslog(LOG_ERR, "%s: %m", kernel); 710 exit(1); 711 } 712 kernelsize = st.st_blocks * S_BLKSIZE; 713 if (statfs(dirname, &fsbuf) < 0) { 714 syslog(LOG_ERR, "%s: %m", dirname); 715 exit(1); 716 } 717 spacefree = fsbuf.f_bavail; 718 spacefree *= fsbuf.f_bsize; 719 spacefree /= 1024; 720 721 (void)snprintf(path, sizeof(path), "%s/minfree", dirname); 722 if ((fp = fopen(path, "r")) == NULL) 723 minfree = 0; 724 else { 725 if (fgets(mbuf, sizeof(mbuf), fp) == NULL) 726 minfree = 0; 727 else 728 minfree = atoi(mbuf); 729 (void)fclose(fp); 730 } 731 732 needed = (dumpsize + kernelsize) / 1024; 733 if (minfree > 0 && spacefree - needed < minfree) { 734 syslog(LOG_WARNING, 735 "no dump, not enough free space in %s", dirname); 736 return (0); 737 } 738 if (spacefree - needed < minfree) 739 syslog(LOG_WARNING, 740 "dump performed, but free space threshold crossed"); 741 return (1); 742} 743 744int 745Open(const char *name, int rw) 746{ 747 int fd; 748 749 if ((fd = open(name, rw, 0)) < 0) { 750 syslog(LOG_ERR, "%s: %m", name); 751 exit(1); 752 } 753 return (fd); 754} 755 756void 757Lseek(int fd, off_t off, int flag) 758{ 759 off_t ret; 760 761 ret = lseek(fd, off, flag); 762 if (ret == -1) { 763 syslog(LOG_ERR, "lseek: %m"); 764 exit(1); 765 } 766} 767 768int 769Create(char *file, int mode) 770{ 771 int fd; 772 773 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode); 774 if (fd < 0) { 775 syslog(LOG_ERR, "%s: %m", file); 776 exit(1); 777 } 778 return (fd); 779} 780 781void 782Write(int fd, void *bp, int size) 783{ 784 int n; 785 786 if ((n = write(fd, bp, size)) < size) { 787 syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO)); 788 exit(1); 789 } 790} 791 792void 793usage(void) 794{ 795 (void)syslog(LOG_ERR, 796 "usage: savecore [-cfvz] [-N system] [-Z level] directory"); 797 exit(1); 798} 799