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