home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / jove-4.16-src.tgz / tar.out / bsd / jove / recover.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  19KB  |  892 lines

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