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