savecore.c revision 1.7
1/* 2 * Copyright (c) 1980, 1986, 1989 The Regents of the University of California. 3 * 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 34#ifndef lint 35char copyright[] = 36"@(#) Copyright (c) 1980, 1986, 1989 The Regents of the University of California.\n\ 37 All rights reserved.\n"; 38#endif /* not lint */ 39 40#ifndef lint 41/*static char sccsid[] = "from: @(#)savecore.c 5.26 (Berkeley) 4/8/91";*/ 42static char rcsid[] = "$Id: savecore.c,v 1.7 1994/04/04 22:45:08 cgd Exp $"; 43#endif /* not lint */ 44 45#include <sys/param.h> 46#include <sys/mount.h> 47#include <sys/stat.h> 48#include <sys/time.h> 49#include <sys/file.h> 50#include <sys/syslog.h> 51#include <dirent.h> 52#include <errno.h> 53#include <nlist.h> 54#include <paths.h> 55#include <stdio.h> 56#include <stdlib.h> 57#include <time.h> 58#include <unistd.h> 59 60#define DAY (60L*60L*24L) 61#define LEEWAY (3*DAY) 62 63#define eq(a,b) (!strcmp(a,b)) 64#define ok(number) ((number) - KERNBASE) 65 66struct nlist current_nl[] = { /* namelist for currently running system */ 67#define X_DUMPDEV 0 68 { "_dumpdev" }, 69#define X_DUMPLO 1 70 { "_dumplo" }, 71#define X_TIME 2 72 { "_time" }, 73#define X_DUMPSIZE 3 74 { "_dumpsize" }, 75#define X_VERSION 4 76 { "_version" }, 77#define X_PANICSTR 5 78 { "_panicstr" }, 79#define X_DUMPMAG 6 80 { "_dumpmag" }, 81 { "" }, 82}; 83 84struct nlist dump_nl[] = { /* name list for dumped system */ 85 { "_dumpdev" }, /* entries MUST be the same as */ 86 { "_dumplo" }, /* those in current_nl[] */ 87 { "_time" }, 88 { "_dumpsize" }, 89 { "_version" }, 90 { "_panicstr" }, 91 { "_dumpmag" }, 92 { "" }, 93}; 94 95char *kernel; 96char *dirname; /* directory to save dumps in */ 97char *ddname; /* name of dump device */ 98int dumpfd; /* read/write descriptor on block dev */ 99dev_t dumpdev; /* dump device */ 100time_t dumptime; /* time the dump was taken */ 101int dumplo; /* where dump starts on dumpdev */ 102int dumpsize; /* amount of memory dumped */ 103int dumpmag; /* magic number in dump */ 104time_t now; /* current date */ 105char vers[80]; 106char core_vers[80]; 107char panic_mesg[80]; 108int panicstr; 109int verbose; 110int force; 111int clear; 112 113char *path __P((char *)); 114char *find_dev __P((dev_t, int)); 115off_t Lseek __P((int, off_t, int)); 116 117main(argc, argv) 118 char **argv; 119 int argc; 120{ 121 int ch; 122 char *cp; 123 124 while ((ch = getopt(argc, argv, "cdfv")) != EOF) 125 switch(ch) { 126 case 'c': 127 clear = 1; 128 break; 129 case 'd': /* not documented */ 130 case 'v': 131 verbose = 1; 132 break; 133 case 'f': 134 force = 1; 135 break; 136 case '?': 137 default: 138 usage(); 139 } 140 argc -= optind; 141 argv += optind; 142 143 /* This is wrong, but I want "savecore -c" to work. */ 144 if (!clear) { 145 if (argc != 1 && argc != 2) 146 usage(); 147 dirname = argv[0]; 148 } 149 if (argc == 2) 150 kernel = argv[1]; 151 152 openlog("savecore", LOG_ODELAY, LOG_AUTH); 153 154 read_kmem(); 155 if (!dump_exists()) { 156/* (void)fprintf(stderr, "savecore: no core dump\n");*/ 157 if (!force) 158 exit(0); 159 } 160 if (clear) { 161 clear_dump(); 162 exit(0); 163 } 164 (void) time(&now); 165 check_kmem(); 166 if (panicstr) 167 log(LOG_CRIT, "reboot after panic: %s\n", panic_mesg); 168 else 169 syslog(LOG_CRIT, "reboot\n"); 170 171 if (access(dirname, W_OK) < 0) { 172 Perror(LOG_ERR, "%s: %m\n", dirname); 173 exit(1); 174 } 175 if ((!get_crashtime() || !check_space()) && !force) 176 exit(1); 177 save_core(); 178 clear_dump(); 179 exit(0); 180} 181 182dump_exists() 183{ 184 int word; 185 186 Lseek(dumpfd, dumplo + ok(dump_nl[X_DUMPMAG].n_value), L_SET); 187 Read(dumpfd, (char *)&word, sizeof (word)); 188 if (verbose && word != dumpmag) 189 printf("magic number mismatch: %x != %x\n", word, dumpmag); 190 return (word == dumpmag); 191} 192 193clear_dump() 194{ 195 int zero = 0; 196 197 Lseek(dumpfd, dumplo + ok(dump_nl[X_DUMPMAG].n_value), L_SET); 198 Write(dumpfd, (char *)&zero, sizeof (zero)); 199} 200 201char * 202find_dev(dev, type) 203 register dev_t dev; 204 register int type; 205{ 206 register DIR *dfd = opendir(_PATH_DEV); 207 struct dirent *dir; 208 struct stat statb; 209 static char devname[MAXPATHLEN + 1]; 210 char *dp; 211 212 strcpy(devname, _PATH_DEV); 213 while ((dir = readdir(dfd))) { 214 strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name); 215 if (stat(devname, &statb)) { 216 perror(devname); 217 continue; 218 } 219 if ((statb.st_mode&S_IFMT) != type) 220 continue; 221 if (dev == statb.st_rdev) { 222 closedir(dfd); 223 dp = malloc(strlen(devname)+1); 224 strcpy(dp, devname); 225 return (dp); 226 } 227 } 228 closedir(dfd); 229 log(LOG_ERR, "Can't find device %d/%d\n", major(dev), minor(dev)); 230 exit(1); 231 /*NOTREACHED*/ 232} 233 234char * 235rawname(s) 236 char *s; 237{ 238 static char name[MAXPATHLEN]; 239 char *sl, *rindex(); 240 241 if ((sl = rindex(s, '/')) == NULL || sl[1] == '0') { 242 log(LOG_ERR, "can't make raw dump device name from %s?\n", s); 243 return (s); 244 } 245 sprintf(name, "%.*s/r%s", sl - s, s, sl + 1); 246 return (name); 247} 248 249int cursyms[] = 250 { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; 251int dumpsyms[] = 252 { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; 253read_kmem() 254{ 255 register char *cp; 256 FILE *fp; 257 char *dump_sys; 258 int kmem, i; 259 260 dump_sys = kernel ? kernel : _PATH_UNIX; 261 nlist(_PATH_UNIX, current_nl); 262 nlist(dump_sys, dump_nl); 263 /* 264 * Some names we need for the currently running system, 265 * others for the system that was running when the dump was made. 266 * The values obtained from the current system are used 267 * to look for things in /dev/kmem that cannot be found 268 * in the dump_sys namelist, but are presumed to be the same 269 * (since the disk partitions are probably the same!) 270 */ 271 for (i = 0; cursyms[i] != -1; i++) 272 if (current_nl[cursyms[i]].n_value == 0) { 273 log(LOG_ERR, "%s: %s not in namelist\n", _PATH_UNIX, 274 current_nl[cursyms[i]].n_name); 275 exit(1); 276 } 277 for (i = 0; dumpsyms[i] != -1; i++) 278 if (dump_nl[dumpsyms[i]].n_value == 0) { 279 log(LOG_ERR, "%s: %s not in namelist\n", dump_sys, 280 dump_nl[dumpsyms[i]].n_name); 281 exit(1); 282 } 283 kmem = Open(_PATH_KMEM, O_RDONLY); 284 Lseek(kmem, current_nl[X_DUMPDEV].n_value, L_SET); 285 Read(kmem, (char *)&dumpdev, sizeof (dumpdev)); 286 if (dumpdev == NODEV) { 287 if (verbose) 288 printf("dumpdev = NODEV\n"); 289 exit(2); 290 } 291 Lseek(kmem, current_nl[X_DUMPLO].n_value, L_SET); 292 Read(kmem, (char *)&dumplo, sizeof (dumplo)); 293 if (verbose) 294 printf("dumplo = %d (%d * %d)\n", dumplo, dumplo/DEV_BSIZE, 295 DEV_BSIZE); 296 Lseek(kmem, current_nl[X_DUMPMAG].n_value, L_SET); 297 Read(kmem, (char *)&dumpmag, sizeof (dumpmag)); 298 dumplo *= DEV_BSIZE; 299 ddname = find_dev(dumpdev, S_IFBLK); 300 dumpfd = Open(ddname, O_RDWR); 301 fp = fdopen(kmem, "r"); 302 if (fp == NULL) { 303 log(LOG_ERR, "Couldn't fdopen kmem\n"); 304 exit(1); 305 } 306 if (kernel) 307 return; 308 fseek(fp, (long)current_nl[X_VERSION].n_value, L_SET); 309 fgets(vers, sizeof (vers), fp); 310 fclose(fp); 311} 312 313check_kmem() 314{ 315 FILE *fp; 316 register char *cp; 317 318 fp = fdopen(dumpfd, "r"); 319 if (fp == NULL) { 320 log(LOG_ERR, "Can't fdopen dumpfd\n"); 321 exit(1); 322 } 323 324 fseek(fp, (off_t)(dumplo+ok(dump_nl[X_VERSION].n_value)), L_SET); 325 fgets(core_vers, sizeof (core_vers), fp); 326 if (!eq(vers, core_vers) && kernel == 0) { 327 log(LOG_WARNING, "Warning: %s version mismatch:\n", _PATH_UNIX); 328 log(LOG_WARNING, "\t%s\n", vers); 329 log(LOG_WARNING, "and\t%s\n", core_vers); 330 } 331 332 fseek(fp, (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET); 333 fread((char *)&panicstr, sizeof (panicstr), 1, fp); 334 if (panicstr) { 335 fseek(fp, dumplo + ok(panicstr), L_SET); 336 cp = panic_mesg; 337 do 338 *cp = getc(fp); 339 while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)]); 340 } 341 /* don't fclose(fp); we want the file descriptor */ 342} 343 344get_crashtime() 345{ 346 time_t clobber = (time_t)0; 347 348 Lseek(dumpfd, dumplo + ok(dump_nl[X_TIME].n_value), L_SET); 349 Read(dumpfd, (char *)&dumptime, sizeof dumptime); 350 if (dumptime == 0) { 351 if (verbose) 352 printf("Dump time is zero.\n"); 353 return (0); 354 } 355 printf("System went down at %s", ctime(&dumptime)); 356 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { 357 printf("dump time is unreasonable\n"); 358 return (0); 359 } 360 return (1); 361} 362 363char * 364path(file) 365 char *file; 366{ 367 register char *cp = malloc(strlen(file) + strlen(dirname) + 2); 368 369 (void) strcpy(cp, dirname); 370 (void) strcat(cp, "/"); 371 (void) strcat(cp, file); 372 return (cp); 373} 374 375check_space() 376{ 377 long minfree, spacefree; 378 struct statfs fsbuf; 379 380 if (statfs(dirname, &fsbuf) < 0) { 381 Perror(LOG_ERR, "%s: %m\n", dirname); 382 exit(1); 383 } 384 spacefree = fsbuf.f_bavail * fsbuf.f_fsize / 1024; 385 minfree = read_number("minfree"); 386 if (minfree > 0 && spacefree - dumpsize < minfree) { 387 log(LOG_WARNING, "Dump omitted, not enough space on device\n"); 388 return (0); 389 } 390 if (spacefree - dumpsize < minfree) 391 log(LOG_WARNING, 392 "Dump performed, but free space threshold crossed\n"); 393 return (1); 394} 395 396read_number(fn) 397 char *fn; 398{ 399 char lin[80]; 400 register FILE *fp; 401 402 fp = fopen(path(fn), "r"); 403 if (fp == NULL) 404 return (0); 405 if (fgets(lin, 80, fp) == NULL) { 406 fclose(fp); 407 return (0); 408 } 409 fclose(fp); 410 return (atoi(lin)); 411} 412 413/*#define BUFSIZE (256*1024) /* 1/4 Mb */ 414#define BUFSIZE (8*1024) 415 416save_core() 417{ 418 register int n; 419 register char *cp; 420 register int ifd, ofd, bounds; 421 int ret; 422 char *bfile; 423 register FILE *fp; 424 425 cp = malloc(BUFSIZE); 426 if (cp == 0) { 427 log(LOG_ERR, "savecore: Can't allocate i/o buffer.\n"); 428 return; 429 } 430 bounds = read_number("bounds"); 431 ifd = Open(kernel ? kernel : _PATH_UNIX, O_RDONLY); 432 (void)sprintf(cp, "system.%d", bounds); 433 ofd = Create(path(cp), 0644); 434 while((n = Read(ifd, cp, BUFSIZE)) > 0) 435 Write(ofd, cp, n); 436 close(ifd); 437 close(ofd); 438 if ((ifd = open(rawname(ddname), O_RDONLY)) == -1) { 439 log(LOG_WARNING, "Can't open %s (%m); using block device", 440 rawname(ddname)); 441 ifd = dumpfd; 442 } 443 Lseek(dumpfd, dumplo + ok(dump_nl[X_DUMPSIZE].n_value), L_SET); 444 Read(dumpfd, (char *)&dumpsize, sizeof (dumpsize)); 445 (void)sprintf(cp, "ram.%d", bounds); 446 ofd = Create(path(cp), 0644); 447 Lseek(ifd, dumplo, L_SET); 448 dumpsize *= NBPG; 449 log(LOG_NOTICE, "Saving %d bytes of image in ram.%d\n", 450 dumpsize, bounds); 451 while (dumpsize > 0) { 452 n = read(ifd, cp, 453 dumpsize > BUFSIZE ? BUFSIZE : dumpsize); 454 if (n <= 0) { 455 if (n == 0) 456 log(LOG_WARNING, 457 "WARNING: EOF on dump device; %s\n", 458 "ram file may be incomplete"); 459 else 460 Perror(LOG_ERR, "read from dumpdev: %m", 461 "read"); 462 break; 463 } 464 if ((ret = write(ofd, cp, n)) < n) { 465 if (ret < 0) 466 Perror(LOG_ERR, "write: %m", "write"); 467 else 468 log(LOG_ERR, "short write: wrote %d of %d\n", 469 ret, n); 470 log(LOG_WARNING, "WARNING: ram file may be incomplete\n"); 471 break; 472 } 473 dumpsize -= n; 474 } 475 close(ifd); 476 close(ofd); 477 bfile = path("bounds"); 478 fp = fopen(bfile, "w"); 479 if (fp) { 480 fprintf(fp, "%d\n", bounds+1); 481 fclose(fp); 482 } else 483 Perror(LOG_ERR, "Can't create bounds file %s: %m", bfile); 484 free(cp); 485} 486 487/* 488 * Versions of std routines that exit on error. 489 */ 490Open(name, rw) 491 char *name; 492 int rw; 493{ 494 int fd; 495 496 fd = open(name, rw); 497 if (fd < 0) { 498 Perror(LOG_ERR, "%s: %m", name); 499 exit(1); 500 } 501 return (fd); 502} 503 504Read(fd, buff, size) 505 int fd, size; 506 char *buff; 507{ 508 int ret; 509 510 ret = read(fd, buff, size); 511 if (ret < 0) { 512 Perror(LOG_ERR, "read: %m", "read"); 513 exit(1); 514 } 515 return (ret); 516} 517 518off_t 519Lseek(fd, off, flag) 520 int fd, flag; 521 off_t off; 522{ 523 long ret; 524 525 ret = lseek(fd, off, flag); 526 if (ret == -1) { 527 Perror(LOG_ERR, "lseek: %m", "lseek"); 528 exit(1); 529 } 530 return (ret); 531} 532 533Create(file, mode) 534 char *file; 535 int mode; 536{ 537 register int fd; 538 539 fd = creat(file, mode); 540 if (fd < 0) { 541 Perror(LOG_ERR, "%s: %m", file); 542 exit(1); 543 } 544 return (fd); 545} 546 547Write(fd, buf, size) 548 int fd, size; 549 char *buf; 550{ 551 int n; 552 553 if ((n = write(fd, buf, size)) < size) { 554 if (n < 0) 555 Perror(LOG_ERR, "write: %m", "write"); 556 else 557 log(LOG_ERR, "short write: wrote %d of %d\n", n, size); 558 exit(1); 559 } 560} 561 562/* VARARGS2 */ 563log(level, msg, a1, a2) 564 int level; 565 char *msg; 566{ 567 568 fprintf(stderr, msg, a1, a2); 569 syslog(level, msg, a1, a2); 570} 571 572Perror(level, msg, s) 573 int level; 574 char *msg, *s; 575{ 576 int oerrno = errno; 577 578 perror(s); 579 errno = oerrno; 580 syslog(level, msg, s); 581} 582 583usage() 584{ 585 (void)fprintf(stderr, "usage: savecore [-cfv] dirname [system]\n"); 586 exit(1); 587} 588