/************************************************************************ * This program is Copyright (C) 1986 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 [-v] [-user name] [-d directory] [-check] #ifdef REC_DIR recover [-v] -preserve [-nomail] The -preserve option is specified in /etc/rc. It directs recover to move all the jove tmp files from TMP_DIR (usually /tmp) to REC_DIR (usually /usr/preserve). recover -preserve 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) but AFTER name daemons like Yellow Pages have started up (since users are notified by mail (unless the -nomail option is given). This may involve moving the cleanup of /tmp around in /etc/rc. The old name of this option, -syscrash, is still supported for compatibility's sake, but this support may go away in the future. #endif The -d option lets you specify the directory to search for tmp files when the default isn't the right one. The -check option checks if you have anything to recover and displays a message if so. This is useful in your shell startup file. Look in Makefile to change the default directories. */ /* TODO: merge -preserve mail messages to the same user. (should be rather straightforward now file-pair list is sorted on UID. */ #include /* Do stdio first so it doesn't override OUR definitions. */ #undef EOF #undef NULL #undef PATH_MAX #define NOT_JOVE /* we're not part of JOVE itself. */ #include "jove.h" RCS("$Id: recover.c,v 14.31.0.9 1994/01/31 14:31:08 tom Exp tom $") #include "io.h" #include "rec.h" #include "temp.h" #include "version.h" #undef signal #include #ifndef PASSWD # if unix || vms # define PASSWD # endif #endif #ifdef PASSWD # include # if !__POSIX__ extern struct passwd *getpwuid __(( int /*uid_t*/ _(uid) )), *getpwnam __(( const char *_(username) )); # endif #endif #define READDIR_IMPLEMENTATION #include "readdir.h" #ifndef TMP_DIR # define TMP_DIR TmpFilePath #endif #ifdef REC_DIR # if !unix /* -preserve option currently only supported on UNIX systems */ # undef REC_DIR # endif #endif char *ctime __(( const time_t *_(timep) )); char blk_buf[BLKSIZ]; int nleft; FILE *ptrs_fp; int data_fd; private struct rec_head Header; long Nchars, Nlines; int UserID, Verbose = 0, l_tempfile; /* length of constant prefix of [dp]_tempfile */ struct file_pair { char *file_data, *file_rec; #define INSPECTED 01 int file_flags; time_t file_updtime; int file_uid; struct file_pair *file_next; } *First = 0; struct rec_entry **buflist = 0; void_* emalloc(size) size_t size; { register void_* p = malloc(size); if (p == NULL) { printf("recover: out of memory\n"); exit(-1); } return p; } #ifdef ATARIST # define AltSlash 0 /* don't want Alternate path sep. here */ #ifndef __GNUC__ /* Avoid clash with gnulib */ private void (*org_pterm)__(( void )) = (void (*)()) -1; private sig_tp (*sigint_proc)__(( int _(sig) )) = SIG_DFL; #define PTERM_EXC 0x102 private void do_sigint __(( void )); private void do_sigint() { int dummy; Super(&dummy); /* back to user mode */ (*sigint_proc)(SIGINT); signal(SIGINT, SIG_DFL); /* re-install system's Pterm handler */ } /* * Emulate signal() * this only handles SIGINT requests; others are ignored. * SIGINT signal MUST be reset to SIG_DFL before exiting. */ sig_tp (*signal(sig, proc))__(( int _(sig) )) int sig; sig_tp (*proc)__(( int _(sig) )); { register sig_tp (*prev)__(( int _(sig) )); switch (sig) { case SIGINT: prev = sigint_proc; sigint_proc = proc; proc = Setexc(PTERM_EXC, (proc == SIG_IGN || proc == SIG_DFL) ? org_pterm : do_sigint); if (org_pterm == (void (*)()) -1) org_pterm = (void (*)__(( void ))) proc; break; default: prev = SIG_DFL; break; } return prev; } #endif /* __GNUC__ */ #endif /* ATARIST */ private int curblock = -1; private int lastblock; char *getblock __(( disk_line _(atl) )); char * getblock(atl) disk_line atl; { int bno, off; bno = daddr2bno(atl); off = daddr2off(atl); nleft = BLKSIZ - off; if ((unsigned) bno >= lastblock) /* guard against bogus values */ return "[Beyond EOF]"; if (bno != curblock) { lseek(data_fd, (long) bno * BLKSIZ, SEEK_SET); read(data_fd, blk_buf, BLKSIZ); curblock = bno; } return blk_buf + off; } /* Get a line at `tl' in the tmp file into `buf' which should be LBSIZE long. */ #if 0 /* we don't need this since JOVE does not address partial lines anymore */ void getline __(( disk_line _(tl), char *_(buf) )); void getline(tl, buf) disk_line 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++) { if (--nl == 0) { tl = forward_block(tl); bp = getblock(tl >> 1); nl = nleft; lp = buf; } } } #endif char * copystr(s) const char *s; { return strcpy(emalloc(strlen(s) + 1), s); } char * make_filename(buf, path, name) char *buf; const char *path, *name; { register const char *s = path; register char *d = buf; while(*d++ = *s++) ; if (--d > buf && !ISDIRSEP(d[-1])) *d++ = SLASH; s = name; while (*d++ = *s++) ; return buf; } char * basename(f) register const char *f; { register const char *cp; register char c; #ifdef DOS if (f[0] && f[1] == ':') f += 2; #endif for (cp = f; (c = *cp++); ) { #if vms /* handle VMS-style filespec. */ if (c == ']' || c == ':' || c == '>') f = cp; #endif if (ISDIRSEP(c)) f = cp; } return (char *) f; } #if vms /* for vms2ux */ char * appcpy(t, f) register char *t; register const char *f; { while (*t++ = *f++) ; return --t; } #endif #ifdef BACKUPFILES /* * make a backup copy of the file, if it exists. * (do a physical copy so that original keeps the same i-node) * set access/modified times of backup copy to those of original. * use iobuff as intermediate buffer. * For DOS systems, do a rename since there is no I-node to preserve * (and it is faster) */ int file_backup __(( const char *_(fname) )); int file_backup(fname) const char *fname; { char *strcpy(); register int i; register int fd1, fd2; char backup[FILESIZE]; struct stat stbuf; sprintf(basename(strcpy(backup, fname)), "#%s", basename(fname)); #ifdef DOS unlink(backup); if ((i = rename(fname, backup)) < 0 && errno == ENOENT) return 0; printf("Backup to %s...", backup); #else if ((fd1 = open(fname, O_RDONLY)) < 0) return 0; fstat(fd1, &stbuf); /* should not fail */ printf("Backup to %s...", backup); fflush(stdout); if ((fd2 = creat(backup, stbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))) >= 0) { char iobuff[BUFSIZ]; time_t times[2]; while ((i = read(fd1, iobuff, BUFSIZ)) > 0 && write(fd2, iobuff, i) == i); #ifdef BSD4_2 fsync(fd2); #endif close(fd2); /* now cheat -- set times of backup to that of original */ times[0] = stbuf.st_atime; times[1] = stbuf.st_mtime; utime(backup, times); } close(fd1); #endif /* DOS */ if (i != 0) { printf((i < 0) ? " [Can't create]" : " [Incomplete]"); } printf("\n"); return i; } #endif /* BACKUPFILES */ /* cleanup invalid recovery file pairs */ void cleanup __(( const char *_(message), const char *_(rfile), const char *_(dfile) )); void cleanup(message, rfile, dfile) const char *message, *rfile, *dfile; { if (Verbose) fprintf(stderr, "%s \"%s\".\nso deleting...\n", message, rfile); unlink(rfile); unlink(dfile); } /* Scan the DIRNAME directory for jove tmp files, and make a linked list out of them. */ const char *CurDir; int add_name __(( struct dirent *_(dp) )); int add_name(dp) register struct dirent *dp; { char dfile[FILESIZE], rfile[FILESIZE]; register struct file_pair *fp; register int fd; if (strncmp(dp->d_name, p_tempfile, l_tempfile) != 0 && strncmp(dp->d_name, d_tempfile, l_tempfile) != 0) return 0; /* * If we get here, we found a "recover" or "data" tmp file, so now we * look for the corresponding "data" resp. "recover" 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. */ make_filename(rfile, CurDir, strncpy(dp->d_name, p_tempfile, l_tempfile)); make_filename(dfile, CurDir, strncpy(dp->d_name, d_tempfile, l_tempfile)); if ((fd = open(rfile, O_RDONLY)) >= 0) { if ((read(fd, (char *) &Header, (int) sizeof Header) != sizeof Header)) { close(fd); cleanup("Empty recovery file", rfile, dfile); return 0; } close(fd); } else if (access(rfile, F_OK) != 0) { /* [TRH 17-Dec-90, revised 26-Jun-91] * Try to find out if this file belongs to an active JOVE. * We don't have its pid (since that would be in the "recover" * file -- that doesn't exist), so we have to make do * with this heuristic: only delete if we (pretend to) own * the file and it has not been accessed during the past hour. */ struct stat stbuf; if (stat(dfile, &stbuf) == 0 && stbuf.st_uid == UserID && stbuf.st_atime < time(NULL) - 60*60) cleanup("Can't find the recovery file for", dfile, rfile); return 0; } if (access(dfile, R_OK) != 0) { /* * Either we don't have permission to read the "data" file * (and we should leave it alone), or it does not exist * (so we can ditch the accompanying "recover" file.) */ if (access(dfile, F_OK) != 0) cleanup("Can't find the data file for", rfile, dfile); return 0; } /* * If we get here, we've found both files, so we put them in the list, * IF they are not already on it. */ if (fp = First) do { if (strcmp(fp->file_data, dfile) == 0) return 0; } while (fp = fp->file_next); fp = (struct file_pair *) emalloc (sizeof *fp); fp->file_data = copystr(dfile); fp->file_rec = copystr(rfile); fp->file_flags = 0; fp->file_updtime = Header.UpdTime; fp->file_uid = Header.Uid; /* Sort file_pairs by used id. and update time (most recent first). */ { register struct file_pair *next, **at; for (at = &First; next = (*at); at = &next->file_next) { if (fp->file_uid <= next->file_uid) { if (fp->file_uid < next->file_uid || fp->file_updtime >= next->file_updtime) break; } } fp->file_next = next; (*at) = fp; } return 1; } void get_files __(( const char *_(dirname) )); void get_files(dirname) const char *dirname; { DIR *dirp; struct dirent *dentry; CurDir = dirname; if ((dirp = opendir(dirname)) == NULL) return; while ((dentry = readdir(dirp)) != NULL) add_name(dentry); closedir(dirp); } #ifdef REC_DIR void free_files __(( void )); void free_files() { register struct file_pair *fp; while (fp = First) { First = fp->file_next; free(fp->file_data); free(fp->file_rec); free(fp); } } #endif private int lastc = '\n'; char *readword __(( char *_(buf) )); char * readword(buf) char *buf; { int c; char *bp = buf; while (index(" \t", c = getchar())) ; do { if (index(" \t\n", c)) break; *bp++ = c; } while ((c = getchar()) != EOF); *bp = 0; lastc = c; return buf; } void tellme __(( const char *_(quest), char *_(answer) )); void tellme(quest, answer) const char *quest; char *answer; { if (lastc == '\n') { printf("%s", quest); fflush(stdout); } readword(answer); } /* list all the options. */ void options __(( void )); void options() { printf("JOVE recover (%s)\n", VERSION_SYSTEM_DATE_AND_GURU); puts("Options are:\n\ ? list options.\n\ g)et get a buffer to a file.\n\ l)ist list known buffers.\n\ p)rint print a buffer to terminal.\n\ q)uit quit and delete jove tmp files.\n\ r)estore restore all buffers."); } /* List all the buffers. */ void list __(( FILE *_(outfile) )); void list(outfile) register FILE *outfile; { register int i; for (i = 1; i <= Header.Nbuffers; i++) { register struct rec_entry *r = buflist[i]; fprintf(outfile, "%2d) buffer %-14s \"%s\" (%d line%s)\n", i, r->r_bname, r->r_fname[0] ? r->r_fname : "[No file]", r->r_nlines, r->r_nlines == 1 ? "" : "s"); } } /* Returns a legitimate buffer # */ struct rec_entry **getsrc __(( void )); struct rec_entry ** getsrc() { char name[BNAMESIZE]; register int number; for (;;) { tellme("Which buffer ('?' for list)? ", name); if (name[0] == '?') list(stdout); else if (name[0] == '\0') return 0; else if ((number = atoi(name)) > 0 && number <= Header.Nbuffers) return &buflist[number]; else { register int i; register int len = strlen(name); number = 0; for (i = 1; i <= Header.Nbuffers; i++) if (strncmp(buflist[i]->r_bname, name, len) == 0) { if (buflist[i]->r_bname[len] == '\0') return &buflist[i]; if (number == 0) number = i; else { number = -1; break; } } if (number > 0) return &buflist[i]; printf((number < 0) ? "%s: supply more characters.\n" : "%s: unknown buffer.\n", name); } } } /* Get a destination file name. */ private const char *getdest __(( const char *_(deflt) )); private const char * getdest(deflt) const char *deflt; { static char filebuf[FILESIZE]; char prompt[FILESIZE+20]; strcpy(prompt, "Output file: "); if (deflt) if (deflt[0]) sprintf(prompt + strlen(prompt), "(default %s) ", deflt); else deflt = 0; tellme(prompt, filebuf); if (filebuf[0] == '\0') return deflt; return filebuf; } /* Print the specified file to standard output. */ jmp_buf int_env; sig_tp catch_sig __(( int _(sig) )); sig_tp catch_sig(sig) { longjmp(int_env, sig); } char *const *scanvec __(( char *const *_(args), const char *_(str) )); char *const * scanvec(args, str) register char *const *args; register const char *str; { while (*args) { if (strcmp(*args, str) == 0) return args; args++; } return 0; } void read_rec __(( struct rec_entry *_(recptr) )); void read_rec(recptr) struct rec_entry *recptr; { if (fread((char *) recptr, sizeof *recptr, 1, ptrs_fp) != 1) fprintf(stderr, "recover: cannot read record.\n"); } /* [TRH] store offsets to records for more efficient seeks. This assumes that current record file is not actively used by a runnning JOVE (we check that in do_it). */ private long *seeklist = 0; void seekto __(( int _(which) )); void seekto(which) { if (seeklist == NULL) { printf("(seekto) seeklist not set!\n"); return; } if (which > Header.Nbuffers) { printf("(seekto) bad record %d (max %d)!\n", which, Header.Nbuffers); return; } if (fseek(ptrs_fp, seeklist[which], SEEK_SET) != 0) printf("(seekto) improper fseek! (record %d)\n", which); } void makblist __(( void )); void makblist() { register int i; register struct rec_entry *rec; register long where = sizeof Header, rec_size; fseek(ptrs_fp, where, SEEK_SET); /* just to be sure */ if (buflist) { for (i = 1; buflist[i]; i++) free(buflist[i]); free(buflist); buflist = NULL; } buflist = (struct rec_entry **) emalloc((Header.Nbuffers + 2) * sizeof(struct rec_entry *)); if (seeklist) { free(seeklist); seeklist = NULL; } seeklist = (long *) emalloc((Header.Nbuffers + 2) * sizeof(long)); for (i = 1; i <= Header.Nbuffers; i++) { rec = (struct rec_entry *) emalloc(sizeof(struct rec_entry)); buflist[i] = rec; seeklist[i] = where; read_rec(rec); rec_size = (long) rec->r_nlines * sizeof(disk_line); where += sizeof(struct rec_entry) + rec_size; if (fseek(ptrs_fp, rec_size, SEEK_CUR) != 0) { printf("(makblist) improper fseek! (record %d)\n", i); Header.Nbuffers = i; /* cheat! */ } } buflist[i] = NULL; } disk_line getaddr __(( FILE *_(fp) )); disk_line getaddr(fp) register FILE *fp; { register int nchars = sizeof (disk_line); disk_line addr; register char *cp = (char *) &addr; while (--nchars >= 0) *cp++ = getc(fp); return addr; } void dump_file __(( FILE *_(out) )); void dump_file(out) register FILE *out; { struct rec_entry record; #ifdef NULLCHARS register char c; register char *lp; #endif register char *buf; register int nlines; read_rec(&record); nlines = record.r_nlines; Nchars = Nlines = 0L; if (nlines > 0) do { /* now copies directly from the data cache. */ buf = getblock(getaddr(ptrs_fp) >> 1); Nlines++; #ifndef NULLCHARS fputs(buf, out); Nchars += 1 + strlen(buf); #else lp = buf; while (c = *lp++) { if (c == '\n') /* \n used internally to represent \0 */ c = '\0'; putc(c, out); } Nchars += (lp - buf); /* includes newline */ #endif } while (--nlines && (putc('\n', out), TRUE)); /* add extra newline if last line was a partial one. */ (buf[0]) ? putc('\n', out) : --Nchars; if (out != stdout) fclose(out); } /* get a buffer to file "dest", or to stdout if dest == NULL */ void get __(( struct rec_entry **_(src), const char *_(dest) )); void get(src, dest) struct rec_entry **src; const char *dest; { FILE *outfile; if (src == 0) return; signal(SIGINT, catch_sig); if (setjmp(int_env) == 0) { if (dest == NULL) outfile = stdout; else { #ifdef BACKUPFILES if (file_backup(dest) < 0) return; #endif if ((outfile = fopen(dest, "w")) == NULL) { printf("recover: cannot create %s.\n", dest); return; } } seekto((int)(src - buflist)); if (dest) printf("\"%s\"", dest); dump_file(outfile); } else printf("\nAborted!\n"); if (dest) { fclose(outfile); printf(" %ld lines, %ld characters.\n", Nlines, Nchars); } signal(SIGINT, SIG_DFL); } void restore __(( void )); void restore() { register int i; char tofile[FILESIZE], answer[FILESIZE]; int nrecovered = 0; for (i = 1; i <= Header.Nbuffers; i++) { #ifdef BACKUPFILES strcpy(tofile, buflist[i]->r_fname); #else strcpy(tofile, buflist[i]->r_fname), sprintf(basename(tofile), "#%s", basename(buflist[i]->r_fname)); #endif tryagain: if (tofile[0] == '\0') #ifdef BACKUPFILES strcpy(tofile, buflist[i]->r_bname); #else sprintf(tofile, "#%s", buflist[i]->r_bname); #endif printf("Restoring %s to \"%s\" ", buflist[i]->r_bname, tofile); tellme("('?' for options) ", answer); switch (answer[0]) { case '?': printf("y)es, n)o or new file name.\n"); goto tryagain; case 'y': if (answer[1] == '\0') break; case 'n': if (answer[1] == '\0') continue; default: strcpy(tofile, answer); break; case '\0': tellme("What file should I use instead? ", tofile); goto tryagain; } get(&buflist[i], tofile); nrecovered++; } printf("Recovered %d buffer%s.\n", nrecovered, nrecovered != 1 ? "s": ""); } void header __(( void )); void header() { printf("Found %d buffer%s last updated: %s", Header.Nbuffers, Header.Nbuffers != 1 ? "s" : "", ctime(&Header.UpdTime)); } void del_files __(( struct file_pair *_(fp) )); void del_files(fp) struct file_pair *fp; { unlink(fp->file_data); unlink(fp->file_rec); } void ask_del __(( const char *_(prompt), struct file_pair *_(fp) )); void ask_del(prompt, fp) const char *prompt; struct file_pair *fp; { char yorn[20]; tellme(prompt, yorn); if (yorn[0] == 'y') del_files(fp); } /* [TRH 18-Aug-92] Sanity checks moved to separate routine. */ int checkit __(( struct file_pair *_(fp), int _(ask) )); int checkit(fp, ask) register struct file_pair *fp; int ask; { ptrs_fp = fopen(fp->file_rec, "rb"); if (ptrs_fp == NULL) { if (Verbose || ask) fprintf(stderr, "recover: cannot read rec file (%s).\n", fp->file_rec); return 0; } fread((char *) &Header, sizeof Header, 1, ptrs_fp); if (Header.Uid != UserID) return 0; #if unix /* Don't ask about JOVE's that are still running ... */ # ifdef LSRHS if (pexist(Header.Pid)) return 0; # else # ifdef KILL0 if (kill(Header.Pid, 0) == 0) return 0; # else /* do the next best thing by sending a relatively harmless signal. */ if (kill(Header.Pid, SIGALRM) == 0) return 0; # endif # endif /* LSRHS */ #endif /* unix */ if (Header.Nbuffers == 0) { if (Verbose || ask) fprintf(stderr, "There are no modified buffers in %s; ", fp->file_rec); if (ask) ask_del("should I delete the tmp file? ", fp); return ask; } if (Header.Nbuffers < 0) { if (Verbose || ask) fprintf(stderr, "recover: %s doesn't look like a jove file.\n", fp->file_rec); if (ask) ask_del("Should I delete it? ", fp); return ask; /* Well, we sort of found something. */ } if (Verbose || ask) header(); if ((data_fd = open(fp->file_data, O_RDONLY)) < 0) { if (Verbose || ask) fprintf(stderr, "recover: I can't read the data file (%s).\n", fp->file_data); if (ask) ask_del("Should I delete the tmp files? ", fp); return ask; } curblock = -1; /* new datafile -- so reset block pointer(!!) */ lastblock = lseek(data_fd, 0L, SEEK_END) / BLKSIZ; return 1; } int doit __(( struct file_pair *_(fp) )); int doit(fp) register struct file_pair *fp; { char answer[30]; if (!checkit(fp, YES)) return 0; makblist(); list(stdout); for (;;) { tellme("(Type '?' for options): ", answer); switch (answer[0]) { case '?': options(); break; case '\0': case 'l': header(); list(stdout); break; case 'p': get(getsrc(), NULL); break; case 'q': ask_del("Shall I delete the tmp files? ", fp); return 1; case 'g': { /* So it asks for src first. */ const char *dest; struct rec_entry **src; if ((src = getsrc()) == 0) break; dest = getdest((*src)->r_fname); get(src, dest); break; } case 'r': restore(); break; default: printf("I don't know how to \"%s\"!\n", answer); break; } } } #ifdef REC_DIR private char *Host __(( void )); private char * Host() { # ifdef SYSV # include static struct utsname mach ZERO; if (mach.sysname[0] == '\0') if (uname(&mach) < 0) strcpy(mach.sysname, "local"); return mach.sysname; # else /* !SYSV */ # ifdef BSD4_2 extern int gethostname __(( char *_(buf), size_t _(size) )); static char mach[256] ZERO; if (mach[0] == '\0') if (gethostname(mach, sizeof mach) < 0) strcpy(mach, "local"); return mach; # else /* !SYSV && !BSD */ return "local"; # endif # endif } private void MailUser __(( void )); private void MailUser() { char mail_cmd[LBSIZE]; /* Let's be grammatically correct! */ char *buf_string = (Header.Nbuffers == 1) ? "buffer" : "buffers"; FILE *mail_pipe; struct passwd *pw; extern FILE *popen __(( const char *_(command), const char *_(mode) )); if ((pw = getpwuid(Header.Uid))== NULL) { if (Verbose) fprintf(stderr, "jove_recover: couldn't find uid %d.\n", Header.Uid); return; } /* Start up mail */ setuid(getuid()); sprintf(mail_cmd, "/usr/bin/mail %s", pw->pw_name); if ((mail_pipe = popen(mail_cmd + 4, "w")) == NULL && (mail_pipe = popen(mail_cmd, "w")) == NULL) { if (Verbose) fprintf(stderr, "jove_recover: couldn't mail %s.\n", pw->pw_name); return; } #ifdef BSD4_2 /* Do other systems need this too? */ fprintf(mail_pipe, "To: %s\n", pw->pw_name); #endif fprintf(mail_pipe, "\ Subject: JOVE preserve\n\ \n\ Jove preserved %d %s when the system \"%s\"\n\ shut down or crashed on %s\n", Header.Nbuffers, buf_string, Host(), ctime(&Header.UpdTime)); fprintf(mail_pipe, "\ You can retrieve the %s using Jove's -r\n\ (recover) option, i.e. give the command:\n\ \tjove -r\n\ See the Jove manual for more details\n\n", buf_string); list(mail_pipe); pclose(mail_pipe); } void savetmps __(( int _(notify_user_by_mail) )); void savetmps(notify_user_by_mail) int notify_user_by_mail; { struct file_pair *fp; if (strcmp(TMP_DIR, REC_DIR) == 0) return; /* Files are moved to the same place. */ get_files(TMP_DIR); if (fp = First) do { int status; struct stat st; char dest_data[FILESIZE], dest_rec[FILESIZE]; int tmp_offset; if (stat(fp->file_data, &st) < 0) continue; if ((ptrs_fp = fopen(fp->file_rec, "rb")) == NULL) continue; if (fread(&Header, sizeof Header, 1, ptrs_fp) <= 0) { fclose(ptrs_fp); continue; } makblist(); fclose(ptrs_fp); if (Verbose) fprintf(stderr, "Recovering: %s, %s\n", fp->file_data, fp->file_rec); /* make sure that file-pair does not exist in destination. */ make_filename(dest_data, REC_DIR, basename(fp->file_data)); make_filename(dest_rec, REC_DIR, basename(fp->file_rec)); tmp_offset = basename(dest_data) - dest_data + l_tempfile; while (access(dest_data, F_OK) == 0 || access(dest_rec, F_OK) == 0) { if (dest_rec[tmp_offset] == '9') dest_rec[tmp_offset] = 'a' - 1; else if (dest_rec[tmp_offset] == 'z') ++tmp_offset; dest_data[tmp_offset] = ++dest_rec[tmp_offset]; } sprintf(blk_buf, "PATH=/bin:/usr/bin;mv %s %s;mv %s %s", fp->file_data, dest_data, fp->file_rec, dest_rec); if ((status = system(blk_buf)) != 0) fprintf(stderr, "recover: non-zero status (%d) returned from mv.\n", status); if (chown(dest_data, (int) st.st_uid, (int) st.st_gid) != 0 || chown(dest_rec, (int) st.st_uid, (int) st.st_gid) != 0) { perror("recover: chown failed."); continue; } if (status == 0 && notify_user_by_mail) MailUser(); } while (fp = fp->file_next); } #endif /* REC_DIR */ int lookup __(( const char *_(dir) )); int lookup(dir) const char *dir; { register struct file_pair *fp; int nfound = 0; printf("\rChecking %s ...\n", dir); get_files(dir); if (fp = First) do { nfound += doit(fp); if (ptrs_fp) { fclose(ptrs_fp); ptrs_fp = NULL; } if (data_fd > 0) { close(data_fd); data_fd = 0; } } while (fp = fp->file_next); #ifdef REC_DIR free_files(); #endif return nfound; } int check __(( const char *_(dir) )); int check(dir) const char *dir; { register struct file_pair *fp; int nfound = 0; get_files(dir); if (fp = First) do { nfound += checkit(fp, NO); if (ptrs_fp) { fclose(ptrs_fp); ptrs_fp = NULL; } if (data_fd > 0) { close(data_fd); data_fd = 0; } } while (fp = fp->file_next); #ifdef REC_DIR free_files(); #endif return nfound; } void main(argc, argv) int argc; char *argv[]; { register int nfound; register char *const *argvp; register const char *tmp_dir; argv[0] = basename(argv[0]); l_tempfile = index(p_tempfile, 'X') - p_tempfile; UserID = getuid(); if (scanvec(argv, "-help")) { printf("\ usage: %s [-v]%s [-d directory] [-check]\n\n\ Use \"%s\" after JOVE has died for some\n\ unknown reason.\n", argv[0], (UserID != 0) ? "" : #ifdef REC_DIR " [-preserve [-nomail]] [-user name]", #else " [-user name]", #endif argv[0]); if (UserID == 0) { /* so only root sees this. */ #ifdef REC_DIR printf("\n\ Use \"%s -preserve\" when the system is in the process\n\ of rebooting (i.e., in /etc/rc.) This moves leftover tmp files\n\ to the directory \"%s\" so that they are not", argv[0], REC_DIR); printf("\n\ wiped out by the reboot. It also notifies the owners of preserved files\n\ by mail, unless \"-nomail\" is given.\n", REC_DIR); #endif printf("\n\ Use \"-user \" to recover the files of a particular user.\n"); } printf("\n\ Use \"%s -d directory\" when the tmp files are stored\n\ in DIRECTORY instead of the default one", argv[0]); #ifdef REC_DIR if (strcmp(TMP_DIR, REC_DIR) != 0) printf("s (%s, %s)\n", TMP_DIR, REC_DIR); else #endif printf(" (%s)\n", TMP_DIR); printf("\n\ Use \"%s -check\" to check for the existence of\n\ preserved JOVE buffers (e.g., in your shell login file.)\n", argv[0]); exit(EXIT_SUCCESS); } if (scanvec(argv, "-v")) Verbose++; #ifdef REC_DIR if ((argvp = scanvec(argv, "-preserve")) || (argvp = scanvec(argv, "-syscrash"))) { if (UserID != 0) { fprintf(stderr, "%s: must be root for \"%s\".\n", argv[0], argvp[0]); exit(EXIT_FAILURE); } if (Verbose) printf("Recovering jove files ... "); savetmps(scanvec(argv, "-nomail") == NULL); if (Verbose) printf("Done.\n"); exit(EXIT_SUCCESS); } #endif if (argvp = scanvec(argv, "-uid")) UserID = atoi(argvp[1]); if (argvp = scanvec(argv, "-user")) { #ifdef PASSWD struct passwd *pw = getpwnam(argvp[1]); if (pw) UserID = pw->pw_uid; else #endif fprintf(stderr, "%s: unknown user (%s).\n", argv[0], argvp[1]), exit(EXIT_FAILURE); } if (argvp = scanvec(argv, "-d")) tmp_dir = argvp[1]; else tmp_dir = TMP_DIR; if (scanvec(argv, "-check")) { nfound = check(tmp_dir); #ifdef REC_DIR /* Check whether anything was preserved when system died? */ if (strcmp(tmp_dir, REC_DIR) != 0) nfound += check(REC_DIR); #endif if (nfound != 0) printf("\ You have preserved JOVE buffers. (use \"jove -r\" to recover)\n"); exit(nfound); } nfound = lookup(tmp_dir); #ifdef REC_DIR /* Check whether anything was preserved when system died? */ if (strcmp(tmp_dir, REC_DIR) != 0) nfound += lookup(REC_DIR); #endif if (nfound == 0) printf("There's nothing to recover.\n"); exit(nfound); } /*====================================================================== * $Log: recover.c,v $ * Revision 14.31.0.9 1994/01/31 14:31:08 tom * (ctime): ANSI-fy prototype. * * Revision 14.31.0.7 1993/10/29 02:43:04 tom * !BACKUPFILES: some fixes in filename generation. * * Revision 14.31.0.6 1993/10/28 00:16:10 tom * (Atari-ST): port to gcc + gnulib. * * Revision 14.31.0.4 1993/08/02 17:22:53 tom * add `-nomail' option, to be used with `-preserve'; doc fix. * * Revision 14.31.0.1 1993/07/07 13:52:50 tom * fix const botches to avoid warnings. * * Revision 14.31 1993/02/15 02:01:50 tom * remove (void) casts. * * Revision 14.30 1993/02/05 00:07:32 tom * cleanup whitespace; some random optimizations; some more Posix-ations. * * Revision 14.28 1992/10/24 01:24:22 tom * convert to "port{ansi,defs}.h" conventions; let mail message make more * sense. * * Revision 14.27 1992/09/21 23:52:32 tom * rename -syscrash to -preserve (still support -syscrash); some rephrasing * of messages. * * Revision 14.26 1992/08/26 23:57:06 tom * add -check option; avoid overwrites when preserving files in -syscrash; * sort temp files by user ID and update time (most recent first); * add RCS directives. * */