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