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