13979Smark /* Copyright (c) 1981 Regents of the University of California */ 2*14625Smckusick static char *sccsid = "@(#)ex3.7preserve.c 7.8 08/14/83"; 3460Smark #include <stdio.h> 4460Smark #include <ctype.h> 512956Sralph #include <sys/param.h> 6460Smark #include <sys/stat.h> 7460Smark #include <sys/dir.h> 8460Smark #include <pwd.h> 913759Ssam #include "uparm.h" 1013759Ssam 113995Smark #define TMP "/tmp" 12460Smark 13483Smark #ifdef VMUNIX 14483Smark #define HBLKS 2 154001Smark #else 164001Smark #define HBLKS 1 17483Smark #endif 18483Smark 193995Smark char xstr[1]; /* make loader happy */ 203995Smark 21460Smark /* 22460Smark * Expreserve - preserve a file in usrpath(preserve) 23460Smark * Bill Joy UCB November 13, 1977 24460Smark * 25460Smark * This routine is very naive - it doesn't remove anything from 2613759Ssam * usrpath(preserve)... this may mean that we leave 2713759Ssam * stuff there... the danger in doing anything with usrpath(preserve) 28460Smark * is that the clock may be screwed up and we may get confused. 29460Smark * 30460Smark * We are called in two ways - first from the editor with no argumentss 31460Smark * and the standard input open on the temp file. Second with an argument 32460Smark * to preserve the entire contents of /tmp (root only). 33460Smark * 34460Smark * BUG: should do something about preserving Rx... (register contents) 35460Smark * temporaries. 36460Smark */ 37460Smark 38483Smark #ifndef VMUNIX 39460Smark #define LBLKS 125 40483Smark #else 41483Smark #define LBLKS 900 42483Smark #endif 43460Smark #define FNSIZE 128 44460Smark 45460Smark struct header { 46460Smark time_t Time; /* Time temp file last updated */ 474051Smark int Uid; /* This users identity */ 48483Smark #ifndef VMUNIX 49460Smark short Flines; /* Number of lines in file */ 50483Smark #else 51483Smark int Flines; 52483Smark #endif 53460Smark char Savedfile[FNSIZE]; /* The current file name */ 54460Smark short Blocks[LBLKS]; /* Blocks where line pointers stashed */ 55460Smark } H; 56460Smark 57460Smark #ifdef lint 58460Smark #define ignore(a) Ignore(a) 59460Smark #define ignorl(a) Ignorl(a) 60460Smark #else 61460Smark #define ignore(a) a 62460Smark #define ignorl(a) a 63460Smark #endif 64460Smark 65460Smark struct passwd *getpwuid(); 66460Smark off_t lseek(); 67460Smark FILE *popen(); 68460Smark 69460Smark #define eq(a, b) strcmp(a, b) == 0 70460Smark 71460Smark main(argc) 72460Smark int argc; 73460Smark { 7412956Sralph register DIR *tf; 7512956Sralph struct direct *dirent; 76460Smark struct stat stbuf; 77460Smark 78460Smark /* 79460Smark * If only one argument, then preserve the standard input. 80460Smark */ 81460Smark if (argc == 1) { 82460Smark if (copyout((char *) 0)) 83460Smark exit(1); 84460Smark exit(0); 85460Smark } 86460Smark 87460Smark /* 88460Smark * If not super user, then can only preserve standard input. 89460Smark */ 90460Smark if (getuid()) { 91460Smark fprintf(stderr, "NOT super user\n"); 92460Smark exit(1); 93460Smark } 94460Smark 95460Smark /* 96460Smark * ... else preserve all the stuff in /tmp, removing 97460Smark * it as we go. 98460Smark */ 993979Smark if (chdir(TMP) < 0) { 1003979Smark perror(TMP); 1013995Smark exit(1); 1023995Smark } 103460Smark 10412956Sralph tf = opendir("."); 105460Smark if (tf == NULL) { 1063995Smark perror(TMP); 107460Smark exit(1); 1083995Smark } 10912956Sralph while ((dirent = readdir(tf)) != NULL) { 11013759Ssam /* Ex temporaries must begin with Ex. */ 11113759Ssam if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x') 112460Smark continue; 11312956Sralph if (stat(dirent->d_name, &stbuf)) 114460Smark continue; 115460Smark if ((stbuf.st_mode & S_IFMT) != S_IFREG) 116460Smark continue; 117460Smark /* 118460Smark * Save the bastard. 119460Smark */ 12012956Sralph ignore(copyout(dirent->d_name)); 121460Smark } 12212956Sralph closedir(tf); 123460Smark exit(0); 124460Smark } 125460Smark 126460Smark char pattern[] = usrpath(preserve/Exaa`XXXXX); 127460Smark 128460Smark /* 129460Smark * Copy file name into usrpath(preserve)/... 130460Smark * If name is (char *) 0, then do the standard input. 131460Smark * We make some checks on the input to make sure it is 132460Smark * really an editor temporary, generate a name for the 133460Smark * file (this is the slowest thing since we must stat 134460Smark * to find a unique name), and finally copy the file. 135460Smark */ 136460Smark copyout(name) 137460Smark char *name; 138460Smark { 139460Smark int i; 140460Smark static int reenter; 141460Smark char buf[BUFSIZ]; 142460Smark 143460Smark /* 144460Smark * The first time we put in the digits of our 145460Smark * process number at the end of the pattern. 146460Smark */ 147460Smark if (reenter == 0) { 148460Smark mkdigits(pattern); 149460Smark reenter++; 150460Smark } 151460Smark 152460Smark /* 153460Smark * If a file name was given, make it the standard 154460Smark * input if possible. 155460Smark */ 156460Smark if (name != 0) { 157460Smark ignore(close(0)); 158460Smark /* 159460Smark * Need read/write access for arcane reasons 160460Smark * (see below). 161460Smark */ 162460Smark if (open(name, 2) < 0) 163460Smark return (-1); 164460Smark } 165460Smark 166460Smark /* 167460Smark * Get the header block. 168460Smark */ 169460Smark ignorl(lseek(0, 0l, 0)); 170460Smark if (read(0, (char *) &H, sizeof H) != sizeof H) { 171460Smark format: 172460Smark if (name == 0) 1731540Smark fprintf(stderr, "Buffer format error\t"); 174460Smark return (-1); 175460Smark } 176460Smark 177460Smark /* 178460Smark * Consistency checsks so we don't copy out garbage. 179460Smark */ 180460Smark if (H.Flines < 0) { 181460Smark #ifdef DEBUG 182460Smark fprintf(stderr, "Negative number of lines\n"); 183460Smark #endif 184460Smark goto format; 185460Smark } 186483Smark if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) { 187460Smark #ifdef DEBUG 188460Smark fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]); 189460Smark #endif 190460Smark goto format; 191460Smark } 192460Smark if (name == 0 && H.Uid != getuid()) { 193460Smark #ifdef DEBUG 194460Smark fprintf(stderr, "Wrong user-id\n"); 195460Smark #endif 196460Smark goto format; 197460Smark } 198460Smark if (lseek(0, 0l, 0)) { 199460Smark #ifdef DEBUG 200460Smark fprintf(stderr, "Negative number of lines\n"); 201460Smark #endif 202460Smark goto format; 203460Smark } 204460Smark 205460Smark /* 206460Smark * If no name was assigned to the file, then give it the name 207460Smark * LOST, by putting this in the header. 208460Smark */ 209460Smark if (H.Savedfile[0] == 0) { 210460Smark strcpy(H.Savedfile, "LOST"); 211460Smark ignore(write(0, (char *) &H, sizeof H)); 212460Smark H.Savedfile[0] = 0; 213460Smark lseek(0, 0l, 0); 214460Smark } 215460Smark 216460Smark /* 217460Smark * File is good. Get a name and create a file for the copy. 218460Smark */ 219460Smark mknext(pattern); 220460Smark ignore(close(1)); 221460Smark if (creat(pattern, 0600) < 0) { 222460Smark if (name == 0) 223460Smark perror(pattern); 224460Smark return (1); 225460Smark } 226460Smark 227460Smark /* 228460Smark * Make the target be owned by the owner of the file. 229460Smark */ 230460Smark ignore(chown(pattern, H.Uid, 0)); 231460Smark 232460Smark /* 233460Smark * Copy the file. 234460Smark */ 235460Smark for (;;) { 236460Smark i = read(0, buf, BUFSIZ); 237460Smark if (i < 0) { 238460Smark if (name) 239460Smark perror("Buffer read error"); 240460Smark ignore(unlink(pattern)); 241460Smark return (-1); 242460Smark } 243460Smark if (i == 0) { 244460Smark if (name) 245460Smark ignore(unlink(name)); 246460Smark notify(H.Uid, H.Savedfile, (int) name); 247460Smark return (0); 248460Smark } 249460Smark if (write(1, buf, i) != i) { 250460Smark if (name == 0) 251460Smark perror(pattern); 252460Smark unlink(pattern); 253460Smark return (-1); 254460Smark } 255460Smark } 256460Smark } 257460Smark 258460Smark /* 259460Smark * Blast the last 5 characters of cp to be the process number. 260460Smark */ 261460Smark mkdigits(cp) 262460Smark char *cp; 263460Smark { 264460Smark register int i, j; 265460Smark 266460Smark for (i = getpid(), j = 5, cp += strlen(cp); j > 0; i /= 10, j--) 267460Smark *--cp = i % 10 | '0'; 268460Smark } 269460Smark 270460Smark /* 271460Smark * Make the name in cp be unique by clobbering up to 272460Smark * three alphabetic characters into a sequence of the form 'aab', 'aac', etc. 273460Smark * Mktemp gets weird names too quickly to be useful here. 274460Smark */ 275460Smark mknext(cp) 276460Smark char *cp; 277460Smark { 278460Smark char *dcp; 279460Smark struct stat stb; 280460Smark 281460Smark dcp = cp + strlen(cp) - 1; 282460Smark while (isdigit(*dcp)) 283460Smark dcp--; 284460Smark whoops: 285460Smark if (dcp[0] == 'z') { 286460Smark dcp[0] = 'a'; 287460Smark if (dcp[-1] == 'z') { 288460Smark dcp[-1] = 'a'; 289460Smark if (dcp[-2] == 'z') 2901540Smark fprintf(stderr, "Can't find a name\t"); 291460Smark dcp[-2]++; 292460Smark } else 293460Smark dcp[-1]++; 294460Smark } else 295460Smark dcp[0]++; 296460Smark if (stat(cp, &stb) == 0) 297460Smark goto whoops; 298460Smark } 299460Smark 300460Smark /* 301460Smark * Notify user uid that his file fname has been saved. 302460Smark */ 303460Smark notify(uid, fname, flag) 304460Smark int uid; 305460Smark char *fname; 306460Smark { 307460Smark struct passwd *pp = getpwuid(uid); 308460Smark register FILE *mf; 309460Smark char cmd[BUFSIZ]; 310460Smark 311460Smark if (pp == NULL) 312460Smark return; 313*14625Smckusick sprintf(cmd, "/bin/mail %s", pp->pw_name); 314460Smark mf = popen(cmd, "w"); 315460Smark if (mf == NULL) 316460Smark return; 317460Smark setbuf(mf, cmd); 318460Smark if (fname[0] == 0) { 319460Smark fprintf(mf, 320460Smark "A copy of an editor buffer of yours was saved when %s.\n", 3211540Smark flag ? "the system went down" : "the editor was killed"); 322460Smark fprintf(mf, 323460Smark "No name was associated with this buffer so it has been named \"LOST\".\n"); 324460Smark } else 325460Smark fprintf(mf, 326460Smark "A copy of an editor buffer of your file \"%s\"\nwas saved when %s.\n", fname, 3271540Smark /* 3281540Smark * "the editor was killed" is perhaps still not an ideal 3291540Smark * error message. Usually, either it was forcably terminated 3301540Smark * or the phone was hung up, but we don't know which. 3311540Smark */ 3321540Smark flag ? "the system went down" : "the editor was killed"); 333460Smark fprintf(mf, 334460Smark "This buffer can be retrieved using the \"recover\" command of the editor.\n"); 335460Smark fprintf(mf, 336460Smark "An easy way to do this is to give the command \"ex -r %s\".\n",fname); 337460Smark fprintf(mf, 338460Smark "This works for \"edit\" and \"vi\" also.\n"); 339460Smark pclose(mf); 340460Smark } 341460Smark 342460Smark /* 343460Smark * people making love 344460Smark * never exactly the same 345460Smark * just like a snowflake 346460Smark */ 347460Smark 348460Smark #ifdef lint 349460Smark Ignore(a) 350460Smark int a; 351460Smark { 352460Smark 353460Smark a = a; 354460Smark } 355460Smark 356460Smark Ignorl(a) 357460Smark long a; 358460Smark { 359460Smark 360460Smark a = a; 361460Smark } 362460Smark #endif 363