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