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