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