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