147702Sbostic /*-
2*63051Sbostic  * Copyright (c) 1991, 1993
3*63051Sbostic  *	The Regents of the University of California.  All rights reserved.
447702Sbostic  *
547702Sbostic  * %sccs.include.redist.c%
621704Sdist  */
721704Sdist 
821704Sdist #ifndef lint
9*63051Sbostic static char copyright[] =
10*63051Sbostic "@(#) Copyright (c) 1991, 1993\n\
11*63051Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1247702Sbostic #endif /* not lint */
1321704Sdist 
1421704Sdist #ifndef lint
15*63051Sbostic static char sccsid[] = "@(#)ex3.7preserve.c	8.1 (Berkeley) 06/09/93";
1647702Sbostic #endif /* not lint */
1721704Sdist 
1812956Sralph #include <sys/param.h>
19460Smark #include <sys/stat.h>
2047702Sbostic #include <dirent.h>
2147702Sbostic #include <fcntl.h>
2247702Sbostic #include <time.h>
23460Smark #include <pwd.h>
2447702Sbostic #include <errno.h>
2547702Sbostic #include <unistd.h>
2637883Sbostic #include <stdio.h>
2737883Sbostic #include <ctype.h>
2847702Sbostic #include <stdlib.h>
2947702Sbostic #include <string.h>
3037883Sbostic #include "pathnames.h"
3113759Ssam 
32460Smark /*
3337883Sbostic  * Expreserve - preserve a file in _PATH_PRESERVE.
34460Smark  * Bill Joy UCB November 13, 1977
35460Smark  *
36460Smark  * This routine is very naive - it doesn't remove anything from
3747702Sbostic  * _PATH_PRESERVE... this may mean that we leave stuff there...
3847702Sbostic  * the danger in doing anything with _PATH_PRESERVE is that the
3947702Sbostic  * clock may be screwed up and we may get confused.
40460Smark  *
4147702Sbostic  * We are called in two ways - first from the editor with no arguments
4247702Sbostic  * and the standard input open on the temp file.  Second with an
4347702Sbostic  * argument to preserve the entire contents of _PATH_VARTMP (root only).
44460Smark  *
45460Smark  * BUG: should do something about preserving Rx... (register contents)
46460Smark  *      temporaries.
47460Smark  */
48460Smark 
4947702Sbostic #ifdef VMUNIX
5047702Sbostic #define	HBLKS	2
5147702Sbostic #define	LBLKS	900
5247702Sbostic #else
5347702Sbostic #define HBLKS	1
54460Smark #define	LBLKS	125
55483Smark #endif
56460Smark #define	FNSIZE	128
57460Smark 
58460Smark struct 	header {
59460Smark 	time_t	Time;			/* Time temp file last updated */
604051Smark 	int	Uid;			/* This users identity */
6147702Sbostic #ifdef VMUNIX
6247702Sbostic 	int	Flines;
6347702Sbostic #else
64460Smark 	short	Flines;			/* Number of lines in file */
65483Smark #endif
66460Smark 	char	Savedfile[FNSIZE];	/* The current file name */
67460Smark 	short	Blocks[LBLKS];		/* Blocks where line pointers stashed */
68460Smark } H;
69460Smark 
main(argc,argv)7047702Sbostic main(argc, argv)
71460Smark 	int argc;
7247702Sbostic 	char **argv;
73460Smark {
7447702Sbostic 	extern int optind;
7512956Sralph 	register DIR *tf;
7647702Sbostic 	struct dirent *dirent;
77460Smark 	struct stat stbuf;
7847702Sbostic 	int aflag, ch, err;
7945858Skarels 	char path[MAXPATHLEN];
80460Smark 
8147702Sbostic 	aflag = 0;
8247702Sbostic 	while ((ch = getopt(argc, argv, "a")) != EOF)
8347702Sbostic 		switch(ch) {
8447702Sbostic 		case 'a':
8547702Sbostic 			aflag = 1;
8647702Sbostic 			break;
8747702Sbostic 		case '?':
8847702Sbostic 		default:
8947702Sbostic 			usage();
9047702Sbostic 		}
9147702Sbostic 	argc -= optind;
9247702Sbostic 	argv += optind;
9345858Skarels 
9447702Sbostic 	if (chdir(_PATH_PRESERVE) < 0)
9547702Sbostic 		error(_PATH_PRESERVE);
9647702Sbostic 
97460Smark 	/*
98460Smark 	 * If only one argument, then preserve the standard input.
99460Smark 	 */
10047702Sbostic 	if (!aflag)
10147702Sbostic 		exit(copyout(NULL) ? 1 : 0);
102460Smark 
103460Smark 	/*
104460Smark 	 * If not super user, then can only preserve standard input.
105460Smark 	 */
106460Smark 	if (getuid()) {
10747702Sbostic 		errno = EPERM;
10847702Sbostic 		error(NULL);
109460Smark 	}
110460Smark 
111460Smark 	/*
11245858Skarels 	 * ... else preserve all the stuff in /var/tmp, removing
113460Smark 	 * it as we go.
114460Smark 	 */
11547702Sbostic 	if (!(tf = opendir(_PATH_VARTMP)))
11647702Sbostic 		error(_PATH_VARTMP);
117460Smark 
11847702Sbostic 	err = 0;
11912956Sralph 	while ((dirent = readdir(tf)) != NULL) {
12013759Ssam 		/* Ex temporaries must begin with Ex. */
12113759Ssam 		if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x')
122460Smark 			continue;
12347702Sbostic 		(void)sprintf(path, "%s/%s", _PATH_VARTMP, dirent->d_name);
12447702Sbostic 		if (stat(path, &stbuf) || !S_ISREG(stbuf.st_mode))
125460Smark 			continue;
126460Smark 		/*
127460Smark 		 * Save the bastard.
128460Smark 		 */
12945858Skarels 		err |= copyout(path);
130460Smark 	}
13147702Sbostic 	(void)closedir(tf);
13245858Skarels 	exit(err);
133460Smark }
134460Smark 
copyout(name)135460Smark copyout(name)
136460Smark 	char *name;
137460Smark {
13847702Sbostic 	struct stat sb;
13947702Sbostic 	register int ifd, ofd, nr, nw, off, rval;
14047702Sbostic 	char buf[8*1024], fname[20];
141460Smark 
14247702Sbostic 	/* Open any given file name. */
14347702Sbostic 	if (name) {
14447702Sbostic 		if ((ifd = open(name, O_RDWR)) < 0)
14547702Sbostic 			return(1);
14647702Sbostic 		(void)fstat(ifd, &sb);
14747702Sbostic 		if (!sb.st_size) {
14847702Sbostic 			(void)unlink(name);
14947702Sbostic 			return(0);
15047702Sbostic 		}
15147702Sbostic 	} else {
15247702Sbostic 		ifd = STDIN_FILENO;
15347702Sbostic 		/* vi hands us an fd, it's not necessarily at the beginning. */
15447702Sbostic 		(void)lseek(ifd, 0l, SEEK_SET);
155460Smark 	}
156460Smark 
15747702Sbostic 	if (read(ifd, &H, sizeof(H)) != sizeof(H))
15847702Sbostic 		goto format;
159460Smark 
160460Smark 	/*
16145858Skarels 	 * Consistency checks so we don't copy out garbage.
162460Smark 	 */
163460Smark 	if (H.Flines < 0) {
16447702Sbostic 		(void)fprintf(stderr,
16547702Sbostic 		    "ex3.7preserve: negative number of lines\n");
166460Smark 		goto format;
167460Smark 	}
16847702Sbostic 
169483Smark 	if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
17047702Sbostic 		(void)fprintf(stderr,
17147702Sbostic 		    "ex3.7preserve: blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
172460Smark 		goto format;
173460Smark 	}
17447702Sbostic 
17547702Sbostic 	if (!name && H.Uid != getuid()) {
17647702Sbostic 		(void)fprintf(stderr, "ex3.7preserve: wrong user-id\n");
177460Smark 		goto format;
178460Smark 	}
17947702Sbostic 
18047702Sbostic 	if (lseek(ifd, 0l, SEEK_SET)) {
18147702Sbostic 		(void)fprintf(stderr,
18247702Sbostic 		    "ex3.7preserve: negative number of lines\n");
18347702Sbostic format:		(void)fprintf(stderr, "ex3.7preserve: %s\n", strerror(EFTYPE));
18447702Sbostic 		return (1);
185460Smark 	}
186460Smark 
187460Smark 	/*
188460Smark 	 * If no name was assigned to the file, then give it the name
18947702Sbostic 	 * LOST, by putting this in the header.  This involves overwriting
19047702Sbostic 	 * the "input" file.
191460Smark 	 */
192460Smark 	if (H.Savedfile[0] == 0) {
19347702Sbostic 		(void)strcpy(H.Savedfile, "LOST");
19447702Sbostic 		(void)write(ifd, &H, sizeof(H));
195460Smark 		H.Savedfile[0] = 0;
19647702Sbostic 		(void)lseek(ifd, 0l, SEEK_SET);
197460Smark 	}
198460Smark 
19947702Sbostic 	/* File is good.  Get a name and create a file for the copy. */
20047702Sbostic 	(void)strcpy(fname, "ExXXXXXX");
20147702Sbostic 	if ((ofd = mkstemp(fname)) == -1)
20247702Sbostic 		return(1);
203460Smark 
20447702Sbostic 	/* Copy the file. */
20547702Sbostic 	rval = 0;
20647702Sbostic 	while ((nr = read(ifd, buf, sizeof(buf))) > 0)
20747702Sbostic 		for (off = 0; off < nr; nr -= nw, off += nw)
20847702Sbostic 			if ((nw = write(ofd, buf + off, nr)) < 0) {
20947702Sbostic 				(void)fprintf(stderr,
21047702Sbostic 				    "ex3.7preserve: tmp file: %s\n",
21147702Sbostic 				    strerror(errno));
21247702Sbostic 				rval = 1;
21347702Sbostic 				break;
21447702Sbostic 			}
21547702Sbostic 	if (nr < 0) {
21647702Sbostic 		(void)fprintf(stderr, "ex3.7preserve: %s: %s\n",
21747702Sbostic 		    name ? name : "stdin", strerror(errno));
21847702Sbostic 		rval = 1;
219460Smark 	}
22047702Sbostic 
22147702Sbostic 	if (rval)
22247702Sbostic 		(void)unlink(fname);
22347702Sbostic 	else {
22447702Sbostic 		(void)fchown(ofd, H.Uid, 0);
22547702Sbostic 		notify(H.Uid, H.Savedfile, (int)name, H.Time);
22647702Sbostic 		if (name)
22747702Sbostic 			(void)unlink(name);
22847702Sbostic 	}
22947702Sbostic 	(void)close(ifd);
23047702Sbostic 	(void)close(ofd);
23147702Sbostic 	return(rval);
232460Smark }
233460Smark 
23447702Sbostic /* Notify user uid that his file fname has been saved. */
notify(uid,fname,flag,time)23516045Srrh notify(uid, fname, flag, time)
236460Smark 	int uid;
237460Smark 	char *fname;
23816045Srrh 	time_t	time;
239460Smark {
24047702Sbostic 	struct passwd *pp;
241460Smark 	register FILE *mf;
24247702Sbostic 	static int reenter;
24347702Sbostic 	static char hostname[MAXHOSTNAMELEN], *timestamp;
24447702Sbostic 	char cmd[MAXPATHLEN + 50], croak[50];
245460Smark 
24647702Sbostic 	pp = getpwuid(uid);
247460Smark 	if (pp == NULL)
248460Smark 		return;
24947702Sbostic 
25047702Sbostic 	if (!reenter) {
25147702Sbostic 		reenter = 1;
25247702Sbostic 		(void)gethostname(hostname, sizeof(hostname));
25347702Sbostic 		timestamp = ctime(&time);
25447702Sbostic 		timestamp[16] = 0;	/* blast from seconds on */
25547702Sbostic 	}
25647702Sbostic 
25747702Sbostic 	(void)snprintf(cmd, sizeof(cmd),
25847702Sbostic 	    "%s -i -t -F \"The Editor\" -f root", _PATH_SENDMAIL);
259460Smark 	mf = popen(cmd, "w");
260460Smark 	if (mf == NULL)
261460Smark 		return;
26247702Sbostic 	(void)fprintf(mf,
26347702Sbostic 	    "Reply-To: root@%s\nFrom: root@%s (The Editor)\nTo: %s\n",
26447702Sbostic 	    hostname, hostname, pp->pw_name);
26547702Sbostic 
26616045Srrh 	/*
26747702Sbostic 	 * flag says how the editor croaked: "the editor was killed" is
26847702Sbostic 	 * perhaps still not an ideal error message.  Usually, either it
26947702Sbostic 	 * was forcably terminated or the phone was hung up, but we don't
27047702Sbostic 	 * know which.
27116045Srrh 	 */
27247702Sbostic 	(void)snprintf(croak, sizeof(croak),
27347702Sbostic 	    flag ? "the system went down"
27447702Sbostic 		 : "the editor was killed");
27547702Sbostic 	if (!fname  || !fname[0]) {
27625941Sbloom 		fname = "LOST";
27747702Sbostic 		fprintf(mf, "Subject: editor saved \"LOST\"\n\n");
27847702Sbostic 		fprintf(mf, "You were editing a file without a name\n");
27947702Sbostic 		fprintf(mf, "at %s on the machine %s when %s.\n",
28047702Sbostic 		    timestamp, hostname, croak);
281460Smark 		fprintf(mf,
28247702Sbostic 		   "Since the file had no name, it has been named \"LOST\".\n");
28316045Srrh 	} else {
28447702Sbostic 		fprintf(mf, "Subject: editor saved \"%s\"\n\n", fname);
28547702Sbostic 		fprintf(mf, "You were editing the file %s\n", fname);
28647702Sbostic 		fprintf(mf, "at %s on the machine %s\n", timestamp, hostname);
28747702Sbostic 		fprintf(mf, "when %s.\n", croak);
28816045Srrh 	}
28947702Sbostic 	fprintf(mf, "\nYou can retrieve most of your changes to this file\n");
29047702Sbostic 	fprintf(mf, "using the \"recover\" command of the editor.\n");
291460Smark 	fprintf(mf,
29247702Sbostic 	    "An easy way to do this is to give the command \"vi -r %s\".\n",
29347702Sbostic 	    fname);
29447702Sbostic 	fprintf(mf, "This method also works using \"ex\" and \"edit\".\n\n");
295460Smark 	pclose(mf);
296460Smark }
297460Smark 
298460Smark /*
299460Smark  *	people making love
300460Smark  *	never exactly the same
301460Smark  *	just like a snowflake
302460Smark  */
error(msg)30347702Sbostic error(msg)
30447702Sbostic 	char *msg;
305460Smark {
30647702Sbostic 	(void)fprintf(stderr, "ex3.7preserve: ");
30747702Sbostic 	if (msg)
30847702Sbostic 		(void)fprintf(stderr, "%s: ", msg);
30947702Sbostic 	(void)fprintf(stderr, "%s\n", strerror(errno));
31047702Sbostic 	exit(1);
311460Smark }
312460Smark 
usage()31347702Sbostic usage()
314460Smark {
31547702Sbostic 	(void)fprintf(stderr, "usage: ex3.7preserve [-a]\n");
31647702Sbostic 	exit(1);
317460Smark }
318