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