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