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