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