1*47702Sbostic /*-
2*47702Sbostic  * Copyright (c) 1991 The Regents of the University of California.
3*47702Sbostic  * All rights reserved.
4*47702Sbostic  *
5*47702Sbostic  * %sccs.include.redist.c%
621704Sdist  */
721704Sdist 
821704Sdist #ifndef lint
9*47702Sbostic char copyright[] =
10*47702Sbostic "@(#) Copyright (c) 1991 The Regents of the University of California.\n\
1121704Sdist  All rights reserved.\n";
12*47702Sbostic #endif /* not lint */
1321704Sdist 
1421704Sdist #ifndef lint
15*47702Sbostic static char sccsid[] = "@(#)ex3.7preserve.c	7.18 (Berkeley) 04/01/91";
16*47702Sbostic #endif /* not lint */
1721704Sdist 
1812956Sralph #include <sys/param.h>
19460Smark #include <sys/stat.h>
20*47702Sbostic #include <dirent.h>
21*47702Sbostic #include <fcntl.h>
22*47702Sbostic #include <time.h>
23460Smark #include <pwd.h>
24*47702Sbostic #include <errno.h>
25*47702Sbostic #include <unistd.h>
2637883Sbostic #include <stdio.h>
2737883Sbostic #include <ctype.h>
28*47702Sbostic #include <stdlib.h>
29*47702Sbostic #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
37*47702Sbostic  * _PATH_PRESERVE... this may mean that we leave stuff there...
38*47702Sbostic  * the danger in doing anything with _PATH_PRESERVE is that the
39*47702Sbostic  * clock may be screwed up and we may get confused.
40460Smark  *
41*47702Sbostic  * We are called in two ways - first from the editor with no arguments
42*47702Sbostic  * and the standard input open on the temp file.  Second with an
43*47702Sbostic  * 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 
49*47702Sbostic #ifdef VMUNIX
50*47702Sbostic #define	HBLKS	2
51*47702Sbostic #define	LBLKS	900
52*47702Sbostic #else
53*47702Sbostic #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 */
61*47702Sbostic #ifdef VMUNIX
62*47702Sbostic 	int	Flines;
63*47702Sbostic #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 
70*47702Sbostic main(argc, argv)
71460Smark 	int argc;
72*47702Sbostic 	char **argv;
73460Smark {
74*47702Sbostic 	extern int optind;
7512956Sralph 	register DIR *tf;
76*47702Sbostic 	struct dirent *dirent;
77460Smark 	struct stat stbuf;
78*47702Sbostic 	int aflag, ch, err;
7945858Skarels 	char path[MAXPATHLEN];
80460Smark 
81*47702Sbostic 	aflag = 0;
82*47702Sbostic 	while ((ch = getopt(argc, argv, "a")) != EOF)
83*47702Sbostic 		switch(ch) {
84*47702Sbostic 		case 'a':
85*47702Sbostic 			aflag = 1;
86*47702Sbostic 			break;
87*47702Sbostic 		case '?':
88*47702Sbostic 		default:
89*47702Sbostic 			usage();
90*47702Sbostic 		}
91*47702Sbostic 	argc -= optind;
92*47702Sbostic 	argv += optind;
9345858Skarels 
94*47702Sbostic 	if (chdir(_PATH_PRESERVE) < 0)
95*47702Sbostic 		error(_PATH_PRESERVE);
96*47702Sbostic 
97460Smark 	/*
98460Smark 	 * If only one argument, then preserve the standard input.
99460Smark 	 */
100*47702Sbostic 	if (!aflag)
101*47702Sbostic 		exit(copyout(NULL) ? 1 : 0);
102460Smark 
103460Smark 	/*
104460Smark 	 * If not super user, then can only preserve standard input.
105460Smark 	 */
106460Smark 	if (getuid()) {
107*47702Sbostic 		errno = EPERM;
108*47702Sbostic 		error(NULL);
109460Smark 	}
110460Smark 
111460Smark 	/*
11245858Skarels 	 * ... else preserve all the stuff in /var/tmp, removing
113460Smark 	 * it as we go.
114460Smark 	 */
115*47702Sbostic 	if (!(tf = opendir(_PATH_VARTMP)))
116*47702Sbostic 		error(_PATH_VARTMP);
117460Smark 
118*47702Sbostic 	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;
123*47702Sbostic 		(void)sprintf(path, "%s/%s", _PATH_VARTMP, dirent->d_name);
124*47702Sbostic 		if (stat(path, &stbuf) || !S_ISREG(stbuf.st_mode))
125460Smark 			continue;
126460Smark 		/*
127460Smark 		 * Save the bastard.
128460Smark 		 */
12945858Skarels 		err |= copyout(path);
130460Smark 	}
131*47702Sbostic 	(void)closedir(tf);
13245858Skarels 	exit(err);
133460Smark }
134460Smark 
135460Smark copyout(name)
136460Smark 	char *name;
137460Smark {
138*47702Sbostic 	struct stat sb;
139*47702Sbostic 	register int ifd, ofd, nr, nw, off, rval;
140*47702Sbostic 	char buf[8*1024], fname[20];
141460Smark 
142*47702Sbostic 	/* Open any given file name. */
143*47702Sbostic 	if (name) {
144*47702Sbostic 		if ((ifd = open(name, O_RDWR)) < 0)
145*47702Sbostic 			return(1);
146*47702Sbostic 		(void)fstat(ifd, &sb);
147*47702Sbostic 		if (!sb.st_size) {
148*47702Sbostic 			(void)unlink(name);
149*47702Sbostic 			return(0);
150*47702Sbostic 		}
151*47702Sbostic 	} else {
152*47702Sbostic 		ifd = STDIN_FILENO;
153*47702Sbostic 		/* vi hands us an fd, it's not necessarily at the beginning. */
154*47702Sbostic 		(void)lseek(ifd, 0l, SEEK_SET);
155460Smark 	}
156460Smark 
157*47702Sbostic 	if (read(ifd, &H, sizeof(H)) != sizeof(H))
158*47702Sbostic 		goto format;
159460Smark 
160460Smark 	/*
16145858Skarels 	 * Consistency checks so we don't copy out garbage.
162460Smark 	 */
163460Smark 	if (H.Flines < 0) {
164*47702Sbostic 		(void)fprintf(stderr,
165*47702Sbostic 		    "ex3.7preserve: negative number of lines\n");
166460Smark 		goto format;
167460Smark 	}
168*47702Sbostic 
169483Smark 	if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
170*47702Sbostic 		(void)fprintf(stderr,
171*47702Sbostic 		    "ex3.7preserve: blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
172460Smark 		goto format;
173460Smark 	}
174*47702Sbostic 
175*47702Sbostic 	if (!name && H.Uid != getuid()) {
176*47702Sbostic 		(void)fprintf(stderr, "ex3.7preserve: wrong user-id\n");
177460Smark 		goto format;
178460Smark 	}
179*47702Sbostic 
180*47702Sbostic 	if (lseek(ifd, 0l, SEEK_SET)) {
181*47702Sbostic 		(void)fprintf(stderr,
182*47702Sbostic 		    "ex3.7preserve: negative number of lines\n");
183*47702Sbostic format:		(void)fprintf(stderr, "ex3.7preserve: %s\n", strerror(EFTYPE));
184*47702Sbostic 		return (1);
185460Smark 	}
186460Smark 
187460Smark 	/*
188460Smark 	 * If no name was assigned to the file, then give it the name
189*47702Sbostic 	 * LOST, by putting this in the header.  This involves overwriting
190*47702Sbostic 	 * the "input" file.
191460Smark 	 */
192460Smark 	if (H.Savedfile[0] == 0) {
193*47702Sbostic 		(void)strcpy(H.Savedfile, "LOST");
194*47702Sbostic 		(void)write(ifd, &H, sizeof(H));
195460Smark 		H.Savedfile[0] = 0;
196*47702Sbostic 		(void)lseek(ifd, 0l, SEEK_SET);
197460Smark 	}
198460Smark 
199*47702Sbostic 	/* File is good.  Get a name and create a file for the copy. */
200*47702Sbostic 	(void)strcpy(fname, "ExXXXXXX");
201*47702Sbostic 	if ((ofd = mkstemp(fname)) == -1)
202*47702Sbostic 		return(1);
203460Smark 
204*47702Sbostic 	/* Copy the file. */
205*47702Sbostic 	rval = 0;
206*47702Sbostic 	while ((nr = read(ifd, buf, sizeof(buf))) > 0)
207*47702Sbostic 		for (off = 0; off < nr; nr -= nw, off += nw)
208*47702Sbostic 			if ((nw = write(ofd, buf + off, nr)) < 0) {
209*47702Sbostic 				(void)fprintf(stderr,
210*47702Sbostic 				    "ex3.7preserve: tmp file: %s\n",
211*47702Sbostic 				    strerror(errno));
212*47702Sbostic 				rval = 1;
213*47702Sbostic 				break;
214*47702Sbostic 			}
215*47702Sbostic 	if (nr < 0) {
216*47702Sbostic 		(void)fprintf(stderr, "ex3.7preserve: %s: %s\n",
217*47702Sbostic 		    name ? name : "stdin", strerror(errno));
218*47702Sbostic 		rval = 1;
219460Smark 	}
220*47702Sbostic 
221*47702Sbostic 	if (rval)
222*47702Sbostic 		(void)unlink(fname);
223*47702Sbostic 	else {
224*47702Sbostic 		(void)fchown(ofd, H.Uid, 0);
225*47702Sbostic 		notify(H.Uid, H.Savedfile, (int)name, H.Time);
226*47702Sbostic 		if (name)
227*47702Sbostic 			(void)unlink(name);
228*47702Sbostic 	}
229*47702Sbostic 	(void)close(ifd);
230*47702Sbostic 	(void)close(ofd);
231*47702Sbostic 	return(rval);
232460Smark }
233460Smark 
234*47702Sbostic /* Notify user uid that his file fname has been saved. */
23516045Srrh notify(uid, fname, flag, time)
236460Smark 	int uid;
237460Smark 	char *fname;
23816045Srrh 	time_t	time;
239460Smark {
240*47702Sbostic 	struct passwd *pp;
241460Smark 	register FILE *mf;
242*47702Sbostic 	static int reenter;
243*47702Sbostic 	static char hostname[MAXHOSTNAMELEN], *timestamp;
244*47702Sbostic 	char cmd[MAXPATHLEN + 50], croak[50];
245460Smark 
246*47702Sbostic 	pp = getpwuid(uid);
247460Smark 	if (pp == NULL)
248460Smark 		return;
249*47702Sbostic 
250*47702Sbostic 	if (!reenter) {
251*47702Sbostic 		reenter = 1;
252*47702Sbostic 		(void)gethostname(hostname, sizeof(hostname));
253*47702Sbostic 		timestamp = ctime(&time);
254*47702Sbostic 		timestamp[16] = 0;	/* blast from seconds on */
255*47702Sbostic 	}
256*47702Sbostic 
257*47702Sbostic 	(void)snprintf(cmd, sizeof(cmd),
258*47702Sbostic 	    "%s -i -t -F \"The Editor\" -f root", _PATH_SENDMAIL);
259460Smark 	mf = popen(cmd, "w");
260460Smark 	if (mf == NULL)
261460Smark 		return;
262*47702Sbostic 	(void)fprintf(mf,
263*47702Sbostic 	    "Reply-To: root@%s\nFrom: root@%s (The Editor)\nTo: %s\n",
264*47702Sbostic 	    hostname, hostname, pp->pw_name);
265*47702Sbostic 
26616045Srrh 	/*
267*47702Sbostic 	 * flag says how the editor croaked: "the editor was killed" is
268*47702Sbostic 	 * perhaps still not an ideal error message.  Usually, either it
269*47702Sbostic 	 * was forcably terminated or the phone was hung up, but we don't
270*47702Sbostic 	 * know which.
27116045Srrh 	 */
272*47702Sbostic 	(void)snprintf(croak, sizeof(croak),
273*47702Sbostic 	    flag ? "the system went down"
274*47702Sbostic 		 : "the editor was killed");
275*47702Sbostic 	if (!fname  || !fname[0]) {
27625941Sbloom 		fname = "LOST";
277*47702Sbostic 		fprintf(mf, "Subject: editor saved \"LOST\"\n\n");
278*47702Sbostic 		fprintf(mf, "You were editing a file without a name\n");
279*47702Sbostic 		fprintf(mf, "at %s on the machine %s when %s.\n",
280*47702Sbostic 		    timestamp, hostname, croak);
281460Smark 		fprintf(mf,
282*47702Sbostic 		   "Since the file had no name, it has been named \"LOST\".\n");
28316045Srrh 	} else {
284*47702Sbostic 		fprintf(mf, "Subject: editor saved \"%s\"\n\n", fname);
285*47702Sbostic 		fprintf(mf, "You were editing the file %s\n", fname);
286*47702Sbostic 		fprintf(mf, "at %s on the machine %s\n", timestamp, hostname);
287*47702Sbostic 		fprintf(mf, "when %s.\n", croak);
28816045Srrh 	}
289*47702Sbostic 	fprintf(mf, "\nYou can retrieve most of your changes to this file\n");
290*47702Sbostic 	fprintf(mf, "using the \"recover\" command of the editor.\n");
291460Smark 	fprintf(mf,
292*47702Sbostic 	    "An easy way to do this is to give the command \"vi -r %s\".\n",
293*47702Sbostic 	    fname);
294*47702Sbostic 	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  */
303*47702Sbostic error(msg)
304*47702Sbostic 	char *msg;
305460Smark {
306*47702Sbostic 	(void)fprintf(stderr, "ex3.7preserve: ");
307*47702Sbostic 	if (msg)
308*47702Sbostic 		(void)fprintf(stderr, "%s: ", msg);
309*47702Sbostic 	(void)fprintf(stderr, "%s\n", strerror(errno));
310*47702Sbostic 	exit(1);
311460Smark }
312460Smark 
313*47702Sbostic usage()
314460Smark {
315*47702Sbostic 	(void)fprintf(stderr, "usage: ex3.7preserve [-a]\n");
316*47702Sbostic 	exit(1);
317460Smark }
318