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