1 /* Copyright (c) 1981 Regents of the University of California */
2 static char *sccsid = "@(#)ex3.7preserve.c	7.10	12/05/84";
3 #include <stdio.h>
4 #include <ctype.h>
5 #include <sys/param.h>
6 #include <sys/stat.h>
7 #include <sys/dir.h>
8 #include <pwd.h>
9 #include "uparm.h"
10 
11 #define TMP	"/tmp"
12 
13 #ifdef VMUNIX
14 #define	HBLKS	2
15 #else
16 #define HBLKS	1
17 #endif
18 
19 char xstr[1];			/* make loader happy */
20 
21 /*
22  * Expreserve - preserve a file in usrpath(preserve)
23  * Bill Joy UCB November 13, 1977
24  *
25  * This routine is very naive - it doesn't remove anything from
26  * usrpath(preserve)... this may mean that we leave
27  * stuff there... the danger in doing anything with usrpath(preserve)
28  * is that the clock may be screwed up and we may get confused.
29  *
30  * We are called in two ways - first from the editor with no argumentss
31  * and the standard input open on the temp file. Second with an argument
32  * to preserve the entire contents of /tmp (root only).
33  *
34  * BUG: should do something about preserving Rx... (register contents)
35  *      temporaries.
36  */
37 
38 #ifndef VMUNIX
39 #define	LBLKS	125
40 #else
41 #define	LBLKS	900
42 #endif
43 #define	FNSIZE	128
44 
45 struct 	header {
46 	time_t	Time;			/* Time temp file last updated */
47 	int	Uid;			/* This users identity */
48 #ifndef VMUNIX
49 	short	Flines;			/* Number of lines in file */
50 #else
51 	int	Flines;
52 #endif
53 	char	Savedfile[FNSIZE];	/* The current file name */
54 	short	Blocks[LBLKS];		/* Blocks where line pointers stashed */
55 } H;
56 
57 #ifdef	lint
58 #define	ignore(a)	Ignore(a)
59 #define	ignorl(a)	Ignorl(a)
60 #else
61 #define	ignore(a)	a
62 #define	ignorl(a)	a
63 #endif
64 
65 struct	passwd *getpwuid();
66 off_t	lseek();
67 FILE	*popen();
68 
69 #define eq(a, b) strcmp(a, b) == 0
70 
71 main(argc)
72 	int argc;
73 {
74 	register DIR *tf;
75 	struct direct *dirent;
76 	struct stat stbuf;
77 
78 	/*
79 	 * If only one argument, then preserve the standard input.
80 	 */
81 	if (argc == 1) {
82 		if (copyout((char *) 0))
83 			exit(1);
84 		exit(0);
85 	}
86 
87 	/*
88 	 * If not super user, then can only preserve standard input.
89 	 */
90 	if (getuid()) {
91 		fprintf(stderr, "NOT super user\n");
92 		exit(1);
93 	}
94 
95 	/*
96 	 * ... else preserve all the stuff in /tmp, removing
97 	 * it as we go.
98 	 */
99 	if (chdir(TMP) < 0) {
100 		perror(TMP);
101 		exit(1);
102 	}
103 
104 	tf = opendir(".");
105 	if (tf == NULL) {
106 		perror(TMP);
107 		exit(1);
108 	}
109 	while ((dirent = readdir(tf)) != NULL) {
110 		/* Ex temporaries must begin with Ex. */
111 		if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x')
112 			continue;
113 		if (stat(dirent->d_name, &stbuf))
114 			continue;
115 		if ((stbuf.st_mode & S_IFMT) != S_IFREG)
116 			continue;
117 		/*
118 		 * Save the bastard.
119 		 */
120 		ignore(copyout(dirent->d_name));
121 	}
122 	closedir(tf);
123 	exit(0);
124 }
125 
126 char	pattern[] =	usrpath(preserve/Exaa`XXXXX);
127 
128 /*
129  * Copy file name into usrpath(preserve)/...
130  * If name is (char *) 0, then do the standard input.
131  * We make some checks on the input to make sure it is
132  * really an editor temporary, generate a name for the
133  * file (this is the slowest thing since we must stat
134  * to find a unique name), and finally copy the file.
135  */
136 copyout(name)
137 	char *name;
138 {
139 	int i;
140 	static int reenter;
141 	char buf[BUFSIZ];
142 
143 	/*
144 	 * The first time we put in the digits of our
145 	 * process number at the end of the pattern.
146 	 */
147 	if (reenter == 0) {
148 		mkdigits(pattern);
149 		reenter++;
150 	}
151 
152 	/*
153 	 * If a file name was given, make it the standard
154 	 * input if possible.
155 	 */
156 	if (name != 0) {
157 		ignore(close(0));
158 		/*
159 		 * Need read/write access for arcane reasons
160 		 * (see below).
161 		 */
162 		if (open(name, 2) < 0)
163 			return (-1);
164 	}
165 
166 	/*
167 	 * Get the header block.
168 	 */
169 	ignorl(lseek(0, 0l, 0));
170 	if (read(0, (char *) &H, sizeof H) != sizeof H) {
171 format:
172 		if (name == 0)
173 			fprintf(stderr, "Buffer format error\t");
174 		return (-1);
175 	}
176 
177 	/*
178 	 * Consistency checsks so we don't copy out garbage.
179 	 */
180 	if (H.Flines < 0) {
181 #ifdef DEBUG
182 		fprintf(stderr, "Negative number of lines\n");
183 #endif
184 		goto format;
185 	}
186 	if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
187 #ifdef DEBUG
188 		fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
189 #endif
190 		goto format;
191 	}
192 	if (name == 0 && H.Uid != getuid()) {
193 #ifdef DEBUG
194 		fprintf(stderr, "Wrong user-id\n");
195 #endif
196 		goto format;
197 	}
198 	if (lseek(0, 0l, 0)) {
199 #ifdef DEBUG
200 		fprintf(stderr, "Negative number of lines\n");
201 #endif
202 		goto format;
203 	}
204 
205 	/*
206 	 * If no name was assigned to the file, then give it the name
207 	 * LOST, by putting this in the header.
208 	 */
209 	if (H.Savedfile[0] == 0) {
210 		strcpy(H.Savedfile, "LOST");
211 		ignore(write(0, (char *) &H, sizeof H));
212 		H.Savedfile[0] = 0;
213 		lseek(0, 0l, 0);
214 	}
215 
216 	/*
217 	 * File is good.  Get a name and create a file for the copy.
218 	 */
219 	mknext(pattern);
220 	ignore(close(1));
221 	if (creat(pattern, 0600) < 0) {
222 		if (name == 0)
223 			perror(pattern);
224 		return (1);
225 	}
226 
227 	/*
228 	 * Make the target be owned by the owner of the file.
229 	 */
230 	ignore(chown(pattern, H.Uid, 0));
231 
232 	/*
233 	 * Copy the file.
234 	 */
235 	for (;;) {
236 		i = read(0, buf, BUFSIZ);
237 		if (i < 0) {
238 			if (name)
239 				perror("Buffer read error");
240 			ignore(unlink(pattern));
241 			return (-1);
242 		}
243 		if (i == 0) {
244 			if (name)
245 				ignore(unlink(name));
246 			notify(H.Uid, H.Savedfile, (int) name, H.Time);
247 			return (0);
248 		}
249 		if (write(1, buf, i) != i) {
250 			if (name == 0)
251 				perror(pattern);
252 			unlink(pattern);
253 			return (-1);
254 		}
255 	}
256 }
257 
258 /*
259  * Blast the last 5 characters of cp to be the process number.
260  */
261 mkdigits(cp)
262 	char *cp;
263 {
264 	register int i, j;
265 
266 	for (i = getpid(), j = 5, cp += strlen(cp); j > 0; i /= 10, j--)
267 		*--cp = i % 10 | '0';
268 }
269 
270 /*
271  * Make the name in cp be unique by clobbering up to
272  * three alphabetic characters into a sequence of the form 'aab', 'aac', etc.
273  * Mktemp gets weird names too quickly to be useful here.
274  */
275 mknext(cp)
276 	char *cp;
277 {
278 	char *dcp;
279 	struct stat stb;
280 
281 	dcp = cp + strlen(cp) - 1;
282 	while (isdigit(*dcp))
283 		dcp--;
284 whoops:
285 	if (dcp[0] == 'z') {
286 		dcp[0] = 'a';
287 		if (dcp[-1] == 'z') {
288 			dcp[-1] = 'a';
289 			if (dcp[-2] == 'z')
290 				fprintf(stderr, "Can't find a name\t");
291 			dcp[-2]++;
292 		} else
293 			dcp[-1]++;
294 	} else
295 		dcp[0]++;
296 	if (stat(cp, &stb) == 0)
297 		goto whoops;
298 }
299 
300 /*
301  * Notify user uid that his file fname has been saved.
302  */
303 notify(uid, fname, flag, time)
304 	int uid;
305 	char *fname;
306 	time_t	time;
307 {
308 	struct passwd *pp = getpwuid(uid);
309 	register FILE *mf;
310 	char	cmd[BUFSIZ];
311 	char	hostname[128];
312 	char	croak[128];
313 	char	*timestamp, *ctime();
314 
315 	if (pp == NULL)
316 		return;
317 	gethostname(hostname, sizeof(hostname));
318 	timestamp = ctime(&time);
319 	timestamp[16] = 0;	/* blast from seconds on */
320 	sprintf(cmd, "/bin/mail %s", pp->pw_name);
321 	setuid(getuid());
322 	mf = popen(cmd, "w");
323 	if (mf == NULL)
324 		return;
325 	setbuf(mf, cmd);
326 	/*
327 	 *	flag says how the editor croaked:
328 	 * "the editor was killed" is perhaps still not an ideal
329 	 * error message.  Usually, either it was forcably terminated
330 	 * or the phone was hung up, but we don't know which.
331 	 */
332 	sprintf(croak, flag
333 		? "the system went down"
334 		: "the editor was killed");
335 	if (fname[0] == 0) {
336 		fprintf(mf,
337 "Subject: editor saved ``LOST''\n");
338 		fprintf(mf,
339 "You were editing a file without a name\n");
340 		fprintf(mf,
341 "at <%s> on the machine ``%s'' when %s.\n", timestamp, hostname, croak);
342 		fprintf(mf,
343 "Since the file had no name, it has been named \"LOST\".\n");
344 	} else {
345 		fprintf(mf,
346 "Subject: editor saved ``%s''\n", fname);
347 		fprintf(mf,
348 "You were editing the file \"%s\"\n", fname);
349 		fprintf(mf,
350 "at <%s> on the machine ``%s''\n", timestamp, hostname);
351 		fprintf(mf,
352 "when %s.\n", croak);
353 	}
354 	fprintf(mf,
355 "\nYou can retrieve most of your changes to this file\n");
356 	fprintf(mf,
357 "using the \"recover\" command of the editor.\n");
358 	fprintf(mf,
359 "An easy way to do this is to give the command \"vi -r %s\".\n", fname);
360 	fprintf(mf,
361 "This method also works using \"ex\" and \"edit\".\n");
362 	pclose(mf);
363 }
364 
365 /*
366  *	people making love
367  *	never exactly the same
368  *	just like a snowflake
369  */
370 
371 #ifdef lint
372 Ignore(a)
373 	int a;
374 {
375 
376 	a = a;
377 }
378 
379 Ignorl(a)
380 	long a;
381 {
382 
383 	a = a;
384 }
385 #endif
386