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