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