1*47702Sbostic /*- 2*47702Sbostic * Copyright (c) 1991 The Regents of the University of California. 3*47702Sbostic * All rights reserved. 4*47702Sbostic * 5*47702Sbostic * %sccs.include.redist.c% 621704Sdist */ 721704Sdist 821704Sdist #ifndef lint 9*47702Sbostic char copyright[] = 10*47702Sbostic "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ 1121704Sdist All rights reserved.\n"; 12*47702Sbostic #endif /* not lint */ 1321704Sdist 1421704Sdist #ifndef lint 15*47702Sbostic static char sccsid[] = "@(#)ex3.7preserve.c 7.18 (Berkeley) 04/01/91"; 16*47702Sbostic #endif /* not lint */ 1721704Sdist 1812956Sralph #include <sys/param.h> 19460Smark #include <sys/stat.h> 20*47702Sbostic #include <dirent.h> 21*47702Sbostic #include <fcntl.h> 22*47702Sbostic #include <time.h> 23460Smark #include <pwd.h> 24*47702Sbostic #include <errno.h> 25*47702Sbostic #include <unistd.h> 2637883Sbostic #include <stdio.h> 2737883Sbostic #include <ctype.h> 28*47702Sbostic #include <stdlib.h> 29*47702Sbostic #include <string.h> 3037883Sbostic #include "pathnames.h" 3113759Ssam 32460Smark /* 3337883Sbostic * Expreserve - preserve a file in _PATH_PRESERVE. 34460Smark * Bill Joy UCB November 13, 1977 35460Smark * 36460Smark * This routine is very naive - it doesn't remove anything from 37*47702Sbostic * _PATH_PRESERVE... this may mean that we leave stuff there... 38*47702Sbostic * the danger in doing anything with _PATH_PRESERVE is that the 39*47702Sbostic * clock may be screwed up and we may get confused. 40460Smark * 41*47702Sbostic * We are called in two ways - first from the editor with no arguments 42*47702Sbostic * and the standard input open on the temp file. Second with an 43*47702Sbostic * argument to preserve the entire contents of _PATH_VARTMP (root only). 44460Smark * 45460Smark * BUG: should do something about preserving Rx... (register contents) 46460Smark * temporaries. 47460Smark */ 48460Smark 49*47702Sbostic #ifdef VMUNIX 50*47702Sbostic #define HBLKS 2 51*47702Sbostic #define LBLKS 900 52*47702Sbostic #else 53*47702Sbostic #define HBLKS 1 54460Smark #define LBLKS 125 55483Smark #endif 56460Smark #define FNSIZE 128 57460Smark 58460Smark struct header { 59460Smark time_t Time; /* Time temp file last updated */ 604051Smark int Uid; /* This users identity */ 61*47702Sbostic #ifdef VMUNIX 62*47702Sbostic int Flines; 63*47702Sbostic #else 64460Smark short Flines; /* Number of lines in file */ 65483Smark #endif 66460Smark char Savedfile[FNSIZE]; /* The current file name */ 67460Smark short Blocks[LBLKS]; /* Blocks where line pointers stashed */ 68460Smark } H; 69460Smark 70*47702Sbostic main(argc, argv) 71460Smark int argc; 72*47702Sbostic char **argv; 73460Smark { 74*47702Sbostic extern int optind; 7512956Sralph register DIR *tf; 76*47702Sbostic struct dirent *dirent; 77460Smark struct stat stbuf; 78*47702Sbostic int aflag, ch, err; 7945858Skarels char path[MAXPATHLEN]; 80460Smark 81*47702Sbostic aflag = 0; 82*47702Sbostic while ((ch = getopt(argc, argv, "a")) != EOF) 83*47702Sbostic switch(ch) { 84*47702Sbostic case 'a': 85*47702Sbostic aflag = 1; 86*47702Sbostic break; 87*47702Sbostic case '?': 88*47702Sbostic default: 89*47702Sbostic usage(); 90*47702Sbostic } 91*47702Sbostic argc -= optind; 92*47702Sbostic argv += optind; 9345858Skarels 94*47702Sbostic if (chdir(_PATH_PRESERVE) < 0) 95*47702Sbostic error(_PATH_PRESERVE); 96*47702Sbostic 97460Smark /* 98460Smark * If only one argument, then preserve the standard input. 99460Smark */ 100*47702Sbostic if (!aflag) 101*47702Sbostic exit(copyout(NULL) ? 1 : 0); 102460Smark 103460Smark /* 104460Smark * If not super user, then can only preserve standard input. 105460Smark */ 106460Smark if (getuid()) { 107*47702Sbostic errno = EPERM; 108*47702Sbostic error(NULL); 109460Smark } 110460Smark 111460Smark /* 11245858Skarels * ... else preserve all the stuff in /var/tmp, removing 113460Smark * it as we go. 114460Smark */ 115*47702Sbostic if (!(tf = opendir(_PATH_VARTMP))) 116*47702Sbostic error(_PATH_VARTMP); 117460Smark 118*47702Sbostic err = 0; 11912956Sralph while ((dirent = readdir(tf)) != NULL) { 12013759Ssam /* Ex temporaries must begin with Ex. */ 12113759Ssam if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x') 122460Smark continue; 123*47702Sbostic (void)sprintf(path, "%s/%s", _PATH_VARTMP, dirent->d_name); 124*47702Sbostic if (stat(path, &stbuf) || !S_ISREG(stbuf.st_mode)) 125460Smark continue; 126460Smark /* 127460Smark * Save the bastard. 128460Smark */ 12945858Skarels err |= copyout(path); 130460Smark } 131*47702Sbostic (void)closedir(tf); 13245858Skarels exit(err); 133460Smark } 134460Smark 135460Smark copyout(name) 136460Smark char *name; 137460Smark { 138*47702Sbostic struct stat sb; 139*47702Sbostic register int ifd, ofd, nr, nw, off, rval; 140*47702Sbostic char buf[8*1024], fname[20]; 141460Smark 142*47702Sbostic /* Open any given file name. */ 143*47702Sbostic if (name) { 144*47702Sbostic if ((ifd = open(name, O_RDWR)) < 0) 145*47702Sbostic return(1); 146*47702Sbostic (void)fstat(ifd, &sb); 147*47702Sbostic if (!sb.st_size) { 148*47702Sbostic (void)unlink(name); 149*47702Sbostic return(0); 150*47702Sbostic } 151*47702Sbostic } else { 152*47702Sbostic ifd = STDIN_FILENO; 153*47702Sbostic /* vi hands us an fd, it's not necessarily at the beginning. */ 154*47702Sbostic (void)lseek(ifd, 0l, SEEK_SET); 155460Smark } 156460Smark 157*47702Sbostic if (read(ifd, &H, sizeof(H)) != sizeof(H)) 158*47702Sbostic goto format; 159460Smark 160460Smark /* 16145858Skarels * Consistency checks so we don't copy out garbage. 162460Smark */ 163460Smark if (H.Flines < 0) { 164*47702Sbostic (void)fprintf(stderr, 165*47702Sbostic "ex3.7preserve: negative number of lines\n"); 166460Smark goto format; 167460Smark } 168*47702Sbostic 169483Smark if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) { 170*47702Sbostic (void)fprintf(stderr, 171*47702Sbostic "ex3.7preserve: blocks %d %d\n", H.Blocks[0], H.Blocks[1]); 172460Smark goto format; 173460Smark } 174*47702Sbostic 175*47702Sbostic if (!name && H.Uid != getuid()) { 176*47702Sbostic (void)fprintf(stderr, "ex3.7preserve: wrong user-id\n"); 177460Smark goto format; 178460Smark } 179*47702Sbostic 180*47702Sbostic if (lseek(ifd, 0l, SEEK_SET)) { 181*47702Sbostic (void)fprintf(stderr, 182*47702Sbostic "ex3.7preserve: negative number of lines\n"); 183*47702Sbostic format: (void)fprintf(stderr, "ex3.7preserve: %s\n", strerror(EFTYPE)); 184*47702Sbostic return (1); 185460Smark } 186460Smark 187460Smark /* 188460Smark * If no name was assigned to the file, then give it the name 189*47702Sbostic * LOST, by putting this in the header. This involves overwriting 190*47702Sbostic * the "input" file. 191460Smark */ 192460Smark if (H.Savedfile[0] == 0) { 193*47702Sbostic (void)strcpy(H.Savedfile, "LOST"); 194*47702Sbostic (void)write(ifd, &H, sizeof(H)); 195460Smark H.Savedfile[0] = 0; 196*47702Sbostic (void)lseek(ifd, 0l, SEEK_SET); 197460Smark } 198460Smark 199*47702Sbostic /* File is good. Get a name and create a file for the copy. */ 200*47702Sbostic (void)strcpy(fname, "ExXXXXXX"); 201*47702Sbostic if ((ofd = mkstemp(fname)) == -1) 202*47702Sbostic return(1); 203460Smark 204*47702Sbostic /* Copy the file. */ 205*47702Sbostic rval = 0; 206*47702Sbostic while ((nr = read(ifd, buf, sizeof(buf))) > 0) 207*47702Sbostic for (off = 0; off < nr; nr -= nw, off += nw) 208*47702Sbostic if ((nw = write(ofd, buf + off, nr)) < 0) { 209*47702Sbostic (void)fprintf(stderr, 210*47702Sbostic "ex3.7preserve: tmp file: %s\n", 211*47702Sbostic strerror(errno)); 212*47702Sbostic rval = 1; 213*47702Sbostic break; 214*47702Sbostic } 215*47702Sbostic if (nr < 0) { 216*47702Sbostic (void)fprintf(stderr, "ex3.7preserve: %s: %s\n", 217*47702Sbostic name ? name : "stdin", strerror(errno)); 218*47702Sbostic rval = 1; 219460Smark } 220*47702Sbostic 221*47702Sbostic if (rval) 222*47702Sbostic (void)unlink(fname); 223*47702Sbostic else { 224*47702Sbostic (void)fchown(ofd, H.Uid, 0); 225*47702Sbostic notify(H.Uid, H.Savedfile, (int)name, H.Time); 226*47702Sbostic if (name) 227*47702Sbostic (void)unlink(name); 228*47702Sbostic } 229*47702Sbostic (void)close(ifd); 230*47702Sbostic (void)close(ofd); 231*47702Sbostic return(rval); 232460Smark } 233460Smark 234*47702Sbostic /* Notify user uid that his file fname has been saved. */ 23516045Srrh notify(uid, fname, flag, time) 236460Smark int uid; 237460Smark char *fname; 23816045Srrh time_t time; 239460Smark { 240*47702Sbostic struct passwd *pp; 241460Smark register FILE *mf; 242*47702Sbostic static int reenter; 243*47702Sbostic static char hostname[MAXHOSTNAMELEN], *timestamp; 244*47702Sbostic char cmd[MAXPATHLEN + 50], croak[50]; 245460Smark 246*47702Sbostic pp = getpwuid(uid); 247460Smark if (pp == NULL) 248460Smark return; 249*47702Sbostic 250*47702Sbostic if (!reenter) { 251*47702Sbostic reenter = 1; 252*47702Sbostic (void)gethostname(hostname, sizeof(hostname)); 253*47702Sbostic timestamp = ctime(&time); 254*47702Sbostic timestamp[16] = 0; /* blast from seconds on */ 255*47702Sbostic } 256*47702Sbostic 257*47702Sbostic (void)snprintf(cmd, sizeof(cmd), 258*47702Sbostic "%s -i -t -F \"The Editor\" -f root", _PATH_SENDMAIL); 259460Smark mf = popen(cmd, "w"); 260460Smark if (mf == NULL) 261460Smark return; 262*47702Sbostic (void)fprintf(mf, 263*47702Sbostic "Reply-To: root@%s\nFrom: root@%s (The Editor)\nTo: %s\n", 264*47702Sbostic hostname, hostname, pp->pw_name); 265*47702Sbostic 26616045Srrh /* 267*47702Sbostic * flag says how the editor croaked: "the editor was killed" is 268*47702Sbostic * perhaps still not an ideal error message. Usually, either it 269*47702Sbostic * was forcably terminated or the phone was hung up, but we don't 270*47702Sbostic * know which. 27116045Srrh */ 272*47702Sbostic (void)snprintf(croak, sizeof(croak), 273*47702Sbostic flag ? "the system went down" 274*47702Sbostic : "the editor was killed"); 275*47702Sbostic if (!fname || !fname[0]) { 27625941Sbloom fname = "LOST"; 277*47702Sbostic fprintf(mf, "Subject: editor saved \"LOST\"\n\n"); 278*47702Sbostic fprintf(mf, "You were editing a file without a name\n"); 279*47702Sbostic fprintf(mf, "at %s on the machine %s when %s.\n", 280*47702Sbostic timestamp, hostname, croak); 281460Smark fprintf(mf, 282*47702Sbostic "Since the file had no name, it has been named \"LOST\".\n"); 28316045Srrh } else { 284*47702Sbostic fprintf(mf, "Subject: editor saved \"%s\"\n\n", fname); 285*47702Sbostic fprintf(mf, "You were editing the file %s\n", fname); 286*47702Sbostic fprintf(mf, "at %s on the machine %s\n", timestamp, hostname); 287*47702Sbostic fprintf(mf, "when %s.\n", croak); 28816045Srrh } 289*47702Sbostic fprintf(mf, "\nYou can retrieve most of your changes to this file\n"); 290*47702Sbostic fprintf(mf, "using the \"recover\" command of the editor.\n"); 291460Smark fprintf(mf, 292*47702Sbostic "An easy way to do this is to give the command \"vi -r %s\".\n", 293*47702Sbostic fname); 294*47702Sbostic fprintf(mf, "This method also works using \"ex\" and \"edit\".\n\n"); 295460Smark pclose(mf); 296460Smark } 297460Smark 298460Smark /* 299460Smark * people making love 300460Smark * never exactly the same 301460Smark * just like a snowflake 302460Smark */ 303*47702Sbostic error(msg) 304*47702Sbostic char *msg; 305460Smark { 306*47702Sbostic (void)fprintf(stderr, "ex3.7preserve: "); 307*47702Sbostic if (msg) 308*47702Sbostic (void)fprintf(stderr, "%s: ", msg); 309*47702Sbostic (void)fprintf(stderr, "%s\n", strerror(errno)); 310*47702Sbostic exit(1); 311460Smark } 312460Smark 313*47702Sbostic usage() 314460Smark { 315*47702Sbostic (void)fprintf(stderr, "usage: ex3.7preserve [-a]\n"); 316*47702Sbostic exit(1); 317460Smark } 318