147702Sbostic /*-
2*63051Sbostic * Copyright (c) 1991, 1993
3*63051Sbostic * The Regents of the University of California. All rights reserved.
447702Sbostic *
547702Sbostic * %sccs.include.redist.c%
621704Sdist */
721704Sdist
821704Sdist #ifndef lint
9*63051Sbostic static char copyright[] =
10*63051Sbostic "@(#) Copyright (c) 1991, 1993\n\
11*63051Sbostic The Regents of the University of California. All rights reserved.\n";
1247702Sbostic #endif /* not lint */
1321704Sdist
1421704Sdist #ifndef lint
15*63051Sbostic static char sccsid[] = "@(#)ex3.7preserve.c 8.1 (Berkeley) 06/09/93";
1647702Sbostic #endif /* not lint */
1721704Sdist
1812956Sralph #include <sys/param.h>
19460Smark #include <sys/stat.h>
2047702Sbostic #include <dirent.h>
2147702Sbostic #include <fcntl.h>
2247702Sbostic #include <time.h>
23460Smark #include <pwd.h>
2447702Sbostic #include <errno.h>
2547702Sbostic #include <unistd.h>
2637883Sbostic #include <stdio.h>
2737883Sbostic #include <ctype.h>
2847702Sbostic #include <stdlib.h>
2947702Sbostic #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
3747702Sbostic * _PATH_PRESERVE... this may mean that we leave stuff there...
3847702Sbostic * the danger in doing anything with _PATH_PRESERVE is that the
3947702Sbostic * clock may be screwed up and we may get confused.
40460Smark *
4147702Sbostic * We are called in two ways - first from the editor with no arguments
4247702Sbostic * and the standard input open on the temp file. Second with an
4347702Sbostic * 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
4947702Sbostic #ifdef VMUNIX
5047702Sbostic #define HBLKS 2
5147702Sbostic #define LBLKS 900
5247702Sbostic #else
5347702Sbostic #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 */
6147702Sbostic #ifdef VMUNIX
6247702Sbostic int Flines;
6347702Sbostic #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
main(argc,argv)7047702Sbostic main(argc, argv)
71460Smark int argc;
7247702Sbostic char **argv;
73460Smark {
7447702Sbostic extern int optind;
7512956Sralph register DIR *tf;
7647702Sbostic struct dirent *dirent;
77460Smark struct stat stbuf;
7847702Sbostic int aflag, ch, err;
7945858Skarels char path[MAXPATHLEN];
80460Smark
8147702Sbostic aflag = 0;
8247702Sbostic while ((ch = getopt(argc, argv, "a")) != EOF)
8347702Sbostic switch(ch) {
8447702Sbostic case 'a':
8547702Sbostic aflag = 1;
8647702Sbostic break;
8747702Sbostic case '?':
8847702Sbostic default:
8947702Sbostic usage();
9047702Sbostic }
9147702Sbostic argc -= optind;
9247702Sbostic argv += optind;
9345858Skarels
9447702Sbostic if (chdir(_PATH_PRESERVE) < 0)
9547702Sbostic error(_PATH_PRESERVE);
9647702Sbostic
97460Smark /*
98460Smark * If only one argument, then preserve the standard input.
99460Smark */
10047702Sbostic if (!aflag)
10147702Sbostic exit(copyout(NULL) ? 1 : 0);
102460Smark
103460Smark /*
104460Smark * If not super user, then can only preserve standard input.
105460Smark */
106460Smark if (getuid()) {
10747702Sbostic errno = EPERM;
10847702Sbostic error(NULL);
109460Smark }
110460Smark
111460Smark /*
11245858Skarels * ... else preserve all the stuff in /var/tmp, removing
113460Smark * it as we go.
114460Smark */
11547702Sbostic if (!(tf = opendir(_PATH_VARTMP)))
11647702Sbostic error(_PATH_VARTMP);
117460Smark
11847702Sbostic 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;
12347702Sbostic (void)sprintf(path, "%s/%s", _PATH_VARTMP, dirent->d_name);
12447702Sbostic if (stat(path, &stbuf) || !S_ISREG(stbuf.st_mode))
125460Smark continue;
126460Smark /*
127460Smark * Save the bastard.
128460Smark */
12945858Skarels err |= copyout(path);
130460Smark }
13147702Sbostic (void)closedir(tf);
13245858Skarels exit(err);
133460Smark }
134460Smark
copyout(name)135460Smark copyout(name)
136460Smark char *name;
137460Smark {
13847702Sbostic struct stat sb;
13947702Sbostic register int ifd, ofd, nr, nw, off, rval;
14047702Sbostic char buf[8*1024], fname[20];
141460Smark
14247702Sbostic /* Open any given file name. */
14347702Sbostic if (name) {
14447702Sbostic if ((ifd = open(name, O_RDWR)) < 0)
14547702Sbostic return(1);
14647702Sbostic (void)fstat(ifd, &sb);
14747702Sbostic if (!sb.st_size) {
14847702Sbostic (void)unlink(name);
14947702Sbostic return(0);
15047702Sbostic }
15147702Sbostic } else {
15247702Sbostic ifd = STDIN_FILENO;
15347702Sbostic /* vi hands us an fd, it's not necessarily at the beginning. */
15447702Sbostic (void)lseek(ifd, 0l, SEEK_SET);
155460Smark }
156460Smark
15747702Sbostic if (read(ifd, &H, sizeof(H)) != sizeof(H))
15847702Sbostic goto format;
159460Smark
160460Smark /*
16145858Skarels * Consistency checks so we don't copy out garbage.
162460Smark */
163460Smark if (H.Flines < 0) {
16447702Sbostic (void)fprintf(stderr,
16547702Sbostic "ex3.7preserve: negative number of lines\n");
166460Smark goto format;
167460Smark }
16847702Sbostic
169483Smark if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
17047702Sbostic (void)fprintf(stderr,
17147702Sbostic "ex3.7preserve: blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
172460Smark goto format;
173460Smark }
17447702Sbostic
17547702Sbostic if (!name && H.Uid != getuid()) {
17647702Sbostic (void)fprintf(stderr, "ex3.7preserve: wrong user-id\n");
177460Smark goto format;
178460Smark }
17947702Sbostic
18047702Sbostic if (lseek(ifd, 0l, SEEK_SET)) {
18147702Sbostic (void)fprintf(stderr,
18247702Sbostic "ex3.7preserve: negative number of lines\n");
18347702Sbostic format: (void)fprintf(stderr, "ex3.7preserve: %s\n", strerror(EFTYPE));
18447702Sbostic return (1);
185460Smark }
186460Smark
187460Smark /*
188460Smark * If no name was assigned to the file, then give it the name
18947702Sbostic * LOST, by putting this in the header. This involves overwriting
19047702Sbostic * the "input" file.
191460Smark */
192460Smark if (H.Savedfile[0] == 0) {
19347702Sbostic (void)strcpy(H.Savedfile, "LOST");
19447702Sbostic (void)write(ifd, &H, sizeof(H));
195460Smark H.Savedfile[0] = 0;
19647702Sbostic (void)lseek(ifd, 0l, SEEK_SET);
197460Smark }
198460Smark
19947702Sbostic /* File is good. Get a name and create a file for the copy. */
20047702Sbostic (void)strcpy(fname, "ExXXXXXX");
20147702Sbostic if ((ofd = mkstemp(fname)) == -1)
20247702Sbostic return(1);
203460Smark
20447702Sbostic /* Copy the file. */
20547702Sbostic rval = 0;
20647702Sbostic while ((nr = read(ifd, buf, sizeof(buf))) > 0)
20747702Sbostic for (off = 0; off < nr; nr -= nw, off += nw)
20847702Sbostic if ((nw = write(ofd, buf + off, nr)) < 0) {
20947702Sbostic (void)fprintf(stderr,
21047702Sbostic "ex3.7preserve: tmp file: %s\n",
21147702Sbostic strerror(errno));
21247702Sbostic rval = 1;
21347702Sbostic break;
21447702Sbostic }
21547702Sbostic if (nr < 0) {
21647702Sbostic (void)fprintf(stderr, "ex3.7preserve: %s: %s\n",
21747702Sbostic name ? name : "stdin", strerror(errno));
21847702Sbostic rval = 1;
219460Smark }
22047702Sbostic
22147702Sbostic if (rval)
22247702Sbostic (void)unlink(fname);
22347702Sbostic else {
22447702Sbostic (void)fchown(ofd, H.Uid, 0);
22547702Sbostic notify(H.Uid, H.Savedfile, (int)name, H.Time);
22647702Sbostic if (name)
22747702Sbostic (void)unlink(name);
22847702Sbostic }
22947702Sbostic (void)close(ifd);
23047702Sbostic (void)close(ofd);
23147702Sbostic return(rval);
232460Smark }
233460Smark
23447702Sbostic /* Notify user uid that his file fname has been saved. */
notify(uid,fname,flag,time)23516045Srrh notify(uid, fname, flag, time)
236460Smark int uid;
237460Smark char *fname;
23816045Srrh time_t time;
239460Smark {
24047702Sbostic struct passwd *pp;
241460Smark register FILE *mf;
24247702Sbostic static int reenter;
24347702Sbostic static char hostname[MAXHOSTNAMELEN], *timestamp;
24447702Sbostic char cmd[MAXPATHLEN + 50], croak[50];
245460Smark
24647702Sbostic pp = getpwuid(uid);
247460Smark if (pp == NULL)
248460Smark return;
24947702Sbostic
25047702Sbostic if (!reenter) {
25147702Sbostic reenter = 1;
25247702Sbostic (void)gethostname(hostname, sizeof(hostname));
25347702Sbostic timestamp = ctime(&time);
25447702Sbostic timestamp[16] = 0; /* blast from seconds on */
25547702Sbostic }
25647702Sbostic
25747702Sbostic (void)snprintf(cmd, sizeof(cmd),
25847702Sbostic "%s -i -t -F \"The Editor\" -f root", _PATH_SENDMAIL);
259460Smark mf = popen(cmd, "w");
260460Smark if (mf == NULL)
261460Smark return;
26247702Sbostic (void)fprintf(mf,
26347702Sbostic "Reply-To: root@%s\nFrom: root@%s (The Editor)\nTo: %s\n",
26447702Sbostic hostname, hostname, pp->pw_name);
26547702Sbostic
26616045Srrh /*
26747702Sbostic * flag says how the editor croaked: "the editor was killed" is
26847702Sbostic * perhaps still not an ideal error message. Usually, either it
26947702Sbostic * was forcably terminated or the phone was hung up, but we don't
27047702Sbostic * know which.
27116045Srrh */
27247702Sbostic (void)snprintf(croak, sizeof(croak),
27347702Sbostic flag ? "the system went down"
27447702Sbostic : "the editor was killed");
27547702Sbostic if (!fname || !fname[0]) {
27625941Sbloom fname = "LOST";
27747702Sbostic fprintf(mf, "Subject: editor saved \"LOST\"\n\n");
27847702Sbostic fprintf(mf, "You were editing a file without a name\n");
27947702Sbostic fprintf(mf, "at %s on the machine %s when %s.\n",
28047702Sbostic timestamp, hostname, croak);
281460Smark fprintf(mf,
28247702Sbostic "Since the file had no name, it has been named \"LOST\".\n");
28316045Srrh } else {
28447702Sbostic fprintf(mf, "Subject: editor saved \"%s\"\n\n", fname);
28547702Sbostic fprintf(mf, "You were editing the file %s\n", fname);
28647702Sbostic fprintf(mf, "at %s on the machine %s\n", timestamp, hostname);
28747702Sbostic fprintf(mf, "when %s.\n", croak);
28816045Srrh }
28947702Sbostic fprintf(mf, "\nYou can retrieve most of your changes to this file\n");
29047702Sbostic fprintf(mf, "using the \"recover\" command of the editor.\n");
291460Smark fprintf(mf,
29247702Sbostic "An easy way to do this is to give the command \"vi -r %s\".\n",
29347702Sbostic fname);
29447702Sbostic 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 */
error(msg)30347702Sbostic error(msg)
30447702Sbostic char *msg;
305460Smark {
30647702Sbostic (void)fprintf(stderr, "ex3.7preserve: ");
30747702Sbostic if (msg)
30847702Sbostic (void)fprintf(stderr, "%s: ", msg);
30947702Sbostic (void)fprintf(stderr, "%s\n", strerror(errno));
31047702Sbostic exit(1);
311460Smark }
312460Smark
usage()31347702Sbostic usage()
314460Smark {
31547702Sbostic (void)fprintf(stderr, "usage: ex3.7preserve [-a]\n");
31647702Sbostic exit(1);
317460Smark }
318