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