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