1babdf84bSdist /*
2*a87ca55fSbostic * Copyright (c) 1983, 1993
3*a87ca55fSbostic * The Regents of the University of California. All rights reserved.
47a864411Sbostic *
54ec22e22Sbostic * %sccs.include.redist.c%
6babdf84bSdist */
7babdf84bSdist
8978e44c9Sralph #ifndef lint
9*a87ca55fSbostic static char sccsid[] = "@(#)server.c 8.1 (Berkeley) 06/09/93";
107a864411Sbostic #endif /* not lint */
11978e44c9Sralph
12bdfadfffSbostic #include <sys/wait.h>
13978e44c9Sralph #include "defs.h"
14978e44c9Sralph
156d401060Sralph #define ack() (void) write(rem, "\0\n", 2)
166d401060Sralph #define err() (void) write(rem, "\1\n", 2)
17978e44c9Sralph
182cb25675Sralph struct linkbuf *ihead; /* list of files with more than one link */
19d49851feSralph char buf[BUFSIZ]; /* general purpose buffer */
20978e44c9Sralph char target[BUFSIZ]; /* target/source directory name */
21978e44c9Sralph char *tp; /* pointer to end of target name */
2226ce117eSsklower char *Tdest; /* pointer to last T dest*/
23978e44c9Sralph int catname; /* cat name to target name */
2465a0d230Sralph char *stp[32]; /* stack of saved tp's for directories */
2501de96ecSralph int oumask; /* old umask for creating files */
26d49851feSralph
27d49851feSralph extern FILE *lfp; /* log file for mailing changes */
28d49851feSralph
29bdfadfffSbostic static int chkparent __P((char *));
30bdfadfffSbostic static void clean __P((char *));
31bdfadfffSbostic static void comment __P((char *));
32bdfadfffSbostic static void dospecial __P((char *));
33bdfadfffSbostic static int fchog __P((int, char *, char *, char *, int));
34bdfadfffSbostic static void hardlink __P((char *));
35bdfadfffSbostic static void note __P((const char *, ...));
36bdfadfffSbostic static void query __P((char *));
37bdfadfffSbostic static void recvf __P((char *, int));
38bdfadfffSbostic static void removeit __P((struct stat *));
39bdfadfffSbostic static int response __P((void));
40bdfadfffSbostic static void rmchk __P((int));
41bdfadfffSbostic static struct linkbuf *
42bdfadfffSbostic savelink __P((struct stat *));
43bdfadfffSbostic static void sendf __P((char *, int));
44bdfadfffSbostic static int update __P((char *, int, struct stat *));
45aca7acf4Sralph
46978e44c9Sralph /*
47978e44c9Sralph * Server routine to read requests and process them.
48978e44c9Sralph * Commands are:
49978e44c9Sralph * Tname - Transmit file if out of date
50978e44c9Sralph * Vname - Verify if file out of date or not
51978e44c9Sralph * Qname - Query if file exists. Return mtime & size if it does.
52978e44c9Sralph */
53bdfadfffSbostic void
server()54978e44c9Sralph server()
55978e44c9Sralph {
56978e44c9Sralph char cmdbuf[BUFSIZ];
57978e44c9Sralph register char *cp;
58978e44c9Sralph
59aca7acf4Sralph signal(SIGHUP, cleanup);
60aca7acf4Sralph signal(SIGINT, cleanup);
61aca7acf4Sralph signal(SIGQUIT, cleanup);
62aca7acf4Sralph signal(SIGTERM, cleanup);
63aca7acf4Sralph signal(SIGPIPE, cleanup);
64aca7acf4Sralph
65aca7acf4Sralph rem = 0;
6601de96ecSralph oumask = umask(0);
6736a9f5b1Sralph (void) sprintf(buf, "V%d\n", VERSION);
6836a9f5b1Sralph (void) write(rem, buf, strlen(buf));
69978e44c9Sralph
70978e44c9Sralph for (;;) {
71978e44c9Sralph cp = cmdbuf;
72978e44c9Sralph if (read(rem, cp, 1) <= 0)
73978e44c9Sralph return;
74978e44c9Sralph if (*cp++ == '\n') {
7501de96ecSralph error("server: expected control record\n");
76978e44c9Sralph continue;
77978e44c9Sralph }
78978e44c9Sralph do {
79978e44c9Sralph if (read(rem, cp, 1) != 1)
80bdfadfffSbostic cleanup(0);
81d49851feSralph } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
82978e44c9Sralph *--cp = '\0';
83978e44c9Sralph cp = cmdbuf;
84978e44c9Sralph switch (*cp++) {
85978e44c9Sralph case 'T': /* init target file/directory name */
86d49851feSralph catname = 1; /* target should be directory */
87d49851feSralph goto dotarget;
88d49851feSralph
89d49851feSralph case 't': /* init target file/directory name */
90978e44c9Sralph catname = 0;
91d49851feSralph dotarget:
9201de96ecSralph if (exptilde(target, cp) == NULL)
9301de96ecSralph continue;
94978e44c9Sralph tp = target;
95978e44c9Sralph while (*tp)
96978e44c9Sralph tp++;
976d401060Sralph ack();
98978e44c9Sralph continue;
99978e44c9Sralph
1002cb25675Sralph case 'R': /* Transfer a regular file. */
1012cb25675Sralph recvf(cp, S_IFREG);
102978e44c9Sralph continue;
103978e44c9Sralph
1042cb25675Sralph case 'D': /* Transfer a directory. */
1052cb25675Sralph recvf(cp, S_IFDIR);
1062cb25675Sralph continue;
1072cb25675Sralph
1082cb25675Sralph case 'K': /* Transfer symbolic link. */
1092cb25675Sralph recvf(cp, S_IFLNK);
1102cb25675Sralph continue;
1112cb25675Sralph
1122cb25675Sralph case 'k': /* Transfer hard link. */
1132cb25675Sralph hardlink(cp);
114978e44c9Sralph continue;
115978e44c9Sralph
116978e44c9Sralph case 'E': /* End. (of directory) */
117978e44c9Sralph *tp = '\0';
11801de96ecSralph if (catname <= 0) {
11901de96ecSralph error("server: too many 'E's\n");
120978e44c9Sralph continue;
121978e44c9Sralph }
12201de96ecSralph tp = stp[--catname];
12365a0d230Sralph *tp = '\0';
1246d401060Sralph ack();
125978e44c9Sralph continue;
126978e44c9Sralph
127c27256c0Sralph case 'C': /* Clean. Cleanup a directory */
1286d401060Sralph clean(cp);
129c27256c0Sralph continue;
130c27256c0Sralph
13101de96ecSralph case 'Q': /* Query. Does the file/directory exist? */
13201de96ecSralph query(cp);
13365a0d230Sralph continue;
13465a0d230Sralph
1356d401060Sralph case 'S': /* Special. Execute commands */
1366d401060Sralph dospecial(cp);
1376d401060Sralph continue;
1386d401060Sralph
13901de96ecSralph #ifdef notdef
14001de96ecSralph /*
14101de96ecSralph * These entries are reserved but not currently used.
14201de96ecSralph * The intent is to allow remote hosts to have master copies.
14301de96ecSralph * Currently, only the host rdist runs on can have masters.
14401de96ecSralph */
14501de96ecSralph case 'X': /* start a new list of files to exclude */
14601de96ecSralph except = bp = NULL;
14701de96ecSralph case 'x': /* add name to list of files to exclude */
14801de96ecSralph if (*cp == '\0') {
1496d401060Sralph ack();
15001de96ecSralph continue;
15101de96ecSralph }
15201de96ecSralph if (*cp == '~') {
15301de96ecSralph if (exptilde(buf, cp) == NULL)
15401de96ecSralph continue;
15501de96ecSralph cp = buf;
15601de96ecSralph }
15701de96ecSralph if (bp == NULL)
1586d401060Sralph except = bp = expand(makeblock(NAME, cp), E_VARS);
15901de96ecSralph else
1606d401060Sralph bp->b_next = expand(makeblock(NAME, cp), E_VARS);
16101de96ecSralph while (bp->b_next != NULL)
16201de96ecSralph bp = bp->b_next;
1636d401060Sralph ack();
16401de96ecSralph continue;
16501de96ecSralph
1666d401060Sralph case 'I': /* Install. Transfer file if out of date. */
16701de96ecSralph opts = 0;
16801de96ecSralph while (*cp >= '0' && *cp <= '7')
16901de96ecSralph opts = (opts << 3) | (*cp++ - '0');
17001de96ecSralph if (*cp++ != ' ') {
17101de96ecSralph error("server: options not delimited\n");
17201de96ecSralph return;
17301de96ecSralph }
1746d401060Sralph install(cp, opts);
175978e44c9Sralph continue;
176978e44c9Sralph
177978e44c9Sralph case 'L': /* Log. save message in log file */
17865a0d230Sralph log(lfp, cp);
179978e44c9Sralph continue;
18001de96ecSralph #endif
181978e44c9Sralph
182d49851feSralph case '\1':
183aca7acf4Sralph nerrs++;
184d49851feSralph continue;
185d49851feSralph
186d49851feSralph case '\2':
187d49851feSralph return;
188d49851feSralph
189978e44c9Sralph default:
19001de96ecSralph error("server: unknown command '%s'\n", cp);
191978e44c9Sralph case '\0':
192978e44c9Sralph continue;
193978e44c9Sralph }
194978e44c9Sralph }
195978e44c9Sralph }
196978e44c9Sralph
197978e44c9Sralph /*
19801de96ecSralph * Update the file(s) if they are different.
19901de96ecSralph * destdir = 1 if destination should be a directory
20001de96ecSralph * (i.e., more than one source is being copied to the same destination).
201978e44c9Sralph */
202bdfadfffSbostic void
install(src,dest,destdir,opts)20301de96ecSralph install(src, dest, destdir, opts)
20401de96ecSralph char *src, *dest;
20501de96ecSralph int destdir, opts;
20601de96ecSralph {
20701de96ecSralph char *rname;
20826ce117eSsklower char destcopy[BUFSIZ];
20901de96ecSralph
21001de96ecSralph if (dest == NULL) {
21101de96ecSralph opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
21201de96ecSralph dest = src;
21301de96ecSralph }
21401de96ecSralph
21501de96ecSralph if (nflag || debug) {
21601de96ecSralph printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
21701de96ecSralph opts & WHOLE ? " -w" : "",
21801de96ecSralph opts & YOUNGER ? " -y" : "",
21901de96ecSralph opts & COMPARE ? " -b" : "",
2206d401060Sralph opts & REMOVE ? " -R" : "", src, dest);
22101de96ecSralph if (nflag)
22201de96ecSralph return;
22301de96ecSralph }
22401de96ecSralph
22501de96ecSralph rname = exptilde(target, src);
22601de96ecSralph if (rname == NULL)
22701de96ecSralph return;
22801de96ecSralph tp = target;
22901de96ecSralph while (*tp)
23001de96ecSralph tp++;
23101de96ecSralph /*
23201de96ecSralph * If we are renaming a directory and we want to preserve
2336d401060Sralph * the directory heirarchy (-w), we must strip off the leading
23401de96ecSralph * directory name and preserve the rest.
23501de96ecSralph */
23601de96ecSralph if (opts & WHOLE) {
2376d401060Sralph while (*rname == '/')
23801de96ecSralph rname++;
2396d401060Sralph destdir = 1;
24001de96ecSralph } else {
24101de96ecSralph rname = rindex(target, '/');
24201de96ecSralph if (rname == NULL)
24301de96ecSralph rname = target;
24401de96ecSralph else
24501de96ecSralph rname++;
24601de96ecSralph }
24701de96ecSralph if (debug)
24801de96ecSralph printf("target = %s, rname = %s\n", target, rname);
24901de96ecSralph /*
25001de96ecSralph * Pass the destination file/directory name to remote.
25101de96ecSralph */
25201de96ecSralph (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest);
25301de96ecSralph if (debug)
25401de96ecSralph printf("buf = %s", buf);
25501de96ecSralph (void) write(rem, buf, strlen(buf));
25601de96ecSralph if (response() < 0)
25701de96ecSralph return;
25801de96ecSralph
25926ce117eSsklower if (destdir) {
26026ce117eSsklower strcpy(destcopy, dest);
26126ce117eSsklower Tdest = destcopy;
26226ce117eSsklower }
26301de96ecSralph sendf(rname, opts);
26426ce117eSsklower Tdest = 0;
26501de96ecSralph }
26601de96ecSralph
26726ce117eSsklower #define protoname() (pw ? pw->pw_name : user)
26826ce117eSsklower #define protogroup() (gr ? gr->gr_name : group)
26901de96ecSralph /*
27001de96ecSralph * Transfer the file or directory in target[].
27101de96ecSralph * rname is the name of the file on the remote host.
27201de96ecSralph */
273bdfadfffSbostic static void
sendf(rname,opts)27401de96ecSralph sendf(rname, opts)
27501de96ecSralph char *rname;
276ad3d87d9Sralph int opts;
277978e44c9Sralph {
278aca7acf4Sralph register struct subcmd *sc;
279978e44c9Sralph struct stat stb;
2802cb25675Sralph int sizerr, f, u, len;
281978e44c9Sralph off_t i;
2822cb25675Sralph DIR *d;
2832cb25675Sralph struct direct *dp;
2842cb25675Sralph char *otp, *cp;
28558fc31f7Sralph extern struct subcmd *subcmds;
28626ce117eSsklower static char user[15], group[15];
287978e44c9Sralph
288978e44c9Sralph if (debug)
28901de96ecSralph printf("sendf(%s, %x)\n", rname, opts);
290978e44c9Sralph
29158fc31f7Sralph if (except(target))
29265a0d230Sralph return;
293e980046fSralph if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
29490bde559Sbostic error("%s: %s\n", target, strerror(errno));
295978e44c9Sralph return;
296978e44c9Sralph }
2972cb25675Sralph if ((u = update(rname, opts, &stb)) == 0) {
2982cb25675Sralph if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
2992cb25675Sralph (void) savelink(&stb);
300978e44c9Sralph return;
3012cb25675Sralph }
302978e44c9Sralph
30301de96ecSralph if (pw == NULL || pw->pw_uid != stb.st_uid)
30401de96ecSralph if ((pw = getpwuid(stb.st_uid)) == NULL) {
3056dfcab80Ssklower log(lfp, "%s: no password entry for uid %d \n",
3066dfcab80Ssklower target, stb.st_uid);
30726ce117eSsklower pw = NULL;
308bdfadfffSbostic (void)sprintf(user, ":%lu", stb.st_uid);
309978e44c9Sralph }
31001de96ecSralph if (gr == NULL || gr->gr_gid != stb.st_gid)
31101de96ecSralph if ((gr = getgrgid(stb.st_gid)) == NULL) {
3126dfcab80Ssklower log(lfp, "%s: no name for group %d\n",
3136dfcab80Ssklower target, stb.st_gid);
31426ce117eSsklower gr = NULL;
315bdfadfffSbostic (void)sprintf(group, ":%lu", stb.st_gid);
316978e44c9Sralph }
317156664fbSralph if (u == 1) {
3186d401060Sralph if (opts & VERIFY) {
3196d401060Sralph log(lfp, "need to install: %s\n", target);
3206d401060Sralph goto dospecial;
3216d401060Sralph }
32201de96ecSralph log(lfp, "installing: %s\n", target);
3236d401060Sralph opts &= ~(COMPARE|REMOVE);
324156664fbSralph }
325978e44c9Sralph
326978e44c9Sralph switch (stb.st_mode & S_IFMT) {
3272cb25675Sralph case S_IFDIR:
3282cb25675Sralph if ((d = opendir(target)) == NULL) {
32990bde559Sbostic error("%s: %s\n", target, strerror(errno));
3302cb25675Sralph return;
3312cb25675Sralph }
3322cb25675Sralph (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
33326ce117eSsklower stb.st_mode & 07777, protoname(), protogroup(), rname);
3342cb25675Sralph if (debug)
33515867ad7Sralph printf("buf = %s", buf);
3362cb25675Sralph (void) write(rem, buf, strlen(buf));
3372cb25675Sralph if (response() < 0) {
3382cb25675Sralph closedir(d);
3392cb25675Sralph return;
3402cb25675Sralph }
3412cb25675Sralph
3422cb25675Sralph if (opts & REMOVE)
3432cb25675Sralph rmchk(opts);
3442cb25675Sralph
3452cb25675Sralph otp = tp;
3462cb25675Sralph len = tp - target;
3472cb25675Sralph while (dp = readdir(d)) {
3482cb25675Sralph if (!strcmp(dp->d_name, ".") ||
3492cb25675Sralph !strcmp(dp->d_name, ".."))
3502cb25675Sralph continue;
3512cb25675Sralph if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
3522cb25675Sralph error("%s/%s: Name too long\n", target,
3532cb25675Sralph dp->d_name);
3542cb25675Sralph continue;
3552cb25675Sralph }
3562cb25675Sralph tp = otp;
3572cb25675Sralph *tp++ = '/';
3582cb25675Sralph cp = dp->d_name;
3592cb25675Sralph while (*tp++ = *cp++)
3602cb25675Sralph ;
3612cb25675Sralph tp--;
3622cb25675Sralph sendf(dp->d_name, opts);
3632cb25675Sralph }
3642cb25675Sralph closedir(d);
3652cb25675Sralph (void) write(rem, "E\n", 2);
3662cb25675Sralph (void) response();
3672cb25675Sralph tp = otp;
3682cb25675Sralph *tp = '\0';
3692cb25675Sralph return;
3702cb25675Sralph
3712cb25675Sralph case S_IFLNK:
3722cb25675Sralph if (u != 1)
3732cb25675Sralph opts |= COMPARE;
37426ce117eSsklower if (stb.st_nlink > 1) {
37526ce117eSsklower struct linkbuf *lp;
37626ce117eSsklower
37726ce117eSsklower if ((lp = savelink(&stb)) != NULL) {
37826ce117eSsklower /* install link */
37926ce117eSsklower if (*lp->target == 0)
38026ce117eSsklower (void) sprintf(buf, "k%o %s %s\n", opts,
38126ce117eSsklower lp->pathname, rname);
38226ce117eSsklower else
38326ce117eSsklower (void) sprintf(buf, "k%o %s/%s %s\n", opts,
38426ce117eSsklower lp->target, lp->pathname, rname);
38526ce117eSsklower if (debug)
38626ce117eSsklower printf("buf = %s", buf);
38726ce117eSsklower (void) write(rem, buf, strlen(buf));
38826ce117eSsklower (void) response();
38926ce117eSsklower return;
39026ce117eSsklower }
39126ce117eSsklower }
392bdfadfffSbostic (void) sprintf(buf, "K%o %o %qd %ld %s %s %s\n", opts,
3932cb25675Sralph stb.st_mode & 07777, stb.st_size, stb.st_mtime,
39426ce117eSsklower protoname(), protogroup(), rname);
3952cb25675Sralph if (debug)
3962cb25675Sralph printf("buf = %s", buf);
3972cb25675Sralph (void) write(rem, buf, strlen(buf));
3982cb25675Sralph if (response() < 0)
3992cb25675Sralph return;
4002cb25675Sralph sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
4012cb25675Sralph (void) write(rem, buf, stb.st_size);
4022cb25675Sralph if (debug)
403e157bfacSbostic printf("readlink = %.*s\n", (int)stb.st_size, buf);
4042cb25675Sralph goto done;
4052cb25675Sralph
406978e44c9Sralph case S_IFREG:
407978e44c9Sralph break;
408978e44c9Sralph
409978e44c9Sralph default:
4102cb25675Sralph error("%s: not a file or directory\n", target);
411978e44c9Sralph return;
412978e44c9Sralph }
413978e44c9Sralph
414156664fbSralph if (u == 2) {
4156d401060Sralph if (opts & VERIFY) {
4166d401060Sralph log(lfp, "need to update: %s\n", target);
4176d401060Sralph goto dospecial;
418156664fbSralph }
4196d401060Sralph log(lfp, "updating: %s\n", target);
4206d401060Sralph }
4212cb25675Sralph
4222cb25675Sralph if (stb.st_nlink > 1) {
4232cb25675Sralph struct linkbuf *lp;
4242cb25675Sralph
4252cb25675Sralph if ((lp = savelink(&stb)) != NULL) {
4262cb25675Sralph /* install link */
42726ce117eSsklower if (*lp->target == 0)
4282cb25675Sralph (void) sprintf(buf, "k%o %s %s\n", opts,
4292cb25675Sralph lp->pathname, rname);
43026ce117eSsklower else
43126ce117eSsklower (void) sprintf(buf, "k%o %s/%s %s\n", opts,
43226ce117eSsklower lp->target, lp->pathname, rname);
4332cb25675Sralph if (debug)
4342cb25675Sralph printf("buf = %s", buf);
4352cb25675Sralph (void) write(rem, buf, strlen(buf));
4362cb25675Sralph (void) response();
4372cb25675Sralph return;
4382cb25675Sralph }
4392cb25675Sralph }
440978e44c9Sralph
441bdfadfffSbostic if ((f = open(target, O_RDONLY, 0)) < 0) {
44290bde559Sbostic error("%s: %s\n", target, strerror(errno));
443d49851feSralph return;
444d49851feSralph }
445bdfadfffSbostic (void) sprintf(buf, "R%o %o %qd %ld %s %s %s\n", opts,
446ad3d87d9Sralph stb.st_mode & 07777, stb.st_size, stb.st_mtime,
44726ce117eSsklower protoname(), protogroup(), rname);
448978e44c9Sralph if (debug)
449978e44c9Sralph printf("buf = %s", buf);
450978e44c9Sralph (void) write(rem, buf, strlen(buf));
451978e44c9Sralph if (response() < 0) {
452978e44c9Sralph (void) close(f);
453978e44c9Sralph return;
454978e44c9Sralph }
455978e44c9Sralph sizerr = 0;
456978e44c9Sralph for (i = 0; i < stb.st_size; i += BUFSIZ) {
457978e44c9Sralph int amt = BUFSIZ;
458978e44c9Sralph if (i + amt > stb.st_size)
459978e44c9Sralph amt = stb.st_size - i;
460978e44c9Sralph if (sizerr == 0 && read(f, buf, amt) != amt)
461978e44c9Sralph sizerr = 1;
462978e44c9Sralph (void) write(rem, buf, amt);
463978e44c9Sralph }
464978e44c9Sralph (void) close(f);
4652cb25675Sralph done:
4666d401060Sralph if (sizerr) {
46701de96ecSralph error("%s: file changed size\n", target);
4686d401060Sralph err();
4696d401060Sralph } else
4706d401060Sralph ack();
4712cb25675Sralph f = response();
4722cb25675Sralph if (f < 0 || f == 0 && (opts & COMPARE))
4736d401060Sralph return;
4746d401060Sralph dospecial:
47558fc31f7Sralph for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
476aca7acf4Sralph if (sc->sc_type != SPECIAL)
4776d401060Sralph continue;
478e980046fSralph if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
4796d401060Sralph continue;
480aca7acf4Sralph log(lfp, "special \"%s\"\n", sc->sc_name);
4816d401060Sralph if (opts & VERIFY)
4826d401060Sralph continue;
483e980046fSralph (void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name);
4846d401060Sralph if (debug)
4856d401060Sralph printf("buf = %s", buf);
4866d401060Sralph (void) write(rem, buf, strlen(buf));
4876d401060Sralph while (response() > 0)
4886d401060Sralph ;
4896d401060Sralph }
490978e44c9Sralph }
491978e44c9Sralph
492bdfadfffSbostic static struct linkbuf *
savelink(stp)4932cb25675Sralph savelink(stp)
4942cb25675Sralph struct stat *stp;
495978e44c9Sralph {
4962cb25675Sralph struct linkbuf *lp;
497978e44c9Sralph
4982cb25675Sralph for (lp = ihead; lp != NULL; lp = lp->nextp)
4992cb25675Sralph if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
5002cb25675Sralph lp->count--;
5012cb25675Sralph return(lp);
502978e44c9Sralph }
5032cb25675Sralph lp = (struct linkbuf *) malloc(sizeof(*lp));
5042cb25675Sralph if (lp == NULL)
5052cb25675Sralph log(lfp, "out of memory, link information lost\n");
5062cb25675Sralph else {
5072cb25675Sralph lp->nextp = ihead;
5082cb25675Sralph ihead = lp;
5092cb25675Sralph lp->inum = stp->st_ino;
5102cb25675Sralph lp->devnum = stp->st_dev;
5112cb25675Sralph lp->count = stp->st_nlink - 1;
5122cb25675Sralph strcpy(lp->pathname, target);
51326ce117eSsklower if (Tdest)
51426ce117eSsklower strcpy(lp->target, Tdest);
51526ce117eSsklower else
51626ce117eSsklower *lp->target = 0;
517978e44c9Sralph }
5182cb25675Sralph return(NULL);
519978e44c9Sralph }
520978e44c9Sralph
521978e44c9Sralph /*
522978e44c9Sralph * Check to see if file needs to be updated on the remote machine.
52301de96ecSralph * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
52401de96ecSralph * and 3 if comparing binaries to determine if out of date.
525978e44c9Sralph */
526bdfadfffSbostic static int
update(rname,opts,stp)5272cb25675Sralph update(rname, opts, stp)
52801de96ecSralph char *rname;
529ad3d87d9Sralph int opts;
5302cb25675Sralph struct stat *stp;
531978e44c9Sralph {
53201de96ecSralph register char *cp, *s;
533978e44c9Sralph register off_t size;
534978e44c9Sralph register time_t mtime;
535978e44c9Sralph
536978e44c9Sralph if (debug)
5372cb25675Sralph printf("update(%s, %x, %x)\n", rname, opts, stp);
538978e44c9Sralph
539978e44c9Sralph /*
540978e44c9Sralph * Check to see if the file exists on the remote machine.
541978e44c9Sralph */
54201de96ecSralph (void) sprintf(buf, "Q%s\n", rname);
543978e44c9Sralph if (debug)
544978e44c9Sralph printf("buf = %s", buf);
545978e44c9Sralph (void) write(rem, buf, strlen(buf));
54626ce117eSsklower again:
54701de96ecSralph cp = s = buf;
548978e44c9Sralph do {
549978e44c9Sralph if (read(rem, cp, 1) != 1)
550bdfadfffSbostic lostconn(0);
551d49851feSralph } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
552978e44c9Sralph
55301de96ecSralph switch (*s++) {
554978e44c9Sralph case 'Y':
555978e44c9Sralph break;
556978e44c9Sralph
557d49851feSralph case 'N': /* file doesn't exist so install it */
558978e44c9Sralph return(1);
559978e44c9Sralph
560978e44c9Sralph case '\1':
561aca7acf4Sralph nerrs++;
56201de96ecSralph if (*s != '\n') {
563d49851feSralph if (!iamremote) {
564d49851feSralph fflush(stdout);
56501de96ecSralph (void) write(2, s, cp - s);
566d49851feSralph }
567d49851feSralph if (lfp != NULL)
56801de96ecSralph (void) fwrite(s, 1, cp - s, lfp);
569d49851feSralph }
570978e44c9Sralph return(0);
571978e44c9Sralph
57226ce117eSsklower case '\3':
57326ce117eSsklower *--cp = '\0';
57426ce117eSsklower if (lfp != NULL)
57526ce117eSsklower log(lfp, "update: note: %s\n", s);
57626ce117eSsklower goto again;
57726ce117eSsklower
578978e44c9Sralph default:
57915867ad7Sralph *--cp = '\0';
58026ce117eSsklower error("update: unexpected response '%s'\n", s);
581978e44c9Sralph return(0);
582978e44c9Sralph }
583978e44c9Sralph
58401de96ecSralph if (*s == '\n')
585d49851feSralph return(2);
586978e44c9Sralph
587156664fbSralph if (opts & COMPARE)
588156664fbSralph return(3);
589156664fbSralph
590978e44c9Sralph size = 0;
59101de96ecSralph while (isdigit(*s))
59201de96ecSralph size = size * 10 + (*s++ - '0');
59301de96ecSralph if (*s++ != ' ') {
59401de96ecSralph error("update: size not delimited\n");
595978e44c9Sralph return(0);
596978e44c9Sralph }
597978e44c9Sralph mtime = 0;
59801de96ecSralph while (isdigit(*s))
59901de96ecSralph mtime = mtime * 10 + (*s++ - '0');
60001de96ecSralph if (*s != '\n') {
60101de96ecSralph error("update: mtime not delimited\n");
602978e44c9Sralph return(0);
603978e44c9Sralph }
604978e44c9Sralph /*
605978e44c9Sralph * File needs to be updated?
606978e44c9Sralph */
607ad3d87d9Sralph if (opts & YOUNGER) {
6082cb25675Sralph if (stp->st_mtime == mtime)
60965a0d230Sralph return(0);
6102cb25675Sralph if (stp->st_mtime < mtime) {
61101de96ecSralph log(lfp, "Warning: %s: remote copy is newer\n", target);
61265a0d230Sralph return(0);
61365a0d230Sralph }
6142cb25675Sralph } else if (stp->st_mtime == mtime && stp->st_size == size)
615978e44c9Sralph return(0);
616d49851feSralph return(2);
617978e44c9Sralph }
618978e44c9Sralph
619978e44c9Sralph /*
620978e44c9Sralph * Query. Check to see if file exists. Return one of the following:
621978e44c9Sralph * N\n - doesn't exist
622978e44c9Sralph * Ysize mtime\n - exists and its a regular file (size & mtime of file)
6232cb25675Sralph * Y\n - exists and its a directory or symbolic link
624978e44c9Sralph * ^Aerror message\n
625978e44c9Sralph */
626bdfadfffSbostic static void
query(name)62701de96ecSralph query(name)
628978e44c9Sralph char *name;
629978e44c9Sralph {
630978e44c9Sralph struct stat stb;
631978e44c9Sralph
632978e44c9Sralph if (catname)
633978e44c9Sralph (void) sprintf(tp, "/%s", name);
63465a0d230Sralph
6352cb25675Sralph if (lstat(target, &stb) < 0) {
636e980046fSralph if (errno == ENOENT)
637978e44c9Sralph (void) write(rem, "N\n", 2);
638e980046fSralph else
63990bde559Sbostic error("%s:%s: %s\n", host, target, strerror(errno));
640978e44c9Sralph *tp = '\0';
641978e44c9Sralph return;
642978e44c9Sralph }
643978e44c9Sralph
644978e44c9Sralph switch (stb.st_mode & S_IFMT) {
645978e44c9Sralph case S_IFREG:
646bdfadfffSbostic (void) sprintf(buf, "Y%qd %ld\n", stb.st_size, stb.st_mtime);
647978e44c9Sralph (void) write(rem, buf, strlen(buf));
648978e44c9Sralph break;
649978e44c9Sralph
6502cb25675Sralph case S_IFLNK:
651978e44c9Sralph case S_IFDIR:
652978e44c9Sralph (void) write(rem, "Y\n", 2);
653978e44c9Sralph break;
654978e44c9Sralph
655978e44c9Sralph default:
6562cb25675Sralph error("%s: not a file or directory\n", name);
657978e44c9Sralph break;
658978e44c9Sralph }
659978e44c9Sralph *tp = '\0';
660978e44c9Sralph }
661978e44c9Sralph
662bdfadfffSbostic static void
recvf(cmd,type)6632cb25675Sralph recvf(cmd, type)
664978e44c9Sralph char *cmd;
6652cb25675Sralph int type;
666978e44c9Sralph {
667978e44c9Sralph register char *cp;
66844f1d1c2Sralph int f, mode, opts, wrerr, olderrno;
669978e44c9Sralph off_t i, size;
670978e44c9Sralph time_t mtime;
671978e44c9Sralph struct stat stb;
672978e44c9Sralph struct timeval tvp[2];
673e980046fSralph char *owner, *group;
674978e44c9Sralph char new[BUFSIZ];
67516580127Seric extern char *tempname;
676978e44c9Sralph
677ad3d87d9Sralph cp = cmd;
678156664fbSralph opts = 0;
679156664fbSralph while (*cp >= '0' && *cp <= '7')
680156664fbSralph opts = (opts << 3) | (*cp++ - '0');
681ad3d87d9Sralph if (*cp++ != ' ') {
68201de96ecSralph error("recvf: options not delimited\n");
683ad3d87d9Sralph return;
684ad3d87d9Sralph }
685978e44c9Sralph mode = 0;
686156664fbSralph while (*cp >= '0' && *cp <= '7')
687ad3d87d9Sralph mode = (mode << 3) | (*cp++ - '0');
688978e44c9Sralph if (*cp++ != ' ') {
68901de96ecSralph error("recvf: mode not delimited\n");
690978e44c9Sralph return;
691978e44c9Sralph }
692978e44c9Sralph size = 0;
693978e44c9Sralph while (isdigit(*cp))
694978e44c9Sralph size = size * 10 + (*cp++ - '0');
695978e44c9Sralph if (*cp++ != ' ') {
69601de96ecSralph error("recvf: size not delimited\n");
697978e44c9Sralph return;
698978e44c9Sralph }
699978e44c9Sralph mtime = 0;
700978e44c9Sralph while (isdigit(*cp))
701978e44c9Sralph mtime = mtime * 10 + (*cp++ - '0');
702978e44c9Sralph if (*cp++ != ' ') {
70301de96ecSralph error("recvf: mtime not delimited\n");
704978e44c9Sralph return;
705978e44c9Sralph }
706978e44c9Sralph owner = cp;
707978e44c9Sralph while (*cp && *cp != ' ')
708978e44c9Sralph cp++;
709978e44c9Sralph if (*cp != ' ') {
71001de96ecSralph error("recvf: owner name not delimited\n");
711978e44c9Sralph return;
712978e44c9Sralph }
713978e44c9Sralph *cp++ = '\0';
714978e44c9Sralph group = cp;
715978e44c9Sralph while (*cp && *cp != ' ')
716978e44c9Sralph cp++;
717978e44c9Sralph if (*cp != ' ') {
71801de96ecSralph error("recvf: group name not delimited\n");
719978e44c9Sralph return;
720978e44c9Sralph }
721978e44c9Sralph *cp++ = '\0';
722978e44c9Sralph
7232cb25675Sralph if (type == S_IFDIR) {
72465a0d230Sralph if (catname >= sizeof(stp)) {
7252cb25675Sralph error("%s:%s: too many directory levels\n",
7262cb25675Sralph host, target);
72765a0d230Sralph return;
72865a0d230Sralph }
72965a0d230Sralph stp[catname] = tp;
730978e44c9Sralph if (catname++) {
731978e44c9Sralph *tp++ = '/';
732978e44c9Sralph while (*tp++ = *cp++)
733978e44c9Sralph ;
734978e44c9Sralph tp--;
735978e44c9Sralph }
736ad3d87d9Sralph if (opts & VERIFY) {
7376d401060Sralph ack();
738d49851feSralph return;
739d49851feSralph }
7402cb25675Sralph if (lstat(target, &stb) == 0) {
7416d401060Sralph if (ISDIR(stb.st_mode)) {
742e980046fSralph if ((stb.st_mode & 07777) == mode) {
7436d401060Sralph ack();
744978e44c9Sralph return;
745d49851feSralph }
7463e1cad88Sralph buf[0] = '\0';
7473e1cad88Sralph (void) sprintf(buf + 1,
748d66beb1aSlepreau "%s: Warning: remote mode %o != local mode %o\n",
749d66beb1aSlepreau target, stb.st_mode & 07777, mode);
7503e1cad88Sralph (void) write(rem, buf, strlen(buf + 1) + 1);
7513e1cad88Sralph return;
7523e1cad88Sralph }
753e980046fSralph errno = ENOTDIR;
754e980046fSralph } else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
755e980046fSralph chkparent(target) == 0 && mkdir(target, mode) == 0)) {
7561b11f3a3Sbostic if (fchog(-1, target, owner, group, mode) == 0)
7576d401060Sralph ack();
7586d401060Sralph return;
7596d401060Sralph }
76090bde559Sbostic error("%s:%s: %s\n", host, target, strerror(errno));
7616d401060Sralph tp = stp[--catname];
7626d401060Sralph *tp = '\0';
763978e44c9Sralph return;
764978e44c9Sralph }
765978e44c9Sralph
766978e44c9Sralph if (catname)
767978e44c9Sralph (void) sprintf(tp, "/%s", cp);
768978e44c9Sralph cp = rindex(target, '/');
769978e44c9Sralph if (cp == NULL)
77016580127Seric strcpy(new, tempname);
771e980046fSralph else if (cp == target)
77216580127Seric (void) sprintf(new, "/%s", tempname);
773e980046fSralph else {
774978e44c9Sralph *cp = '\0';
77516580127Seric (void) sprintf(new, "%s/%s", target, tempname);
776d49851feSralph *cp = '/';
777e980046fSralph }
7782cb25675Sralph
7792cb25675Sralph if (type == S_IFLNK) {
7802cb25675Sralph int j;
7812cb25675Sralph
7822cb25675Sralph ack();
7832cb25675Sralph cp = buf;
7842cb25675Sralph for (i = 0; i < size; i += j) {
7852cb25675Sralph if ((j = read(rem, cp, size - i)) <= 0)
786bdfadfffSbostic cleanup(0);
7872cb25675Sralph cp += j;
7882cb25675Sralph }
7892cb25675Sralph *cp = '\0';
790e980046fSralph if (response() < 0) {
791e980046fSralph err();
792e980046fSralph return;
793e980046fSralph }
79475527c89Sralph if (symlink(buf, new) < 0) {
79575527c89Sralph if (errno != ENOENT || chkparent(new) < 0 ||
79675527c89Sralph symlink(buf, new) < 0)
7971b11f3a3Sbostic goto badnew1;
79875527c89Sralph }
7992cb25675Sralph mode &= 0777;
8002cb25675Sralph if (opts & COMPARE) {
8012cb25675Sralph char tbuf[BUFSIZ];
8022cb25675Sralph
80375527c89Sralph if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
80475527c89Sralph i == size && strncmp(buf, tbuf, size) == 0) {
8052cb25675Sralph (void) unlink(new);
8062cb25675Sralph ack();
8072cb25675Sralph return;
8082cb25675Sralph }
80915867ad7Sralph if (opts & VERIFY)
81015867ad7Sralph goto differ;
8112cb25675Sralph }
8122cb25675Sralph goto fixup;
8132cb25675Sralph }
8142cb25675Sralph
815e980046fSralph if ((f = creat(new, mode)) < 0) {
816e980046fSralph if (errno != ENOENT || chkparent(new) < 0 ||
817e980046fSralph (f = creat(new, mode)) < 0)
8181b11f3a3Sbostic goto badnew1;
819e980046fSralph }
820e980046fSralph
8216d401060Sralph ack();
822978e44c9Sralph wrerr = 0;
823978e44c9Sralph for (i = 0; i < size; i += BUFSIZ) {
824978e44c9Sralph int amt = BUFSIZ;
825978e44c9Sralph
82665a0d230Sralph cp = buf;
827978e44c9Sralph if (i + amt > size)
828978e44c9Sralph amt = size - i;
829978e44c9Sralph do {
830978e44c9Sralph int j = read(rem, cp, amt);
831978e44c9Sralph
832d49851feSralph if (j <= 0) {
833d49851feSralph (void) close(f);
834d49851feSralph (void) unlink(new);
835bdfadfffSbostic cleanup(0);
836d49851feSralph }
837978e44c9Sralph amt -= j;
838978e44c9Sralph cp += j;
839978e44c9Sralph } while (amt > 0);
840978e44c9Sralph amt = BUFSIZ;
841978e44c9Sralph if (i + amt > size)
842978e44c9Sralph amt = size - i;
843978e44c9Sralph if (wrerr == 0 && write(f, buf, amt) != amt) {
844978e44c9Sralph olderrno = errno;
845978e44c9Sralph wrerr++;
846978e44c9Sralph }
847978e44c9Sralph }
848e980046fSralph if (response() < 0) {
849e980046fSralph err();
8501b11f3a3Sbostic goto badnew2;
851e980046fSralph }
8521b11f3a3Sbostic if (wrerr)
8531b11f3a3Sbostic goto badnew1;
854156664fbSralph if (opts & COMPARE) {
855156664fbSralph FILE *f1, *f2;
856156664fbSralph int c;
857156664fbSralph
858156664fbSralph if ((f1 = fopen(target, "r")) == NULL)
8591b11f3a3Sbostic goto badtarget;
86026ce117eSsklower if ((f2 = fopen(new, "r")) == NULL) {
8611b11f3a3Sbostic badnew1: error("%s:%s: %s\n", host, new, strerror(errno));
8621b11f3a3Sbostic goto badnew2;
86326ce117eSsklower }
864156664fbSralph while ((c = getc(f1)) == getc(f2))
865156664fbSralph if (c == EOF) {
866156664fbSralph (void) fclose(f1);
867156664fbSralph (void) fclose(f2);
8686d401060Sralph ack();
8691b11f3a3Sbostic goto badnew2;
870156664fbSralph }
871156664fbSralph (void) fclose(f1);
872156664fbSralph (void) fclose(f2);
873156664fbSralph if (opts & VERIFY) {
8741b11f3a3Sbostic differ: buf[0] = '\0';
875e980046fSralph (void) sprintf(buf + 1, "need to update: %s\n",target);
876156664fbSralph (void) write(rem, buf, strlen(buf + 1) + 1);
8771b11f3a3Sbostic goto badnew2;
878156664fbSralph }
879156664fbSralph }
880978e44c9Sralph
881978e44c9Sralph /*
882978e44c9Sralph * Set last modified time
883978e44c9Sralph */
8841b11f3a3Sbostic tvp[0].tv_sec = time(0);
885978e44c9Sralph tvp[0].tv_usec = 0;
886978e44c9Sralph tvp[1].tv_sec = mtime;
887978e44c9Sralph tvp[1].tv_usec = 0;
8881b11f3a3Sbostic if (utimes(new, tvp) < 0)
88990bde559Sbostic note("%s: utimes failed %s: %s\n", host, new, strerror(errno));
8901b11f3a3Sbostic
8911b11f3a3Sbostic if (fchog(f, new, owner, group, mode) < 0) {
8921b11f3a3Sbostic badnew2: (void) close(f);
893d49851feSralph (void) unlink(new);
894978e44c9Sralph return;
895978e44c9Sralph }
8961b11f3a3Sbostic (void) close(f);
8971b11f3a3Sbostic
8981b11f3a3Sbostic fixup: if (rename(new, target) < 0) {
8991b11f3a3Sbostic badtarget: error("%s:%s: %s\n", host, target, strerror(errno));
900d49851feSralph (void) unlink(new);
901978e44c9Sralph return;
902978e44c9Sralph }
9031b11f3a3Sbostic
904156664fbSralph if (opts & COMPARE) {
905156664fbSralph buf[0] = '\0';
906d66beb1aSlepreau (void) sprintf(buf + 1, "updated %s\n", target);
907156664fbSralph (void) write(rem, buf, strlen(buf + 1) + 1);
908156664fbSralph } else
9096d401060Sralph ack();
910978e44c9Sralph }
911978e44c9Sralph
912978e44c9Sralph /*
9132cb25675Sralph * Creat a hard link to existing file.
9142cb25675Sralph */
915bdfadfffSbostic static void
hardlink(cmd)9162cb25675Sralph hardlink(cmd)
9172cb25675Sralph char *cmd;
9182cb25675Sralph {
9192cb25675Sralph register char *cp;
9202cb25675Sralph struct stat stb;
9212cb25675Sralph char *oldname;
9222cb25675Sralph int opts, exists = 0;
9232cb25675Sralph
9242cb25675Sralph cp = cmd;
9252cb25675Sralph opts = 0;
9262cb25675Sralph while (*cp >= '0' && *cp <= '7')
9272cb25675Sralph opts = (opts << 3) | (*cp++ - '0');
9282cb25675Sralph if (*cp++ != ' ') {
9292cb25675Sralph error("hardlink: options not delimited\n");
9302cb25675Sralph return;
9312cb25675Sralph }
9322cb25675Sralph oldname = cp;
9332cb25675Sralph while (*cp && *cp != ' ')
9342cb25675Sralph cp++;
9352cb25675Sralph if (*cp != ' ') {
9362cb25675Sralph error("hardlink: oldname name not delimited\n");
9372cb25675Sralph return;
9382cb25675Sralph }
9392cb25675Sralph *cp++ = '\0';
9402cb25675Sralph
94126ce117eSsklower if (catname) {
9422cb25675Sralph (void) sprintf(tp, "/%s", cp);
94326ce117eSsklower }
9442cb25675Sralph if (lstat(target, &stb) == 0) {
94526ce117eSsklower int mode = stb.st_mode & S_IFMT;
94626ce117eSsklower if (mode != S_IFREG && mode != S_IFLNK) {
9472cb25675Sralph error("%s:%s: not a regular file\n", host, target);
9482cb25675Sralph return;
9492cb25675Sralph }
9502cb25675Sralph exists = 1;
9512cb25675Sralph }
95226ce117eSsklower if (chkparent(target) < 0 ) {
95326ce117eSsklower error("%s:%s: %s (no parent)\n",
95490bde559Sbostic host, target, strerror(errno));
95526ce117eSsklower return;
95626ce117eSsklower }
95726ce117eSsklower if (exists && (unlink(target) < 0)) {
95826ce117eSsklower error("%s:%s: %s (unlink)\n",
95990bde559Sbostic host, target, strerror(errno));
96026ce117eSsklower return;
96126ce117eSsklower }
96226ce117eSsklower if (link(oldname, target) < 0) {
96326ce117eSsklower error("%s:can't link %s to %s\n",
96426ce117eSsklower host, target, oldname);
9652cb25675Sralph return;
9662cb25675Sralph }
9672cb25675Sralph ack();
9682cb25675Sralph }
9692cb25675Sralph
9702cb25675Sralph /*
971e980046fSralph * Check to see if parent directory exists and create one if not.
97265a0d230Sralph */
973bdfadfffSbostic static int
chkparent(name)97465a0d230Sralph chkparent(name)
97565a0d230Sralph char *name;
97665a0d230Sralph {
977e980046fSralph register char *cp;
978e980046fSralph struct stat stb;
97965a0d230Sralph
98065a0d230Sralph cp = rindex(name, '/');
981e980046fSralph if (cp == NULL || cp == name)
982e980046fSralph return(0);
98365a0d230Sralph *cp = '\0';
984e980046fSralph if (lstat(name, &stb) < 0) {
985e980046fSralph if (errno == ENOENT && chkparent(name) >= 0 &&
986e980046fSralph mkdir(name, 0777 & ~oumask) >= 0) {
98765a0d230Sralph *cp = '/';
98865a0d230Sralph return(0);
98965a0d230Sralph }
990e980046fSralph } else if (ISDIR(stb.st_mode)) {
99165a0d230Sralph *cp = '/';
99265a0d230Sralph return(0);
99365a0d230Sralph }
99465a0d230Sralph *cp = '/';
99565a0d230Sralph return(-1);
99665a0d230Sralph }
99765a0d230Sralph
99865a0d230Sralph /*
9996d401060Sralph * Change owner, group and mode of file.
1000978e44c9Sralph */
1001bdfadfffSbostic static int
fchog(fd,file,owner,group,mode)10021b11f3a3Sbostic fchog(fd, file, owner, group, mode)
10031b11f3a3Sbostic int fd;
1004978e44c9Sralph char *file, *owner, *group;
1005d49851feSralph int mode;
1006978e44c9Sralph {
1007978e44c9Sralph register int i;
1008978e44c9Sralph int uid, gid;
1009e980046fSralph extern char user[];
1010e980046fSralph extern int userid;
1011978e44c9Sralph
1012978e44c9Sralph uid = userid;
1013978e44c9Sralph if (userid == 0) {
101426ce117eSsklower if (*owner == ':') {
101526ce117eSsklower uid = atoi(owner + 1);
101626ce117eSsklower } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
101701de96ecSralph if ((pw = getpwnam(owner)) == NULL) {
1018d49851feSralph if (mode & 04000) {
101926ce117eSsklower note("%s:%s: unknown login name, clearing setuid",
10202cb25675Sralph host, owner);
102126ce117eSsklower mode &= ~04000;
102226ce117eSsklower uid = 0;
1023978e44c9Sralph }
1024d49851feSralph } else
102501de96ecSralph uid = pw->pw_uid;
1026d49851feSralph } else
102701de96ecSralph uid = pw->pw_uid;
102826ce117eSsklower if (*group == ':') {
102926ce117eSsklower gid = atoi(group + 1);
103026ce117eSsklower goto ok;
103126ce117eSsklower }
10326d401060Sralph } else if ((mode & 04000) && strcmp(user, owner) != 0)
10336d401060Sralph mode &= ~04000;
1034e980046fSralph gid = -1;
103501de96ecSralph if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
103626ce117eSsklower if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
103726ce117eSsklower || ((gr = getgrnam(group)) == NULL)) {
1038d49851feSralph if (mode & 02000) {
103926ce117eSsklower note("%s:%s: unknown group", host, group);
104026ce117eSsklower mode &= ~02000;
1041978e44c9Sralph }
1042d49851feSralph } else
104301de96ecSralph gid = gr->gr_gid;
1044d49851feSralph } else
104501de96ecSralph gid = gr->gr_gid;
1046e980046fSralph if (userid && gid >= 0) {
104726ce117eSsklower if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
104801de96ecSralph if (!(strcmp(user, gr->gr_mem[i])))
1049978e44c9Sralph goto ok;
10506d401060Sralph mode &= ~02000;
1051e980046fSralph gid = -1;
1052978e44c9Sralph }
10531b11f3a3Sbostic ok: if (fd != -1 && fchown(fd, uid, gid) < 0 || chown(file, uid, gid) < 0)
10541b11f3a3Sbostic note("%s: %s chown: %s", host, file, strerror(errno));
10551b11f3a3Sbostic else if (mode & 07000 &&
10561b11f3a3Sbostic (fd != -1 && fchmod(fd, mode) < 0 || chmod(file, mode) < 0))
10571b11f3a3Sbostic note("%s: %s chmod: %s", host, file, strerror(errno));
1058978e44c9Sralph return(0);
1059978e44c9Sralph }
1060978e44c9Sralph
1061c27256c0Sralph /*
1062c27256c0Sralph * Check for files on the machine being updated that are not on the master
1063c27256c0Sralph * machine and remove them.
1064c27256c0Sralph */
1065bdfadfffSbostic static void
rmchk(opts)10666d401060Sralph rmchk(opts)
1067c27256c0Sralph int opts;
1068c27256c0Sralph {
106901de96ecSralph register char *cp, *s;
1070c27256c0Sralph struct stat stb;
1071c27256c0Sralph
1072c27256c0Sralph if (debug)
10736d401060Sralph printf("rmchk()\n");
1074156664fbSralph
1075c27256c0Sralph /*
1076c27256c0Sralph * Tell the remote to clean the files from the last directory sent.
1077c27256c0Sralph */
10786d401060Sralph (void) sprintf(buf, "C%o\n", opts & VERIFY);
1079c27256c0Sralph if (debug)
1080c27256c0Sralph printf("buf = %s", buf);
1081c27256c0Sralph (void) write(rem, buf, strlen(buf));
1082c27256c0Sralph if (response() < 0)
1083c27256c0Sralph return;
1084c27256c0Sralph for (;;) {
108501de96ecSralph cp = s = buf;
1086c27256c0Sralph do {
1087c27256c0Sralph if (read(rem, cp, 1) != 1)
1088bdfadfffSbostic lostconn(0);
1089c27256c0Sralph } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1090c27256c0Sralph
109101de96ecSralph switch (*s++) {
10926d401060Sralph case 'Q': /* Query if file should be removed */
1093156664fbSralph /*
1094156664fbSralph * Return the following codes to remove query.
10956d401060Sralph * N\n -- file exists - DON'T remove.
10966d401060Sralph * Y\n -- file doesn't exist - REMOVE.
1097156664fbSralph */
1098c27256c0Sralph *--cp = '\0';
109901de96ecSralph (void) sprintf(tp, "/%s", s);
1100c27256c0Sralph if (debug)
1101c27256c0Sralph printf("check %s\n", target);
110258fc31f7Sralph if (except(target))
1103c27256c0Sralph (void) write(rem, "N\n", 2);
11042cb25675Sralph else if (lstat(target, &stb) < 0)
1105c27256c0Sralph (void) write(rem, "Y\n", 2);
11066d401060Sralph else
11076d401060Sralph (void) write(rem, "N\n", 2);
1108c27256c0Sralph break;
1109c27256c0Sralph
1110c27256c0Sralph case '\0':
1111c27256c0Sralph *--cp = '\0';
111201de96ecSralph if (*s != '\0')
111301de96ecSralph log(lfp, "%s\n", s);
1114c27256c0Sralph break;
1115c27256c0Sralph
11166d401060Sralph case 'E':
11176d401060Sralph *tp = '\0';
11186d401060Sralph ack();
11196d401060Sralph return;
11206d401060Sralph
1121c27256c0Sralph case '\1':
1122c27256c0Sralph case '\2':
1123aca7acf4Sralph nerrs++;
112401de96ecSralph if (*s != '\n') {
1125c27256c0Sralph if (!iamremote) {
1126c27256c0Sralph fflush(stdout);
112701de96ecSralph (void) write(2, s, cp - s);
1128c27256c0Sralph }
1129c27256c0Sralph if (lfp != NULL)
113001de96ecSralph (void) fwrite(s, 1, cp - s, lfp);
1131c27256c0Sralph }
1132c27256c0Sralph if (buf[0] == '\2')
1133bdfadfffSbostic lostconn(0);
1134c27256c0Sralph break;
1135c27256c0Sralph
1136c27256c0Sralph default:
11376d401060Sralph error("rmchk: unexpected response '%s'\n", buf);
11386d401060Sralph err();
1139c27256c0Sralph }
1140c27256c0Sralph }
1141c27256c0Sralph }
1142c27256c0Sralph
1143c27256c0Sralph /*
11446d401060Sralph * Check the current directory (initialized by the 'T' command to server())
1145156664fbSralph * for extraneous files and remove them.
1146c27256c0Sralph */
1147bdfadfffSbostic static void
clean(cp)114800fd8e74Sralph clean(cp)
114900fd8e74Sralph register char *cp;
1150c27256c0Sralph {
1151c27256c0Sralph DIR *d;
11526d401060Sralph register struct direct *dp;
1153c27256c0Sralph struct stat stb;
11546d401060Sralph char *otp;
11556d401060Sralph int len, opts;
1156c27256c0Sralph
11576d401060Sralph opts = 0;
11586d401060Sralph while (*cp >= '0' && *cp <= '7')
11596d401060Sralph opts = (opts << 3) | (*cp++ - '0');
11606d401060Sralph if (*cp != '\0') {
11616d401060Sralph error("clean: options not delimited\n");
1162c27256c0Sralph return;
1163c27256c0Sralph }
1164e980046fSralph if ((d = opendir(target)) == NULL) {
116590bde559Sbostic error("%s:%s: %s\n", host, target, strerror(errno));
116601de96ecSralph return;
116701de96ecSralph }
11686d401060Sralph ack();
1169c27256c0Sralph
1170c27256c0Sralph otp = tp;
1171c27256c0Sralph len = tp - target;
1172c27256c0Sralph while (dp = readdir(d)) {
1173c27256c0Sralph if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1174c27256c0Sralph continue;
1175c27256c0Sralph if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
11762cb25675Sralph error("%s:%s/%s: Name too long\n",
11772cb25675Sralph host, target, dp->d_name);
1178c27256c0Sralph continue;
1179c27256c0Sralph }
1180c27256c0Sralph tp = otp;
1181c27256c0Sralph *tp++ = '/';
1182c27256c0Sralph cp = dp->d_name;;
1183c27256c0Sralph while (*tp++ = *cp++)
1184c27256c0Sralph ;
1185c27256c0Sralph tp--;
11862cb25675Sralph if (lstat(target, &stb) < 0) {
118790bde559Sbostic error("%s:%s: %s\n", host, target, strerror(errno));
1188c27256c0Sralph continue;
1189c27256c0Sralph }
11906d401060Sralph (void) sprintf(buf, "Q%s\n", dp->d_name);
1191c27256c0Sralph (void) write(rem, buf, strlen(buf));
1192c27256c0Sralph cp = buf;
1193c27256c0Sralph do {
1194c27256c0Sralph if (read(rem, cp, 1) != 1)
1195bdfadfffSbostic cleanup(0);
1196c27256c0Sralph } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1197c27256c0Sralph *--cp = '\0';
1198c27256c0Sralph cp = buf;
11996d401060Sralph if (*cp != 'Y')
1200c27256c0Sralph continue;
120101de96ecSralph if (opts & VERIFY) {
120201de96ecSralph cp = buf;
120301de96ecSralph *cp++ = '\0';
1204e980046fSralph (void) sprintf(cp, "need to remove: %s\n", target);
120501de96ecSralph (void) write(rem, buf, strlen(cp) + 1);
120601de96ecSralph } else
12076d57652cStorek removeit(&stb);
1208c27256c0Sralph }
1209c27256c0Sralph closedir(d);
1210c27256c0Sralph (void) write(rem, "E\n", 2);
1211c27256c0Sralph (void) response();
12126d401060Sralph tp = otp;
121301de96ecSralph *tp = '\0';
1214c27256c0Sralph }
1215c27256c0Sralph
121601de96ecSralph /*
121701de96ecSralph * Remove a file or directory (recursively) and send back an acknowledge
121801de96ecSralph * or an error message.
121901de96ecSralph */
1220bdfadfffSbostic static void
removeit(stp)12216d57652cStorek removeit(stp)
12222cb25675Sralph struct stat *stp;
1223c27256c0Sralph {
1224c27256c0Sralph DIR *d;
1225c27256c0Sralph struct direct *dp;
1226c27256c0Sralph register char *cp;
1227c27256c0Sralph struct stat stb;
1228c27256c0Sralph char *otp;
1229c27256c0Sralph int len;
1230c27256c0Sralph
12312cb25675Sralph switch (stp->st_mode & S_IFMT) {
1232c27256c0Sralph case S_IFREG:
12332cb25675Sralph case S_IFLNK:
1234c27256c0Sralph if (unlink(target) < 0)
1235c27256c0Sralph goto bad;
1236c27256c0Sralph goto removed;
1237c27256c0Sralph
1238c27256c0Sralph case S_IFDIR:
1239c27256c0Sralph break;
1240c27256c0Sralph
1241c27256c0Sralph default:
12422cb25675Sralph error("%s:%s: not a plain file\n", host, target);
1243c27256c0Sralph return;
1244c27256c0Sralph }
1245c27256c0Sralph
1246e980046fSralph if ((d = opendir(target)) == NULL)
1247c27256c0Sralph goto bad;
1248c27256c0Sralph
1249c27256c0Sralph otp = tp;
1250c27256c0Sralph len = tp - target;
1251c27256c0Sralph while (dp = readdir(d)) {
1252c27256c0Sralph if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1253c27256c0Sralph continue;
1254c27256c0Sralph if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
12552cb25675Sralph error("%s:%s/%s: Name too long\n",
12562cb25675Sralph host, target, dp->d_name);
1257c27256c0Sralph continue;
1258c27256c0Sralph }
1259c27256c0Sralph tp = otp;
1260c27256c0Sralph *tp++ = '/';
1261c27256c0Sralph cp = dp->d_name;;
1262c27256c0Sralph while (*tp++ = *cp++)
1263c27256c0Sralph ;
1264c27256c0Sralph tp--;
12652cb25675Sralph if (lstat(target, &stb) < 0) {
126690bde559Sbostic error("%s:%s: %s\n", host, target, strerror(errno));
1267c27256c0Sralph continue;
1268c27256c0Sralph }
12696d57652cStorek removeit(&stb);
1270c27256c0Sralph }
1271c27256c0Sralph closedir(d);
1272c27256c0Sralph tp = otp;
1273c27256c0Sralph *tp = '\0';
1274c27256c0Sralph if (rmdir(target) < 0) {
1275c27256c0Sralph bad:
127690bde559Sbostic error("%s:%s: %s\n", host, target, strerror(errno));
1277c27256c0Sralph return;
1278c27256c0Sralph }
1279c27256c0Sralph removed:
1280c27256c0Sralph cp = buf;
1281c27256c0Sralph *cp++ = '\0';
1282c27256c0Sralph (void) sprintf(cp, "removed %s\n", target);
1283c27256c0Sralph (void) write(rem, buf, strlen(cp) + 1);
1284c27256c0Sralph }
1285c27256c0Sralph
12866d401060Sralph /*
12876d401060Sralph * Execute a shell command to handle special cases.
12886d401060Sralph */
1289bdfadfffSbostic static void
dospecial(cmd)12906d401060Sralph dospecial(cmd)
12916d401060Sralph char *cmd;
12926d401060Sralph {
12936d401060Sralph int fd[2], status, pid, i;
12946d401060Sralph register char *cp, *s;
12956d401060Sralph char sbuf[BUFSIZ];
1296e980046fSralph extern int userid, groupid;
12976d401060Sralph
12986d401060Sralph if (pipe(fd) < 0) {
129990bde559Sbostic error("%s\n", strerror(errno));
13006d401060Sralph return;
13016d401060Sralph }
13026d401060Sralph if ((pid = fork()) == 0) {
13036d401060Sralph /*
13046d401060Sralph * Return everything the shell commands print.
13056d401060Sralph */
13066d401060Sralph (void) close(0);
13076d401060Sralph (void) close(1);
13086d401060Sralph (void) close(2);
13090af019deSbostic (void) open(_PATH_DEVNULL, O_RDONLY);
13106d401060Sralph (void) dup(fd[1]);
13116d401060Sralph (void) dup(fd[1]);
13126d401060Sralph (void) close(fd[0]);
13136d401060Sralph (void) close(fd[1]);
1314e980046fSralph setgid(groupid);
1315a5437550Sralph setuid(userid);
13160af019deSbostic execl(_PATH_BSHELL, "sh", "-c", cmd, 0);
13176d401060Sralph _exit(127);
13186d401060Sralph }
13196d401060Sralph (void) close(fd[1]);
13206d401060Sralph s = sbuf;
13216d401060Sralph *s++ = '\0';
13226d401060Sralph while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
13236d401060Sralph cp = buf;
13246d401060Sralph do {
13256d401060Sralph *s++ = *cp++;
13266d401060Sralph if (cp[-1] != '\n') {
13276d401060Sralph if (s < &sbuf[sizeof(sbuf)-1])
13286d401060Sralph continue;
13296d401060Sralph *s++ = '\n';
13306d401060Sralph }
13316d401060Sralph /*
13326d401060Sralph * Throw away blank lines.
13336d401060Sralph */
13346d401060Sralph if (s == &sbuf[2]) {
13356d401060Sralph s--;
13366d401060Sralph continue;
13376d401060Sralph }
13386d401060Sralph (void) write(rem, sbuf, s - sbuf);
13396d401060Sralph s = &sbuf[1];
13406d401060Sralph } while (--i);
13416d401060Sralph }
13426d401060Sralph if (s > &sbuf[1]) {
13436d401060Sralph *s++ = '\n';
13446d401060Sralph (void) write(rem, sbuf, s - sbuf);
13456d401060Sralph }
13466d401060Sralph while ((i = wait(&status)) != pid && i != -1)
13476d401060Sralph ;
13486d401060Sralph if (i == -1)
13496d401060Sralph status = -1;
1350e980046fSralph (void) close(fd[0]);
13516d401060Sralph if (status)
13526d401060Sralph error("shell returned %d\n", status);
13536d401060Sralph else
13546d401060Sralph ack();
13556d401060Sralph }
13566d401060Sralph
1357bdfadfffSbostic #if __STDC__
1358bdfadfffSbostic #include <stdarg.h>
1359bdfadfffSbostic #else
1360bdfadfffSbostic #include <varargs.h>
1361bdfadfffSbostic #endif
1362bdfadfffSbostic
1363bdfadfffSbostic void
1364bdfadfffSbostic #if __STDC__
log(FILE * fp,const char * fmt,...)1365bdfadfffSbostic log(FILE *fp, const char *fmt, ...)
1366bdfadfffSbostic #else
1367bdfadfffSbostic log(fp, fmt, va_alist)
1368d49851feSralph FILE *fp;
1369978e44c9Sralph char *fmt;
1370bdfadfffSbostic va_dcl
1371bdfadfffSbostic #endif
1372978e44c9Sralph {
1373bdfadfffSbostic va_list ap;
1374bdfadfffSbostic #if __STDC__
1375bdfadfffSbostic va_start(ap, fmt);
1376bdfadfffSbostic #else
1377bdfadfffSbostic va_start(ap);
1378bdfadfffSbostic #endif
1379978e44c9Sralph /* Print changes locally if not quiet mode */
1380978e44c9Sralph if (!qflag)
1381bdfadfffSbostic (void)vprintf(fmt, ap);
1382978e44c9Sralph
1383978e44c9Sralph /* Save changes (for mailing) if really updating files */
1384ad3d87d9Sralph if (!(options & VERIFY) && fp != NULL)
1385bdfadfffSbostic (void)vfprintf(fp, fmt, ap);
1386bdfadfffSbostic va_end(ap);
1387978e44c9Sralph }
1388978e44c9Sralph
1389bdfadfffSbostic void
1390bdfadfffSbostic #if __STDC__
error(const char * fmt,...)1391bdfadfffSbostic error(const char *fmt, ...)
1392bdfadfffSbostic #else
1393bdfadfffSbostic error(fmt, va_alist)
1394978e44c9Sralph char *fmt;
1395bdfadfffSbostic va_dcl
1396bdfadfffSbostic #endif
1397978e44c9Sralph {
139852d94563Sbostic static FILE *fp;
1399bdfadfffSbostic va_list ap;
1400bdfadfffSbostic #if __STDC__
1401bdfadfffSbostic va_start(ap, fmt);
1402bdfadfffSbostic #else
1403bdfadfffSbostic va_start(ap);
1404bdfadfffSbostic #endif
140552d94563Sbostic
140652d94563Sbostic ++nerrs;
140752d94563Sbostic if (!fp && !(fp = fdopen(rem, "w")))
140852d94563Sbostic return;
140952d94563Sbostic if (iamremote) {
141052d94563Sbostic (void)fprintf(fp, "%crdist: ", 0x01);
1411bdfadfffSbostic (void)vfprintf(fp, fmt, ap);
141252d94563Sbostic fflush(fp);
141352d94563Sbostic }
141452d94563Sbostic else {
1415d49851feSralph fflush(stdout);
141652d94563Sbostic (void)fprintf(stderr, "rdist: ");
1417bdfadfffSbostic (void)vfprintf(stderr, fmt, ap);
141852d94563Sbostic fflush(stderr);
141952d94563Sbostic }
142052d94563Sbostic if (lfp != NULL) {
142152d94563Sbostic (void)fprintf(lfp, "rdist: ");
1422bdfadfffSbostic (void)vfprintf(lfp, fmt, ap);
142352d94563Sbostic fflush(lfp);
142452d94563Sbostic }
1425bdfadfffSbostic va_end(ap);
1426978e44c9Sralph }
1427978e44c9Sralph
1428bdfadfffSbostic void
1429bdfadfffSbostic #if __STDC__
fatal(const char * fmt,...)1430bdfadfffSbostic fatal(const char *fmt, ...)
1431bdfadfffSbostic #else
1432bdfadfffSbostic fatal(fmt, va_alist)
1433978e44c9Sralph char *fmt;
1434bdfadfffSbostic va_dcl
1435bdfadfffSbostic #endif
1436978e44c9Sralph {
143752d94563Sbostic static FILE *fp;
1438bdfadfffSbostic va_list ap;
1439bdfadfffSbostic #if __STDC__
1440bdfadfffSbostic va_start(ap, fmt);
1441bdfadfffSbostic #else
1442bdfadfffSbostic va_start(ap);
1443bdfadfffSbostic #endif
144452d94563Sbostic
144552d94563Sbostic ++nerrs;
144652d94563Sbostic if (!fp && !(fp = fdopen(rem, "w")))
144752d94563Sbostic return;
144852d94563Sbostic if (iamremote) {
144952d94563Sbostic (void)fprintf(fp, "%crdist: ", 0x02);
1450bdfadfffSbostic (void)vfprintf(fp, fmt, ap);
145152d94563Sbostic fflush(fp);
145252d94563Sbostic }
145352d94563Sbostic else {
1454d49851feSralph fflush(stdout);
145552d94563Sbostic (void)fprintf(stderr, "rdist: ");
1456bdfadfffSbostic (void)vfprintf(stderr, fmt, ap);
145752d94563Sbostic fflush(stderr);
145852d94563Sbostic }
145952d94563Sbostic if (lfp != NULL) {
146052d94563Sbostic (void)fprintf(lfp, "rdist: ");
1461bdfadfffSbostic (void)vfprintf(lfp, fmt, ap);
146252d94563Sbostic fflush(lfp);
146352d94563Sbostic }
1464bdfadfffSbostic cleanup(0);
1465978e44c9Sralph }
1466978e44c9Sralph
1467bdfadfffSbostic static int
response()1468978e44c9Sralph response()
1469978e44c9Sralph {
1470156664fbSralph char *cp, *s;
147115867ad7Sralph char resp[BUFSIZ];
1472978e44c9Sralph
1473978e44c9Sralph if (debug)
1474978e44c9Sralph printf("response()\n");
1475978e44c9Sralph
147615867ad7Sralph cp = s = resp;
1477978e44c9Sralph do {
1478978e44c9Sralph if (read(rem, cp, 1) != 1)
1479bdfadfffSbostic lostconn(0);
148015867ad7Sralph } while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
1481156664fbSralph
1482156664fbSralph switch (*s++) {
1483156664fbSralph case '\0':
1484156664fbSralph *--cp = '\0';
14856d401060Sralph if (*s != '\0') {
1486156664fbSralph log(lfp, "%s\n", s);
14876d401060Sralph return(1);
14886d401060Sralph }
1489156664fbSralph return(0);
149026ce117eSsklower case '\3':
149126ce117eSsklower *--cp = '\0';
149226ce117eSsklower log(lfp, "Note: %s\n",s);
149326ce117eSsklower return(response());
1494156664fbSralph
1495156664fbSralph default:
1496156664fbSralph s--;
1497156664fbSralph /* fall into... */
1498156664fbSralph case '\1':
1499156664fbSralph case '\2':
1500aca7acf4Sralph nerrs++;
1501156664fbSralph if (*s != '\n') {
1502d49851feSralph if (!iamremote) {
1503d49851feSralph fflush(stdout);
1504156664fbSralph (void) write(2, s, cp - s);
1505d49851feSralph }
1506978e44c9Sralph if (lfp != NULL)
1507156664fbSralph (void) fwrite(s, 1, cp - s, lfp);
1508978e44c9Sralph }
150915867ad7Sralph if (resp[0] == '\2')
1510bdfadfffSbostic lostconn(0);
151101de96ecSralph return(-1);
1512978e44c9Sralph }
1513978e44c9Sralph }
1514978e44c9Sralph
1515aca7acf4Sralph /*
1516aca7acf4Sralph * Remove temporary files and do any cleanup operations before exiting.
1517aca7acf4Sralph */
151821439bbcSbostic void
cleanup(signo)1519bdfadfffSbostic cleanup(signo)
1520bdfadfffSbostic int signo;
1521978e44c9Sralph {
152216580127Seric (void) unlink(tempfile);
1523aca7acf4Sralph exit(1);
1524978e44c9Sralph }
152526ce117eSsklower
1526bdfadfffSbostic static void
1527bdfadfffSbostic #if __STDC__
note(const char * fmt,...)1528bdfadfffSbostic note(const char *fmt, ...)
1529bdfadfffSbostic #else
1530bdfadfffSbostic note(fmt, va_alist)
15316d57652cStorek char *fmt;
1532bdfadfffSbostic va_dcl
1533bdfadfffSbostic #endif
153426ce117eSsklower {
153526ce117eSsklower static char buf[BUFSIZ];
1536bdfadfffSbostic va_list ap;
1537bdfadfffSbostic #if __STDC__
1538bdfadfffSbostic va_start(ap, fmt);
1539bdfadfffSbostic #else
1540bdfadfffSbostic va_start(ap);
1541bdfadfffSbostic #endif
1542bdfadfffSbostic (void)vsnprintf(buf, sizeof(buf), fmt, ap);
1543bdfadfffSbostic va_end(ap);
154426ce117eSsklower comment(buf);
154526ce117eSsklower }
154626ce117eSsklower
1547bdfadfffSbostic static void
comment(s)154826ce117eSsklower comment(s)
154926ce117eSsklower char *s;
155026ce117eSsklower {
1551bdfadfffSbostic char c;
1552bdfadfffSbostic
1553bdfadfffSbostic c = '\3';
155426ce117eSsklower write(rem, &c, 1);
155526ce117eSsklower write(rem, s, strlen(s));
155626ce117eSsklower c = '\n';
155726ce117eSsklower write(rem, &c, 1);
155826ce117eSsklower }
1559