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 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 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. */ 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 */ 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 313 usage() 314 { 315 (void)fprintf(stderr, "usage: ex3.7preserve [-a]\n"); 316 exit(1); 317 } 318