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