1 /*- 2 * Copyright (c) 1986, 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 static const char copyright[] = 36 "@(#) Copyright (c) 1986, 1992, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)savecore.c 8.3 (Berkeley) 1/2/94"; 43 #endif 44 static const char rcsid[] = 45 "$FreeBSD$"; 46 #endif /* not lint */ 47 48 #include <sys/param.h> 49 #include <sys/stat.h> 50 #include <sys/mount.h> 51 #include <sys/syslog.h> 52 #include <sys/sysctl.h> 53 54 #include <vm/vm.h> 55 #include <vm/vm_param.h> 56 #include <vm/pmap.h> 57 58 #include <dirent.h> 59 #include <fcntl.h> 60 #include <nlist.h> 61 #include <paths.h> 62 #include <stdio.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <unistd.h> 66 #include "zopen.h" 67 68 #ifdef __alpha__ 69 #define ok(number) ALPHA_K0SEG_TO_PHYS(number) 70 #endif 71 72 #ifdef __i386__ 73 #define ok(number) ((number) - KERNBASE) 74 #endif 75 76 struct nlist current_nl[] = { /* Namelist for currently running system. */ 77 #define X_DUMPLO 0 78 { "_dumplo" }, 79 #define X_TIME 1 80 { "_time_second" }, 81 #define X_DUMPSIZE 2 82 { "_dumpsize" }, 83 #define X_VERSION 3 84 { "_version" }, 85 #define X_PANICSTR 4 86 { "_panicstr" }, 87 #define X_DUMPMAG 5 88 { "_dumpmag" }, 89 { "" }, 90 }; 91 int cursyms[] = { X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; 92 int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; 93 94 struct nlist dump_nl[] = { /* Name list for dumped system. */ 95 { "_dumplo" }, /* Entries MUST be the same as */ 96 { "_time_second" }, /* those in current_nl[]. */ 97 { "_dumpsize" }, 98 { "_version" }, 99 { "_panicstr" }, 100 { "_dumpmag" }, 101 { "" }, 102 }; 103 104 /* Types match kernel declarations. */ 105 off_t dumplo; /* where dump starts on dumpdev */ 106 int dumpmag; /* magic number in dump */ 107 int dumpsize; /* amount of memory dumped */ 108 109 char *kernel; /* user-specified kernel */ 110 char *savedir; /* directory to save dumps in */ 111 char ddname[MAXPATHLEN]; /* name of dump device */ 112 dev_t dumpdev; /* dump device */ 113 int dumpfd; /* read/write descriptor on char dev */ 114 time_t now; /* current date */ 115 char panic_mesg[1024]; /* panic message */ 116 int panicstr; /* flag: dump was caused by panic */ 117 char vers[1024]; /* version of kernel that crashed */ 118 119 int clear, compress, force, verbose; /* flags */ 120 int keep; /* keep dump on device */ 121 122 void check_kmem __P((void)); 123 int check_space __P((void)); 124 void clear_dump __P((void)); 125 void DumpRead __P((int fd, void *bp, int size, off_t off, int flag)); 126 void DumpWrite __P((int fd, void *bp, int size, off_t off, int flag)); 127 int dump_exists __P((void)); 128 void find_dev __P((dev_t)); 129 int get_crashtime __P((void)); 130 void get_dumpsize __P((void)); 131 void kmem_setup __P((void)); 132 void log __P((int, char *, ...)); 133 void Lseek __P((int, off_t, int)); 134 int Open __P((const char *, int rw)); 135 int Read __P((int, void *, int)); 136 void save_core __P((void)); 137 void usage __P((void)); 138 void Write __P((int, void *, int)); 139 140 int 141 main(argc, argv) 142 int argc; 143 char *argv[]; 144 { 145 int ch; 146 147 openlog("savecore", LOG_PERROR, LOG_DAEMON); 148 149 while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1) 150 switch(ch) { 151 case 'c': 152 clear = 1; 153 break; 154 case 'd': /* Not documented. */ 155 case 'v': 156 verbose = 1; 157 break; 158 case 'f': 159 force = 1; 160 break; 161 case 'k': 162 keep = 1; 163 break; 164 case 'N': 165 kernel = optarg; 166 break; 167 case 'z': 168 compress = 1; 169 break; 170 case '?': 171 default: 172 usage(); 173 } 174 argc -= optind; 175 argv += optind; 176 177 if (!clear) { 178 if (argc != 1 && argc != 2) 179 usage(); 180 savedir = argv[0]; 181 } 182 if (argc == 2) 183 kernel = argv[1]; 184 185 (void)time(&now); 186 kmem_setup(); 187 188 if (clear) { 189 clear_dump(); 190 exit(0); 191 } 192 193 if (!dump_exists() && !force) 194 exit(1); 195 196 check_kmem(); 197 198 if (panicstr) 199 syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg); 200 else 201 syslog(LOG_ALERT, "reboot"); 202 203 get_dumpsize(); 204 205 if ((!get_crashtime() || !check_space()) && !force) 206 exit(1); 207 208 save_core(); 209 210 if (!keep) 211 clear_dump(); 212 213 exit(0); 214 } 215 216 void 217 kmem_setup() 218 { 219 int kmem, i; 220 const char *dump_sys; 221 size_t len; 222 long kdumplo; /* block number where dump starts on dumpdev */ 223 char *p; 224 225 /* 226 * Some names we need for the currently running system, others for 227 * the system that was running when the dump was made. The values 228 * obtained from the current system are used to look for things in 229 * /dev/kmem that cannot be found in the dump_sys namelist, but are 230 * presumed to be the same (since the disk partitions are probably 231 * the same!) 232 */ 233 if ((nlist(getbootfile(), current_nl)) == -1) 234 syslog(LOG_ERR, "%s: nlist: %m", getbootfile()); 235 for (i = 0; cursyms[i] != -1; i++) 236 if (current_nl[cursyms[i]].n_value == 0) { 237 syslog(LOG_ERR, "%s: %s not in namelist", 238 getbootfile(), current_nl[cursyms[i]].n_name); 239 exit(1); 240 } 241 242 dump_sys = kernel ? kernel : getbootfile(); 243 if ((nlist(dump_sys, dump_nl)) == -1) 244 syslog(LOG_ERR, "%s: nlist: %m", dump_sys); 245 for (i = 0; dumpsyms[i] != -1; i++) 246 if (dump_nl[dumpsyms[i]].n_value == 0) { 247 syslog(LOG_ERR, "%s: %s not in namelist", 248 dump_sys, dump_nl[dumpsyms[i]].n_name); 249 exit(1); 250 } 251 252 len = sizeof dumpdev; 253 if (sysctlbyname("kern.dumpdev", &dumpdev, &len, NULL, 0) == -1) { 254 syslog(LOG_ERR, "sysctl: kern.dumpdev: %m"); 255 exit(1); 256 } 257 if (dumpdev == NODEV) { 258 syslog(LOG_WARNING, "no core dump (no dumpdev)"); 259 exit(1); 260 } 261 262 kmem = Open(_PATH_KMEM, O_RDONLY); 263 Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, L_SET); 264 (void)Read(kmem, &kdumplo, sizeof(kdumplo)); 265 dumplo = (off_t)kdumplo * DEV_BSIZE; 266 if (verbose) 267 (void)printf("dumplo = %lld (%ld * %d)\n", 268 (long long)dumplo, kdumplo, DEV_BSIZE); 269 Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, L_SET); 270 (void)Read(kmem, &dumpmag, sizeof(dumpmag)); 271 find_dev(dumpdev); 272 dumpfd = Open(ddname, O_RDWR); 273 if (kernel) 274 return; 275 276 lseek(kmem, (off_t)current_nl[X_VERSION].n_value, SEEK_SET); 277 Read(kmem, vers, sizeof(vers)); 278 vers[sizeof(vers) - 1] = '\0'; 279 p = strchr(vers, '\n'); 280 if (p) 281 p[1] = '\0'; 282 283 /* Don't fclose(fp), we use kmem later. */ 284 } 285 286 void 287 check_kmem() 288 { 289 char core_vers[1024], *p; 290 291 DumpRead(dumpfd, core_vers, sizeof(core_vers), 292 (off_t)(dumplo + ok(dump_nl[X_VERSION].n_value)), L_SET); 293 core_vers[sizeof(core_vers) - 1] = '\0'; 294 p = strchr(core_vers, '\n'); 295 if (p) 296 p[1] = '\0'; 297 if (strcmp(vers, core_vers) && kernel == 0) 298 syslog(LOG_WARNING, 299 "warning: %s version mismatch:\n\t\"%s\"\nand\t\"%s\"\n", 300 getbootfile(), vers, core_vers); 301 DumpRead(dumpfd, &panicstr, sizeof(panicstr), 302 (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET); 303 if (panicstr) { 304 DumpRead(dumpfd, panic_mesg, sizeof(panic_mesg), 305 (off_t)(dumplo + ok(panicstr)), L_SET); 306 } 307 } 308 309 /* 310 * Clear the magic number in the dump header. 311 */ 312 void 313 clear_dump() 314 { 315 int newdumpmag; 316 317 newdumpmag = 0; 318 DumpWrite(dumpfd, &newdumpmag, sizeof(newdumpmag), 319 (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); 320 close(dumpfd); 321 } 322 323 /* 324 * Check if a dump exists by looking for a magic number in the dump 325 * header. 326 */ 327 int 328 dump_exists() 329 { 330 int newdumpmag; 331 332 DumpRead(dumpfd, &newdumpmag, sizeof(newdumpmag), 333 (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); 334 if (newdumpmag != dumpmag) { 335 if (verbose) 336 syslog(LOG_WARNING, "magic number mismatch (%x != %x)", 337 newdumpmag, dumpmag); 338 syslog(LOG_WARNING, "no core dump"); 339 return (0); 340 } 341 return (1); 342 } 343 344 char buf[1024 * 1024]; 345 #define BLOCKSIZE (1<<12) 346 #define BLOCKMASK (~(BLOCKSIZE-1)) 347 348 /* 349 * Save the core dump. 350 */ 351 void 352 save_core() 353 { 354 register FILE *fp; 355 register int bounds, ifd, nr, nw; 356 int hs, he; /* start and end of hole */ 357 char path[MAXPATHLEN]; 358 mode_t oumask; 359 360 /* 361 * Get the current number and update the bounds file. Do the update 362 * now, because may fail later and don't want to overwrite anything. 363 */ 364 (void)snprintf(path, sizeof(path), "%s/bounds", savedir); 365 if ((fp = fopen(path, "r")) == NULL) 366 goto err1; 367 if (fgets(buf, sizeof(buf), fp) == NULL) { 368 if (ferror(fp)) 369 err1: syslog(LOG_WARNING, "%s: %m", path); 370 bounds = 0; 371 } else 372 bounds = atoi(buf); 373 if (fp != NULL) 374 (void)fclose(fp); 375 if ((fp = fopen(path, "w")) == NULL) 376 syslog(LOG_ERR, "%s: %m", path); 377 else { 378 (void)fprintf(fp, "%d\n", bounds + 1); 379 (void)fclose(fp); 380 } 381 382 /* Create the core file. */ 383 oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/ 384 (void)snprintf(path, sizeof(path), "%s/vmcore.%d%s", 385 savedir, bounds, compress ? ".Z" : ""); 386 if (compress) 387 fp = zopen(path, "w", 0); 388 else 389 fp = fopen(path, "w"); 390 if (fp == NULL) { 391 syslog(LOG_ERR, "%s: %m", path); 392 exit(1); 393 } 394 (void)umask(oumask); 395 396 /* Seek to the start of the core. */ 397 Lseek(dumpfd, (off_t)dumplo, L_SET); 398 399 /* Copy the core file. */ 400 syslog(LOG_NOTICE, "writing %score to %s", 401 compress ? "compressed " : "", path); 402 for (; dumpsize > 0; dumpsize -= nr) { 403 (void)printf("%6dK\r", dumpsize / 1024); 404 (void)fflush(stdout); 405 nr = read(dumpfd, buf, MIN(dumpsize, sizeof(buf))); 406 if (nr <= 0) { 407 if (nr == 0) 408 syslog(LOG_WARNING, 409 "WARNING: EOF on dump device"); 410 else 411 syslog(LOG_ERR, "%s: %m", ddname); 412 goto err2; 413 } 414 for (nw = 0; nw < nr; nw = he) { 415 /* find a contiguous block of zeroes */ 416 for (hs = nw; hs < nr; hs += BLOCKSIZE) { 417 for (he = hs; he < nr && buf[he] == 0; ++he) 418 /* nothing */ ; 419 420 /* is the hole long enough to matter? */ 421 if (he >= hs + BLOCKSIZE) 422 break; 423 } 424 425 /* back down to a block boundary */ 426 he &= BLOCKMASK; 427 428 /* 429 * 1) Don't go beyond the end of the buffer. 430 * 2) If the end of the buffer is less than 431 * BLOCKSIZE bytes away, we're at the end 432 * of the file, so just grab what's left. 433 */ 434 if (hs + BLOCKSIZE > nr) 435 hs = he = nr; 436 437 /* 438 * At this point, we have a partial ordering: 439 * nw <= hs <= he <= nr 440 * If hs > nw, buf[nw..hs] contains non-zero data. 441 * If he > hs, buf[hs..he] is all zeroes. 442 */ 443 if (hs > nw) 444 if (fwrite(buf + nw, hs - nw, 1, fp) != 1) 445 break; 446 if (he > hs) 447 if (fseek(fp, he - hs, SEEK_CUR) == -1) 448 break; 449 } 450 if (nw != nr) { 451 syslog(LOG_ERR, "%s: %m", path); 452 err2: syslog(LOG_WARNING, 453 "WARNING: vmcore may be incomplete"); 454 (void)printf("\n"); 455 exit(1); 456 } 457 } 458 459 (void)fclose(fp); 460 461 /* Copy the kernel. */ 462 ifd = Open(kernel ? kernel : getbootfile(), O_RDONLY); 463 (void)snprintf(path, sizeof(path), "%s/kernel.%d%s", 464 savedir, bounds, compress ? ".Z" : ""); 465 if (compress) 466 fp = zopen(path, "w", 0); 467 else 468 fp = fopen(path, "w"); 469 if (fp == NULL) { 470 syslog(LOG_ERR, "%s: %m", path); 471 exit(1); 472 } 473 syslog(LOG_NOTICE, "writing %skernel to %s", 474 compress ? "compressed " : "", path); 475 while ((nr = read(ifd, buf, sizeof(buf))) > 0) { 476 nw = fwrite(buf, 1, nr, fp); 477 if (nw != nr) { 478 syslog(LOG_ERR, "%s: %m", path); 479 syslog(LOG_WARNING, 480 "WARNING: kernel may be incomplete"); 481 exit(1); 482 } 483 } 484 if (nr < 0) { 485 syslog(LOG_ERR, "%s: %m", kernel ? kernel : getbootfile()); 486 syslog(LOG_WARNING, 487 "WARNING: kernel may be incomplete"); 488 exit(1); 489 } 490 (void)fclose(fp); 491 close(ifd); 492 } 493 494 /* 495 * Verify that the specified device node exists and matches the 496 * specified device. 497 */ 498 int 499 verify_dev(name, dev) 500 char *name; 501 register dev_t dev; 502 { 503 struct stat sb; 504 505 if (lstat(name, &sb) == -1) 506 return (-1); 507 if (!S_ISCHR(sb.st_mode) || sb.st_rdev != dev) 508 return (-1); 509 return (0); 510 } 511 512 /* 513 * Find the dump device. 514 * 515 * 1) try devname(3); see if it returns something sensible 516 * 2) scan /dev for the desired node 517 * 3) as a last resort, try to create the node we need 518 */ 519 void 520 find_dev(dev) 521 register dev_t dev; 522 { 523 struct dirent *ent; 524 char *dn, *dnp; 525 DIR *d; 526 527 strcpy(ddname, _PATH_DEV); 528 dnp = ddname + sizeof _PATH_DEV - 1; 529 if ((dn = devname(dev, S_IFCHR)) != NULL) { 530 strcpy(dnp, dn); 531 if (verify_dev(ddname, dev) == 0) 532 return; 533 } 534 if ((d = opendir(_PATH_DEV)) != NULL) { 535 while ((ent = readdir(d))) { 536 strcpy(dnp, ent->d_name); 537 if (verify_dev(ddname, dev) == 0) { 538 closedir(d); 539 return; 540 } 541 } 542 closedir(d); 543 } 544 strcpy(dnp, "dump"); 545 if (mknod(ddname, S_IFCHR|S_IRUSR|S_IWUSR, dev) == 0) 546 return; 547 syslog(LOG_ERR, "can't find device %d/%#x", major(dev), minor(dev)); 548 exit(1); 549 } 550 551 /* 552 * Extract the date and time of the crash from the dump header, and 553 * make sure it looks sane (within one week of current date and time). 554 */ 555 int 556 get_crashtime() 557 { 558 time_t dumptime; /* Time the dump was taken. */ 559 560 DumpRead(dumpfd, &dumptime, sizeof(dumptime), 561 (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET); 562 if (dumptime == 0) { 563 if (verbose) 564 syslog(LOG_ERR, "dump time is zero"); 565 return (0); 566 } 567 (void)printf("savecore: system went down at %s", ctime(&dumptime)); 568 #define LEEWAY (7 * 86400) 569 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { 570 (void)printf("dump time is unreasonable\n"); 571 return (0); 572 } 573 return (1); 574 } 575 576 /* 577 * Extract the size of the dump from the dump header. 578 */ 579 void 580 get_dumpsize() 581 { 582 /* Read the dump size. */ 583 DumpRead(dumpfd, &dumpsize, sizeof(dumpsize), 584 (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET); 585 dumpsize *= getpagesize(); 586 } 587 588 /* 589 * Check that sufficient space is available on the disk that holds the 590 * save directory. 591 */ 592 int 593 check_space() 594 { 595 register FILE *fp; 596 const char *tkernel; 597 off_t minfree, spacefree, totfree, kernelsize, needed; 598 struct stat st; 599 struct statfs fsbuf; 600 char buf[100], path[MAXPATHLEN]; 601 602 tkernel = kernel ? kernel : getbootfile(); 603 if (stat(tkernel, &st) < 0) { 604 syslog(LOG_ERR, "%s: %m", tkernel); 605 exit(1); 606 } 607 kernelsize = st.st_blocks * S_BLKSIZE; 608 609 if (statfs(savedir, &fsbuf) < 0) { 610 syslog(LOG_ERR, "%s: %m", savedir); 611 exit(1); 612 } 613 spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024; 614 totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024; 615 616 (void)snprintf(path, sizeof(path), "%s/minfree", savedir); 617 if ((fp = fopen(path, "r")) == NULL) 618 minfree = 0; 619 else { 620 if (fgets(buf, sizeof(buf), fp) == NULL) 621 minfree = 0; 622 else 623 minfree = atoi(buf); 624 (void)fclose(fp); 625 } 626 627 needed = (dumpsize + kernelsize) / 1024; 628 if (((minfree > 0) ? spacefree : totfree) - needed < minfree) { 629 syslog(LOG_WARNING, 630 "no dump, not enough free space on device (%lld available, need %lld)", 631 (long long)(minfree > 0 ? spacefree : totfree), 632 (long long)needed); 633 return (0); 634 } 635 if (spacefree - needed < 0) 636 syslog(LOG_WARNING, 637 "dump performed, but free space threshold crossed"); 638 return (1); 639 } 640 641 int 642 Open(name, rw) 643 const char *name; 644 int rw; 645 { 646 int fd; 647 648 if ((fd = open(name, rw, 0)) < 0) { 649 syslog(LOG_ERR, "%s: %m", name); 650 exit(1); 651 } 652 return (fd); 653 } 654 655 int 656 Read(fd, bp, size) 657 int fd, size; 658 void *bp; 659 { 660 int nr; 661 662 nr = read(fd, bp, size); 663 if (nr != size) { 664 syslog(LOG_ERR, "read: %m"); 665 exit(1); 666 } 667 return (nr); 668 } 669 670 void 671 Lseek(fd, off, flag) 672 int fd, flag; 673 off_t off; 674 { 675 off_t ret; 676 677 ret = lseek(fd, off, flag); 678 if (ret == -1) { 679 syslog(LOG_ERR, "lseek: %m"); 680 exit(1); 681 } 682 } 683 684 /* 685 * DumpWrite and DumpRead block io requests to the * dump device. 686 */ 687 #define DUMPBUFSIZE 8192 688 void 689 DumpWrite(fd, bp, size, off, flag) 690 int fd, size, flag; 691 void *bp; 692 off_t off; 693 { 694 unsigned char buf[DUMPBUFSIZE], *p, *q; 695 off_t pos; 696 int i, j; 697 698 if (flag != L_SET) { 699 syslog(LOG_ERR, "lseek: not LSET"); 700 exit(2); 701 } 702 q = bp; 703 while (size) { 704 pos = off & ~(DUMPBUFSIZE - 1); 705 Lseek(fd, pos, flag); 706 (void)Read(fd, buf, sizeof(buf)); 707 j = off & (DUMPBUFSIZE - 1); 708 p = buf + j; 709 i = size; 710 if (i > DUMPBUFSIZE - j) 711 i = DUMPBUFSIZE - j; 712 memcpy(p, q, i); 713 Lseek(fd, pos, flag); 714 (void)Write(fd, buf, sizeof(buf)); 715 size -= i; 716 q += i; 717 off += i; 718 } 719 } 720 721 void 722 DumpRead(fd, bp, size, off, flag) 723 int fd, size, flag; 724 void *bp; 725 off_t off; 726 { 727 unsigned char buf[DUMPBUFSIZE], *p, *q; 728 off_t pos; 729 int i, j; 730 731 if (flag != L_SET) { 732 syslog(LOG_ERR, "lseek: not LSET"); 733 exit(2); 734 } 735 q = bp; 736 while (size) { 737 pos = off & ~(DUMPBUFSIZE - 1); 738 Lseek(fd, pos, flag); 739 (void)Read(fd, buf, sizeof(buf)); 740 j = off & (DUMPBUFSIZE - 1); 741 p = buf + j; 742 i = size; 743 if (i > DUMPBUFSIZE - j) 744 i = DUMPBUFSIZE - j; 745 memcpy(q, p, i); 746 size -= i; 747 q += i; 748 off += i; 749 } 750 } 751 752 void 753 Write(fd, bp, size) 754 int fd, size; 755 void *bp; 756 { 757 int n; 758 759 if ((n = write(fd, bp, size)) < size) { 760 syslog(LOG_ERR, "write: %m"); 761 exit(1); 762 } 763 } 764 765 void 766 usage() 767 { 768 (void)syslog(LOG_ERR, "usage: savecore [-cfkvz] [-N system] directory"); 769 exit(1); 770 } 771