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