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