1/*
2 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6/*
7 * Copyright (c) 1983 Regents of the University of California.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms are permitted
11 * provided that the above copyright notice and this paragraph are
12 * duplicated in all such forms and that any documentation,
13 * advertising materials, and other materials related to such
14 * distribution and use acknowledge that the software was developed
15 * by the University of California, Berkeley.  The name of the
16 * University may not be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 */
19
20#include "defs.h"
21#include <signal.h>
22#include <string.h>
23#include <errno.h>
24#include <limits.h>
25#include <ctype.h>
26#include <krb5defs.h>
27
28/*
29 * If we want to write *to* the client rdist program, *from* the server
30 * side (server-side child `rdist -Server' process exec'ed off of in.rshd),
31 * we write to stdout/stderr, since there is a pipe connecting stdout/stderr
32 * to the outside world (which is why we use `wrem' and not `rem').
33 */
34int wrem = 1;
35
36#define	ack() 	(void) write(wrem, "\0\n", 2)
37#define	err() 	(void) write(wrem, "\1\n", 2)
38
39/*
40 * Set when a desread() is reqd. in response()
41 */
42
43struct	linkbuf *ihead;		/* list of files with more than one link */
44char	buf[RDIST_BUFSIZ];	/* general purpose buffer */
45char	source[RDIST_BUFSIZ];	/* base source directory name */
46char	destination[RDIST_BUFSIZ];	/* base destination directory name */
47char	target[RDIST_BUFSIZ];	/* target/source directory name */
48char	*tp;			/* pointer to end of target name */
49char	*Tdest;			/* pointer to last T dest */
50int	catname;		/* cat name to target name */
51char	*stp[32];		/* stack of saved tp's for directories */
52int	oumask;			/* old umask for creating files */
53
54extern	FILE *lfp;		/* log file for mailing changes */
55
56void	cleanup();
57struct	linkbuf *savelink();
58char	*strsub();
59
60static void comment(char *s);
61static void note();
62static void hardlink(char *cmd);
63void error();
64void log();
65static void recursive_remove(struct stat *stp);
66static void recvf(char *cmd, int type);
67static void query(char *name);
68static void sendf(char *rname, int opts);
69static void rmchk(int opts);
70static void dospecial(char *cmd);
71static void clean(char *cp);
72
73/*
74 * Server routine to read requests and process them.
75 * Commands are:
76 *	Tname	- Transmit file if out of date
77 *	Vname	- Verify if file out of date or not
78 *	Qname	- Query if file exists. Return mtime & size if it does.
79 */
80void
81server()
82{
83	char cmdbuf[RDIST_BUFSIZ];
84	register char *cp;
85
86	signal(SIGHUP, cleanup);
87	signal(SIGINT, cleanup);
88	signal(SIGQUIT, cleanup);
89	signal(SIGTERM, cleanup);
90	signal(SIGPIPE, cleanup);
91
92	rem = 0;
93	oumask = umask(0);
94
95	(void) sprintf(buf, "V%d\n", VERSION);
96	(void) write(wrem, buf, strlen(buf));
97
98	for (;;) {
99		cp = cmdbuf;
100		if (read(rem, cp, 1) <= 0)
101			return;
102		if (*cp++ == '\n') {
103			error("server: expected control record\n");
104			continue;
105		}
106		do {
107			if (read(rem, cp, 1) != 1)
108				cleanup();
109		} while (*cp++ != '\n' && cp < &cmdbuf[RDIST_BUFSIZ]);
110		*--cp = '\0';
111		cp = cmdbuf;
112		switch (*cp++) {
113		case 'T':  /* init target file/directory name */
114			catname = 1;	/* target should be directory */
115			goto dotarget;
116
117		case 't':  /* init target file/directory name */
118			catname = 0;
119		dotarget:
120			if (exptilde(target, sizeof (target), cp) == NULL)
121				continue;
122			tp = target;
123			while (*tp)
124				tp++;
125			ack();
126			continue;
127
128		case 'R':  /* Transfer a regular file. */
129			recvf(cp, S_IFREG);
130			continue;
131
132		case 'D':  /* Transfer a directory. */
133			recvf(cp, S_IFDIR);
134			continue;
135
136		case 'K':  /* Transfer symbolic link. */
137			recvf(cp, S_IFLNK);
138			continue;
139
140		case 'k':  /* Transfer hard link. */
141			hardlink(cp);
142			continue;
143
144		case 'E':  /* End. (of directory) */
145			*tp = '\0';
146			if (catname <= 0) {
147				error("server: too many 'E's\n");
148				continue;
149			}
150			tp = stp[--catname];
151			*tp = '\0';
152			ack();
153			continue;
154
155		case 'C':  /* Clean. Cleanup a directory */
156			clean(cp);
157			continue;
158
159		case 'Q':  /* Query. Does the file/directory exist? */
160			query(cp);
161			continue;
162
163		case 'S':  /* Special. Execute commands */
164			dospecial(cp);
165			continue;
166
167#ifdef notdef
168		/*
169		 * These entries are reserved but not currently used.
170		 * The intent is to allow remote hosts to have master copies.
171		 * Currently, only the host rdist runs on can have masters.
172		 */
173		case 'X':  /* start a new list of files to exclude */
174			except = bp = NULL;
175		case 'x':  /* add name to list of files to exclude */
176			if (*cp == '\0') {
177				ack();
178				continue;
179			}
180			if (*cp == '~') {
181				if (exptilde(buf, sizeof (buf), cp) == NULL)
182					continue;
183				cp = buf;
184			}
185			if (bp == NULL)
186				except = bp = expand(makeblock(NAME, cp),
187				    E_VARS);
188			else
189				bp->b_next = expand(makeblock(NAME, cp),
190				    E_VARS);
191			while (bp->b_next != NULL)
192				bp = bp->b_next;
193			ack();
194			continue;
195
196		case 'I':  /* Install. Transfer file if out of date. */
197			opts = 0;
198			while (*cp >= '0' && *cp <= '7')
199				opts = (opts << 3) | (*cp++ - '0');
200			if (*cp++ != ' ') {
201				error("server: options not delimited\n");
202				return;
203			}
204			install(cp, opts);
205			continue;
206
207		case 'L':  /* Log. save message in log file */
208			log(lfp, cp);
209			continue;
210#endif
211
212		case '\1':
213			nerrs++;
214			continue;
215
216		case '\2':
217			return;
218
219		default:
220			error("server: unknown command '%s'\n", cp);
221			continue;
222		case '\0':
223			continue;
224		}
225	}
226}
227
228/*
229 * Update the file(s) if they are different.
230 * destdir = 1 if destination should be a directory
231 * (i.e., more than one source is being copied to the same destination).
232 */
233void
234install(src, dest, destdir, opts)
235	char *src, *dest;
236	int destdir, opts;
237{
238	char *rname;
239	char destcopy[RDIST_BUFSIZ];
240
241	if (dest == NULL) {
242		opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
243		dest = src;
244	}
245
246	if (nflag || debug) {
247		printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
248			opts & WHOLE ? " -w" : "",
249			opts & YOUNGER ? " -y" : "",
250			opts & COMPARE ? " -b" : "",
251			opts & REMOVE ? " -R" : "", src, dest);
252		if (nflag)
253			return;
254	}
255
256	rname = exptilde(target, sizeof (target), src);
257	if (rname == NULL)
258		return;
259	tp = target;
260	while (*tp)
261		tp++;
262	/*
263	 * If we are renaming a directory and we want to preserve
264	 * the directory heirarchy (-w), we must strip off the leading
265	 * directory name and preserve the rest.
266	 */
267	if (opts & WHOLE) {
268		while (*rname == '/')
269			rname++;
270		destdir = 1;
271	} else {
272		rname = rindex(target, '/');
273		if (rname == NULL)
274			rname = target;
275		else
276			rname++;
277	}
278	if (debug)
279		printf("target = %s, rname = %s\n", target, rname);
280	/*
281	 * Pass the destination file/directory name to remote.
282	 */
283	if (snprintf(buf, sizeof (buf), "%c%s\n", destdir ? 'T' : 't', dest) >=
284	    sizeof (buf)) {
285		error("%s: Name too long\n", dest);
286		return;
287	}
288	if (debug)
289		printf("buf = %s", buf);
290	(void) deswrite(rem, buf, strlen(buf), 0);
291
292	if (response() < 0)
293		return;
294
295	strcpy(source, src);
296	if (destdir) {
297		strcpy(destcopy, dest);
298		Tdest = destcopy;
299		strcpy(destination, rname);
300	} else {
301		strcpy(destination, dest);
302	}
303	sendf(rname, opts);
304	Tdest = 0;
305}
306
307#define	protoname()	(pw ? pw->pw_name : user)
308#define	protogroup()	(gr ? gr->gr_name : group)
309/*
310 * Transfer the file or directory in target[].
311 * rname is the name of the file on the remote host.
312 */
313void
314sendf(rname, opts)
315	char *rname;
316	int opts;
317{
318	register struct subcmd *sc;
319	struct stat stb;
320	int sizerr, f, u, len;
321	off_t i;
322	DIR *d;
323	struct dirent *dp;
324	char *otp, *cp;
325	extern struct subcmd *subcmds;
326	static char user[15], group[15];
327
328	if (debug)
329		printf("sendf(%s, %x%s)\n", rname, opts, printb(opts, OBITS));
330
331	if (except(target))
332		return;
333	if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
334		error("%s: %s\n", target, strerror(errno));
335		return;
336	}
337	if (index(rname, '\n')) {
338		error("file name '%s' contains an embedded newline - "
339		    "can't update\n", rname);
340		return;
341	}
342	if ((u = update(rname, opts, &stb)) == 0) {
343		if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
344			(void) savelink(&stb, opts);
345		return;
346	}
347
348	if (pw == NULL || pw->pw_uid != stb.st_uid)
349		if ((pw = getpwuid(stb.st_uid)) == NULL) {
350			log(lfp, "%s: no password entry for uid %d \n",
351				target, stb.st_uid);
352			pw = NULL;
353			sprintf(user, ":%d", stb.st_uid);
354		}
355	if (gr == NULL || gr->gr_gid != stb.st_gid)
356		if ((gr = getgrgid(stb.st_gid)) == NULL) {
357			log(lfp, "%s: no name for group %d\n",
358				target, stb.st_gid);
359			gr = NULL;
360			sprintf(group, ":%d", stb.st_gid);
361		}
362	if (u == 1) {
363		if (opts & VERIFY) {
364			log(lfp, "need to install: %s\n", target);
365			goto dospecial;
366		}
367		log(lfp, "installing: %s\n", target);
368		opts &= ~(COMPARE|REMOVE);
369	}
370
371	switch (stb.st_mode & S_IFMT) {
372	case S_IFDIR:
373		if ((d = opendir(target)) == NULL) {
374			error("%s: %s\n", target, strerror(errno));
375			return;
376		}
377		if (snprintf(buf, sizeof (buf), "D%o %04o 0 0 %s %s %s\n",
378		    opts, stb.st_mode & 07777, protoname(), protogroup(),
379		    rname) >= sizeof (buf)) {
380			error("%s: Name too long\n", rname);
381			closedir(d);
382			return;
383		}
384		if (debug)
385			printf("buf = %s", buf);
386		(void) deswrite(rem, buf, strlen(buf), 0);
387		if (response() < 0) {
388			closedir(d);
389			return;
390		}
391
392		if (opts & REMOVE)
393			rmchk(opts);
394
395		otp = tp;
396		len = tp - target;
397		while (dp = readdir(d)) {
398			if ((strcmp(dp->d_name, ".") == 0)||
399			    (strcmp(dp->d_name, "..") == 0))
400				continue;
401			if ((int)(len + 1 + strlen(dp->d_name)) >=
402			    (int)(RDIST_BUFSIZ - 1)) {
403				error("%.*s/%s: Name too long\n", len, target,
404					dp->d_name);
405				continue;
406			}
407			tp = otp;
408			*tp++ = '/';
409			cp = dp->d_name;
410			while (*tp++ = *cp++)
411				;
412			tp--;
413			sendf(dp->d_name, opts);
414		}
415		closedir(d);
416		(void) deswrite(rem, "E\n", 2, 0);
417		(void) response();
418		tp = otp;
419		*tp = '\0';
420		return;
421
422	case S_IFLNK:
423		if (u != 1)
424			opts |= COMPARE;
425		if (stb.st_nlink > 1) {
426			struct linkbuf *lp;
427
428			if ((lp = savelink(&stb, opts)) != NULL) {
429				/* install link */
430				if (*lp->target == 0)
431					len = snprintf(buf, sizeof (buf),
432					    "k%o %s %s\n", opts, lp->pathname,
433					    rname);
434				else
435					len = snprintf(buf, sizeof (buf),
436					    "k%o %s/%s %s\n", opts, lp->target,
437					    lp->pathname, rname);
438				if (len >= sizeof (buf)) {
439					error("%s: Name too long\n", rname);
440					return;
441				}
442				if (debug)
443					printf("buf = %s", buf);
444				(void) deswrite(rem, buf, strlen(buf), 0);
445				(void) response();
446				return;
447			}
448		}
449		(void) snprintf(buf, sizeof (buf), "K%o %o %ld %ld %s %s %s\n",
450		    opts, stb.st_mode & 07777, stb.st_size, stb.st_mtime,
451		    protoname(), protogroup(), rname);
452		if (debug)
453			printf("buf = %s", buf);
454		(void) deswrite(rem, buf, strlen(buf), 0);
455		if (response() < 0)
456			return;
457		sizerr = (readlink(target, buf, RDIST_BUFSIZ) != stb.st_size);
458		(void) deswrite(rem, buf, stb.st_size, 0);
459		if (debug)
460			printf("readlink = %.*s\n", (int)stb.st_size, buf);
461		goto done;
462
463	case S_IFREG:
464		break;
465
466	default:
467		error("%s: not a file or directory\n", target);
468		return;
469	}
470
471	if (u == 2) {
472		if (opts & VERIFY) {
473			log(lfp, "need to update: %s\n", target);
474			goto dospecial;
475		}
476		log(lfp, "updating: %s\n", target);
477	}
478
479	if (stb.st_nlink > 1) {
480		struct linkbuf *lp;
481
482		if ((lp = savelink(&stb, opts)) != NULL) {
483			/* install link */
484			if (*lp->target == 0)
485				len = snprintf(buf, sizeof (buf), "k%o %s %s\n",
486				    opts, lp->pathname, rname);
487			else
488				len = snprintf(buf, sizeof (buf),
489				    "k%o %s/%s %s\n", opts, lp->target,
490				    lp->pathname, rname);
491			if (len >= sizeof (buf)) {
492				error("%s: Name too long\n", rname);
493				return;
494			}
495			if (debug)
496				printf("buf = %s", buf);
497			(void) deswrite(rem, buf, strlen(buf), 0);
498			(void) response();
499			return;
500		}
501	}
502
503	if ((f = open(target, 0)) < 0) {
504		error("%s: %s\n", target, strerror(errno));
505		return;
506	}
507	(void) snprintf(buf, sizeof (buf), "R%o %o %ld %ld %s %s %s\n", opts,
508		stb.st_mode & 07777, stb.st_size, stb.st_mtime,
509		protoname(), protogroup(), rname);
510	if (debug)
511		printf("buf = %s", buf);
512	(void) deswrite(rem, buf, strlen(buf), 0);
513
514	if (response() < 0) {
515		(void) close(f);
516		return;
517	}
518
519	sizerr = 0;
520
521	for (i = 0; i < stb.st_size; i += RDIST_BUFSIZ) {
522		int amt = RDIST_BUFSIZ;
523		if (i + amt > stb.st_size)
524			amt = stb.st_size - i;
525		if (sizerr == 0 && read(f, buf, amt) != amt)
526			sizerr = 1;
527		(void) deswrite(rem, buf, amt, 0);
528	}
529	(void) close(f);
530done:
531	if (sizerr) {
532		error("%s: file changed size\n", target);
533		(void) deswrite(rem, "\1\n", 2, 0);
534	} else
535		(void) deswrite(rem, "\0\n", 2, 0);
536	f = response();
537
538	if (f < 0 || f == 0 && (opts & COMPARE))
539		return;
540dospecial:
541	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
542		if (sc->sc_type != SPECIAL)
543			continue;
544		if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
545			continue;
546		log(lfp, "special \"%s\"\n", sc->sc_name);
547		if (opts & VERIFY)
548			continue;
549		(void) snprintf(buf, sizeof (buf), "SFILE=%s;%s\n", target,
550		    sc->sc_name);
551		if (debug)
552			printf("buf = %s", buf);
553		(void) deswrite(rem, buf, strlen(buf), 0);
554		while (response() > 0)
555			;
556	}
557}
558
559struct linkbuf *
560savelink(stp, opts)
561	struct stat *stp;
562	int opts;
563{
564	struct linkbuf *lp;
565
566	for (lp = ihead; lp != NULL; lp = lp->nextp)
567		if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
568			lp->count--;
569			return (lp);
570		}
571	lp = (struct linkbuf *)malloc(sizeof (*lp));
572	if (lp == NULL)
573		log(lfp, "out of memory, link information lost\n");
574	else {
575		lp->nextp = ihead;
576		ihead = lp;
577		lp->inum = stp->st_ino;
578		lp->devnum = stp->st_dev;
579		lp->count = stp->st_nlink - 1;
580
581		if (strlcpy(lp->pathname,
582		    opts & WHOLE ? target : strsub(source, destination, target),
583		    sizeof (lp->pathname)) >= sizeof (lp->pathname)) {
584			error("%s: target name too long\n", target);
585		}
586
587		if (Tdest) {
588			if (strlcpy(lp->target, Tdest,
589			    sizeof (lp->target)) >= sizeof (lp->target))
590				error("%s: target name too long\n", Tdest);
591		} else
592			*lp->target = 0;
593	}
594	return (NULL);
595}
596
597/*
598 * Check to see if file needs to be updated on the remote machine.
599 * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
600 * and 3 if comparing binaries to determine if out of date.
601 */
602int
603update(rname, opts, stp)
604	char *rname;
605	int opts;
606	struct stat *stp;
607{
608	register char *cp, *s;
609	register off_t size;
610	register time_t mtime;
611
612	if (debug)
613		printf("update(%s, %x%s, %x)\n", rname, opts,
614			printb(opts, OBITS), stp);
615
616	/*
617	 * Check to see if the file exists on the remote machine.
618	 */
619	if (snprintf(buf, sizeof (buf), "Q%s\n", rname) >= sizeof (buf)) {
620		error("%s: Name too long\n", rname);
621		return (0);
622	}
623	if (debug)
624		printf("buf = %s", buf);
625	(void) deswrite(rem, buf, strlen(buf), 0);
626again:
627	cp = s = buf;
628more:
629	do {
630		if (desread(rem, cp, 1, 0) != 1)
631			lostconn();
632	} while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
633
634	if (cp <  &buf[RDIST_BUFSIZ])
635		*cp = '\0';
636	if (debug) {
637		printf("update reply:  ");
638		switch (*s) {
639			case 'Y':
640			case 'N':
641				putchar(*s);
642				break;
643			default:
644				if (iscntrl(*s)) {
645					putchar('^');
646					putchar('A' + *s - 1);
647				} else
648					printf("%#x", *s & 0xff);
649				break;
650		}
651		printf("%s", &s[1]);
652	}
653
654	switch (*s++) {
655	case 'Y':
656		break;
657
658	case 'N':  /* file doesn't exist so install it */
659		return (1);
660
661	case '\1':
662		nerrs++;
663		if (*s != '\n') {
664			if (!iamremote) {
665				fflush(stdout);
666				(void) write(2, s, cp - s);
667			}
668			if (lfp != NULL)
669				(void) fwrite(s, 1, cp - s, lfp);
670		}
671		if (cp == &buf[RDIST_BUFSIZ] && *(cp - 1) != '\n') {
672			/* preserve status code */
673			cp = s;
674			s = buf;
675			goto more;
676		}
677		return (0);
678
679	case '\3':
680		*--cp = '\0';
681		if (lfp != NULL)
682			log(lfp, "update: note: %s\n", s);
683		goto again;
684
685	default:
686		*--cp = '\0';
687		error("update: unexpected response '%s'\n", s);
688		return (0);
689	}
690
691	if (*s == '\n')
692		return (2);
693
694	if (opts & COMPARE)
695		return (3);
696
697	size = 0;
698	while (isdigit(*s))
699		size = size * 10 + (*s++ - '0');
700	if (*s++ != ' ') {
701		error("update: size not delimited\n");
702		return (0);
703	}
704	mtime = 0;
705	while (isdigit(*s))
706		mtime = mtime * 10 + (*s++ - '0');
707	if (*s != '\n') {
708		error("update: mtime not delimited\n");
709		return (0);
710	}
711	/*
712	 * File needs to be updated?
713	 */
714	if (opts & YOUNGER) {
715		if (stp->st_mtime == mtime)
716			return (0);
717		if (stp->st_mtime < mtime) {
718			log(lfp, "Warning: %s: remote copy is newer\n", target);
719			return (0);
720		}
721	} else if (stp->st_mtime == mtime && stp->st_size == size)
722		return (0);
723	return (2);
724}
725
726/*
727 * Query. Check to see if file exists. Return one of the following:
728 *	N\n		- doesn't exist
729 *	Ysize mtime\n	- exists and its a regular file (size & mtime of file)
730 *	Y\n		- exists and its a directory or symbolic link
731 *	^Aerror message\n
732 */
733static void
734query(name)
735	char *name;
736{
737	struct stat stb;
738
739	if (catname) {
740		if (sizeof (target) - (tp - target) >= strlen(name) + 2) {
741			(void) sprintf(tp, "/%s", name);
742		} else {
743			error("%.*s/%s: Name too long\n", tp - target,
744			    target, name);
745			return;
746		}
747	}
748
749	if (lstat(target, &stb) < 0) {
750		if (errno == ENOENT)
751			(void) write(wrem, "N\n", 2);
752		else
753			error("%s:%s: %s\n", host, target, strerror(errno));
754		*tp = '\0';
755		return;
756	}
757
758	switch (stb.st_mode & S_IFMT) {
759	case S_IFREG:
760		(void) sprintf(buf, "Y%ld %ld\n", stb.st_size, stb.st_mtime);
761		(void) write(wrem, buf, strlen(buf));
762		break;
763
764	case S_IFLNK:
765	case S_IFDIR:
766		(void) write(wrem, "Y\n", 2);
767		break;
768
769	default:
770		error("%s: not a file or directory\n", name);
771		break;
772	}
773	*tp = '\0';
774}
775
776static void
777recvf(cmd, type)
778	char *cmd;
779	int type;
780{
781	register char *cp;
782	int f, mode, opts, wrerr, olderrno;
783	off_t i, size;
784	time_t mtime;
785	struct stat stb;
786	struct timeval tvp[2];
787	char *owner, *group;
788	char new[RDIST_BUFSIZ];
789	extern char *tmpname;
790
791	cp = cmd;
792	opts = 0;
793	while (*cp >= '0' && *cp <= '7')
794		opts = (opts << 3) | (*cp++ - '0');
795	if (*cp++ != ' ') {
796		error("recvf: options not delimited\n");
797		return;
798	}
799	mode = 0;
800	while (*cp >= '0' && *cp <= '7')
801		mode = (mode << 3) | (*cp++ - '0');
802	if (*cp++ != ' ') {
803		error("recvf: mode not delimited\n");
804		return;
805	}
806	size = 0;
807	while (isdigit(*cp))
808		size = size * 10 + (*cp++ - '0');
809	if (*cp++ != ' ') {
810		error("recvf: size not delimited\n");
811		return;
812	}
813	mtime = 0;
814	while (isdigit(*cp))
815		mtime = mtime * 10 + (*cp++ - '0');
816	if (*cp++ != ' ') {
817		error("recvf: mtime not delimited\n");
818		return;
819	}
820	owner = cp;
821	while (*cp && *cp != ' ')
822		cp++;
823	if (*cp != ' ') {
824		error("recvf: owner name not delimited\n");
825		return;
826	}
827	*cp++ = '\0';
828	group = cp;
829	while (*cp && *cp != ' ')
830		cp++;
831	if (*cp != ' ') {
832		error("recvf: group name not delimited\n");
833		return;
834	}
835	*cp++ = '\0';
836
837	if (type == S_IFDIR) {
838		int	isdot;
839
840		if (strcmp(cp, ".") == 0)
841			isdot = 1;
842		else
843			isdot = 0;
844		if (catname >= sizeof (stp) / sizeof (stp[0])) {
845			error("%s:%s: too many directory levels\n",
846				host, target);
847			return;
848		}
849		stp[catname] = tp;
850		if (catname++) {
851			*tp++ = '/';
852			while (*tp++ = *cp++)
853				;
854			tp--;
855		}
856		if (opts & VERIFY) {
857			ack();
858			return;
859		}
860		if (lstat(target, &stb) == 0) {
861			if (ISDIR(stb.st_mode)) {
862				if ((stb.st_mode & 07777) == mode) {
863					ack();
864					return;
865				}
866				sendrem("%s: Warning: remote mode %o != "
867				    "local mode %o", target,
868				    stb.st_mode & 07777, mode);
869				return;
870			}
871			errno = ENOTDIR;
872		} else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
873		    chkparent(target) == 0 &&
874		    (isdot == 1 || mkdir(target, mode) == 0))) {
875			if (chog(target, owner, group, mode) == 0)
876				ack();
877			return;
878		}
879		error("%s:%s: %s\n", host, target, strerror(errno));
880		tp = stp[--catname];
881		*tp = '\0';
882		return;
883	}
884
885	if (catname) {
886		if (sizeof (target) - (tp - target) >= strlen(cp) + 2) {
887			(void) sprintf(tp, "/%s", cp);
888		} else {
889			error("%.*s/%s: Name too long\n", tp - target,
890			    target, cp);
891			return;
892		}
893	}
894	cp = rindex(target, '/');
895	if (cp == NULL)
896		strcpy(new, tmpname);
897	else if (cp == target)
898		(void) sprintf(new, "/%s", tmpname);
899	else {
900		*cp = '\0';
901
902		/*
903		 * sizeof (target) =  RDIST_BUFSIZ and sizeof (tmpname) = 11
904		 * RDIST_BUFSIZ = 50*1024 is much greater than PATH_MAX that is
905		 * allowed by the kernel, so it's safe to call snprintf() here
906		 */
907		(void) snprintf(new, sizeof (new), "%s/%s", target, tmpname);
908		*cp = '/';
909	}
910
911	if (type == S_IFLNK) {
912		int j;
913
914		ack();
915		cp = buf;
916		for (i = 0; i < size; i += j) {
917			if ((j = read(rem, cp, size - i)) <= 0)
918				cleanup();
919			cp += j;
920		}
921		*cp = '\0';
922		if (response() < 0) {
923			err();
924			return;
925		}
926		if (symlink(buf, new) < 0) {
927			if (errno != ENOENT || chkparent(new) < 0 ||
928			    symlink(buf, new) < 0)
929				goto badn;
930		}
931		mode &= 0777;
932		if (opts & COMPARE) {
933			char tbuf[MAXPATHLEN];
934
935			if ((i = readlink(target, tbuf, MAXPATHLEN)) >= 0 &&
936			    i == size && strncmp(buf, tbuf, size) == 0) {
937				(void) unlink(new);
938				ack();
939				return;
940			}
941			if (opts & VERIFY)
942				goto differ;
943		}
944		goto fixup;
945	}
946
947	if ((f = creat(new, mode & ~06000)) < 0) {
948		if (errno != ENOENT || chkparent(new) < 0 ||
949		    (f = creat(new, mode & ~06000)) < 0)
950			goto badn;
951	}
952
953	ack();
954	wrerr = 0;
955	for (i = 0; i < size; i += RDIST_BUFSIZ) {
956		int amt = RDIST_BUFSIZ;
957
958		cp = buf;
959		if (i + amt > size)
960			amt = size - i;
961		do {
962			int j = read(rem, cp, amt);
963			if (j <= 0) {
964				(void) close(f);
965				(void) unlink(new);
966				cleanup();
967			}
968			amt -= j;
969			cp += j;
970		} while (amt > 0);
971		amt = RDIST_BUFSIZ;
972		if (i + amt > size)
973			amt = size - i;
974		if (wrerr == 0 && write(f, buf, amt) != amt) {
975			olderrno = errno;
976			wrerr++;
977		}
978	}
979	(void) close(f);
980
981	if (response() < 0) {
982		err();
983		(void) unlink(new);
984		return;
985	}
986	if (wrerr) {
987		error("%s:%s: %s\n", host, new, strerror(olderrno));
988		(void) unlink(new);
989		return;
990	}
991	if (opts & COMPARE) {
992		FILE *f1, *f2;
993		int c;
994
995		if ((f1 = fopen(target, "r")) == NULL)
996			goto badt;
997		if ((f2 = fopen(new, "r")) == NULL) {
998		badn:
999			error("%s:%s: %s\n", host, new, strerror(errno));
1000			(void) unlink(new);
1001			return;
1002		}
1003		while ((c = getc(f1)) == getc(f2))
1004			if (c == EOF) {
1005				(void) fclose(f1);
1006				(void) fclose(f2);
1007				(void) unlink(new);
1008				ack();
1009				return;
1010			}
1011		(void) fclose(f1);
1012		(void) fclose(f2);
1013		if (opts & VERIFY) {
1014		differ:
1015			(void) unlink(new);
1016			sendrem("need to update: %s", target);
1017			return;
1018		}
1019	}
1020
1021	/*
1022	 * Set last modified time.  For type == S_IFDIR, the lstat above filled
1023	 * in stb.  Otherwise, do it now.
1024	 */
1025	if (type != S_IFDIR)
1026		(void) lstat(new, &stb);
1027	tvp[0].tv_sec = stb.st_atime;	/* old atime from target */
1028	tvp[0].tv_usec = 0;
1029	tvp[1].tv_sec = mtime;
1030	tvp[1].tv_usec = 0;
1031	if (utimes(new, tvp) < 0) {
1032		note("%s:utimes failed %s: %s", host, new, strerror(errno));
1033	}
1034	if (chog(new, owner, group, mode) < 0) {
1035		(void) unlink(new);
1036		return;
1037	}
1038fixup:
1039	if (rename(new, target) < 0) {
1040badt:
1041		error("%s:%s: %s\n", host, target, strerror(errno));
1042		(void) unlink(new);
1043		return;
1044	}
1045	if (opts & COMPARE) {
1046		sendrem("updated %s", target);
1047	} else
1048		ack();
1049}
1050
1051/*
1052 * Creat a hard link to existing file.
1053 */
1054static void
1055hardlink(cmd)
1056	char *cmd;
1057{
1058	register char *cp;
1059	struct stat stb;
1060	char *oldname;
1061	int opts, exists = 0;
1062	char oldnamebuf[RDIST_BUFSIZ];
1063
1064	cp = cmd;
1065	opts = 0;
1066	while (*cp >= '0' && *cp <= '7')
1067		opts = (opts << 3) | (*cp++ - '0');
1068	if (*cp++ != ' ') {
1069		error("hardlink: options not delimited\n");
1070		return;
1071	}
1072	oldname = cp;
1073	while (*cp && *cp != ' ')
1074		cp++;
1075	if (*cp != ' ') {
1076		error("hardlink: oldname name not delimited\n");
1077		return;
1078	}
1079	*cp++ = '\0';
1080
1081	if (catname) {
1082		if (sizeof (target) - (tp - target) >= strlen(cp) + 2) {
1083			(void) sprintf(tp, "/%s", cp);
1084		} else {
1085			error("%.*s/%s: Name too long\n", tp - target,
1086			    target, cp);
1087			return;
1088		}
1089	}
1090	if (lstat(target, &stb) == 0) {
1091		int mode = stb.st_mode & S_IFMT;
1092		if (mode != S_IFREG && mode != S_IFLNK) {
1093			error("%s:%s: not a regular file\n", host, target);
1094			return;
1095		}
1096		exists = 1;
1097	}
1098	if (chkparent(target) < 0) {
1099		error("%s:%s: %s (no parent)\n",
1100			host, target, strerror(errno));
1101		return;
1102	}
1103	if (opts & VERIFY) {
1104		struct stat nstb;
1105
1106		if (exists && lstat(oldname, &nstb) == 0 &&
1107		    nstb.st_mode == stb.st_mode &&
1108		    nstb.st_ino == stb.st_ino &&
1109		    nstb.st_dev == stb.st_dev) {
1110			ack();
1111			return;
1112		} else {
1113			sendrem("need to update: %s", target);
1114			return;
1115		}
1116	}
1117	if (exists && (unlink(target) < 0)) {
1118		error("%s:%s: %s (unlink)\n",
1119			host, target, strerror(errno));
1120		return;
1121	}
1122	if (*oldname == '~')
1123		oldname = exptilde(oldnamebuf, sizeof (oldnamebuf), oldname);
1124	if (link(oldname, target) < 0) {
1125		error("%s:can't link %s to %s\n",
1126			host, target, oldname);
1127		return;
1128	}
1129	ack();
1130}
1131
1132/*
1133 * Check to see if parent directory exists and create one if not.
1134 */
1135int
1136chkparent(name)
1137	char *name;
1138{
1139	register char *cp;
1140	struct stat stb;
1141
1142	cp = rindex(name, '/');
1143	if (cp == NULL || cp == name)
1144		return (0);
1145	*cp = '\0';
1146	if (lstat(name, &stb) < 0) {
1147		if (errno == ENOENT && chkparent(name) >= 0 &&
1148		    mkdir(name, 0777 & ~oumask) >= 0) {
1149			*cp = '/';
1150			return (0);
1151		}
1152	} else if (ISDIR(stb.st_mode)) {
1153		*cp = '/';
1154		return (0);
1155	}
1156	*cp = '/';
1157	return (-1);
1158}
1159
1160/*
1161 * Change owner, group and mode of file.
1162 */
1163int
1164chog(file, owner, group, mode)
1165	char *file, *owner, *group;
1166	int mode;
1167{
1168	register int i;
1169	uid_t uid, gid;
1170	extern char user[];
1171
1172	/*
1173	 * by default, set uid of file to the uid of the person running
1174	 * this program.
1175	 */
1176	uid = getuid();
1177
1178	/*
1179	 * We'll use available privileges so we just try to do what
1180	 * the client specifies.  If the chown() fails we'll not
1181	 * add the set-[ug]id bits; and if we want to add the set-[ug]id
1182	 * bits and we're not permitted to do so, the OS will prevent us
1183	 * from doing so.
1184	 */
1185	if (*owner == ':') {
1186		uid = atoi(owner + 1);
1187	} else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
1188		if ((pw = getpwnam(owner)) == NULL) {
1189			if (mode & 04000) {
1190				note("%s:%s: unknown login name, "
1191				    "clearing setuid", host, owner);
1192				mode &= ~04000;
1193			}
1194		} else {
1195			uid = pw->pw_uid;
1196		}
1197	} else {
1198		uid = pw->pw_uid;
1199	}
1200
1201	if (*group == ':') {
1202		gid = atoi(group + 1);
1203		goto ok;
1204	}
1205
1206	gid = -1;
1207	if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
1208		if ((*group == ':' &&
1209		    (getgrgid(gid = atoi(group + 1)) == NULL)) ||
1210		    ((gr = getgrnam(group)) == NULL)) {
1211			if (mode & 02000) {
1212				note("%s:%s: unknown group", host, group);
1213				mode &= ~02000;
1214			}
1215		} else
1216			gid = gr->gr_gid;
1217	} else
1218		gid = gr->gr_gid;
1219ok:
1220	if (chown(file, uid, gid) < 0 ||
1221	    (mode & 07000) && chmod(file, mode) < 0) {
1222		note("%s: chown or chmod failed: file %s:  %s",
1223		    host, file, strerror(errno));
1224	}
1225	return (0);
1226}
1227
1228/*
1229 * Check for files on the machine being updated that are not on the master
1230 * machine and remove them.
1231 */
1232static void
1233rmchk(opts)
1234	int opts;
1235{
1236	register char *cp, *s;
1237	struct stat stb;
1238
1239	if (debug)
1240		printf("rmchk()\n");
1241
1242	/*
1243	 * Tell the remote to clean the files from the last directory sent.
1244	 */
1245	(void) sprintf(buf, "C%o\n", opts & VERIFY);
1246	if (debug)
1247		printf("buf = %s", buf);
1248	(void) deswrite(rem, buf, strlen(buf), 0);
1249	if (response() < 0)
1250		return;
1251	for (;;) {
1252		cp = s = buf;
1253		do {
1254			if (desread(rem, cp, 1, 0) != 1)
1255				lostconn();
1256		} while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
1257
1258		switch (*s++) {
1259		case 'Q': /* Query if file should be removed */
1260			/*
1261			 * Return the following codes to remove query.
1262			 * N\n -- file exists - DON'T remove.
1263			 * Y\n -- file doesn't exist - REMOVE.
1264			 */
1265			*--cp = '\0';
1266			(void) sprintf(tp, "/%s", s);
1267			if (debug)
1268				printf("check %s\n", target);
1269			if (except(target))
1270				(void) deswrite(rem, "N\n", 2, 0);
1271			else if (lstat(target, &stb) < 0)
1272				(void) deswrite(rem, "Y\n", 2, 0);
1273			else
1274				(void) deswrite(rem, "N\n", 2, 0);
1275			break;
1276
1277		case '\0':
1278			*--cp = '\0';
1279			if (*s != '\0')
1280				log(lfp, "%s\n", s);
1281			break;
1282
1283		case 'E':
1284			*tp = '\0';
1285			(void) deswrite(rem, "\0\n", 2, 0);
1286			return;
1287
1288		case '\1':
1289		case '\2':
1290			nerrs++;
1291			if (*s != '\n') {
1292				if (!iamremote) {
1293					fflush(stdout);
1294					(void) write(2, s, cp - s);
1295				}
1296				if (lfp != NULL)
1297					(void) fwrite(s, 1, cp - s, lfp);
1298			}
1299			if (buf[0] == '\2')
1300				lostconn();
1301			break;
1302
1303		default:
1304			error("rmchk: unexpected response '%s'\n", buf);
1305			(void) deswrite(rem, "\1\n", 2, 0);
1306		}
1307	}
1308}
1309
1310/*
1311 * Check the current directory (initialized by the 'T' command to server())
1312 * for extraneous files and remove them.
1313 */
1314static void
1315clean(cp)
1316	register char *cp;
1317{
1318	DIR *d;
1319	register struct dirent *dp;
1320	struct stat stb;
1321	char *otp;
1322	int len, opts;
1323
1324	opts = 0;
1325	while (*cp >= '0' && *cp <= '7')
1326		opts = (opts << 3) | (*cp++ - '0');
1327	if (*cp != '\0') {
1328		error("clean: options not delimited\n");
1329		return;
1330	}
1331	if ((d = opendir(target)) == NULL) {
1332		error("%s:%s: %s\n", host, target, strerror(errno));
1333		return;
1334	}
1335	ack();
1336
1337	otp = tp;
1338	len = tp - target;
1339	while (dp = readdir(d)) {
1340		if ((strcmp(dp->d_name, ".") == 0) ||
1341		    (strcmp(dp->d_name, "..") == 0))
1342			continue;
1343		if ((int)(len + 1 + strlen(dp->d_name)) >=
1344		    (int)(RDIST_BUFSIZ - 1)) {
1345			error("%s:%s/%s: Name too long\n",
1346				host, target, dp->d_name);
1347			continue;
1348		}
1349		tp = otp;
1350		*tp++ = '/';
1351		cp = dp->d_name;
1352		while (*tp++ = *cp++)
1353			;
1354		tp--;
1355		if (lstat(target, &stb) < 0) {
1356			error("%s:%s: %s\n", host, target, strerror(errno));
1357			continue;
1358		}
1359		(void) snprintf(buf, sizeof (buf), "Q%s\n", dp->d_name);
1360		(void) write(wrem, buf, strlen(buf));
1361		cp = buf;
1362		do {
1363			if (read(rem, cp, 1) != 1)
1364				cleanup();
1365		} while (*cp++ != '\n' && cp < &buf[RDIST_BUFSIZ]);
1366		*--cp = '\0';
1367		cp = buf;
1368		if (*cp != 'Y')
1369			continue;
1370		if (opts & VERIFY) {
1371			sendrem("need to remove: %s", target);
1372		} else
1373			(void) recursive_remove(&stb);
1374	}
1375	closedir(d);
1376	(void) write(wrem, "E\n", 2);
1377	(void) response();
1378	tp = otp;
1379	*tp = '\0';
1380}
1381
1382/*
1383 * Remove a file or directory (recursively) and send back an acknowledge
1384 * or an error message.
1385 */
1386static void
1387recursive_remove(stp)
1388	struct stat *stp;
1389{
1390	DIR *d;
1391	struct dirent *dp;
1392	register char *cp;
1393	struct stat stb;
1394	char *otp;
1395	int len;
1396
1397	switch (stp->st_mode & S_IFMT) {
1398	case S_IFREG:
1399	case S_IFLNK:
1400		if (unlink(target) < 0)
1401			goto bad;
1402		goto removed;
1403
1404	case S_IFDIR:
1405		break;
1406
1407	default:
1408		error("%s:%s: not a plain file\n", host, target);
1409		return;
1410	}
1411
1412	if ((d = opendir(target)) == NULL)
1413		goto bad;
1414
1415	otp = tp;
1416	len = tp - target;
1417	while (dp = readdir(d)) {
1418		if ((strcmp(dp->d_name, ".") == 0) ||
1419		    (strcmp(dp->d_name, "..") == 0))
1420			continue;
1421		if ((int)(len + 1 + strlen(dp->d_name)) >=
1422		    (int)(RDIST_BUFSIZ - 1)) {
1423			error("%s:%s/%s: Name too long\n",
1424				host, target, dp->d_name);
1425			continue;
1426		}
1427		tp = otp;
1428		*tp++ = '/';
1429		cp = dp->d_name;
1430		while (*tp++ = *cp++)
1431			;
1432		tp--;
1433		if (lstat(target, &stb) < 0) {
1434			error("%s:%s: %s\n", host, target, strerror(errno));
1435			continue;
1436		}
1437		recursive_remove(&stb);
1438	}
1439	closedir(d);
1440	tp = otp;
1441	*tp = '\0';
1442	if (rmdir(target) < 0) {
1443bad:
1444		error("%s:%s: %s\n", host, target, strerror(errno));
1445		return;
1446	}
1447removed:
1448	sendrem("removed %s", target);
1449}
1450
1451/*
1452 * Execute a shell command to handle special cases.
1453 */
1454static void
1455dospecial(cmd)
1456	char *cmd;
1457{
1458	int fd[2], status, pid, i;
1459	register char *cp, *s;
1460	char sbuf[RDIST_BUFSIZ];
1461
1462	if (pipe(fd) < 0) {
1463		error("%s\n", strerror(errno));
1464		return;
1465	}
1466	if ((pid = fork()) == 0) {
1467		/*
1468		 * Return everything the shell commands print.
1469		 */
1470		(void) close(0);
1471		(void) close(1);
1472		(void) close(2);
1473		(void) open("/dev/null", 0);
1474		(void) dup(fd[1]);
1475		(void) dup(fd[1]);
1476		(void) close(fd[0]);
1477		(void) close(fd[1]);
1478		execl("/bin/sh", "sh", "-c", cmd, 0);
1479		_exit(127);
1480	}
1481	(void) close(fd[1]);
1482	s = sbuf;
1483	*s++ = '\0';
1484	while ((i = read(fd[0], buf, RDIST_BUFSIZ)) > 0) {
1485		cp = buf;
1486		do {
1487			*s++ = *cp++;
1488			if (cp[-1] != '\n') {
1489				if (s < &sbuf[RDIST_BUFSIZ - 1])
1490					continue;
1491				*s++ = '\n';
1492			}
1493			/*
1494			 * Throw away blank lines.
1495			 */
1496			if (s == &sbuf[2]) {
1497				s--;
1498				continue;
1499			}
1500			(void) write(wrem, sbuf, s - sbuf);
1501			s = &sbuf[1];
1502		} while (--i);
1503	}
1504	if (s > &sbuf[1]) {
1505		*s++ = '\n';
1506		(void) write(wrem, sbuf, s - sbuf);
1507	}
1508	while ((i = wait(&status)) != pid && i != -1)
1509		;
1510	if (i == -1)
1511		status = -1;
1512	(void) close(fd[0]);
1513	if (status)
1514		error("shell returned %d\n", status);
1515	else
1516		ack();
1517}
1518
1519/*VARARGS2*/
1520void
1521log(fp, fmt, a1, a2, a3)
1522	FILE *fp;
1523	char *fmt;
1524	int a1, a2, a3;
1525{
1526	/* Print changes locally if not quiet mode */
1527	if (!qflag)
1528		printf(fmt, a1, a2, a3);
1529
1530	/* Save changes (for mailing) if really updating files */
1531	if (!(options & VERIFY) && fp != NULL)
1532		fprintf(fp, fmt, a1, a2, a3);
1533}
1534
1535/*VARARGS1*/
1536void
1537error(fmt, a1, a2, a3)
1538	char *fmt;
1539	int a1, a2, a3;
1540{
1541	static FILE *fp;
1542
1543	nerrs++;
1544	if (!fp && !(fp = fdopen(rem, "w")))
1545		return;
1546	if (iamremote) {
1547		(void) fprintf(fp, "%crdist: ", 0x01);
1548		(void) fprintf(fp, fmt, a1, a2, a3);
1549		fflush(fp);
1550	} else {
1551		fflush(stdout);
1552		(void) fprintf(stderr, "rdist: ");
1553		(void) fprintf(stderr, fmt, a1, a2, a3);
1554		fflush(stderr);
1555	}
1556	if (lfp != NULL) {
1557		(void) fprintf(lfp, "rdist: ");
1558		(void) fprintf(lfp, fmt, a1, a2, a3);
1559		fflush(lfp);
1560	}
1561}
1562
1563/*VARARGS1*/
1564void
1565fatal(fmt, a1, a2, a3)
1566	char *fmt;
1567	int a1, a2, a3;
1568{
1569	static FILE *fp;
1570
1571	nerrs++;
1572	if (!fp && !(fp = fdopen(rem, "w")))
1573		return;
1574	if (iamremote) {
1575		(void) fprintf(fp, "%crdist: ", 0x02);
1576		(void) fprintf(fp, fmt, a1, a2, a3);
1577		fflush(fp);
1578	} else {
1579		fflush(stdout);
1580		(void) fprintf(stderr, "rdist: ");
1581		(void) fprintf(stderr, fmt, a1, a2, a3);
1582		fflush(stderr);
1583	}
1584	if (lfp != NULL) {
1585		(void) fprintf(lfp, "rdist: ");
1586		(void) fprintf(lfp, fmt, a1, a2, a3);
1587		fflush(lfp);
1588	}
1589	cleanup();
1590}
1591
1592int
1593response()
1594{
1595	char *cp, *s;
1596	char resp[RDIST_BUFSIZ];
1597
1598	if (debug)
1599		printf("response()\n");
1600
1601	cp = s = resp;
1602more:
1603	do {
1604		if (desread(rem, cp, 1, 0) != 1)
1605			lostconn();
1606	} while (*cp++ != '\n' && cp < &resp[RDIST_BUFSIZ]);
1607
1608	switch (*s++) {
1609	case '\0':
1610		*--cp = '\0';
1611		if (*s != '\0') {
1612			log(lfp, "%s\n", s);
1613			return (1);
1614		}
1615		return (0);
1616	case '\3':
1617		*--cp = '\0';
1618		log(lfp, "Note: %s\n", s);
1619		return (response());
1620
1621	default:
1622		s--;
1623		/* FALLTHROUGH */
1624	case '\1':
1625	case '\2':
1626		nerrs++;
1627		if (*s != '\n') {
1628			if (!iamremote) {
1629				fflush(stdout);
1630				(void) write(2, s, cp - s);
1631			}
1632			if (lfp != NULL)
1633				(void) fwrite(s, 1, cp - s, lfp);
1634		}
1635		if (cp == &resp[RDIST_BUFSIZ] && *(cp - 1) != '\n') {
1636			/* preserve status code */
1637			cp = s;
1638			s = resp;
1639			goto more;
1640		}
1641		if (resp[0] == '\2')
1642			lostconn();
1643		return (-1);
1644	}
1645}
1646
1647/*
1648 * Remove temporary files and do any cleanup operations before exiting.
1649 */
1650void
1651cleanup()
1652{
1653	(void) unlink(Tmpfile);
1654	exit(1);
1655}
1656
1657static void
1658note(fmt, a1, a2, a3)
1659char *fmt;
1660int a1, a2, a3;
1661{
1662	static char buf[RDIST_BUFSIZ];
1663	(void) snprintf(buf, sizeof (buf) - 1, fmt, a1, a2, a3);
1664	comment(buf);
1665}
1666
1667static void
1668comment(s)
1669char *s;
1670{
1671	char three = '\3';
1672	char nl = '\n';
1673	struct iovec iov[3];
1674
1675	iov[0].iov_base = &three;
1676	iov[0].iov_len = sizeof (char);
1677	iov[1].iov_base = s;
1678	iov[1].iov_len = strlen(s);
1679	iov[2].iov_base = &nl;
1680	iov[2].iov_len = sizeof (char);
1681	(void) writev(rem, iov, 3);
1682}
1683
1684/*
1685 * Send message to other end.
1686 * N.B.: uses buf[].
1687 */
1688void
1689sendrem(fmt, a1, a2, a3)
1690char *fmt;
1691int a1, a2, a3;
1692{
1693	register int len;
1694
1695	buf[0] = '\0';
1696	len = snprintf(buf + 1, sizeof (buf) - 1, fmt, a1, a2, a3) + 2;
1697	if (len > sizeof (buf))
1698		len = sizeof (buf);
1699	buf[len - 1] = '\n';
1700	(void) write(wrem, buf, len);
1701}
1702
1703/*
1704 * strsub(old, new, s)
1705 *
1706 * Return a pointer to a new string created by replacing substring old
1707 * with substring new in string s.  String s is assumed to begin with
1708 * substring old.
1709 */
1710char *
1711strsub(old, new, s)
1712	char *old, *new, *s;
1713{
1714	static char pbuf[PATH_MAX];
1715	register char *p, *q, *r, *plim;
1716
1717	/* prepend new to pbuf */
1718	for (p = pbuf, q = new, plim = pbuf + sizeof (pbuf) - 1;
1719	/* CSTYLED */
1720	    *q && (p < plim);)
1721		*p++ = *q++;
1722	/* p now points to the byte in pbuf where more copying should begin */
1723
1724	/* skip over the part of s which begins with old */
1725	for (r = old, q = s; *r; q++, r++)
1726		;
1727	/* q now points to the byte in s where more copying should begin */
1728
1729	while (*q && (p < plim))
1730		*p++ = *q++;
1731	*p = '\0';
1732
1733	return (pbuf);
1734}
1735