xref: /csrg-svn/bin/rcp/rcp.c (revision 38098)
121569Sdist /*
235619Sbostic  * Copyright (c) 1983 The Regents of the University of California.
335619Sbostic  * All rights reserved.
435619Sbostic  *
535619Sbostic  * Redistribution and use in source and binary forms are permitted
635619Sbostic  * provided that the above copyright notice and this paragraph are
735619Sbostic  * duplicated in all such forms and that any documentation,
835619Sbostic  * advertising materials, and other materials related to such
935619Sbostic  * distribution and use acknowledge that the software was developed
1035619Sbostic  * by the University of California, Berkeley.  The name of the
1135619Sbostic  * University may not be used to endorse or promote products derived
1235619Sbostic  * from this software without specific prior written permission.
1335619Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1435619Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1537038Sbostic  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1621569Sdist  */
1721569Sdist 
186440Swnj #ifndef lint
1921569Sdist char copyright[] =
2035619Sbostic "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
2121569Sdist  All rights reserved.\n";
2235619Sbostic #endif /* not lint */
236440Swnj 
2421569Sdist #ifndef lint
25*38098Sbostic static char sccsid[] = "@(#)rcp.c	5.16 (Berkeley) 05/21/89";
2635619Sbostic #endif /* not lint */
2721569Sdist 
2812989Ssam /*
2912989Ssam  * rcp
3012989Ssam  */
316720Smckusick #include <sys/param.h>
3232127Sbostic #include <sys/file.h>
336440Swnj #include <sys/stat.h>
3423101Slepreau #include <sys/time.h>
356440Swnj #include <sys/ioctl.h>
36*38098Sbostic #include <sys/dir.h>
37*38098Sbostic #include <sys/signal.h>
389199Ssam #include <netinet/in.h>
396440Swnj #include <pwd.h>
4018026Sralph #include <netdb.h>
416440Swnj #include <errno.h>
42*38098Sbostic #include <strings.h>
43*38098Sbostic #include <stdio.h>
44*38098Sbostic #include <ctype.h>
4537037Sbostic #include "pathnames.h"
4612989Ssam 
47*38098Sbostic #ifdef KERBEROS
4836624Skfall #include <kerberos/krb.h>
49*38098Sbostic 
50*38098Sbostic char krb_realm[REALM_SZ];
51*38098Sbostic int use_kerberos = 1, encrypt = 0;
52*38098Sbostic CREDENTIALS cred;
53*38098Sbostic Key_schedule schedule;
5436624Skfall #endif
5536624Skfall 
56*38098Sbostic extern int errno;
57*38098Sbostic extern char *sys_errlist[];
58*38098Sbostic struct passwd *pwd;
59*38098Sbostic int errs, pflag, port, rem, userid;
60*38098Sbostic int iamremote, iamrecursive, targetshouldbedirectory;
616440Swnj 
62*38098Sbostic char cmd[20];			/* must hold "rcp -r -p -d\0" */
63*38098Sbostic 
64*38098Sbostic typedef struct _buf {
6521254Smckusick 	int	cnt;
6621254Smckusick 	char	*buf;
67*38098Sbostic } BUF;
6821254Smckusick 
696440Swnj main(argc, argv)
706440Swnj 	int argc;
716440Swnj 	char **argv;
726440Swnj {
73*38098Sbostic 	extern int optind;
7418126Sralph 	struct servent *sp;
75*38098Sbostic 	int ch;
76*38098Sbostic 	char *targ, *colon();
77*38098Sbostic 	struct passwd *getpwuid();
78*38098Sbostic 	int lostconn();
7918026Sralph 
80*38098Sbostic #ifdef KERBEROS
8136626Skfall 	sp = getservbyname("kshell", "tcp");
82*38098Sbostic 	if (sp == NULL) {
8336626Skfall 		use_kerberos = 0;
8436626Skfall 		old_warning("kshell service unknown");
8536626Skfall 		sp = getservbyname("kshell", "tcp");
8636626Skfall 	}
8736626Skfall #else
8818026Sralph 	sp = getservbyname("shell", "tcp");
8936626Skfall #endif
90*38098Sbostic 	if (!sp) {
91*38098Sbostic 		(void)fprintf(stderr, "rcp: shell/tcp: unknown service\n");
9218026Sralph 		exit(1);
9318026Sralph 	}
9418126Sralph 	port = sp->s_port;
95*38098Sbostic 
96*38098Sbostic 	if (!(pwd = getpwuid(userid = getuid()))) {
97*38098Sbostic 		(void)fprintf(stderr, "rcp: unknown user %d.\n", userid);
986440Swnj 		exit(1);
996440Swnj 	}
10023101Slepreau 
101*38098Sbostic 	while ((ch = getopt(argc, argv, "dfkprtx")) != EOF)
102*38098Sbostic 		switch(ch) {
103*38098Sbostic 		case 'd':
104*38098Sbostic 			targetshouldbedirectory = 1;
10523101Slepreau 			break;
106*38098Sbostic 		case 'f':			/* "from" */
107*38098Sbostic 			iamremote = 1;
108*38098Sbostic 			(void)response();
109*38098Sbostic 			(void)setuid(userid);
110*38098Sbostic 			source(--argc, ++argv);
111*38098Sbostic 			exit(errs);
112*38098Sbostic #ifdef KERBEROS
113*38098Sbostic 		case 'k':
11436624Skfall 			strncpy(krb_realm, ++argv, REALM_SZ);
11536624Skfall 			break;
11636624Skfall #endif
117*38098Sbostic 		case 'p':			/* preserve access/mod times */
118*38098Sbostic 			++pflag;
11923101Slepreau 			break;
120*38098Sbostic 		case 'r':
121*38098Sbostic 			++iamrecursive;
12223101Slepreau 			break;
123*38098Sbostic 		case 't':			/* "to" */
12423101Slepreau 			iamremote = 1;
125*38098Sbostic 			(void)setuid(userid);
12623101Slepreau 			sink(--argc, ++argv);
12723101Slepreau 			exit(errs);
128*38098Sbostic #ifdef KERBEROS
129*38098Sbostic 		case 'x':
130*38098Sbostic 			encrypt = 1;
131*38098Sbostic 			des_set_key(cred.session, schedule);
132*38098Sbostic 			break;
133*38098Sbostic #endif
134*38098Sbostic 		case '?':
135*38098Sbostic 		default:
13632127Sbostic 			usage();
13723101Slepreau 		}
138*38098Sbostic 	argc -= optind;
139*38098Sbostic 	argv += optind;
140*38098Sbostic 
14132127Sbostic 	if (argc < 2)
14232127Sbostic 		usage();
1436440Swnj 	if (argc > 2)
1446440Swnj 		targetshouldbedirectory = 1;
145*38098Sbostic 
14632127Sbostic 	rem = -1;
147*38098Sbostic 	(void)sprintf(cmd, "rcp%s%s%s", iamrecursive ? " -r" : "",
148*38098Sbostic 	    pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "");
149*38098Sbostic 
150*38098Sbostic 	(void)signal(SIGPIPE, lostconn);
151*38098Sbostic 
152*38098Sbostic 	if (targ = colon(argv[argc - 1]))
153*38098Sbostic 		toremote(targ, argc, argv);
154*38098Sbostic 	else {
155*38098Sbostic 		tolocal(argc, argv);
156*38098Sbostic 		if (targetshouldbedirectory)
157*38098Sbostic 			verifydir(argv[argc - 1]);
158*38098Sbostic 	}
159*38098Sbostic 	exit(errs);
160*38098Sbostic }
161*38098Sbostic 
162*38098Sbostic toremote(targ, argc, argv)
163*38098Sbostic 	char *targ;
164*38098Sbostic 	int argc;
165*38098Sbostic 	char **argv;
166*38098Sbostic {
167*38098Sbostic 	int i;
168*38098Sbostic 	char *host, *src, *suser, *thost, *tuser;
169*38098Sbostic 	char buf[1024], *colon();
170*38098Sbostic 
171*38098Sbostic 	*targ++ = 0;
172*38098Sbostic 	if (*targ == 0)
173*38098Sbostic 		targ = ".";
174*38098Sbostic 
175*38098Sbostic 	thost = index(argv[argc - 1], '@');
176*38098Sbostic 	if (thost) {
177*38098Sbostic 		*thost++ = 0;
178*38098Sbostic 		tuser = argv[argc - 1];
179*38098Sbostic 		if (*tuser == '\0')
18024711Sbloom 			tuser = NULL;
181*38098Sbostic 		else if (!okname(tuser))
182*38098Sbostic 			exit(1);
183*38098Sbostic 	} else {
184*38098Sbostic 		thost = argv[argc - 1];
185*38098Sbostic 		tuser = NULL;
186*38098Sbostic 	}
18736624Skfall 
188*38098Sbostic 	for (i = 0; i < argc - 1; i++) {
189*38098Sbostic 		src = colon(argv[i]);
190*38098Sbostic 		if (src) {			/* remote to remote */
191*38098Sbostic 			*src++ = 0;
192*38098Sbostic 			if (*src == 0)
193*38098Sbostic 				src = ".";
194*38098Sbostic 			host = index(argv[i], '@');
195*38098Sbostic 			if (host) {
196*38098Sbostic 				*host++ = 0;
197*38098Sbostic 				suser = argv[i];
198*38098Sbostic 				if (*suser == '\0')
199*38098Sbostic 					suser = pwd->pw_name;
200*38098Sbostic 				else if (!okname(suser))
201*38098Sbostic 					continue;
202*38098Sbostic 				(void)sprintf(buf,
203*38098Sbostic 				    "%s %s -l %s -n %s %s '%s%s%s:%s'",
204*38098Sbostic 				    _PATH_RSH, host, suser, cmd, src,
205*38098Sbostic 				    tuser ? tuser : "", tuser ? "@" : "",
206*38098Sbostic 				    thost, targ);
207*38098Sbostic 			} else
208*38098Sbostic 				(void)sprintf(buf,
209*38098Sbostic 				    "%s %s -n %s %s '%s%s%s:%s'",
210*38098Sbostic 				    _PATH_RSH, argv[i], cmd, src,
211*38098Sbostic 				    tuser ? tuser : "", tuser ? "@" : "",
212*38098Sbostic 				    thost, targ);
213*38098Sbostic 			(void)susystem(buf);
214*38098Sbostic 		} else {			/* local to remote */
215*38098Sbostic 			if (rem == -1) {
216*38098Sbostic 				(void)sprintf(buf, "%s -t %s", cmd, targ);
217*38098Sbostic 				host = thost;
218*38098Sbostic #ifdef KERBEROS
219*38098Sbostic 				if (use_kerberos)
220*38098Sbostic 					kerberos(buf, tuser ?
221*38098Sbostic 					    tuser : pwd->pw_name);
222*38098Sbostic 				else
223*38098Sbostic #endif
22424711Sbloom 					rem = rcmd(&host, port, pwd->pw_name,
22524711Sbloom 					    tuser ? tuser : pwd->pw_name,
2266440Swnj 					    buf, 0);
227*38098Sbostic 				if (rem < 0)
228*38098Sbostic 					exit(1);
229*38098Sbostic 				if (response() < 0)
230*38098Sbostic 					exit(1);
231*38098Sbostic 				(void)setuid(userid);
2326440Swnj 			}
233*38098Sbostic 			source(1, argv+i);
2346440Swnj 		}
235*38098Sbostic 	}
236*38098Sbostic }
237*38098Sbostic 
238*38098Sbostic tolocal(argc, argv)
239*38098Sbostic 	int argc;
240*38098Sbostic 	char **argv;
241*38098Sbostic {
242*38098Sbostic 	int i;
243*38098Sbostic 	char *host, *src, *suser;
244*38098Sbostic 	char buf[1024], *colon();
245*38098Sbostic 
246*38098Sbostic 	for (i = 0; i < argc - 1; i++) {
247*38098Sbostic 		src = colon(argv[i]);
248*38098Sbostic 		if (src == 0) {			/* local to local */
249*38098Sbostic 			(void)sprintf(buf, "%s%s%s %s %s",
250*38098Sbostic 			    _PATH_CP, iamrecursive ? " -r" : "",
251*38098Sbostic 			    pflag ? " -p" : "", argv[i], argv[argc - 1]);
252*38098Sbostic 			(void)susystem(buf);
253*38098Sbostic 		} else {			/* remote to local */
254*38098Sbostic 			*src++ = 0;
255*38098Sbostic 			if (*src == 0)
256*38098Sbostic 				src = ".";
257*38098Sbostic 			host = index(argv[i], '@');
258*38098Sbostic 			if (host) {
259*38098Sbostic 				*host++ = 0;
260*38098Sbostic 				suser = argv[i];
261*38098Sbostic 				if (*suser == '\0')
2626440Swnj 					suser = pwd->pw_name;
263*38098Sbostic 				else if (!okname(suser))
26418259Sralph 					continue;
265*38098Sbostic 			} else {
266*38098Sbostic 				host = argv[i];
267*38098Sbostic 				suser = pwd->pw_name;
2686440Swnj 			}
269*38098Sbostic 			(void)sprintf(buf, "%s -f %s", cmd, src);
270*38098Sbostic #ifdef KERBEROS
271*38098Sbostic 			if (use_kerberos)
272*38098Sbostic 				kerberos(buf, suser);
273*38098Sbostic 			else
274*38098Sbostic #endif
275*38098Sbostic 				rem = rcmd(&host, port, pwd->pw_name, suser,
276*38098Sbostic 				    buf, 0);
277*38098Sbostic 			if (rem < 0)
278*38098Sbostic 				continue;
279*38098Sbostic 			(void)setreuid(0, userid);
280*38098Sbostic 			sink(1, argv + argc - 1);
281*38098Sbostic 			(void)setreuid(userid, 0);
282*38098Sbostic 			(void)close(rem);
283*38098Sbostic 			rem = -1;
2846440Swnj 		}
2856440Swnj 	}
2866440Swnj }
2876440Swnj 
288*38098Sbostic #ifdef KERBEROS
289*38098Sbostic kerberos(buf, user)
290*38098Sbostic 	char *buf, *user;
291*38098Sbostic {
292*38098Sbostic 	struct servent *sp;
293*38098Sbostic 	char *host;
294*38098Sbostic 
295*38098Sbostic again:	rem = KSUCCESS;
296*38098Sbostic 	if (krb_realm[0] == '\0')
297*38098Sbostic 		rem = krb_get_lrealm(krb_realm, 1);
298*38098Sbostic 	if (rem == KSUCCESS) {
299*38098Sbostic 		if (encrypt)
300*38098Sbostic 			rem = krcmd_mutual(&host, port, user, buf,
301*38098Sbostic 			    0, krb_realm, &cred, schedule);
302*38098Sbostic 		else
303*38098Sbostic 			rem = krcmd(&host, port, user, buf, 0, krb_realm);
304*38098Sbostic 	} else {
305*38098Sbostic 		(void)fprintf(stderr,
306*38098Sbostic 		    "rcp: error getting local realm %s\n", krb_err_txt[rem]);
307*38098Sbostic 		exit(1);
308*38098Sbostic 	}
309*38098Sbostic 	if (rem < 0 && errno == ECONNREFUSED) {
310*38098Sbostic 		use_kerberos = 0;
311*38098Sbostic 		old_warning("remote host doesn't support Kerberos");
312*38098Sbostic 		sp = getservbyname("shell", "tcp");
313*38098Sbostic 		if (sp == NULL) {
314*38098Sbostic 			(void)fprintf(stderr,
315*38098Sbostic 			    "rcp: unknown service shell/tcp\n");
316*38098Sbostic 			exit(1);
317*38098Sbostic 		}
318*38098Sbostic 		port = sp->s_port;
319*38098Sbostic 		goto again;
320*38098Sbostic 	}
321*38098Sbostic }
322*38098Sbostic #endif /* KERBEROS */
323*38098Sbostic 
3246440Swnj verifydir(cp)
3256440Swnj 	char *cp;
3266440Swnj {
3276440Swnj 	struct stat stb;
3286440Swnj 
32918126Sralph 	if (stat(cp, &stb) >= 0) {
33018126Sralph 		if ((stb.st_mode & S_IFMT) == S_IFDIR)
33118126Sralph 			return;
33218126Sralph 		errno = ENOTDIR;
33318126Sralph 	}
3346440Swnj 	error("rcp: %s: %s.\n", cp, sys_errlist[errno]);
3356440Swnj 	exit(1);
3366440Swnj }
3376440Swnj 
3386440Swnj char *
3396440Swnj colon(cp)
340*38098Sbostic 	register char *cp;
3416440Swnj {
342*38098Sbostic 	for (; *cp; ++cp) {
3436440Swnj 		if (*cp == ':')
344*38098Sbostic 			return(cp);
3456440Swnj 		if (*cp == '/')
346*38098Sbostic 			return(0);
3476440Swnj 	}
348*38098Sbostic 	return(0);
3496440Swnj }
3506440Swnj 
3516440Swnj okname(cp0)
3526440Swnj 	char *cp0;
3536440Swnj {
3546440Swnj 	register char *cp = cp0;
3556440Swnj 	register int c;
3566440Swnj 
3576440Swnj 	do {
3586440Swnj 		c = *cp;
3596440Swnj 		if (c & 0200)
3606440Swnj 			goto bad;
3616440Swnj 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
3626440Swnj 			goto bad;
3636440Swnj 		cp++;
3646440Swnj 	} while (*cp);
365*38098Sbostic 	return(1);
3666440Swnj bad:
3676440Swnj 	fprintf(stderr, "rcp: invalid user name %s\n", cp0);
368*38098Sbostic 	return(0);
3696440Swnj }
3706440Swnj 
37117999Sserge susystem(s)
37217999Sserge 	char *s;
3736440Swnj {
37417999Sserge 	int status, pid, w;
37517999Sserge 	register int (*istat)(), (*qstat)();
3766440Swnj 
37717999Sserge 	if ((pid = vfork()) == 0) {
378*38098Sbostic 		(void)setuid(userid);
37937037Sbostic 		execl(_PATH_BSHELL, "sh", "-c", s, (char *)0);
38017999Sserge 		_exit(127);
38117999Sserge 	}
38217999Sserge 	istat = signal(SIGINT, SIG_IGN);
38317999Sserge 	qstat = signal(SIGQUIT, SIG_IGN);
38417999Sserge 	while ((w = wait(&status)) != pid && w != -1)
38517999Sserge 		;
38617999Sserge 	if (w == -1)
38717999Sserge 		status = -1;
388*38098Sbostic 	(void)signal(SIGINT, istat);
389*38098Sbostic 	(void)signal(SIGQUIT, qstat);
390*38098Sbostic 	return(status);
3916440Swnj }
3926440Swnj 
3936440Swnj source(argc, argv)
3946440Swnj 	int argc;
3956440Swnj 	char **argv;
3966440Swnj {
3976440Swnj 	struct stat stb;
398*38098Sbostic 	static BUF buffer;
399*38098Sbostic 	BUF *bp;
400*38098Sbostic 	off_t i;
40132268Sbostic 	int x, readerr, f, amt;
402*38098Sbostic 	char *last, *name, buf[BUFSIZ];
403*38098Sbostic 	BUF *allocbuf();
4046440Swnj 
4056440Swnj 	for (x = 0; x < argc; x++) {
4066440Swnj 		name = argv[x];
407*38098Sbostic 		if ((f = open(name, O_RDONLY, 0)) < 0) {
4086440Swnj 			error("rcp: %s: %s\n", name, sys_errlist[errno]);
4096440Swnj 			continue;
4106440Swnj 		}
4116440Swnj 		if (fstat(f, &stb) < 0)
4126440Swnj 			goto notreg;
4136440Swnj 		switch (stb.st_mode&S_IFMT) {
4146440Swnj 
4156440Swnj 		case S_IFREG:
4166440Swnj 			break;
4176440Swnj 
4186440Swnj 		case S_IFDIR:
4196440Swnj 			if (iamrecursive) {
420*38098Sbostic 				(void)close(f);
42123101Slepreau 				rsource(name, &stb);
4226440Swnj 				continue;
4236440Swnj 			}
424*38098Sbostic 			/* FALLTHROUGH */
4256440Swnj 		default:
426*38098Sbostic notreg:			(void)close(f);
4276440Swnj 			error("rcp: %s: not a plain file\n", name);
4286440Swnj 			continue;
4296440Swnj 		}
4306440Swnj 		last = rindex(name, '/');
4316440Swnj 		if (last == 0)
4326440Swnj 			last = name;
4336440Swnj 		else
4346440Swnj 			last++;
43523101Slepreau 		if (pflag) {
43623101Slepreau 			/*
43723101Slepreau 			 * Make it compatible with possible future
43823101Slepreau 			 * versions expecting microseconds.
43923101Slepreau 			 */
440*38098Sbostic 			(void)sprintf(buf, "T%ld 0 %ld 0\n",
44123101Slepreau 			    stb.st_mtime, stb.st_atime);
442*38098Sbostic 			(void)write(rem, buf, strlen(buf));
44323101Slepreau 			if (response() < 0) {
444*38098Sbostic 				(void)close(f);
44523101Slepreau 				continue;
44623101Slepreau 			}
44723101Slepreau 		}
448*38098Sbostic 		(void)sprintf(buf, "C%04o %ld %s\n",
4496440Swnj 		    stb.st_mode&07777, stb.st_size, last);
450*38098Sbostic 		(void)write(rem, buf, strlen(buf));
4516440Swnj 		if (response() < 0) {
452*38098Sbostic 			(void)close(f);
4536440Swnj 			continue;
4546440Swnj 		}
45535689Sbostic 		if ((bp = allocbuf(&buffer, f, BUFSIZ)) == 0) {
456*38098Sbostic 			(void)close(f);
45721254Smckusick 			continue;
45821254Smckusick 		}
45932268Sbostic 		readerr = 0;
46021254Smckusick 		for (i = 0; i < stb.st_size; i += bp->cnt) {
46121254Smckusick 			amt = bp->cnt;
4626440Swnj 			if (i + amt > stb.st_size)
4636440Swnj 				amt = stb.st_size - i;
46432268Sbostic 			if (readerr == 0 && read(f, bp->buf, amt) != amt)
46532268Sbostic 				readerr = errno;
466*38098Sbostic 			(void)write(rem, bp->buf, amt);
4676440Swnj 		}
468*38098Sbostic 		(void)close(f);
46932268Sbostic 		if (readerr == 0)
470*38098Sbostic 			(void)write(rem, "", 1);
4716440Swnj 		else
47232268Sbostic 			error("rcp: %s: %s\n", name, sys_errlist[readerr]);
473*38098Sbostic 		(void)response();
4746440Swnj 	}
4756440Swnj }
4766440Swnj 
47723101Slepreau rsource(name, statp)
4786440Swnj 	char *name;
47923101Slepreau 	struct stat *statp;
4806440Swnj {
481*38098Sbostic 	DIR *d;
4826440Swnj 	struct direct *dp;
483*38098Sbostic 	char *last, *bufv[1], buf[BUFSIZ];
4846440Swnj 
485*38098Sbostic 	if (!(d = opendir(name))) {
48618126Sralph 		error("rcp: %s: %s\n", name, sys_errlist[errno]);
4876440Swnj 		return;
4886440Swnj 	}
4896440Swnj 	last = rindex(name, '/');
4906440Swnj 	if (last == 0)
4916440Swnj 		last = name;
4926440Swnj 	else
4936440Swnj 		last++;
49423101Slepreau 	if (pflag) {
495*38098Sbostic 		(void)sprintf(buf, "T%ld 0 %ld 0\n",
49623101Slepreau 		    statp->st_mtime, statp->st_atime);
497*38098Sbostic 		(void)write(rem, buf, strlen(buf));
49823101Slepreau 		if (response() < 0) {
49923101Slepreau 			closedir(d);
50023101Slepreau 			return;
50123101Slepreau 		}
50223101Slepreau 	}
503*38098Sbostic 	(void)sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last);
504*38098Sbostic 	(void)write(rem, buf, strlen(buf));
5056440Swnj 	if (response() < 0) {
5066440Swnj 		closedir(d);
5076440Swnj 		return;
5086440Swnj 	}
5096440Swnj 	while (dp = readdir(d)) {
5106440Swnj 		if (dp->d_ino == 0)
5116440Swnj 			continue;
5126440Swnj 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
5136440Swnj 			continue;
5146440Swnj 		if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
5156440Swnj 			error("%s/%s: Name too long.\n", name, dp->d_name);
5166440Swnj 			continue;
5176440Swnj 		}
518*38098Sbostic 		(void)sprintf(buf, "%s/%s", name, dp->d_name);
5196440Swnj 		bufv[0] = buf;
5206440Swnj 		source(1, bufv);
5216440Swnj 	}
5226440Swnj 	closedir(d);
523*38098Sbostic 	(void)write(rem, "E\n", 2);
524*38098Sbostic 	(void)response();
5256440Swnj }
5266440Swnj 
5276440Swnj response()
5286440Swnj {
529*38098Sbostic 	register char *cp;
530*38098Sbostic 	char ch, resp, rbuf[BUFSIZ];
5316440Swnj 
532*38098Sbostic 	if (read(rem, &resp, sizeof(resp)) != sizeof(resp))
5336440Swnj 		lostconn();
5346440Swnj 
535*38098Sbostic 	cp = rbuf;
536*38098Sbostic 	switch(resp) {
53723101Slepreau 	case 0:				/* ok */
538*38098Sbostic 		return(0);
5396440Swnj 	default:
5406440Swnj 		*cp++ = resp;
541*38098Sbostic 		/* FALLTHROUGH */
54223101Slepreau 	case 1:				/* error, followed by err msg */
54323101Slepreau 	case 2:				/* fatal error, "" */
5446440Swnj 		do {
545*38098Sbostic 			if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
5466440Swnj 				lostconn();
547*38098Sbostic 			*cp++ = ch;
548*38098Sbostic 		} while (cp < &rbuf[BUFSIZ] && ch != '\n');
549*38098Sbostic 
550*38098Sbostic 		if (!iamremote)
551*38098Sbostic 			(void)write(2, rbuf, cp - rbuf);
552*38098Sbostic 		++errs;
5536440Swnj 		if (resp == 1)
554*38098Sbostic 			return(-1);
5556440Swnj 		exit(1);
5566440Swnj 	}
5576440Swnj 	/*NOTREACHED*/
5586440Swnj }
5596440Swnj 
5606440Swnj lostconn()
5616440Swnj {
562*38098Sbostic 	if (!iamremote)
563*38098Sbostic 		(void)fprintf(stderr, "rcp: lost connection\n");
5646440Swnj 	exit(1);
5656440Swnj }
5666440Swnj 
5676440Swnj sink(argc, argv)
5686440Swnj 	int argc;
5696440Swnj 	char **argv;
5706440Swnj {
57121254Smckusick 	off_t i, j;
57221254Smckusick 	char *targ, *whopp, *cp;
57321254Smckusick 	int of, mode, wrerr, exists, first, count, amt, size;
574*38098Sbostic 	BUF *bp;
575*38098Sbostic 	static BUF buffer;
57621254Smckusick 	struct stat stb;
57721254Smckusick 	int targisdir = 0;
5786440Swnj 	int mask = umask(0);
5796440Swnj 	char *myargv[1];
58023101Slepreau 	char cmdbuf[BUFSIZ], nambuf[BUFSIZ];
58123101Slepreau 	int setimes = 0;
58223101Slepreau 	struct timeval tv[2];
583*38098Sbostic 	BUF *allocbuf();
58423101Slepreau #define atime	tv[0]
58523101Slepreau #define mtime	tv[1]
58621254Smckusick #define	SCREWUP(str)	{ whopp = str; goto screwup; }
5876440Swnj 
58823112Slepreau 	if (!pflag)
589*38098Sbostic 		(void)umask(mask);
59018126Sralph 	if (argc != 1) {
5916440Swnj 		error("rcp: ambiguous target\n");
5926440Swnj 		exit(1);
5936440Swnj 	}
5946440Swnj 	targ = *argv;
5956440Swnj 	if (targetshouldbedirectory)
5966440Swnj 		verifydir(targ);
597*38098Sbostic 	(void)write(rem, "", 1);
5986440Swnj 	if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
5996440Swnj 		targisdir = 1;
60014580Sralph 	for (first = 1; ; first = 0) {
6016440Swnj 		cp = cmdbuf;
6026440Swnj 		if (read(rem, cp, 1) <= 0)
6036440Swnj 			return;
6046440Swnj 		if (*cp++ == '\n')
6056440Swnj 			SCREWUP("unexpected '\\n'");
6066440Swnj 		do {
6076440Swnj 			if (read(rem, cp, 1) != 1)
6086440Swnj 				SCREWUP("lost connection");
6096440Swnj 		} while (*cp++ != '\n');
6106440Swnj 		*cp = 0;
6116440Swnj 		if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
6126440Swnj 			if (iamremote == 0)
613*38098Sbostic 				(void)write(2, cmdbuf+1, strlen(cmdbuf+1));
6146440Swnj 			if (cmdbuf[0] == '\02')
6156440Swnj 				exit(1);
6166440Swnj 			errs++;
6176440Swnj 			continue;
6186440Swnj 		}
6196440Swnj 		*--cp = 0;
6206440Swnj 		cp = cmdbuf;
6216440Swnj 		if (*cp == 'E') {
622*38098Sbostic 			(void)write(rem, "", 1);
6236440Swnj 			return;
6246440Swnj 		}
62523101Slepreau 
62623101Slepreau #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
62723101Slepreau 		if (*cp == 'T') {
62823101Slepreau 			setimes++;
62923101Slepreau 			cp++;
63023101Slepreau 			getnum(mtime.tv_sec);
63123101Slepreau 			if (*cp++ != ' ')
63223101Slepreau 				SCREWUP("mtime.sec not delimited");
63323101Slepreau 			getnum(mtime.tv_usec);
63423101Slepreau 			if (*cp++ != ' ')
63523101Slepreau 				SCREWUP("mtime.usec not delimited");
63623101Slepreau 			getnum(atime.tv_sec);
63723101Slepreau 			if (*cp++ != ' ')
63823101Slepreau 				SCREWUP("atime.sec not delimited");
63923101Slepreau 			getnum(atime.tv_usec);
64023101Slepreau 			if (*cp++ != '\0')
64123101Slepreau 				SCREWUP("atime.usec not delimited");
642*38098Sbostic 			(void)write(rem, "", 1);
64323101Slepreau 			continue;
64423101Slepreau 		}
64514580Sralph 		if (*cp != 'C' && *cp != 'D') {
64614580Sralph 			/*
64714580Sralph 			 * Check for the case "rcp remote:foo\* local:bar".
64814580Sralph 			 * In this case, the line "No match." can be returned
64914580Sralph 			 * by the shell before the rcp command on the remote is
65014580Sralph 			 * executed so the ^Aerror_message convention isn't
65114580Sralph 			 * followed.
65214580Sralph 			 */
65314580Sralph 			if (first) {
65414580Sralph 				error("%s\n", cp);
65514580Sralph 				exit(1);
65614580Sralph 			}
6576440Swnj 			SCREWUP("expected control record");
65814580Sralph 		}
6596440Swnj 		cp++;
6606440Swnj 		mode = 0;
6616440Swnj 		for (; cp < cmdbuf+5; cp++) {
6626440Swnj 			if (*cp < '0' || *cp > '7')
6636440Swnj 				SCREWUP("bad mode");
6646440Swnj 			mode = (mode << 3) | (*cp - '0');
6656440Swnj 		}
6666440Swnj 		if (*cp++ != ' ')
6676440Swnj 			SCREWUP("mode not delimited");
6686440Swnj 		size = 0;
66923112Slepreau 		while (isdigit(*cp))
6706440Swnj 			size = size * 10 + (*cp++ - '0');
6716440Swnj 		if (*cp++ != ' ')
6726440Swnj 			SCREWUP("size not delimited");
6736440Swnj 		if (targisdir)
674*38098Sbostic 			(void)sprintf(nambuf, "%s%s%s", targ,
6756440Swnj 			    *targ ? "/" : "", cp);
6766440Swnj 		else
677*38098Sbostic 			(void)strcpy(nambuf, targ);
6786440Swnj 		exists = stat(nambuf, &stb) == 0;
67918126Sralph 		if (cmdbuf[0] == 'D') {
68018126Sralph 			if (exists) {
6816440Swnj 				if ((stb.st_mode&S_IFMT) != S_IFDIR) {
6826440Swnj 					errno = ENOTDIR;
6836440Swnj 					goto bad;
6846440Swnj 				}
68523112Slepreau 				if (pflag)
686*38098Sbostic 					(void)chmod(nambuf, mode);
68718126Sralph 			} else if (mkdir(nambuf, mode) < 0)
6886440Swnj 				goto bad;
6896440Swnj 			myargv[0] = nambuf;
6906440Swnj 			sink(1, myargv);
69123101Slepreau 			if (setimes) {
69223101Slepreau 				setimes = 0;
69323101Slepreau 				if (utimes(nambuf, tv) < 0)
69423101Slepreau 					error("rcp: can't set times on %s: %s\n",
69523101Slepreau 					    nambuf, sys_errlist[errno]);
69623101Slepreau 			}
6976440Swnj 			continue;
69818126Sralph 		}
69932127Sbostic 		if ((of = open(nambuf, O_WRONLY|O_CREAT, mode)) < 0) {
7006440Swnj 	bad:
7016440Swnj 			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
7026440Swnj 			continue;
7036440Swnj 		}
70423112Slepreau 		if (exists && pflag)
705*38098Sbostic 			(void)fchmod(of, mode);
706*38098Sbostic 		(void)write(rem, "", 1);
70735689Sbostic 		if ((bp = allocbuf(&buffer, of, BUFSIZ)) == 0) {
708*38098Sbostic 			(void)close(of);
70921254Smckusick 			continue;
71021254Smckusick 		}
71121254Smckusick 		cp = bp->buf;
71221254Smckusick 		count = 0;
7136440Swnj 		wrerr = 0;
7146440Swnj 		for (i = 0; i < size; i += BUFSIZ) {
71521254Smckusick 			amt = BUFSIZ;
7166440Swnj 			if (i + amt > size)
7176440Swnj 				amt = size - i;
71821254Smckusick 			count += amt;
7196440Swnj 			do {
72021254Smckusick 				j = read(rem, cp, amt);
72124711Sbloom 				if (j <= 0) {
72224711Sbloom 					if (j == 0)
72324711Sbloom 					    error("rcp: dropped connection");
72424711Sbloom 					else
72524711Sbloom 					    error("rcp: %s\n",
72624711Sbloom 						sys_errlist[errno]);
7276440Swnj 					exit(1);
72824711Sbloom 				}
7296440Swnj 				amt -= j;
7306440Swnj 				cp += j;
7316440Swnj 			} while (amt > 0);
73221254Smckusick 			if (count == bp->cnt) {
73321254Smckusick 				if (wrerr == 0 &&
73421254Smckusick 				    write(of, bp->buf, count) != count)
73521254Smckusick 					wrerr++;
73621254Smckusick 				count = 0;
73721254Smckusick 				cp = bp->buf;
73821254Smckusick 			}
7396440Swnj 		}
74021254Smckusick 		if (count != 0 && wrerr == 0 &&
74121254Smckusick 		    write(of, bp->buf, count) != count)
74221254Smckusick 			wrerr++;
74332127Sbostic 		if (ftruncate(of, size))
74432127Sbostic 			error("rcp: can't truncate %s: %s\n",
74532127Sbostic 			    nambuf, sys_errlist[errno]);
746*38098Sbostic 		(void)close(of);
747*38098Sbostic 		(void)response();
74823101Slepreau 		if (setimes) {
74923101Slepreau 			setimes = 0;
75023101Slepreau 			if (utimes(nambuf, tv) < 0)
75123101Slepreau 				error("rcp: can't set times on %s: %s\n",
75223101Slepreau 				    nambuf, sys_errlist[errno]);
75323101Slepreau 		}
7546440Swnj 		if (wrerr)
75523101Slepreau 			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
7566440Swnj 		else
757*38098Sbostic 			(void)write(rem, "", 1);
7586440Swnj 	}
7596440Swnj screwup:
7606440Swnj 	error("rcp: protocol screwup: %s\n", whopp);
7616440Swnj 	exit(1);
7626440Swnj }
7636440Swnj 
764*38098Sbostic BUF *
76521254Smckusick allocbuf(bp, fd, blksize)
766*38098Sbostic 	BUF *bp;
76721254Smckusick 	int fd, blksize;
76821254Smckusick {
76921254Smckusick 	struct stat stb;
77021254Smckusick 	int size;
771*38098Sbostic 	char *malloc();
77221254Smckusick 
77321254Smckusick 	if (fstat(fd, &stb) < 0) {
77421254Smckusick 		error("rcp: fstat: %s\n", sys_errlist[errno]);
775*38098Sbostic 		return(0);
77621254Smckusick 	}
77721254Smckusick 	size = roundup(stb.st_blksize, blksize);
77821254Smckusick 	if (size == 0)
77921254Smckusick 		size = blksize;
78021254Smckusick 	if (bp->cnt < size) {
78121254Smckusick 		if (bp->buf != 0)
78221254Smckusick 			free(bp->buf);
783*38098Sbostic 		bp->buf = (char *)malloc((u_int)size);
78421254Smckusick 		if (bp->buf == 0) {
78521254Smckusick 			error("rcp: malloc: out of memory\n");
786*38098Sbostic 			return(0);
78721254Smckusick 		}
78821254Smckusick 	}
78921254Smckusick 	bp->cnt = size;
790*38098Sbostic 	return(bp);
79121254Smckusick }
79221254Smckusick 
793*38098Sbostic /* VARARGS1 */
7946440Swnj error(fmt, a1, a2, a3, a4, a5)
7956440Swnj 	char *fmt;
7966440Swnj 	int a1, a2, a3, a4, a5;
7976440Swnj {
798*38098Sbostic 	int len;
799*38098Sbostic 	char buf[BUFSIZ];
8006440Swnj 
801*38098Sbostic 	++errs;
802*38098Sbostic 	buf[0] = 0x01;
803*38098Sbostic 	(void)sprintf(buf + 1, fmt, a1, a2, a3, a4, a5);
804*38098Sbostic 	len = strlen(buf);
805*38098Sbostic 	(void)write(rem, buf, len);
806*38098Sbostic 	if (!iamremote)
807*38098Sbostic 		(void)write(2, buf + 1, len - 1);
8086440Swnj }
80932127Sbostic 
81032127Sbostic usage()
81132127Sbostic {
812*38098Sbostic #ifdef KERBEROS
813*38098Sbostic 	(void)fprintf(stderr, "%s\n\t%s\n",
814*38098Sbostic 	    "usage: rcp [-k realm] [-px] f1 f2",
815*38098Sbostic 	    "or: rcp [-k realm] [-rpx] f1 ... fn directory");
81636626Skfall #else
817*38098Sbostic 	(void)fprintf(stderr,
818*38098Sbostic 	    "usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn directory\n");
81936626Skfall #endif
82032127Sbostic 	exit(1);
82132127Sbostic }
82236624Skfall 
823*38098Sbostic #ifdef KERBEROS
82436624Skfall old_warning(str)
825*38098Sbostic 	char *str;
82636624Skfall {
827*38098Sbostic 	(void)fprintf(stderr, "rcp: warning: %s, using standard rcp\n", str);
82836624Skfall }
82936624Skfall #endif
830