1 /* Copyright (c) 1981 Regents of the University of California */
2 static char *sccsid = "@(#)ex3.7preserve.c	7.5	08/02/81";
3 #include <stdio.h>
4 #include <ctype.h>
5 #include <sys/types.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 FILE *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 = fopen(".", "r");
104 	if (tf == NULL) {
105 		perror(TMP);
106 		exit(1);
107 	}
108 	while (fread((char *) &dirent, sizeof dirent, 1, tf) == 1) {
109 		if (dirent.d_ino == 0)
110 			continue;
111 		/*
112 		 * Ex temporaries must begin with Ex;
113 		 * we check that the 10th character of the name is null
114 		 * so we won't have to worry about non-null terminated names
115 		 * later on.
116 		 */
117 		if (dirent.d_name[0] != 'E' || dirent.d_name[1] != 'x' || dirent.d_name[10])
118 			continue;
119 		if (stat(dirent.d_name, &stbuf))
120 			continue;
121 		if ((stbuf.st_mode & S_IFMT) != S_IFREG)
122 			continue;
123 		/*
124 		 * Save the bastard.
125 		 */
126 		ignore(copyout(dirent.d_name));
127 	}
128 	exit(0);
129 }
130 
131 char	pattern[] =	usrpath(preserve/Exaa`XXXXX);
132 
133 /*
134  * Copy file name into usrpath(preserve)/...
135  * If name is (char *) 0, then do the standard input.
136  * We make some checks on the input to make sure it is
137  * really an editor temporary, generate a name for the
138  * file (this is the slowest thing since we must stat
139  * to find a unique name), and finally copy the file.
140  */
141 copyout(name)
142 	char *name;
143 {
144 	int i;
145 	static int reenter;
146 	char buf[BUFSIZ];
147 
148 	/*
149 	 * The first time we put in the digits of our
150 	 * process number at the end of the pattern.
151 	 */
152 	if (reenter == 0) {
153 		mkdigits(pattern);
154 		reenter++;
155 	}
156 
157 	/*
158 	 * If a file name was given, make it the standard
159 	 * input if possible.
160 	 */
161 	if (name != 0) {
162 		ignore(close(0));
163 		/*
164 		 * Need read/write access for arcane reasons
165 		 * (see below).
166 		 */
167 		if (open(name, 2) < 0)
168 			return (-1);
169 	}
170 
171 	/*
172 	 * Get the header block.
173 	 */
174 	ignorl(lseek(0, 0l, 0));
175 	if (read(0, (char *) &H, sizeof H) != sizeof H) {
176 format:
177 		if (name == 0)
178 			fprintf(stderr, "Buffer format error\t");
179 		return (-1);
180 	}
181 
182 	/*
183 	 * Consistency checsks so we don't copy out garbage.
184 	 */
185 	if (H.Flines < 0) {
186 #ifdef DEBUG
187 		fprintf(stderr, "Negative number of lines\n");
188 #endif
189 		goto format;
190 	}
191 	if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
192 #ifdef DEBUG
193 		fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
194 #endif
195 		goto format;
196 	}
197 	if (name == 0 && H.Uid != getuid()) {
198 #ifdef DEBUG
199 		fprintf(stderr, "Wrong user-id\n");
200 #endif
201 		goto format;
202 	}
203 	if (lseek(0, 0l, 0)) {
204 #ifdef DEBUG
205 		fprintf(stderr, "Negative number of lines\n");
206 #endif
207 		goto format;
208 	}
209 
210 	/*
211 	 * If no name was assigned to the file, then give it the name
212 	 * LOST, by putting this in the header.
213 	 */
214 	if (H.Savedfile[0] == 0) {
215 		strcpy(H.Savedfile, "LOST");
216 		ignore(write(0, (char *) &H, sizeof H));
217 		H.Savedfile[0] = 0;
218 		lseek(0, 0l, 0);
219 	}
220 
221 	/*
222 	 * File is good.  Get a name and create a file for the copy.
223 	 */
224 	mknext(pattern);
225 	ignore(close(1));
226 	if (creat(pattern, 0600) < 0) {
227 		if (name == 0)
228 			perror(pattern);
229 		return (1);
230 	}
231 
232 	/*
233 	 * Make the target be owned by the owner of the file.
234 	 */
235 	ignore(chown(pattern, H.Uid, 0));
236 
237 	/*
238 	 * Copy the file.
239 	 */
240 	for (;;) {
241 		i = read(0, buf, BUFSIZ);
242 		if (i < 0) {
243 			if (name)
244 				perror("Buffer read error");
245 			ignore(unlink(pattern));
246 			return (-1);
247 		}
248 		if (i == 0) {
249 			if (name)
250 				ignore(unlink(name));
251 			notify(H.Uid, H.Savedfile, (int) name);
252 			return (0);
253 		}
254 		if (write(1, buf, i) != i) {
255 			if (name == 0)
256 				perror(pattern);
257 			unlink(pattern);
258 			return (-1);
259 		}
260 	}
261 }
262 
263 /*
264  * Blast the last 5 characters of cp to be the process number.
265  */
266 mkdigits(cp)
267 	char *cp;
268 {
269 	register int i, j;
270 
271 	for (i = getpid(), j = 5, cp += strlen(cp); j > 0; i /= 10, j--)
272 		*--cp = i % 10 | '0';
273 }
274 
275 /*
276  * Make the name in cp be unique by clobbering up to
277  * three alphabetic characters into a sequence of the form 'aab', 'aac', etc.
278  * Mktemp gets weird names too quickly to be useful here.
279  */
280 mknext(cp)
281 	char *cp;
282 {
283 	char *dcp;
284 	struct stat stb;
285 
286 	dcp = cp + strlen(cp) - 1;
287 	while (isdigit(*dcp))
288 		dcp--;
289 whoops:
290 	if (dcp[0] == 'z') {
291 		dcp[0] = 'a';
292 		if (dcp[-1] == 'z') {
293 			dcp[-1] = 'a';
294 			if (dcp[-2] == 'z')
295 				fprintf(stderr, "Can't find a name\t");
296 			dcp[-2]++;
297 		} else
298 			dcp[-1]++;
299 	} else
300 		dcp[0]++;
301 	if (stat(cp, &stb) == 0)
302 		goto whoops;
303 }
304 
305 /*
306  * Notify user uid that his file fname has been saved.
307  */
308 notify(uid, fname, flag)
309 	int uid;
310 	char *fname;
311 {
312 	struct passwd *pp = getpwuid(uid);
313 	register FILE *mf;
314 	char cmd[BUFSIZ];
315 
316 	if (pp == NULL)
317 		return;
318 	sprintf(cmd, "mail %s", pp->pw_name);
319 	mf = popen(cmd, "w");
320 	if (mf == NULL)
321 		return;
322 	setbuf(mf, cmd);
323 	if (fname[0] == 0) {
324 		fprintf(mf,
325 "A copy of an editor buffer of yours was saved when %s.\n",
326 		flag ? "the system went down" : "the editor was killed");
327 		fprintf(mf,
328 "No name was associated with this buffer so it has been named \"LOST\".\n");
329 	} else
330 		fprintf(mf,
331 "A copy of an editor buffer of your file \"%s\"\nwas saved when %s.\n", fname,
332 		/*
333 		 * "the editor was killed" is perhaps still not an ideal
334 		 * error message.  Usually, either it was forcably terminated
335 		 * or the phone was hung up, but we don't know which.
336 		 */
337 		flag ? "the system went down" : "the editor was killed");
338 	fprintf(mf,
339 "This buffer can be retrieved using the \"recover\" command of the editor.\n");
340 	fprintf(mf,
341 "An easy way to do this is to give the command \"ex -r %s\".\n",fname);
342 	fprintf(mf,
343 "This works for \"edit\" and \"vi\" also.\n");
344 	pclose(mf);
345 }
346 
347 /*
348  *	people making love
349  *	never exactly the same
350  *	just like a snowflake
351  */
352 
353 #ifdef lint
354 Ignore(a)
355 	int a;
356 {
357 
358 	a = a;
359 }
360 
361 Ignorl(a)
362 	long a;
363 {
364 
365 	a = a;
366 }
367 #endif
368