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*37883Sbostic static char *sccsid = "@(#)ex3.7preserve.c 7.14 (Berkeley) 05/11/89"; 1521704Sdist #endif not lint 1621704Sdist 1712956Sralph #include <sys/param.h> 18460Smark #include <sys/stat.h> 19460Smark #include <sys/dir.h> 20460Smark #include <pwd.h> 21*37883Sbostic #include <stdio.h> 22*37883Sbostic #include <ctype.h> 23*37883Sbostic #include "pathnames.h" 2413759Ssam 25483Smark #ifdef VMUNIX 26483Smark #define HBLKS 2 274001Smark #else 284001Smark #define HBLKS 1 29483Smark #endif 30483Smark 313995Smark char xstr[1]; /* make loader happy */ 323995Smark 33460Smark /* 34*37883Sbostic * Expreserve - preserve a file in _PATH_PRESERVE. 35460Smark * Bill Joy UCB November 13, 1977 36460Smark * 37460Smark * This routine is very naive - it doesn't remove anything from 38*37883Sbostic * _PATH_PRESERVE... this may mean that we leave 39*37883Sbostic * stuff there... the danger in doing anything with _PATH_PRESERVE 40460Smark * is that the clock may be screwed up and we may get confused. 41460Smark * 42460Smark * We are called in two ways - first from the editor with no argumentss 43460Smark * and the standard input open on the temp file. Second with an argument 44460Smark * to preserve the entire contents of /tmp (root only). 45460Smark * 46460Smark * BUG: should do something about preserving Rx... (register contents) 47460Smark * temporaries. 48460Smark */ 49460Smark 50483Smark #ifndef VMUNIX 51460Smark #define LBLKS 125 52483Smark #else 53483Smark #define LBLKS 900 54483Smark #endif 55460Smark #define FNSIZE 128 56460Smark 57460Smark struct header { 58460Smark time_t Time; /* Time temp file last updated */ 594051Smark int Uid; /* This users identity */ 60483Smark #ifndef VMUNIX 61460Smark short Flines; /* Number of lines in file */ 62483Smark #else 63483Smark int Flines; 64483Smark #endif 65460Smark char Savedfile[FNSIZE]; /* The current file name */ 66460Smark short Blocks[LBLKS]; /* Blocks where line pointers stashed */ 67460Smark } H; 68460Smark 69460Smark #ifdef lint 70460Smark #define ignore(a) Ignore(a) 71460Smark #define ignorl(a) Ignorl(a) 72460Smark #else 73460Smark #define ignore(a) a 74460Smark #define ignorl(a) a 75460Smark #endif 76460Smark 77460Smark struct passwd *getpwuid(); 78460Smark off_t lseek(); 79460Smark FILE *popen(); 80460Smark 81460Smark #define eq(a, b) strcmp(a, b) == 0 82460Smark 83460Smark main(argc) 84460Smark int argc; 85460Smark { 8612956Sralph register DIR *tf; 8712956Sralph struct direct *dirent; 88460Smark struct stat stbuf; 89460Smark 90460Smark /* 91460Smark * If only one argument, then preserve the standard input. 92460Smark */ 93460Smark if (argc == 1) { 94460Smark if (copyout((char *) 0)) 95460Smark exit(1); 96460Smark exit(0); 97460Smark } 98460Smark 99460Smark /* 100460Smark * If not super user, then can only preserve standard input. 101460Smark */ 102460Smark if (getuid()) { 103460Smark fprintf(stderr, "NOT super user\n"); 104460Smark exit(1); 105460Smark } 106460Smark 107460Smark /* 108460Smark * ... else preserve all the stuff in /tmp, removing 109460Smark * it as we go. 110460Smark */ 111*37883Sbostic if (chdir(_PATH_TMP) < 0) { 112*37883Sbostic perror(_PATH_TMP); 1133995Smark exit(1); 1143995Smark } 115460Smark 11612956Sralph tf = opendir("."); 117460Smark if (tf == NULL) { 118*37883Sbostic perror(_PATH_TMP); 119460Smark exit(1); 1203995Smark } 12112956Sralph while ((dirent = readdir(tf)) != NULL) { 12213759Ssam /* Ex temporaries must begin with Ex. */ 12313759Ssam if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x') 124460Smark continue; 12512956Sralph if (stat(dirent->d_name, &stbuf)) 126460Smark continue; 127460Smark if ((stbuf.st_mode & S_IFMT) != S_IFREG) 128460Smark continue; 129460Smark /* 130460Smark * Save the bastard. 131460Smark */ 13212956Sralph ignore(copyout(dirent->d_name)); 133460Smark } 13412956Sralph closedir(tf); 135460Smark exit(0); 136460Smark } 137460Smark 138*37883Sbostic char pattern[MAXPATHLEN]; 139460Smark 140460Smark /* 141*37883Sbostic * Copy file name into pattern[]. 142460Smark * If name is (char *) 0, then do the standard input. 143460Smark * We make some checks on the input to make sure it is 144460Smark * really an editor temporary, generate a name for the 145460Smark * file (this is the slowest thing since we must stat 146460Smark * to find a unique name), and finally copy the file. 147460Smark */ 148460Smark copyout(name) 149460Smark char *name; 150460Smark { 151460Smark int i; 152460Smark static int reenter; 153460Smark char buf[BUFSIZ]; 154460Smark 155*37883Sbostic (void)sprintf(pattern, "%s/Exaa`XXXXX", _PATH_PRESERVE); 156460Smark /* 157460Smark * The first time we put in the digits of our 158460Smark * process number at the end of the pattern. 159460Smark */ 160460Smark if (reenter == 0) { 161460Smark mkdigits(pattern); 162460Smark reenter++; 163460Smark } 164460Smark 165460Smark /* 166460Smark * If a file name was given, make it the standard 167460Smark * input if possible. 168460Smark */ 169460Smark if (name != 0) { 170460Smark ignore(close(0)); 171460Smark /* 172460Smark * Need read/write access for arcane reasons 173460Smark * (see below). 174460Smark */ 175460Smark if (open(name, 2) < 0) 176460Smark return (-1); 177460Smark } 178460Smark 179460Smark /* 180460Smark * Get the header block. 181460Smark */ 182460Smark ignorl(lseek(0, 0l, 0)); 183460Smark if (read(0, (char *) &H, sizeof H) != sizeof H) { 184460Smark format: 185460Smark if (name == 0) 1861540Smark fprintf(stderr, "Buffer format error\t"); 187460Smark return (-1); 188460Smark } 189460Smark 190460Smark /* 191460Smark * Consistency checsks so we don't copy out garbage. 192460Smark */ 193460Smark if (H.Flines < 0) { 194460Smark #ifdef DEBUG 195460Smark fprintf(stderr, "Negative number of lines\n"); 196460Smark #endif 197460Smark goto format; 198460Smark } 199483Smark if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) { 200460Smark #ifdef DEBUG 201460Smark fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]); 202460Smark #endif 203460Smark goto format; 204460Smark } 205460Smark if (name == 0 && H.Uid != getuid()) { 206460Smark #ifdef DEBUG 207460Smark fprintf(stderr, "Wrong user-id\n"); 208460Smark #endif 209460Smark goto format; 210460Smark } 211460Smark if (lseek(0, 0l, 0)) { 212460Smark #ifdef DEBUG 213460Smark fprintf(stderr, "Negative number of lines\n"); 214460Smark #endif 215460Smark goto format; 216460Smark } 217460Smark 218460Smark /* 219460Smark * If no name was assigned to the file, then give it the name 220460Smark * LOST, by putting this in the header. 221460Smark */ 222460Smark if (H.Savedfile[0] == 0) { 223460Smark strcpy(H.Savedfile, "LOST"); 224460Smark ignore(write(0, (char *) &H, sizeof H)); 225460Smark H.Savedfile[0] = 0; 226460Smark lseek(0, 0l, 0); 227460Smark } 228460Smark 229460Smark /* 230460Smark * File is good. Get a name and create a file for the copy. 231460Smark */ 232460Smark mknext(pattern); 233460Smark ignore(close(1)); 234460Smark if (creat(pattern, 0600) < 0) { 235460Smark if (name == 0) 236460Smark perror(pattern); 237460Smark return (1); 238460Smark } 239460Smark 240460Smark /* 241460Smark * Make the target be owned by the owner of the file. 242460Smark */ 243460Smark ignore(chown(pattern, H.Uid, 0)); 244460Smark 245460Smark /* 246460Smark * Copy the file. 247460Smark */ 248460Smark for (;;) { 249460Smark i = read(0, buf, BUFSIZ); 250460Smark if (i < 0) { 251460Smark if (name) 252460Smark perror("Buffer read error"); 253460Smark ignore(unlink(pattern)); 254460Smark return (-1); 255460Smark } 256460Smark if (i == 0) { 257460Smark if (name) 258460Smark ignore(unlink(name)); 25916045Srrh notify(H.Uid, H.Savedfile, (int) name, H.Time); 260460Smark return (0); 261460Smark } 262460Smark if (write(1, buf, i) != i) { 263460Smark if (name == 0) 264460Smark perror(pattern); 265460Smark unlink(pattern); 266460Smark return (-1); 267460Smark } 268460Smark } 269460Smark } 270460Smark 271460Smark /* 272460Smark * Blast the last 5 characters of cp to be the process number. 273460Smark */ 274460Smark mkdigits(cp) 275460Smark char *cp; 276460Smark { 277460Smark register int i, j; 278460Smark 279460Smark for (i = getpid(), j = 5, cp += strlen(cp); j > 0; i /= 10, j--) 280460Smark *--cp = i % 10 | '0'; 281460Smark } 282460Smark 283460Smark /* 284460Smark * Make the name in cp be unique by clobbering up to 285460Smark * three alphabetic characters into a sequence of the form 'aab', 'aac', etc. 286460Smark * Mktemp gets weird names too quickly to be useful here. 287460Smark */ 288460Smark mknext(cp) 289460Smark char *cp; 290460Smark { 291460Smark char *dcp; 292460Smark struct stat stb; 293460Smark 294460Smark dcp = cp + strlen(cp) - 1; 295460Smark while (isdigit(*dcp)) 296460Smark dcp--; 297460Smark whoops: 298460Smark if (dcp[0] == 'z') { 299460Smark dcp[0] = 'a'; 300460Smark if (dcp[-1] == 'z') { 301460Smark dcp[-1] = 'a'; 302460Smark if (dcp[-2] == 'z') 3031540Smark fprintf(stderr, "Can't find a name\t"); 304460Smark dcp[-2]++; 305460Smark } else 306460Smark dcp[-1]++; 307460Smark } else 308460Smark dcp[0]++; 309460Smark if (stat(cp, &stb) == 0) 310460Smark goto whoops; 311460Smark } 312460Smark 313460Smark /* 314460Smark * Notify user uid that his file fname has been saved. 315460Smark */ 31616045Srrh notify(uid, fname, flag, time) 317460Smark int uid; 318460Smark char *fname; 31916045Srrh time_t time; 320460Smark { 321460Smark struct passwd *pp = getpwuid(uid); 322460Smark register FILE *mf; 32316045Srrh char cmd[BUFSIZ]; 32416045Srrh char hostname[128]; 32516045Srrh char croak[128]; 32616045Srrh char *timestamp, *ctime(); 327460Smark 328460Smark if (pp == NULL) 329460Smark return; 33016045Srrh gethostname(hostname, sizeof(hostname)); 33116045Srrh timestamp = ctime(&time); 33216045Srrh timestamp[16] = 0; /* blast from seconds on */ 333*37883Sbostic sprintf(cmd, "%s %s", _PATH_BINMAIL, pp->pw_name); 33417462Sralph setuid(getuid()); 335460Smark mf = popen(cmd, "w"); 336460Smark if (mf == NULL) 337460Smark return; 338460Smark setbuf(mf, cmd); 33916045Srrh /* 34016045Srrh * flag says how the editor croaked: 34116045Srrh * "the editor was killed" is perhaps still not an ideal 34216045Srrh * error message. Usually, either it was forcably terminated 34316045Srrh * or the phone was hung up, but we don't know which. 34416045Srrh */ 34516045Srrh sprintf(croak, flag 34616045Srrh ? "the system went down" 34716045Srrh : "the editor was killed"); 348460Smark if (fname[0] == 0) { 34925941Sbloom fname = "LOST"; 350460Smark fprintf(mf, 35116045Srrh "Subject: editor saved ``LOST''\n"); 352460Smark fprintf(mf, 35316045Srrh "You were editing a file without a name\n"); 354460Smark fprintf(mf, 35516045Srrh "at <%s> on the machine ``%s'' when %s.\n", timestamp, hostname, croak); 35616045Srrh fprintf(mf, 35716045Srrh "Since the file had no name, it has been named \"LOST\".\n"); 35816045Srrh } else { 35916045Srrh fprintf(mf, 36016045Srrh "Subject: editor saved ``%s''\n", fname); 36116045Srrh fprintf(mf, 36216045Srrh "You were editing the file \"%s\"\n", fname); 36316045Srrh fprintf(mf, 36416045Srrh "at <%s> on the machine ``%s''\n", timestamp, hostname); 36516045Srrh fprintf(mf, 36616045Srrh "when %s.\n", croak); 36716045Srrh } 368460Smark fprintf(mf, 36916045Srrh "\nYou can retrieve most of your changes to this file\n"); 370460Smark fprintf(mf, 37116045Srrh "using the \"recover\" command of the editor.\n"); 372460Smark fprintf(mf, 37316045Srrh "An easy way to do this is to give the command \"vi -r %s\".\n", fname); 37416045Srrh fprintf(mf, 37516045Srrh "This method also works using \"ex\" and \"edit\".\n"); 376460Smark pclose(mf); 377460Smark } 378460Smark 379460Smark /* 380460Smark * people making love 381460Smark * never exactly the same 382460Smark * just like a snowflake 383460Smark */ 384460Smark 385460Smark #ifdef lint 386460Smark Ignore(a) 387460Smark int a; 388460Smark { 389460Smark 390460Smark a = a; 391460Smark } 392460Smark 393460Smark Ignorl(a) 394460Smark long a; 395460Smark { 396460Smark 397460Smark a = a; 398460Smark } 399460Smark #endif 400