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