home *** CD-ROM | disk | FTP | other *** search
- /***************************************************************************
- * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne. JOVE *
- * is provided to you without charge, and with no warranty. You may give *
- * away copies of JOVE, including sources, provided that this notice is *
- * included in all the files. *
- ***************************************************************************/
-
- /* Recovers JOVE files after a system/editor crash.
- Usage: recover [-d directory] [-syscrash]
- The -syscrash option is specified in /etc/rc and what it does it
- move all the jove tmp files from TMP_DIR (/tmp) to REC_DIR (/usr/preserve).
- recover -syscrash must be invoked in /ect/rc BEFORE /tmp gets cleared out.
- (about the same place as expreserve gets invoked to save ed/vi/ex files.
-
- The -d option lets you specify the directory to search for tmp files when
- the default isn't the right one.
-
- Look in Makefile to change the default directories. */
-
- #include <stdio.h> /* Do stdio first so it doesn't override OUR
- definitions. */
- #include "jove.h"
- #include "temp.h"
- #include "rec.h"
- #include "rectune.h"
- #include <signal.h>
- #include <sys/file.h>
- #include <sys/stat.h>
- #include <sys/dir.h>
- #include <pwd.h>
- #include <time.h>
- #ifdef SYSV
- # include <sys/utsname.h>
- #endif
-
- #ifndef L_SET
- # define L_SET 0
- # define L_INCR 1
- #endif
-
- extern char *ctime proto((const time_t *));
-
- private char blk_buf[JBUFSIZ];
- private int nleft;
- private FILE *ptrs_fp;
- private int data_fd;
- private struct rec_head Header;
- private long Nchars,
- Nlines;
- private char tty[] = "/dev/tty";
- private int UserID,
- Verbose = 0;
- private char *Directory = 0; /* the directory we're looking in */
-
- private struct file_pair {
- char *file_data,
- *file_rec;
- #define INSPECTED 01
- int file_flags;
- struct file_pair *file_next;
- } *First = 0;
-
- private struct rec_entry *buflist[100]; /* system initializes to 0 */
-
- #ifndef BSD_DIR
-
- typedef struct {
- int d_fd; /* File descriptor for this directory */
- } DIR;
-
- DIR *
- opendir(dir)
- char *dir;
- {
- DIR *dp = (DIR *) malloc(sizeof *dp);
-
- if ((dp->d_fd = open(dir, 0)) == -1)
- return NULL;
- return dp;
- }
-
- closedir(dp)
- DIR *dp;
- {
- (void) close(dp->d_fd);
- free(dp);
- }
-
- struct direct *
- readdir(dp)
- DIR *dp;
- {
- static struct direct dir;
-
- do
- if (read(dp->d_fd, &dir, sizeof dir) != sizeof dir)
- return NULL;
- #if defined(elxsi) && defined(SYSV)
- /*
- * Elxsi has a BSD4.2 implementation which may or may not use
- * `twisted inodes' ... Anyone able to check?
- */
- while (*(unsigned short *)&dir.d_ino == 0);
- #else
- while (dir.d_ino == 0);
- #endif
-
- return &dir;
- }
-
- #endif /* BSD4_2 */
-
- /* Get a line at `tl' in the tmp file into `buf' which should be LBSIZE
- long. */
-
- private char *getblock proto((daddr atl));
-
- void
- getline(tl, buf)
- daddr tl;
- char *buf;
- {
- register char *bp,
- *lp;
- register int nl;
-
- lp = buf;
- bp = getblock(tl >> 1);
- nl = nleft;
- tl = blk_round(tl);
-
- while ((*lp++ = *bp++) != '\0') {
- if (--nl == 0) {
- tl = forward_block(tl);
- bp = getblock(tl >> 1);
- nl = nleft;
- }
- }
- }
-
- private char *
- getblock(atl)
- daddr atl;
- {
- int bno,
- off;
- static int curblock = -1;
-
- bno = da_to_bno(atl);
- off = da_to_off(atl);
- nleft = JBUFSIZ - off;
-
- if (bno != curblock) {
- extern long lseek proto((int, long, int));
-
- lseek(data_fd, (long) bno * JBUFSIZ, L_SET);
- read(data_fd, blk_buf, (size_t)JBUFSIZ);
- curblock = bno;
- }
- return blk_buf + off;
- }
-
- char *
- copystr(s)
- char *s;
- {
- char *str;
-
- str = malloc((size_t) (strlen(s) + 1));
- strcpy(str, s);
-
- return str;
- }
-
- /* Scandir returns the number of entries or -1 if the directory cannoot
- be opened or malloc fails. */
-
- private int
- scandir(dir, nmptr, qualify, sorter)
- char *dir;
- struct direct ***nmptr;
- int (*qualify) proto((struct direct *));
- int (*sorter) proto((UnivConstPtr, UnivConstPtr));
- {
- DIR *dirp;
- struct direct *entry,
- **ourarray;
- int nalloc = 10,
- nentries = 0;
-
- if ((dirp = opendir(dir)) == NULL)
- return -1;
- ourarray = (struct direct **) malloc(nalloc * sizeof (struct direct *));
- while ((entry = readdir(dirp)) != NULL) {
- if (qualify != NULL && (*qualify)(entry) == 0)
- continue;
- if (nentries == nalloc) {
- ourarray = (struct direct **) realloc((char *)ourarray,
- (nalloc += 10) * sizeof (struct direct));
- if (ourarray == NULL)
- return -1;
- }
- ourarray[nentries] = (struct direct *) malloc(sizeof *entry);
- *ourarray[nentries] = *entry;
- nentries += 1;
- }
- closedir(dirp);
- if (nentries != nalloc)
- ourarray = (struct direct **) realloc((char *)ourarray,
- (nentries * sizeof (struct direct)));
- if (sorter != NULL)
- qsort((UnivPtr)ourarray, (size_t) nentries, sizeof (struct direct **), sorter);
- *nmptr = ourarray;
-
- return nentries;
- }
-
- private char *CurDir;
-
- /* Scan the DIRNAME directory for jove tmp files, and make a linked list
- out of them. */
-
- private int add_name proto((struct direct *dp));
-
- private void
- get_files(dirname)
- char *dirname;
- {
- struct direct **nmptr;
-
- CurDir = dirname;
- First = NULL;
- scandir(dirname, &nmptr, add_name,
- (int (*) proto((UnivConstPtr, UnivConstPtr)))NULL);
- }
-
- private int
- add_name(dp)
- struct direct *dp;
- {
- char dfile[128],
- rfile[128];
- struct file_pair *fp;
- struct rec_head header;
- int fd;
-
- if (strncmp(dp->d_name, "jrec", (size_t)4) != 0)
- return 0;
- /* If we get here, we found a "recover" tmp file, so now
- we look for the corresponding "data" tmp file. First,
- though, we check to see whether there is anything in
- the "recover" file. If it's 0 length, there's no point
- in saving its name. */
- (void) sprintf(rfile, "%s/%s", CurDir, dp->d_name);
- (void) sprintf(dfile, "%s/jove%s", CurDir, dp->d_name + 4);
- if ((fd = open(rfile, 0)) != -1) {
- if ((read(fd, (char *) &header, sizeof header) != sizeof header)) {
- close(fd);
- return 0;
- } else
- close(fd);
- }
- if (access(dfile, 0) != 0) {
- fprintf(stderr, "recover: can't find the data file for %s/%s\n", Directory, dp->d_name);
- fprintf(stderr, "so deleting...\n");
- (void) unlink(rfile);
- (void) unlink(dfile);
- return 0;
- }
- /* If we get here, we've found both files, so we put them
- in the list. */
- fp = (struct file_pair *) malloc (sizeof *fp);
- if ((char *) fp == 0) {
- fprintf(stderr, "recover: cannot malloc for file_pair.\n");
- exit(-1);
- }
- fp->file_data = copystr(dfile);
- fp->file_rec = copystr(rfile);
- fp->file_flags = 0;
- fp->file_next = First;
- First = fp;
-
- return 1;
- }
-
- private void
- options()
- {
- printf("Options are:\n");
- printf(" ? list options.\n");
- printf(" get get a buffer to a file.\n");
- printf(" list list known buffers.\n");
- printf(" print print a buffer to terminal.\n");
- printf(" quit quit and delete jove tmp files.\n");
- printf(" restore restore all buffers.\n");
- }
-
- /* Returns a legitimate buffer # */
-
- private void tellme proto((char *, char *)),
- list proto((void));
-
- private struct rec_entry **
- getsrc()
- {
- char name[128];
- int number;
-
- for (;;) {
- tellme("Which buffer ('?' for list)? ", name);
- if (name[0] == '?')
- list();
- else if (name[0] == '\0')
- return 0;
- else if ((number = atoi(name)) > 0 && number <= Header.Nbuffers)
- return &buflist[number];
- else {
- int i;
-
- for (i = 1; i <= Header.Nbuffers; i++)
- if (strcmp(buflist[i]->r_bname, name) == 0)
- return &buflist[i];
- printf("%s: unknown buffer.\n", name);
- }
- }
- }
-
- /* Get a destination file name. */
-
- static char *
- getdest()
- {
- static char filebuf[256];
-
- tellme("Output file: ", filebuf);
- if (filebuf[0] == '\0')
- return 0;
- return filebuf;
- }
-
- #include "ctype.h"
-
- private char *
- readword(buf)
- char *buf;
- {
- int c;
- char *bp = buf;
-
- while (strchr(" \t\n", c = getchar()))
- ;
-
- do {
- if (strchr(" \t\n", c))
- break;
- *bp++ = c;
- } while ((c = getchar()) != EOF);
- *bp = 0;
-
- return buf;
- }
-
- private void
- tellme(quest, answer)
- char *quest,
- *answer;
- {
- if (stdin->_cnt <= 0) {
- printf("%s", quest);
- fflush(stdout);
- }
- readword(answer);
- }
-
- /* Print the specified file to standard output. */
-
- private jmp_buf int_env;
-
- private SIGRESULT
- catch(junk)
- int junk;
- {
- longjmp(int_env, 1);
- /*NOTREACHED*/
- }
-
- private void get proto((struct rec_entry **src, char *dest));
-
- private void
- restore()
- {
- register int i;
- char tofile[100],
- answer[30];
- int nrecovered = 0;
-
- for (i = 1; i <= Header.Nbuffers; i++) {
- (void) sprintf(tofile, "#%s", buflist[i]->r_bname);
- tryagain:
- printf("Restoring %s to %s, okay?", buflist[i]->r_bname,
- tofile);
- tellme(" ", answer);
- switch (answer[0]) {
- case 'y':
- break;
-
- case 'n':
- continue;
-
- default:
- tellme("What file should I use instead? ", tofile);
- goto tryagain;
- }
- get(&buflist[i], tofile);
- nrecovered += 1;
- }
- printf("Recovered %d buffers.\n", nrecovered);
- }
-
- private void dump_file proto((int which, FILE *out));
-
- private void
- get(src, dest)
- struct rec_entry **src;
- char *dest;
- {
- FILE *outfile;
-
- if (src == 0 || dest == 0)
- return;
- (void) signal(SIGINT, catch);
- if (setjmp(int_env) == 0) {
- if (dest == tty)
- outfile = stdout;
- else {
- if ((outfile = fopen(dest, "w")) == NULL) {
- printf("recover: cannot create %s.\n", dest);
- (void) signal(SIGINT, SIG_DFL);
- return;
- }
- printf("\"%s\"", dest);
- }
- dump_file(src - buflist, outfile);
- } else
- printf("\nAborted!\n");
- (void) signal(SIGINT, SIG_DFL);
- if (dest != tty) {
- fclose(outfile);
- printf(" %ld lines, %ld characters.\n", Nlines, Nchars);
- }
- }
-
- private char **
- scanvec(args, str)
- register char **args,
- *str;
- {
- while (*args) {
- if (strcmp(*args, str) == 0)
- return args;
- args += 1;
- }
- return 0;
- }
-
- private void
- read_rec(recptr)
- struct rec_entry *recptr;
- {
- if (fread((char *) recptr, sizeof *recptr, (size_t)1, ptrs_fp) != 1)
- fprintf(stderr, "recover: cannot read record.\n");
- }
-
- private void
- seekto(which)
- int which;
- {
- long offset;
- int i;
-
- offset = sizeof (Header) + (Header.Nbuffers * sizeof (struct rec_entry));
- for (i = 1; i < which; i++)
- offset += buflist[i]->r_nlines * sizeof (daddr);
- fseek(ptrs_fp, offset, L_SET);
- }
-
- private void
- makblist()
- {
- int i;
-
- fseek(ptrs_fp, (long) sizeof (Header), L_SET);
- for (i = 1; i <= Header.Nbuffers; i++) {
- if (buflist[i] == 0)
- buflist[i] = (struct rec_entry *) malloc (sizeof (struct rec_entry));
- read_rec(buflist[i]);
- }
- while (buflist[i]) {
- free((char *) buflist[i]);
- buflist[i] = 0;
- i += 1;
- }
- }
-
- private daddr
- getaddr(fp)
- register FILE *fp;
- {
- register int nchars = sizeof (daddr);
- daddr addr;
- register char *cp = (char *) &addr;
-
- while (--nchars >= 0)
- *cp++ = getc(fp);
-
- return addr;
- }
-
- private void
- dump_file(which, out)
- int which;
- FILE *out;
- {
- register int nlines;
- register daddr addr;
- char buf[JBUFSIZ];
-
- seekto(which);
- nlines = buflist[which]->r_nlines;
- Nchars = Nlines = 0L;
- while (--nlines >= 0) {
- addr = getaddr(ptrs_fp);
- getline(addr, buf);
- Nlines += 1;
- Nchars += 1 + strlen(buf);
- fputs(buf, out);
- if (nlines > 0)
- fputc('\n', out);
- }
- }
-
- /* List all the buffers. */
-
- private void
- list()
- {
- int i;
-
- for (i = 1; i <= Header.Nbuffers; i++)
- printf("%d) buffer %s \"%s\" (%d lines)\n", i,
- buflist[i]->r_bname,
- buflist[i]->r_fname,
- buflist[i]->r_nlines);
- }
-
- private void ask_del proto((char *prompt, struct file_pair *fp));
-
- private int
- doit(fp)
- struct file_pair *fp;
- {
- char answer[30];
- char *datafile = fp->file_data,
- *pntrfile = fp->file_rec;
-
- ptrs_fp = fopen(pntrfile, "r");
- if (ptrs_fp == NULL) {
- if (Verbose)
- fprintf(stderr, "recover: cannot read rec file (%s).\n", pntrfile);
- return 0;
- }
- fread((char *) &Header, sizeof Header, (size_t)1, ptrs_fp);
- if (Header.Uid != UserID)
- return 0;
-
- /* Don't ask about JOVE's that are still running ... */
- #ifdef KILL0
- if (kill(Header.Pid, 0) == 0)
- return 0;
- #endif /* KILL0 */
-
- if (Header.Nbuffers == 0) {
- printf("There are no modified buffers in %s; should I delete the tmp file?", pntrfile);
- ask_del(" ", fp);
- return 1;
- }
-
- if (Header.Nbuffers < 0) {
- fprintf(stderr, "recover: %s doesn't look like a jove file.\n", pntrfile);
- ask_del("Should I delete it? ", fp);
- return 1; /* We'll, we sort of found something. */
- }
- printf("Found %d buffer%s last updated: %s",
- Header.Nbuffers,
- Header.Nbuffers != 1 ? "s" : "",
- ctime(&Header.UpdTime));
- data_fd = open(datafile, 0);
- if (data_fd == -1) {
- fprintf(stderr, "recover: but I can't read the data file (%s).\n", datafile);
- ask_del("Should I delete the tmp files? ", fp);
- return 1;
- }
- makblist();
- list();
-
- for (;;) {
- tellme("(Type '?' for options): ", answer);
- switch (answer[0]) {
- case '\0':
- continue;
-
- case '?':
- options();
- break;
-
- case 'l':
- list();
- break;
-
- case 'p':
- get(getsrc(), tty);
- break;
-
- case 'q':
- ask_del("Shall I delete the tmp files? ", fp);
- return 1;
-
- case 'g':
- { /* So it asks for src first. */
- char *dest;
- struct rec_entry **src;
-
- if ((src = getsrc()) == 0)
- break;
- dest = getdest();
- get(src, dest);
- break;
- }
-
- case 'r':
- restore();
- break;
-
- default:
- printf("I don't know how to \"%s\"!\n", answer);
- break;
- }
- }
- }
-
- private void del_files proto((struct file_pair *fp));
-
- private void
- ask_del(prompt, fp)
- char *prompt;
- struct file_pair *fp;
- {
- char yorn[20];
-
- tellme(prompt, yorn);
- if (yorn[0] == 'y')
- del_files(fp);
- }
-
- private void
- del_files(fp)
- struct file_pair *fp;
- {
- (void) unlink(fp->file_data);
- (void) unlink(fp->file_rec);
- }
-
-
-
- MailUser(rec)
- struct rec_head *rec;
- {
- #ifdef SYSV
- struct utsname mach;
- #else
- char mach[BUFSIZ];
- #endif
- char mail_cmd[BUFSIZ];
- char *last_update;
- char *buf_string;
- FILE *mail_pipe;
- struct passwd *pw;
- extern struct passwd *getpwuid proto((int));
-
- if ((pw = getpwuid(rec->Uid))== NULL)
- return;
- #ifdef SYSV
- if (uname(&mach) < 0)
- strcpy(mach.sysname, "unknown");
- #else
- gethostname(mach, sizeof(mach));
- #endif
- last_update = ctime(&(rec->UpdTime));
- /* Start up mail */
- sprintf(mail_cmd, "/bin/mail %s", pw->pw_name);
- setuid(getuid());
- if ((mail_pipe = popen(mail_cmd, "w")) == NULL)
- return;
- setbuf(mail_pipe, mail_cmd);
- /* Let's be grammatically correct! */
- if (rec->Nbuffers == 1)
- buf_string = "buffer";
- else
- buf_string = "buffers";
- fprintf(mail_pipe, "Subject: System crash\n");
- fprintf(mail_pipe, " \n");
- fprintf(mail_pipe, "Jove saved %d %s when the system \"%s\"\n",
- rec->Nbuffers, buf_string,
- #ifdef SYSV
- mach.sysname
- #else
- mach
- #endif
- );
- fprintf(mail_pipe, "crashed on %s\n\n", last_update);
- fprintf(mail_pipe, "You can retrieve the %s using Jove's -r\n",
- buf_string);
- fprintf(mail_pipe, "(recover option) i.e. give the command.\n");
- fprintf(mail_pipe, "\tjove -r\n");
- fprintf(mail_pipe, "See the Jove manual for more details\n");
- pclose(mail_pipe);
- }
-
-
- savetmps()
- {
- struct file_pair *fp;
- int status,
- pid,
- fd;
- struct rec_head header;
- char buf[BUFSIZ];
- char *fname;
- struct stat stbuf;
-
- if (strcmp(TMP_DIR, REC_DIR) == 0)
- return; /* Files are moved to the same place. */
- get_files(TMP_DIR);
- for (fp = First; fp != 0; fp = fp->file_next) {
- stat(fp->file_data, &stbuf);
- switch (pid = fork()) {
- case -1:
- fprintf(stderr, "recover: can't fork\n!");
- exit(-1);
-
- case 0:
- fprintf(stderr, "Recovering: %s, %s\n", fp->file_data,
- fp->file_rec);
- if ((fd = open(fp->file_rec, 0)) != -1) {
- if ((read(fd, (char *) &header, sizeof header) != sizeof header)) {
- close(fd);
- return 0;
- } else
- close(fd);
- }
- MailUser(&header);
- execl("/bin/mv", "mv", fp->file_data, fp->file_rec,
- REC_DIR, (char *)0);
- fprintf(stderr, "recover: cannot execl /bin/mv.\n");
- exit(-1);
-
- default:
- while (wait(&status) != pid)
- ;
- if (status != 0)
- fprintf(stderr, "recover: non-zero status (%d) returned from copy.\n", status);
- fname = fp->file_data + strlen(TMP_DIR);
- strcpy(buf, REC_DIR);
- strcat(buf, fname);
- if(chown(buf, (int) stbuf.st_uid, (int) stbuf.st_gid) != 0)
- perror("recover: chown failed.");
- fname = fp->file_rec + strlen(TMP_DIR);
- strcpy(buf, REC_DIR);
- strcat(buf, fname);
- if(chown(buf, (int) stbuf.st_uid, (int) stbuf.st_gid) != 0)
- perror("recover: chown failed.");
- }
- }
- }
-
- private int
- lookup(dir)
- char *dir;
- {
- struct file_pair *fp;
- int nfound = 0;
-
- printf("Checking %s ...\n", dir);
- Directory = dir;
- get_files(dir);
- for (fp = First; fp != 0; fp = fp->file_next) {
- nfound += doit(fp);
- if (ptrs_fp)
- (void) fclose(ptrs_fp);
- if (data_fd > 0)
- (void) close(data_fd);
- }
- return nfound;
- }
-
- void
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int nfound;
- char **argvp;
- char *tmp_dir;
-
- UserID = getuid();
-
- if (scanvec(argv, "-help")) {
- printf("recover: usage: recover [-d directory] [-syscrash]\n");
- printf("Use \"jove -r\" after JOVE has died for some\n");
- printf("unknown reason.\n\n");
- printf("Use \"%s -syscrash\"\n", Recover);
- printf("when the system is in the process of rebooting.");
- printf("This is done automatically at reboot time\n");
- printf("and so most of you don't have to worry about that.\n\n");
- printf("Use \"recover -d directory\" when the tmp files are store\n");
- printf("in DIRECTORY instead of the default one (/tmp).\n");
- exit(0);
- }
- if (scanvec(argv, "-v"))
- Verbose = YES;
- if (scanvec(argv, "-syscrash")) {
- printf("Recovering jove files ... ");
- savetmps();
- printf("Done.\n");
- exit(0);
- }
- if ((argvp = scanvec(argv, "-uid")) != NULL)
- UserID = atoi(argvp[1]);
- if ((argvp = scanvec(argv, "-d")) != NULL)
- tmp_dir = argvp[1];
- else
- tmp_dir = TmpFilePath;
- /* Check default directory */
- nfound = lookup(tmp_dir);
- /* Check whether anything was saved when system died? */
- if (strcmp(tmp_dir, REC_DIR) != 0)
- nfound += lookup(REC_DIR);
- if (nfound == 0)
- printf("There's nothing to recover.\n");
- }
-