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