|
|
1.1 ! root 1: /************************************************************************* ! 2: * This program is copyright (C) 1985, 1986 by Jonathan Payne. It is * ! 3: * provided to you without charge for use only on a licensed Unix * ! 4: * system. You may copy JOVE provided that this notice is included with * ! 5: * the copy. You may not sell copies of this program or versions * ! 6: * modified for use on microcomputer systems, unless the copies are * ! 7: * included with a Unix system distribution and the source is provided. * ! 8: *************************************************************************/ ! 9: ! 10: /* Recovers JOVE files after a system/editor crash. ! 11: Usage: recover [-d directory] [-syscrash] ! 12: The -syscrash option is specified in /etc/rc and what it does it ! 13: move all the jove tmp files from TMP_DIR to REC_DIR. ! 14: ! 15: The -d option lets you specify the directory to search for tmp files when ! 16: the default isn't the right one. ! 17: ! 18: Look in Makefile to change the default directories. */ ! 19: ! 20: #include <stdio.h> /* Do stdio first so it doesn't override OUR ! 21: definitions. */ ! 22: #undef EOF ! 23: #undef BUFSIZ ! 24: #undef putchar ! 25: ! 26: #include "jove.h" ! 27: #include "temp.h" ! 28: #include "rec.h" ! 29: #include <signal.h> ! 30: #include <sys/file.h> ! 31: #include <sys/stat.h> ! 32: #include <sys/dir.h> ! 33: ! 34: #ifndef L_SET ! 35: # define L_SET 0 ! 36: # define L_INCR 1 ! 37: #endif ! 38: ! 39: char blk_buf[BUFSIZ]; ! 40: int nleft; ! 41: FILE *ptrs_fp; ! 42: int data_fd; ! 43: struct rec_head Header; ! 44: char datafile[40], ! 45: pntrfile[40]; ! 46: long Nchars, ! 47: Nlines; ! 48: char tty[] = "/dev/tty"; ! 49: int UserID, ! 50: Verbose = 0; ! 51: ! 52: struct file_pair { ! 53: char *file_data, ! 54: *file_rec; ! 55: #define INSPECTED 01 ! 56: int file_flags; ! 57: struct file_pair *file_next; ! 58: } *First = 0, ! 59: *Last = 0; ! 60: ! 61: struct rec_entry *buflist[100] = {0}; ! 62: ! 63: #ifndef BSD4_2 ! 64: ! 65: typedef struct { ! 66: int d_fd; /* File descriptor for this directory */ ! 67: } DIR; ! 68: ! 69: DIR * ! 70: opendir(dir) ! 71: char *dir; ! 72: { ! 73: DIR *dp = (DIR *) malloc(sizeof *dp); ! 74: ! 75: if ((dp->d_fd = open(dir, 0)) == -1) ! 76: return NULL; ! 77: return dp; ! 78: } ! 79: ! 80: closedir(dp) ! 81: DIR *dp; ! 82: { ! 83: (void) close(dp->d_fd); ! 84: free(dp); ! 85: } ! 86: ! 87: struct direct * ! 88: readdir(dp) ! 89: DIR *dp; ! 90: { ! 91: static struct direct dir; ! 92: ! 93: do ! 94: if (read(dp->d_fd, &dir, sizeof dir) != sizeof dir) ! 95: return NULL; ! 96: while (dir.d_ino == 0); ! 97: ! 98: return &dir; ! 99: } ! 100: ! 101: #endif BSD4_2 ! 102: ! 103: /* Get a line at `tl' in the tmp file into `buf' which should be LBSIZE ! 104: long. */ ! 105: ! 106: char * ! 107: getline(tl, buf) ! 108: disk_line tl; ! 109: char *buf; ! 110: { ! 111: register char *bp, ! 112: *lp; ! 113: register int nl; ! 114: ! 115: lp = buf; ! 116: bp = getblock(tl); ! 117: nl = nleft; ! 118: tl &= ~OFFMSK; ! 119: ! 120: while (*lp++ = *bp++) { ! 121: if (--nl == 0) { ! 122: /* += INCRMT moves tl to the next block in ! 123: the tmp file. */ ! 124: bp = getblock(tl += INCRMT); ! 125: nl = nleft; ! 126: } ! 127: } ! 128: } ! 129: ! 130: char * ! 131: getblock(atl) ! 132: disk_line atl; ! 133: { ! 134: int bno, ! 135: off; ! 136: static int curblock = -1; ! 137: ! 138: bno = (atl >> OFFBTS) & BLKMSK; ! 139: off = (atl << SHFT) & LBTMSK; ! 140: nleft = BUFSIZ - off; ! 141: ! 142: if (bno != curblock) { ! 143: lseek(data_fd, (long) bno * BUFSIZ, L_SET); ! 144: read(data_fd, blk_buf, BUFSIZ); ! 145: curblock = bno; ! 146: } ! 147: return blk_buf + off; ! 148: } ! 149: ! 150: char * ! 151: copystr(s) ! 152: char *s; ! 153: { ! 154: char *str; ! 155: ! 156: str = malloc(strlen(s) + 1); ! 157: strcpy(str, s); ! 158: ! 159: return str; ! 160: } ! 161: ! 162: /* Scandir returns the number of entries or -1 if the directory cannoot ! 163: be opened or malloc fails. */ ! 164: ! 165: scandir(dir, nmptr, qualify, sorter) ! 166: char *dir; ! 167: struct direct ***nmptr; ! 168: int (*qualify)(); ! 169: struct direct *(*sorter)(); ! 170: { ! 171: DIR *dirp; ! 172: struct direct *entry, ! 173: **ourarray; ! 174: int nalloc = 10, ! 175: nentries = 0; ! 176: ! 177: if ((dirp = opendir(dir)) == NULL) ! 178: return -1; ! 179: ourarray = (struct direct **) malloc(nalloc * sizeof (struct direct *)); ! 180: while ((entry = readdir(dirp)) != NULL) { ! 181: if (qualify != 0 && (*qualify)(entry) == 0) ! 182: continue; ! 183: if (nentries == nalloc) { ! 184: ourarray = (struct direct **) realloc(ourarray, (nalloc += 10) * sizeof (struct direct)); ! 185: if (ourarray == NULL) ! 186: return -1; ! 187: } ! 188: ourarray[nentries] = (struct direct *) malloc(sizeof *entry); ! 189: *ourarray[nentries] = *entry; ! 190: nentries++; ! 191: } ! 192: closedir(dirp); ! 193: if (nentries != nalloc) ! 194: ourarray = (struct direct **) realloc(ourarray, ! 195: (nentries * sizeof (struct direct))); ! 196: if (sorter != 0) ! 197: qsort(ourarray, nentries, sizeof (struct direct **), sorter); ! 198: *nmptr = ourarray; ! 199: ! 200: return nentries; ! 201: } ! 202: ! 203: alphacomp(a, b) ! 204: struct direct **a, ! 205: **b; ! 206: { ! 207: return strcmp((*a)->d_name, (*b)->d_name); ! 208: } ! 209: ! 210: char *CurDir; ! 211: ! 212: /* Scan the DIRNAME directory for jove tmp files, and make a linked list ! 213: out of them. */ ! 214: ! 215: get_files(dirname) ! 216: char *dirname; ! 217: { ! 218: int add_name(); ! 219: struct direct **nmptr; ! 220: ! 221: CurDir = dirname; ! 222: scandir(dirname, &nmptr, add_name, (int (*)())0); ! 223: } ! 224: ! 225: add_name(dp) ! 226: struct direct *dp; ! 227: { ! 228: char dfile[128], ! 229: rfile[128]; ! 230: struct file_pair *fp; ! 231: struct rec_head header; ! 232: int fd; ! 233: ! 234: if (strncmp(dp->d_name, REC_BASE, strlen(REC_BASE)) != 0) ! 235: return 0; ! 236: /* If we get here, we found a "recover" tmp file, so now ! 237: we look for the corresponding "data" tmp file. First, ! 238: though, we check to see whether there is anything in ! 239: the "recover" file. If it's 0 length, there's no point ! 240: in saving its name. */ ! 241: (void) sprintf(rfile, "%s/%s", CurDir, dp->d_name); ! 242: (void) sprintf(dfile, "%s/jove%s", CurDir, dp->d_name + strlen(REC_BASE)); ! 243: if ((fd = open(rfile, 0)) != -1) { ! 244: if ((read(fd, (char *) &header, sizeof header) != sizeof header)) { ! 245: close(fd); ! 246: return 0; ! 247: } else ! 248: close(fd); ! 249: } ! 250: if (access(dfile, 0) != 0) { ! 251: fprintf(stderr, "recover: can't find the data file for %s/%s\n", TMP_DIR, dp->d_name); ! 252: fprintf(stderr, "so deleting...\n"); ! 253: (void) unlink(rfile); ! 254: (void) unlink(dfile); ! 255: return 0; ! 256: } ! 257: /* If we get here, we've found both files, so we put them ! 258: in the list. */ ! 259: fp = (struct file_pair *) malloc (sizeof *fp); ! 260: if ((char *) fp == 0) { ! 261: fprintf(stderr, "recover: cannot malloc for file_pair.\n"); ! 262: exit(-1); ! 263: } ! 264: fp->file_data = copystr(dfile); ! 265: fp->file_rec = copystr(rfile); ! 266: fp->file_flags = 0; ! 267: fp->file_next = First; ! 268: First = fp; ! 269: ! 270: return 1; ! 271: } ! 272: ! 273: options() ! 274: { ! 275: printf("Options are:\n"); ! 276: printf(" ? list options.\n"); ! 277: printf(" get get a buffer to a file.\n"); ! 278: printf(" list list known buffers.\n"); ! 279: printf(" print print a buffer to terminal.\n"); ! 280: printf(" quit quit and delete jove tmp files.\n"); ! 281: printf(" restore restore all buffers.\n"); ! 282: } ! 283: ! 284: /* Returns a legitimate buffer # */ ! 285: ! 286: struct rec_entry ** ! 287: getsrc() ! 288: { ! 289: char name[128]; ! 290: int number; ! 291: ! 292: for (;;) { ! 293: tellme("Which buffer ('?' for list)? ", name); ! 294: if (name[0] == '?') ! 295: list(); ! 296: else if (name[0] == '\0') ! 297: return 0; ! 298: else if ((number = atoi(name)) > 0 && number <= Header.Nbuffers) ! 299: return &buflist[number]; ! 300: else { ! 301: int i; ! 302: ! 303: for (i = 1; i <= Header.Nbuffers; i++) ! 304: if (strcmp(buflist[i]->r_bname, name) == 0) ! 305: return &buflist[i]; ! 306: printf("%s: unknown buffer.\n", name); ! 307: } ! 308: } ! 309: } ! 310: ! 311: /* Get a destination file name. */ ! 312: ! 313: static char * ! 314: getdest() ! 315: { ! 316: static char filebuf[256]; ! 317: ! 318: tellme("Output file: ", filebuf); ! 319: if (filebuf[0] == '\0') ! 320: return 0; ! 321: return filebuf; ! 322: } ! 323: ! 324: #include "ctype.h" ! 325: ! 326: char * ! 327: readword(buf) ! 328: char *buf; ! 329: { ! 330: int c; ! 331: char *bp = buf; ! 332: ! 333: while (index(" \t\n", c = getchar())) ! 334: ; ! 335: ! 336: do { ! 337: if (index(" \t\n", c)) ! 338: break; ! 339: *bp++ = c; ! 340: } while ((c = getchar()) != EOF); ! 341: *bp = 0; ! 342: ! 343: return buf; ! 344: } ! 345: ! 346: tellme(quest, answer) ! 347: char *quest, ! 348: *answer; ! 349: { ! 350: if (stdin->_cnt <= 0) { ! 351: printf("%s", quest); ! 352: fflush(stdout); ! 353: } ! 354: readword(answer); ! 355: } ! 356: ! 357: /* Print the specified file to strandard output. */ ! 358: ! 359: jmp_buf int_env; ! 360: ! 361: catch() ! 362: { ! 363: longjmp(int_env, 1); ! 364: } ! 365: ! 366: restore() ! 367: { ! 368: register int i; ! 369: char tofile[100], ! 370: answer[30]; ! 371: int nrecovered = 0; ! 372: ! 373: for (i = 1; i <= Header.Nbuffers; i++) { ! 374: (void) sprintf(tofile, "#%s", buflist[i]->r_bname); ! 375: tryagain: ! 376: printf("Restoring %s to %s, okay?", buflist[i]->r_bname, ! 377: tofile); ! 378: tellme(" ", answer); ! 379: switch (answer[0]) { ! 380: case 'y': ! 381: break; ! 382: ! 383: case 'n': ! 384: continue; ! 385: ! 386: default: ! 387: tellme("What file should I use instead? ", tofile); ! 388: goto tryagain; ! 389: } ! 390: get(&buflist[i], tofile); ! 391: nrecovered++; ! 392: } ! 393: printf("Recovered %d buffers.\n", nrecovered); ! 394: } ! 395: ! 396: get(src, dest) ! 397: struct rec_entry **src; ! 398: char *dest; ! 399: { ! 400: FILE *outfile; ! 401: ! 402: if (src == 0 || dest == 0) ! 403: return; ! 404: (void) signal(SIGINT, catch); ! 405: if (setjmp(int_env) == 0) { ! 406: if ((outfile = fopen(dest, "w")) == NULL) { ! 407: printf("recover: cannot create %s.\n", dest); ! 408: return; ! 409: } ! 410: seekto(src - buflist); ! 411: if (dest != tty) ! 412: printf("\"%s\"", dest); ! 413: dump_file(outfile); ! 414: } else ! 415: printf("\nAborted!\n"); ! 416: fclose(outfile); ! 417: if (dest != tty) ! 418: printf(" %ld lines, %ld characters.\n", Nlines, Nchars); ! 419: (void) signal(SIGINT, SIG_DFL); ! 420: } ! 421: ! 422: char ** ! 423: scanvec(args, str) ! 424: register char **args, ! 425: *str; ! 426: { ! 427: while (*args) { ! 428: if (strcmp(*args, str) == 0) ! 429: return args; ! 430: args++; ! 431: } ! 432: return 0; ! 433: } ! 434: ! 435: read_rec(recptr) ! 436: struct rec_entry *recptr; ! 437: { ! 438: if (fread((char *) recptr, sizeof *recptr, 1, ptrs_fp) != 1) ! 439: fprintf(stderr, "recover: cannot read record.\n"); ! 440: } ! 441: ! 442: seekto(which) ! 443: { ! 444: struct rec_entry rec; ! 445: ! 446: fseek(ptrs_fp, (long) (sizeof Header), L_SET); ! 447: ! 448: while (which-- > 1) { ! 449: read_rec(&rec); ! 450: if (fseek(ptrs_fp, (long) rec.r_nlines * sizeof (disk_line), ! 451: L_INCR) == -1) ! 452: printf("recover: improper fseek!\n"); ! 453: } ! 454: } ! 455: ! 456: makblist() ! 457: { ! 458: int i; ! 459: ! 460: for (i = 1; i <= Header.Nbuffers; i++) { ! 461: seekto(i); ! 462: if (buflist[i] == 0) ! 463: buflist[i] = (struct rec_entry *) malloc (sizeof (struct rec_entry)); ! 464: read_rec(buflist[i]); ! 465: } ! 466: if (buflist[i]) { ! 467: free((char *) buflist[i]); ! 468: buflist[i] = 0; ! 469: } ! 470: } ! 471: ! 472: disk_line ! 473: getaddr(fp) ! 474: register FILE *fp; ! 475: { ! 476: register int nchars = sizeof (disk_line); ! 477: disk_line addr; ! 478: register char *cp = (char *) &addr; ! 479: ! 480: while (--nchars >= 0) ! 481: *cp++ = getc(fp); ! 482: ! 483: return addr; ! 484: } ! 485: ! 486: dump_file(out) ! 487: FILE *out; ! 488: { ! 489: struct rec_entry record; ! 490: register int nlines; ! 491: register disk_line daddr; ! 492: char buf[BUFSIZ]; ! 493: ! 494: read_rec(&record); ! 495: nlines = record.r_nlines; ! 496: Nchars = Nlines = 0L; ! 497: while (--nlines >= 0) { ! 498: daddr = getaddr(ptrs_fp); ! 499: getline(daddr, buf); ! 500: Nlines++; ! 501: Nchars += 1 + strlen(buf); ! 502: fputs(buf, out); ! 503: if (nlines > 0) ! 504: fputc('\n', out); ! 505: } ! 506: if (out != stdout) ! 507: fclose(out); ! 508: } ! 509: ! 510: /* List all the buffers. */ ! 511: ! 512: list() ! 513: { ! 514: int i; ! 515: ! 516: for (i = 1; i <= Header.Nbuffers; i++) ! 517: printf("%d) buffer %s \"%s\" (%d lines)\n", i, ! 518: buflist[i]->r_bname, ! 519: buflist[i]->r_fname, ! 520: buflist[i]->r_nlines); ! 521: } ! 522: ! 523: doit(fp) ! 524: struct file_pair *fp; ! 525: { ! 526: char answer[30]; ! 527: char *datafile = fp->file_data, ! 528: *pntrfile = fp->file_rec; ! 529: ! 530: ptrs_fp = fopen(pntrfile, "r"); ! 531: if (ptrs_fp == NULL) { ! 532: if (Verbose) ! 533: fprintf(stderr, "recover: cannot read rec file (%s).\n", pntrfile); ! 534: return 0; ! 535: } ! 536: fread((char *) &Header, sizeof Header, 1, ptrs_fp); ! 537: if (Header.Uid != UserID) ! 538: return 0; ! 539: ! 540: /* Don't ask about JOVE's that are still running ... */ ! 541: #ifdef KILL0 ! 542: if (kill(Header.Pid, 0) == 0) ! 543: return 0; ! 544: #else ! 545: #ifdef LSRHS ! 546: if (pexist(Header.Pid)) ! 547: return 0; ! 548: #endif LSRHS ! 549: #endif KILL0 ! 550: ! 551: if (Header.Nbuffers == 0) { ! 552: printf("There are no modified buffers in %s; should I delete the tmp file?", pntrfile); ! 553: ask_del(" ", fp); ! 554: return 1; ! 555: } ! 556: ! 557: if (Header.Nbuffers < 0) { ! 558: fprintf(stderr, "recover: %s doesn't look like a jove file.\n", pntrfile); ! 559: ask_del("Should I delete it? ", fp); ! 560: return 1; /* We'll, we sort of found something. */ ! 561: } ! 562: printf("Found %d buffer%s last updated: %s", ! 563: Header.Nbuffers, ! 564: Header.Nbuffers != 1 ? "s" : "", ! 565: ctime(&Header.UpdTime)); ! 566: data_fd = open(datafile, 0); ! 567: if (data_fd == -1) { ! 568: fprintf(stderr, "recover: but I can't read the data file (%s).\n", datafile); ! 569: ask_del("Should I delete the tmp files? ", fp); ! 570: return 1; ! 571: } ! 572: makblist(); ! 573: ! 574: for (;;) { ! 575: tellme("(Type '?' for options): ", answer); ! 576: switch (answer[0]) { ! 577: case '\0': ! 578: continue; ! 579: ! 580: case '?': ! 581: options(); ! 582: break; ! 583: ! 584: case 'l': ! 585: list(); ! 586: break; ! 587: ! 588: case 'p': ! 589: get(getsrc(), tty); ! 590: break; ! 591: ! 592: case 'q': ! 593: ask_del("Shall I delete the tmp files? ", fp); ! 594: return 1; ! 595: ! 596: case 'g': ! 597: { /* So it asks for src first. */ ! 598: char *dest; ! 599: struct rec_entry **src; ! 600: ! 601: if ((src = getsrc()) == 0) ! 602: break; ! 603: dest = getdest(); ! 604: get(src, dest); ! 605: break; ! 606: } ! 607: ! 608: case 'r': ! 609: restore(); ! 610: break; ! 611: ! 612: default: ! 613: printf("I don't know how to \"%s\"!\n", answer); ! 614: break; ! 615: } ! 616: } ! 617: } ! 618: ! 619: ask_del(prompt, fp) ! 620: char *prompt; ! 621: struct file_pair *fp; ! 622: { ! 623: char yorn[20]; ! 624: ! 625: tellme(prompt, yorn); ! 626: if (yorn[0] == 'y') ! 627: del_files(fp); ! 628: } ! 629: ! 630: del_files(fp) ! 631: struct file_pair *fp; ! 632: { ! 633: (void) unlink(fp->file_data); ! 634: (void) unlink(fp->file_rec); ! 635: } ! 636: ! 637: savetmps() ! 638: { ! 639: struct file_pair *fp; ! 640: int status, ! 641: pid; ! 642: ! 643: if (strcmp(TMP_DIR, REC_DIR) == 0) ! 644: return; /* Files are moved to the same place. */ ! 645: get_files(TMP_DIR); ! 646: for (fp = First; fp != 0; fp = fp->file_next) { ! 647: switch (pid = fork()) { ! 648: case -1: ! 649: fprintf(stderr, "recover: can't fork\n!"); ! 650: exit(-1); ! 651: ! 652: case 0: ! 653: execl("/bin/cp", "cp", fp->file_data, fp->file_rec, REC_DIR, 0); ! 654: fprintf(stderr, "recover: cannot execl /bin/cp.\n"); ! 655: exit(-1); ! 656: ! 657: default: ! 658: while (wait(&status) != pid) ! 659: ; ! 660: if (status != 0) ! 661: fprintf(stderr, "recover: non-zero status (%d) returned from copy.\n", status); ! 662: } ! 663: } ! 664: } ! 665: ! 666: lookup(dir) ! 667: char *dir; ! 668: { ! 669: struct file_pair *fp; ! 670: struct rec_head header; ! 671: char yorn[20]; ! 672: int nfound = 0, ! 673: this_one; ! 674: ! 675: get_files(dir); ! 676: for (fp = First; fp != 0; fp = fp->file_next) { ! 677: nfound += doit(fp); ! 678: if (ptrs_fp) ! 679: (void) fclose(ptrs_fp); ! 680: if (data_fd > 0) ! 681: (void) close(data_fd); ! 682: } ! 683: return nfound; ! 684: } ! 685: ! 686: main(argc, argv) ! 687: int argc; ! 688: char *argv[]; ! 689: { ! 690: int nfound; ! 691: char **argvp; ! 692: ! 693: UserID = getuid(); ! 694: ! 695: if (scanvec(argv, "-help")) { ! 696: printf("recover: usage: recover [-d directory] [-syscrash]\n"); ! 697: printf("Use \"recover\" after JOVE has died for some\n"); ! 698: printf("unknown reason.\n\n"); ! 699: printf("Use \"recover -syscrash\" when the system is in the process\n"); ! 700: printf("of rebooting. This is done automatically at reboot time\n"); ! 701: printf("and so most of you don't have to worry about that.\n\n"); ! 702: printf("Use \"recover -d directory\" when the tmp files are store\n"); ! 703: printf("in DIRECTORY instead of the default one (%s).\n", TMP_DIR); ! 704: exit(0); ! 705: } ! 706: if (scanvec(argv, "-v")) ! 707: Verbose++; ! 708: if (scanvec(argv, "-syscrash")) { ! 709: printf("Recovering jove files ... "); ! 710: savetmps(); ! 711: printf("Done.\n"); ! 712: exit(0); ! 713: } ! 714: if (argvp = scanvec(argv, "-uid")) ! 715: UserID = atoi(argvp[1]); ! 716: if (argvp = scanvec(argv, "-d")) ! 717: nfound = lookup(argvp[1]); ! 718: else { ! 719: if ((nfound = lookup(REC_DIR)) || ! 720: (nfound = lookup(TMP_DIR))) ! 721: ; ! 722: } ! 723: if (nfound == 0) ! 724: printf("There's nothing to recover.\n"); ! 725: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.