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