rcp.c revision 90926
1200SN/A/*
23294Salanb * Copyright (c) 1983, 1990, 1992, 1993
3200SN/A *	The Regents of the University of California.  All rights reserved.
4200SN/A *
5200SN/A * Redistribution and use in source and binary forms, with or without
6200SN/A * modification, are permitted provided that the following conditions
7553SN/A * are met:
8200SN/A * 1. Redistributions of source code must retain the above copyright
9553SN/A *    notice, this list of conditions and the following disclaimer.
10200SN/A * 2. Redistributions in binary form must reproduce the above copyright
11200SN/A *    notice, this list of conditions and the following disclaimer in the
12200SN/A *    documentation and/or other materials provided with the distribution.
13200SN/A * 3. All advertising materials mentioning features or use of this software
14200SN/A *    must display the following acknowledgement:
15200SN/A *	This product includes software developed by the University of
16200SN/A *	California, Berkeley and its contributors.
17200SN/A * 4. Neither the name of the University nor the names of its contributors
18200SN/A *    may be used to endorse or promote products derived from this software
19200SN/A *    without specific prior written permission.
20200SN/A *
21553SN/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22553SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23553SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24200SN/A * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25200SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261223Sohrstrom * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271223Sohrstrom * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28200SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29200SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30200SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31200SN/A * SUCH DAMAGE.
32200SN/A */
33200SN/A
34200SN/A#include "rcp_locl.h"
35200SN/A#include <getarg.h>
36200SN/A
37200SN/A#define RSH_PROGRAM "rsh"
38200SN/A
39200SN/Astruct  passwd *pwd;
40200SN/Auid_t	userid;
41200SN/Aint     errs, remin, remout;
42200SN/Aint     pflag, iamremote, iamrecursive, targetshouldbedirectory;
43200SN/Aint     doencrypt, noencrypt;
442020Smcimadamoreint     usebroken, usekrb5, forwardtkt;
45200SN/Achar    *port;
462603Sksrini
47200SN/A#define	CMDNEEDS	64
48200SN/Achar cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
49200SN/A
50200SN/Aint	 response (void);
51200SN/Avoid	 rsource (char *, struct stat *);
52200SN/Avoid	 sink (int, char *[]);
53200SN/Avoid	 source (int, char *[]);
54200SN/Avoid	 tolocal (int, char *[]);
55200SN/Avoid	 toremote (char *, int, char *[]);
56200SN/A
572603Sksriniint      do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
58200SN/A
59200SN/Astatic int fflag, tflag;
60200SN/A
61200SN/Astatic int version_flag, help_flag;
62200SN/A
63200SN/Astruct getargs args[] = {
64200SN/A    { NULL,	'5', arg_flag,		&usekrb5,	"use Kerberos 5 authentication" },
65200SN/A    { NULL,	'F', arg_flag,		&forwardtkt,	"forward credentials" },
66200SN/A    { NULL,	'K', arg_flag,		&usebroken,	"use BSD authentication" },
67200SN/A    { NULL,	'P', arg_string,	&port,		"non-default port", "port" },
68200SN/A    { NULL,	'p', arg_flag,		&pflag,	"preserve file permissions" },
69200SN/A    { NULL,	'r', arg_flag,		&iamrecursive,	"recursive mode" },
70200SN/A    { NULL,	'x', arg_flag,		&doencrypt,	"use encryption" },
71200SN/A    { NULL,	'z', arg_flag,		&noencrypt,	"don't encrypt" },
72200SN/A    { NULL,	'd', arg_flag,		&targetshouldbedirectory },
73200SN/A    { NULL,	'f', arg_flag,		&fflag },
742020Smcimadamore    { NULL,	't', arg_flag,		&tflag },
752020Smcimadamore    { "version", 0,  arg_flag,		&version_flag },
762020Smcimadamore    { "help",	 0,  arg_flag,		&help_flag }
773294Salanb};
782020Smcimadamore
792020Smcimadamorestatic void
803062Sjlahodausage (int ret)
813062Sjlahoda{
822020Smcimadamore    arg_printusage (args,
832020Smcimadamore		    sizeof(args) / sizeof(args[0]),
842020Smcimadamore		    NULL,
852020Smcimadamore		    "file1 file2|file... directory");
862020Smcimadamore    exit (ret);
872020Smcimadamore}
882020Smcimadamore
892020Smcimadamoreint
902063Svromeromain(int argc, char **argv)
912020Smcimadamore{
922063Svromero	char *targ;
932020Smcimadamore	int optind = 0;
942020Smcimadamore
952020Smcimadamore	setprogname(argv[0]);
962020Smcimadamore	if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
972020Smcimadamore		    &optind))
982020Smcimadamore	    usage (1);
992020Smcimadamore	if(help_flag)
1002020Smcimadamore	    usage(0);
101200SN/A	if (version_flag) {
102200SN/A	    print_version (NULL);
103200SN/A	    return 0;
1042603Sksrini	}
105200SN/A
106200SN/A	iamremote = (fflag || tflag);
107200SN/A
108200SN/A	argc -= optind;
109200SN/A	argv += optind;
110200SN/A
111200SN/A	if ((pwd = getpwuid(userid = getuid())) == NULL)
112200SN/A		errx(1, "unknown user %d", (int)userid);
113200SN/A
1142603Sksrini	remin = STDIN_FILENO;		/* XXX */
115200SN/A	remout = STDOUT_FILENO;
116200SN/A
117200SN/A	if (fflag) {			/* Follow "protocol", send data. */
118200SN/A		response();
119200SN/A		setuid(userid);
120200SN/A		source(argc, argv);
121200SN/A		exit(errs);
122200SN/A	}
1232603Sksrini
124200SN/A	if (tflag) {			/* Receive data. */
125200SN/A		setuid(userid);
126200SN/A		sink(argc, argv);
127200SN/A		exit(errs);
128200SN/A	}
129200SN/A
130200SN/A	if (argc < 2)
131200SN/A	    usage(1);
1322603Sksrini	if (argc > 2)
133200SN/A		targetshouldbedirectory = 1;
134200SN/A
135200SN/A	remin = remout = -1;
136200SN/A	/* Command to be executed on remote system using "rsh". */
137200SN/A	snprintf(cmd, sizeof(cmd),
138200SN/A		 "rcp%s%s%s", iamrecursive ? " -r" : "",
139200SN/A		 pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "");
140200SN/A
141200SN/A	signal(SIGPIPE, lostconn);
142200SN/A
143200SN/A	if ((targ = colon(argv[argc - 1])))	/* Dest is remote host. */
144200SN/A		toremote(targ, argc, argv);
145200SN/A	else {
146200SN/A		tolocal(argc, argv);		/* Dest is local host. */
147200SN/A		if (targetshouldbedirectory)
148200SN/A			verifydir(argv[argc - 1]);
149200SN/A	}
150200SN/A	exit(errs);
151200SN/A}
152200SN/A
153200SN/Avoid
154200SN/Atoremote(char *targ, int argc, char **argv)
155200SN/A{
156200SN/A	int i;
157200SN/A	char *bp, *host, *src, *suser, *thost, *tuser;
158200SN/A
159200SN/A	*targ++ = 0;
160200SN/A	if (*targ == 0)
161200SN/A		targ = ".";
162200SN/A
163200SN/A	if ((thost = strchr(argv[argc - 1], '@'))) {
164200SN/A		/* user@host */
165200SN/A		*thost++ = 0;
166200SN/A		tuser = argv[argc - 1];
167200SN/A		if (*tuser == '\0')
1682710Sjlahoda			tuser = NULL;
1692710Sjlahoda		else if (!okname(tuser))
1703294Salanb			exit(1);
171200SN/A	} else {
172200SN/A		thost = argv[argc - 1];
173200SN/A		tuser = NULL;
174200SN/A	}
1752020Smcimadamore
176200SN/A	for (i = 0; i < argc - 1; i++) {
1773294Salanb		src = colon(argv[i]);
178200SN/A		if (src) {			/* remote to remote */
179200SN/A			*src++ = 0;
180200SN/A			if (*src == 0)
181200SN/A				src = ".";
182200SN/A			host = strchr(argv[i], '@');
183200SN/A			if (host) {
184200SN/A				*host++ = '\0';
185200SN/A				suser = argv[i];
186200SN/A				if (*suser == '\0')
187200SN/A					suser = pwd->pw_name;
188200SN/A				else if (!okname(suser))
189200SN/A					continue;
190200SN/A				asprintf(&bp,
191200SN/A				    "%s %s -l %s -n %s %s '%s%s%s:%s'",
1922020Smcimadamore				    _PATH_RSH, host, suser, cmd, src,
193200SN/A				    tuser ? tuser : "", tuser ? "@" : "",
194200SN/A				    thost, targ);
1952020Smcimadamore			} else {
1962020Smcimadamore				asprintf(&bp,
197200SN/A				    "exec %s %s -n %s %s '%s%s%s:%s'",
198200SN/A				    _PATH_RSH, argv[i], cmd, src,
1992020Smcimadamore				    tuser ? tuser : "", tuser ? "@" : "",
2002020Smcimadamore				    thost, targ);
2012603Sksrini			}
2022710Sjlahoda			if (bp == NULL)
2032710Sjlahoda				err (1, "malloc");
2042710Sjlahoda			susystem(bp, userid);
2052710Sjlahoda			free(bp);
2063827Smcimadamore		} else {			/* local to remote */
2073827Smcimadamore			if (remin == -1) {
2083827Smcimadamore				asprintf(&bp, "%s -t %s", cmd, targ);
2093827Smcimadamore				if (bp == NULL)
2103827Smcimadamore					err (1, "malloc");
211200SN/A				host = thost;
2122020Smcimadamore
2132020Smcimadamore				if (do_cmd(host, tuser, bp, &remin, &remout) < 0)
2142020Smcimadamore					exit(1);
2152020Smcimadamore
2162020Smcimadamore				if (response() < 0)
2172020Smcimadamore					exit(1);
2182020Smcimadamore				free(bp);
2192020Smcimadamore				setuid(userid);
220200SN/A			}
2212710Sjlahoda			source(1, argv+i);
222200SN/A		}
223200SN/A	}
224200SN/A}
225200SN/A
226200SN/Avoid
2272603Sksrinitolocal(int argc, char **argv)
228200SN/A{
229200SN/A	int i;
2302603Sksrini	char *bp, *host, *src, *suser;
231200SN/A
2322020Smcimadamore	for (i = 0; i < argc - 1; i++) {
233200SN/A		if (!(src = colon(argv[i]))) {		/* Local to local. */
234200SN/A			asprintf(&bp, "exec %s%s%s %s %s", _PATH_CP,
235200SN/A			    iamrecursive ? " -PR" : "", pflag ? " -p" : "",
236200SN/A			    argv[i], argv[argc - 1]);
237200SN/A			if (bp == NULL)
238200SN/A				err (1, "malloc");
239200SN/A			if (susystem(bp, userid))
240200SN/A				++errs;
241200SN/A			free(bp);
242200SN/A			continue;
243200SN/A		}
244200SN/A		*src++ = 0;
245200SN/A		if (*src == 0)
246200SN/A			src = ".";
247200SN/A		if ((host = strchr(argv[i], '@')) == NULL) {
2483827Smcimadamore			host = argv[i];
2493827Smcimadamore			suser = pwd->pw_name;
2503827Smcimadamore		} else {
251200SN/A			*host++ = 0;
252200SN/A			suser = argv[i];
253200SN/A			if (*suser == '\0')
254200SN/A				suser = pwd->pw_name;
255200SN/A			else if (!okname(suser))
256200SN/A				continue;
257200SN/A		}
258200SN/A		asprintf(&bp, "%s -f %s", cmd, src);
259200SN/A		if (bp == NULL)
260200SN/A			err (1, "malloc");
261200SN/A		if (do_cmd(host, suser, bp, &remin, &remout) < 0) {
262200SN/A			free(bp);
263200SN/A			++errs;
264200SN/A			continue;
265200SN/A		}
266200SN/A		free(bp);
267200SN/A		sink(1, argv + argc - 1);
268200SN/A		seteuid(0);
269200SN/A		close(remin);
270200SN/A		remin = remout = -1;
271200SN/A	}
272200SN/A}
273200SN/A
274200SN/Avoid
275200SN/Asource(int argc, char **argv)
276200SN/A{
277200SN/A	struct stat stb;
278200SN/A	static BUF buffer;
279200SN/A	BUF *bp;
280200SN/A	off_t i;
281200SN/A	int amt, fd, haderr, indx, result;
282200SN/A	char *last, *name, buf[BUFSIZ];
283200SN/A
284200SN/A	for (indx = 0; indx < argc; ++indx) {
285200SN/A                name = argv[indx];
286200SN/A		if ((fd = open(name, O_RDONLY, 0)) < 0)
287200SN/A			goto syserr;
288200SN/A		if (fstat(fd, &stb)) {
289200SN/Asyserr:			run_err("%s: %s", name, strerror(errno));
290200SN/A			goto next;
291200SN/A		}
292200SN/A		switch (stb.st_mode & S_IFMT) {
293200SN/A		case S_IFREG:
294200SN/A			break;
295200SN/A		case S_IFDIR:
296200SN/A			if (iamrecursive) {
297200SN/A				rsource(name, &stb);
298200SN/A				goto next;
299200SN/A			}
300200SN/A			/* FALLTHROUGH */
301200SN/A		default:
302200SN/A			run_err("%s: not a regular file", name);
303200SN/A			goto next;
3042710Sjlahoda		}
3052710Sjlahoda		if ((last = strrchr(name, '/')) == NULL)
3062710Sjlahoda			last = name;
3073294Salanb		else
3082710Sjlahoda			++last;
309200SN/A		if (pflag) {
310200SN/A			/*
311200SN/A			 * Make it compatible with possible future
312200SN/A			 * versions expecting microseconds.
313200SN/A			 */
314200SN/A			snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n",
315200SN/A			    (long)stb.st_mtime,
316200SN/A			    (long)stb.st_atime);
317200SN/A			write(remout, buf, strlen(buf));
3182603Sksrini			if (response() < 0)
319200SN/A				goto next;
320200SN/A		}
321200SN/A#define	MODEMASK	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
322200SN/A		snprintf(buf, sizeof(buf), "C%04o %lu %s\n",
323200SN/A			 stb.st_mode & MODEMASK,
324200SN/A			 (unsigned long)stb.st_size,
325200SN/A			 last);
326200SN/A		write(remout, buf, strlen(buf));
327		if (response() < 0)
328			goto next;
329		if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) {
330next:			close(fd);
331			continue;
332		}
333
334		/* Keep writing after an error so that we stay sync'd up. */
335		for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
336			amt = bp->cnt;
337			if (i + amt > stb.st_size)
338				amt = stb.st_size - i;
339			if (!haderr) {
340				result = read(fd, bp->buf, amt);
341				if (result != amt)
342					haderr = result >= 0 ? EIO : errno;
343			}
344			if (haderr)
345				write(remout, bp->buf, amt);
346			else {
347				result = write(remout, bp->buf, amt);
348				if (result != amt)
349					haderr = result >= 0 ? EIO : errno;
350			}
351		}
352		if (close(fd) && !haderr)
353			haderr = errno;
354		if (!haderr)
355			write(remout, "", 1);
356		else
357			run_err("%s: %s", name, strerror(haderr));
358		response();
359	}
360}
361
362void
363rsource(char *name, struct stat *statp)
364{
365	DIR *dirp;
366	struct dirent *dp;
367	char *last, *vect[1], path[MAXPATHLEN];
368
369	if (!(dirp = opendir(name))) {
370		run_err("%s: %s", name, strerror(errno));
371		return;
372	}
373	last = strrchr(name, '/');
374	if (last == 0)
375		last = name;
376	else
377		last++;
378	if (pflag) {
379		snprintf(path, sizeof(path), "T%ld 0 %ld 0\n",
380		    (long)statp->st_mtime,
381		    (long)statp->st_atime);
382		write(remout, path, strlen(path));
383		if (response() < 0) {
384			closedir(dirp);
385			return;
386		}
387	}
388	snprintf(path, sizeof(path),
389	    "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last);
390	write(remout, path, strlen(path));
391	if (response() < 0) {
392		closedir(dirp);
393		return;
394	}
395	while ((dp = readdir(dirp))) {
396		if (dp->d_ino == 0)
397			continue;
398		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
399			continue;
400		if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) {
401			run_err("%s/%s: name too long", name, dp->d_name);
402			continue;
403		}
404		snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
405		vect[0] = path;
406		source(1, vect);
407	}
408	closedir(dirp);
409	write(remout, "E\n", 2);
410	response();
411}
412
413void
414sink(int argc, char **argv)
415{
416	static BUF buffer;
417	struct stat stb;
418	struct timeval tv[2];
419	enum { YES, NO, DISPLAYED } wrerr;
420	BUF *bp;
421	off_t i, j, size;
422	int amt, count, exists, first, mask, mode, ofd, omode;
423	int setimes, targisdir, wrerrno = 0;
424	char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ];
425
426#define	atime	tv[0]
427#define	mtime	tv[1]
428#define	SCREWUP(str)	{ why = str; goto screwup; }
429
430	setimes = targisdir = 0;
431	mask = umask(0);
432	if (!pflag)
433		umask(mask);
434	if (argc != 1) {
435		run_err("ambiguous target");
436		exit(1);
437	}
438	targ = *argv;
439	if (targetshouldbedirectory)
440		verifydir(targ);
441	write(remout, "", 1);
442	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
443		targisdir = 1;
444	for (first = 1;; first = 0) {
445		cp = buf;
446		if (read(remin, cp, 1) <= 0)
447			return;
448		if (*cp++ == '\n')
449			SCREWUP("unexpected <newline>");
450		do {
451			if (read(remin, &ch, sizeof(ch)) != sizeof(ch))
452				SCREWUP("lost connection");
453			*cp++ = ch;
454		} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
455		*cp = 0;
456
457		if (buf[0] == '\01' || buf[0] == '\02') {
458			if (iamremote == 0)
459				write(STDERR_FILENO,
460				    buf + 1, strlen(buf + 1));
461			if (buf[0] == '\02')
462				exit(1);
463			++errs;
464			continue;
465		}
466		if (buf[0] == 'E') {
467			write(remout, "", 1);
468			return;
469		}
470
471		if (ch == '\n')
472			*--cp = 0;
473
474		cp = buf;
475		if (*cp == 'T') {
476			setimes++;
477			cp++;
478			mtime.tv_sec = strtol(cp, &cp, 10);
479			if (!cp || *cp++ != ' ')
480				SCREWUP("mtime.sec not delimited");
481			mtime.tv_usec = strtol(cp, &cp, 10);
482			if (!cp || *cp++ != ' ')
483				SCREWUP("mtime.usec not delimited");
484			atime.tv_sec = strtol(cp, &cp, 10);
485			if (!cp || *cp++ != ' ')
486				SCREWUP("atime.sec not delimited");
487			atime.tv_usec = strtol(cp, &cp, 10);
488			if (!cp || *cp++ != '\0')
489				SCREWUP("atime.usec not delimited");
490			write(remout, "", 1);
491			continue;
492		}
493		if (*cp != 'C' && *cp != 'D') {
494			/*
495			 * Check for the case "rcp remote:foo\* local:bar".
496			 * In this case, the line "No match." can be returned
497			 * by the shell before the rcp command on the remote is
498			 * executed so the ^Aerror_message convention isn't
499			 * followed.
500			 */
501			if (first) {
502				run_err("%s", cp);
503				exit(1);
504			}
505			SCREWUP("expected control record");
506		}
507		mode = 0;
508		for (++cp; cp < buf + 5; cp++) {
509			if (*cp < '0' || *cp > '7')
510				SCREWUP("bad mode");
511			mode = (mode << 3) | (*cp - '0');
512		}
513		if (*cp++ != ' ')
514			SCREWUP("mode not delimited");
515
516		for (size = 0; isdigit((unsigned char)*cp);)
517			size = size * 10 + (*cp++ - '0');
518		if (*cp++ != ' ')
519			SCREWUP("size not delimited");
520		if (targisdir) {
521			static char *namebuf;
522			static int cursize;
523			size_t need;
524
525			need = strlen(targ) + strlen(cp) + 250;
526			if (need > cursize) {
527				if (!(namebuf = malloc(need)))
528					run_err("%s", strerror(errno));
529			}
530			snprintf(namebuf, need, "%s%s%s", targ,
531			    *targ ? "/" : "", cp);
532			np = namebuf;
533		} else
534			np = targ;
535		exists = stat(np, &stb) == 0;
536		if (buf[0] == 'D') {
537			int mod_flag = pflag;
538			if (exists) {
539				if (!S_ISDIR(stb.st_mode)) {
540					errno = ENOTDIR;
541					goto bad;
542				}
543				if (pflag)
544					chmod(np, mode);
545			} else {
546				/* Handle copying from a read-only directory */
547				mod_flag = 1;
548				if (mkdir(np, mode | S_IRWXU) < 0)
549					goto bad;
550			}
551			vect[0] = np;
552			sink(1, vect);
553			if (setimes) {
554				setimes = 0;
555				if (utimes(np, tv) < 0)
556				    run_err("%s: set times: %s",
557					np, strerror(errno));
558			}
559			if (mod_flag)
560				chmod(np, mode);
561			continue;
562		}
563		omode = mode;
564		mode |= S_IWRITE;
565		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
566bad:			run_err("%s: %s", np, strerror(errno));
567			continue;
568		}
569		write(remout, "", 1);
570		if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) {
571			close(ofd);
572			continue;
573		}
574		cp = bp->buf;
575		wrerr = NO;
576		for (count = i = 0; i < size; i += BUFSIZ) {
577			amt = BUFSIZ;
578			if (i + amt > size)
579				amt = size - i;
580			count += amt;
581			if((j = net_read(remin, cp, amt)) != amt) {
582			    run_err("%s", j ? strerror(errno) :
583				    "dropped connection");
584			    exit(1);
585			}
586			amt -= j;
587			cp += j;
588			if (count == bp->cnt) {
589				/* Keep reading so we stay sync'd up. */
590				if (wrerr == NO) {
591					j = write(ofd, bp->buf, count);
592					if (j != count) {
593						wrerr = YES;
594						wrerrno = j >= 0 ? EIO : errno;
595					}
596				}
597				count = 0;
598				cp = bp->buf;
599			}
600		}
601		if (count != 0 && wrerr == NO &&
602		    (j = write(ofd, bp->buf, count)) != count) {
603			wrerr = YES;
604			wrerrno = j >= 0 ? EIO : errno;
605		}
606		if (ftruncate(ofd, size)) {
607			run_err("%s: truncate: %s", np, strerror(errno));
608			wrerr = DISPLAYED;
609		}
610		if (pflag) {
611			if (exists || omode != mode)
612				if (fchmod(ofd, omode))
613					run_err("%s: set mode: %s",
614					    np, strerror(errno));
615		} else {
616			if (!exists && omode != mode)
617				if (fchmod(ofd, omode & ~mask))
618					run_err("%s: set mode: %s",
619					    np, strerror(errno));
620		}
621		close(ofd);
622		response();
623		if (setimes && wrerr == NO) {
624			setimes = 0;
625			if (utimes(np, tv) < 0) {
626				run_err("%s: set times: %s",
627				    np, strerror(errno));
628				wrerr = DISPLAYED;
629			}
630		}
631		switch(wrerr) {
632		case YES:
633			run_err("%s: %s", np, strerror(wrerrno));
634			break;
635		case NO:
636			write(remout, "", 1);
637			break;
638		case DISPLAYED:
639			break;
640		}
641	}
642screwup:
643	run_err("protocol error: %s", why);
644	exit(1);
645}
646
647int
648response(void)
649{
650	char ch, *cp, resp, rbuf[BUFSIZ];
651
652	if (read(remin, &resp, sizeof(resp)) != sizeof(resp))
653		lostconn(0);
654
655	cp = rbuf;
656	switch(resp) {
657	case 0:				/* ok */
658		return (0);
659	default:
660		*cp++ = resp;
661		/* FALLTHROUGH */
662	case 1:				/* error, followed by error msg */
663	case 2:				/* fatal error, "" */
664		do {
665			if (read(remin, &ch, sizeof(ch)) != sizeof(ch))
666				lostconn(0);
667			*cp++ = ch;
668		} while (cp < &rbuf[BUFSIZ] && ch != '\n');
669
670		if (!iamremote)
671			write(STDERR_FILENO, rbuf, cp - rbuf);
672		++errs;
673		if (resp == 1)
674			return (-1);
675		exit(1);
676	}
677	/* NOTREACHED */
678}
679
680#include <stdarg.h>
681
682void
683run_err(const char *fmt, ...)
684{
685	static FILE *fp;
686	va_list ap;
687
688	++errs;
689	if (fp == NULL && !(fp = fdopen(remout, "w")))
690		return;
691	va_start(ap, fmt);
692	fprintf(fp, "%c", 0x01);
693	fprintf(fp, "rcp: ");
694	vfprintf(fp, fmt, ap);
695	fprintf(fp, "\n");
696	fflush(fp);
697	va_end(ap);
698
699	if (!iamremote) {
700	    va_start(ap, fmt);
701	    vwarnx(fmt, ap);
702	    va_end(ap);
703	}
704}
705
706/*
707 * This function executes the given command as the specified user on the
708 * given host.  This returns < 0 if execution fails, and >= 0 otherwise. This
709 * assigns the input and output file descriptors on success.
710 *
711 * If it cannot create necessary pipes it exits with error message.
712 */
713
714int
715do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
716{
717	int pin[2], pout[2], reserved[2];
718
719	/*
720	 * Reserve two descriptors so that the real pipes won't get
721	 * descriptors 0 and 1 because that will screw up dup2 below.
722	 */
723	pipe(reserved);
724
725	/* Create a socket pair for communicating with rsh. */
726	if (pipe(pin) < 0) {
727		perror("pipe");
728		exit(255);
729	}
730	if (pipe(pout) < 0) {
731		perror("pipe");
732		exit(255);
733	}
734
735	/* Free the reserved descriptors. */
736	close(reserved[0]);
737	close(reserved[1]);
738
739	/* For a child to execute the command on the remote host using rsh. */
740	if (fork() == 0) {
741		char *args[100];
742		unsigned int i;
743
744		/* Child. */
745		close(pin[1]);
746		close(pout[0]);
747		dup2(pin[0], 0);
748		dup2(pout[1], 1);
749		close(pin[0]);
750		close(pout[1]);
751
752		i = 0;
753		args[i++] = RSH_PROGRAM;
754		if (usekrb5)
755			args[i++] = "-5";
756		if (usebroken)
757			args[i++] = "-K";
758		if (doencrypt)
759			args[i++] = "-x";
760		if (forwardtkt)
761			args[i++] = "-F";
762		if (noencrypt)
763			args[i++] = "-z";
764		if (port != NULL) {
765			args[i++] = "-p";
766			args[i++] = port;
767		}
768		if (remuser != NULL) {
769			args[i++] = "-l";
770			args[i++] = remuser;
771		}
772		args[i++] = host;
773		args[i++] = cmd;
774		args[i++] = NULL;
775
776		execvp(RSH_PROGRAM, args);
777		perror(RSH_PROGRAM);
778		exit(1);
779	}
780	/* Parent.  Close the other side, and return the local side. */
781	close(pin[0]);
782	*fdout = pin[1];
783	close(pout[1]);
784	*fdin = pout[0];
785	return 0;
786}
787