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