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
main(argc,argv)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
emalloc(size)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
erealloc(ptr,size)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
null_ncpy(to,from,n)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
get_line(tl,buf)163 get_line(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 *
getblock(atl)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 *
copystr(s)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
get_files(dirname)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
add_name(fname)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
options()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 **
getsrc()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 *
getdest()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 *
readword(buf,buflen)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
tellme(quest,answer,anslen)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
catch(junk)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
restore()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
get(src,dest)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 **
scanvec(args,str)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
read_rec(recptr)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
seekto(which)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
makblist()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
getaddr(fp)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
dump_file(which,out)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 		get_line(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
list()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
doit(fp)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
ask_del(prompt,fp)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
del_files(fp)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 *
hname()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
MailUser(rec)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
savetmps()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
lookup(dir)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
main(argc,argv)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