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