xref: /csrg-svn/bin/rcp/rcp.c (revision 35619)
121569Sdist /*
2*35619Sbostic  * Copyright (c) 1983 The Regents of the University of California.
3*35619Sbostic  * All rights reserved.
4*35619Sbostic  *
5*35619Sbostic  * Redistribution and use in source and binary forms are permitted
6*35619Sbostic  * provided that the above copyright notice and this paragraph are
7*35619Sbostic  * duplicated in all such forms and that any documentation,
8*35619Sbostic  * advertising materials, and other materials related to such
9*35619Sbostic  * distribution and use acknowledge that the software was developed
10*35619Sbostic  * by the University of California, Berkeley.  The name of the
11*35619Sbostic  * University may not be used to endorse or promote products derived
12*35619Sbostic  * from this software without specific prior written permission.
13*35619Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*35619Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*35619Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1621569Sdist  */
1721569Sdist 
186440Swnj #ifndef lint
1921569Sdist char copyright[] =
20*35619Sbostic "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
2121569Sdist  All rights reserved.\n";
22*35619Sbostic #endif /* not lint */
236440Swnj 
2421569Sdist #ifndef lint
25*35619Sbostic static char sccsid[] = "@(#)rcp.c	5.10 (Berkeley) 09/20/88";
26*35619Sbostic #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>
369199Ssam 
379199Ssam #include <netinet/in.h>
389199Ssam 
399199Ssam #include <stdio.h>
409199Ssam #include <signal.h>
416440Swnj #include <pwd.h>
426440Swnj #include <ctype.h>
4318026Sralph #include <netdb.h>
446440Swnj #include <errno.h>
4512989Ssam 
466440Swnj int	rem;
4732419Sbostic char	*colon(), *index(), *rindex(), *malloc(), *strcpy();
486440Swnj int	errs;
496440Swnj int	lostconn();
506440Swnj int	errno;
516440Swnj char	*sys_errlist[];
526440Swnj int	iamremote, targetshouldbedirectory;
536440Swnj int	iamrecursive;
5423101Slepreau int	pflag;
556440Swnj struct	passwd *pwd;
566440Swnj struct	passwd *getpwuid();
5718126Sralph int	userid;
5818126Sralph int	port;
596440Swnj 
6021254Smckusick struct buffer {
6121254Smckusick 	int	cnt;
6221254Smckusick 	char	*buf;
6321254Smckusick } *allocbuf();
6421254Smckusick 
656440Swnj /*VARARGS*/
666440Swnj int	error();
676440Swnj 
686440Swnj #define	ga()	 	(void) write(rem, "", 1)
696440Swnj 
706440Swnj main(argc, argv)
716440Swnj 	int argc;
726440Swnj 	char **argv;
736440Swnj {
746440Swnj 	char *targ, *host, *src;
7518259Sralph 	char *suser, *tuser, *thost;
766440Swnj 	int i;
776440Swnj 	char buf[BUFSIZ], cmd[16];
7818126Sralph 	struct servent *sp;
7918026Sralph 
8018026Sralph 	sp = getservbyname("shell", "tcp");
8118026Sralph 	if (sp == NULL) {
8218026Sralph 		fprintf(stderr, "rcp: shell/tcp: unknown service\n");
8318026Sralph 		exit(1);
8418026Sralph 	}
8518126Sralph 	port = sp->s_port;
8618126Sralph 	pwd = getpwuid(userid = getuid());
876440Swnj 	if (pwd == 0) {
886440Swnj 		fprintf(stderr, "who are you?\n");
896440Swnj 		exit(1);
906440Swnj 	}
9123101Slepreau 
9223101Slepreau 	for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
9323101Slepreau 		(*argv)++;
9423101Slepreau 		while (**argv) switch (*(*argv)++) {
9523101Slepreau 
9623101Slepreau 		    case 'r':
9723101Slepreau 			iamrecursive++;
9823101Slepreau 			break;
9923101Slepreau 
10023101Slepreau 		    case 'p':		/* preserve mtimes and atimes */
10123101Slepreau 			pflag++;
10223101Slepreau 			break;
10323101Slepreau 
10423101Slepreau 		    /* The rest of these are not for users. */
10523101Slepreau 		    case 'd':
10623101Slepreau 			targetshouldbedirectory = 1;
10723101Slepreau 			break;
10823101Slepreau 
10923101Slepreau 		    case 'f':		/* "from" */
11023101Slepreau 			iamremote = 1;
11123101Slepreau 			(void) response();
11223101Slepreau 			(void) setuid(userid);
11323101Slepreau 			source(--argc, ++argv);
11423101Slepreau 			exit(errs);
11523101Slepreau 
11623101Slepreau 		    case 't':		/* "to" */
11723101Slepreau 			iamremote = 1;
11823101Slepreau 			(void) setuid(userid);
11923101Slepreau 			sink(--argc, ++argv);
12023101Slepreau 			exit(errs);
12123101Slepreau 
12223101Slepreau 		    default:
12332127Sbostic 			usage();
12423101Slepreau 		}
1256440Swnj 	}
12632127Sbostic 	if (argc < 2)
12732127Sbostic 		usage();
1286440Swnj 	if (argc > 2)
1296440Swnj 		targetshouldbedirectory = 1;
13032127Sbostic 	rem = -1;
13123101Slepreau 	(void) sprintf(cmd, "rcp%s%s%s",
13223101Slepreau 	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
13323101Slepreau 	    targetshouldbedirectory ? " -d" : "");
13423101Slepreau 	(void) signal(SIGPIPE, lostconn);
1356440Swnj 	targ = colon(argv[argc - 1]);
13623101Slepreau 	if (targ) {				/* ... to remote */
1376440Swnj 		*targ++ = 0;
13813542Ssam 		if (*targ == 0)
13913542Ssam 			targ = ".";
14018259Sralph 		thost = index(argv[argc - 1], '@');
14118259Sralph 		if (thost) {
14218259Sralph 			*thost++ = 0;
14318259Sralph 			tuser = argv[argc - 1];
14418259Sralph 			if (*tuser == '\0')
14524711Sbloom 				tuser = NULL;
14618259Sralph 			else if (!okname(tuser))
1476440Swnj 				exit(1);
14818259Sralph 		} else {
14918259Sralph 			thost = argv[argc - 1];
15024711Sbloom 			tuser = NULL;
15118259Sralph 		}
1526440Swnj 		for (i = 0; i < argc - 1; i++) {
1536440Swnj 			src = colon(argv[i]);
15423101Slepreau 			if (src) {		/* remote to remote */
1556440Swnj 				*src++ = 0;
15613542Ssam 				if (*src == 0)
15713738Ssam 					src = ".";
15818259Sralph 				host = index(argv[i], '@');
15918259Sralph 				if (host) {
16018259Sralph 					*host++ = 0;
16118259Sralph 					suser = argv[i];
16218259Sralph 					if (*suser == '\0')
16318259Sralph 						suser = pwd->pw_name;
16418259Sralph 					else if (!okname(suser))
1656440Swnj 						continue;
16631028Sbostic 		(void) sprintf(buf, "/usr/ucb/rsh %s -l %s -n %s %s '%s%s%s:%s'",
16731945Sbostic 					    host, suser, cmd, src,
16831945Sbostic 					    tuser ? tuser : "",
16924711Sbloom 					    tuser ? "@" : "",
17024711Sbloom 					    thost, targ);
17118631Sralph 				} else
17231028Sbostic 		(void) sprintf(buf, "/usr/ucb/rsh %s -n %s %s '%s%s%s:%s'",
17331945Sbostic 					    argv[i], cmd, src,
17431945Sbostic 					    tuser ? tuser : "",
17524711Sbloom 					    tuser ? "@" : "",
17624711Sbloom 					    thost, targ);
1776440Swnj 				(void) susystem(buf);
17823101Slepreau 			} else {		/* local to remote */
1796440Swnj 				if (rem == -1) {
1806440Swnj 					(void) sprintf(buf, "%s -t %s",
1816440Swnj 					    cmd, targ);
18218259Sralph 					host = thost;
18324711Sbloom 					rem = rcmd(&host, port, pwd->pw_name,
18424711Sbloom 					    tuser ? tuser : pwd->pw_name,
1856440Swnj 					    buf, 0);
1866440Swnj 					if (rem < 0)
1876440Swnj 						exit(1);
1886440Swnj 					if (response() < 0)
1896440Swnj 						exit(1);
19018126Sralph 					(void) setuid(userid);
1916440Swnj 				}
1926440Swnj 				source(1, argv+i);
1936440Swnj 			}
1946440Swnj 		}
19523101Slepreau 	} else {				/* ... to local */
1966440Swnj 		if (targetshouldbedirectory)
1976440Swnj 			verifydir(argv[argc - 1]);
1986440Swnj 		for (i = 0; i < argc - 1; i++) {
1996440Swnj 			src = colon(argv[i]);
20023101Slepreau 			if (src == 0) {		/* local to local */
20123101Slepreau 				(void) sprintf(buf, "/bin/cp%s%s %s %s",
2026440Swnj 				    iamrecursive ? " -r" : "",
20323101Slepreau 				    pflag ? " -p" : "",
2046440Swnj 				    argv[i], argv[argc - 1]);
2056440Swnj 				(void) susystem(buf);
20623101Slepreau 			} else {		/* remote to local */
2076440Swnj 				*src++ = 0;
20813542Ssam 				if (*src == 0)
20913542Ssam 					src = ".";
21018259Sralph 				host = index(argv[i], '@');
21118259Sralph 				if (host) {
21218259Sralph 					*host++ = 0;
21318259Sralph 					suser = argv[i];
21418259Sralph 					if (*suser == '\0')
21518259Sralph 						suser = pwd->pw_name;
21618259Sralph 					else if (!okname(suser))
2176440Swnj 						continue;
21818259Sralph 				} else {
21918259Sralph 					host = argv[i];
2206440Swnj 					suser = pwd->pw_name;
22118259Sralph 				}
2226440Swnj 				(void) sprintf(buf, "%s -f %s", cmd, src);
22318126Sralph 				rem = rcmd(&host, port, pwd->pw_name, suser,
2246440Swnj 				    buf, 0);
2256440Swnj 				if (rem < 0)
22618259Sralph 					continue;
22718126Sralph 				(void) setreuid(0, userid);
2286440Swnj 				sink(1, argv+argc-1);
22918126Sralph 				(void) setreuid(userid, 0);
2306440Swnj 				(void) close(rem);
2316440Swnj 				rem = -1;
2326440Swnj 			}
2336440Swnj 		}
2346440Swnj 	}
2356440Swnj 	exit(errs);
2366440Swnj }
2376440Swnj 
2386440Swnj verifydir(cp)
2396440Swnj 	char *cp;
2406440Swnj {
2416440Swnj 	struct stat stb;
2426440Swnj 
24318126Sralph 	if (stat(cp, &stb) >= 0) {
24418126Sralph 		if ((stb.st_mode & S_IFMT) == S_IFDIR)
24518126Sralph 			return;
24618126Sralph 		errno = ENOTDIR;
24718126Sralph 	}
2486440Swnj 	error("rcp: %s: %s.\n", cp, sys_errlist[errno]);
2496440Swnj 	exit(1);
2506440Swnj }
2516440Swnj 
2526440Swnj char *
2536440Swnj colon(cp)
2546440Swnj 	char *cp;
2556440Swnj {
2566440Swnj 
2576440Swnj 	while (*cp) {
2586440Swnj 		if (*cp == ':')
2596440Swnj 			return (cp);
2606440Swnj 		if (*cp == '/')
2616440Swnj 			return (0);
2626440Swnj 		cp++;
2636440Swnj 	}
2646440Swnj 	return (0);
2656440Swnj }
2666440Swnj 
2676440Swnj okname(cp0)
2686440Swnj 	char *cp0;
2696440Swnj {
2706440Swnj 	register char *cp = cp0;
2716440Swnj 	register int c;
2726440Swnj 
2736440Swnj 	do {
2746440Swnj 		c = *cp;
2756440Swnj 		if (c & 0200)
2766440Swnj 			goto bad;
2776440Swnj 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
2786440Swnj 			goto bad;
2796440Swnj 		cp++;
2806440Swnj 	} while (*cp);
2816440Swnj 	return (1);
2826440Swnj bad:
2836440Swnj 	fprintf(stderr, "rcp: invalid user name %s\n", cp0);
2846440Swnj 	return (0);
2856440Swnj }
2866440Swnj 
28717999Sserge susystem(s)
28817999Sserge 	char *s;
2896440Swnj {
29017999Sserge 	int status, pid, w;
29117999Sserge 	register int (*istat)(), (*qstat)();
2926440Swnj 
29317999Sserge 	if ((pid = vfork()) == 0) {
29423101Slepreau 		(void) setuid(userid);
29517999Sserge 		execl("/bin/sh", "sh", "-c", s, (char *)0);
29617999Sserge 		_exit(127);
29717999Sserge 	}
29817999Sserge 	istat = signal(SIGINT, SIG_IGN);
29917999Sserge 	qstat = signal(SIGQUIT, SIG_IGN);
30017999Sserge 	while ((w = wait(&status)) != pid && w != -1)
30117999Sserge 		;
30217999Sserge 	if (w == -1)
30317999Sserge 		status = -1;
30423101Slepreau 	(void) signal(SIGINT, istat);
30523101Slepreau 	(void) signal(SIGQUIT, qstat);
30617999Sserge 	return (status);
3076440Swnj }
3086440Swnj 
3096440Swnj source(argc, argv)
3106440Swnj 	int argc;
3116440Swnj 	char **argv;
3126440Swnj {
3136440Swnj 	char *last, *name;
3146440Swnj 	struct stat stb;
31521254Smckusick 	static struct buffer buffer;
31621254Smckusick 	struct buffer *bp;
31732268Sbostic 	int x, readerr, f, amt;
31821254Smckusick 	off_t i;
3196440Swnj 	char buf[BUFSIZ];
3206440Swnj 
3216440Swnj 	for (x = 0; x < argc; x++) {
3226440Swnj 		name = argv[x];
32318126Sralph 		if ((f = open(name, 0)) < 0) {
3246440Swnj 			error("rcp: %s: %s\n", name, sys_errlist[errno]);
3256440Swnj 			continue;
3266440Swnj 		}
3276440Swnj 		if (fstat(f, &stb) < 0)
3286440Swnj 			goto notreg;
3296440Swnj 		switch (stb.st_mode&S_IFMT) {
3306440Swnj 
3316440Swnj 		case S_IFREG:
3326440Swnj 			break;
3336440Swnj 
3346440Swnj 		case S_IFDIR:
3356440Swnj 			if (iamrecursive) {
3366440Swnj 				(void) close(f);
33723101Slepreau 				rsource(name, &stb);
3386440Swnj 				continue;
3396440Swnj 			}
3406440Swnj 			/* fall into ... */
3416440Swnj 		default:
3426440Swnj notreg:
3436440Swnj 			(void) close(f);
3446440Swnj 			error("rcp: %s: not a plain file\n", name);
3456440Swnj 			continue;
3466440Swnj 		}
3476440Swnj 		last = rindex(name, '/');
3486440Swnj 		if (last == 0)
3496440Swnj 			last = name;
3506440Swnj 		else
3516440Swnj 			last++;
35223101Slepreau 		if (pflag) {
35323101Slepreau 			/*
35423101Slepreau 			 * Make it compatible with possible future
35523101Slepreau 			 * versions expecting microseconds.
35623101Slepreau 			 */
35723101Slepreau 			(void) sprintf(buf, "T%ld 0 %ld 0\n",
35823101Slepreau 			    stb.st_mtime, stb.st_atime);
35923101Slepreau 			(void) write(rem, buf, strlen(buf));
36023101Slepreau 			if (response() < 0) {
36123101Slepreau 				(void) close(f);
36223101Slepreau 				continue;
36323101Slepreau 			}
36423101Slepreau 		}
36523101Slepreau 		(void) sprintf(buf, "C%04o %ld %s\n",
3666440Swnj 		    stb.st_mode&07777, stb.st_size, last);
3676440Swnj 		(void) write(rem, buf, strlen(buf));
3686440Swnj 		if (response() < 0) {
3696440Swnj 			(void) close(f);
3706440Swnj 			continue;
3716440Swnj 		}
37221254Smckusick 		if ((bp = allocbuf(&buffer, f, BUFSIZ)) < 0) {
37321254Smckusick 			(void) close(f);
37421254Smckusick 			continue;
37521254Smckusick 		}
37632268Sbostic 		readerr = 0;
37721254Smckusick 		for (i = 0; i < stb.st_size; i += bp->cnt) {
37821254Smckusick 			amt = bp->cnt;
3796440Swnj 			if (i + amt > stb.st_size)
3806440Swnj 				amt = stb.st_size - i;
38132268Sbostic 			if (readerr == 0 && read(f, bp->buf, amt) != amt)
38232268Sbostic 				readerr = errno;
38321254Smckusick 			(void) write(rem, bp->buf, amt);
3846440Swnj 		}
3856440Swnj 		(void) close(f);
38632268Sbostic 		if (readerr == 0)
3876440Swnj 			ga();
3886440Swnj 		else
38932268Sbostic 			error("rcp: %s: %s\n", name, sys_errlist[readerr]);
3906440Swnj 		(void) response();
3916440Swnj 	}
3926440Swnj }
3936440Swnj 
39413542Ssam #include <sys/dir.h>
3956440Swnj 
39623101Slepreau rsource(name, statp)
3976440Swnj 	char *name;
39823101Slepreau 	struct stat *statp;
3996440Swnj {
4006440Swnj 	DIR *d = opendir(name);
4016440Swnj 	char *last;
4026440Swnj 	struct direct *dp;
4036440Swnj 	char buf[BUFSIZ];
4046440Swnj 	char *bufv[1];
4056440Swnj 
4066440Swnj 	if (d == 0) {
40718126Sralph 		error("rcp: %s: %s\n", name, sys_errlist[errno]);
4086440Swnj 		return;
4096440Swnj 	}
4106440Swnj 	last = rindex(name, '/');
4116440Swnj 	if (last == 0)
4126440Swnj 		last = name;
4136440Swnj 	else
4146440Swnj 		last++;
41523101Slepreau 	if (pflag) {
41623101Slepreau 		(void) sprintf(buf, "T%ld 0 %ld 0\n",
41723101Slepreau 		    statp->st_mtime, statp->st_atime);
41823101Slepreau 		(void) write(rem, buf, strlen(buf));
41923101Slepreau 		if (response() < 0) {
42023101Slepreau 			closedir(d);
42123101Slepreau 			return;
42223101Slepreau 		}
42323101Slepreau 	}
42423101Slepreau 	(void) sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last);
4256440Swnj 	(void) write(rem, buf, strlen(buf));
4266440Swnj 	if (response() < 0) {
4276440Swnj 		closedir(d);
4286440Swnj 		return;
4296440Swnj 	}
4306440Swnj 	while (dp = readdir(d)) {
4316440Swnj 		if (dp->d_ino == 0)
4326440Swnj 			continue;
4336440Swnj 		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
4346440Swnj 			continue;
4356440Swnj 		if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
4366440Swnj 			error("%s/%s: Name too long.\n", name, dp->d_name);
4376440Swnj 			continue;
4386440Swnj 		}
4396440Swnj 		(void) sprintf(buf, "%s/%s", name, dp->d_name);
4406440Swnj 		bufv[0] = buf;
4416440Swnj 		source(1, bufv);
4426440Swnj 	}
4436440Swnj 	closedir(d);
4446440Swnj 	(void) write(rem, "E\n", 2);
4456440Swnj 	(void) response();
4466440Swnj }
4476440Swnj 
4486440Swnj response()
4496440Swnj {
4506440Swnj 	char resp, c, rbuf[BUFSIZ], *cp = rbuf;
4516440Swnj 
4526440Swnj 	if (read(rem, &resp, 1) != 1)
4536440Swnj 		lostconn();
4546440Swnj 	switch (resp) {
4556440Swnj 
45623101Slepreau 	case 0:				/* ok */
4576440Swnj 		return (0);
4586440Swnj 
4596440Swnj 	default:
4606440Swnj 		*cp++ = resp;
4616440Swnj 		/* fall into... */
46223101Slepreau 	case 1:				/* error, followed by err msg */
46323101Slepreau 	case 2:				/* fatal error, "" */
4646440Swnj 		do {
4656440Swnj 			if (read(rem, &c, 1) != 1)
4666440Swnj 				lostconn();
4676440Swnj 			*cp++ = c;
4686440Swnj 		} while (cp < &rbuf[BUFSIZ] && c != '\n');
4696440Swnj 		if (iamremote == 0)
4706440Swnj 			(void) write(2, rbuf, cp - rbuf);
4716440Swnj 		errs++;
4726440Swnj 		if (resp == 1)
4736440Swnj 			return (-1);
4746440Swnj 		exit(1);
4756440Swnj 	}
4766440Swnj 	/*NOTREACHED*/
4776440Swnj }
4786440Swnj 
4796440Swnj lostconn()
4806440Swnj {
4816440Swnj 
4826440Swnj 	if (iamremote == 0)
4836440Swnj 		fprintf(stderr, "rcp: lost connection\n");
4846440Swnj 	exit(1);
4856440Swnj }
4866440Swnj 
4876440Swnj sink(argc, argv)
4886440Swnj 	int argc;
4896440Swnj 	char **argv;
4906440Swnj {
49121254Smckusick 	off_t i, j;
49221254Smckusick 	char *targ, *whopp, *cp;
49321254Smckusick 	int of, mode, wrerr, exists, first, count, amt, size;
49421254Smckusick 	struct buffer *bp;
49521254Smckusick 	static struct buffer buffer;
49621254Smckusick 	struct stat stb;
49721254Smckusick 	int targisdir = 0;
4986440Swnj 	int mask = umask(0);
4996440Swnj 	char *myargv[1];
50023101Slepreau 	char cmdbuf[BUFSIZ], nambuf[BUFSIZ];
50123101Slepreau 	int setimes = 0;
50223101Slepreau 	struct timeval tv[2];
50323101Slepreau #define atime	tv[0]
50423101Slepreau #define mtime	tv[1]
50521254Smckusick #define	SCREWUP(str)	{ whopp = str; goto screwup; }
5066440Swnj 
50723112Slepreau 	if (!pflag)
50823112Slepreau 		(void) umask(mask);
50918126Sralph 	if (argc != 1) {
5106440Swnj 		error("rcp: ambiguous target\n");
5116440Swnj 		exit(1);
5126440Swnj 	}
5136440Swnj 	targ = *argv;
5146440Swnj 	if (targetshouldbedirectory)
5156440Swnj 		verifydir(targ);
5166440Swnj 	ga();
5176440Swnj 	if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
5186440Swnj 		targisdir = 1;
51914580Sralph 	for (first = 1; ; first = 0) {
5206440Swnj 		cp = cmdbuf;
5216440Swnj 		if (read(rem, cp, 1) <= 0)
5226440Swnj 			return;
5236440Swnj 		if (*cp++ == '\n')
5246440Swnj 			SCREWUP("unexpected '\\n'");
5256440Swnj 		do {
5266440Swnj 			if (read(rem, cp, 1) != 1)
5276440Swnj 				SCREWUP("lost connection");
5286440Swnj 		} while (*cp++ != '\n');
5296440Swnj 		*cp = 0;
5306440Swnj 		if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
5316440Swnj 			if (iamremote == 0)
53213542Ssam 				(void) write(2, cmdbuf+1, strlen(cmdbuf+1));
5336440Swnj 			if (cmdbuf[0] == '\02')
5346440Swnj 				exit(1);
5356440Swnj 			errs++;
5366440Swnj 			continue;
5376440Swnj 		}
5386440Swnj 		*--cp = 0;
5396440Swnj 		cp = cmdbuf;
5406440Swnj 		if (*cp == 'E') {
5416440Swnj 			ga();
5426440Swnj 			return;
5436440Swnj 		}
54423101Slepreau 
54523101Slepreau #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
54623101Slepreau 		if (*cp == 'T') {
54723101Slepreau 			setimes++;
54823101Slepreau 			cp++;
54923101Slepreau 			getnum(mtime.tv_sec);
55023101Slepreau 			if (*cp++ != ' ')
55123101Slepreau 				SCREWUP("mtime.sec not delimited");
55223101Slepreau 			getnum(mtime.tv_usec);
55323101Slepreau 			if (*cp++ != ' ')
55423101Slepreau 				SCREWUP("mtime.usec not delimited");
55523101Slepreau 			getnum(atime.tv_sec);
55623101Slepreau 			if (*cp++ != ' ')
55723101Slepreau 				SCREWUP("atime.sec not delimited");
55823101Slepreau 			getnum(atime.tv_usec);
55923101Slepreau 			if (*cp++ != '\0')
56023101Slepreau 				SCREWUP("atime.usec not delimited");
56123101Slepreau 			ga();
56223101Slepreau 			continue;
56323101Slepreau 		}
56414580Sralph 		if (*cp != 'C' && *cp != 'D') {
56514580Sralph 			/*
56614580Sralph 			 * Check for the case "rcp remote:foo\* local:bar".
56714580Sralph 			 * In this case, the line "No match." can be returned
56814580Sralph 			 * by the shell before the rcp command on the remote is
56914580Sralph 			 * executed so the ^Aerror_message convention isn't
57014580Sralph 			 * followed.
57114580Sralph 			 */
57214580Sralph 			if (first) {
57314580Sralph 				error("%s\n", cp);
57414580Sralph 				exit(1);
57514580Sralph 			}
5766440Swnj 			SCREWUP("expected control record");
57714580Sralph 		}
5786440Swnj 		cp++;
5796440Swnj 		mode = 0;
5806440Swnj 		for (; cp < cmdbuf+5; cp++) {
5816440Swnj 			if (*cp < '0' || *cp > '7')
5826440Swnj 				SCREWUP("bad mode");
5836440Swnj 			mode = (mode << 3) | (*cp - '0');
5846440Swnj 		}
5856440Swnj 		if (*cp++ != ' ')
5866440Swnj 			SCREWUP("mode not delimited");
5876440Swnj 		size = 0;
58823112Slepreau 		while (isdigit(*cp))
5896440Swnj 			size = size * 10 + (*cp++ - '0');
5906440Swnj 		if (*cp++ != ' ')
5916440Swnj 			SCREWUP("size not delimited");
5926440Swnj 		if (targisdir)
5936440Swnj 			(void) sprintf(nambuf, "%s%s%s", targ,
5946440Swnj 			    *targ ? "/" : "", cp);
5956440Swnj 		else
5966440Swnj 			(void) strcpy(nambuf, targ);
5976440Swnj 		exists = stat(nambuf, &stb) == 0;
59818126Sralph 		if (cmdbuf[0] == 'D') {
59918126Sralph 			if (exists) {
6006440Swnj 				if ((stb.st_mode&S_IFMT) != S_IFDIR) {
6016440Swnj 					errno = ENOTDIR;
6026440Swnj 					goto bad;
6036440Swnj 				}
60423112Slepreau 				if (pflag)
60523112Slepreau 					(void) chmod(nambuf, mode);
60618126Sralph 			} else if (mkdir(nambuf, mode) < 0)
6076440Swnj 				goto bad;
6086440Swnj 			myargv[0] = nambuf;
6096440Swnj 			sink(1, myargv);
61023101Slepreau 			if (setimes) {
61123101Slepreau 				setimes = 0;
61223101Slepreau 				if (utimes(nambuf, tv) < 0)
61323101Slepreau 					error("rcp: can't set times on %s: %s\n",
61423101Slepreau 					    nambuf, sys_errlist[errno]);
61523101Slepreau 			}
6166440Swnj 			continue;
61718126Sralph 		}
61832127Sbostic 		if ((of = open(nambuf, O_WRONLY|O_CREAT, mode)) < 0) {
6196440Swnj 	bad:
6206440Swnj 			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
6216440Swnj 			continue;
6226440Swnj 		}
62323112Slepreau 		if (exists && pflag)
62423112Slepreau 			(void) fchmod(of, mode);
6256440Swnj 		ga();
62621254Smckusick 		if ((bp = allocbuf(&buffer, of, BUFSIZ)) < 0) {
62723101Slepreau 			(void) close(of);
62821254Smckusick 			continue;
62921254Smckusick 		}
63021254Smckusick 		cp = bp->buf;
63121254Smckusick 		count = 0;
6326440Swnj 		wrerr = 0;
6336440Swnj 		for (i = 0; i < size; i += BUFSIZ) {
63421254Smckusick 			amt = BUFSIZ;
6356440Swnj 			if (i + amt > size)
6366440Swnj 				amt = size - i;
63721254Smckusick 			count += amt;
6386440Swnj 			do {
63921254Smckusick 				j = read(rem, cp, amt);
64024711Sbloom 				if (j <= 0) {
64124711Sbloom 					if (j == 0)
64224711Sbloom 					    error("rcp: dropped connection");
64324711Sbloom 					else
64424711Sbloom 					    error("rcp: %s\n",
64524711Sbloom 						sys_errlist[errno]);
6466440Swnj 					exit(1);
64724711Sbloom 				}
6486440Swnj 				amt -= j;
6496440Swnj 				cp += j;
6506440Swnj 			} while (amt > 0);
65121254Smckusick 			if (count == bp->cnt) {
65221254Smckusick 				if (wrerr == 0 &&
65321254Smckusick 				    write(of, bp->buf, count) != count)
65421254Smckusick 					wrerr++;
65521254Smckusick 				count = 0;
65621254Smckusick 				cp = bp->buf;
65721254Smckusick 			}
6586440Swnj 		}
65921254Smckusick 		if (count != 0 && wrerr == 0 &&
66021254Smckusick 		    write(of, bp->buf, count) != count)
66121254Smckusick 			wrerr++;
66232127Sbostic 		if (ftruncate(of, size))
66332127Sbostic 			error("rcp: can't truncate %s: %s\n",
66432127Sbostic 			    nambuf, sys_errlist[errno]);
6656440Swnj 		(void) close(of);
6666440Swnj 		(void) response();
66723101Slepreau 		if (setimes) {
66823101Slepreau 			setimes = 0;
66923101Slepreau 			if (utimes(nambuf, tv) < 0)
67023101Slepreau 				error("rcp: can't set times on %s: %s\n",
67123101Slepreau 				    nambuf, sys_errlist[errno]);
67223101Slepreau 		}
6736440Swnj 		if (wrerr)
67423101Slepreau 			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
6756440Swnj 		else
6766440Swnj 			ga();
6776440Swnj 	}
6786440Swnj screwup:
6796440Swnj 	error("rcp: protocol screwup: %s\n", whopp);
6806440Swnj 	exit(1);
6816440Swnj }
6826440Swnj 
68321254Smckusick struct buffer *
68421254Smckusick allocbuf(bp, fd, blksize)
68521254Smckusick 	struct buffer *bp;
68621254Smckusick 	int fd, blksize;
68721254Smckusick {
68821254Smckusick 	struct stat stb;
68921254Smckusick 	int size;
69021254Smckusick 
69121254Smckusick 	if (fstat(fd, &stb) < 0) {
69221254Smckusick 		error("rcp: fstat: %s\n", sys_errlist[errno]);
69321254Smckusick 		return ((struct buffer *)-1);
69421254Smckusick 	}
69521254Smckusick 	size = roundup(stb.st_blksize, blksize);
69621254Smckusick 	if (size == 0)
69721254Smckusick 		size = blksize;
69821254Smckusick 	if (bp->cnt < size) {
69921254Smckusick 		if (bp->buf != 0)
70021254Smckusick 			free(bp->buf);
70123101Slepreau 		bp->buf = (char *)malloc((unsigned) size);
70221254Smckusick 		if (bp->buf == 0) {
70321254Smckusick 			error("rcp: malloc: out of memory\n");
70421254Smckusick 			return ((struct buffer *)-1);
70521254Smckusick 		}
70621254Smckusick 	}
70721254Smckusick 	bp->cnt = size;
70821254Smckusick 	return (bp);
70921254Smckusick }
71021254Smckusick 
71123101Slepreau /*VARARGS1*/
7126440Swnj error(fmt, a1, a2, a3, a4, a5)
7136440Swnj 	char *fmt;
7146440Swnj 	int a1, a2, a3, a4, a5;
7156440Swnj {
7166440Swnj 	char buf[BUFSIZ], *cp = buf;
7176440Swnj 
7186440Swnj 	errs++;
7196440Swnj 	*cp++ = 1;
7206440Swnj 	(void) sprintf(cp, fmt, a1, a2, a3, a4, a5);
7216440Swnj 	(void) write(rem, buf, strlen(buf));
7226440Swnj 	if (iamremote == 0)
7236440Swnj 		(void) write(2, buf+1, strlen(buf+1));
7246440Swnj }
72532127Sbostic 
72632127Sbostic usage()
72732127Sbostic {
72832127Sbostic 	fputs("usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n", stderr);
72932127Sbostic 	exit(1);
73032127Sbostic }
731