savecore.c revision 94580
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 * $FreeBSD: head/sbin/savecore/savecore.c 94580 2002-04-13 08:20:15Z marcel $ 36 */ 37 38#include <sys/types.h> 39#include <sys/disk.h> 40#include <sys/kerneldump.h> 41#include <sys/stat.h> 42#include <err.h> 43#include <errno.h> 44#include <fcntl.h> 45#include <fstab.h> 46#include <md5.h> 47#include <stdio.h> 48#include <stdlib.h> 49#include <string.h> 50#include <time.h> 51#include <unistd.h> 52 53int clear, force, keep, verbose; /* flags */ 54int nfound, nsaved; /* statistics */ 55 56static void 57printheader(FILE *f, const struct kerneldumpheader *h, const char *device, 58 const char *md5) 59{ 60 uint64_t dumplen; 61 time_t t; 62 63 fprintf(f, "Good dump found on device %s\n", device); 64 fprintf(f, " Architecture: %s\n", h->architecture); 65 fprintf(f, " Architecture version: %d\n", 66 dtoh32(h->architectureversion)); 67 dumplen = dtoh64(h->dumplength); 68 fprintf(f, " Dump length: %lldB (%lld MB)\n", (long long)dumplen, 69 (long long)(dumplen >> 20)); 70 fprintf(f, " Blocksize: %d\n", dtoh32(h->blocksize)); 71 t = dtoh64(h->dumptime); 72 fprintf(f, " Dumptime: %s", ctime(&t)); 73 fprintf(f, " Hostname: %s\n", h->hostname); 74 fprintf(f, " Versionstring: %s", h->versionstring); 75 fprintf(f, " Panicstring: %s\n", h->panicstring); 76 fprintf(f, " MD5: %s\n", md5); 77} 78 79 80static void 81DoFile(const char *device) 82{ 83 struct kerneldumpheader kdhf, kdhl; 84 char buf[BUFSIZ]; 85 struct stat sb; 86 off_t mediasize, dumpsize, firsthd, lasthd; 87 char *md5; 88 FILE *info; 89 int fd, fdcore, fdinfo, error, wl; 90 u_int sectorsize; 91 92 if (verbose) 93 printf("Checking for kernel dump on device %s\n", device); 94 95 mediasize = 0; 96 fd = open(device, O_RDWR); 97 if (fd < 0) { 98 warn("%s", device); 99 return; 100 } 101 error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 102 if (!error) 103 error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 104 if (error) { 105 warn("Couldn't find media and/or sector size of %s)", device); 106 goto closefd; 107 } 108 109 if (verbose) { 110 printf("Mediasize = %lld\n", (long long)mediasize); 111 printf("Sectorsize = %u\n", sectorsize); 112 } 113 114 lasthd = mediasize - sectorsize; 115 lseek(fd, lasthd, SEEK_SET); 116 error = read(fd, &kdhl, sizeof kdhl); 117 if (error != sizeof kdhl) { 118 warn("Error reading last dump header at offset %lld in %s", 119 (long long)lasthd, device); 120 goto closefd; 121 } 122 if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) { 123 if (verbose) 124 warnx("Magic mismatch on last dump header on %s", 125 device); 126 goto closefd; 127 } 128 if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 129 warnx("Unknown version (%d) in last dump header on %s", 130 dtoh32(kdhl.version), device); 131 goto closefd; 132 } 133 134 nfound++; 135 if (clear) 136 goto nuke; 137 138 if (kerneldump_parity(&kdhl)) { 139 warnx("Parity error on last dump header on %s", device); 140 goto closefd; 141 } 142 dumpsize = dtoh64(kdhl.dumplength); 143 firsthd = lasthd - dumpsize - sizeof kdhf; 144 lseek(fd, firsthd, SEEK_SET); 145 error = read(fd, &kdhf, sizeof kdhf); 146 if (error != sizeof kdhf) { 147 warn("Error reading first dump header at offset %lld in %s", 148 (long long)firsthd, device); 149 goto closefd; 150 } 151 if (memcmp(&kdhl, &kdhf, sizeof kdhl)) { 152 warn("First and last dump headers disagree on %s", device); 153 goto closefd; 154 } 155 md5 = MD5Data((unsigned char *)&kdhl, sizeof kdhl, NULL); 156 sprintf(buf, "%s.info", md5); 157 158 /* 159 * See if the dump has been saved already. Don't save the dump 160 * again, unless 'force' is in effect. 161 */ 162 if (stat(buf, &sb) == 0) { 163 if (!force) { 164 if (verbose) 165 printf("Dump on device %s already saved\n", 166 device); 167 goto closefd; 168 } 169 } else if (errno != ENOENT) { 170 warn("Error while checking for pre-saved core file"); 171 goto closefd; 172 } 173 174 /* 175 * Create or overwrite any existing files. 176 */ 177 fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 178 if (fdinfo < 0) { 179 warn("%s", buf); 180 goto closefd; 181 } 182 sprintf(buf, "%s.core", md5); 183 fdcore = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 184 if (fdcore < 0) { 185 warn("%s", buf); 186 close(fdinfo); 187 goto closefd; 188 } 189 info = fdopen(fdinfo, "w"); 190 191 if (verbose) 192 printheader(stdout, &kdhl, device, md5); 193 194 printf("Saving dump to file %s\n", buf); 195 nsaved++; 196 197 printheader(info, &kdhl, device, md5); 198 199 while (dumpsize > 0) { 200 wl = sizeof(buf); 201 if (wl > dumpsize) 202 wl = dumpsize; 203 error = read(fd, buf, wl); 204 if (error != wl) { 205 warn("Read error on %s", device); 206 goto closeall; 207 } 208 error = write(fdcore, buf, wl); 209 if (error != wl) { 210 warn("Write error on %s.core file", md5); 211 goto closeall; 212 } 213 dumpsize -= wl; 214 } 215 close(fdinfo); 216 close(fdcore); 217 218 if (verbose) 219 printf("Dump saved\n"); 220 221 nuke: 222 if (clear || !keep) { 223 if (verbose) 224 printf("Clearing dump header\n"); 225 memset(&kdhl, 0, sizeof kdhl); 226 lseek(fd, lasthd, SEEK_SET); 227 error = write(fd, &kdhl, sizeof kdhl); 228 if (error != sizeof kdhl) 229 warn("Error while clearing the dump header"); 230 } 231 close(fd); 232 return; 233 234 closeall: 235 close(fdinfo); 236 close(fdcore); 237 238 closefd: 239 close(fd); 240} 241 242static void 243usage(void) 244{ 245 errx(1, "usage: savecore [-cfkv] [directory [device...]]"); 246 exit (1); 247} 248 249int 250main(int argc, char **argv) 251{ 252 int i, ch, error; 253 struct fstab *fsp; 254 255 while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1) 256 switch(ch) { 257 case 'c': 258 clear = 1; 259 break; 260 case 'k': 261 keep = 1; 262 break; 263 case 'v': 264 verbose = 1; 265 break; 266 case 'f': 267 force = 1; 268 break; 269 case 'd': /* Obsolete */ 270 case 'N': 271 case 'z': 272 case '?': 273 default: 274 usage(); 275 } 276 argc -= optind; 277 argv += optind; 278 if (argc >= 1) { 279 error = chdir(argv[0]); 280 if (error) 281 err(1, "chdir(%s)", argv[0]); 282 argc--; 283 argv++; 284 } 285 if (argc == 0) { 286 for (;;) { 287 fsp = getfsent(); 288 if (fsp == NULL) 289 break; 290 if (strcmp(fsp->fs_vfstype, "swap") && 291 strcmp(fsp->fs_vfstype, "dump")) 292 continue; 293 DoFile(fsp->fs_spec); 294 } 295 } else { 296 for (i = 0; i < argc; i++) 297 DoFile(argv[i]); 298 } 299 300 /* Emit minimal output. */ 301 if (nfound == 0) 302 printf("No dumps found\n"); 303 else if (nsaved == 0) 304 printf("No unsaved dumps found\n"); 305 306 return (0); 307} 308