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