savecore.c revision 96025
1/*- 2 * Copyright (c) 2002 Poul-Henning Kamp 3 * Copyright (c) 2002 Networks Associates Technology, Inc. 4 * All rights reserved. 5 * 6 * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7 * and NAI Labs, the Security Research Division of Network Associates, Inc. 8 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9 * DARPA CHATS research program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. The names of the authors may not be used to endorse or promote 20 * products derived from this software without specific prior written 21 * permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include <sys/cdefs.h> 37__FBSDID("$FreeBSD: head/sbin/savecore/savecore.c 96025 2002-05-04 10:36:35Z mux $"); 38 39#include <sys/types.h> 40#include <sys/disk.h> 41#include <sys/kerneldump.h> 42#include <sys/param.h> 43#include <sys/mount.h> 44#include <sys/stat.h> 45#include <err.h> 46#include <errno.h> 47#include <fcntl.h> 48#include <fstab.h> 49#include <md5.h> 50#include <paths.h> 51#include <stdio.h> 52#include <stdlib.h> 53#include <string.h> 54#include <time.h> 55#include <unistd.h> 56 57int clear, force, keep, verbose; /* flags */ 58int nfound, nsaved, nerr; /* statistics */ 59 60static void 61printheader(FILE *f, const struct kerneldumpheader *h, const char *device, 62 const char *md5) 63{ 64 uint64_t dumplen; 65 time_t t; 66 67 fprintf(f, "Good dump found on device %s\n", device); 68 fprintf(f, " Architecture: %s\n", h->architecture); 69 fprintf(f, " Architecture version: %d\n", 70 dtoh32(h->architectureversion)); 71 dumplen = dtoh64(h->dumplength); 72 fprintf(f, " Dump length: %lldB (%lld MB)\n", (long long)dumplen, 73 (long long)(dumplen >> 20)); 74 fprintf(f, " Blocksize: %d\n", dtoh32(h->blocksize)); 75 t = dtoh64(h->dumptime); 76 fprintf(f, " Dumptime: %s", ctime(&t)); 77 fprintf(f, " Hostname: %s\n", h->hostname); 78 fprintf(f, " Versionstring: %s", h->versionstring); 79 fprintf(f, " Panicstring: %s\n", h->panicstring); 80 fprintf(f, " MD5: %s\n", md5); 81 fflush(f); 82} 83 84/* 85 * Check that sufficient space is available on the disk that holds the 86 * save directory. 87 */ 88static int 89check_space(char *savedir, off_t dumpsize) 90{ 91 FILE *fp; 92 const char *tkernel; 93 off_t minfree, spacefree, totfree, kernelsize, needed; 94 struct stat st; 95 struct statfs fsbuf; 96 char buf[100], path[MAXPATHLEN]; 97 98 tkernel = getbootfile(); 99 if (stat(tkernel, &st) < 0) 100 err(1, "%s", tkernel); 101 kernelsize = st.st_blocks * S_BLKSIZE; 102 103 if (statfs(savedir, &fsbuf) < 0) 104 err(1, "%s", savedir); 105 spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024; 106 totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024; 107 108 (void)snprintf(path, sizeof(path), "%s/minfree", savedir); 109 if ((fp = fopen(path, "r")) == NULL) 110 minfree = 0; 111 else { 112 if (fgets(buf, sizeof(buf), fp) == NULL) 113 minfree = 0; 114 else 115 minfree = atoi(buf); 116 (void)fclose(fp); 117 } 118 119 needed = (dumpsize + kernelsize) / 1024; 120 if (((minfree > 0) ? spacefree : totfree) - needed < minfree) { 121 warnx("no dump, not enough free space on device" 122 " (%lld available, need %lld)", 123 (long long)(minfree > 0 ? spacefree : totfree), 124 (long long)needed); 125 return (0); 126 } 127 if (spacefree - needed < 0) 128 warnx("dump performed, but free space threshold crossed"); 129 return (1); 130} 131 132 133 134static void 135DoFile(char *savedir, const char *device) 136{ 137 struct kerneldumpheader kdhf, kdhl; 138 char buf[BUFSIZ]; 139 struct stat sb; 140 off_t mediasize, dumpsize, firsthd, lasthd; 141 char *md5; 142 FILE *info; 143 int fd, fdcore, fdinfo, error, wl; 144 u_int sectorsize; 145 146 if (verbose) 147 printf("Checking for kernel dump on device %s\n", device); 148 149 mediasize = 0; 150 fd = open(device, O_RDWR); 151 if (fd < 0) { 152 warn("%s", device); 153 return; 154 } 155 error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 156 if (!error) 157 error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 158 if (error) { 159 warn("couldn't find media and/or sector size of %s", device); 160 goto closefd; 161 } 162 163 if (verbose) { 164 printf("Mediasize = %lld\n", (long long)mediasize); 165 printf("Sectorsize = %u\n", sectorsize); 166 } 167 168 lasthd = mediasize - sectorsize; 169 lseek(fd, lasthd, SEEK_SET); 170 error = read(fd, &kdhl, sizeof kdhl); 171 if (error != sizeof kdhl) { 172 warn("error reading last dump header at offset %lld in %s", 173 (long long)lasthd, device); 174 goto closefd; 175 } 176 if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) { 177 if (verbose) 178 warnx("magic mismatch on last dump header on %s", 179 device); 180 goto closefd; 181 } 182 if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 183 warnx("unknown version (%d) in last dump header on %s", 184 dtoh32(kdhl.version), device); 185 goto closefd; 186 } 187 188 nfound++; 189 if (clear) 190 goto nuke; 191 192 if (kerneldump_parity(&kdhl)) { 193 warnx("parity error on last dump header on %s", device); 194 nerr++; 195 goto closefd; 196 } 197 dumpsize = dtoh64(kdhl.dumplength); 198 firsthd = lasthd - dumpsize - sizeof kdhf; 199 lseek(fd, firsthd, SEEK_SET); 200 error = read(fd, &kdhf, sizeof kdhf); 201 if (error != sizeof kdhf) { 202 warn("error reading first dump header at offset %lld in %s", 203 (long long)firsthd, device); 204 nerr++; 205 goto closefd; 206 } 207 if (memcmp(&kdhl, &kdhf, sizeof kdhl)) { 208 warn("first and last dump headers disagree on %s", device); 209 nerr++; 210 goto closefd; 211 } 212 md5 = MD5Data((unsigned char *)&kdhl, sizeof kdhl, NULL); 213 sprintf(buf, "%s.info", md5); 214 215 /* 216 * See if the dump has been saved already. Don't save the dump 217 * again, unless 'force' is in effect. 218 */ 219 if (stat(buf, &sb) == 0) { 220 if (!force) { 221 if (verbose) 222 printf("Dump on device %s already saved\n", 223 device); 224 goto closefd; 225 } 226 } else if (errno != ENOENT) { 227 warn("error while checking for pre-saved core file"); 228 nerr++; 229 goto closefd; 230 } 231 232 if (!check_space(savedir, dumpsize)) { 233 nerr++; 234 goto closefd; 235 } 236 /* 237 * Create or overwrite any existing files. 238 */ 239 fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 240 if (fdinfo < 0) { 241 warn("%s", buf); 242 nerr++; 243 goto closefd; 244 } 245 sprintf(buf, "%s.core", md5); 246 fdcore = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 247 if (fdcore < 0) { 248 warn("%s", buf); 249 close(fdinfo); 250 nerr++; 251 goto closefd; 252 } 253 info = fdopen(fdinfo, "w"); 254 255 if (verbose) 256 printheader(stdout, &kdhl, device, md5); 257 258 printf("Saving dump to file %s\n", buf); 259 260 printheader(info, &kdhl, device, md5); 261 262 while (dumpsize > 0) { 263 wl = sizeof(buf); 264 if (wl > dumpsize) 265 wl = dumpsize; 266 error = read(fd, buf, wl); 267 if (error != wl) { 268 warn("read error on %s", device); 269 nerr++; 270 goto closeall; 271 } 272 error = write(fdcore, buf, wl); 273 if (error != wl) { 274 warn("write error on %s.core file", md5); 275 nerr++; 276 goto closeall; 277 } 278 dumpsize -= wl; 279 } 280 nsaved++; 281 close(fdinfo); 282 close(fdcore); 283 284 if (verbose) 285 printf("Dump saved\n"); 286 287 nuke: 288 if (clear || !keep) { 289 if (verbose) 290 printf("Clearing dump header\n"); 291 memset(&kdhl, 0, sizeof kdhl); 292 lseek(fd, lasthd, SEEK_SET); 293 error = write(fd, &kdhl, sizeof kdhl); 294 if (error != sizeof kdhl) 295 warn("error while clearing the dump header"); 296 } 297 close(fd); 298 return; 299 300 closeall: 301 close(fdinfo); 302 close(fdcore); 303 304 closefd: 305 close(fd); 306} 307 308static void 309usage(void) 310{ 311 fprintf(stderr, "usage: savecore [-cfkv] [directory [device...]]\n"); 312 exit (1); 313} 314 315int 316main(int argc, char **argv) 317{ 318 int i, ch, error; 319 struct fstab *fsp; 320 char *savedir; 321 322 savedir = strdup("."); 323 if (savedir == NULL) 324 errx(1, "Cannot allocate memory"); 325 while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1) 326 switch(ch) { 327 case 'c': 328 clear = 1; 329 break; 330 case 'k': 331 keep = 1; 332 break; 333 case 'v': 334 verbose = 1; 335 break; 336 case 'f': 337 force = 1; 338 break; 339 case 'd': /* Obsolete */ 340 case 'N': 341 case 'z': 342 case '?': 343 default: 344 usage(); 345 } 346 argc -= optind; 347 argv += optind; 348 if (argc >= 1) { 349 error = chdir(argv[0]); 350 if (error) 351 err(1, "chdir(%s)", argv[0]); 352 savedir = argv[0]; 353 argc--; 354 argv++; 355 } 356 if (argc == 0) { 357 for (;;) { 358 fsp = getfsent(); 359 if (fsp == NULL) 360 break; 361 if (strcmp(fsp->fs_vfstype, "swap") && 362 strcmp(fsp->fs_vfstype, "dump")) 363 continue; 364 DoFile(savedir, fsp->fs_spec); 365 } 366 } else { 367 for (i = 0; i < argc; i++) 368 DoFile(savedir, argv[i]); 369 } 370 371 /* Emit minimal output. */ 372 if (nfound == 0) 373 printf("No dumps found\n"); 374 else if (nsaved == 0) { 375 if (nerr != 0) 376 printf("Unsaved dumps found but not saved\n"); 377 else 378 printf("No unsaved dumps found\n"); 379 } 380 381 return (0); 382} 383