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