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