1 /*-
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1991, 1993\n\
11 The Regents of the University of California. All rights reserved.\n";
12 #endif /* not lint */
13
14 #ifndef lint
15 static char sccsid[] = "@(#)ex3.7preserve.c 8.1 (Berkeley) 06/09/93";
16 #endif /* not lint */
17
18 #include <sys/param.h>
19 #include <sys/stat.h>
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <time.h>
23 #include <pwd.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include "pathnames.h"
31
32 /*
33 * Expreserve - preserve a file in _PATH_PRESERVE.
34 * Bill Joy UCB November 13, 1977
35 *
36 * This routine is very naive - it doesn't remove anything from
37 * _PATH_PRESERVE... this may mean that we leave stuff there...
38 * the danger in doing anything with _PATH_PRESERVE is that the
39 * clock may be screwed up and we may get confused.
40 *
41 * We are called in two ways - first from the editor with no arguments
42 * and the standard input open on the temp file. Second with an
43 * argument to preserve the entire contents of _PATH_VARTMP (root only).
44 *
45 * BUG: should do something about preserving Rx... (register contents)
46 * temporaries.
47 */
48
49 #ifdef VMUNIX
50 #define HBLKS 2
51 #define LBLKS 900
52 #else
53 #define HBLKS 1
54 #define LBLKS 125
55 #endif
56 #define FNSIZE 128
57
58 struct header {
59 time_t Time; /* Time temp file last updated */
60 int Uid; /* This users identity */
61 #ifdef VMUNIX
62 int Flines;
63 #else
64 short Flines; /* Number of lines in file */
65 #endif
66 char Savedfile[FNSIZE]; /* The current file name */
67 short Blocks[LBLKS]; /* Blocks where line pointers stashed */
68 } H;
69
main(argc,argv)70 main(argc, argv)
71 int argc;
72 char **argv;
73 {
74 extern int optind;
75 register DIR *tf;
76 struct dirent *dirent;
77 struct stat stbuf;
78 int aflag, ch, err;
79 char path[MAXPATHLEN];
80
81 aflag = 0;
82 while ((ch = getopt(argc, argv, "a")) != EOF)
83 switch(ch) {
84 case 'a':
85 aflag = 1;
86 break;
87 case '?':
88 default:
89 usage();
90 }
91 argc -= optind;
92 argv += optind;
93
94 if (chdir(_PATH_PRESERVE) < 0)
95 error(_PATH_PRESERVE);
96
97 /*
98 * If only one argument, then preserve the standard input.
99 */
100 if (!aflag)
101 exit(copyout(NULL) ? 1 : 0);
102
103 /*
104 * If not super user, then can only preserve standard input.
105 */
106 if (getuid()) {
107 errno = EPERM;
108 error(NULL);
109 }
110
111 /*
112 * ... else preserve all the stuff in /var/tmp, removing
113 * it as we go.
114 */
115 if (!(tf = opendir(_PATH_VARTMP)))
116 error(_PATH_VARTMP);
117
118 err = 0;
119 while ((dirent = readdir(tf)) != NULL) {
120 /* Ex temporaries must begin with Ex. */
121 if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x')
122 continue;
123 (void)sprintf(path, "%s/%s", _PATH_VARTMP, dirent->d_name);
124 if (stat(path, &stbuf) || !S_ISREG(stbuf.st_mode))
125 continue;
126 /*
127 * Save the bastard.
128 */
129 err |= copyout(path);
130 }
131 (void)closedir(tf);
132 exit(err);
133 }
134
copyout(name)135 copyout(name)
136 char *name;
137 {
138 struct stat sb;
139 register int ifd, ofd, nr, nw, off, rval;
140 char buf[8*1024], fname[20];
141
142 /* Open any given file name. */
143 if (name) {
144 if ((ifd = open(name, O_RDWR)) < 0)
145 return(1);
146 (void)fstat(ifd, &sb);
147 if (!sb.st_size) {
148 (void)unlink(name);
149 return(0);
150 }
151 } else {
152 ifd = STDIN_FILENO;
153 /* vi hands us an fd, it's not necessarily at the beginning. */
154 (void)lseek(ifd, 0l, SEEK_SET);
155 }
156
157 if (read(ifd, &H, sizeof(H)) != sizeof(H))
158 goto format;
159
160 /*
161 * Consistency checks so we don't copy out garbage.
162 */
163 if (H.Flines < 0) {
164 (void)fprintf(stderr,
165 "ex3.7preserve: negative number of lines\n");
166 goto format;
167 }
168
169 if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
170 (void)fprintf(stderr,
171 "ex3.7preserve: blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
172 goto format;
173 }
174
175 if (!name && H.Uid != getuid()) {
176 (void)fprintf(stderr, "ex3.7preserve: wrong user-id\n");
177 goto format;
178 }
179
180 if (lseek(ifd, 0l, SEEK_SET)) {
181 (void)fprintf(stderr,
182 "ex3.7preserve: negative number of lines\n");
183 format: (void)fprintf(stderr, "ex3.7preserve: %s\n", strerror(EFTYPE));
184 return (1);
185 }
186
187 /*
188 * If no name was assigned to the file, then give it the name
189 * LOST, by putting this in the header. This involves overwriting
190 * the "input" file.
191 */
192 if (H.Savedfile[0] == 0) {
193 (void)strcpy(H.Savedfile, "LOST");
194 (void)write(ifd, &H, sizeof(H));
195 H.Savedfile[0] = 0;
196 (void)lseek(ifd, 0l, SEEK_SET);
197 }
198
199 /* File is good. Get a name and create a file for the copy. */
200 (void)strcpy(fname, "ExXXXXXX");
201 if ((ofd = mkstemp(fname)) == -1)
202 return(1);
203
204 /* Copy the file. */
205 rval = 0;
206 while ((nr = read(ifd, buf, sizeof(buf))) > 0)
207 for (off = 0; off < nr; nr -= nw, off += nw)
208 if ((nw = write(ofd, buf + off, nr)) < 0) {
209 (void)fprintf(stderr,
210 "ex3.7preserve: tmp file: %s\n",
211 strerror(errno));
212 rval = 1;
213 break;
214 }
215 if (nr < 0) {
216 (void)fprintf(stderr, "ex3.7preserve: %s: %s\n",
217 name ? name : "stdin", strerror(errno));
218 rval = 1;
219 }
220
221 if (rval)
222 (void)unlink(fname);
223 else {
224 (void)fchown(ofd, H.Uid, 0);
225 notify(H.Uid, H.Savedfile, (int)name, H.Time);
226 if (name)
227 (void)unlink(name);
228 }
229 (void)close(ifd);
230 (void)close(ofd);
231 return(rval);
232 }
233
234 /* Notify user uid that his file fname has been saved. */
notify(uid,fname,flag,time)235 notify(uid, fname, flag, time)
236 int uid;
237 char *fname;
238 time_t time;
239 {
240 struct passwd *pp;
241 register FILE *mf;
242 static int reenter;
243 static char hostname[MAXHOSTNAMELEN], *timestamp;
244 char cmd[MAXPATHLEN + 50], croak[50];
245
246 pp = getpwuid(uid);
247 if (pp == NULL)
248 return;
249
250 if (!reenter) {
251 reenter = 1;
252 (void)gethostname(hostname, sizeof(hostname));
253 timestamp = ctime(&time);
254 timestamp[16] = 0; /* blast from seconds on */
255 }
256
257 (void)snprintf(cmd, sizeof(cmd),
258 "%s -i -t -F \"The Editor\" -f root", _PATH_SENDMAIL);
259 mf = popen(cmd, "w");
260 if (mf == NULL)
261 return;
262 (void)fprintf(mf,
263 "Reply-To: root@%s\nFrom: root@%s (The Editor)\nTo: %s\n",
264 hostname, hostname, pp->pw_name);
265
266 /*
267 * flag says how the editor croaked: "the editor was killed" is
268 * perhaps still not an ideal error message. Usually, either it
269 * was forcably terminated or the phone was hung up, but we don't
270 * know which.
271 */
272 (void)snprintf(croak, sizeof(croak),
273 flag ? "the system went down"
274 : "the editor was killed");
275 if (!fname || !fname[0]) {
276 fname = "LOST";
277 fprintf(mf, "Subject: editor saved \"LOST\"\n\n");
278 fprintf(mf, "You were editing a file without a name\n");
279 fprintf(mf, "at %s on the machine %s when %s.\n",
280 timestamp, hostname, croak);
281 fprintf(mf,
282 "Since the file had no name, it has been named \"LOST\".\n");
283 } else {
284 fprintf(mf, "Subject: editor saved \"%s\"\n\n", fname);
285 fprintf(mf, "You were editing the file %s\n", fname);
286 fprintf(mf, "at %s on the machine %s\n", timestamp, hostname);
287 fprintf(mf, "when %s.\n", croak);
288 }
289 fprintf(mf, "\nYou can retrieve most of your changes to this file\n");
290 fprintf(mf, "using the \"recover\" command of the editor.\n");
291 fprintf(mf,
292 "An easy way to do this is to give the command \"vi -r %s\".\n",
293 fname);
294 fprintf(mf, "This method also works using \"ex\" and \"edit\".\n\n");
295 pclose(mf);
296 }
297
298 /*
299 * people making love
300 * never exactly the same
301 * just like a snowflake
302 */
error(msg)303 error(msg)
304 char *msg;
305 {
306 (void)fprintf(stderr, "ex3.7preserve: ");
307 if (msg)
308 (void)fprintf(stderr, "%s: ", msg);
309 (void)fprintf(stderr, "%s\n", strerror(errno));
310 exit(1);
311 }
312
usage()313 usage()
314 {
315 (void)fprintf(stderr, "usage: ex3.7preserve [-a]\n");
316 exit(1);
317 }
318