savecore.c revision 96025
11541Srgrimes/*- 21541Srgrimes * Copyright (c) 2002 Poul-Henning Kamp 31541Srgrimes * Copyright (c) 2002 Networks Associates Technology, Inc. 41541Srgrimes * All rights reserved. 51541Srgrimes * 61541Srgrimes * This software was developed for the FreeBSD Project by Poul-Henning Kamp 71541Srgrimes * and NAI Labs, the Security Research Division of Network Associates, Inc. 81541Srgrimes * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 91541Srgrimes * DARPA CHATS research program. 101541Srgrimes * 111541Srgrimes * Redistribution and use in source and binary forms, with or without 121541Srgrimes * modification, are permitted provided that the following conditions 131541Srgrimes * are met: 141541Srgrimes * 1. Redistributions of source code must retain the above copyright 151541Srgrimes * notice, this list of conditions and the following disclaimer. 161541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 171541Srgrimes * notice, this list of conditions and the following disclaimer in the 181541Srgrimes * documentation and/or other materials provided with the distribution. 191541Srgrimes * 3. The names of the authors may not be used to endorse or promote 201541Srgrimes * products derived from this software without specific prior written 211541Srgrimes * permission. 221541Srgrimes * 231541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 241541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 251541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 261541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 271541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 281541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 291541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 301541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 311541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 321541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 331541Srgrimes * SUCH DAMAGE. 3413765Smpp */ 351541Srgrimes 361541Srgrimes#include <sys/cdefs.h> 372165Spaul__FBSDID("$FreeBSD: head/sbin/savecore/savecore.c 96025 2002-05-04 10:36:35Z mux $"); 382811Sbde 392165Spaul#include <sys/types.h> 401541Srgrimes#include <sys/disk.h> 411541Srgrimes#include <sys/kerneldump.h> 421541Srgrimes#include <sys/param.h> 431541Srgrimes#include <sys/mount.h> 441541Srgrimes#include <sys/stat.h> 451541Srgrimes#include <err.h> 461541Srgrimes#include <errno.h> 471541Srgrimes#include <fcntl.h> 481541Srgrimes#include <fstab.h> 491541Srgrimes#include <md5.h> 501541Srgrimes#include <paths.h> 511541Srgrimes#include <stdio.h> 521541Srgrimes#include <stdlib.h> 531541Srgrimes#include <string.h> 541541Srgrimes#include <time.h> 551541Srgrimes#include <unistd.h> 561541Srgrimes 571541Srgrimesint clear, force, keep, verbose; /* flags */ 581541Srgrimesint nfound, nsaved, nerr; /* statistics */ 591541Srgrimes 601541Srgrimesstatic void 617945Sjulianprintheader(FILE *f, const struct kerneldumpheader *h, const char *device, 621541Srgrimes const char *md5) 631541Srgrimes{ 641541Srgrimes uint64_t dumplen; 651541Srgrimes time_t t; 661541Srgrimes 671541Srgrimes fprintf(f, "Good dump found on device %s\n", device); 681541Srgrimes fprintf(f, " Architecture: %s\n", h->architecture); 691541Srgrimes fprintf(f, " Architecture version: %d\n", 7012158Sbde dtoh32(h->architectureversion)); 7112158Sbde dumplen = dtoh64(h->dumplength); 721541Srgrimes fprintf(f, " Dump length: %lldB (%lld MB)\n", (long long)dumplen, 731541Srgrimes (long long)(dumplen >> 20)); 741541Srgrimes fprintf(f, " Blocksize: %d\n", dtoh32(h->blocksize)); 751541Srgrimes t = dtoh64(h->dumptime); 761541Srgrimes fprintf(f, " Dumptime: %s", ctime(&t)); 771541Srgrimes fprintf(f, " Hostname: %s\n", h->hostname); 781541Srgrimes fprintf(f, " Versionstring: %s", h->versionstring); 791541Srgrimes fprintf(f, " Panicstring: %s\n", h->panicstring); 8012158Sbde fprintf(f, " MD5: %s\n", md5); 811541Srgrimes fflush(f); 821541Srgrimes} 831541Srgrimes 841541Srgrimes/* 851541Srgrimes * Check that sufficient space is available on the disk that holds the 861541Srgrimes * save directory. 871541Srgrimes */ 881541Srgrimesstatic int 891541Srgrimescheck_space(char *savedir, off_t dumpsize) 901541Srgrimes{ 911541Srgrimes FILE *fp; 921541Srgrimes const char *tkernel; 931541Srgrimes off_t minfree, spacefree, totfree, kernelsize, needed; 941541Srgrimes struct stat st; 951541Srgrimes struct statfs fsbuf; 961541Srgrimes char buf[100], path[MAXPATHLEN]; 971541Srgrimes 981541Srgrimes tkernel = getbootfile(); 9913490Sdyson if (stat(tkernel, &st) < 0) 1001541Srgrimes err(1, "%s", tkernel); 1019356Sdg kernelsize = st.st_blocks * S_BLKSIZE; 1021541Srgrimes 1031541Srgrimes if (statfs(savedir, &fsbuf) < 0) 1041541Srgrimes err(1, "%s", savedir); 1051541Srgrimes spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024; 1061541Srgrimes totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024; 1071541Srgrimes 1081541Srgrimes (void)snprintf(path, sizeof(path), "%s/minfree", savedir); 1091541Srgrimes if ((fp = fopen(path, "r")) == NULL) 1101541Srgrimes minfree = 0; 1111541Srgrimes else { 1121541Srgrimes if (fgets(buf, sizeof(buf), fp) == NULL) 11312873Sbde minfree = 0; 11412873Sbde else 11512873Sbde minfree = atoi(buf); 11612873Sbde (void)fclose(fp); 11712873Sbde } 11812873Sbde 11912873Sbde needed = (dumpsize + kernelsize) / 1024; 12012873Sbde if (((minfree > 0) ? spacefree : totfree) - needed < minfree) { 12112873Sbde warnx("no dump, not enough free space on device" 12212873Sbde " (%lld available, need %lld)", 12312873Sbde (long long)(minfree > 0 ? spacefree : totfree), 12412873Sbde (long long)needed); 12512873Sbde return (0); 1261541Srgrimes } 1271541Srgrimes if (spacefree - needed < 0) 1281541Srgrimes warnx("dump performed, but free space threshold crossed"); 1291541Srgrimes return (1); 1301541Srgrimes} 1311541Srgrimes 1321541Srgrimes 1331541Srgrimes 1341541Srgrimesstatic void 1351541SrgrimesDoFile(char *savedir, const char *device) 1361541Srgrimes{ 1371541Srgrimes struct kerneldumpheader kdhf, kdhl; 1381541Srgrimes char buf[BUFSIZ]; 1391541Srgrimes struct stat sb; 1401541Srgrimes off_t mediasize, dumpsize, firsthd, lasthd; 1411541Srgrimes char *md5; 1421541Srgrimes FILE *info; 1431541Srgrimes int fd, fdcore, fdinfo, error, wl; 1441541Srgrimes u_int sectorsize; 1451541Srgrimes 1461541Srgrimes if (verbose) 1471541Srgrimes printf("Checking for kernel dump on device %s\n", device); 1481541Srgrimes 1491541Srgrimes mediasize = 0; 1501541Srgrimes fd = open(device, O_RDWR); 1511541Srgrimes if (fd < 0) { 1521541Srgrimes warn("%s", device); 1531541Srgrimes return; 1541541Srgrimes } 1551541Srgrimes error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 1561541Srgrimes if (!error) 1571541Srgrimes error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 1581541Srgrimes if (error) { 1591541Srgrimes warn("couldn't find media and/or sector size of %s", device); 1601541Srgrimes goto closefd; 1611541Srgrimes } 1621541Srgrimes 1631541Srgrimes if (verbose) { 1641541Srgrimes printf("Mediasize = %lld\n", (long long)mediasize); 1651541Srgrimes printf("Sectorsize = %u\n", sectorsize); 16612873Sbde } 1671541Srgrimes 1681541Srgrimes lasthd = mediasize - sectorsize; 1691541Srgrimes lseek(fd, lasthd, SEEK_SET); 1701541Srgrimes error = read(fd, &kdhl, sizeof kdhl); 1711541Srgrimes if (error != sizeof kdhl) { 1721541Srgrimes warn("error reading last dump header at offset %lld in %s", 1731541Srgrimes (long long)lasthd, device); 1741541Srgrimes goto closefd; 1751541Srgrimes } 1761541Srgrimes if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) { 1771541Srgrimes if (verbose) 1781541Srgrimes warnx("magic mismatch on last dump header on %s", 1791541Srgrimes device); 1801541Srgrimes goto closefd; 1811541Srgrimes } 1821541Srgrimes if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 1831541Srgrimes warnx("unknown version (%d) in last dump header on %s", 1841541Srgrimes dtoh32(kdhl.version), device); 1851541Srgrimes goto closefd; 1861541Srgrimes } 1871541Srgrimes 1881541Srgrimes nfound++; 1891541Srgrimes if (clear) 1901541Srgrimes goto nuke; 1911541Srgrimes 1921541Srgrimes if (kerneldump_parity(&kdhl)) { 1931541Srgrimes warnx("parity error on last dump header on %s", device); 1941541Srgrimes nerr++; 1951541Srgrimes goto closefd; 1961541Srgrimes } 1971541Srgrimes dumpsize = dtoh64(kdhl.dumplength); 19813765Smpp firsthd = lasthd - dumpsize - sizeof kdhf; 19913765Smpp lseek(fd, firsthd, SEEK_SET); 2001541Srgrimes error = read(fd, &kdhf, sizeof kdhf); 2011541Srgrimes if (error != sizeof kdhf) { 2021541Srgrimes warn("error reading first dump header at offset %lld in %s", 2031541Srgrimes (long long)firsthd, device); 2041541Srgrimes nerr++; 2051541Srgrimes goto closefd; 2061541Srgrimes } 2071541Srgrimes if (memcmp(&kdhl, &kdhf, sizeof kdhl)) { 2081541Srgrimes warn("first and last dump headers disagree on %s", device); 2091541Srgrimes nerr++; 2101541Srgrimes goto closefd; 2111541Srgrimes } 2121541Srgrimes md5 = MD5Data((unsigned char *)&kdhl, sizeof kdhl, NULL); 2131541Srgrimes sprintf(buf, "%s.info", md5); 2141541Srgrimes 2151541Srgrimes /* 2161541Srgrimes * See if the dump has been saved already. Don't save the dump 2171541Srgrimes * again, unless 'force' is in effect. 2181541Srgrimes */ 2191541Srgrimes if (stat(buf, &sb) == 0) { 2201541Srgrimes if (!force) { 2212946Swollman if (verbose) 22212873Sbde printf("Dump on device %s already saved\n", 2232946Swollman device); 22412873Sbde goto closefd; 2252946Swollman } 2262946Swollman } else if (errno != ENOENT) { 2271541Srgrimes warn("error while checking for pre-saved core file"); 2281541Srgrimes nerr++; 2291541Srgrimes goto closefd; 2301541Srgrimes } 2311541Srgrimes 2327090Sbde if (!check_space(savedir, dumpsize)) { 2331541Srgrimes nerr++; 2341541Srgrimes goto closefd; 2351541Srgrimes } 2361541Srgrimes /* 2371541Srgrimes * Create or overwrite any existing files. 2381541Srgrimes */ 2391541Srgrimes fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 2401541Srgrimes if (fdinfo < 0) { 2412997Swollman warn("%s", buf); 2422997Swollman nerr++; 2432997Swollman goto closefd; 2442997Swollman } 2451541Srgrimes sprintf(buf, "%s.core", md5); 2467461Sdg fdcore = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 2471541Srgrimes if (fdcore < 0) { 2481541Srgrimes warn("%s", buf); 2491541Srgrimes close(fdinfo); 25012873Sbde nerr++; 25112873Sbde goto closefd; 2527461Sdg } 2537461Sdg info = fdopen(fdinfo, "w"); 25412873Sbde 2552997Swollman if (verbose) 25612873Sbde printheader(stdout, &kdhl, device, md5); 2572997Swollman 2581541Srgrimes printf("Saving dump to file %s\n", buf); 2591541Srgrimes 2601541Srgrimes printheader(info, &kdhl, device, md5); 2611541Srgrimes 2621541Srgrimes while (dumpsize > 0) { 26313765Smpp wl = sizeof(buf); 2641541Srgrimes if (wl > dumpsize) 2651541Srgrimes wl = dumpsize; 2661541Srgrimes error = read(fd, buf, wl); 2671541Srgrimes if (error != wl) { 2681541Srgrimes warn("read error on %s", device); 2691541Srgrimes nerr++; 2701541Srgrimes goto closeall; 2711541Srgrimes } 2721541Srgrimes error = write(fdcore, buf, wl); 2731541Srgrimes if (error != wl) { 2741541Srgrimes warn("write error on %s.core file", md5); 2751541Srgrimes nerr++; 2761541Srgrimes goto closeall; 2771541Srgrimes } 2781541Srgrimes dumpsize -= wl; 2791541Srgrimes } 2801541Srgrimes nsaved++; 2811541Srgrimes close(fdinfo); 2821541Srgrimes close(fdcore); 2831541Srgrimes 2841541Srgrimes if (verbose) 2851541Srgrimes printf("Dump saved\n"); 2861541Srgrimes 2871541Srgrimes nuke: 2881541Srgrimes if (clear || !keep) { 2891541Srgrimes if (verbose) 2901541Srgrimes printf("Clearing dump header\n"); 2911541Srgrimes memset(&kdhl, 0, sizeof kdhl); 2921541Srgrimes lseek(fd, lasthd, SEEK_SET); 2931541Srgrimes error = write(fd, &kdhl, sizeof kdhl); 2941541Srgrimes if (error != sizeof kdhl) 2951541Srgrimes warn("error while clearing the dump header"); 2961541Srgrimes } 2971541Srgrimes close(fd); 2981541Srgrimes return; 2991541Srgrimes 3001541Srgrimes closeall: 3011541Srgrimes close(fdinfo); 3021541Srgrimes close(fdcore); 3031541Srgrimes 3041541Srgrimes closefd: 3051541Srgrimes close(fd); 3061541Srgrimes} 3071541Srgrimes 3081541Srgrimesstatic void 3091541Srgrimesusage(void) 3101541Srgrimes{ 3111541Srgrimes fprintf(stderr, "usage: savecore [-cfkv] [directory [device...]]\n"); 3121541Srgrimes exit (1); 3131541Srgrimes} 3141541Srgrimes 3151541Srgrimesint 3161541Srgrimesmain(int argc, char **argv) 3171541Srgrimes{ 3181541Srgrimes int i, ch, error; 3191541Srgrimes struct fstab *fsp; 3201541Srgrimes char *savedir; 32113765Smpp 3221541Srgrimes savedir = strdup("."); 32313765Smpp if (savedir == NULL) 3241541Srgrimes errx(1, "Cannot allocate memory"); 3251541Srgrimes while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1) 3261541Srgrimes switch(ch) { 3271541Srgrimes case 'c': 3281541Srgrimes clear = 1; 3291541Srgrimes break; 3301541Srgrimes case 'k': 3311541Srgrimes keep = 1; 3321541Srgrimes break; 3331541Srgrimes case 'v': 3341541Srgrimes verbose = 1; 3351541Srgrimes break; 3361541Srgrimes case 'f': 3371541Srgrimes force = 1; 33812158Sbde break; 3391541Srgrimes case 'd': /* Obsolete */ 3401541Srgrimes case 'N': 3411541Srgrimes case 'z': 34212158Sbde case '?': 3431541Srgrimes default: 3441541Srgrimes usage(); 3451541Srgrimes } 3461541Srgrimes argc -= optind; 3471541Srgrimes argv += optind; 3481541Srgrimes if (argc >= 1) { 3491541Srgrimes error = chdir(argv[0]); 3501541Srgrimes if (error) 3511541Srgrimes err(1, "chdir(%s)", argv[0]); 3521541Srgrimes savedir = argv[0]; 3531541Srgrimes argc--; 3541541Srgrimes argv++; 3551541Srgrimes } 3561541Srgrimes if (argc == 0) { 3571541Srgrimes for (;;) { 3581541Srgrimes fsp = getfsent(); 3591541Srgrimes if (fsp == NULL) 3601541Srgrimes break; 3611541Srgrimes if (strcmp(fsp->fs_vfstype, "swap") && 3621541Srgrimes strcmp(fsp->fs_vfstype, "dump")) 3631541Srgrimes continue; 3641541Srgrimes DoFile(savedir, fsp->fs_spec); 3651541Srgrimes } 3661541Srgrimes } else { 3671541Srgrimes for (i = 0; i < argc; i++) 3681541Srgrimes DoFile(savedir, argv[i]); 3691541Srgrimes } 3701541Srgrimes 3711541Srgrimes /* Emit minimal output. */ 3721541Srgrimes if (nfound == 0) 3731541Srgrimes printf("No dumps found\n"); 3741541Srgrimes else if (nsaved == 0) { 3751541Srgrimes if (nerr != 0) 3761541Srgrimes printf("Unsaved dumps found but not saved\n"); 3771541Srgrimes else 3781541Srgrimes printf("No unsaved dumps found\n"); 3791541Srgrimes } 3801541Srgrimes 3811541Srgrimes return (0); 3822811Sbde} 3831541Srgrimes