1 /* $NetBSD: savecore.c,v 1.59 2002/12/06 02:20:00 thorpej 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 44 static char sccsid[] = "@(#)savecore.c 8.5 (Berkeley) 4/28/95"; 45 #else 46 __RCSID("$NetBSD: savecore.c,v 1.59 2002/12/06 02:20:00 thorpej 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 72 extern 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 77 struct 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 }; 100 int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; 101 int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; 102 103 struct 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. */ 118 off_t dumplo; /* where dump starts on dumpdev */ 119 u_int32_t dumpmag; /* magic number in dump */ 120 int dumpsize; /* amount of memory dumped */ 121 122 const char *kernel; /* name of used kernel */ 123 char *dirname; /* directory to save dumps in */ 124 char *ddname; /* name of dump device */ 125 dev_t dumpdev; /* dump device */ 126 int dumpfd; /* read/write descriptor on block dev */ 127 kvm_t *kd_dump; /* kvm descriptor on block dev */ 128 time_t now; /* current date */ 129 char panic_mesg[1024]; 130 long panicstr; 131 char vers[1024]; 132 char gzmode[3]; 133 134 static int clear, compress, force, verbose; /* flags */ 135 136 void check_kmem(void); 137 int check_space(void); 138 void clear_dump(void); 139 int Create(char *, int); 140 int dump_exists(void); 141 char *find_dev(dev_t, int); 142 int get_crashtime(void); 143 void kmem_setup(void); 144 void Lseek(int, off_t, int); 145 int main(int, char *[]); 146 int Open(const char *, int rw); 147 char *rawname(char *s); 148 void save_core(void); 149 void usage(void); 150 void Write(int, void *, int); 151 152 int 153 main(int argc, char *argv[]) 154 { 155 int ch, level; 156 char *ep; 157 158 dirname = NULL; 159 kernel = NULL; 160 level = 1; /* default to fastest gzip compression */ 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 break; 191 case '?': 192 default: 193 usage(); 194 } 195 argc -= optind; 196 argv += optind; 197 198 if (argc != (clear ? 0 : 1)) 199 usage(); 200 201 gzmode[1] = level + '0'; 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 236 void 237 kmem_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 { 277 long l_dumplo; 278 279 if (KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &l_dumplo) != 0) { 280 if (verbose) 281 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern)); 282 exit(1); 283 } 284 if (l_dumplo == -1) { 285 syslog(LOG_WARNING, "no core dump (invalid dumplo)"); 286 exit(1); 287 } 288 dumplo = DEV_BSIZE * (off_t) l_dumplo; 289 } 290 291 if (verbose) 292 (void)printf("dumplo = %lld (%ld * %ld)\n", 293 (long long)dumplo, (long)(dumplo / DEV_BSIZE), (long)DEV_BSIZE); 294 if (KREAD(kd_kern, current_nl[X_DUMPMAG].n_value, &dumpmag) != 0) { 295 if (verbose) 296 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern)); 297 exit(1); 298 } 299 300 (void)kvm_read(kd_kern, current_nl[X_VERSION].n_value, vers, 301 sizeof(vers)); 302 vers[sizeof(vers) - 1] = '\0'; 303 304 ddname = find_dev(dumpdev, S_IFBLK); 305 dumpfd = Open(ddname, O_RDWR); 306 307 kd_dump = kvm_openfiles(kernel, ddname, NULL, O_RDWR, errbuf); 308 if (kd_dump == NULL) { 309 syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf); 310 exit(1); 311 } 312 313 if (kvm_nlist(kd_dump, dump_nl) == -1) 314 syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel, 315 kvm_geterr(kd_dump)); 316 317 for (i = 0; dumpsyms[i] != -1; i++) 318 if (dump_nl[dumpsyms[i]].n_value == 0) { 319 syslog(LOG_ERR, "%s: %s not in namelist", 320 kernel, dump_nl[dumpsyms[i]].n_name); 321 exit(1); 322 } 323 hdrsz = kvm_dump_mkheader(kd_dump, dumplo); 324 325 /* 326 * If 'hdrsz' == 0, kvm_dump_mkheader() failed on the magic-number 327 * checks, ergo no dump is present... 328 */ 329 if (hdrsz == 0) { 330 syslog(LOG_WARNING, "no core dump"); 331 exit(1); 332 } 333 if (hdrsz == -1) { 334 syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", kernel, 335 kvm_geterr(kd_dump)); 336 exit(1); 337 } 338 dumplo += hdrsz; 339 kvm_close(kd_kern); 340 } 341 342 void 343 check_kmem(void) 344 { 345 char *cp, *bufdata; 346 struct kern_msgbuf msgbuf, *bufp; 347 long panicloc, panicstart, panicend; 348 char core_vers[1024]; 349 350 (void)kvm_read(kd_dump, dump_nl[X_VERSION].n_value, core_vers, 351 sizeof(core_vers)); 352 core_vers[sizeof(core_vers) - 1] = '\0'; 353 354 if (strcmp(vers, core_vers) != 0) 355 syslog(LOG_WARNING, 356 "warning: %s version mismatch:\n\t%s\nand\t%s\n", 357 kernel, vers, core_vers); 358 359 panicstart = panicend = 0; 360 if (KREAD(kd_dump, dump_nl[X_PANICSTART].n_value, &panicstart) != 0) { 361 if (verbose) 362 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 363 goto nomsguf; 364 } 365 if (KREAD(kd_dump, dump_nl[X_PANICEND].n_value, &panicend) != 0) { 366 if (verbose) 367 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 368 goto nomsguf; 369 } 370 if (panicstart != 0 && panicend != 0) { 371 if (KREAD(kd_dump, dump_nl[X_MSGBUF].n_value, &bufp)) { 372 if (verbose) 373 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 374 goto nomsguf; 375 } 376 if (kvm_read(kd_dump, (long)bufp, &msgbuf, 377 offsetof(struct kern_msgbuf, msg_bufc)) != 378 offsetof(struct kern_msgbuf, msg_bufc)) { 379 if (verbose) 380 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 381 goto nomsguf; 382 } 383 if (msgbuf.msg_magic != MSG_MAGIC) { 384 if (verbose) 385 syslog(LOG_WARNING, "msgbuf magic incorrect"); 386 goto nomsguf; 387 } 388 bufdata = malloc(msgbuf.msg_bufs); 389 if (bufdata == NULL) { 390 if (verbose) 391 syslog(LOG_WARNING, "couldn't allocate space for msgbuf data"); 392 goto nomsguf; 393 } 394 if (kvm_read(kd_dump, (long)&bufp->msg_bufc, bufdata, 395 msgbuf.msg_bufs) != msgbuf.msg_bufs) { 396 if (verbose) 397 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 398 goto nomsguf; 399 } 400 cp = panic_mesg; 401 while (panicstart != panicend && cp < &panic_mesg[sizeof(panic_mesg)-1]) { 402 *cp++ = bufdata[panicstart]; 403 panicstart++; 404 if (panicstart >= msgbuf.msg_bufs) 405 panicstart = 0; 406 } 407 /* Don't end in a new-line */ 408 cp = &panic_mesg[strlen(panic_mesg)] - 1; 409 if (*cp == '\n') 410 *cp = '\0'; 411 panic_mesg[sizeof(panic_mesg) - 1] = '\0'; 412 413 panicstr = 1; /* anything not zero */ 414 return; 415 } 416 nomsguf: 417 if (KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr) != 0) { 418 if (verbose) 419 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 420 return; 421 } 422 if (panicstr) { 423 cp = panic_mesg; 424 panicloc = panicstr; 425 do { 426 if (KREAD(kd_dump, panicloc, cp) != 0) { 427 if (verbose) 428 syslog(LOG_WARNING, "kvm_read: %s", 429 kvm_geterr(kd_dump)); 430 break; 431 } 432 panicloc++; 433 } while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)-1]); 434 panic_mesg[sizeof(panic_mesg) - 1] = '\0'; 435 } 436 } 437 438 int 439 dump_exists(void) 440 { 441 u_int32_t newdumpmag; 442 443 if (KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag) != 0) { 444 if (verbose) 445 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 446 return (0); 447 } 448 449 /* Read the dump size. */ 450 if (KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumpsize) != 0) { 451 if (verbose) 452 syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump)); 453 return (0); 454 } 455 dumpsize *= getpagesize(); 456 457 /* 458 * Return zero if core dump doesn't seem to be there, and note 459 * it for syslog. This check and return happens after the dump size 460 * is read, so dumpsize is whether or not the core is valid (for -f). 461 */ 462 if (newdumpmag != dumpmag) { 463 if (verbose) 464 syslog(LOG_WARNING, 465 "magic number mismatch (0x%x != 0x%x)", 466 newdumpmag, dumpmag); 467 syslog(LOG_WARNING, "no core dump"); 468 return (0); 469 } 470 return (1); 471 } 472 473 void 474 clear_dump(void) 475 { 476 if (kvm_dump_inval(kd_dump) == -1) 477 syslog(LOG_ERR, "%s: kvm_clear_dump: %s", ddname, 478 kvm_geterr(kd_dump)); 479 480 } 481 482 char buf[1024 * 1024]; 483 484 void 485 save_core(void) 486 { 487 FILE *fp; 488 int bounds, ifd, nr, nw, ofd; 489 char *rawp, path[MAXPATHLEN]; 490 491 ofd = -1; 492 /* 493 * Get the current number and update the bounds file. Do the update 494 * now, because may fail later and don't want to overwrite anything. 495 */ 496 umask(066); 497 (void)snprintf(path, sizeof(path), "%s/bounds", dirname); 498 if ((fp = fopen(path, "r")) == NULL) 499 goto err1; 500 if (fgets(buf, sizeof(buf), fp) == NULL) { 501 if (ferror(fp)) 502 err1: syslog(LOG_WARNING, "%s: %m", path); 503 bounds = 0; 504 } else 505 bounds = atoi(buf); 506 if (fp != NULL) 507 (void)fclose(fp); 508 if ((fp = fopen(path, "w")) == NULL) 509 syslog(LOG_ERR, "%s: %m", path); 510 else { 511 (void)fprintf(fp, "%d\n", bounds + 1); 512 (void)fclose(fp); 513 } 514 515 /* Create the core file. */ 516 (void)snprintf(path, sizeof(path), "%s/netbsd.%d.core%s", 517 dirname, bounds, compress ? ".gz" : ""); 518 if (compress) { 519 if ((fp = zopen(path, gzmode)) == NULL) { 520 syslog(LOG_ERR, "%s: %m", path); 521 exit(1); 522 } 523 } else { 524 ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 525 fp = fdopen(ofd, "w"); 526 if (fp == NULL) { 527 syslog(LOG_ERR, "%s: fdopen: %m", path); 528 exit(1); 529 } 530 } 531 532 /* Open the raw device. */ 533 rawp = rawname(ddname); 534 if ((ifd = open(rawp, O_RDONLY)) == -1) { 535 syslog(LOG_WARNING, "%s: %m; using block device", rawp); 536 ifd = dumpfd; 537 } 538 539 /* Seek to the start of the core. */ 540 Lseek(ifd, dumplo, SEEK_SET); 541 542 if (kvm_dump_wrtheader(kd_dump, fp, dumpsize) == -1) { 543 syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path, 544 kvm_geterr(kd_dump)); 545 exit(1); 546 } 547 548 /* Copy the core file. */ 549 syslog(LOG_NOTICE, "writing %score to %s", 550 compress ? "compressed " : "", path); 551 for (; dumpsize > 0; dumpsize -= nr) { 552 char nbuf[7]; 553 humanize_number(nbuf, 7, dumpsize, "", HN_AUTOSCALE, 0); 554 (void)printf("%7s\r", nbuf); 555 (void)fflush(stdout); 556 nr = read(ifd, buf, MIN(dumpsize, sizeof(buf))); 557 if (nr <= 0) { 558 if (nr == 0) 559 syslog(LOG_WARNING, 560 "WARNING: EOF on dump device"); 561 else 562 syslog(LOG_ERR, "%s: %m", rawp); 563 goto err2; 564 } 565 nw = fwrite(buf, 1, nr, fp); 566 if (nw != nr) { 567 syslog(LOG_ERR, "%s: %s", 568 path, strerror(nw == 0 ? EIO : errno)); 569 err2: syslog(LOG_WARNING, 570 "WARNING: core may be incomplete"); 571 (void)printf("\n"); 572 exit(1); 573 } 574 } 575 (void)close(ifd); 576 (void)fclose(fp); 577 578 /* Copy the kernel. */ 579 ifd = Open(kernel, O_RDONLY); 580 (void)snprintf(path, sizeof(path), "%s/netbsd.%d%s", 581 dirname, bounds, compress ? ".gz" : ""); 582 if (compress) { 583 if ((fp = zopen(path, gzmode)) == NULL) { 584 syslog(LOG_ERR, "%s: %m", path); 585 exit(1); 586 } 587 } else 588 ofd = Create(path, S_IRUSR | S_IWUSR); 589 syslog(LOG_NOTICE, "writing %skernel to %s", 590 compress ? "compressed " : "", path); 591 while ((nr = read(ifd, buf, sizeof(buf))) > 0) { 592 if (compress) 593 nw = fwrite(buf, 1, nr, fp); 594 else 595 nw = write(ofd, buf, nr); 596 if (nw != nr) { 597 syslog(LOG_ERR, "%s: %s", 598 path, strerror(nw == 0 ? EIO : errno)); 599 syslog(LOG_WARNING, 600 "WARNING: kernel may be incomplete"); 601 exit(1); 602 } 603 } 604 if (nr < 0) { 605 syslog(LOG_ERR, "%s: %m", kernel); 606 syslog(LOG_WARNING, "WARNING: kernel may be incomplete"); 607 exit(1); 608 } 609 if (compress) 610 (void)fclose(fp); 611 else 612 (void)close(ofd); 613 } 614 615 char * 616 find_dev(dev_t dev, int type) 617 { 618 DIR *dfd; 619 struct dirent *dir; 620 struct stat sb; 621 char *dp, device[MAXPATHLEN + 1]; 622 623 if ((dfd = opendir(_PATH_DEV)) == NULL) { 624 syslog(LOG_ERR, "%s: %m", _PATH_DEV); 625 exit(1); 626 } 627 (void)strcpy(device, _PATH_DEV); 628 while ((dir = readdir(dfd))) { 629 (void)strcpy(device + sizeof(_PATH_DEV) - 1, dir->d_name); 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 650 char * 651 rawname(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 670 int 671 get_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 (7 * 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 696 int 697 check_space(void) 698 { 699 FILE *fp; 700 off_t minfree, spacefree, kernelsize, needed; 701 struct stat st; 702 struct statfs 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 (statfs(dirname, &fsbuf) < 0) { 715 syslog(LOG_ERR, "%s: %m", dirname); 716 exit(1); 717 } 718 spacefree = fsbuf.f_bavail; 719 spacefree *= fsbuf.f_bsize; 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 = (dumpsize + 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 745 int 746 Open(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 757 void 758 Lseek(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 769 int 770 Create(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 782 void 783 Write(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 793 void 794 usage(void) 795 { 796 (void)syslog(LOG_ERR, 797 "usage: savecore [-cfvz] [-N system] [-Z level] directory"); 798 exit(1); 799 } 800