|
|
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 it
11: move all the jove tmp files from TMP_DIR (/tmp) to REC_DIR (/usr/preserve).
12: recover -syscrash must be invoked in /ect/rc BEFORE /tmp gets cleared out.
13: (about the same place as expreserve gets invoked to save ed/vi/ex files.
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: #include "jove.h"
23: #include "temp.h"
24: #include "rec.h"
25: #include "rectune.h"
26: #include <signal.h>
27: #include <sys/file.h>
28: #include <sys/stat.h>
29: #include <sys/dir.h>
30: #include <pwd.h>
31: #include <time.h>
32: #ifdef SYSV
33: # include <sys/utsname.h>
34: #endif
35:
36: #ifndef L_SET
37: # define L_SET 0
38: # define L_INCR 1
39: #endif
40:
41: extern char *ctime proto((const time_t *));
42:
43: private char blk_buf[JBUFSIZ];
44: private int nleft;
45: private FILE *ptrs_fp;
46: private int data_fd;
47: private struct rec_head Header;
48: private long Nchars,
49: Nlines;
50: private char tty[] = "/dev/tty";
51: private int UserID,
52: Verbose = 0;
53: private char *Directory = 0; /* the directory we're looking in */
54:
55: private struct file_pair {
56: char *file_data,
57: *file_rec;
58: #define INSPECTED 01
59: int file_flags;
60: struct file_pair *file_next;
61: } *First = 0;
62:
63: private struct rec_entry *buflist[100]; /* system initializes to 0 */
64:
65: #ifndef BSD_DIR
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: private char *getblock proto((daddr atl));
117:
118: void
119: getline(tl, buf)
120: daddr tl;
121: char *buf;
122: {
123: register char *bp,
124: *lp;
125: register int nl;
126:
127: lp = buf;
128: bp = getblock(tl >> 1);
129: nl = nleft;
130: tl = blk_round(tl);
131:
132: while ((*lp++ = *bp++) != '\0') {
133: if (--nl == 0) {
134: tl = forward_block(tl);
135: bp = getblock(tl >> 1);
136: nl = nleft;
137: }
138: }
139: }
140:
141: private char *
142: getblock(atl)
143: daddr atl;
144: {
145: int bno,
146: off;
147: static int curblock = -1;
148:
149: bno = da_to_bno(atl);
150: off = da_to_off(atl);
151: nleft = JBUFSIZ - off;
152:
153: if (bno != curblock) {
154: extern long lseek proto((int, long, int));
155:
156: lseek(data_fd, (long) bno * JBUFSIZ, L_SET);
157: read(data_fd, blk_buf, (size_t)JBUFSIZ);
158: curblock = bno;
159: }
160: return blk_buf + off;
161: }
162:
163: char *
164: copystr(s)
165: char *s;
166: {
167: char *str;
168:
169: str = malloc((size_t) (strlen(s) + 1));
170: strcpy(str, s);
171:
172: return str;
173: }
174:
175: /* Scandir returns the number of entries or -1 if the directory cannoot
176: be opened or malloc fails. */
177:
178: private int
179: scandir(dir, nmptr, qualify, sorter)
180: char *dir;
181: struct direct ***nmptr;
182: int (*qualify) proto((struct direct *));
183: int (*sorter) proto((UnivConstPtr, UnivConstPtr));
184: {
185: DIR *dirp;
186: struct direct *entry,
187: **ourarray;
188: int nalloc = 10,
189: nentries = 0;
190:
191: if ((dirp = opendir(dir)) == NULL)
192: return -1;
193: ourarray = (struct direct **) malloc(nalloc * sizeof (struct direct *));
194: while ((entry = readdir(dirp)) != NULL) {
195: if (qualify != NULL && (*qualify)(entry) == 0)
196: continue;
197: if (nentries == nalloc) {
198: ourarray = (struct direct **) realloc((char *)ourarray,
199: (nalloc += 10) * sizeof (struct direct));
200: if (ourarray == NULL)
201: return -1;
202: }
203: ourarray[nentries] = (struct direct *) malloc(sizeof *entry);
204: *ourarray[nentries] = *entry;
205: nentries += 1;
206: }
207: closedir(dirp);
208: if (nentries != nalloc)
209: ourarray = (struct direct **) realloc((char *)ourarray,
210: (nentries * sizeof (struct direct)));
211: if (sorter != NULL)
212: qsort((UnivPtr)ourarray, (size_t) nentries, sizeof (struct direct **), sorter);
213: *nmptr = ourarray;
214:
215: return nentries;
216: }
217:
218: private char *CurDir;
219:
220: /* Scan the DIRNAME directory for jove tmp files, and make a linked list
221: out of them. */
222:
223: private int add_name proto((struct direct *dp));
224:
225: private void
226: get_files(dirname)
227: char *dirname;
228: {
229: struct direct **nmptr;
230:
231: CurDir = dirname;
232: First = NULL;
233: scandir(dirname, &nmptr, add_name,
234: (int (*) proto((UnivConstPtr, UnivConstPtr)))NULL);
235: }
236:
237: private int
238: add_name(dp)
239: struct direct *dp;
240: {
241: char dfile[128],
242: rfile[128];
243: struct file_pair *fp;
244: struct rec_head header;
245: int fd;
246:
247: if (strncmp(dp->d_name, "jrec", (size_t)4) != 0)
248: return 0;
249: /* If we get here, we found a "recover" tmp file, so now
250: we look for the corresponding "data" tmp file. First,
251: though, we check to see whether there is anything in
252: the "recover" file. If it's 0 length, there's no point
253: in saving its name. */
254: (void) sprintf(rfile, "%s/%s", CurDir, dp->d_name);
255: (void) sprintf(dfile, "%s/jove%s", CurDir, dp->d_name + 4);
256: if ((fd = open(rfile, 0)) != -1) {
257: if ((read(fd, (char *) &header, sizeof header) != sizeof header)) {
258: close(fd);
259: return 0;
260: } else
261: close(fd);
262: }
263: if (access(dfile, 0) != 0) {
264: fprintf(stderr, "recover: can't find the data file for %s/%s\n", Directory, dp->d_name);
265: fprintf(stderr, "so deleting...\n");
266: (void) unlink(rfile);
267: (void) unlink(dfile);
268: return 0;
269: }
270: /* If we get here, we've found both files, so we put them
271: in the list. */
272: fp = (struct file_pair *) malloc (sizeof *fp);
273: if ((char *) fp == 0) {
274: fprintf(stderr, "recover: cannot malloc for file_pair.\n");
275: exit(-1);
276: }
277: fp->file_data = copystr(dfile);
278: fp->file_rec = copystr(rfile);
279: fp->file_flags = 0;
280: fp->file_next = First;
281: First = fp;
282:
283: return 1;
284: }
285:
286: private void
287: options()
288: {
289: printf("Options are:\n");
290: printf(" ? list options.\n");
291: printf(" get get a buffer to a file.\n");
292: printf(" list list known buffers.\n");
293: printf(" print print a buffer to terminal.\n");
294: printf(" quit quit and delete jove tmp files.\n");
295: printf(" restore restore all buffers.\n");
296: }
297:
298: /* Returns a legitimate buffer # */
299:
300: private void tellme proto((char *, char *)),
301: list proto((void));
302:
303: private struct rec_entry **
304: getsrc()
305: {
306: char name[128];
307: int number;
308:
309: for (;;) {
310: tellme("Which buffer ('?' for list)? ", name);
311: if (name[0] == '?')
312: list();
313: else if (name[0] == '\0')
314: return 0;
315: else if ((number = atoi(name)) > 0 && number <= Header.Nbuffers)
316: return &buflist[number];
317: else {
318: int i;
319:
320: for (i = 1; i <= Header.Nbuffers; i++)
321: if (strcmp(buflist[i]->r_bname, name) == 0)
322: return &buflist[i];
323: printf("%s: unknown buffer.\n", name);
324: }
325: }
326: }
327:
328: /* Get a destination file name. */
329:
330: static char *
331: getdest()
332: {
333: static char filebuf[256];
334:
335: tellme("Output file: ", filebuf);
336: if (filebuf[0] == '\0')
337: return 0;
338: return filebuf;
339: }
340:
341: #include "ctype.h"
342:
343: private char *
344: readword(buf)
345: char *buf;
346: {
347: int c;
348: char *bp = buf;
349:
350: while (strchr(" \t\n", c = getchar()))
351: ;
352:
353: do {
354: if (strchr(" \t\n", c))
355: break;
356: *bp++ = c;
357: } while ((c = getchar()) != EOF);
358: *bp = 0;
359:
360: return buf;
361: }
362:
363: private void
364: tellme(quest, answer)
365: char *quest,
366: *answer;
367: {
368: if (stdin->_cnt <= 0) {
369: printf("%s", quest);
370: fflush(stdout);
371: }
372: readword(answer);
373: }
374:
375: /* Print the specified file to standard output. */
376:
377: private jmp_buf int_env;
378:
379: private SIGRESULT
380: catch(junk)
381: int junk;
382: {
383: longjmp(int_env, 1);
384: /*NOTREACHED*/
385: }
386:
387: private void get proto((struct rec_entry **src, char *dest));
388:
389: private void
390: restore()
391: {
392: register int i;
393: char tofile[100],
394: answer[30];
395: int nrecovered = 0;
396:
397: for (i = 1; i <= Header.Nbuffers; i++) {
398: (void) sprintf(tofile, "#%s", buflist[i]->r_bname);
399: tryagain:
400: printf("Restoring %s to %s, okay?", buflist[i]->r_bname,
401: tofile);
402: tellme(" ", answer);
403: switch (answer[0]) {
404: case 'y':
405: break;
406:
407: case 'n':
408: continue;
409:
410: default:
411: tellme("What file should I use instead? ", tofile);
412: goto tryagain;
413: }
414: get(&buflist[i], tofile);
415: nrecovered += 1;
416: }
417: printf("Recovered %d buffers.\n", nrecovered);
418: }
419:
420: private void dump_file proto((int which, FILE *out));
421:
422: private void
423: get(src, dest)
424: struct rec_entry **src;
425: char *dest;
426: {
427: FILE *outfile;
428:
429: if (src == 0 || dest == 0)
430: return;
431: (void) signal(SIGINT, catch);
432: if (setjmp(int_env) == 0) {
433: if (dest == tty)
434: outfile = stdout;
435: else {
436: if ((outfile = fopen(dest, "w")) == NULL) {
437: printf("recover: cannot create %s.\n", dest);
438: (void) signal(SIGINT, SIG_DFL);
439: return;
440: }
441: printf("\"%s\"", dest);
442: }
443: dump_file(src - buflist, outfile);
444: } else
445: printf("\nAborted!\n");
446: (void) signal(SIGINT, SIG_DFL);
447: if (dest != tty) {
448: fclose(outfile);
449: printf(" %ld lines, %ld characters.\n", Nlines, Nchars);
450: }
451: }
452:
453: private char **
454: scanvec(args, str)
455: register char **args,
456: *str;
457: {
458: while (*args) {
459: if (strcmp(*args, str) == 0)
460: return args;
461: args += 1;
462: }
463: return 0;
464: }
465:
466: private void
467: read_rec(recptr)
468: struct rec_entry *recptr;
469: {
470: if (fread((char *) recptr, sizeof *recptr, (size_t)1, ptrs_fp) != 1)
471: fprintf(stderr, "recover: cannot read record.\n");
472: }
473:
474: private void
475: seekto(which)
476: int which;
477: {
478: long offset;
479: int i;
480:
481: offset = sizeof (Header) + (Header.Nbuffers * sizeof (struct rec_entry));
482: for (i = 1; i < which; i++)
483: offset += buflist[i]->r_nlines * sizeof (daddr);
484: fseek(ptrs_fp, offset, L_SET);
485: }
486:
487: private void
488: makblist()
489: {
490: int i;
491:
492: fseek(ptrs_fp, (long) sizeof (Header), L_SET);
493: for (i = 1; i <= Header.Nbuffers; i++) {
494: if (buflist[i] == 0)
495: buflist[i] = (struct rec_entry *) malloc (sizeof (struct rec_entry));
496: read_rec(buflist[i]);
497: }
498: while (buflist[i]) {
499: free((char *) buflist[i]);
500: buflist[i] = 0;
501: i += 1;
502: }
503: }
504:
505: private daddr
506: getaddr(fp)
507: register FILE *fp;
508: {
509: register int nchars = sizeof (daddr);
510: daddr addr;
511: register char *cp = (char *) &addr;
512:
513: while (--nchars >= 0)
514: *cp++ = getc(fp);
515:
516: return addr;
517: }
518:
519: private void
520: dump_file(which, out)
521: int which;
522: FILE *out;
523: {
524: register int nlines;
525: register daddr addr;
526: char buf[JBUFSIZ];
527:
528: seekto(which);
529: nlines = buflist[which]->r_nlines;
530: Nchars = Nlines = 0L;
531: while (--nlines >= 0) {
532: addr = getaddr(ptrs_fp);
533: getline(addr, buf);
534: Nlines += 1;
535: Nchars += 1 + strlen(buf);
536: fputs(buf, out);
537: if (nlines > 0)
538: fputc('\n', out);
539: }
540: }
541:
542: /* List all the buffers. */
543:
544: private void
545: list()
546: {
547: int i;
548:
549: for (i = 1; i <= Header.Nbuffers; i++)
550: printf("%d) buffer %s \"%s\" (%d lines)\n", i,
551: buflist[i]->r_bname,
552: buflist[i]->r_fname,
553: buflist[i]->r_nlines);
554: }
555:
556: private void ask_del proto((char *prompt, struct file_pair *fp));
557:
558: private int
559: doit(fp)
560: struct file_pair *fp;
561: {
562: char answer[30];
563: char *datafile = fp->file_data,
564: *pntrfile = fp->file_rec;
565:
566: ptrs_fp = fopen(pntrfile, "r");
567: if (ptrs_fp == NULL) {
568: if (Verbose)
569: fprintf(stderr, "recover: cannot read rec file (%s).\n", pntrfile);
570: return 0;
571: }
572: fread((char *) &Header, sizeof Header, (size_t)1, ptrs_fp);
573: if (Header.Uid != UserID)
574: return 0;
575:
576: /* Don't ask about JOVE's that are still running ... */
577: #ifdef KILL0
578: if (kill(Header.Pid, 0) == 0)
579: return 0;
580: #endif /* KILL0 */
581:
582: if (Header.Nbuffers == 0) {
583: printf("There are no modified buffers in %s; should I delete the tmp file?", pntrfile);
584: ask_del(" ", fp);
585: return 1;
586: }
587:
588: if (Header.Nbuffers < 0) {
589: fprintf(stderr, "recover: %s doesn't look like a jove file.\n", pntrfile);
590: ask_del("Should I delete it? ", fp);
591: return 1; /* We'll, we sort of found something. */
592: }
593: printf("Found %d buffer%s last updated: %s",
594: Header.Nbuffers,
595: Header.Nbuffers != 1 ? "s" : "",
596: ctime(&Header.UpdTime));
597: data_fd = open(datafile, 0);
598: if (data_fd == -1) {
599: fprintf(stderr, "recover: but I can't read the data file (%s).\n", datafile);
600: ask_del("Should I delete the tmp files? ", fp);
601: return 1;
602: }
603: makblist();
604: list();
605:
606: for (;;) {
607: tellme("(Type '?' for options): ", answer);
608: switch (answer[0]) {
609: case '\0':
610: continue;
611:
612: case '?':
613: options();
614: break;
615:
616: case 'l':
617: list();
618: break;
619:
620: case 'p':
621: get(getsrc(), tty);
622: break;
623:
624: case 'q':
625: ask_del("Shall I delete the tmp files? ", fp);
626: return 1;
627:
628: case 'g':
629: { /* So it asks for src first. */
630: char *dest;
631: struct rec_entry **src;
632:
633: if ((src = getsrc()) == 0)
634: break;
635: dest = getdest();
636: get(src, dest);
637: break;
638: }
639:
640: case 'r':
641: restore();
642: break;
643:
644: default:
645: printf("I don't know how to \"%s\"!\n", answer);
646: break;
647: }
648: }
649: }
650:
651: private void del_files proto((struct file_pair *fp));
652:
653: private void
654: ask_del(prompt, fp)
655: char *prompt;
656: struct file_pair *fp;
657: {
658: char yorn[20];
659:
660: tellme(prompt, yorn);
661: if (yorn[0] == 'y')
662: del_files(fp);
663: }
664:
665: private void
666: del_files(fp)
667: struct file_pair *fp;
668: {
669: (void) unlink(fp->file_data);
670: (void) unlink(fp->file_rec);
671: }
672:
673:
674:
675: MailUser(rec)
676: struct rec_head *rec;
677: {
678: #ifdef SYSV
679: struct utsname mach;
680: #else
681: char mach[BUFSIZ];
682: #endif
683: char mail_cmd[BUFSIZ];
684: char *last_update;
685: char *buf_string;
686: FILE *mail_pipe;
687: struct passwd *pw;
688: extern struct passwd *getpwuid proto((int));
689:
690: if ((pw = getpwuid(rec->Uid))== NULL)
691: return;
692: #ifdef SYSV
693: if (uname(&mach) < 0)
694: strcpy(mach.sysname, "unknown");
695: #else
696: gethostname(mach, sizeof(mach));
697: #endif
698: last_update = ctime(&(rec->UpdTime));
699: /* Start up mail */
700: sprintf(mail_cmd, "/bin/mail %s", pw->pw_name);
701: setuid(getuid());
702: if ((mail_pipe = popen(mail_cmd, "w")) == NULL)
703: return;
704: setbuf(mail_pipe, mail_cmd);
705: /* Let's be grammatically correct! */
706: if (rec->Nbuffers == 1)
707: buf_string = "buffer";
708: else
709: buf_string = "buffers";
710: fprintf(mail_pipe, "Subject: System crash\n");
711: fprintf(mail_pipe, " \n");
712: fprintf(mail_pipe, "Jove saved %d %s when the system \"%s\"\n",
713: rec->Nbuffers, buf_string,
714: #ifdef SYSV
715: mach.sysname
716: #else
717: mach
718: #endif
719: );
720: fprintf(mail_pipe, "crashed on %s\n\n", last_update);
721: fprintf(mail_pipe, "You can retrieve the %s using Jove's -r\n",
722: buf_string);
723: fprintf(mail_pipe, "(recover option) i.e. give the command.\n");
724: fprintf(mail_pipe, "\tjove -r\n");
725: fprintf(mail_pipe, "See the Jove manual for more details\n");
726: pclose(mail_pipe);
727: }
728:
729:
730: savetmps()
731: {
732: struct file_pair *fp;
733: int status,
734: pid,
735: fd;
736: struct rec_head header;
737: char buf[BUFSIZ];
738: char *fname;
739: struct stat stbuf;
740:
741: if (strcmp(TMP_DIR, REC_DIR) == 0)
742: return; /* Files are moved to the same place. */
743: get_files(TMP_DIR);
744: for (fp = First; fp != 0; fp = fp->file_next) {
745: stat(fp->file_data, &stbuf);
746: switch (pid = fork()) {
747: case -1:
748: fprintf(stderr, "recover: can't fork\n!");
749: exit(-1);
750:
751: case 0:
752: fprintf(stderr, "Recovering: %s, %s\n", fp->file_data,
753: fp->file_rec);
754: if ((fd = open(fp->file_rec, 0)) != -1) {
755: if ((read(fd, (char *) &header, sizeof header) != sizeof header)) {
756: close(fd);
757: return 0;
758: } else
759: close(fd);
760: }
761: MailUser(&header);
762: execl("/bin/mv", "mv", fp->file_data, fp->file_rec,
763: REC_DIR, (char *)0);
764: fprintf(stderr, "recover: cannot execl /bin/mv.\n");
765: exit(-1);
766:
767: default:
768: while (wait(&status) != pid)
769: ;
770: if (status != 0)
771: fprintf(stderr, "recover: non-zero status (%d) returned from copy.\n", status);
772: fname = fp->file_data + strlen(TMP_DIR);
773: strcpy(buf, REC_DIR);
774: strcat(buf, fname);
775: if(chown(buf, (int) stbuf.st_uid, (int) stbuf.st_gid) != 0)
776: perror("recover: chown failed.");
777: fname = fp->file_rec + strlen(TMP_DIR);
778: strcpy(buf, REC_DIR);
779: strcat(buf, fname);
780: if(chown(buf, (int) stbuf.st_uid, (int) stbuf.st_gid) != 0)
781: perror("recover: chown failed.");
782: }
783: }
784: }
785:
786: private int
787: lookup(dir)
788: char *dir;
789: {
790: struct file_pair *fp;
791: int nfound = 0;
792:
793: printf("Checking %s ...\n", dir);
794: Directory = dir;
795: get_files(dir);
796: for (fp = First; fp != 0; fp = fp->file_next) {
797: nfound += doit(fp);
798: if (ptrs_fp)
799: (void) fclose(ptrs_fp);
800: if (data_fd > 0)
801: (void) close(data_fd);
802: }
803: return nfound;
804: }
805:
806: void
807: main(argc, argv)
808: int argc;
809: char *argv[];
810: {
811: int nfound;
812: char **argvp;
813: char *tmp_dir;
814:
815: UserID = getuid();
816:
817: if (scanvec(argv, "-help")) {
818: printf("recover: usage: recover [-d directory] [-syscrash]\n");
819: printf("Use \"jove -r\" after JOVE has died for some\n");
820: printf("unknown reason.\n\n");
821: printf("Use \"%s -syscrash\"\n", Recover);
822: printf("when the system is in the process of rebooting.");
823: printf("This is done automatically at reboot time\n");
824: printf("and so most of you don't have to worry about that.\n\n");
825: printf("Use \"recover -d directory\" when the tmp files are store\n");
826: printf("in DIRECTORY instead of the default one (/tmp).\n");
827: exit(0);
828: }
829: if (scanvec(argv, "-v"))
830: Verbose = YES;
831: if (scanvec(argv, "-syscrash")) {
832: printf("Recovering jove files ... ");
833: savetmps();
834: printf("Done.\n");
835: exit(0);
836: }
837: if ((argvp = scanvec(argv, "-uid")) != NULL)
838: UserID = atoi(argvp[1]);
839: if ((argvp = scanvec(argv, "-d")) != NULL)
840: tmp_dir = argvp[1];
841: else
842: tmp_dir = TmpFilePath;
843: /* Check default directory */
844: nfound = lookup(tmp_dir);
845: /* Check whether anything was saved when system died? */
846: if (strcmp(tmp_dir, REC_DIR) != 0)
847: nfound += lookup(REC_DIR);
848: if (nfound == 0)
849: printf("There's nothing to recover.\n");
850: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.