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