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