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