121704Sdist /* 221704Sdist * Copyright (c) 1980 Regents of the University of California. 321704Sdist * All rights reserved. The Berkeley software License Agreement 421704Sdist * specifies the terms and conditions for redistribution. 521704Sdist */ 621704Sdist 721704Sdist #ifndef lint 822752Sbloom char *copyright = 921704Sdist "@(#) Copyright (c) 1980 Regents of the University of California.\n\ 1021704Sdist All rights reserved.\n"; 1121704Sdist #endif not lint 1221704Sdist 1321704Sdist #ifndef lint 14*25941Sbloom static char *sccsid = "@(#)ex3.7preserve.c 7.13 (Berkeley) 01/22/86"; 1521704Sdist #endif not lint 1621704Sdist 17460Smark #include <stdio.h> 18460Smark #include <ctype.h> 1912956Sralph #include <sys/param.h> 20460Smark #include <sys/stat.h> 21460Smark #include <sys/dir.h> 22460Smark #include <pwd.h> 2313759Ssam #include "uparm.h" 2413759Ssam 253995Smark #define TMP "/tmp" 26460Smark 27483Smark #ifdef VMUNIX 28483Smark #define HBLKS 2 294001Smark #else 304001Smark #define HBLKS 1 31483Smark #endif 32483Smark 333995Smark char xstr[1]; /* make loader happy */ 343995Smark 35460Smark /* 36460Smark * Expreserve - preserve a file in usrpath(preserve) 37460Smark * Bill Joy UCB November 13, 1977 38460Smark * 39460Smark * This routine is very naive - it doesn't remove anything from 4013759Ssam * usrpath(preserve)... this may mean that we leave 4113759Ssam * stuff there... the danger in doing anything with usrpath(preserve) 42460Smark * is that the clock may be screwed up and we may get confused. 43460Smark * 44460Smark * We are called in two ways - first from the editor with no argumentss 45460Smark * and the standard input open on the temp file. Second with an argument 46460Smark * to preserve the entire contents of /tmp (root only). 47460Smark * 48460Smark * BUG: should do something about preserving Rx... (register contents) 49460Smark * temporaries. 50460Smark */ 51460Smark 52483Smark #ifndef VMUNIX 53460Smark #define LBLKS 125 54483Smark #else 55483Smark #define LBLKS 900 56483Smark #endif 57460Smark #define FNSIZE 128 58460Smark 59460Smark struct header { 60460Smark time_t Time; /* Time temp file last updated */ 614051Smark int Uid; /* This users identity */ 62483Smark #ifndef VMUNIX 63460Smark short Flines; /* Number of lines in file */ 64483Smark #else 65483Smark int Flines; 66483Smark #endif 67460Smark char Savedfile[FNSIZE]; /* The current file name */ 68460Smark short Blocks[LBLKS]; /* Blocks where line pointers stashed */ 69460Smark } H; 70460Smark 71460Smark #ifdef lint 72460Smark #define ignore(a) Ignore(a) 73460Smark #define ignorl(a) Ignorl(a) 74460Smark #else 75460Smark #define ignore(a) a 76460Smark #define ignorl(a) a 77460Smark #endif 78460Smark 79460Smark struct passwd *getpwuid(); 80460Smark off_t lseek(); 81460Smark FILE *popen(); 82460Smark 83460Smark #define eq(a, b) strcmp(a, b) == 0 84460Smark 85460Smark main(argc) 86460Smark int argc; 87460Smark { 8812956Sralph register DIR *tf; 8912956Sralph struct direct *dirent; 90460Smark struct stat stbuf; 91460Smark 92460Smark /* 93460Smark * If only one argument, then preserve the standard input. 94460Smark */ 95460Smark if (argc == 1) { 96460Smark if (copyout((char *) 0)) 97460Smark exit(1); 98460Smark exit(0); 99460Smark } 100460Smark 101460Smark /* 102460Smark * If not super user, then can only preserve standard input. 103460Smark */ 104460Smark if (getuid()) { 105460Smark fprintf(stderr, "NOT super user\n"); 106460Smark exit(1); 107460Smark } 108460Smark 109460Smark /* 110460Smark * ... else preserve all the stuff in /tmp, removing 111460Smark * it as we go. 112460Smark */ 1133979Smark if (chdir(TMP) < 0) { 1143979Smark perror(TMP); 1153995Smark exit(1); 1163995Smark } 117460Smark 11812956Sralph tf = opendir("."); 119460Smark if (tf == NULL) { 1203995Smark perror(TMP); 121460Smark exit(1); 1223995Smark } 12312956Sralph while ((dirent = readdir(tf)) != NULL) { 12413759Ssam /* Ex temporaries must begin with Ex. */ 12513759Ssam if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x') 126460Smark continue; 12712956Sralph if (stat(dirent->d_name, &stbuf)) 128460Smark continue; 129460Smark if ((stbuf.st_mode & S_IFMT) != S_IFREG) 130460Smark continue; 131460Smark /* 132460Smark * Save the bastard. 133460Smark */ 13412956Sralph ignore(copyout(dirent->d_name)); 135460Smark } 13612956Sralph closedir(tf); 137460Smark exit(0); 138460Smark } 139460Smark 140460Smark char pattern[] = usrpath(preserve/Exaa`XXXXX); 141460Smark 142460Smark /* 143460Smark * Copy file name into usrpath(preserve)/... 144460Smark * If name is (char *) 0, then do the standard input. 145460Smark * We make some checks on the input to make sure it is 146460Smark * really an editor temporary, generate a name for the 147460Smark * file (this is the slowest thing since we must stat 148460Smark * to find a unique name), and finally copy the file. 149460Smark */ 150460Smark copyout(name) 151460Smark char *name; 152460Smark { 153460Smark int i; 154460Smark static int reenter; 155460Smark char buf[BUFSIZ]; 156460Smark 157460Smark /* 158460Smark * The first time we put in the digits of our 159460Smark * process number at the end of the pattern. 160460Smark */ 161460Smark if (reenter == 0) { 162460Smark mkdigits(pattern); 163460Smark reenter++; 164460Smark } 165460Smark 166460Smark /* 167460Smark * If a file name was given, make it the standard 168460Smark * input if possible. 169460Smark */ 170460Smark if (name != 0) { 171460Smark ignore(close(0)); 172460Smark /* 173460Smark * Need read/write access for arcane reasons 174460Smark * (see below). 175460Smark */ 176460Smark if (open(name, 2) < 0) 177460Smark return (-1); 178460Smark } 179460Smark 180460Smark /* 181460Smark * Get the header block. 182460Smark */ 183460Smark ignorl(lseek(0, 0l, 0)); 184460Smark if (read(0, (char *) &H, sizeof H) != sizeof H) { 185460Smark format: 186460Smark if (name == 0) 1871540Smark fprintf(stderr, "Buffer format error\t"); 188460Smark return (-1); 189460Smark } 190460Smark 191460Smark /* 192460Smark * Consistency checsks so we don't copy out garbage. 193460Smark */ 194460Smark if (H.Flines < 0) { 195460Smark #ifdef DEBUG 196460Smark fprintf(stderr, "Negative number of lines\n"); 197460Smark #endif 198460Smark goto format; 199460Smark } 200483Smark if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) { 201460Smark #ifdef DEBUG 202460Smark fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]); 203460Smark #endif 204460Smark goto format; 205460Smark } 206460Smark if (name == 0 && H.Uid != getuid()) { 207460Smark #ifdef DEBUG 208460Smark fprintf(stderr, "Wrong user-id\n"); 209460Smark #endif 210460Smark goto format; 211460Smark } 212460Smark if (lseek(0, 0l, 0)) { 213460Smark #ifdef DEBUG 214460Smark fprintf(stderr, "Negative number of lines\n"); 215460Smark #endif 216460Smark goto format; 217460Smark } 218460Smark 219460Smark /* 220460Smark * If no name was assigned to the file, then give it the name 221460Smark * LOST, by putting this in the header. 222460Smark */ 223460Smark if (H.Savedfile[0] == 0) { 224460Smark strcpy(H.Savedfile, "LOST"); 225460Smark ignore(write(0, (char *) &H, sizeof H)); 226460Smark H.Savedfile[0] = 0; 227460Smark lseek(0, 0l, 0); 228460Smark } 229460Smark 230460Smark /* 231460Smark * File is good. Get a name and create a file for the copy. 232460Smark */ 233460Smark mknext(pattern); 234460Smark ignore(close(1)); 235460Smark if (creat(pattern, 0600) < 0) { 236460Smark if (name == 0) 237460Smark perror(pattern); 238460Smark return (1); 239460Smark } 240460Smark 241460Smark /* 242460Smark * Make the target be owned by the owner of the file. 243460Smark */ 244460Smark ignore(chown(pattern, H.Uid, 0)); 245460Smark 246460Smark /* 247460Smark * Copy the file. 248460Smark */ 249460Smark for (;;) { 250460Smark i = read(0, buf, BUFSIZ); 251460Smark if (i < 0) { 252460Smark if (name) 253460Smark perror("Buffer read error"); 254460Smark ignore(unlink(pattern)); 255460Smark return (-1); 256460Smark } 257460Smark if (i == 0) { 258460Smark if (name) 259460Smark ignore(unlink(name)); 26016045Srrh notify(H.Uid, H.Savedfile, (int) name, H.Time); 261460Smark return (0); 262460Smark } 263460Smark if (write(1, buf, i) != i) { 264460Smark if (name == 0) 265460Smark perror(pattern); 266460Smark unlink(pattern); 267460Smark return (-1); 268460Smark } 269460Smark } 270460Smark } 271460Smark 272460Smark /* 273460Smark * Blast the last 5 characters of cp to be the process number. 274460Smark */ 275460Smark mkdigits(cp) 276460Smark char *cp; 277460Smark { 278460Smark register int i, j; 279460Smark 280460Smark for (i = getpid(), j = 5, cp += strlen(cp); j > 0; i /= 10, j--) 281460Smark *--cp = i % 10 | '0'; 282460Smark } 283460Smark 284460Smark /* 285460Smark * Make the name in cp be unique by clobbering up to 286460Smark * three alphabetic characters into a sequence of the form 'aab', 'aac', etc. 287460Smark * Mktemp gets weird names too quickly to be useful here. 288460Smark */ 289460Smark mknext(cp) 290460Smark char *cp; 291460Smark { 292460Smark char *dcp; 293460Smark struct stat stb; 294460Smark 295460Smark dcp = cp + strlen(cp) - 1; 296460Smark while (isdigit(*dcp)) 297460Smark dcp--; 298460Smark whoops: 299460Smark if (dcp[0] == 'z') { 300460Smark dcp[0] = 'a'; 301460Smark if (dcp[-1] == 'z') { 302460Smark dcp[-1] = 'a'; 303460Smark if (dcp[-2] == 'z') 3041540Smark fprintf(stderr, "Can't find a name\t"); 305460Smark dcp[-2]++; 306460Smark } else 307460Smark dcp[-1]++; 308460Smark } else 309460Smark dcp[0]++; 310460Smark if (stat(cp, &stb) == 0) 311460Smark goto whoops; 312460Smark } 313460Smark 314460Smark /* 315460Smark * Notify user uid that his file fname has been saved. 316460Smark */ 31716045Srrh notify(uid, fname, flag, time) 318460Smark int uid; 319460Smark char *fname; 32016045Srrh time_t time; 321460Smark { 322460Smark struct passwd *pp = getpwuid(uid); 323460Smark register FILE *mf; 32416045Srrh char cmd[BUFSIZ]; 32516045Srrh char hostname[128]; 32616045Srrh char croak[128]; 32716045Srrh char *timestamp, *ctime(); 328460Smark 329460Smark if (pp == NULL) 330460Smark return; 33116045Srrh gethostname(hostname, sizeof(hostname)); 33216045Srrh timestamp = ctime(&time); 33316045Srrh timestamp[16] = 0; /* blast from seconds on */ 33414625Smckusick sprintf(cmd, "/bin/mail %s", pp->pw_name); 33517462Sralph setuid(getuid()); 336460Smark mf = popen(cmd, "w"); 337460Smark if (mf == NULL) 338460Smark return; 339460Smark setbuf(mf, cmd); 34016045Srrh /* 34116045Srrh * flag says how the editor croaked: 34216045Srrh * "the editor was killed" is perhaps still not an ideal 34316045Srrh * error message. Usually, either it was forcably terminated 34416045Srrh * or the phone was hung up, but we don't know which. 34516045Srrh */ 34616045Srrh sprintf(croak, flag 34716045Srrh ? "the system went down" 34816045Srrh : "the editor was killed"); 349460Smark if (fname[0] == 0) { 350*25941Sbloom fname = "LOST"; 351460Smark fprintf(mf, 35216045Srrh "Subject: editor saved ``LOST''\n"); 353460Smark fprintf(mf, 35416045Srrh "You were editing a file without a name\n"); 355460Smark fprintf(mf, 35616045Srrh "at <%s> on the machine ``%s'' when %s.\n", timestamp, hostname, croak); 35716045Srrh fprintf(mf, 35816045Srrh "Since the file had no name, it has been named \"LOST\".\n"); 35916045Srrh } else { 36016045Srrh fprintf(mf, 36116045Srrh "Subject: editor saved ``%s''\n", fname); 36216045Srrh fprintf(mf, 36316045Srrh "You were editing the file \"%s\"\n", fname); 36416045Srrh fprintf(mf, 36516045Srrh "at <%s> on the machine ``%s''\n", timestamp, hostname); 36616045Srrh fprintf(mf, 36716045Srrh "when %s.\n", croak); 36816045Srrh } 369460Smark fprintf(mf, 37016045Srrh "\nYou can retrieve most of your changes to this file\n"); 371460Smark fprintf(mf, 37216045Srrh "using the \"recover\" command of the editor.\n"); 373460Smark fprintf(mf, 37416045Srrh "An easy way to do this is to give the command \"vi -r %s\".\n", fname); 37516045Srrh fprintf(mf, 37616045Srrh "This method also works using \"ex\" and \"edit\".\n"); 377460Smark pclose(mf); 378460Smark } 379460Smark 380460Smark /* 381460Smark * people making love 382460Smark * never exactly the same 383460Smark * just like a snowflake 384460Smark */ 385460Smark 386460Smark #ifdef lint 387460Smark Ignore(a) 388460Smark int a; 389460Smark { 390460Smark 391460Smark a = a; 392460Smark } 393460Smark 394460Smark Ignorl(a) 395460Smark long a; 396460Smark { 397460Smark 398460Smark a = a; 399460Smark } 400460Smark #endif 401