rework susystem
[unix-history] / usr / src / bin / rcp / rcp.c
CommitLineData
22e155fc 1/*
cd5a06d8
KB
2 * Copyright (c) 1983, 1990, 1992, 1993
3 * The Regents of the University of California. All rights reserved.
ebb479fc 4 *
27c71911 5 * %sccs.include.redist.c%
22e155fc
DF
6 */
7
8#ifndef lint
cd5a06d8
KB
9static char copyright[] =
10"@(#) Copyright (c) 1983, 1990, 1992, 1993\n\
11 The Regents of the University of California. All rights reserved.\n";
ebb479fc 12#endif /* not lint */
22e155fc 13
b7ff3bb3 14#ifndef lint
cd5a06d8 15static char sccsid[] = "@(#)rcp.c 8.1 (Berkeley) %G%";
ebb479fc 16#endif /* not lint */
b7ff3bb3 17
ed0072ae 18#include <sys/param.h>
b7ff3bb3 19#include <sys/stat.h>
af86e024 20#include <sys/time.h>
a045d3a2 21#include <sys/socket.h>
94a2d2a7 22#include <netinet/in.h>
f8b77578
MK
23#include <netinet/in_systm.h>
24#include <netinet/ip.h>
0b35f0fe 25
bd7da30a 26#include <ctype.h>
a045d3a2 27#include <dirent.h>
8809a3e2 28#include <err.h>
bd7da30a 29#include <errno.h>
a045d3a2 30#include <fcntl.h>
bd7da30a 31#include <netdb.h>
b7ff3bb3 32#include <pwd.h>
bd7da30a 33#include <signal.h>
35673a6d 34#include <stdio.h>
42029053 35#include <stdlib.h>
a045d3a2 36#include <string.h>
bd7da30a
KB
37#include <string.h>
38#include <unistd.h>
39
e4ba000a 40#include "pathnames.h"
0b35f0fe 41#include "extern.h"
4ca10280 42
35673a6d 43#ifdef KERBEROS
1b8d273e 44#include <kerberosIV/des.h>
579f51c2 45#include <kerberosIV/krb.h>
0b35f0fe 46
5d178979
KF
47char dst_realm_buf[REALM_SZ];
48char *dest_realm = NULL;
898015bc 49int use_kerberos = 1;
5d178979
KF
50CREDENTIALS cred;
51Key_schedule schedule;
52extern char *krb_realmofhost();
0b35f0fe 53#define OPTIONS "dfKk:prt"
898015bc 54#else
5d178979 55#define OPTIONS "dfprt"
2b6b7619
KF
56#endif
57
35673a6d 58struct passwd *pwd;
5d178979
KF
59u_short port;
60uid_t userid;
61int errs, rem;
62int pflag, iamremote, iamrecursive, targetshouldbedirectory;
371655cc 63
5d178979 64#define CMDNEEDS 64
d4c44137 65char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */
b7ff3bb3 66
0b35f0fe
KB
67#ifdef KERBEROS
68int kerberos __P((char **, char *, char *, char *));
69void oldw __P((const char *, ...));
70#endif
71int response __P((void));
72void rsource __P((char *, struct stat *));
73void sink __P((int, char *[]));
74void source __P((int, char *[]));
75void tolocal __P((int, char *[]));
76void toremote __P((char *, int, char *[]));
77void usage __P((void));
a045d3a2 78
0b35f0fe 79int
b7ff3bb3
BJ
80main(argc, argv)
81 int argc;
0b35f0fe 82 char *argv[];
b7ff3bb3 83{
11bfe9c9 84 struct servent *sp;
f95b553a 85 int ch, fflag, tflag;
0b35f0fe 86 char *targ, *shell;
92e58d95 87
f95b553a 88 fflag = tflag = 0;
5d178979 89 while ((ch = getopt(argc, argv, OPTIONS)) != EOF)
0b35f0fe
KB
90 switch(ch) { /* User-visible flags. */
91 case 'K':
92#ifdef KERBEROS
93 use_kerberos = 0;
94#endif
af86e024 95 break;
5d178979
KF
96#ifdef KERBEROS
97 case 'k':
5d178979 98 dest_realm = dst_realm_buf;
0b35f0fe 99 (void)strncpy(dst_realm_buf, optarg, REALM_SZ);
f95b553a 100 break;
35673a6d 101#endif
0b35f0fe
KB
102 case 'p':
103 pflag = 1;
104 break;
105 case 'r':
106 iamrecursive = 1;
107 break;
108 /* Server options. */
5d178979
KF
109 case 'd':
110 targetshouldbedirectory = 1;
111 break;
112 case 'f': /* "from" */
113 iamremote = 1;
114 fflag = 1;
115 break;
116 case 't': /* "to" */
117 iamremote = 1;
118 tflag = 1;
119 break;
35673a6d
KB
120 case '?':
121 default:
5f286db0 122 usage();
af86e024 123 }
35673a6d
KB
124 argc -= optind;
125 argv += optind;
126
5d178979 127#ifdef KERBEROS
0b35f0fe
KB
128 if (use_kerberos) {
129 shell = "kshell";
130 if ((sp = getservbyname(shell, "tcp")) == NULL) {
131 use_kerberos = 0;
132 oldw("can't get entry for %s/tcp service", shell);
133 sp = getservbyname(shell = "shell", "tcp");
134 }
135 } else
42029053 136 sp = getservbyname(shell = "shell", "tcp");
5d178979 137#else
42029053 138 sp = getservbyname(shell = "shell", "tcp");
5d178979 139#endif
8809a3e2
KB
140 if (sp == NULL)
141 errx(1, "%s/tcp: unknown service", shell);
5d178979
KF
142 port = sp->s_port;
143
8809a3e2
KB
144 if ((pwd = getpwuid(userid = getuid())) == NULL)
145 errx(1, "unknown user %d", (int)userid);
5d178979 146
0b35f0fe
KB
147 rem = STDIN_FILENO; /* XXX */
148
149 if (fflag) { /* Follow "protocol", send data. */
f95b553a
KB
150 (void)response();
151 (void)setuid(userid);
152 source(argc, argv);
153 exit(errs);
154 }
155
0b35f0fe 156 if (tflag) { /* Receive data. */
f95b553a
KB
157 (void)setuid(userid);
158 sink(argc, argv);
159 exit(errs);
160 }
161
5f286db0
KB
162 if (argc < 2)
163 usage();
b7ff3bb3
BJ
164 if (argc > 2)
165 targetshouldbedirectory = 1;
35673a6d 166
5f286db0 167 rem = -1;
0b35f0fe 168 /* Command to be executed on remote system using "rsh". */
5d178979 169#ifdef KERBEROS
a045d3a2
KB
170 (void)snprintf(cmd, sizeof(cmd),
171 "rcp%s%s%s%s", iamrecursive ? " -r" : "",
898015bc 172 "",
5d178979
KF
173 pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "");
174#else
a045d3a2
KB
175 (void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s",
176 iamrecursive ? " -r" : "", pflag ? " -p" : "",
177 targetshouldbedirectory ? " -d" : "");
5d178979 178#endif
35673a6d
KB
179
180 (void)signal(SIGPIPE, lostconn);
181
0b35f0fe
KB
182 if (targ = colon(argv[argc - 1])) /* Dest is remote host. */
183 toremote(targ, argc, argv);
35673a6d 184 else {
0b35f0fe 185 tolocal(argc, argv); /* Dest is local host. */
35673a6d
KB
186 if (targetshouldbedirectory)
187 verifydir(argv[argc - 1]);
188 }
189 exit(errs);
190}
191
0b35f0fe 192void
35673a6d 193toremote(targ, argc, argv)
0b35f0fe 194 char *targ, *argv[];
35673a6d 195 int argc;
35673a6d 196{
a045d3a2 197 int i, len, tos;
d4c44137 198 char *bp, *host, *src, *suser, *thost, *tuser;
35673a6d
KB
199
200 *targ++ = 0;
201 if (*targ == 0)
202 targ = ".";
203
8809a3e2 204 if (thost = strchr(argv[argc - 1], '@')) {
5d178979 205 /* user@host */
35673a6d
KB
206 *thost++ = 0;
207 tuser = argv[argc - 1];
208 if (*tuser == '\0')
9e9252a8 209 tuser = NULL;
35673a6d
KB
210 else if (!okname(tuser))
211 exit(1);
212 } else {
213 thost = argv[argc - 1];
214 tuser = NULL;
215 }
2b6b7619 216
35673a6d
KB
217 for (i = 0; i < argc - 1; i++) {
218 src = colon(argv[i]);
219 if (src) { /* remote to remote */
220 *src++ = 0;
221 if (*src == 0)
222 src = ".";
8809a3e2 223 host = strchr(argv[i], '@');
a045d3a2
KB
224 len = strlen(_PATH_RSH) + strlen(argv[i]) +
225 strlen(src) + (tuser ? strlen(tuser) : 0) +
226 strlen(thost) + strlen(targ) + CMDNEEDS + 20;
227 if (!(bp = malloc(len)))
8809a3e2 228 err(1, NULL);
35673a6d
KB
229 if (host) {
230 *host++ = 0;
231 suser = argv[i];
232 if (*suser == '\0')
233 suser = pwd->pw_name;
234 else if (!okname(suser))
235 continue;
a045d3a2 236 (void)snprintf(bp, len,
35673a6d
KB
237 "%s %s -l %s -n %s %s '%s%s%s:%s'",
238 _PATH_RSH, host, suser, cmd, src,
239 tuser ? tuser : "", tuser ? "@" : "",
240 thost, targ);
241 } else
a045d3a2
KB
242 (void)snprintf(bp, len,
243 "%s %s -n %s %s '%s%s%s:%s'",
35673a6d
KB
244 _PATH_RSH, argv[i], cmd, src,
245 tuser ? tuser : "", tuser ? "@" : "",
246 thost, targ);
0b35f0fe 247 (void)susystem(bp, userid);
d4c44137 248 (void)free(bp);
35673a6d
KB
249 } else { /* local to remote */
250 if (rem == -1) {
a045d3a2
KB
251 len = strlen(targ) + CMDNEEDS + 20;
252 if (!(bp = malloc(len)))
8809a3e2 253 err(1, NULL);
a045d3a2 254 (void)snprintf(bp, len, "%s -t %s", cmd, targ);
35673a6d
KB
255 host = thost;
256#ifdef KERBEROS
257 if (use_kerberos)
5d178979
KF
258 rem = kerberos(&host, bp,
259 pwd->pw_name,
d4c44137 260 tuser ? tuser : pwd->pw_name);
35673a6d
KB
261 else
262#endif
9e9252a8
JB
263 rem = rcmd(&host, port, pwd->pw_name,
264 tuser ? tuser : pwd->pw_name,
d4c44137 265 bp, 0);
35673a6d
KB
266 if (rem < 0)
267 exit(1);
f8b77578
MK
268 tos = IPTOS_THROUGHPUT;
269 if (setsockopt(rem, IPPROTO_IP, IP_TOS,
0b35f0fe 270 &tos, sizeof(int)) < 0)
8809a3e2 271 warn("TOS (ignored)");
35673a6d
KB
272 if (response() < 0)
273 exit(1);
d4c44137 274 (void)free(bp);
35673a6d 275 (void)setuid(userid);
b7ff3bb3 276 }
35673a6d 277 source(1, argv+i);
b7ff3bb3 278 }
35673a6d
KB
279 }
280}
281
0b35f0fe 282void
35673a6d
KB
283tolocal(argc, argv)
284 int argc;
0b35f0fe 285 char *argv[];
35673a6d 286{
a045d3a2 287 int i, len, tos;
d4c44137 288 char *bp, *host, *src, *suser;
35673a6d
KB
289
290 for (i = 0; i < argc - 1; i++) {
0b35f0fe 291 if (!(src = colon(argv[i]))) { /* Local to local. */
a045d3a2
KB
292 len = strlen(_PATH_CP) + strlen(argv[i]) +
293 strlen(argv[argc - 1]) + 20;
294 if (!(bp = malloc(len)))
8809a3e2 295 err(1, NULL);
a045d3a2 296 (void)snprintf(bp, len, "%s%s%s %s %s", _PATH_CP,
d4c44137
KB
297 iamrecursive ? " -r" : "", pflag ? " -p" : "",
298 argv[i], argv[argc - 1]);
83cb1dc5
AC
299 if (susystem(bp, userid))
300 ++errs;
d4c44137
KB
301 (void)free(bp);
302 continue;
303 }
304 *src++ = 0;
305 if (*src == 0)
306 src = ".";
8809a3e2 307 if ((host = strchr(argv[i], '@')) == NULL) {
0b35f0fe
KB
308 host = argv[i];
309 suser = pwd->pw_name;
310 } else {
d4c44137
KB
311 *host++ = 0;
312 suser = argv[i];
313 if (*suser == '\0')
35673a6d 314 suser = pwd->pw_name;
d4c44137 315 else if (!okname(suser))
35673a6d 316 continue;
b7ff3bb3 317 }
a045d3a2 318 len = strlen(src) + CMDNEEDS + 20;
0b35f0fe 319 if ((bp = malloc(len)) == NULL)
8809a3e2 320 err(1, NULL);
a045d3a2 321 (void)snprintf(bp, len, "%s -f %s", cmd, src);
0b35f0fe 322 rem =
d4c44137 323#ifdef KERBEROS
0b35f0fe
KB
324 use_kerberos ?
325 kerberos(&host, bp, pwd->pw_name, suser) :
d4c44137 326#endif
0b35f0fe 327 rcmd(&host, port, pwd->pw_name, suser, bp, 0);
d4c44137 328 (void)free(bp);
83cb1dc5
AC
329 if (rem < 0) {
330 ++errs;
d4c44137 331 continue;
83cb1dc5 332 }
f8b77578
MK
333 (void)seteuid(userid);
334 tos = IPTOS_THROUGHPUT;
0b35f0fe 335 if (setsockopt(rem, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0)
8809a3e2 336 warn("TOS (ignored)");
d4c44137 337 sink(1, argv + argc - 1);
f8b77578 338 (void)seteuid(0);
d4c44137
KB
339 (void)close(rem);
340 rem = -1;
b7ff3bb3 341 }
b7ff3bb3
BJ
342}
343
0b35f0fe 344void
b7ff3bb3
BJ
345source(argc, argv)
346 int argc;
0b35f0fe 347 char *argv[];
b7ff3bb3 348{
b7ff3bb3 349 struct stat stb;
35673a6d
KB
350 static BUF buffer;
351 BUF *bp;
5f10dd79 352 off_t i;
83cb1dc5 353 int amt, fd, haderr, indx, result;
35673a6d 354 char *last, *name, buf[BUFSIZ];
b7ff3bb3 355
83cb1dc5
AC
356 for (indx = 0; indx < argc; ++indx) {
357 name = argv[indx];
0b35f0fe
KB
358 if ((fd = open(name, O_RDONLY, 0)) < 0)
359 goto syserr;
360 if (fstat(fd, &stb)) {
8809a3e2 361syserr: run_err("%s: %s", name, strerror(errno));
0b35f0fe 362 goto next;
b7ff3bb3 363 }
0b35f0fe 364 switch (stb.st_mode & S_IFMT) {
b7ff3bb3
BJ
365 case S_IFREG:
366 break;
b7ff3bb3
BJ
367 case S_IFDIR:
368 if (iamrecursive) {
af86e024 369 rsource(name, &stb);
0b35f0fe 370 goto next;
b7ff3bb3 371 }
35673a6d 372 /* FALLTHROUGH */
b7ff3bb3 373 default:
8809a3e2 374 run_err("%s: not a regular file", name);
0b35f0fe 375 goto next;
b7ff3bb3 376 }
8809a3e2 377 if ((last = strrchr(name, '/')) == NULL)
b7ff3bb3
BJ
378 last = name;
379 else
0b35f0fe 380 ++last;
af86e024
JL
381 if (pflag) {
382 /*
383 * Make it compatible with possible future
384 * versions expecting microseconds.
385 */
bd7da30a 386 (void)snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n",
0b35f0fe 387 stb.st_mtimespec.ts_sec, stb.st_atimespec.ts_sec);
bd7da30a 388 (void)write(rem, buf, strlen(buf));
0b35f0fe
KB
389 if (response() < 0)
390 goto next;
371655cc 391 }
0b35f0fe 392#define MODEMASK (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
bd7da30a 393 (void)snprintf(buf, sizeof(buf), "C%04o %qd %s\n",
0b35f0fe 394 stb.st_mode & MODEMASK, stb.st_size, last);
bd7da30a 395 (void)write(rem, buf, strlen(buf));
0b35f0fe
KB
396 if (response() < 0)
397 goto next;
83cb1dc5
AC
398 if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) {
399next: (void)close(fd);
400 continue;
401 }
402
403 /* Keep writing after an error so that we stay sync'd up. */
404 for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
371655cc 405 amt = bp->cnt;
b7ff3bb3
BJ
406 if (i + amt > stb.st_size)
407 amt = stb.st_size - i;
83cb1dc5
AC
408 if (!haderr) {
409 result = read(fd, bp->buf, amt);
410 if (result != amt)
411 haderr = result >= 0 ? EIO : errno;
412 }
413 if (haderr)
414 (void)write(rem, bp->buf, amt);
415 else {
416 result = write(rem, bp->buf, amt);
417 if (result != amt)
418 haderr = result >= 0 ? EIO : errno;
0b35f0fe 419 }
b7ff3bb3 420 }
83cb1dc5
AC
421 if (close(fd) && !haderr)
422 haderr = errno;
423 if (!haderr)
35673a6d 424 (void)write(rem, "", 1);
83cb1dc5 425 else
8809a3e2 426 run_err("%s: %s", name, strerror(haderr));
35673a6d 427 (void)response();
b7ff3bb3
BJ
428 }
429}
430
0b35f0fe 431void
af86e024 432rsource(name, statp)
b7ff3bb3 433 char *name;
af86e024 434 struct stat *statp;
b7ff3bb3 435{
a045d3a2
KB
436 DIR *dirp;
437 struct dirent *dp;
d4c44137 438 char *last, *vect[1], path[MAXPATHLEN];
b7ff3bb3 439
a045d3a2 440 if (!(dirp = opendir(name))) {
8809a3e2 441 run_err("%s: %s", name, strerror(errno));
b7ff3bb3
BJ
442 return;
443 }
8809a3e2 444 last = strrchr(name, '/');
b7ff3bb3
BJ
445 if (last == 0)
446 last = name;
447 else
448 last++;
af86e024 449 if (pflag) {
bd7da30a 450 (void)snprintf(path, sizeof(path), "T%ld 0 %ld 0\n",
0b35f0fe 451 statp->st_mtimespec.ts_sec, statp->st_atimespec.ts_sec);
bd7da30a 452 (void)write(rem, path, strlen(path));
af86e024 453 if (response() < 0) {
a045d3a2 454 closedir(dirp);
af86e024
JL
455 return;
456 }
457 }
bd7da30a 458 (void)snprintf(path, sizeof(path),
0b35f0fe 459 "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last);
bd7da30a 460 (void)write(rem, path, strlen(path));
b7ff3bb3 461 if (response() < 0) {
a045d3a2 462 closedir(dirp);
b7ff3bb3
BJ
463 return;
464 }
a045d3a2 465 while (dp = readdir(dirp)) {
b7ff3bb3
BJ
466 if (dp->d_ino == 0)
467 continue;
468 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
469 continue;
d4c44137 470 if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) {
8809a3e2 471 run_err("%s/%s: name too long", name, dp->d_name);
b7ff3bb3
BJ
472 continue;
473 }
a045d3a2 474 (void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
d4c44137
KB
475 vect[0] = path;
476 source(1, vect);
b7ff3bb3 477 }
0b35f0fe 478 (void)closedir(dirp);
35673a6d
KB
479 (void)write(rem, "E\n", 2);
480 (void)response();
b7ff3bb3
BJ
481}
482
a045d3a2 483void
b7ff3bb3
BJ
484sink(argc, argv)
485 int argc;
0b35f0fe 486 char *argv[];
b7ff3bb3 487{
35673a6d 488 static BUF buffer;
0b35f0fe 489 register char *cp;
371655cc 490 struct stat stb;
af86e024 491 struct timeval tv[2];
ef49bf4f 492 enum { YES, NO, DISPLAYED } wrerr;
0b35f0fe 493 BUF *bp;
d4c44137 494 off_t i, j;
83cb1dc5
AC
495 int amt, count, exists, first, mask, mode, ofd, omode;
496 int setimes, size, targisdir, wrerrno;
497 char ch, *np, *targ, *why, *vect[1], buf[BUFSIZ];
b7ff3bb3 498
d4c44137
KB
499#define atime tv[0]
500#define mtime tv[1]
501#define SCREWUP(str) { why = str; goto screwup; }
502
503 setimes = targisdir = 0;
504 mask = umask(0);
a4372a41 505 if (!pflag)
35673a6d 506 (void)umask(mask);
11bfe9c9 507 if (argc != 1) {
8809a3e2 508 run_err("ambiguous target");
b7ff3bb3
BJ
509 exit(1);
510 }
511 targ = *argv;
512 if (targetshouldbedirectory)
513 verifydir(targ);
35673a6d 514 (void)write(rem, "", 1);
0b35f0fe 515 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
b7ff3bb3 516 targisdir = 1;
d4c44137
KB
517 for (first = 1;; first = 0) {
518 cp = buf;
b7ff3bb3
BJ
519 if (read(rem, cp, 1) <= 0)
520 return;
521 if (*cp++ == '\n')
d4c44137 522 SCREWUP("unexpected <newline>");
b7ff3bb3 523 do {
d4c44137 524 if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
b7ff3bb3 525 SCREWUP("lost connection");
d4c44137
KB
526 *cp++ = ch;
527 } while (cp < &buf[BUFSIZ - 1] && ch != '\n');
b7ff3bb3 528 *cp = 0;
d4c44137
KB
529
530 if (buf[0] == '\01' || buf[0] == '\02') {
b7ff3bb3 531 if (iamremote == 0)
0b35f0fe
KB
532 (void)write(STDERR_FILENO,
533 buf + 1, strlen(buf + 1));
d4c44137 534 if (buf[0] == '\02')
b7ff3bb3 535 exit(1);
0b35f0fe 536 ++errs;
b7ff3bb3
BJ
537 continue;
538 }
d4c44137 539 if (buf[0] == 'E') {
35673a6d 540 (void)write(rem, "", 1);
b7ff3bb3
BJ
541 return;
542 }
af86e024 543
3eafec57 544 if (ch == '\n')
6f6da275 545 *--cp = 0;
3eafec57 546
af86e024 547#define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
d4c44137 548 cp = buf;
af86e024
JL
549 if (*cp == 'T') {
550 setimes++;
551 cp++;
552 getnum(mtime.tv_sec);
553 if (*cp++ != ' ')
554 SCREWUP("mtime.sec not delimited");
555 getnum(mtime.tv_usec);
556 if (*cp++ != ' ')
557 SCREWUP("mtime.usec not delimited");
558 getnum(atime.tv_sec);
559 if (*cp++ != ' ')
560 SCREWUP("atime.sec not delimited");
561 getnum(atime.tv_usec);
562 if (*cp++ != '\0')
563 SCREWUP("atime.usec not delimited");
35673a6d 564 (void)write(rem, "", 1);
af86e024
JL
565 continue;
566 }
4550ab9e
RC
567 if (*cp != 'C' && *cp != 'D') {
568 /*
569 * Check for the case "rcp remote:foo\* local:bar".
570 * In this case, the line "No match." can be returned
571 * by the shell before the rcp command on the remote is
572 * executed so the ^Aerror_message convention isn't
573 * followed.
574 */
575 if (first) {
8809a3e2 576 run_err("%s", cp);
4550ab9e
RC
577 exit(1);
578 }
b7ff3bb3 579 SCREWUP("expected control record");
4550ab9e 580 }
b7ff3bb3 581 mode = 0;
d4c44137 582 for (++cp; cp < buf + 5; cp++) {
b7ff3bb3
BJ
583 if (*cp < '0' || *cp > '7')
584 SCREWUP("bad mode");
585 mode = (mode << 3) | (*cp - '0');
586 }
587 if (*cp++ != ' ')
588 SCREWUP("mode not delimited");
0b35f0fe
KB
589
590 for (size = 0; isdigit(*cp);)
b7ff3bb3
BJ
591 size = size * 10 + (*cp++ - '0');
592 if (*cp++ != ' ')
593 SCREWUP("size not delimited");
d4c44137
KB
594 if (targisdir) {
595 static char *namebuf;
596 static int cursize;
42029053 597 size_t need;
d4c44137
KB
598
599 need = strlen(targ) + strlen(cp) + 250;
600 if (need > cursize) {
42029053 601 if (!(namebuf = malloc(need)))
8809a3e2 602 run_err("%s", strerror(errno));
d4c44137 603 }
a045d3a2 604 (void)snprintf(namebuf, need, "%s%s%s", targ,
b7ff3bb3 605 *targ ? "/" : "", cp);
d4c44137 606 np = namebuf;
83cb1dc5 607 } else
d4c44137
KB
608 np = targ;
609 exists = stat(np, &stb) == 0;
610 if (buf[0] == 'D') {
83cb1dc5 611 int mod_flag = pflag;
11bfe9c9 612 if (exists) {
0b35f0fe 613 if (!S_ISDIR(stb.st_mode)) {
b7ff3bb3
BJ
614 errno = ENOTDIR;
615 goto bad;
616 }
a4372a41 617 if (pflag)
d4c44137 618 (void)chmod(np, mode);
83cb1dc5
AC
619 } else {
620 /* Handle copying from a read-only directory */
621 mod_flag = 1;
622 if (mkdir(np, mode | S_IRWXU) < 0)
623 goto bad;
624 }
d4c44137
KB
625 vect[0] = np;
626 sink(1, vect);
af86e024
JL
627 if (setimes) {
628 setimes = 0;
d4c44137 629 if (utimes(np, tv) < 0)
8809a3e2 630 run_err("%s: set times: %s",
ef49bf4f 631 np, strerror(errno));
af86e024 632 }
83cb1dc5
AC
633 if (mod_flag)
634 (void)chmod(np, mode);
b7ff3bb3 635 continue;
11bfe9c9 636 }
83cb1dc5
AC
637 omode = mode;
638 mode |= S_IWRITE;
d4c44137 639 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
8809a3e2 640bad: run_err("%s: %s", np, strerror(errno));
b7ff3bb3 641 continue;
b7ff3bb3 642 }
35673a6d 643 (void)write(rem, "", 1);
0b35f0fe 644 if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) {
d4c44137 645 (void)close(ofd);
371655cc
KM
646 continue;
647 }
648 cp = bp->buf;
ef49bf4f 649 wrerr = NO;
83cb1dc5 650 for (count = i = 0; i < size; i += BUFSIZ) {
371655cc 651 amt = BUFSIZ;
b7ff3bb3
BJ
652 if (i + amt > size)
653 amt = size - i;
371655cc 654 count += amt;
b7ff3bb3 655 do {
371655cc 656 j = read(rem, cp, amt);
9e9252a8 657 if (j <= 0) {
8809a3e2 658 run_err("%s", j ? strerror(errno) :
d4c44137 659 "dropped connection");
b7ff3bb3 660 exit(1);
9e9252a8 661 }
b7ff3bb3
BJ
662 amt -= j;
663 cp += j;
664 } while (amt > 0);
371655cc 665 if (count == bp->cnt) {
83cb1dc5
AC
666 /* Keep reading so we stay sync'd up. */
667 if (wrerr == NO) {
668 j = write(ofd, bp->buf, count);
669 if (j != count) {
670 wrerr = YES;
671 wrerrno = j >= 0 ? EIO : errno;
672 }
673 }
371655cc
KM
674 count = 0;
675 cp = bp->buf;
676 }
b7ff3bb3 677 }
ef49bf4f 678 if (count != 0 && wrerr == NO &&
bd7da30a 679 (j = write(ofd, bp->buf, count)) != count) {
ef49bf4f 680 wrerr = YES;
bd7da30a
KB
681 wrerrno = j >= 0 ? EIO : errno;
682 }
ef49bf4f 683 if (ftruncate(ofd, size)) {
8809a3e2 684 run_err("%s: truncate: %s", np, strerror(errno));
ef49bf4f
KB
685 wrerr = DISPLAYED;
686 }
83cb1dc5
AC
687 if (pflag) {
688 if (exists || omode != mode)
8809a3e2
KB
689 if (fchmod(ofd, omode))
690 run_err("%s: set mode: %s",
691 np, strerror(errno));
83cb1dc5
AC
692 } else {
693 if (!exists && omode != mode)
8809a3e2
KB
694 if (fchmod(ofd, omode & ~mask))
695 run_err("%s: set mode: %s",
696 np, strerror(errno));
83cb1dc5 697 }
d4c44137 698 (void)close(ofd);
35673a6d 699 (void)response();
ef49bf4f 700 if (setimes && wrerr == NO) {
af86e024 701 setimes = 0;
ef49bf4f 702 if (utimes(np, tv) < 0) {
8809a3e2 703 run_err("%s: set times: %s",
ef49bf4f
KB
704 np, strerror(errno));
705 wrerr = DISPLAYED;
706 }
707 }
708 switch(wrerr) {
709 case YES:
8809a3e2 710 run_err("%s: %s", np, strerror(wrerrno));
ef49bf4f
KB
711 break;
712 case NO:
35673a6d 713 (void)write(rem, "", 1);
ef49bf4f
KB
714 break;
715 case DISPLAYED:
716 break;
717 }
b7ff3bb3
BJ
718 }
719screwup:
8809a3e2 720 run_err("protocol error: %s", why);
5f286db0
KB
721 exit(1);
722}
5d178979
KF
723
724#ifdef KERBEROS
5d178979
KF
725int
726kerberos(host, bp, locuser, user)
5d178979
KF
727 char **host, *bp, *locuser, *user;
728{
729 struct servent *sp;
730
731again:
732 if (use_kerberos) {
733 rem = KSUCCESS;
734 errno = 0;
735 if (dest_realm == NULL)
736 dest_realm = krb_realmofhost(*host);
0b35f0fe
KB
737 rem =
738 krcmd(host, port, user, bp, 0, dest_realm);
5d178979
KF
739
740 if (rem < 0) {
741 use_kerberos = 0;
8809a3e2
KB
742 if ((sp = getservbyname("shell", "tcp")) == NULL)
743 errx(1, "unknown service shell/tcp");
5d178979 744 if (errno == ECONNREFUSED)
0b35f0fe
KB
745 oldw("remote host doesn't support Kerberos");
746 else if (errno == ENOENT)
747 oldw("can't provide Kerberos authentication data");
5d178979
KF
748 port = sp->s_port;
749 goto again;
750 }
751 } else {
bd7da30a 752 rem = rcmd(host, port, locuser, user, bp, 0);
5d178979 753 }
0b35f0fe 754 return (rem);
5d178979
KF
755}
756#endif /* KERBEROS */
0b35f0fe
KB
757
758int
759response()
760{
761 register char *cp;
762 char ch, resp, rbuf[BUFSIZ];
763
764 if (read(rem, &resp, sizeof(resp)) != sizeof(resp))
765 lostconn(0);
766
767 cp = rbuf;
768 switch(resp) {
769 case 0: /* ok */
770 return (0);
771 default:
772 *cp++ = resp;
773 /* FALLTHROUGH */
774 case 1: /* error, followed by error msg */
775 case 2: /* fatal error, "" */
776 do {
777 if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
778 lostconn(0);
779 *cp++ = ch;
780 } while (cp < &rbuf[BUFSIZ] && ch != '\n');
781
782 if (!iamremote)
783 (void)write(STDERR_FILENO, rbuf, cp - rbuf);
784 ++errs;
785 if (resp == 1)
786 return (-1);
787 exit(1);
788 }
789 /* NOTREACHED */
790}
791
792void
793usage()
794{
795#ifdef KERBEROS
0b35f0fe
KB
796 (void)fprintf(stderr, "%s\n\t%s\n",
797 "usage: rcp [-Kp] [-k realm] f1 f2",
798 "or: rcp [-Kpr] [-k realm] f1 ... fn directory");
0b35f0fe
KB
799#else
800 (void)fprintf(stderr,
801 "usage: rcp [-p] f1 f2; or: rcp [-pr] f1 ... fn directory\n");
802#endif
803 exit(1);
804}
805
806#if __STDC__
807#include <stdarg.h>
808#else
809#include <varargs.h>
810#endif
811
812#ifdef KERBEROS
813void
814#if __STDC__
815oldw(const char *fmt, ...)
816#else
817oldw(fmt, va_alist)
818 char *fmt;
819 va_dcl
820#endif
821{
822 va_list ap;
823#if __STDC__
824 va_start(ap, fmt);
825#else
826 va_start(ap);
827#endif
828 (void)fprintf(stderr, "rcp: ");
829 (void)vfprintf(stderr, fmt, ap);
830 (void)fprintf(stderr, ", using standard rcp\n");
831 va_end(ap);
832}
833#endif
834
835void
836#if __STDC__
8809a3e2 837run_err(const char *fmt, ...)
0b35f0fe 838#else
8809a3e2 839run_err(fmt, va_alist)
0b35f0fe
KB
840 char *fmt;
841 va_dcl
842#endif
843{
844 static FILE *fp;
845 va_list ap;
846#if __STDC__
847 va_start(ap, fmt);
848#else
849 va_start(ap);
850#endif
851
852 ++errs;
8809a3e2 853 if (fp == NULL && !(fp = fdopen(rem, "w")))
0b35f0fe
KB
854 return;
855 (void)fprintf(fp, "%c", 0x01);
856 (void)fprintf(fp, "rcp: ");
857 (void)vfprintf(fp, fmt, ap);
858 (void)fprintf(fp, "\n");
0b35f0fe 859 (void)fflush(fp);
8809a3e2
KB
860
861 if (!iamremote)
862 vwarnx(fmt, ap);
863
864 va_end(ap);
0b35f0fe 865}