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