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