1*fbffadb9Smrg /* $NetBSD: server.c,v 1.33 2019/02/03 03:19:29 mrg Exp $ */
23aab4f7dSthorpej
361f28255Scgd /*
4a5bfdf78Scgd * Copyright (c) 1983, 1993
5a5bfdf78Scgd * The Regents of the University of California. All rights reserved.
661f28255Scgd *
761f28255Scgd * Redistribution and use in source and binary forms, with or without
861f28255Scgd * modification, are permitted provided that the following conditions
961f28255Scgd * are met:
1061f28255Scgd * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd * notice, this list of conditions and the following disclaimer.
1261f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd * notice, this list of conditions and the following disclaimer in the
1461f28255Scgd * documentation and/or other materials provided with the distribution.
1589aaa1bbSagc * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd * may be used to endorse or promote products derived from this software
1761f28255Scgd * without specific prior written permission.
1861f28255Scgd *
1961f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd * SUCH DAMAGE.
3061f28255Scgd */
3161f28255Scgd
323b3cf635Slukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
343aab4f7dSthorpej #if 0
353aab4f7dSthorpej static char sccsid[] = "@(#)server.c 8.1 (Berkeley) 6/9/93";
363aab4f7dSthorpej #else
37*fbffadb9Smrg __RCSID("$NetBSD: server.c,v 1.33 2019/02/03 03:19:29 mrg Exp $");
383aab4f7dSthorpej #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd
410026c9e9Smrg #include <sys/types.h>
42a5bfdf78Scgd #include <sys/wait.h>
430026c9e9Smrg
440026c9e9Smrg #include <errno.h>
45b751ad2cSchristos #include <fcntl.h>
46a9b4ca62Swiz #include <grp.h>
47a9b4ca62Swiz #include <pwd.h>
48a9b4ca62Swiz #include <stdarg.h>
490026c9e9Smrg
5061f28255Scgd #include "defs.h"
5161f28255Scgd
52c08b0cf3Smrg #define ack() do { if (write(rem, "\0\n", 2) < 0) error("ack failed: %s\n", strerror(errno)); } while (0)
53c08b0cf3Smrg #define err() do { if (write(rem, "\1\n", 2) < 0) error("err failed: %s\n", strerror(errno)); } while (0)
5461f28255Scgd
5561f28255Scgd struct linkbuf *ihead; /* list of files with more than one link */
5661f28255Scgd char buf[BUFSIZ]; /* general purpose buffer */
5761f28255Scgd char target[BUFSIZ]; /* target/source directory name */
5861f28255Scgd char *tp; /* pointer to end of target name */
5961f28255Scgd char *Tdest; /* pointer to last T dest*/
600428b497Smycroft char *Destcopy; /* pointer to current dest */
610428b497Smycroft int Destcopylen; /* length of destination directory name */
620428b497Smycroft int Sourcelen; /* length of source directory name */
6361f28255Scgd int catname; /* cat name to target name */
6461f28255Scgd char *stp[32]; /* stack of saved tp's for directories */
6561f28255Scgd int oumask; /* old umask for creating files */
6661f28255Scgd
6761f28255Scgd extern FILE *lfp; /* log file for mailing changes */
6861f28255Scgd
69a9b4ca62Swiz static int chkparent(char *);
70a9b4ca62Swiz static void clean(char *);
71a9b4ca62Swiz static void comment(char *);
72a9b4ca62Swiz static void dospecial(char *);
73a9b4ca62Swiz static int fchtogm(int, char *, time_t, char *, char *, mode_t);
74a9b4ca62Swiz static void hardlink(char *);
75a9b4ca62Swiz static void note(const char *, ...)
76135600f9Sis __attribute__((__format__(__printf__, 1, 2)));
77a9b4ca62Swiz static void query(char *);
78a9b4ca62Swiz static void recvf(char *, int);
79a9b4ca62Swiz static void removeit(struct stat *);
80a9b4ca62Swiz static int response(void);
81a9b4ca62Swiz static void rmchk(int);
82a5bfdf78Scgd static struct linkbuf *
83a9b4ca62Swiz savelink(struct stat *);
84a9b4ca62Swiz static void sendf(char *, int);
85a9b4ca62Swiz static int update(char *, int, struct stat *);
8661f28255Scgd
8761f28255Scgd /*
8861f28255Scgd * Server routine to read requests and process them.
8961f28255Scgd * Commands are:
9061f28255Scgd * Tname - Transmit file if out of date
9161f28255Scgd * Vname - Verify if file out of date or not
9261f28255Scgd * Qname - Query if file exists. Return mtime & size if it does.
9361f28255Scgd */
94a5bfdf78Scgd void
server(void)95a9b4ca62Swiz server(void)
9661f28255Scgd {
9761f28255Scgd char cmdbuf[BUFSIZ];
983b3cf635Slukem char *cp;
9961f28255Scgd
10061f28255Scgd signal(SIGHUP, cleanup);
10161f28255Scgd signal(SIGINT, cleanup);
10261f28255Scgd signal(SIGQUIT, cleanup);
10361f28255Scgd signal(SIGTERM, cleanup);
10461f28255Scgd signal(SIGPIPE, cleanup);
10561f28255Scgd
10661f28255Scgd rem = 0;
10761f28255Scgd oumask = umask(0);
108336eeb5fSthorpej (void) snprintf(buf, sizeof(buf), "V%d\n", VERSION);
109c08b0cf3Smrg if (write(rem, buf, strlen(buf)) < 0)
110c08b0cf3Smrg error("server: could not write remote end: %s\n",
111c08b0cf3Smrg strerror(errno));
11261f28255Scgd
11361f28255Scgd for (;;) {
11461f28255Scgd cp = cmdbuf;
11561f28255Scgd if (read(rem, cp, 1) <= 0)
11661f28255Scgd return;
11761f28255Scgd if (*cp++ == '\n') {
11861f28255Scgd error("server: expected control record\n");
11961f28255Scgd continue;
12061f28255Scgd }
12161f28255Scgd do {
12261f28255Scgd if (read(rem, cp, 1) != 1)
123a5bfdf78Scgd cleanup(0);
12461f28255Scgd } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
12561f28255Scgd *--cp = '\0';
12661f28255Scgd cp = cmdbuf;
12761f28255Scgd switch (*cp++) {
12861f28255Scgd case 'T': /* init target file/directory name */
12961f28255Scgd catname = 1; /* target should be directory */
13061f28255Scgd goto dotarget;
13161f28255Scgd
13261f28255Scgd case 't': /* init target file/directory name */
13361f28255Scgd catname = 0;
13461f28255Scgd dotarget:
13561f28255Scgd if (exptilde(target, cp) == NULL)
13661f28255Scgd continue;
13761f28255Scgd tp = target;
13861f28255Scgd while (*tp)
13961f28255Scgd tp++;
14061f28255Scgd ack();
14161f28255Scgd continue;
14261f28255Scgd
14361f28255Scgd case 'R': /* Transfer a regular file. */
14461f28255Scgd recvf(cp, S_IFREG);
14561f28255Scgd continue;
14661f28255Scgd
14761f28255Scgd case 'D': /* Transfer a directory. */
14861f28255Scgd recvf(cp, S_IFDIR);
14961f28255Scgd continue;
15061f28255Scgd
15161f28255Scgd case 'K': /* Transfer symbolic link. */
15261f28255Scgd recvf(cp, S_IFLNK);
15361f28255Scgd continue;
15461f28255Scgd
15561f28255Scgd case 'k': /* Transfer hard link. */
15661f28255Scgd hardlink(cp);
15761f28255Scgd continue;
15861f28255Scgd
15961f28255Scgd case 'E': /* End. (of directory) */
16061f28255Scgd *tp = '\0';
16161f28255Scgd if (catname <= 0) {
16261f28255Scgd error("server: too many 'E's\n");
16361f28255Scgd continue;
16461f28255Scgd }
16561f28255Scgd tp = stp[--catname];
16661f28255Scgd *tp = '\0';
16761f28255Scgd ack();
16861f28255Scgd continue;
16961f28255Scgd
17061f28255Scgd case 'C': /* Clean. Cleanup a directory */
17161f28255Scgd clean(cp);
17261f28255Scgd continue;
17361f28255Scgd
17461f28255Scgd case 'Q': /* Query. Does the file/directory exist? */
17561f28255Scgd query(cp);
17661f28255Scgd continue;
17761f28255Scgd
17861f28255Scgd case 'S': /* Special. Execute commands */
17961f28255Scgd dospecial(cp);
18061f28255Scgd continue;
18161f28255Scgd
18261f28255Scgd #ifdef notdef
18361f28255Scgd /*
18461f28255Scgd * These entries are reserved but not currently used.
18561f28255Scgd * The intent is to allow remote hosts to have master copies.
18661f28255Scgd * Currently, only the host rdist runs on can have masters.
18761f28255Scgd */
18861f28255Scgd case 'X': /* start a new list of files to exclude */
18961f28255Scgd except = bp = NULL;
19061f28255Scgd case 'x': /* add name to list of files to exclude */
19161f28255Scgd if (*cp == '\0') {
19261f28255Scgd ack();
19361f28255Scgd continue;
19461f28255Scgd }
19561f28255Scgd if (*cp == '~') {
19661f28255Scgd if (exptilde(buf, cp) == NULL)
19761f28255Scgd continue;
19861f28255Scgd cp = buf;
19961f28255Scgd }
20061f28255Scgd if (bp == NULL)
20161f28255Scgd except = bp = expand(makeblock(NAME, cp), E_VARS);
20261f28255Scgd else
20361f28255Scgd bp->b_next = expand(makeblock(NAME, cp), E_VARS);
20461f28255Scgd while (bp->b_next != NULL)
20561f28255Scgd bp = bp->b_next;
20661f28255Scgd ack();
20761f28255Scgd continue;
20861f28255Scgd
20961f28255Scgd case 'I': /* Install. Transfer file if out of date. */
21061f28255Scgd opts = 0;
21161f28255Scgd while (*cp >= '0' && *cp <= '7')
21261f28255Scgd opts = (opts << 3) | (*cp++ - '0');
21361f28255Scgd if (*cp++ != ' ') {
21461f28255Scgd error("server: options not delimited\n");
21561f28255Scgd return;
21661f28255Scgd }
21761f28255Scgd install(cp, opts);
21861f28255Scgd continue;
21961f28255Scgd
22061f28255Scgd case 'L': /* Log. save message in log file */
22161f28255Scgd log(lfp, cp);
22261f28255Scgd continue;
22361f28255Scgd #endif
22461f28255Scgd
22561f28255Scgd case '\1':
22661f28255Scgd nerrs++;
22761f28255Scgd continue;
22861f28255Scgd
22961f28255Scgd case '\2':
23061f28255Scgd return;
23161f28255Scgd
23261f28255Scgd default:
23361f28255Scgd error("server: unknown command '%s'\n", cp);
234*fbffadb9Smrg /* FALLTHROUGH */
23561f28255Scgd case '\0':
23661f28255Scgd continue;
23761f28255Scgd }
23861f28255Scgd }
23961f28255Scgd }
24061f28255Scgd
24161f28255Scgd /*
24261f28255Scgd * Update the file(s) if they are different.
24361f28255Scgd * destdir = 1 if destination should be a directory
24461f28255Scgd * (i.e., more than one source is being copied to the same destination).
24561f28255Scgd */
246a5bfdf78Scgd void
install(char * src,char * dest,int destdir,int opts)247a9b4ca62Swiz install(char *src, char *dest, int destdir, int opts)
24861f28255Scgd {
24961f28255Scgd char *rname;
25061f28255Scgd char destcopy[BUFSIZ];
25161f28255Scgd
25261f28255Scgd if (dest == NULL) {
25361f28255Scgd opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
25461f28255Scgd dest = src;
2550428b497Smycroft } else if (!(opts & WHOLE)) {
2560428b497Smycroft /* prepare for proper renaming of directory trees */
2570428b497Smycroft Destcopy = destcopy;
2580428b497Smycroft Destcopylen = strlen(dest);
2590428b497Smycroft while (Destcopylen > 0 && dest[Destcopylen] == '/')
2600428b497Smycroft Destcopylen--;
26161f28255Scgd }
2620428b497Smycroft strlcpy(destcopy, dest, sizeof(destcopy));
26361f28255Scgd
26461f28255Scgd if (nflag || debug) {
26561f28255Scgd printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
26661f28255Scgd opts & WHOLE ? " -w" : "",
26761f28255Scgd opts & YOUNGER ? " -y" : "",
26861f28255Scgd opts & COMPARE ? " -b" : "",
26961f28255Scgd opts & REMOVE ? " -R" : "", src, dest);
27061f28255Scgd if (nflag)
27161f28255Scgd return;
27261f28255Scgd }
27361f28255Scgd
27461f28255Scgd rname = exptilde(target, src);
27561f28255Scgd if (rname == NULL)
27661f28255Scgd return;
27761f28255Scgd tp = target;
27861f28255Scgd while (*tp)
27961f28255Scgd tp++;
2800428b497Smycroft if (Destcopy) {
2810428b497Smycroft /* We can only do this after expansion of src */
2820428b497Smycroft Sourcelen = strlen(target);
2830428b497Smycroft while (Sourcelen > 0 && target[Sourcelen] == '/')
2840428b497Smycroft Sourcelen--;
2850428b497Smycroft }
28661f28255Scgd /*
28761f28255Scgd * If we are renaming a directory and we want to preserve
288251b3464Swiz * the directory hierarchy (-w), we must strip off the leading
28961f28255Scgd * directory name and preserve the rest.
29061f28255Scgd */
29161f28255Scgd if (opts & WHOLE) {
29261f28255Scgd while (*rname == '/')
29361f28255Scgd rname++;
29461f28255Scgd destdir = 1;
29561f28255Scgd } else {
2963b3cf635Slukem rname = strrchr(target, '/');
29761f28255Scgd if (rname == NULL)
29861f28255Scgd rname = target;
29961f28255Scgd else
30061f28255Scgd rname++;
30161f28255Scgd }
30261f28255Scgd if (debug)
30361f28255Scgd printf("target = %s, rname = %s\n", target, rname);
30461f28255Scgd /*
30561f28255Scgd * Pass the destination file/directory name to remote.
30661f28255Scgd */
307336eeb5fSthorpej (void) snprintf(buf, sizeof(buf), "%c%s\n", destdir ? 'T' : 't', dest);
30861f28255Scgd if (debug)
30961f28255Scgd printf("buf = %s", buf);
310c08b0cf3Smrg if (write(rem, buf, strlen(buf)) < 0)
311c08b0cf3Smrg error("could not pass filename to remote: %s\n",
312c08b0cf3Smrg strerror(errno));
31361f28255Scgd if (response() < 0)
31461f28255Scgd return;
31561f28255Scgd
3160428b497Smycroft if (destdir)
31761f28255Scgd Tdest = destcopy;
31861f28255Scgd sendf(rname, opts);
3190428b497Smycroft Destcopy = 0;
32061f28255Scgd Tdest = 0;
32161f28255Scgd }
32261f28255Scgd
32361f28255Scgd #define protoname() (pw ? pw->pw_name : user)
32461f28255Scgd #define protogroup() (gr ? gr->gr_name : group)
32561f28255Scgd /*
32661f28255Scgd * Transfer the file or directory in target[].
32761f28255Scgd * rname is the name of the file on the remote host.
32861f28255Scgd */
329a5bfdf78Scgd static void
sendf(char * rname,int opts)330a9b4ca62Swiz sendf(char *rname, int opts)
33161f28255Scgd {
3323b3cf635Slukem struct subcmd *sc;
33361f28255Scgd struct stat stb;
33461f28255Scgd int sizerr, f, u, len;
33561f28255Scgd off_t i;
33661f28255Scgd DIR *d;
337b751ad2cSchristos struct dirent *dp;
33861f28255Scgd char *otp, *cp;
33961f28255Scgd extern struct subcmd *subcmds;
34061f28255Scgd static char user[15], group[15];
34161f28255Scgd
34261f28255Scgd if (debug)
34361f28255Scgd printf("sendf(%s, %x)\n", rname, opts);
34461f28255Scgd
34561f28255Scgd if (except(target))
34661f28255Scgd return;
34761f28255Scgd if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
34861f28255Scgd error("%s: %s\n", target, strerror(errno));
34961f28255Scgd return;
35061f28255Scgd }
35161f28255Scgd if ((u = update(rname, opts, &stb)) == 0) {
352985cca83Smycroft if (S_ISREG(stb.st_mode) && stb.st_nlink > 1)
35361f28255Scgd (void) savelink(&stb);
35461f28255Scgd return;
35561f28255Scgd }
35661f28255Scgd
35761f28255Scgd if (pw == NULL || pw->pw_uid != stb.st_uid)
35861f28255Scgd if ((pw = getpwuid(stb.st_uid)) == NULL) {
359f393397bSthorpej dolog(lfp, "%s: no password entry for uid %d \n",
36061f28255Scgd target, stb.st_uid);
36161f28255Scgd pw = NULL;
3623b3cf635Slukem (void)snprintf(user, sizeof(user), ":%lu",
3633b3cf635Slukem (u_long)stb.st_uid);
36461f28255Scgd }
36561f28255Scgd if (gr == NULL || gr->gr_gid != stb.st_gid)
36661f28255Scgd if ((gr = getgrgid(stb.st_gid)) == NULL) {
367f393397bSthorpej dolog(lfp, "%s: no name for group %d\n",
36861f28255Scgd target, stb.st_gid);
36961f28255Scgd gr = NULL;
370336eeb5fSthorpej (void)snprintf(group, sizeof(group), ":%lu",
3713b3cf635Slukem (u_long)stb.st_gid);
37261f28255Scgd }
37361f28255Scgd if (u == 1) {
37461f28255Scgd if (opts & VERIFY) {
375f393397bSthorpej dolog(lfp, "need to install: %s\n", target);
37661f28255Scgd goto dospecial;
37761f28255Scgd }
378f393397bSthorpej dolog(lfp, "installing: %s\n", target);
37961f28255Scgd opts &= ~(COMPARE|REMOVE);
38061f28255Scgd }
38161f28255Scgd
38261f28255Scgd switch (stb.st_mode & S_IFMT) {
38361f28255Scgd case S_IFDIR:
38461f28255Scgd if ((d = opendir(target)) == NULL) {
38561f28255Scgd error("%s: %s\n", target, strerror(errno));
38661f28255Scgd return;
38761f28255Scgd }
388336eeb5fSthorpej (void) snprintf(buf, sizeof(buf), "D%o %04o 0 0 %s %s %s\n",
389336eeb5fSthorpej opts, stb.st_mode & 07777, protoname(), protogroup(),
390336eeb5fSthorpej rname);
39161f28255Scgd if (debug)
39261f28255Scgd printf("buf = %s", buf);
393c08b0cf3Smrg if (write(rem, buf, strlen(buf)) < 0)
394c08b0cf3Smrg error("can not write dir spec to remote: %s\n",
395c08b0cf3Smrg strerror(errno));
396c08b0cf3Smrg
39761f28255Scgd if (response() < 0) {
39861f28255Scgd closedir(d);
39961f28255Scgd return;
40061f28255Scgd }
40161f28255Scgd
40261f28255Scgd if (opts & REMOVE)
40361f28255Scgd rmchk(opts);
40461f28255Scgd
40561f28255Scgd otp = tp;
40661f28255Scgd len = tp - target;
4073b3cf635Slukem while ((dp = readdir(d)) != NULL) {
40861f28255Scgd if (!strcmp(dp->d_name, ".") ||
40961f28255Scgd !strcmp(dp->d_name, ".."))
41061f28255Scgd continue;
41161f28255Scgd if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
41261f28255Scgd error("%s/%s: Name too long\n", target,
41361f28255Scgd dp->d_name);
41461f28255Scgd continue;
41561f28255Scgd }
41661f28255Scgd tp = otp;
41761f28255Scgd *tp++ = '/';
41861f28255Scgd cp = dp->d_name;
4193b3cf635Slukem while ((*tp++ = *cp++) != 0)
42061f28255Scgd ;
42161f28255Scgd tp--;
42261f28255Scgd sendf(dp->d_name, opts);
42361f28255Scgd }
42461f28255Scgd closedir(d);
425c08b0cf3Smrg if (write(rem, "E\n", 2) < 0)
426c08b0cf3Smrg error("can not write E to remote: %s\n",
427c08b0cf3Smrg strerror(errno));
42861f28255Scgd (void) response();
42961f28255Scgd tp = otp;
43061f28255Scgd *tp = '\0';
43161f28255Scgd return;
43261f28255Scgd
43361f28255Scgd case S_IFLNK:
43461f28255Scgd if (u != 1)
43561f28255Scgd opts |= COMPARE;
43661f28255Scgd if (stb.st_nlink > 1) {
43761f28255Scgd struct linkbuf *lp;
43861f28255Scgd
43961f28255Scgd if ((lp = savelink(&stb)) != NULL) {
44061f28255Scgd /* install link */
44161f28255Scgd if (*lp->target == 0)
442336eeb5fSthorpej (void) snprintf(buf, sizeof(buf),
443336eeb5fSthorpej "k%o %s %s\n", opts, lp->pathname, rname);
44461f28255Scgd else
445336eeb5fSthorpej (void) snprintf(buf, sizeof(buf),
446336eeb5fSthorpej "k%o %s/%s %s\n", opts, lp->target,
447336eeb5fSthorpej lp->pathname, rname);
44861f28255Scgd if (debug)
44961f28255Scgd printf("buf = %s", buf);
450c08b0cf3Smrg if (write(rem, buf, strlen(buf)) < 0)
451c08b0cf3Smrg error("can not write link spec to remote: %s\n",
452c08b0cf3Smrg strerror(errno));
45361f28255Scgd (void) response();
45461f28255Scgd return;
45561f28255Scgd }
45661f28255Scgd }
457c2b84904Slukem (void) snprintf(buf, sizeof(buf), "K%o %o %lld %ld %s %s %s\n",
458538e6929Smrg opts, stb.st_mode & 07777, (unsigned long long)stb.st_size,
459538e6929Smrg (u_long)stb.st_mtime, protoname(), protogroup(), rname);
46061f28255Scgd if (debug)
46161f28255Scgd printf("buf = %s", buf);
462c08b0cf3Smrg if (write(rem, buf, strlen(buf)) < 0)
463c08b0cf3Smrg error("can not write link spec to remote: %s\n",
464c08b0cf3Smrg strerror(errno));
46561f28255Scgd if (response() < 0)
46661f28255Scgd return;
46761f28255Scgd sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
468c08b0cf3Smrg if (write(rem, buf, stb.st_size) < 0)
469c08b0cf3Smrg error("can not write link name to remote: %s\n",
470c08b0cf3Smrg strerror(errno));
47161f28255Scgd if (debug)
47261f28255Scgd printf("readlink = %.*s\n", (int)stb.st_size, buf);
47361f28255Scgd goto done;
47461f28255Scgd
47561f28255Scgd case S_IFREG:
47661f28255Scgd break;
47761f28255Scgd
47861f28255Scgd default:
47961f28255Scgd error("%s: not a file or directory\n", target);
48061f28255Scgd return;
48161f28255Scgd }
48261f28255Scgd
48361f28255Scgd if (u == 2) {
48461f28255Scgd if (opts & VERIFY) {
485f393397bSthorpej dolog(lfp, "need to update: %s\n", target);
48661f28255Scgd goto dospecial;
48761f28255Scgd }
488f393397bSthorpej dolog(lfp, "updating: %s\n", target);
48961f28255Scgd }
49061f28255Scgd
49161f28255Scgd if (stb.st_nlink > 1) {
49261f28255Scgd struct linkbuf *lp;
49361f28255Scgd
49461f28255Scgd if ((lp = savelink(&stb)) != NULL) {
49561f28255Scgd /* install link */
49661f28255Scgd if (*lp->target == 0)
497336eeb5fSthorpej (void) snprintf(buf, sizeof(buf), "k%o %s %s\n", opts,
49861f28255Scgd lp->pathname, rname);
49961f28255Scgd else
500336eeb5fSthorpej (void) snprintf(buf, sizeof(buf), "k%o %s/%s %s\n",
501336eeb5fSthorpej opts, lp->target, lp->pathname, rname);
50261f28255Scgd if (debug)
50361f28255Scgd printf("buf = %s", buf);
504c08b0cf3Smrg if (write(rem, buf, strlen(buf)) <0)
505c08b0cf3Smrg error("write of file name failed: %s\n",
506c08b0cf3Smrg strerror(errno));
50761f28255Scgd (void) response();
50861f28255Scgd return;
50961f28255Scgd }
51061f28255Scgd }
51161f28255Scgd
512a5bfdf78Scgd if ((f = open(target, O_RDONLY, 0)) < 0) {
51361f28255Scgd error("%s: %s\n", target, strerror(errno));
51461f28255Scgd return;
51561f28255Scgd }
516c2b84904Slukem (void)snprintf(buf, sizeof(buf), "R%o %o %lld %lu %s %s %s\n", opts,
517538e6929Smrg stb.st_mode & 07777, (unsigned long long)stb.st_size,
518538e6929Smrg (u_long)stb.st_mtime, protoname(), protogroup(), rname);
51961f28255Scgd if (debug)
52061f28255Scgd printf("buf = %s", buf);
521c08b0cf3Smrg if (write(rem, buf, strlen(buf)) < 0)
522c08b0cf3Smrg error("write of file name failed: %s\n", strerror(errno));
52361f28255Scgd if (response() < 0) {
52461f28255Scgd (void) close(f);
52561f28255Scgd return;
52661f28255Scgd }
52761f28255Scgd sizerr = 0;
52861f28255Scgd for (i = 0; i < stb.st_size; i += BUFSIZ) {
52961f28255Scgd int amt = BUFSIZ;
53061f28255Scgd if (i + amt > stb.st_size)
53161f28255Scgd amt = stb.st_size - i;
53261f28255Scgd if (sizerr == 0 && read(f, buf, amt) != amt)
53361f28255Scgd sizerr = 1;
534c08b0cf3Smrg if (write(rem, buf, amt) < 0)
535c08b0cf3Smrg error("write of file data failed: %s\n", strerror(errno));
53661f28255Scgd }
53761f28255Scgd (void) close(f);
53861f28255Scgd done:
53961f28255Scgd if (sizerr) {
54061f28255Scgd error("%s: file changed size\n", target);
54161f28255Scgd err();
54261f28255Scgd } else
54361f28255Scgd ack();
54461f28255Scgd f = response();
5453b3cf635Slukem if (f < 0 || (f == 0 && (opts & COMPARE)))
54661f28255Scgd return;
54761f28255Scgd dospecial:
54861f28255Scgd for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
54961f28255Scgd if (sc->sc_type != SPECIAL)
55061f28255Scgd continue;
55161f28255Scgd if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
55261f28255Scgd continue;
553f393397bSthorpej dolog(lfp, "special \"%s\"\n", sc->sc_name);
55461f28255Scgd if (opts & VERIFY)
55561f28255Scgd continue;
556336eeb5fSthorpej (void) snprintf(buf, sizeof(buf), "SFILE=%s;%s\n", target,
557336eeb5fSthorpej sc->sc_name);
55861f28255Scgd if (debug)
55961f28255Scgd printf("buf = %s", buf);
560c08b0cf3Smrg if (write(rem, buf, strlen(buf)) < 0)
561c08b0cf3Smrg error("write of special failed: %s\n", strerror(errno));
56261f28255Scgd while (response() > 0)
56361f28255Scgd ;
56461f28255Scgd }
56561f28255Scgd }
56661f28255Scgd
567a5bfdf78Scgd static struct linkbuf *
savelink(struct stat * st)5684538623fSlukem savelink(struct stat *st)
56961f28255Scgd {
57061f28255Scgd struct linkbuf *lp;
57161f28255Scgd
57261f28255Scgd for (lp = ihead; lp != NULL; lp = lp->nextp)
5734538623fSlukem if (lp->inum == st->st_ino && lp->devnum == st->st_dev) {
57461f28255Scgd lp->count--;
57561f28255Scgd return(lp);
57661f28255Scgd }
57761f28255Scgd lp = (struct linkbuf *) malloc(sizeof(*lp));
57861f28255Scgd if (lp == NULL)
579f393397bSthorpej dolog(lfp, "out of memory, link information lost\n");
58061f28255Scgd else {
58161f28255Scgd lp->nextp = ihead;
58261f28255Scgd ihead = lp;
5834538623fSlukem lp->inum = st->st_ino;
5844538623fSlukem lp->devnum = st->st_dev;
5854538623fSlukem lp->count = st->st_nlink - 1;
5860428b497Smycroft if (Destcopy) {
5870428b497Smycroft /*
5880428b497Smycroft * Change the starting directory of target
5890428b497Smycroft * into the destination directory
5900428b497Smycroft */
5910428b497Smycroft strncpy(lp->pathname, Destcopy, Destcopylen);
5920428b497Smycroft strlcpy(lp->pathname + Destcopylen, target + Sourcelen, sizeof(lp->pathname) - Destcopylen);
5930428b497Smycroft } else
594e97c220eSitojun strlcpy(lp->pathname, target, sizeof(lp->pathname));
59561f28255Scgd if (Tdest)
596e97c220eSitojun strlcpy(lp->target, Tdest, sizeof(lp->target));
59761f28255Scgd else
59861f28255Scgd *lp->target = 0;
59961f28255Scgd }
60061f28255Scgd return(NULL);
60161f28255Scgd }
60261f28255Scgd
60361f28255Scgd /*
60461f28255Scgd * Check to see if file needs to be updated on the remote machine.
60561f28255Scgd * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
60661f28255Scgd * and 3 if comparing binaries to determine if out of date.
60761f28255Scgd */
608a5bfdf78Scgd static int
update(char * rname,int opts,struct stat * st)6094538623fSlukem update(char *rname, int opts, struct stat *st)
61061f28255Scgd {
6113b3cf635Slukem char *cp, *s;
6123b3cf635Slukem off_t size;
6133b3cf635Slukem time_t mtime;
61461f28255Scgd
61561f28255Scgd if (debug)
6164538623fSlukem printf("update(%s, %lx, %lx)\n", rname, (long)opts, (long)st);
61761f28255Scgd
61861f28255Scgd /*
61961f28255Scgd * Check to see if the file exists on the remote machine.
62061f28255Scgd */
621336eeb5fSthorpej (void) snprintf(buf, sizeof(buf), "Q%s\n", rname);
62261f28255Scgd if (debug)
62361f28255Scgd printf("buf = %s", buf);
624c08b0cf3Smrg if (write(rem, buf, strlen(buf)) < 0)
625c08b0cf3Smrg error("write to remote failed: %s\n", strerror(errno));
62661f28255Scgd again:
62761f28255Scgd cp = s = buf;
62861f28255Scgd do {
62961f28255Scgd if (read(rem, cp, 1) != 1)
630a5bfdf78Scgd lostconn(0);
63161f28255Scgd } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
63261f28255Scgd
63361f28255Scgd switch (*s++) {
63461f28255Scgd case 'Y':
63561f28255Scgd break;
63661f28255Scgd
63761f28255Scgd case 'N': /* file doesn't exist so install it */
63861f28255Scgd return(1);
63961f28255Scgd
64061f28255Scgd case '\1':
64161f28255Scgd nerrs++;
64261f28255Scgd if (*s != '\n') {
64361f28255Scgd if (!iamremote) {
64461f28255Scgd fflush(stdout);
64561f28255Scgd (void) write(2, s, cp - s);
64661f28255Scgd }
64761f28255Scgd if (lfp != NULL)
64861f28255Scgd (void) fwrite(s, 1, cp - s, lfp);
64961f28255Scgd }
65061f28255Scgd return(0);
65161f28255Scgd
65261f28255Scgd case '\3':
65361f28255Scgd *--cp = '\0';
65461f28255Scgd if (lfp != NULL)
655f393397bSthorpej dolog(lfp, "update: note: %s\n", s);
65661f28255Scgd goto again;
65761f28255Scgd
65861f28255Scgd default:
65961f28255Scgd *--cp = '\0';
66061f28255Scgd error("update: unexpected response '%s'\n", s);
66161f28255Scgd return(0);
66261f28255Scgd }
66361f28255Scgd
66461f28255Scgd if (*s == '\n')
66561f28255Scgd return(2);
66661f28255Scgd
66761f28255Scgd if (opts & COMPARE)
66861f28255Scgd return(3);
66961f28255Scgd
67061f28255Scgd size = 0;
6717b232cdfSchristos while (isdigit((unsigned char)*s))
67261f28255Scgd size = size * 10 + (*s++ - '0');
67361f28255Scgd if (*s++ != ' ') {
67461f28255Scgd error("update: size not delimited\n");
67561f28255Scgd return(0);
67661f28255Scgd }
67761f28255Scgd mtime = 0;
6787b232cdfSchristos while (isdigit((unsigned char)*s))
67961f28255Scgd mtime = mtime * 10 + (*s++ - '0');
68061f28255Scgd if (*s != '\n') {
68161f28255Scgd error("update: mtime not delimited\n");
68261f28255Scgd return(0);
68361f28255Scgd }
68461f28255Scgd /*
68561f28255Scgd * File needs to be updated?
68661f28255Scgd */
68761f28255Scgd if (opts & YOUNGER) {
6884538623fSlukem if (st->st_mtime == mtime)
68961f28255Scgd return(0);
6904538623fSlukem if (st->st_mtime < mtime) {
691f393397bSthorpej dolog(lfp, "Warning: %s: remote copy is newer\n",
692f393397bSthorpej target);
69361f28255Scgd return(0);
69461f28255Scgd }
6954538623fSlukem } else if (st->st_mtime == mtime && st->st_size == size)
69661f28255Scgd return(0);
69761f28255Scgd return(2);
69861f28255Scgd }
69961f28255Scgd
70061f28255Scgd /*
70161f28255Scgd * Query. Check to see if file exists. Return one of the following:
70261f28255Scgd * N\n - doesn't exist
70361f28255Scgd * Ysize mtime\n - exists and its a regular file (size & mtime of file)
70461f28255Scgd * Y\n - exists and its a directory or symbolic link
70561f28255Scgd * ^Aerror message\n
70661f28255Scgd */
707a5bfdf78Scgd static void
query(char * name)708a9b4ca62Swiz query(char *name)
70961f28255Scgd {
71061f28255Scgd struct stat stb;
71161f28255Scgd
71261f28255Scgd if (catname)
713cf0afdf8Sthorpej (void) snprintf(tp, sizeof(target) - (tp - target),
714cf0afdf8Sthorpej "/%s", name);
71561f28255Scgd
71661f28255Scgd if (lstat(target, &stb) < 0) {
717c08b0cf3Smrg if (errno == ENOENT) {
718c08b0cf3Smrg if (write(rem, "N\n", 2) < 0)
719c08b0cf3Smrg error("write to remote failed: %s\n",
720c08b0cf3Smrg strerror(errno));
721c08b0cf3Smrg } else
72261f28255Scgd error("%s:%s: %s\n", host, target, strerror(errno));
72361f28255Scgd *tp = '\0';
72461f28255Scgd return;
72561f28255Scgd }
72661f28255Scgd
72761f28255Scgd switch (stb.st_mode & S_IFMT) {
72861f28255Scgd case S_IFREG:
729c2b84904Slukem (void)snprintf(buf, sizeof(buf), "Y%lld %ld\n",
730538e6929Smrg (unsigned long long)stb.st_size, (u_long)stb.st_mtime);
731c08b0cf3Smrg if (write(rem, buf, strlen(buf)) < 0)
732c08b0cf3Smrg error("write to remote failed: %s\n", strerror(errno));
73361f28255Scgd break;
73461f28255Scgd
73561f28255Scgd case S_IFLNK:
73661f28255Scgd case S_IFDIR:
737c08b0cf3Smrg if (write(rem, "Y\n", 2) < 0)
738c08b0cf3Smrg error("write to remote failed: %s\n", strerror(errno));
73961f28255Scgd break;
74061f28255Scgd
74161f28255Scgd default:
74261f28255Scgd error("%s: not a file or directory\n", name);
74361f28255Scgd break;
74461f28255Scgd }
74561f28255Scgd *tp = '\0';
74661f28255Scgd }
74761f28255Scgd
748a5bfdf78Scgd static void
recvf(char * cmd,int type)749a9b4ca62Swiz recvf(char *cmd, int type)
75061f28255Scgd {
7513b3cf635Slukem char *cp = cmd;
752b52ab07dSchristos int f = -1, opts = 0, wrerr;
753985cca83Smycroft mode_t mode;
75461f28255Scgd off_t i, size;
75561f28255Scgd time_t mtime;
75661f28255Scgd struct stat stb;
75761f28255Scgd char *owner, *group;
75861f28255Scgd char new[BUFSIZ];
75961f28255Scgd extern char *tempname;
76061f28255Scgd
76161f28255Scgd while (*cp >= '0' && *cp <= '7')
76261f28255Scgd opts = (opts << 3) | (*cp++ - '0');
76361f28255Scgd if (*cp++ != ' ') {
76461f28255Scgd error("recvf: options not delimited\n");
76561f28255Scgd return;
76661f28255Scgd }
76761f28255Scgd mode = 0;
76861f28255Scgd while (*cp >= '0' && *cp <= '7')
76961f28255Scgd mode = (mode << 3) | (*cp++ - '0');
77061f28255Scgd if (*cp++ != ' ') {
77161f28255Scgd error("recvf: mode not delimited\n");
77261f28255Scgd return;
77361f28255Scgd }
77461f28255Scgd size = 0;
7757b232cdfSchristos while (isdigit((unsigned char)*cp))
77661f28255Scgd size = size * 10 + (*cp++ - '0');
77761f28255Scgd if (*cp++ != ' ') {
77861f28255Scgd error("recvf: size not delimited\n");
77961f28255Scgd return;
78061f28255Scgd }
78161f28255Scgd mtime = 0;
7827b232cdfSchristos while (isdigit((unsigned char)*cp))
78361f28255Scgd mtime = mtime * 10 + (*cp++ - '0');
78461f28255Scgd if (*cp++ != ' ') {
78561f28255Scgd error("recvf: mtime not delimited\n");
78661f28255Scgd return;
78761f28255Scgd }
78861f28255Scgd owner = cp;
78961f28255Scgd while (*cp && *cp != ' ')
79061f28255Scgd cp++;
79161f28255Scgd if (*cp != ' ') {
79261f28255Scgd error("recvf: owner name not delimited\n");
79361f28255Scgd return;
79461f28255Scgd }
79561f28255Scgd *cp++ = '\0';
79661f28255Scgd group = cp;
79761f28255Scgd while (*cp && *cp != ' ')
79861f28255Scgd cp++;
79961f28255Scgd if (*cp != ' ') {
80061f28255Scgd error("recvf: group name not delimited\n");
80161f28255Scgd return;
80261f28255Scgd }
80361f28255Scgd *cp++ = '\0';
80461f28255Scgd
80561f28255Scgd if (type == S_IFDIR) {
8064538623fSlukem if (catname >= (int)sizeof(stp)) {
80761f28255Scgd error("%s:%s: too many directory levels\n",
80861f28255Scgd host, target);
80961f28255Scgd return;
81061f28255Scgd }
81161f28255Scgd stp[catname] = tp;
81261f28255Scgd if (catname++) {
81361f28255Scgd *tp++ = '/';
8143b3cf635Slukem while ((*tp++ = *cp++) != 0)
81561f28255Scgd ;
81661f28255Scgd tp--;
81761f28255Scgd }
81861f28255Scgd if (opts & VERIFY) {
81961f28255Scgd ack();
82061f28255Scgd return;
82161f28255Scgd }
82261f28255Scgd if (lstat(target, &stb) == 0) {
823985cca83Smycroft if (S_ISDIR(stb.st_mode)) {
82461f28255Scgd if ((stb.st_mode & 07777) == mode) {
82561f28255Scgd ack();
82661f28255Scgd return;
82761f28255Scgd }
82861f28255Scgd buf[0] = '\0';
829336eeb5fSthorpej (void) snprintf(buf + 1, sizeof(buf) - 1,
83061f28255Scgd "%s: Warning: remote mode %o != local mode %o\n",
83161f28255Scgd target, stb.st_mode & 07777, mode);
832c08b0cf3Smrg if (write(rem, buf, strlen(buf + 1) + 1) < 0)
833c08b0cf3Smrg error("write to remote failed: %s\n",
834c08b0cf3Smrg strerror(errno));
83561f28255Scgd return;
83661f28255Scgd }
83761f28255Scgd errno = ENOTDIR;
838985cca83Smycroft } else if ((errno == ENOENT && mkdir(target, mode) == 0) ||
8393b3cf635Slukem (chkparent(target) == 0 && mkdir(target, mode) == 0)) {
840985cca83Smycroft if (fchtogm(-1, target, mtime, owner, group, mode) == 0)
84161f28255Scgd ack();
84261f28255Scgd return;
84361f28255Scgd }
84461f28255Scgd error("%s:%s: %s\n", host, target, strerror(errno));
84561f28255Scgd tp = stp[--catname];
84661f28255Scgd *tp = '\0';
84761f28255Scgd return;
84861f28255Scgd }
84961f28255Scgd
85061f28255Scgd if (catname)
851cf0afdf8Sthorpej (void) snprintf(tp, sizeof(target) - (tp - target), "/%s", cp);
8523b3cf635Slukem cp = strrchr(target, '/');
85361f28255Scgd if (cp == NULL)
854e97c220eSitojun strlcpy(new, tempname, sizeof(new));
85561f28255Scgd else if (cp == target)
856336eeb5fSthorpej (void) snprintf(new, sizeof(new), "/%s", tempname);
85761f28255Scgd else {
85861f28255Scgd *cp = '\0';
859336eeb5fSthorpej (void) snprintf(new, sizeof(new), "%s/%s", target, tempname);
86061f28255Scgd *cp = '/';
86161f28255Scgd }
86261f28255Scgd
86361f28255Scgd if (type == S_IFLNK) {
86461f28255Scgd int j;
86561f28255Scgd
86661f28255Scgd ack();
86761f28255Scgd cp = buf;
86861f28255Scgd for (i = 0; i < size; i += j) {
86961f28255Scgd if ((j = read(rem, cp, size - i)) <= 0)
870a5bfdf78Scgd cleanup(0);
87161f28255Scgd cp += j;
87261f28255Scgd }
87361f28255Scgd *cp = '\0';
87461f28255Scgd if (response() < 0) {
87561f28255Scgd err();
87661f28255Scgd return;
87761f28255Scgd }
87861f28255Scgd if (symlink(buf, new) < 0) {
87961f28255Scgd if (errno != ENOENT || chkparent(new) < 0 ||
88061f28255Scgd symlink(buf, new) < 0)
881a5bfdf78Scgd goto badnew1;
88261f28255Scgd }
88361f28255Scgd mode &= 0777;
88461f28255Scgd if (opts & COMPARE) {
88561f28255Scgd char tbuf[BUFSIZ];
88661f28255Scgd
88761f28255Scgd if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
88861f28255Scgd i == size && strncmp(buf, tbuf, size) == 0) {
88961f28255Scgd (void) unlink(new);
89061f28255Scgd ack();
89161f28255Scgd return;
89261f28255Scgd }
89361f28255Scgd if (opts & VERIFY)
89461f28255Scgd goto differ;
89561f28255Scgd }
89661f28255Scgd goto fixup;
89761f28255Scgd }
89861f28255Scgd
89961f28255Scgd if ((f = creat(new, mode)) < 0) {
90061f28255Scgd if (errno != ENOENT || chkparent(new) < 0 ||
90161f28255Scgd (f = creat(new, mode)) < 0)
902a5bfdf78Scgd goto badnew1;
90361f28255Scgd }
90461f28255Scgd
90561f28255Scgd ack();
90661f28255Scgd wrerr = 0;
90761f28255Scgd for (i = 0; i < size; i += BUFSIZ) {
90861f28255Scgd int amt = BUFSIZ;
90961f28255Scgd
91061f28255Scgd cp = buf;
91161f28255Scgd if (i + amt > size)
91261f28255Scgd amt = size - i;
91361f28255Scgd do {
91461f28255Scgd int j = read(rem, cp, amt);
91561f28255Scgd
91661f28255Scgd if (j <= 0) {
91761f28255Scgd (void) close(f);
91861f28255Scgd (void) unlink(new);
919a5bfdf78Scgd cleanup(0);
92061f28255Scgd }
92161f28255Scgd amt -= j;
92261f28255Scgd cp += j;
92361f28255Scgd } while (amt > 0);
92461f28255Scgd amt = BUFSIZ;
92561f28255Scgd if (i + amt > size)
92661f28255Scgd amt = size - i;
92761f28255Scgd if (wrerr == 0 && write(f, buf, amt) != amt) {
92861f28255Scgd wrerr++;
92961f28255Scgd }
93061f28255Scgd }
93161f28255Scgd if (response() < 0) {
93261f28255Scgd err();
933a5bfdf78Scgd goto badnew2;
93461f28255Scgd }
935a5bfdf78Scgd if (wrerr)
936a5bfdf78Scgd goto badnew1;
93761f28255Scgd if (opts & COMPARE) {
93861f28255Scgd FILE *f1, *f2;
93961f28255Scgd int c;
94061f28255Scgd
94161f28255Scgd if ((f1 = fopen(target, "r")) == NULL)
942a5bfdf78Scgd goto badtarget;
94361f28255Scgd if ((f2 = fopen(new, "r")) == NULL) {
944a5bfdf78Scgd badnew1: error("%s:%s: %s\n", host, new, strerror(errno));
945a5bfdf78Scgd goto badnew2;
94661f28255Scgd }
94761f28255Scgd while ((c = getc(f1)) == getc(f2))
94861f28255Scgd if (c == EOF) {
94961f28255Scgd (void) fclose(f1);
95061f28255Scgd (void) fclose(f2);
95161f28255Scgd ack();
952a5bfdf78Scgd goto badnew2;
95361f28255Scgd }
95461f28255Scgd (void) fclose(f1);
95561f28255Scgd (void) fclose(f2);
95661f28255Scgd if (opts & VERIFY) {
957a5bfdf78Scgd differ: buf[0] = '\0';
958336eeb5fSthorpej (void)snprintf(buf + 1, sizeof(buf) - 1,
959336eeb5fSthorpej "need to update: %s\n",target);
96061f28255Scgd (void) write(rem, buf, strlen(buf + 1) + 1);
961a5bfdf78Scgd goto badnew2;
96261f28255Scgd }
96361f28255Scgd }
96461f28255Scgd
965985cca83Smycroft if (fchtogm(f, new, mtime, owner, group, mode) < 0) {
966bd664fdbSmrg badnew2:
967bd664fdbSmrg if (f != -1)
968bd664fdbSmrg (void) close(f);
96961f28255Scgd (void) unlink(new);
97061f28255Scgd return;
97161f28255Scgd }
972a5bfdf78Scgd (void) close(f);
973a5bfdf78Scgd
974a5bfdf78Scgd fixup: if (rename(new, target) < 0) {
975a5bfdf78Scgd badtarget: error("%s:%s: %s\n", host, target, strerror(errno));
97661f28255Scgd (void) unlink(new);
97761f28255Scgd return;
97861f28255Scgd }
979a5bfdf78Scgd
98061f28255Scgd if (opts & COMPARE) {
98161f28255Scgd buf[0] = '\0';
982336eeb5fSthorpej (void) snprintf(buf + 1, sizeof(buf) - 1,
983336eeb5fSthorpej "updated %s\n", target);
98461f28255Scgd (void) write(rem, buf, strlen(buf + 1) + 1);
98561f28255Scgd } else
98661f28255Scgd ack();
98761f28255Scgd }
98861f28255Scgd
98961f28255Scgd /*
99061f28255Scgd * Creat a hard link to existing file.
99161f28255Scgd */
992a5bfdf78Scgd static void
hardlink(char * cmd)993a9b4ca62Swiz hardlink(char *cmd)
99461f28255Scgd {
9953b3cf635Slukem char *cp;
99661f28255Scgd struct stat stb;
99761f28255Scgd char *oldname;
99861f28255Scgd int opts, exists = 0;
99961f28255Scgd
100061f28255Scgd cp = cmd;
100161f28255Scgd opts = 0;
100261f28255Scgd while (*cp >= '0' && *cp <= '7')
100361f28255Scgd opts = (opts << 3) | (*cp++ - '0');
100461f28255Scgd if (*cp++ != ' ') {
100561f28255Scgd error("hardlink: options not delimited\n");
100661f28255Scgd return;
100761f28255Scgd }
100861f28255Scgd oldname = cp;
100961f28255Scgd while (*cp && *cp != ' ')
101061f28255Scgd cp++;
101161f28255Scgd if (*cp != ' ') {
101261f28255Scgd error("hardlink: oldname name not delimited\n");
101361f28255Scgd return;
101461f28255Scgd }
101561f28255Scgd *cp++ = '\0';
101661f28255Scgd
101761f28255Scgd if (catname) {
1018cf0afdf8Sthorpej (void) snprintf(tp, sizeof(target) - (tp - target), "/%s", cp);
101961f28255Scgd }
102061f28255Scgd if (lstat(target, &stb) == 0) {
1021985cca83Smycroft if (!S_ISREG(stb.st_mode) && !S_ISLNK(stb.st_mode)) {
102261f28255Scgd error("%s: %s: not a regular file\n", host, target);
102361f28255Scgd return;
102461f28255Scgd }
102561f28255Scgd exists = 1;
102661f28255Scgd }
102761f28255Scgd if (chkparent(target) < 0 ) {
102861f28255Scgd error("%s:%s: %s (no parent)\n",
102961f28255Scgd host, target, strerror(errno));
103061f28255Scgd return;
103161f28255Scgd }
103261f28255Scgd if (exists && (unlink(target) < 0)) {
103361f28255Scgd error("%s:%s: %s (unlink)\n",
103461f28255Scgd host, target, strerror(errno));
103561f28255Scgd return;
103661f28255Scgd }
103761f28255Scgd if (link(oldname, target) < 0) {
103861f28255Scgd error("%s:can't link %s to %s\n",
103961f28255Scgd host, target, oldname);
104061f28255Scgd return;
104161f28255Scgd }
104261f28255Scgd ack();
104361f28255Scgd }
104461f28255Scgd
104561f28255Scgd /*
104661f28255Scgd * Check to see if parent directory exists and create one if not.
104761f28255Scgd */
1048a5bfdf78Scgd static int
chkparent(char * name)1049a9b4ca62Swiz chkparent(char *name)
105061f28255Scgd {
10513b3cf635Slukem char *cp;
105261f28255Scgd struct stat stb;
105361f28255Scgd
10543b3cf635Slukem cp = strrchr(name, '/');
105561f28255Scgd if (cp == NULL || cp == name)
105661f28255Scgd return(0);
105761f28255Scgd *cp = '\0';
105861f28255Scgd if (lstat(name, &stb) < 0) {
105961f28255Scgd if (errno == ENOENT && chkparent(name) >= 0 &&
106061f28255Scgd mkdir(name, 0777 & ~oumask) >= 0) {
106161f28255Scgd *cp = '/';
106261f28255Scgd return(0);
106361f28255Scgd }
1064985cca83Smycroft } else if (S_ISDIR(stb.st_mode)) {
106561f28255Scgd *cp = '/';
106661f28255Scgd return(0);
106761f28255Scgd }
106861f28255Scgd *cp = '/';
106961f28255Scgd return(-1);
107061f28255Scgd }
107161f28255Scgd
107261f28255Scgd /*
107361f28255Scgd * Change owner, group and mode of file.
107461f28255Scgd */
1075a5bfdf78Scgd static int
fchtogm(int fd,char * file,time_t mtime,char * owner,char * group,__mode_t mode)1076a9b4ca62Swiz fchtogm(int fd, char *file, time_t mtime, char *owner, char *group, __mode_t mode)
107761f28255Scgd {
10783b3cf635Slukem int i;
1079985cca83Smycroft struct timeval tv[2];
1080985cca83Smycroft uid_t uid;
1081985cca83Smycroft gid_t gid;
108261f28255Scgd extern char user[];
108361f28255Scgd
108461f28255Scgd uid = userid;
108561f28255Scgd if (userid == 0) {
108661f28255Scgd if (*owner == ':') {
108761f28255Scgd uid = atoi(owner + 1);
108861f28255Scgd } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
108961f28255Scgd if ((pw = getpwnam(owner)) == NULL) {
109061f28255Scgd if (mode & 04000) {
109161f28255Scgd note("%s:%s: unknown login name, clearing setuid",
109261f28255Scgd host, owner);
109361f28255Scgd mode &= ~04000;
109461f28255Scgd uid = 0;
109561f28255Scgd }
109661f28255Scgd } else
109761f28255Scgd uid = pw->pw_uid;
109861f28255Scgd } else
109961f28255Scgd uid = pw->pw_uid;
110061f28255Scgd if (*group == ':') {
110161f28255Scgd gid = atoi(group + 1);
110261f28255Scgd goto ok;
110361f28255Scgd }
110461f28255Scgd } else if ((mode & 04000) && strcmp(user, owner) != 0)
110561f28255Scgd mode &= ~04000;
110661f28255Scgd gid = -1;
110761f28255Scgd if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
110861f28255Scgd if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
110961f28255Scgd || ((gr = getgrnam(group)) == NULL)) {
111061f28255Scgd if (mode & 02000) {
111161f28255Scgd note("%s:%s: unknown group", host, group);
111261f28255Scgd mode &= ~02000;
111361f28255Scgd }
111461f28255Scgd } else
111561f28255Scgd gid = gr->gr_gid;
111661f28255Scgd } else
111761f28255Scgd gid = gr->gr_gid;
11187d2ef9d9Schristos if (userid && gid != (gid_t)-1) {
111961f28255Scgd if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
112061f28255Scgd if (!(strcmp(user, gr->gr_mem[i])))
112161f28255Scgd goto ok;
112261f28255Scgd mode &= ~02000;
112361f28255Scgd gid = -1;
112461f28255Scgd }
1125985cca83Smycroft ok:
1126985cca83Smycroft (void) gettimeofday(&tv[0], (struct timezone *)0);
1127985cca83Smycroft tv[1].tv_sec = mtime;
1128985cca83Smycroft tv[1].tv_usec = 0;
1129985cca83Smycroft if (fd != -1 ? futimes(fd, tv) < 0 : utimes(file, tv) < 0)
1130985cca83Smycroft note("%s: %s utimes: %s", host, file, strerror(errno));
1131985cca83Smycroft if (fd != -1 ? fchown(fd, uid, gid) < 0 : chown(file, uid, gid) < 0)
1132a5bfdf78Scgd note("%s: %s chown: %s", host, file, strerror(errno));
1133a5bfdf78Scgd else if (mode & 07000 &&
1134985cca83Smycroft (fd != -1 ? fchmod(fd, mode) < 0 : chmod(file, mode) < 0))
1135a5bfdf78Scgd note("%s: %s chmod: %s", host, file, strerror(errno));
113661f28255Scgd return(0);
113761f28255Scgd }
113861f28255Scgd
113961f28255Scgd /*
114061f28255Scgd * Check for files on the machine being updated that are not on the master
114161f28255Scgd * machine and remove them.
114261f28255Scgd */
1143a5bfdf78Scgd static void
rmchk(int opts)1144a9b4ca62Swiz rmchk(int opts)
114561f28255Scgd {
11463b3cf635Slukem char *cp, *s;
114761f28255Scgd struct stat stb;
114861f28255Scgd
114961f28255Scgd if (debug)
115061f28255Scgd printf("rmchk()\n");
115161f28255Scgd
115261f28255Scgd /*
115361f28255Scgd * Tell the remote to clean the files from the last directory sent.
115461f28255Scgd */
1155336eeb5fSthorpej (void) snprintf(buf, sizeof(buf), "C%o\n", opts & VERIFY);
115661f28255Scgd if (debug)
115761f28255Scgd printf("buf = %s", buf);
115861f28255Scgd (void) write(rem, buf, strlen(buf));
115961f28255Scgd if (response() < 0)
116061f28255Scgd return;
116161f28255Scgd for (;;) {
116261f28255Scgd cp = s = buf;
116361f28255Scgd do {
116461f28255Scgd if (read(rem, cp, 1) != 1)
1165a5bfdf78Scgd lostconn(0);
116661f28255Scgd } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
116761f28255Scgd
116861f28255Scgd switch (*s++) {
116961f28255Scgd case 'Q': /* Query if file should be removed */
117061f28255Scgd /*
117161f28255Scgd * Return the following codes to remove query.
117261f28255Scgd * N\n -- file exists - DON'T remove.
117361f28255Scgd * Y\n -- file doesn't exist - REMOVE.
117461f28255Scgd */
117561f28255Scgd *--cp = '\0';
1176cf0afdf8Sthorpej (void) snprintf(tp, sizeof(target) - (tp - target),
1177cf0afdf8Sthorpej "/%s", s);
117861f28255Scgd if (debug)
117961f28255Scgd printf("check %s\n", target);
118061f28255Scgd if (except(target))
118161f28255Scgd (void) write(rem, "N\n", 2);
118261f28255Scgd else if (lstat(target, &stb) < 0)
118361f28255Scgd (void) write(rem, "Y\n", 2);
118461f28255Scgd else
118561f28255Scgd (void) write(rem, "N\n", 2);
118661f28255Scgd break;
118761f28255Scgd
118861f28255Scgd case '\0':
118961f28255Scgd *--cp = '\0';
119061f28255Scgd if (*s != '\0')
1191f393397bSthorpej dolog(lfp, "%s\n", s);
119261f28255Scgd break;
119361f28255Scgd
119461f28255Scgd case 'E':
119561f28255Scgd *tp = '\0';
119661f28255Scgd ack();
119761f28255Scgd return;
119861f28255Scgd
119961f28255Scgd case '\1':
120061f28255Scgd case '\2':
120161f28255Scgd nerrs++;
120261f28255Scgd if (*s != '\n') {
120361f28255Scgd if (!iamremote) {
120461f28255Scgd fflush(stdout);
120561f28255Scgd (void) write(2, s, cp - s);
120661f28255Scgd }
120761f28255Scgd if (lfp != NULL)
120861f28255Scgd (void) fwrite(s, 1, cp - s, lfp);
120961f28255Scgd }
121061f28255Scgd if (buf[0] == '\2')
1211a5bfdf78Scgd lostconn(0);
121261f28255Scgd break;
121361f28255Scgd
121461f28255Scgd default:
121561f28255Scgd error("rmchk: unexpected response '%s'\n", buf);
121661f28255Scgd err();
121761f28255Scgd }
121861f28255Scgd }
121961f28255Scgd }
122061f28255Scgd
122161f28255Scgd /*
122261f28255Scgd * Check the current directory (initialized by the 'T' command to server())
122361f28255Scgd * for extraneous files and remove them.
122461f28255Scgd */
1225a5bfdf78Scgd static void
clean(char * cp)1226a9b4ca62Swiz clean(char *cp)
122761f28255Scgd {
122861f28255Scgd DIR *d;
1229b751ad2cSchristos struct dirent *dp;
123061f28255Scgd struct stat stb;
123161f28255Scgd char *otp;
123261f28255Scgd int len, opts;
123361f28255Scgd
123461f28255Scgd opts = 0;
123561f28255Scgd while (*cp >= '0' && *cp <= '7')
123661f28255Scgd opts = (opts << 3) | (*cp++ - '0');
123761f28255Scgd if (*cp != '\0') {
123861f28255Scgd error("clean: options not delimited\n");
123961f28255Scgd return;
124061f28255Scgd }
124161f28255Scgd if ((d = opendir(target)) == NULL) {
124261f28255Scgd error("%s:%s: %s\n", host, target, strerror(errno));
124361f28255Scgd return;
124461f28255Scgd }
124561f28255Scgd ack();
124661f28255Scgd
124761f28255Scgd otp = tp;
124861f28255Scgd len = tp - target;
12493b3cf635Slukem while ((dp = readdir(d)) != NULL) {
125061f28255Scgd if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
125161f28255Scgd continue;
125261f28255Scgd if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
125361f28255Scgd error("%s:%s/%s: Name too long\n",
125461f28255Scgd host, target, dp->d_name);
125561f28255Scgd continue;
125661f28255Scgd }
125761f28255Scgd tp = otp;
125861f28255Scgd *tp++ = '/';
1259276fd166Ssimonb cp = dp->d_name;
12603b3cf635Slukem while ((*tp++ = *cp++) != 0)
126161f28255Scgd ;
126261f28255Scgd tp--;
126361f28255Scgd if (lstat(target, &stb) < 0) {
126461f28255Scgd error("%s:%s: %s\n", host, target, strerror(errno));
126561f28255Scgd continue;
126661f28255Scgd }
1267336eeb5fSthorpej (void) snprintf(buf, sizeof(buf), "Q%s\n", dp->d_name);
126861f28255Scgd (void) write(rem, buf, strlen(buf));
126961f28255Scgd cp = buf;
127061f28255Scgd do {
127161f28255Scgd if (read(rem, cp, 1) != 1)
1272a5bfdf78Scgd cleanup(0);
127361f28255Scgd } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
127461f28255Scgd *--cp = '\0';
127561f28255Scgd cp = buf;
127661f28255Scgd if (*cp != 'Y')
127761f28255Scgd continue;
127861f28255Scgd if (opts & VERIFY) {
127961f28255Scgd cp = buf;
128061f28255Scgd *cp++ = '\0';
1281336eeb5fSthorpej (void) snprintf(cp, sizeof(buf) - 1,
1282336eeb5fSthorpej "need to remove: %s\n", target);
128361f28255Scgd (void) write(rem, buf, strlen(cp) + 1);
128461f28255Scgd } else
128561f28255Scgd removeit(&stb);
128661f28255Scgd }
128761f28255Scgd closedir(d);
128861f28255Scgd (void) write(rem, "E\n", 2);
128961f28255Scgd (void) response();
129061f28255Scgd tp = otp;
129161f28255Scgd *tp = '\0';
129261f28255Scgd }
129361f28255Scgd
129461f28255Scgd /*
129561f28255Scgd * Remove a file or directory (recursively) and send back an acknowledge
129661f28255Scgd * or an error message.
129761f28255Scgd */
1298a5bfdf78Scgd static void
removeit(struct stat * st)12994538623fSlukem removeit(struct stat *st)
130061f28255Scgd {
130161f28255Scgd DIR *d;
1302b751ad2cSchristos struct dirent *dp;
13033b3cf635Slukem char *cp;
130461f28255Scgd struct stat stb;
130561f28255Scgd char *otp;
130661f28255Scgd int len;
130761f28255Scgd
13084538623fSlukem switch (st->st_mode & S_IFMT) {
130961f28255Scgd case S_IFREG:
131061f28255Scgd case S_IFLNK:
131161f28255Scgd if (unlink(target) < 0)
131261f28255Scgd goto bad;
131361f28255Scgd goto removed;
131461f28255Scgd
131561f28255Scgd case S_IFDIR:
131661f28255Scgd break;
131761f28255Scgd
131861f28255Scgd default:
131961f28255Scgd error("%s:%s: not a plain file\n", host, target);
132061f28255Scgd return;
132161f28255Scgd }
132261f28255Scgd
132361f28255Scgd if ((d = opendir(target)) == NULL)
132461f28255Scgd goto bad;
132561f28255Scgd
132661f28255Scgd otp = tp;
132761f28255Scgd len = tp - target;
13283b3cf635Slukem while ((dp = readdir(d)) != NULL) {
132961f28255Scgd if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
133061f28255Scgd continue;
133161f28255Scgd if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
133261f28255Scgd error("%s:%s/%s: Name too long\n",
133361f28255Scgd host, target, dp->d_name);
133461f28255Scgd continue;
133561f28255Scgd }
133661f28255Scgd tp = otp;
133761f28255Scgd *tp++ = '/';
1338276fd166Ssimonb cp = dp->d_name;
13393b3cf635Slukem while ((*tp++ = *cp++) != 0)
134061f28255Scgd ;
134161f28255Scgd tp--;
134261f28255Scgd if (lstat(target, &stb) < 0) {
134361f28255Scgd error("%s:%s: %s\n", host, target, strerror(errno));
134461f28255Scgd continue;
134561f28255Scgd }
134661f28255Scgd removeit(&stb);
134761f28255Scgd }
134861f28255Scgd closedir(d);
134961f28255Scgd tp = otp;
135061f28255Scgd *tp = '\0';
135161f28255Scgd if (rmdir(target) < 0) {
135261f28255Scgd bad:
135361f28255Scgd error("%s:%s: %s\n", host, target, strerror(errno));
135461f28255Scgd return;
135561f28255Scgd }
135661f28255Scgd removed:
135761f28255Scgd cp = buf;
135861f28255Scgd *cp++ = '\0';
1359336eeb5fSthorpej (void) snprintf(cp, sizeof(buf) - 1, "removed %s\n", target);
136061f28255Scgd (void) write(rem, buf, strlen(cp) + 1);
136161f28255Scgd }
136261f28255Scgd
136361f28255Scgd /*
136461f28255Scgd * Execute a shell command to handle special cases.
136561f28255Scgd */
1366a5bfdf78Scgd static void
dospecial(char * cmd)1367a9b4ca62Swiz dospecial(char *cmd)
136861f28255Scgd {
136961f28255Scgd int fd[2], status, pid, i;
13703b3cf635Slukem char *cp, *s;
137161f28255Scgd char sbuf[BUFSIZ];
137261f28255Scgd
137361f28255Scgd if (pipe(fd) < 0) {
137461f28255Scgd error("%s\n", strerror(errno));
137561f28255Scgd return;
137661f28255Scgd }
137761f28255Scgd if ((pid = fork()) == 0) {
137861f28255Scgd /*
137961f28255Scgd * Return everything the shell commands print.
138061f28255Scgd */
138161f28255Scgd (void) close(0);
138261f28255Scgd (void) close(1);
138361f28255Scgd (void) close(2);
138461f28255Scgd (void) open(_PATH_DEVNULL, O_RDONLY);
138561f28255Scgd (void) dup(fd[1]);
138661f28255Scgd (void) dup(fd[1]);
138761f28255Scgd (void) close(fd[0]);
138861f28255Scgd (void) close(fd[1]);
138961f28255Scgd setgid(groupid);
139061f28255Scgd setuid(userid);
13914bc6feceSmrg execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
139261f28255Scgd _exit(127);
139361f28255Scgd }
139461f28255Scgd (void) close(fd[1]);
139561f28255Scgd s = sbuf;
139661f28255Scgd *s++ = '\0';
139761f28255Scgd while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
139861f28255Scgd cp = buf;
139961f28255Scgd do {
140061f28255Scgd *s++ = *cp++;
140161f28255Scgd if (cp[-1] != '\n') {
140261f28255Scgd if (s < &sbuf[sizeof(sbuf)-1])
140361f28255Scgd continue;
140461f28255Scgd *s++ = '\n';
140561f28255Scgd }
140661f28255Scgd /*
140761f28255Scgd * Throw away blank lines.
140861f28255Scgd */
140961f28255Scgd if (s == &sbuf[2]) {
141061f28255Scgd s--;
141161f28255Scgd continue;
141261f28255Scgd }
141361f28255Scgd (void) write(rem, sbuf, s - sbuf);
141461f28255Scgd s = &sbuf[1];
141561f28255Scgd } while (--i);
141661f28255Scgd }
141761f28255Scgd if (s > &sbuf[1]) {
141861f28255Scgd *s++ = '\n';
141961f28255Scgd (void) write(rem, sbuf, s - sbuf);
142061f28255Scgd }
142161f28255Scgd while ((i = wait(&status)) != pid && i != -1)
142261f28255Scgd ;
142361f28255Scgd if (i == -1)
142461f28255Scgd status = -1;
142561f28255Scgd (void) close(fd[0]);
142661f28255Scgd if (status)
142761f28255Scgd error("shell returned %d\n", status);
142861f28255Scgd else
142961f28255Scgd ack();
143061f28255Scgd }
143161f28255Scgd
1432a5bfdf78Scgd
1433a5bfdf78Scgd void
dolog(FILE * fp,const char * fmt,...)1434f393397bSthorpej dolog(FILE *fp, const char *fmt, ...)
143561f28255Scgd {
1436a5bfdf78Scgd va_list ap;
14374c999163Swiz
14384c999163Swiz /* Print changes locally if not quiet mode */
14394c999163Swiz if (!qflag) {
1440a5bfdf78Scgd va_start(ap, fmt);
1441a5bfdf78Scgd (void)vprintf(fmt, ap);
14424c999163Swiz va_end(ap);
14434c999163Swiz }
144461f28255Scgd
144561f28255Scgd /* Save changes (for mailing) if really updating files */
14464c999163Swiz if (!(options & VERIFY) && fp != NULL) {
14474c999163Swiz va_start(ap, fmt);
1448a5bfdf78Scgd (void)vfprintf(fp, fmt, ap);
1449a5bfdf78Scgd va_end(ap);
145061f28255Scgd }
14514c999163Swiz }
145261f28255Scgd
1453a5bfdf78Scgd void
error(const char * fmt,...)1454a5bfdf78Scgd error(const char *fmt, ...)
145561f28255Scgd {
145661f28255Scgd static FILE *fp;
1457a5bfdf78Scgd va_list ap;
14584c999163Swiz
14594c999163Swiz ++nerrs;
14604c999163Swiz if (!fp && !(fp = fdopen(rem, "w")))
14614c999163Swiz return;
1462a5bfdf78Scgd va_start(ap, fmt);
146361f28255Scgd if (iamremote) {
146461f28255Scgd (void)fprintf(fp, "%crdist: ", 0x01);
1465a5bfdf78Scgd (void)vfprintf(fp, fmt, ap);
146661f28255Scgd fflush(fp);
146761f28255Scgd }
146861f28255Scgd else {
146961f28255Scgd fflush(stdout);
147061f28255Scgd (void)fprintf(stderr, "rdist: ");
1471a5bfdf78Scgd (void)vfprintf(stderr, fmt, ap);
147261f28255Scgd fflush(stderr);
147361f28255Scgd }
14744c999163Swiz va_end(ap);
147561f28255Scgd if (lfp != NULL) {
147661f28255Scgd (void)fprintf(lfp, "rdist: ");
14774c999163Swiz va_start(ap, fmt);
1478a5bfdf78Scgd (void)vfprintf(lfp, fmt, ap);
14794c999163Swiz va_end(ap);
148061f28255Scgd fflush(lfp);
148161f28255Scgd }
148261f28255Scgd }
148361f28255Scgd
1484a5bfdf78Scgd void
fatal(const char * fmt,...)1485a5bfdf78Scgd fatal(const char *fmt, ...)
148661f28255Scgd {
148761f28255Scgd static FILE *fp;
1488a5bfdf78Scgd va_list ap;
14894c999163Swiz
14904c999163Swiz ++nerrs;
14914c999163Swiz if (!fp && !(fp = fdopen(rem, "w")))
14924c999163Swiz return;
1493a5bfdf78Scgd va_start(ap, fmt);
149461f28255Scgd if (iamremote) {
149561f28255Scgd (void)fprintf(fp, "%crdist: ", 0x02);
1496a5bfdf78Scgd (void)vfprintf(fp, fmt, ap);
149761f28255Scgd fflush(fp);
149861f28255Scgd }
149961f28255Scgd else {
150061f28255Scgd fflush(stdout);
150161f28255Scgd (void)fprintf(stderr, "rdist: ");
1502a5bfdf78Scgd (void)vfprintf(stderr, fmt, ap);
150361f28255Scgd fflush(stderr);
150461f28255Scgd }
15054c999163Swiz va_end(ap);
150661f28255Scgd if (lfp != NULL) {
150761f28255Scgd (void)fprintf(lfp, "rdist: ");
15084c999163Swiz va_start(ap, fmt);
1509a5bfdf78Scgd (void)vfprintf(lfp, fmt, ap);
15104c999163Swiz va_end(ap);
151161f28255Scgd fflush(lfp);
151261f28255Scgd }
1513a5bfdf78Scgd cleanup(0);
151461f28255Scgd }
151561f28255Scgd
1516a5bfdf78Scgd static int
response(void)1517a9b4ca62Swiz response(void)
151861f28255Scgd {
151961f28255Scgd char *cp, *s;
152061f28255Scgd char resp[BUFSIZ];
152161f28255Scgd
152261f28255Scgd if (debug)
152361f28255Scgd printf("response()\n");
152461f28255Scgd
152561f28255Scgd cp = s = resp;
152661f28255Scgd do {
152761f28255Scgd if (read(rem, cp, 1) != 1)
1528a5bfdf78Scgd lostconn(0);
152961f28255Scgd } while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
153061f28255Scgd
153161f28255Scgd switch (*s++) {
153261f28255Scgd case '\0':
153361f28255Scgd *--cp = '\0';
153461f28255Scgd if (*s != '\0') {
1535f393397bSthorpej dolog(lfp, "%s\n", s);
153661f28255Scgd return(1);
153761f28255Scgd }
153861f28255Scgd return(0);
153961f28255Scgd case '\3':
154061f28255Scgd *--cp = '\0';
1541f393397bSthorpej dolog(lfp, "Note: %s\n",s);
154261f28255Scgd return(response());
154361f28255Scgd
154461f28255Scgd default:
154561f28255Scgd s--;
1546*fbffadb9Smrg /* FALLTHROUGH */
154761f28255Scgd case '\1':
154861f28255Scgd case '\2':
154961f28255Scgd nerrs++;
155061f28255Scgd if (*s != '\n') {
155161f28255Scgd if (!iamremote) {
155261f28255Scgd fflush(stdout);
155361f28255Scgd (void) write(2, s, cp - s);
155461f28255Scgd }
155561f28255Scgd if (lfp != NULL)
155661f28255Scgd (void) fwrite(s, 1, cp - s, lfp);
155761f28255Scgd }
155861f28255Scgd if (resp[0] == '\2')
1559a5bfdf78Scgd lostconn(0);
156061f28255Scgd return(-1);
156161f28255Scgd }
156261f28255Scgd }
156361f28255Scgd
156461f28255Scgd /*
156561f28255Scgd * Remove temporary files and do any cleanup operations before exiting.
156661f28255Scgd */
156761f28255Scgd void
15687d2ef9d9Schristos /*ARGSUSED*/
cleanup(int signo __unused)15697d2ef9d9Schristos cleanup(int signo __unused)
157061f28255Scgd {
157161f28255Scgd (void) unlink(tempfile);
157261f28255Scgd exit(1);
157361f28255Scgd }
157461f28255Scgd
1575a5bfdf78Scgd static void
note(const char * fmt,...)1576a5bfdf78Scgd note(const char *fmt, ...)
157761f28255Scgd {
15784538623fSlukem static char nbuf[BUFSIZ];
1579a5bfdf78Scgd va_list ap;
1580a9b4ca62Swiz
1581a5bfdf78Scgd va_start(ap, fmt);
15824538623fSlukem (void)vsnprintf(nbuf, sizeof(nbuf), fmt, ap);
1583a5bfdf78Scgd va_end(ap);
15844538623fSlukem comment(nbuf);
158561f28255Scgd }
158661f28255Scgd
1587a5bfdf78Scgd static void
comment(char * s)1588a9b4ca62Swiz comment(char *s)
158961f28255Scgd {
1590a5bfdf78Scgd char c;
1591a5bfdf78Scgd
1592a5bfdf78Scgd c = '\3';
159361f28255Scgd write(rem, &c, 1);
159461f28255Scgd write(rem, s, strlen(s));
159561f28255Scgd c = '\n';
159661f28255Scgd write(rem, &c, 1);
159761f28255Scgd }
1598