1/*-
2 * Copyright (c) 1983, 1990, 1992, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 2002 Networks Associates Technology, Inc.
5 * All rights reserved.
6 *
7 * Portions of this software were developed for the FreeBSD Project by
8 * ThinkSec AS and NAI Labs, the Security Research Division of Network
9 * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10 * ("CBOSS"), as part of the DARPA CHATS research program.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#if 0
38#ifndef lint
39static char const copyright[] =
40"@(#) Copyright (c) 1983, 1990, 1992, 1993\n\
41	The Regents of the University of California.  All rights reserved.\n";
42#endif /* not lint */
43
44#ifndef lint
45static char sccsid[] = "@(#)rcp.c	8.2 (Berkeley) 4/2/94";
46#endif /* not lint */
47#endif
48#include <sys/cdefs.h>
49__FBSDID("$FreeBSD$");
50
51#include <sys/param.h>
52#include <sys/stat.h>
53#include <sys/time.h>
54#include <sys/socket.h>
55#include <netinet/in.h>
56#include <netinet/in_systm.h>
57#include <netinet/ip.h>
58
59#include <ctype.h>
60#include <dirent.h>
61#include <err.h>
62#include <errno.h>
63#include <fcntl.h>
64#include <limits.h>
65#include <netdb.h>
66#include <paths.h>
67#include <pwd.h>
68#include <signal.h>
69#include <stdint.h>
70#include <stdio.h>
71#include <stdlib.h>
72#include <string.h>
73#include <unistd.h>
74
75#include "extern.h"
76
77#define	OPTIONS "46dfprt"
78
79static struct passwd *pwd;
80static u_short	port;
81static uid_t	userid;
82static int errs, rem;
83int iamremote;
84static int pflag, iamrecursive, targetshouldbedirectory;
85static int family = PF_UNSPEC;
86
87static int argc_copy;
88static const char **argv_copy;
89
90static char period[] = ".";
91
92#define	CMDNEEDS	64
93static char cmd[CMDNEEDS];	/* must hold "rcp -r -p -d\0" */
94
95int	 response(void);
96void	 rsource(char *, struct stat *);
97void	 sink(int, char *[]);
98void	 source(int, char *[]);
99void	 tolocal(int, char *[]);
100void	 toremote(char *, int, char *[]);
101void	 usage(void);
102
103int
104main(int argc, char *argv[])
105{
106	struct servent *sp;
107	int ch, fflag, i, tflag;
108	char *targ;
109
110	/*
111	 * Prepare for execing ourselves.
112	 */
113	argc_copy = argc + 1;
114	argv_copy = malloc((argc_copy + 1) * sizeof(*argv_copy));
115	if (argv_copy == NULL)
116		err(1, "malloc");
117	argv_copy[0] = argv[0];
118	argv_copy[1] = "-K";
119	for (i = 1; i < argc; ++i) {
120		argv_copy[i + 1] = strdup(argv[i]);
121		if (argv_copy[i + 1] == NULL)
122			errx(1, "strdup: out of memory");
123	}
124	argv_copy[argc + 1] = NULL;
125
126	fflag = tflag = 0;
127	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
128		switch(ch) {			/* User-visible flags. */
129		case '4':
130			family = PF_INET;
131			break;
132
133		case '6':
134			family = PF_INET6;
135			break;
136
137		case 'p':
138			pflag = 1;
139			break;
140		case 'r':
141			iamrecursive = 1;
142			break;
143						/* Server options. */
144		case 'd':
145			targetshouldbedirectory = 1;
146			break;
147		case 'f':			/* "from" */
148			iamremote = 1;
149			fflag = 1;
150			break;
151		case 't':			/* "to" */
152			iamremote = 1;
153			tflag = 1;
154			break;
155		case '?':
156		default:
157			usage();
158		}
159	argc -= optind;
160	argv += optind;
161
162	sp = getservbyname("shell", "tcp");
163	if (sp == NULL)
164		errx(1, "shell/tcp: unknown service");
165	port = sp->s_port;
166
167	if ((pwd = getpwuid(userid = getuid())) == NULL)
168		errx(1, "unknown user %d", (int)userid);
169
170	rem = STDIN_FILENO;		/* XXX */
171
172	if (fflag) {			/* Follow "protocol", send data. */
173		(void)response();
174		(void)setuid(userid);
175		source(argc, argv);
176		exit(errs);
177	}
178
179	if (tflag) {			/* Receive data. */
180		(void)setuid(userid);
181		sink(argc, argv);
182		exit(errs);
183	}
184
185	if (argc < 2)
186		usage();
187	if (argc > 2)
188		targetshouldbedirectory = 1;
189
190	rem = -1;
191	/* Command to be executed on remote system using "rsh". */
192	(void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s",
193	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
194	    targetshouldbedirectory ? " -d" : "");
195
196	(void)signal(SIGPIPE, lostconn);
197
198	if ((targ = colon(argv[argc - 1])))	/* Dest is remote host. */
199		toremote(targ, argc, argv);
200	else {
201		tolocal(argc, argv);		/* Dest is local host. */
202		if (targetshouldbedirectory)
203			verifydir(argv[argc - 1]);
204	}
205	exit(errs);
206}
207
208void
209toremote(char *targ, int argc, char *argv[])
210{
211	int i, tos;
212	char *bp, *host, *src, *suser, *thost, *tuser;
213
214	*targ++ = 0;
215	if (*targ == 0)
216		targ = period;
217
218	if ((thost = strchr(argv[argc - 1], '@'))) {
219		/* user@host */
220		*thost++ = 0;
221		tuser = argv[argc - 1];
222		if (*tuser == '\0')
223			tuser = NULL;
224		else if (!okname(tuser))
225			exit(1);
226	} else {
227		thost = argv[argc - 1];
228		tuser = NULL;
229	}
230
231	for (i = 0; i < argc - 1; i++) {
232		src = colon(argv[i]);
233		if (src) {			/* remote to remote */
234			*src++ = 0;
235			if (*src == 0)
236				src = period;
237			host = strchr(argv[i], '@');
238			if (host) {
239				*host++ = 0;
240				suser = argv[i];
241				if (*suser == '\0')
242					suser = pwd->pw_name;
243				else if (!okname(suser)) {
244					++errs;
245					continue;
246				}
247				if (asprintf(&bp,
248				    "%s %s -l %s -n %s %s '%s%s%s:%s'",
249				    _PATH_RSH, host, suser, cmd, src,
250				    tuser ? tuser : "", tuser ? "@" : "",
251				    thost, targ) == -1)
252					err(1, "asprintf");
253			} else
254				if (asprintf(&bp,
255				    "exec %s %s -n %s %s '%s%s%s:%s'",
256				    _PATH_RSH, argv[i], cmd, src,
257				    tuser ? tuser : "", tuser ? "@" : "",
258				    thost, targ) == -1)
259					err(1, "asprintf");
260			(void)susystem(bp, userid);
261			(void)free(bp);
262		} else {			/* local to remote */
263			if (rem == -1) {
264				if (asprintf(&bp, "%s -t %s", cmd, targ)
265					== -1)
266					err(1, "asprintf");
267				host = thost;
268				rem = rcmd_af(&host, port,
269				    pwd->pw_name,
270				    tuser ? tuser : pwd->pw_name,
271				    bp, 0, family);
272				if (rem < 0)
273					exit(1);
274				if (family == PF_INET) {
275					tos = IPTOS_THROUGHPUT;
276					if (setsockopt(rem, IPPROTO_IP, IP_TOS,
277					    &tos, sizeof(int)) < 0)
278						warn("TOS (ignored)");
279				}
280				if (response() < 0)
281					exit(1);
282				(void)free(bp);
283				(void)setuid(userid);
284			}
285			source(1, argv+i);
286		}
287	}
288}
289
290void
291tolocal(int argc, char *argv[])
292{
293	int i, len, tos;
294	char *bp, *host, *src, *suser;
295
296	for (i = 0; i < argc - 1; i++) {
297		if (!(src = colon(argv[i]))) {		/* Local to local. */
298			len = strlen(_PATH_CP) + strlen(argv[i]) +
299			    strlen(argv[argc - 1]) + 20;
300			if (!(bp = malloc(len)))
301				err(1, "malloc");
302			(void)snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP,
303			    iamrecursive ? " -PR" : "", pflag ? " -p" : "",
304			    argv[i], argv[argc - 1]);
305			if (susystem(bp, userid))
306				++errs;
307			(void)free(bp);
308			continue;
309		}
310		*src++ = 0;
311		if (*src == 0)
312			src = period;
313		if ((host = strchr(argv[i], '@')) == NULL) {
314			host = argv[i];
315			suser = pwd->pw_name;
316		} else {
317			*host++ = 0;
318			suser = argv[i];
319			if (*suser == '\0')
320				suser = pwd->pw_name;
321			else if (!okname(suser)) {
322				++errs;
323				continue;
324			}
325		}
326		len = strlen(src) + CMDNEEDS + 20;
327		if ((bp = malloc(len)) == NULL)
328			err(1, "malloc");
329		(void)snprintf(bp, len, "%s -f %s", cmd, src);
330		rem = rcmd_af(&host, port, pwd->pw_name, suser, bp, 0,
331			    family);
332		(void)free(bp);
333		if (rem < 0) {
334			++errs;
335			continue;
336		}
337		(void)seteuid(userid);
338		if (family == PF_INET) {
339			tos = IPTOS_THROUGHPUT;
340			if (setsockopt(rem, IPPROTO_IP, IP_TOS, &tos,
341			    sizeof(int)) < 0)
342				warn("TOS (ignored)");
343		}
344		sink(1, argv + argc - 1);
345		(void)seteuid(0);
346		(void)close(rem);
347		rem = -1;
348	}
349}
350
351void
352source(int argc, char *argv[])
353{
354	struct stat stb;
355	static BUF buffer;
356	BUF *bp;
357	off_t i;
358	int amt, fd, haderr, indx, result;
359	char *last, *name, buf[BUFSIZ];
360
361	for (indx = 0; indx < argc; ++indx) {
362		name = argv[indx];
363		if ((fd = open(name, O_RDONLY, 0)) < 0)
364			goto syserr;
365		if (fstat(fd, &stb)) {
366syserr:			run_err("%s: %s", name, strerror(errno));
367			goto next;
368		}
369		switch (stb.st_mode & S_IFMT) {
370		case S_IFREG:
371			break;
372		case S_IFDIR:
373			if (iamrecursive) {
374				rsource(name, &stb);
375				goto next;
376			}
377			/* FALLTHROUGH */
378		default:
379			run_err("%s: not a regular file", name);
380			goto next;
381		}
382		if ((last = strrchr(name, '/')) == NULL)
383			last = name;
384		else
385			++last;
386		if (pflag) {
387			/*
388			 * Make it compatible with possible future
389			 * versions expecting microseconds.
390			 */
391			(void)snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n",
392			    (long)stb.st_mtim.tv_sec,
393			    (long)stb.st_atim.tv_sec);
394			(void)write(rem, buf, strlen(buf));
395			if (response() < 0)
396				goto next;
397		}
398#define	MODEMASK	(S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
399		(void)snprintf(buf, sizeof(buf), "C%04o %jd %s\n",
400		    stb.st_mode & MODEMASK, (intmax_t)stb.st_size, last);
401		(void)write(rem, buf, strlen(buf));
402		if (response() < 0)
403			goto next;
404		if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) {
405next:			if (fd >= 0)
406				(void)close(fd);
407			continue;
408		}
409
410		/* Keep writing after an error so that we stay sync'd up. */
411		for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
412			amt = bp->cnt;
413			if (i + amt > stb.st_size)
414				amt = stb.st_size - i;
415			if (!haderr) {
416				result = read(fd, bp->buf, amt);
417				if (result != amt)
418					haderr = result >= 0 ? EIO : errno;
419			}
420			if (haderr)
421				(void)write(rem, bp->buf, amt);
422			else {
423				result = write(rem, bp->buf, amt);
424				if (result != amt)
425					haderr = result >= 0 ? EIO : errno;
426			}
427		}
428		if (close(fd) && !haderr)
429			haderr = errno;
430		if (!haderr)
431			(void)write(rem, "", 1);
432		else
433			run_err("%s: %s", name, strerror(haderr));
434		(void)response();
435	}
436}
437
438void
439rsource(char *name, struct stat *statp)
440{
441	DIR *dirp;
442	struct dirent *dp;
443	char *last, *vect[1], path[PATH_MAX];
444
445	if (!(dirp = opendir(name))) {
446		run_err("%s: %s", name, strerror(errno));
447		return;
448	}
449	last = strrchr(name, '/');
450	if (last == 0)
451		last = name;
452	else
453		last++;
454	if (pflag) {
455		(void)snprintf(path, sizeof(path), "T%ld 0 %ld 0\n",
456		    (long)statp->st_mtim.tv_sec,
457		    (long)statp->st_atim.tv_sec);
458		(void)write(rem, path, strlen(path));
459		if (response() < 0) {
460			closedir(dirp);
461			return;
462		}
463	}
464	(void)snprintf(path, sizeof(path),
465	    "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last);
466	(void)write(rem, path, strlen(path));
467	if (response() < 0) {
468		closedir(dirp);
469		return;
470	}
471	while ((dp = readdir(dirp))) {
472		if (dp->d_ino == 0)
473			continue;
474		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
475			continue;
476		if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path)) {
477			run_err("%s/%s: name too long", name, dp->d_name);
478			continue;
479		}
480		(void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
481		vect[0] = path;
482		source(1, vect);
483	}
484	(void)closedir(dirp);
485	(void)write(rem, "E\n", 2);
486	(void)response();
487}
488
489void
490sink(int argc, char *argv[])
491{
492	static BUF buffer;
493	struct stat stb;
494	struct timeval tv[2];
495	enum { YES, NO, DISPLAYED } wrerr;
496	BUF *bp;
497	off_t i, j, size;
498	int amt, exists, first, mask, mode, ofd, omode;
499	size_t count;
500	int setimes, targisdir, wrerrno = 0;
501	char ch, *cp, *np, *targ, *vect[1], buf[BUFSIZ], path[PATH_MAX];
502	const char *why;
503
504#define	atime	tv[0]
505#define	mtime	tv[1]
506#define	SCREWUP(str)	{ why = str; goto screwup; }
507
508	setimes = targisdir = 0;
509	mask = umask(0);
510	if (!pflag)
511		(void)umask(mask);
512	if (argc != 1) {
513		run_err("ambiguous target");
514		exit(1);
515	}
516	targ = *argv;
517	if (targetshouldbedirectory)
518		verifydir(targ);
519	(void)write(rem, "", 1);
520	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
521		targisdir = 1;
522	for (first = 1;; first = 0) {
523		cp = buf;
524		if (read(rem, cp, 1) <= 0)
525			return;
526		if (*cp++ == '\n')
527			SCREWUP("unexpected <newline>");
528		do {
529			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
530				SCREWUP("lost connection");
531			*cp++ = ch;
532		} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
533		*cp = 0;
534
535		if (buf[0] == '\01' || buf[0] == '\02') {
536			if (iamremote == 0)
537				(void)write(STDERR_FILENO,
538				    buf + 1, strlen(buf + 1));
539			if (buf[0] == '\02')
540				exit(1);
541			++errs;
542			continue;
543		}
544		if (buf[0] == 'E') {
545			(void)write(rem, "", 1);
546			return;
547		}
548
549		if (ch == '\n')
550			*--cp = 0;
551
552		cp = buf;
553		if (*cp == 'T') {
554			setimes++;
555			cp++;
556			mtime.tv_sec = strtol(cp, &cp, 10);
557			if (!cp || *cp++ != ' ')
558				SCREWUP("mtime.sec not delimited");
559			mtime.tv_usec = strtol(cp, &cp, 10);
560			if (!cp || *cp++ != ' ')
561				SCREWUP("mtime.usec not delimited");
562			atime.tv_sec = strtol(cp, &cp, 10);
563			if (!cp || *cp++ != ' ')
564				SCREWUP("atime.sec not delimited");
565			atime.tv_usec = strtol(cp, &cp, 10);
566			if (!cp || *cp++ != '\0')
567				SCREWUP("atime.usec not delimited");
568			(void)write(rem, "", 1);
569			continue;
570		}
571		if (*cp != 'C' && *cp != 'D') {
572			/*
573			 * Check for the case "rcp remote:foo\* local:bar".
574			 * In this case, the line "No match." can be returned
575			 * by the shell before the rcp command on the remote is
576			 * executed so the ^Aerror_message convention isn't
577			 * followed.
578			 */
579			if (first) {
580				run_err("%s", cp);
581				exit(1);
582			}
583			SCREWUP("expected control record");
584		}
585		mode = 0;
586		for (++cp; cp < buf + 5; cp++) {
587			if (*cp < '0' || *cp > '7')
588				SCREWUP("bad mode");
589			mode = (mode << 3) | (*cp - '0');
590		}
591		if (*cp++ != ' ')
592			SCREWUP("mode not delimited");
593
594		for (size = 0; isdigit(*cp);)
595			size = size * 10 + (*cp++ - '0');
596		if (*cp++ != ' ')
597			SCREWUP("size not delimited");
598		if (targisdir) {
599			if (strlen(targ) + (*targ ? 1 : 0) + strlen(cp)
600					>= sizeof(path)) {
601				run_err("%s%s%s: name too long", targ,
602					*targ ? "/" : "", cp);
603				exit(1);
604			}
605			(void)snprintf(path, sizeof(path), "%s%s%s", targ,
606			    *targ ? "/" : "", cp);
607			np = path;
608		} else
609			np = targ;
610		exists = stat(np, &stb) == 0;
611		if (buf[0] == 'D') {
612			int mod_flag = pflag;
613			if (exists) {
614				if (!S_ISDIR(stb.st_mode)) {
615					errno = ENOTDIR;
616					goto bad;
617				}
618				if (pflag)
619					(void)chmod(np, mode);
620			} else {
621				/* Handle copying from a read-only directory */
622				mod_flag = 1;
623				if (mkdir(np, mode | S_IRWXU) < 0)
624					goto bad;
625			}
626			vect[0] = np;
627			sink(1, vect);
628			if (setimes) {
629				setimes = 0;
630				if (utimes(np, tv) < 0)
631				    run_err("%s: set times: %s",
632					np, strerror(errno));
633			}
634			if (mod_flag)
635				(void)chmod(np, mode);
636			continue;
637		}
638		omode = mode;
639		mode |= S_IWRITE;
640		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
641bad:			run_err("%s: %s", np, strerror(errno));
642			continue;
643		}
644		(void)write(rem, "", 1);
645		if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) {
646			(void)close(ofd);
647			continue;
648		}
649		cp = bp->buf;
650		wrerr = NO;
651		for (count = i = 0; i < size; i += BUFSIZ) {
652			amt = BUFSIZ;
653			if (i + amt > size)
654				amt = size - i;
655			count += amt;
656			do {
657				j = read(rem, cp, amt);
658				if (j <= 0) {
659					run_err("%s", j ? strerror(errno) :
660					    "dropped connection");
661					exit(1);
662				}
663				amt -= j;
664				cp += j;
665			} while (amt > 0);
666			if (count == bp->cnt) {
667				/* Keep reading so we stay sync'd up. */
668				if (wrerr == NO) {
669					j = write(ofd, bp->buf, count);
670					if (j != (off_t)count) {
671						wrerr = YES;
672						wrerrno = j >= 0 ? EIO : errno;
673					}
674				}
675				count = 0;
676				cp = bp->buf;
677			}
678		}
679		if (count != 0 && wrerr == NO &&
680		    (j = write(ofd, bp->buf, count)) != (off_t)count) {
681			wrerr = YES;
682			wrerrno = j >= 0 ? EIO : errno;
683		}
684		if (ftruncate(ofd, size)) {
685			run_err("%s: truncate: %s", np, strerror(errno));
686			wrerr = DISPLAYED;
687		}
688		if (pflag) {
689			if (exists || omode != mode)
690				if (fchmod(ofd, omode))
691					run_err("%s: set mode: %s",
692					    np, strerror(errno));
693		} else {
694			if (!exists && omode != mode)
695				if (fchmod(ofd, omode & ~mask))
696					run_err("%s: set mode: %s",
697					    np, strerror(errno));
698		}
699		(void)close(ofd);
700		(void)response();
701		if (setimes && wrerr == NO) {
702			setimes = 0;
703			if (utimes(np, tv) < 0) {
704				run_err("%s: set times: %s",
705				    np, strerror(errno));
706				wrerr = DISPLAYED;
707			}
708		}
709		switch(wrerr) {
710		case YES:
711			run_err("%s: %s", np, strerror(wrerrno));
712			break;
713		case NO:
714			(void)write(rem, "", 1);
715			break;
716		case DISPLAYED:
717			break;
718		}
719	}
720screwup:
721	run_err("protocol error: %s", why);
722	exit(1);
723}
724
725int
726response(void)
727{
728	char ch, *cp, resp, rbuf[BUFSIZ];
729
730	if (read(rem, &resp, sizeof(resp)) != sizeof(resp))
731		lostconn(0);
732
733	cp = rbuf;
734	switch(resp) {
735	case 0:				/* ok */
736		return (0);
737	default:
738		*cp++ = resp;
739		/* FALLTHROUGH */
740	case 1:				/* error, followed by error msg */
741	case 2:				/* fatal error, "" */
742		do {
743			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
744				lostconn(0);
745			*cp++ = ch;
746		} while (cp < &rbuf[BUFSIZ] && ch != '\n');
747
748		if (!iamremote)
749			(void)write(STDERR_FILENO, rbuf, cp - rbuf);
750		++errs;
751		if (resp == 1)
752			return (-1);
753		exit(1);
754	}
755	/* NOTREACHED */
756}
757
758void
759usage(void)
760{
761	(void)fprintf(stderr, "%s\n%s\n",
762	    "usage: rcp [-46p] file1 file2",
763	    "       rcp [-46pr] file ... directory");
764	exit(1);
765}
766
767#include <stdarg.h>
768
769void
770run_err(const char *fmt, ...)
771{
772	static FILE *fp;
773	va_list ap;
774
775	++errs;
776	if (fp == NULL && !(fp = fdopen(rem, "w")))
777		return;
778	(void)fprintf(fp, "%c", 0x01);
779	(void)fprintf(fp, "rcp: ");
780	va_start(ap, fmt);
781	(void)vfprintf(fp, fmt, ap);
782	va_end(ap);
783	(void)fprintf(fp, "\n");
784	(void)fflush(fp);
785
786	if (!iamremote) {
787		va_start(ap, fmt);
788		vwarnx(fmt, ap);
789		va_end(ap);
790	}
791}
792