1 /*-
2  * Copyright (c) 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1991, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)ex3.7preserve.c	8.1 (Berkeley) 06/09/93";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/stat.h>
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <time.h>
23 #include <pwd.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include "pathnames.h"
31 
32 /*
33  * Expreserve - preserve a file in _PATH_PRESERVE.
34  * Bill Joy UCB November 13, 1977
35  *
36  * This routine is very naive - it doesn't remove anything from
37  * _PATH_PRESERVE... this may mean that we leave stuff there...
38  * the danger in doing anything with _PATH_PRESERVE is that the
39  * clock may be screwed up and we may get confused.
40  *
41  * We are called in two ways - first from the editor with no arguments
42  * and the standard input open on the temp file.  Second with an
43  * argument to preserve the entire contents of _PATH_VARTMP (root only).
44  *
45  * BUG: should do something about preserving Rx... (register contents)
46  *      temporaries.
47  */
48 
49 #ifdef VMUNIX
50 #define	HBLKS	2
51 #define	LBLKS	900
52 #else
53 #define HBLKS	1
54 #define	LBLKS	125
55 #endif
56 #define	FNSIZE	128
57 
58 struct 	header {
59 	time_t	Time;			/* Time temp file last updated */
60 	int	Uid;			/* This users identity */
61 #ifdef VMUNIX
62 	int	Flines;
63 #else
64 	short	Flines;			/* Number of lines in file */
65 #endif
66 	char	Savedfile[FNSIZE];	/* The current file name */
67 	short	Blocks[LBLKS];		/* Blocks where line pointers stashed */
68 } H;
69 
70 main(argc, argv)
71 	int argc;
72 	char **argv;
73 {
74 	extern int optind;
75 	register DIR *tf;
76 	struct dirent *dirent;
77 	struct stat stbuf;
78 	int aflag, ch, err;
79 	char path[MAXPATHLEN];
80 
81 	aflag = 0;
82 	while ((ch = getopt(argc, argv, "a")) != EOF)
83 		switch(ch) {
84 		case 'a':
85 			aflag = 1;
86 			break;
87 		case '?':
88 		default:
89 			usage();
90 		}
91 	argc -= optind;
92 	argv += optind;
93 
94 	if (chdir(_PATH_PRESERVE) < 0)
95 		error(_PATH_PRESERVE);
96 
97 	/*
98 	 * If only one argument, then preserve the standard input.
99 	 */
100 	if (!aflag)
101 		exit(copyout(NULL) ? 1 : 0);
102 
103 	/*
104 	 * If not super user, then can only preserve standard input.
105 	 */
106 	if (getuid()) {
107 		errno = EPERM;
108 		error(NULL);
109 	}
110 
111 	/*
112 	 * ... else preserve all the stuff in /var/tmp, removing
113 	 * it as we go.
114 	 */
115 	if (!(tf = opendir(_PATH_VARTMP)))
116 		error(_PATH_VARTMP);
117 
118 	err = 0;
119 	while ((dirent = readdir(tf)) != NULL) {
120 		/* Ex temporaries must begin with Ex. */
121 		if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x')
122 			continue;
123 		(void)sprintf(path, "%s/%s", _PATH_VARTMP, dirent->d_name);
124 		if (stat(path, &stbuf) || !S_ISREG(stbuf.st_mode))
125 			continue;
126 		/*
127 		 * Save the bastard.
128 		 */
129 		err |= copyout(path);
130 	}
131 	(void)closedir(tf);
132 	exit(err);
133 }
134 
135 copyout(name)
136 	char *name;
137 {
138 	struct stat sb;
139 	register int ifd, ofd, nr, nw, off, rval;
140 	char buf[8*1024], fname[20];
141 
142 	/* Open any given file name. */
143 	if (name) {
144 		if ((ifd = open(name, O_RDWR)) < 0)
145 			return(1);
146 		(void)fstat(ifd, &sb);
147 		if (!sb.st_size) {
148 			(void)unlink(name);
149 			return(0);
150 		}
151 	} else {
152 		ifd = STDIN_FILENO;
153 		/* vi hands us an fd, it's not necessarily at the beginning. */
154 		(void)lseek(ifd, 0l, SEEK_SET);
155 	}
156 
157 	if (read(ifd, &H, sizeof(H)) != sizeof(H))
158 		goto format;
159 
160 	/*
161 	 * Consistency checks so we don't copy out garbage.
162 	 */
163 	if (H.Flines < 0) {
164 		(void)fprintf(stderr,
165 		    "ex3.7preserve: negative number of lines\n");
166 		goto format;
167 	}
168 
169 	if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
170 		(void)fprintf(stderr,
171 		    "ex3.7preserve: blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
172 		goto format;
173 	}
174 
175 	if (!name && H.Uid != getuid()) {
176 		(void)fprintf(stderr, "ex3.7preserve: wrong user-id\n");
177 		goto format;
178 	}
179 
180 	if (lseek(ifd, 0l, SEEK_SET)) {
181 		(void)fprintf(stderr,
182 		    "ex3.7preserve: negative number of lines\n");
183 format:		(void)fprintf(stderr, "ex3.7preserve: %s\n", strerror(EFTYPE));
184 		return (1);
185 	}
186 
187 	/*
188 	 * If no name was assigned to the file, then give it the name
189 	 * LOST, by putting this in the header.  This involves overwriting
190 	 * the "input" file.
191 	 */
192 	if (H.Savedfile[0] == 0) {
193 		(void)strcpy(H.Savedfile, "LOST");
194 		(void)write(ifd, &H, sizeof(H));
195 		H.Savedfile[0] = 0;
196 		(void)lseek(ifd, 0l, SEEK_SET);
197 	}
198 
199 	/* File is good.  Get a name and create a file for the copy. */
200 	(void)strcpy(fname, "ExXXXXXX");
201 	if ((ofd = mkstemp(fname)) == -1)
202 		return(1);
203 
204 	/* Copy the file. */
205 	rval = 0;
206 	while ((nr = read(ifd, buf, sizeof(buf))) > 0)
207 		for (off = 0; off < nr; nr -= nw, off += nw)
208 			if ((nw = write(ofd, buf + off, nr)) < 0) {
209 				(void)fprintf(stderr,
210 				    "ex3.7preserve: tmp file: %s\n",
211 				    strerror(errno));
212 				rval = 1;
213 				break;
214 			}
215 	if (nr < 0) {
216 		(void)fprintf(stderr, "ex3.7preserve: %s: %s\n",
217 		    name ? name : "stdin", strerror(errno));
218 		rval = 1;
219 	}
220 
221 	if (rval)
222 		(void)unlink(fname);
223 	else {
224 		(void)fchown(ofd, H.Uid, 0);
225 		notify(H.Uid, H.Savedfile, (int)name, H.Time);
226 		if (name)
227 			(void)unlink(name);
228 	}
229 	(void)close(ifd);
230 	(void)close(ofd);
231 	return(rval);
232 }
233 
234 /* Notify user uid that his file fname has been saved. */
235 notify(uid, fname, flag, time)
236 	int uid;
237 	char *fname;
238 	time_t	time;
239 {
240 	struct passwd *pp;
241 	register FILE *mf;
242 	static int reenter;
243 	static char hostname[MAXHOSTNAMELEN], *timestamp;
244 	char cmd[MAXPATHLEN + 50], croak[50];
245 
246 	pp = getpwuid(uid);
247 	if (pp == NULL)
248 		return;
249 
250 	if (!reenter) {
251 		reenter = 1;
252 		(void)gethostname(hostname, sizeof(hostname));
253 		timestamp = ctime(&time);
254 		timestamp[16] = 0;	/* blast from seconds on */
255 	}
256 
257 	(void)snprintf(cmd, sizeof(cmd),
258 	    "%s -i -t -F \"The Editor\" -f root", _PATH_SENDMAIL);
259 	mf = popen(cmd, "w");
260 	if (mf == NULL)
261 		return;
262 	(void)fprintf(mf,
263 	    "Reply-To: root@%s\nFrom: root@%s (The Editor)\nTo: %s\n",
264 	    hostname, hostname, pp->pw_name);
265 
266 	/*
267 	 * flag says how the editor croaked: "the editor was killed" is
268 	 * perhaps still not an ideal error message.  Usually, either it
269 	 * was forcably terminated or the phone was hung up, but we don't
270 	 * know which.
271 	 */
272 	(void)snprintf(croak, sizeof(croak),
273 	    flag ? "the system went down"
274 		 : "the editor was killed");
275 	if (!fname  || !fname[0]) {
276 		fname = "LOST";
277 		fprintf(mf, "Subject: editor saved \"LOST\"\n\n");
278 		fprintf(mf, "You were editing a file without a name\n");
279 		fprintf(mf, "at %s on the machine %s when %s.\n",
280 		    timestamp, hostname, croak);
281 		fprintf(mf,
282 		   "Since the file had no name, it has been named \"LOST\".\n");
283 	} else {
284 		fprintf(mf, "Subject: editor saved \"%s\"\n\n", fname);
285 		fprintf(mf, "You were editing the file %s\n", fname);
286 		fprintf(mf, "at %s on the machine %s\n", timestamp, hostname);
287 		fprintf(mf, "when %s.\n", croak);
288 	}
289 	fprintf(mf, "\nYou can retrieve most of your changes to this file\n");
290 	fprintf(mf, "using the \"recover\" command of the editor.\n");
291 	fprintf(mf,
292 	    "An easy way to do this is to give the command \"vi -r %s\".\n",
293 	    fname);
294 	fprintf(mf, "This method also works using \"ex\" and \"edit\".\n\n");
295 	pclose(mf);
296 }
297 
298 /*
299  *	people making love
300  *	never exactly the same
301  *	just like a snowflake
302  */
303 error(msg)
304 	char *msg;
305 {
306 	(void)fprintf(stderr, "ex3.7preserve: ");
307 	if (msg)
308 		(void)fprintf(stderr, "%s: ", msg);
309 	(void)fprintf(stderr, "%s\n", strerror(errno));
310 	exit(1);
311 }
312 
313 usage()
314 {
315 	(void)fprintf(stderr, "usage: ex3.7preserve [-a]\n");
316 	exit(1);
317 }
318