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