xref: /netbsd-src/bin/rcp/rcp.c (revision f386908b273c66be8430ac51f71a66ae1d59dc37)
1*f386908bSmrg /*	$NetBSD: rcp.c,v 1.53 2023/08/01 08:47:24 mrg Exp $	*/
26ff461a4Scgd 
361f28255Scgd /*
46ff461a4Scgd  * Copyright (c) 1983, 1990, 1992, 1993
56ff461a4Scgd  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
15b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
32e800d25eSchristos #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
342fe2731dSlukem __COPYRIGHT("@(#) Copyright (c) 1983, 1990, 1992, 1993\
352fe2731dSlukem  The Regents of the University of California.  All rights reserved.");
3661f28255Scgd #endif /* not lint */
3761f28255Scgd 
3861f28255Scgd #ifndef lint
396ff461a4Scgd #if 0
406ff461a4Scgd static char sccsid[] = "@(#)rcp.c	8.2 (Berkeley) 4/2/94";
416ff461a4Scgd #else
42*f386908bSmrg __RCSID("$NetBSD: rcp.c,v 1.53 2023/08/01 08:47:24 mrg Exp $");
436ff461a4Scgd #endif
4461f28255Scgd #endif /* not lint */
4561f28255Scgd 
4661f28255Scgd #include <sys/param.h>
4761f28255Scgd #include <sys/stat.h>
4861f28255Scgd #include <sys/time.h>
4961f28255Scgd #include <sys/socket.h>
5061f28255Scgd #include <netinet/in.h>
5161f28255Scgd #include <netinet/in_systm.h>
5261f28255Scgd #include <netinet/ip.h>
536ff461a4Scgd 
546ff461a4Scgd #include <ctype.h>
5561f28255Scgd #include <dirent.h>
566ff461a4Scgd #include <err.h>
5761f28255Scgd #include <errno.h>
586ff461a4Scgd #include <fcntl.h>
599655f5c2Schristos #include <locale.h>
606ff461a4Scgd #include <netdb.h>
611ca2da5bSrin #include <paths.h>
626ff461a4Scgd #include <pwd.h>
636ff461a4Scgd #include <signal.h>
6461f28255Scgd #include <stdio.h>
6561f28255Scgd #include <stdlib.h>
6661f28255Scgd #include <string.h>
676ff461a4Scgd #include <unistd.h>
686ff461a4Scgd 
6961f28255Scgd #include "pathnames.h"
706ff461a4Scgd #include "extern.h"
7161f28255Scgd 
7225e3a7ecSginsbach #define	OPTIONS "46dfprt"
7361f28255Scgd 
7461f28255Scgd struct passwd *pwd;
75b021feb7Schristos char *pwname;
7661f28255Scgd u_short	port;
7761f28255Scgd uid_t	userid;
7861f28255Scgd int errs, rem;
7961f28255Scgd int pflag, iamremote, iamrecursive, targetshouldbedirectory;
8025e3a7ecSginsbach int family = AF_UNSPEC;
816310b596Schristos static char dot[] = ".";
8261f28255Scgd 
831ca2da5bSrin static sig_atomic_t print_info = 0;
841ca2da5bSrin 
8561f28255Scgd #define	CMDNEEDS	64
8661f28255Scgd char cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
8761f28255Scgd 
880b39b8daSxtraeme int	 response(void);
890b39b8daSxtraeme void	 rsource(char *, struct stat *);
900b39b8daSxtraeme void	 sink(int, char *[]);
910b39b8daSxtraeme void	 source(int, char *[]);
920b39b8daSxtraeme void	 tolocal(int, char *[]);
930b39b8daSxtraeme void	 toremote(char *, int, char *[]);
940b39b8daSxtraeme void	 usage(void);
951ca2da5bSrin static void	got_siginfo(int);
961ca2da5bSrin static void	progress(const char *, uintmax_t, uintmax_t);
9761f28255Scgd 
985dad1439Scgd int
main(int argc,char * argv[])990b39b8daSxtraeme main(int argc, char *argv[])
10061f28255Scgd {
10161f28255Scgd 	struct servent *sp;
10261f28255Scgd 	int ch, fflag, tflag;
1036310b596Schristos 	char *targ;
1046310b596Schristos 	const char *shell;
10561f28255Scgd 
1069655f5c2Schristos 	setprogname(argv[0]);
1079655f5c2Schristos 	(void)setlocale(LC_ALL, "");
1089655f5c2Schristos 
10961f28255Scgd 	fflag = tflag = 0;
1107ff9d68cSlukem 	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
1116ff461a4Scgd 		switch(ch) {			/* User-visible flags. */
11225e3a7ecSginsbach 		case '4':
11325e3a7ecSginsbach 			family = AF_INET;
11425e3a7ecSginsbach 			break;
11525e3a7ecSginsbach 		case '6':
11625e3a7ecSginsbach 			family = AF_INET6;
11725e3a7ecSginsbach 			break;
1186ff461a4Scgd 		case 'K':
11961f28255Scgd 			break;
1206ff461a4Scgd 		case 'p':
1216ff461a4Scgd 			pflag = 1;
1226ff461a4Scgd 			break;
1236ff461a4Scgd 		case 'r':
1246ff461a4Scgd 			iamrecursive = 1;
1256ff461a4Scgd 			break;
1266ff461a4Scgd 						/* Server options. */
12761f28255Scgd 		case 'd':
12861f28255Scgd 			targetshouldbedirectory = 1;
12961f28255Scgd 			break;
13061f28255Scgd 		case 'f':			/* "from" */
13161f28255Scgd 			iamremote = 1;
13261f28255Scgd 			fflag = 1;
13361f28255Scgd 			break;
13461f28255Scgd 		case 't':			/* "to" */
13561f28255Scgd 			iamremote = 1;
13661f28255Scgd 			tflag = 1;
13761f28255Scgd 			break;
13861f28255Scgd 		case '?':
13961f28255Scgd 		default:
14061f28255Scgd 			usage();
14161f28255Scgd 		}
14261f28255Scgd 	argc -= optind;
14361f28255Scgd 	argv += optind;
14461f28255Scgd 
14561f28255Scgd 	sp = getservbyname(shell = "shell", "tcp");
1466ff461a4Scgd 	if (sp == NULL)
1476ff461a4Scgd 		errx(1, "%s/tcp: unknown service", shell);
14861f28255Scgd 	port = sp->s_port;
14961f28255Scgd 
1506ff461a4Scgd 	if ((pwd = getpwuid(userid = getuid())) == NULL)
1516ff461a4Scgd 		errx(1, "unknown user %d", (int)userid);
15261f28255Scgd 
153b021feb7Schristos 	if ((pwname = strdup(pwd->pw_name)) == NULL)
15485cbf55dSdrochner 		err(1, NULL);
155b021feb7Schristos 
1566ff461a4Scgd 	rem = STDIN_FILENO;		/* XXX */
1576ff461a4Scgd 
1586ff461a4Scgd 	if (fflag) {			/* Follow "protocol", send data. */
15961f28255Scgd 		(void)response();
16061f28255Scgd 		source(argc, argv);
16161f28255Scgd 		exit(errs);
16261f28255Scgd 	}
16361f28255Scgd 
1646ff461a4Scgd 	if (tflag) {			/* Receive data. */
16561f28255Scgd 		sink(argc, argv);
16661f28255Scgd 		exit(errs);
16761f28255Scgd 	}
16861f28255Scgd 
16961f28255Scgd 	if (argc < 2)
17061f28255Scgd 		usage();
17161f28255Scgd 	if (argc > 2)
17261f28255Scgd 		targetshouldbedirectory = 1;
17361f28255Scgd 
17461f28255Scgd 	rem = -1;
1756ff461a4Scgd 	/* Command to be executed on remote system using "rsh". */
17661f28255Scgd 	(void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s",
17761f28255Scgd 	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
17861f28255Scgd 	    targetshouldbedirectory ? " -d" : "");
17961f28255Scgd 
18061f28255Scgd 	(void)signal(SIGPIPE, lostconn);
1811ca2da5bSrin 	(void)signal(SIGINFO, got_siginfo);
18261f28255Scgd 
183e800d25eSchristos 	if ((targ = colon(argv[argc - 1])) != NULL)/* Dest is remote host. */
1846ff461a4Scgd 		toremote(targ, argc, argv);
18561f28255Scgd 	else {
1866ff461a4Scgd 		tolocal(argc, argv);		/* Dest is local host. */
18761f28255Scgd 		if (targetshouldbedirectory)
18861f28255Scgd 			verifydir(argv[argc - 1]);
18961f28255Scgd 	}
19061f28255Scgd 	exit(errs);
1919dc385beSmycroft 	/* NOTREACHED */
19261f28255Scgd }
19361f28255Scgd 
1945dad1439Scgd void
toremote(char * targ,int argc,char * argv[])1950b39b8daSxtraeme toremote(char *targ, int argc, char *argv[])
19661f28255Scgd {
1976f4d1070Schristos 	int i;
1986f4d1070Schristos 	size_t len;
199b021feb7Schristos 	char *bp, *host, *src, *suser, *thost, *tuser;
20061f28255Scgd 
20161f28255Scgd 	*targ++ = 0;
20261f28255Scgd 	if (*targ == 0)
2036310b596Schristos 		targ = dot;
20461f28255Scgd 
205e800d25eSchristos 	if ((thost = strchr(argv[argc - 1], '@')) != NULL) {
20661f28255Scgd 		/* user@host */
20761f28255Scgd 		*thost++ = 0;
20861f28255Scgd 		tuser = argv[argc - 1];
20961f28255Scgd 		if (*tuser == '\0')
21061f28255Scgd 			tuser = NULL;
211ee9e50eaSmycroft 		else if (!okname(tuser))
21261f28255Scgd 			exit(1);
21361f28255Scgd 	} else {
21461f28255Scgd 		thost = argv[argc - 1];
21561f28255Scgd 		tuser = NULL;
21661f28255Scgd 	}
2170d1bfba8Sginsbach 	thost = unbracket(thost);
21861f28255Scgd 
21961f28255Scgd 	for (i = 0; i < argc - 1; i++) {
22061f28255Scgd 		src = colon(argv[i]);
22161f28255Scgd 		if (src) {			/* remote to remote */
22261f28255Scgd 			*src++ = 0;
22361f28255Scgd 			if (*src == 0)
2246310b596Schristos 				src = dot;
2256ff461a4Scgd 			host = strchr(argv[i], '@');
22661f28255Scgd 			len = strlen(_PATH_RSH) + strlen(argv[i]) +
22761f28255Scgd 			    strlen(src) + (tuser ? strlen(tuser) : 0) +
22861f28255Scgd 			    strlen(thost) + strlen(targ) + CMDNEEDS + 20;
22961f28255Scgd 			if (!(bp = malloc(len)))
23085cbf55dSdrochner 				err(1, NULL);
23161f28255Scgd 			if (host) {
23261f28255Scgd 				*host++ = 0;
2330d1bfba8Sginsbach 				host = unbracket(host);
23461f28255Scgd 				suser = argv[i];
23561f28255Scgd 				if (*suser == '\0')
236b021feb7Schristos 					suser = pwname;
237f127f217Sginsbach 				else if (!okname(suser)) {
238f127f217Sginsbach 					(void)free(bp);
23961f28255Scgd 					continue;
240f127f217Sginsbach 				}
24161f28255Scgd 				(void)snprintf(bp, len,
24261f28255Scgd 				    "%s %s -l %s -n %s %s '%s%s%s:%s'",
24361f28255Scgd 				    _PATH_RSH, host, suser, cmd, src,
24461f28255Scgd 				    tuser ? tuser : "", tuser ? "@" : "",
24561f28255Scgd 				    thost, targ);
2460d1bfba8Sginsbach 			} else {
2470d1bfba8Sginsbach 				host = unbracket(argv[i]);
24861f28255Scgd 				(void)snprintf(bp, len,
2496ff461a4Scgd 				    "exec %s %s -n %s %s '%s%s%s:%s'",
25061f28255Scgd 				    _PATH_RSH, argv[i], cmd, src,
25161f28255Scgd 				    tuser ? tuser : "", tuser ? "@" : "",
25261f28255Scgd 				    thost, targ);
2530d1bfba8Sginsbach 			}
254bf5e0e48Smrg 			(void)susystem(bp);
25561f28255Scgd 			(void)free(bp);
25661f28255Scgd 		} else {			/* local to remote */
25761f28255Scgd 			if (rem == -1) {
25861f28255Scgd 				len = strlen(targ) + CMDNEEDS + 20;
25961f28255Scgd 				if (!(bp = malloc(len)))
26085cbf55dSdrochner 					err(1, NULL);
26161f28255Scgd 				(void)snprintf(bp, len, "%s -t %s", cmd, targ);
26261f28255Scgd 				host = thost;
263cf0f7c26Sjdolecek 					rem = rcmd_af(&host, port, pwname,
264b021feb7Schristos 					    tuser ? tuser : pwname,
26525e3a7ecSginsbach 					    bp, NULL, family);
266ee9e50eaSmycroft 				if (rem < 0)
26761f28255Scgd 					exit(1);
268ee9e50eaSmycroft 				if (response() < 0)
26961f28255Scgd 					exit(1);
27061f28255Scgd 				(void)free(bp);
27161f28255Scgd 			}
27261f28255Scgd 			source(1, argv+i);
27361f28255Scgd 		}
27461f28255Scgd 	}
27561f28255Scgd }
27661f28255Scgd 
2775dad1439Scgd void
tolocal(int argc,char * argv[])2780b39b8daSxtraeme tolocal(int argc, char *argv[])
27961f28255Scgd {
2806f4d1070Schristos 	int i;
2816f4d1070Schristos 	size_t len;
28261f28255Scgd 	char *bp, *host, *src, *suser;
28361f28255Scgd 
28461f28255Scgd 	for (i = 0; i < argc - 1; i++) {
2856ff461a4Scgd 		if (!(src = colon(argv[i]))) {		/* Local to local. */
28661f28255Scgd 			len = strlen(_PATH_CP) + strlen(argv[i]) +
28761f28255Scgd 			    strlen(argv[argc - 1]) + 20;
28861f28255Scgd 			if (!(bp = malloc(len)))
28985cbf55dSdrochner 				err(1, NULL);
2906ff461a4Scgd 			(void)snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP,
29161f28255Scgd 			    iamrecursive ? " -r" : "", pflag ? " -p" : "",
29261f28255Scgd 			    argv[i], argv[argc - 1]);
293bf5e0e48Smrg 			if (susystem(bp))
2946ff461a4Scgd 				++errs;
29561f28255Scgd 			(void)free(bp);
29661f28255Scgd 			continue;
29761f28255Scgd 		}
29861f28255Scgd 		*src++ = 0;
29961f28255Scgd 		if (*src == 0)
3006310b596Schristos 			src = dot;
3016ff461a4Scgd 		if ((host = strchr(argv[i], '@')) == NULL) {
3026ff461a4Scgd 			host = argv[i];
303b021feb7Schristos 			suser = pwname;
3046ff461a4Scgd 		} else {
30561f28255Scgd 			*host++ = 0;
30661f28255Scgd 			suser = argv[i];
30761f28255Scgd 			if (*suser == '\0')
308b021feb7Schristos 				suser = pwname;
30961f28255Scgd 			else if (!okname(suser))
31061f28255Scgd 				continue;
31161f28255Scgd 		}
3120d1bfba8Sginsbach 		host = unbracket(host);
31361f28255Scgd 		len = strlen(src) + CMDNEEDS + 20;
3146ff461a4Scgd 		if ((bp = malloc(len)) == NULL)
31585cbf55dSdrochner 			err(1, NULL);
31661f28255Scgd 		(void)snprintf(bp, len, "%s -f %s", cmd, src);
3176ff461a4Scgd 		rem =
31825e3a7ecSginsbach 			rcmd_af(&host, port, pwname, suser, bp, NULL, family);
31961f28255Scgd 		(void)free(bp);
3206ff461a4Scgd 		if (rem < 0) {
3216ff461a4Scgd 			++errs;
32261f28255Scgd 			continue;
3236ff461a4Scgd 		}
32461f28255Scgd 		sink(1, argv + argc - 1);
32561f28255Scgd 		(void)close(rem);
32661f28255Scgd 		rem = -1;
32761f28255Scgd 	}
32861f28255Scgd }
32961f28255Scgd 
3305dad1439Scgd void
source(int argc,char * argv[])3310b39b8daSxtraeme source(int argc, char *argv[])
33261f28255Scgd {
33361f28255Scgd 	struct stat stb;
33461f28255Scgd 	static BUF buffer;
33561f28255Scgd 	BUF *bp;
33661f28255Scgd 	off_t i;
3376f4d1070Schristos 	off_t amt;
3382316fa6fSrin 	size_t resid;
3392316fa6fSrin 	ssize_t result;
3402316fa6fSrin 	int fd, haderr, indx;
3412316fa6fSrin 	char *last, *name, *cp, buf[BUFSIZ];
34261f28255Scgd 
3436ff461a4Scgd 	for (indx = 0; indx < argc; ++indx) {
3446ff461a4Scgd 		name = argv[indx];
3456ff461a4Scgd 		if ((fd = open(name, O_RDONLY, 0)) < 0)
3466ff461a4Scgd 			goto syserr;
3476ff461a4Scgd 		if (fstat(fd, &stb)) {
3486ff461a4Scgd syserr:			run_err("%s: %s", name, strerror(errno));
3496ff461a4Scgd 			goto next;
35061f28255Scgd 		}
35161f28255Scgd 		switch (stb.st_mode & S_IFMT) {
35261f28255Scgd 		case S_IFREG:
35361f28255Scgd 			break;
35461f28255Scgd 		case S_IFDIR:
35561f28255Scgd 			if (iamrecursive) {
35661f28255Scgd 				rsource(name, &stb);
3576ff461a4Scgd 				goto next;
35861f28255Scgd 			}
35961f28255Scgd 			/* FALLTHROUGH */
36061f28255Scgd 		default:
3616ff461a4Scgd 			run_err("%s: not a regular file", name);
3626ff461a4Scgd 			goto next;
36361f28255Scgd 		}
3646ff461a4Scgd 		if ((last = strrchr(name, '/')) == NULL)
36561f28255Scgd 			last = name;
36661f28255Scgd 		else
3676ff461a4Scgd 			++last;
36861f28255Scgd 		if (pflag) {
36961f28255Scgd 			/*
37061f28255Scgd 			 * Make it compatible with possible future
37161f28255Scgd 			 * versions expecting microseconds.
37261f28255Scgd 			 */
373fe5990acSdholland 			(void)snprintf(buf, sizeof(buf), "T%lld %ld %lld %ld\n",
374fe5990acSdholland 			    (long long)stb.st_mtimespec.tv_sec,
3752d6dc609Smycroft 			    (long)stb.st_mtimespec.tv_nsec / 1000,
376fe5990acSdholland 			    (long long)stb.st_atimespec.tv_sec,
3772d6dc609Smycroft 			    (long)stb.st_atimespec.tv_nsec / 1000);
3786ff461a4Scgd 			(void)write(rem, buf, strlen(buf));
3796ff461a4Scgd 			if (response() < 0)
3806ff461a4Scgd 				goto next;
3816ff461a4Scgd 		}
382040da092Schristos #define	RCPMODEMASK	(S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
383f819878cSlukem 		(void)snprintf(buf, sizeof(buf), "C%04o %lld %s\n",
384040da092Schristos 		    stb.st_mode & RCPMODEMASK, (long long)stb.st_size, last);
3856ff461a4Scgd 		(void)write(rem, buf, strlen(buf));
3866ff461a4Scgd 		if (response() < 0)
3876ff461a4Scgd 			goto next;
3886ff461a4Scgd 		if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) {
3896ff461a4Scgd next:			(void)close(fd);
39061f28255Scgd 			continue;
39161f28255Scgd 		}
3926ff461a4Scgd 
3936ff461a4Scgd 		/* Keep writing after an error so that we stay sync'd up. */
3946f4d1070Schristos 		haderr = 0;
3956f4d1070Schristos 		for (i = 0; i < stb.st_size; i += bp->cnt) {
3961ca2da5bSrin 			if (print_info)
3971ca2da5bSrin 				progress(name, i, stb.st_size);
39861f28255Scgd 			amt = bp->cnt;
39961f28255Scgd 			if (i + amt > stb.st_size)
40061f28255Scgd 				amt = stb.st_size - i;
4012316fa6fSrin 			for (resid = (size_t)amt, cp = bp->buf; resid > 0;
4022316fa6fSrin 			    resid -= result, cp += result) {
4032316fa6fSrin 				result = read(fd, cp, resid);
4042316fa6fSrin 				if (result == -1) {
4052316fa6fSrin 					haderr = errno;
4062316fa6fSrin 					goto error;
4076ff461a4Scgd 				}
4086ff461a4Scgd 			}
4092316fa6fSrin 			for (resid = (size_t)amt, cp = bp->buf; resid > 0;
4102316fa6fSrin 			    resid -= result, cp += result) {
4112316fa6fSrin 				result = write(rem, cp, resid);
4122316fa6fSrin 				if (result == -1) {
4132316fa6fSrin 					haderr = errno;
4142316fa6fSrin 					goto error;
4152316fa6fSrin 				}
4162316fa6fSrin 			}
4172316fa6fSrin 		}
4182316fa6fSrin  error:
4196ff461a4Scgd 		if (close(fd) && !haderr)
4206ff461a4Scgd 			haderr = errno;
4216ff461a4Scgd 		if (!haderr)
42261f28255Scgd 			(void)write(rem, "", 1);
42361f28255Scgd 		else
4246ff461a4Scgd 			run_err("%s: %s", name, strerror(haderr));
42561f28255Scgd 		(void)response();
42661f28255Scgd 	}
42761f28255Scgd }
42861f28255Scgd 
4295dad1439Scgd void
rsource(char * name,struct stat * statp)4300b39b8daSxtraeme rsource(char *name, struct stat *statp)
43161f28255Scgd {
43261f28255Scgd 	DIR *dirp;
43361f28255Scgd 	struct dirent *dp;
43461f28255Scgd 	char *last, *vect[1], path[MAXPATHLEN];
43561f28255Scgd 
43661f28255Scgd 	if (!(dirp = opendir(name))) {
4376ff461a4Scgd 		run_err("%s: %s", name, strerror(errno));
43861f28255Scgd 		return;
43961f28255Scgd 	}
4406ff461a4Scgd 	last = strrchr(name, '/');
44161f28255Scgd 	if (last == 0)
44261f28255Scgd 		last = name;
44361f28255Scgd 	else
44461f28255Scgd 		last++;
44561f28255Scgd 	if (pflag) {
446fe5990acSdholland 		(void)snprintf(path, sizeof(path), "T%lld %ld %lld %ld\n",
447fe5990acSdholland 		    (long long)statp->st_mtimespec.tv_sec,
4482d6dc609Smycroft 		    (long)statp->st_mtimespec.tv_nsec / 1000,
449fe5990acSdholland 		    (long long)statp->st_atimespec.tv_sec,
4502d6dc609Smycroft 		    (long)statp->st_atimespec.tv_nsec / 1000);
4516ff461a4Scgd 		(void)write(rem, path, strlen(path));
45261f28255Scgd 		if (response() < 0) {
4536f4d1070Schristos 			(void)closedir(dirp);
45461f28255Scgd 			return;
45561f28255Scgd 		}
45661f28255Scgd 	}
45761f28255Scgd 	(void)snprintf(path, sizeof(path),
45809fb774dSchristos 	    "D%04o %d %s\n", statp->st_mode & RCPMODEMASK, 0, last);
4596ff461a4Scgd 	(void)write(rem, path, strlen(path));
46061f28255Scgd 	if (response() < 0) {
4616f4d1070Schristos 		(void)closedir(dirp);
46261f28255Scgd 		return;
46361f28255Scgd 	}
464e800d25eSchristos 	while ((dp = readdir(dirp)) != NULL) {
46561f28255Scgd 		if (dp->d_ino == 0)
46661f28255Scgd 			continue;
4676310b596Schristos 		if (!strcmp(dp->d_name, dot) || !strcmp(dp->d_name, ".."))
46861f28255Scgd 			continue;
469*f386908bSmrg 		if (snprintf(path, sizeof(path), "%s/%s", name, dp->d_name) >=
470*f386908bSmrg 		    sizeof(path)) {
4716ff461a4Scgd 			run_err("%s/%s: name too long", name, dp->d_name);
47261f28255Scgd 			continue;
47361f28255Scgd 		}
47461f28255Scgd 		vect[0] = path;
47561f28255Scgd 		source(1, vect);
47661f28255Scgd 	}
4776ff461a4Scgd 	(void)closedir(dirp);
47861f28255Scgd 	(void)write(rem, "E\n", 2);
47961f28255Scgd 	(void)response();
48061f28255Scgd }
48161f28255Scgd 
4825dad1439Scgd void
sink(int argc,char * argv[])4830b39b8daSxtraeme sink(int argc, char *argv[])
48461f28255Scgd {
48561f28255Scgd 	static BUF buffer;
48661f28255Scgd 	struct stat stb;
48761f28255Scgd 	struct timeval tv[2];
4886ff461a4Scgd 	BUF *bp;
4892316fa6fSrin 	size_t resid;
4902316fa6fSrin 	ssize_t result;
4916f4d1070Schristos 	off_t i;
4926f4d1070Schristos 	off_t amt;
4936f4d1070Schristos 	int exists, first, ofd;
4946f4d1070Schristos 	mode_t mask;
4956f4d1070Schristos 	mode_t mode;
4966f4d1070Schristos 	mode_t omode;
497c79f400cSaymeric 	int setimes, targisdir, wrerr;
498e800d25eSchristos 	int wrerrno = 0;	/* pacify gcc */
499c79f400cSaymeric 	const char *wrcontext = NULL;
5006310b596Schristos 	char ch, *cp, *np, *targ, *vect[1], buf[BUFSIZ];
5016310b596Schristos 	const char *why;
5020ccd7cbdSragge 	off_t size;
503e9b012a3Schs 	char *namebuf = NULL;
504e9b012a3Schs 	size_t cursize = 0;
50561f28255Scgd 
50661f28255Scgd #define	atime	tv[0]
50761f28255Scgd #define	mtime	tv[1]
50861f28255Scgd #define	SCREWUP(str)	{ why = str; goto screwup; }
50961f28255Scgd 
51061f28255Scgd 	setimes = targisdir = 0;
51161f28255Scgd 	mask = umask(0);
51261f28255Scgd 	if (!pflag)
51361f28255Scgd 		(void)umask(mask);
51461f28255Scgd 	if (argc != 1) {
5156ff461a4Scgd 		run_err("ambiguous target");
51661f28255Scgd 		exit(1);
51761f28255Scgd 	}
51861f28255Scgd 	targ = *argv;
51961f28255Scgd 	if (targetshouldbedirectory)
52061f28255Scgd 		verifydir(targ);
52161f28255Scgd 	(void)write(rem, "", 1);
522705a6ebaSmycroft 	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
52361f28255Scgd 		targisdir = 1;
52461f28255Scgd 	for (first = 1;; first = 0) {
52561f28255Scgd 		cp = buf;
52661f28255Scgd 		if (read(rem, cp, 1) <= 0)
527e9b012a3Schs 			goto out;
52861f28255Scgd 		if (*cp++ == '\n')
52961f28255Scgd 			SCREWUP("unexpected <newline>");
53061f28255Scgd 		do {
53161f28255Scgd 			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
53261f28255Scgd 				SCREWUP("lost connection");
53361f28255Scgd 			*cp++ = ch;
53461f28255Scgd 		} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
53561f28255Scgd 		*cp = 0;
53661f28255Scgd 
53761f28255Scgd 		if (buf[0] == '\01' || buf[0] == '\02') {
53861f28255Scgd 			if (iamremote == 0)
5396ff461a4Scgd 				(void)write(STDERR_FILENO,
5406ff461a4Scgd 				    buf + 1, strlen(buf + 1));
541ee9e50eaSmycroft 			if (buf[0] == '\02')
54261f28255Scgd 				exit(1);
5436ff461a4Scgd 			++errs;
54461f28255Scgd 			continue;
54561f28255Scgd 		}
54661f28255Scgd 		if (buf[0] == 'E') {
54761f28255Scgd 			(void)write(rem, "", 1);
548e9b012a3Schs 			goto out;
54961f28255Scgd 		}
55061f28255Scgd 
55161f28255Scgd 		if (ch == '\n')
55261f28255Scgd 			*--cp = 0;
55361f28255Scgd 
554040da092Schristos #define getnum(t) (t) = 0; while (isdigit((unsigned char)*cp)) (t) = (t) * 10 + (*cp++ - '0');
55561f28255Scgd 		cp = buf;
55661f28255Scgd 		if (*cp == 'T') {
55761f28255Scgd 			setimes++;
55861f28255Scgd 			cp++;
55961f28255Scgd 			getnum(mtime.tv_sec);
56061f28255Scgd 			if (*cp++ != ' ')
56161f28255Scgd 				SCREWUP("mtime.sec not delimited");
56261f28255Scgd 			getnum(mtime.tv_usec);
56361f28255Scgd 			if (*cp++ != ' ')
56461f28255Scgd 				SCREWUP("mtime.usec not delimited");
56561f28255Scgd 			getnum(atime.tv_sec);
56661f28255Scgd 			if (*cp++ != ' ')
56761f28255Scgd 				SCREWUP("atime.sec not delimited");
56861f28255Scgd 			getnum(atime.tv_usec);
56961f28255Scgd 			if (*cp++ != '\0')
57061f28255Scgd 				SCREWUP("atime.usec not delimited");
57161f28255Scgd 			(void)write(rem, "", 1);
57261f28255Scgd 			continue;
57361f28255Scgd 		}
57461f28255Scgd 		if (*cp != 'C' && *cp != 'D') {
57561f28255Scgd 			/*
57661f28255Scgd 			 * Check for the case "rcp remote:foo\* local:bar".
57761f28255Scgd 			 * In this case, the line "No match." can be returned
57861f28255Scgd 			 * by the shell before the rcp command on the remote is
57961f28255Scgd 			 * executed so the ^Aerror_message convention isn't
58061f28255Scgd 			 * followed.
58161f28255Scgd 			 */
58261f28255Scgd 			if (first) {
5836ff461a4Scgd 				run_err("%s", cp);
58461f28255Scgd 				exit(1);
58561f28255Scgd 			}
58661f28255Scgd 			SCREWUP("expected control record");
58761f28255Scgd 		}
58861f28255Scgd 		mode = 0;
58961f28255Scgd 		for (++cp; cp < buf + 5; cp++) {
59061f28255Scgd 			if (*cp < '0' || *cp > '7')
59161f28255Scgd 				SCREWUP("bad mode");
59261f28255Scgd 			mode = (mode << 3) | (*cp - '0');
59361f28255Scgd 		}
59461f28255Scgd 		if (*cp++ != ' ')
59561f28255Scgd 			SCREWUP("mode not delimited");
5966ff461a4Scgd 
597040da092Schristos 		for (size = 0; isdigit((unsigned char)*cp);)
59861f28255Scgd 			size = size * 10 + (*cp++ - '0');
59961f28255Scgd 		if (*cp++ != ' ')
60061f28255Scgd 			SCREWUP("size not delimited");
60161f28255Scgd 		if (targisdir) {
60283b3dbf6Sdholland 			char *newnamebuf;
60361f28255Scgd 			size_t need;
60461f28255Scgd 
605e9b012a3Schs 			need = strlen(targ) + strlen(cp) + 2;
60661f28255Scgd 			if (need > cursize) {
607e9b012a3Schs 				need += 256;
60883b3dbf6Sdholland 				newnamebuf = realloc(namebuf, need);
60983b3dbf6Sdholland 				if (newnamebuf != NULL) {
61083b3dbf6Sdholland 					namebuf = newnamebuf;
61183b3dbf6Sdholland 					cursize = need;
61283b3dbf6Sdholland 				} else {
6136ff461a4Scgd 					run_err("%s", strerror(errno));
614e9b012a3Schs 					exit(1);
61561f28255Scgd 				}
61683b3dbf6Sdholland 			}
61783b3dbf6Sdholland 			(void)snprintf(namebuf, cursize, "%s%s%s", targ,
61861f28255Scgd 			    *targ ? "/" : "", cp);
61961f28255Scgd 			np = namebuf;
6206ff461a4Scgd 		} else
62161f28255Scgd 			np = targ;
62261f28255Scgd 		exists = stat(np, &stb) == 0;
62361f28255Scgd 		if (buf[0] == 'D') {
6246ff461a4Scgd 			int mod_flag = pflag;
62561f28255Scgd 			if (exists) {
626705a6ebaSmycroft 				if (!S_ISDIR(stb.st_mode)) {
62761f28255Scgd 					errno = ENOTDIR;
62861f28255Scgd 					goto bad;
62961f28255Scgd 				}
63061f28255Scgd 				if (pflag)
63161f28255Scgd 					(void)chmod(np, mode);
6326ff461a4Scgd 			} else {
6336ff461a4Scgd 				/* Handle copying from a read-only directory */
6346ff461a4Scgd 				mod_flag = 1;
6356ff461a4Scgd 				if (mkdir(np, mode | S_IRWXU) < 0)
63661f28255Scgd 					goto bad;
6376ff461a4Scgd 			}
63861f28255Scgd 			vect[0] = np;
63961f28255Scgd 			sink(1, vect);
64061f28255Scgd 			if (setimes) {
64161f28255Scgd 				setimes = 0;
642c79f400cSaymeric 				(void) utimes(np, tv);
64361f28255Scgd 			}
6446ff461a4Scgd 			if (mod_flag)
6456ff461a4Scgd 				(void)chmod(np, mode);
64661f28255Scgd 			continue;
64761f28255Scgd 		}
6486ff461a4Scgd 		omode = mode;
6496ff461a4Scgd 		mode |= S_IWRITE;
65061f28255Scgd 		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
6516ff461a4Scgd bad:			run_err("%s: %s", np, strerror(errno));
65261f28255Scgd 			continue;
65361f28255Scgd 		}
65461f28255Scgd 		(void)write(rem, "", 1);
6556ff461a4Scgd 		if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) {
65661f28255Scgd 			(void)close(ofd);
65761f28255Scgd 			continue;
65861f28255Scgd 		}
659c79f400cSaymeric 		wrerr = 0;
660c79f400cSaymeric 
661c79f400cSaymeric /*
662c79f400cSaymeric  * Like run_err(), but don't send any message to the remote end.
663c79f400cSaymeric  * Instead, record the first error and send that in the end.
664c79f400cSaymeric  */
665c79f400cSaymeric #define RUN_ERR(w_context) do { \
666c79f400cSaymeric 	if (!wrerr) {							\
667c79f400cSaymeric 		wrerrno = errno;					\
668c79f400cSaymeric 		wrcontext = w_context;					\
669c79f400cSaymeric 		wrerr = 1;						\
670c79f400cSaymeric 	}								\
671c79f400cSaymeric } while(0)
672c79f400cSaymeric 
6732316fa6fSrin 		for (i = 0; i < size; i += bp->cnt) {
6741ca2da5bSrin 			if (print_info)
6751ca2da5bSrin 				progress(np, i, size);
6762316fa6fSrin 			amt = bp->cnt;
67761f28255Scgd 			if (i + amt > size)
67861f28255Scgd 				amt = size - i;
6792316fa6fSrin 			for (resid = (size_t)amt, cp = bp->buf; resid > 0;
6802316fa6fSrin 			    resid -= result, cp += result) {
6812316fa6fSrin 				result = read(rem, cp, resid);
6822316fa6fSrin 				if (result == -1) {
6832316fa6fSrin 					run_err("%s", strerror(errno));
68461f28255Scgd 					exit(1);
68561f28255Scgd 				}
6862316fa6fSrin 			}
6872316fa6fSrin 			for (resid = (size_t)amt, cp = bp->buf; resid > 0;
6882316fa6fSrin 			    resid -= result, cp += result) {
6896ff461a4Scgd 				/* Keep reading so we stay sync'd up. */
690c79f400cSaymeric 				if (!wrerr) {
6912316fa6fSrin 					result = write(ofd, cp, resid);
6922316fa6fSrin 					if (result == -1) {
693c79f400cSaymeric 						RUN_ERR("write");
6942316fa6fSrin 						goto error;
6956ff461a4Scgd 					}
6966ff461a4Scgd 				}
69761f28255Scgd 			}
69861f28255Scgd 		}
6992316fa6fSrin  error:
700c79f400cSaymeric 		if (ftruncate(ofd, size))
701c79f400cSaymeric 			RUN_ERR("truncate");
702c79f400cSaymeric 
7036ff461a4Scgd 		if (pflag) {
7046ff461a4Scgd 			if (exists || omode != mode)
7056ff461a4Scgd 				if (fchmod(ofd, omode))
706c79f400cSaymeric 					RUN_ERR("set mode");
7076ff461a4Scgd 		} else {
7086ff461a4Scgd 			if (!exists && omode != mode)
7096ff461a4Scgd 				if (fchmod(ofd, omode & ~mask))
710c79f400cSaymeric 					RUN_ERR("set mode");
7116ff461a4Scgd 		}
712040da092Schristos #ifndef __SVR4
713c79f400cSaymeric 		if (setimes && !wrerr) {
71461f28255Scgd 			setimes = 0;
715c79f400cSaymeric 			if (futimes(ofd, tv) < 0)
716c79f400cSaymeric 				RUN_ERR("set times");
71761f28255Scgd 		}
718040da092Schristos #endif
7192d6dc609Smycroft 		(void)close(ofd);
720040da092Schristos #ifdef __SVR4
721c79f400cSaymeric 		if (setimes && !wrerr) {
722040da092Schristos 			setimes = 0;
723c79f400cSaymeric 			if (utimes(np, tv) < 0)
724c79f400cSaymeric 				RUN_ERR("set times");
725040da092Schristos 		}
726040da092Schristos #endif
7272d6dc609Smycroft 		(void)response();
728c79f400cSaymeric 		if (wrerr)
729c79f400cSaymeric 			run_err("%s: %s: %s", np, wrcontext, strerror(wrerrno));
730c79f400cSaymeric 		else
73161f28255Scgd 			(void)write(rem, "", 1);
73261f28255Scgd 	}
733e9b012a3Schs 
734e9b012a3Schs out:
735e9b012a3Schs 	if (namebuf) {
736e9b012a3Schs 		free(namebuf);
737e9b012a3Schs 	}
738e9b012a3Schs 	return;
739e9b012a3Schs 
74061f28255Scgd screwup:
7416ff461a4Scgd 	run_err("protocol error: %s", why);
74261f28255Scgd 	exit(1);
7439dc385beSmycroft 	/* NOTREACHED */
74461f28255Scgd }
74561f28255Scgd 
7466ff461a4Scgd 
7476ff461a4Scgd int
response(void)7480b39b8daSxtraeme response(void)
7496ff461a4Scgd {
7506ff461a4Scgd 	char ch, *cp, resp, rbuf[BUFSIZ];
7516ff461a4Scgd 
7526ff461a4Scgd 	if (read(rem, &resp, sizeof(resp)) != sizeof(resp))
7536ff461a4Scgd 		lostconn(0);
7546ff461a4Scgd 
7556ff461a4Scgd 	cp = rbuf;
7566ff461a4Scgd 	switch(resp) {
7576ff461a4Scgd 	case 0:				/* ok */
7586ff461a4Scgd 		return (0);
7596ff461a4Scgd 	default:
7606ff461a4Scgd 		*cp++ = resp;
7616ff461a4Scgd 		/* FALLTHROUGH */
7626ff461a4Scgd 	case 1:				/* error, followed by error msg */
7636ff461a4Scgd 	case 2:				/* fatal error, "" */
7646ff461a4Scgd 		do {
7656ff461a4Scgd 			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
7666ff461a4Scgd 				lostconn(0);
7676ff461a4Scgd 			*cp++ = ch;
7686ff461a4Scgd 		} while (cp < &rbuf[BUFSIZ] && ch != '\n');
7696ff461a4Scgd 
7706ff461a4Scgd 		if (!iamremote)
7716f4d1070Schristos 			(void)write(STDERR_FILENO, rbuf, (size_t)(cp - rbuf));
7726ff461a4Scgd 		++errs;
7736ff461a4Scgd 		if (resp == 1)
7746ff461a4Scgd 			return (-1);
7756ff461a4Scgd 		exit(1);
7766ff461a4Scgd 	}
7776ff461a4Scgd 	/* NOTREACHED */
7786ff461a4Scgd }
7796ff461a4Scgd 
7806ff461a4Scgd void
usage(void)7810b39b8daSxtraeme usage(void)
7826ff461a4Scgd {
7836ff461a4Scgd 	(void)fprintf(stderr,
784165812e4Swiz 	    "usage: rcp [-46p] f1 f2; or: rcp [-46pr] f1 ... fn directory\n");
7856ff461a4Scgd 	exit(1);
7869dc385beSmycroft 	/* NOTREACHED */
7876ff461a4Scgd }
7886ff461a4Scgd 
7896ff461a4Scgd #include <stdarg.h>
7906ff461a4Scgd 
7916ff461a4Scgd 
7926ff461a4Scgd void
run_err(const char * fmt,...)7936ff461a4Scgd run_err(const char *fmt, ...)
7946ff461a4Scgd {
7956ff461a4Scgd 	static FILE *fp;
7966ff461a4Scgd 	va_list ap;
7974c999163Swiz 
7984c999163Swiz 	++errs;
7994c999163Swiz 	if (fp == NULL && !(fp = fdopen(rem, "w")))
8004c999163Swiz 		return;
8014c999163Swiz 
8026ff461a4Scgd 	va_start(ap, fmt);
8036ff461a4Scgd 
8046ff461a4Scgd 	(void)fprintf(fp, "%c", 0x01);
8056ff461a4Scgd 	(void)fprintf(fp, "rcp: ");
8066ff461a4Scgd 	(void)vfprintf(fp, fmt, ap);
8076ff461a4Scgd 	(void)fprintf(fp, "\n");
8086ff461a4Scgd 	(void)fflush(fp);
8096ff461a4Scgd 	va_end(ap);
8104c999163Swiz 
8114c999163Swiz 	if (!iamremote) {
8124c999163Swiz 		va_start(ap, fmt);
8134c999163Swiz 		vwarnx(fmt, ap);
8144c999163Swiz 		va_end(ap);
8154c999163Swiz 	}
8166ff461a4Scgd }
8171ca2da5bSrin 
8181ca2da5bSrin static void
got_siginfo(int signo)8191ca2da5bSrin got_siginfo(int signo)
8201ca2da5bSrin {
8211ca2da5bSrin 
8221ca2da5bSrin 	print_info = 1;
8231ca2da5bSrin }
8241ca2da5bSrin 
8251ca2da5bSrin static void
progress(const char * file,uintmax_t done,uintmax_t total)8261ca2da5bSrin progress(const char *file, uintmax_t done, uintmax_t total)
8271ca2da5bSrin {
8281ca2da5bSrin 	static int ttyfd = -2;
8291ca2da5bSrin 	const double pcent = (100.0 * done) / total;
8301ca2da5bSrin 	char buf[2048];
8311ca2da5bSrin 	int n;
8321ca2da5bSrin 
8331ca2da5bSrin 	if (ttyfd == -2)
8341ca2da5bSrin 		ttyfd = open(_PATH_TTY, O_RDWR | O_CLOEXEC);
8351ca2da5bSrin 
8361ca2da5bSrin 	if (ttyfd == -1)
8371ca2da5bSrin 		return;
8381ca2da5bSrin 
8391ca2da5bSrin 	n = snprintf(buf, sizeof(buf),
8401ca2da5bSrin 	    "%s: %s: %ju/%ju bytes %3.2f%% written\n",
8411ca2da5bSrin 	    getprogname(), file, done, total, pcent);
8421ca2da5bSrin 
8431ca2da5bSrin 	if (n < 0)
8441ca2da5bSrin 		return;
8451ca2da5bSrin 
8461ca2da5bSrin 	write(ttyfd, buf, (size_t)n);
8471ca2da5bSrin 
8481ca2da5bSrin 	print_info = 0;
8491ca2da5bSrin }
850