This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / bin / rcp / rcp.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1983, 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35char copyright[] =
36"@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\
37 All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)rcp.c 5.32 (Berkeley) 2/25/91";
42#endif /* not lint */
43
44/*
45 * rcp
46 */
47#include <sys/param.h>
48#include <sys/stat.h>
49#include <sys/time.h>
50#include <sys/ioctl.h>
51#include <sys/socket.h>
52#include <sys/wait.h>
53#include <netinet/in.h>
54#include <netinet/in_systm.h>
55#include <netinet/ip.h>
56#include <dirent.h>
57#include <fcntl.h>
58#include <signal.h>
59#include <pwd.h>
60#include <netdb.h>
61#include <errno.h>
62#include <unistd.h>
63#include <stdio.h>
64#include <stdlib.h>
65#include <string.h>
66#include <ctype.h>
67#include "pathnames.h"
68
69#ifdef KERBEROS
70#include <kerberosIV/des.h>
71#include <kerberosIV/krb.h>
72char dst_realm_buf[REALM_SZ];
73char *dest_realm = NULL;
74int use_kerberos = 1;
75CREDENTIALS cred;
76Key_schedule schedule;
77extern char *krb_realmofhost();
78#ifdef CRYPT
79int doencrypt = 0;
80#define OPTIONS "dfk:prtx"
81#else
82#define OPTIONS "dfk:prt"
83#endif
84#else
85#define OPTIONS "dfprt"
86#endif
87
88struct passwd *pwd;
89u_short port;
90uid_t userid;
91int errs, rem;
92int pflag, iamremote, iamrecursive, targetshouldbedirectory;
93
94#define CMDNEEDS 64
95char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */
96
97typedef struct _buf {
98 int cnt;
99 char *buf;
100} BUF;
101
102void lostconn();
103
104main(argc, argv)
105 int argc;
106 char **argv;
107{
108 extern int optind;
109 extern char *optarg;
110 struct servent *sp;
111 int ch, fflag, tflag;
112 char *targ, *shell, *colon();
113
114 fflag = tflag = 0;
115 while ((ch = getopt(argc, argv, OPTIONS)) != EOF)
116 switch(ch) {
117 /* user-visible flags */
118 case 'p': /* preserve access/mod times */
119 ++pflag;
120 break;
121 case 'r':
122 ++iamrecursive;
123 break;
124#ifdef KERBEROS
125 case 'k':
126 strncpy(dst_realm_buf, optarg, REALM_SZ);
127 dest_realm = dst_realm_buf;
128 break;
129#ifdef CRYPT
130 case 'x':
131 doencrypt = 1;
132 /* des_set_key(cred.session, schedule); */
133 break;
134#endif
135#endif
136 /* rshd-invoked options (server) */
137 case 'd':
138 targetshouldbedirectory = 1;
139 break;
140 case 'f': /* "from" */
141 iamremote = 1;
142 fflag = 1;
143 break;
144 case 't': /* "to" */
145 iamremote = 1;
146 tflag = 1;
147 break;
148
149 case '?':
150 default:
151 usage();
152 }
153 argc -= optind;
154 argv += optind;
155
156#ifdef KERBEROS
157#ifdef CRYPT
158 shell = doencrypt ? "ekshell" : "kshell";
159#else
160 shell = "kshell";
161#endif
162 sp = getservbyname(shell, "tcp");
163 if (sp == NULL) {
164 char msgbuf[64];
165 use_kerberos = 0;
166 (void)snprintf(msgbuf, sizeof(msgbuf),
167 "can't get entry for %s/tcp service", shell);
168 old_warning(msgbuf);
169 sp = getservbyname(shell = "shell", "tcp");
170 }
171#else
172 sp = getservbyname(shell = "shell", "tcp");
173#endif
174 if (sp == NULL) {
175 (void)fprintf(stderr, "rcp: %s/tcp: unknown service\n", shell);
176 exit(1);
177 }
178 port = sp->s_port;
179
180 if (!(pwd = getpwuid(userid = getuid()))) {
181 (void)fprintf(stderr, "rcp: unknown user %d.\n", (int)userid);
182 exit(1);
183 }
184
185 if (fflag) {
186 /* follow "protocol", send data */
187 (void)response();
188 (void)setuid(userid);
189 source(argc, argv);
190 exit(errs);
191 }
192
193 if (tflag) {
194 /* receive data */
195 (void)setuid(userid);
196 sink(argc, argv);
197 exit(errs);
198 }
199
200 if (argc < 2)
201 usage();
202 if (argc > 2)
203 targetshouldbedirectory = 1;
204
205 rem = -1;
206 /* command to be executed on remote system using "rsh" */
207#ifdef KERBEROS
208 (void)snprintf(cmd, sizeof(cmd),
209 "rcp%s%s%s%s", iamrecursive ? " -r" : "",
210#ifdef CRYPT
211 ((doencrypt && use_kerberos) ? " -x" : ""),
212#else
213 "",
214#endif
215 pflag ? " -p" : "", targetshouldbedirectory ? " -d" : "");
216#else
217 (void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s",
218 iamrecursive ? " -r" : "", pflag ? " -p" : "",
219 targetshouldbedirectory ? " -d" : "");
220#endif
221
222 (void)signal(SIGPIPE, lostconn);
223
224 if (targ = colon(argv[argc - 1]))
225 toremote(targ, argc, argv); /* destination is remote host */
226 else {
227 tolocal(argc, argv); /* destination is local host */
228 if (targetshouldbedirectory)
229 verifydir(argv[argc - 1]);
230 }
231 exit(errs);
232}
233
234toremote(targ, argc, argv)
235 char *targ;
236 int argc;
237 char **argv;
238{
239 int i, len, tos;
240 char *bp, *host, *src, *suser, *thost, *tuser;
241 char *colon();
242
243 *targ++ = 0;
244 if (*targ == 0)
245 targ = ".";
246
247 if (thost = index(argv[argc - 1], '@')) {
248 /* user@host */
249 *thost++ = 0;
250 tuser = argv[argc - 1];
251 if (*tuser == '\0')
252 tuser = NULL;
253 else if (!okname(tuser))
254 exit(1);
255 } else {
256 thost = argv[argc - 1];
257 tuser = NULL;
258 }
259
260 for (i = 0; i < argc - 1; i++) {
261 src = colon(argv[i]);
262 if (src) { /* remote to remote */
263 *src++ = 0;
264 if (*src == 0)
265 src = ".";
266 host = index(argv[i], '@');
267 len = strlen(_PATH_RSH) + strlen(argv[i]) +
268 strlen(src) + (tuser ? strlen(tuser) : 0) +
269 strlen(thost) + strlen(targ) + CMDNEEDS + 20;
270 if (!(bp = malloc(len)))
271 nospace();
272 if (host) {
273 *host++ = 0;
274 suser = argv[i];
275 if (*suser == '\0')
276 suser = pwd->pw_name;
277 else if (!okname(suser))
278 continue;
279 (void)snprintf(bp, len,
280 "%s %s -l %s -n %s %s '%s%s%s:%s'",
281 _PATH_RSH, host, suser, cmd, src,
282 tuser ? tuser : "", tuser ? "@" : "",
283 thost, targ);
284 } else
285 (void)snprintf(bp, len,
286 "%s %s -n %s %s '%s%s%s:%s'",
287 _PATH_RSH, argv[i], cmd, src,
288 tuser ? tuser : "", tuser ? "@" : "",
289 thost, targ);
290 (void)susystem(bp);
291 (void)free(bp);
292 } else { /* local to remote */
293 if (rem == -1) {
294 len = strlen(targ) + CMDNEEDS + 20;
295 if (!(bp = malloc(len)))
296 nospace();
297 (void)snprintf(bp, len, "%s -t %s", cmd, targ);
298 host = thost;
299#ifdef KERBEROS
300 if (use_kerberos)
301 rem = kerberos(&host, bp,
302 pwd->pw_name,
303 tuser ? tuser : pwd->pw_name);
304 else
305#endif
306 rem = rcmd(&host, port, pwd->pw_name,
307 tuser ? tuser : pwd->pw_name,
308 bp, 0);
309 if (rem < 0)
310 exit(1);
311 tos = IPTOS_THROUGHPUT;
312 if (setsockopt(rem, IPPROTO_IP, IP_TOS,
313 (char *)&tos, sizeof(int)) < 0)
314 perror("rcp: setsockopt TOS (ignored)");
315 if (response() < 0)
316 exit(1);
317 (void)free(bp);
318 (void)setuid(userid);
319 }
320 source(1, argv+i);
321 }
322 }
323}
324
325tolocal(argc, argv)
326 int argc;
327 char **argv;
328{
329 int i, len, tos;
330 char *bp, *host, *src, *suser;
331 char *colon();
332
333 for (i = 0; i < argc - 1; i++) {
334 if (!(src = colon(argv[i]))) { /* local to local */
335 len = strlen(_PATH_CP) + strlen(argv[i]) +
336 strlen(argv[argc - 1]) + 20;
337 if (!(bp = malloc(len)))
338 nospace();
339 (void)snprintf(bp, len, "%s%s%s %s %s", _PATH_CP,
340 iamrecursive ? " -r" : "", pflag ? " -p" : "",
341 argv[i], argv[argc - 1]);
342 (void)susystem(bp);
343 (void)free(bp);
344 continue;
345 }
346 *src++ = 0;
347 if (*src == 0)
348 src = ".";
349 host = index(argv[i], '@');
350 if (host) {
351 *host++ = 0;
352 suser = argv[i];
353 if (*suser == '\0')
354 suser = pwd->pw_name;
355 else if (!okname(suser))
356 continue;
357 } else {
358 host = argv[i];
359 suser = pwd->pw_name;
360 }
361 len = strlen(src) + CMDNEEDS + 20;
362 if (!(bp = malloc(len)))
363 nospace();
364 (void)snprintf(bp, len, "%s -f %s", cmd, src);
365#ifdef KERBEROS
366 if (use_kerberos)
367 rem = kerberos(&host, bp, pwd->pw_name, suser);
368 else
369#endif
370 rem = rcmd(&host, port, pwd->pw_name, suser, bp, 0);
371 (void)free(bp);
372 if (rem < 0)
373 continue;
374 (void)seteuid(userid);
375 tos = IPTOS_THROUGHPUT;
376 if (setsockopt(rem, IPPROTO_IP, IP_TOS,
377 (char *)&tos, sizeof(int)) < 0)
378 perror("rcp: setsockopt TOS (ignored)");
379 sink(1, argv + argc - 1);
380 (void)seteuid(0);
381 (void)close(rem);
382 rem = -1;
383 }
384}
385
386verifydir(cp)
387 char *cp;
388{
389 struct stat stb;
390
391 if (stat(cp, &stb) >= 0) {
392 if ((stb.st_mode & S_IFMT) == S_IFDIR)
393 return;
394 errno = ENOTDIR;
395 }
396 error("rcp: %s: %s.\n", cp, strerror(errno));
397 exit(1);
398}
399
400char *
401colon(cp)
402 register char *cp;
403{
404 for (; *cp; ++cp) {
405 if (*cp == ':')
406 return(cp);
407 if (*cp == '/')
408 return(0);
409 }
410 return(0);
411}
412
413okname(cp0)
414 char *cp0;
415{
416 register char *cp = cp0;
417 register int c;
418
419 do {
420 c = *cp;
421 if (c & 0200)
422 goto bad;
423 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
424 goto bad;
425 } while (*++cp);
426 return(1);
427bad:
428 (void)fprintf(stderr, "rcp: invalid user name %s\n", cp0);
429 return(0);
430}
431
432susystem(s)
433 char *s;
434{
435 int status, pid, w;
436 register sig_t istat, qstat;
437
438 if ((pid = vfork()) == 0) {
439 (void)setuid(userid);
440 execl(_PATH_BSHELL, "sh", "-c", s, (char *)0);
441 _exit(127);
442 }
443 istat = signal(SIGINT, SIG_IGN);
444 qstat = signal(SIGQUIT, SIG_IGN);
445 while ((w = wait(&status)) != pid && w != -1)
446 ;
447 if (w == -1)
448 status = -1;
449 (void)signal(SIGINT, istat);
450 (void)signal(SIGQUIT, qstat);
451 return(status);
452}
453
454source(argc, argv)
455 int argc;
456 char **argv;
457{
458 struct stat stb;
459 static BUF buffer;
460 BUF *bp;
461 off_t i;
462 int x, readerr, f, amt;
463 char *last, *name, buf[BUFSIZ];
464 BUF *allocbuf();
465
466 for (x = 0; x < argc; x++) {
467 name = argv[x];
468 if ((f = open(name, O_RDONLY, 0)) < 0) {
469 error("rcp: %s: %s\n", name, strerror(errno));
470 continue;
471 }
472 if (fstat(f, &stb) < 0)
473 goto notreg;
474 switch (stb.st_mode&S_IFMT) {
475
476 case S_IFREG:
477 break;
478
479 case S_IFDIR:
480 if (iamrecursive) {
481 (void)close(f);
482 rsource(name, &stb);
483 continue;
484 }
485 /* FALLTHROUGH */
486 default:
487notreg: (void)close(f);
488 error("rcp: %s: not a plain file\n", name);
489 continue;
490 }
491 last = rindex(name, '/');
492 if (last == 0)
493 last = name;
494 else
495 last++;
496 if (pflag) {
497 /*
498 * Make it compatible with possible future
499 * versions expecting microseconds.
500 */
501 (void)snprintf(buf, sizeof(buf),
502 "T%ld 0 %ld 0\n", stb.st_mtime, stb.st_atime);
503 (void)write(rem, buf, (int)strlen(buf));
504 if (response() < 0) {
505 (void)close(f);
506 continue;
507 }
508 }
509 (void)snprintf(buf, sizeof(buf),
510 "C%04o %ld %s\n", stb.st_mode&07777, stb.st_size, last);
511 (void)write(rem, buf, (int)strlen(buf));
512 if (response() < 0) {
513 (void)close(f);
514 continue;
515 }
516 if ((bp = allocbuf(&buffer, f, BUFSIZ)) == 0) {
517 (void)close(f);
518 continue;
519 }
520 readerr = 0;
521 for (i = 0; i < stb.st_size; i += bp->cnt) {
522 amt = bp->cnt;
523 if (i + amt > stb.st_size)
524 amt = stb.st_size - i;
525 if (readerr == 0 && read(f, bp->buf, amt) != amt)
526 readerr = errno;
527 (void)write(rem, bp->buf, amt);
528 }
529 (void)close(f);
530 if (readerr == 0)
531 (void)write(rem, "", 1);
532 else
533 error("rcp: %s: %s\n", name, strerror(readerr));
534 (void)response();
535 }
536}
537
538rsource(name, statp)
539 char *name;
540 struct stat *statp;
541{
542 DIR *dirp;
543 struct dirent *dp;
544 char *last, *vect[1], path[MAXPATHLEN];
545
546 if (!(dirp = opendir(name))) {
547 error("rcp: %s: %s\n", name, strerror(errno));
548 return;
549 }
550 last = rindex(name, '/');
551 if (last == 0)
552 last = name;
553 else
554 last++;
555 if (pflag) {
556 (void)snprintf(path, sizeof(path),
557 "T%ld 0 %ld 0\n", statp->st_mtime, statp->st_atime);
558 (void)write(rem, path, (int)strlen(path));
559 if (response() < 0) {
560 closedir(dirp);
561 return;
562 }
563 }
564 (void)snprintf(path, sizeof(path),
565 "D%04o %d %s\n", statp->st_mode&07777, 0, last);
566 (void)write(rem, path, (int)strlen(path));
567 if (response() < 0) {
568 closedir(dirp);
569 return;
570 }
571 while (dp = readdir(dirp)) {
572 if (dp->d_ino == 0)
573 continue;
574 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
575 continue;
576 if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) {
577 error("%s/%s: name too long.\n", name, dp->d_name);
578 continue;
579 }
580 (void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name);
581 vect[0] = path;
582 source(1, vect);
583 }
584 closedir(dirp);
585 (void)write(rem, "E\n", 2);
586 (void)response();
587}
588
589response()
590{
591 register char *cp;
592 char ch, resp, rbuf[BUFSIZ];
593
594 if (read(rem, &resp, sizeof(resp)) != sizeof(resp))
595 lostconn();
596
597 cp = rbuf;
598 switch(resp) {
599 case 0: /* ok */
600 return(0);
601 default:
602 *cp++ = resp;
603 /* FALLTHROUGH */
604 case 1: /* error, followed by err msg */
605 case 2: /* fatal error, "" */
606 do {
607 if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
608 lostconn();
609 *cp++ = ch;
610 } while (cp < &rbuf[BUFSIZ] && ch != '\n');
611
612 if (!iamremote)
613 (void)write(2, rbuf, cp - rbuf);
614 ++errs;
615 if (resp == 1)
616 return(-1);
617 exit(1);
618 }
619 /*NOTREACHED*/
620}
621
622void
623lostconn()
624{
625 if (!iamremote)
626 (void)fprintf(stderr, "rcp: lost connection\n");
627 exit(1);
628}
629
630sink(argc, argv)
631 int argc;
632 char **argv;
633{
634 register char *cp;
635 static BUF buffer;
636 struct stat stb;
637 struct timeval tv[2];
638 enum { YES, NO, DISPLAYED } wrerr;
639 BUF *bp, *allocbuf();
640 off_t i, j;
641 char ch, *targ, *why;
642 int amt, count, exists, first, mask, mode;
643 int ofd, setimes, size, targisdir;
644 char *np, *vect[1], buf[BUFSIZ];
645
646#define atime tv[0]
647#define mtime tv[1]
648#define SCREWUP(str) { why = str; goto screwup; }
649
650 setimes = targisdir = 0;
651 mask = umask(0);
652 if (!pflag)
653 (void)umask(mask);
654 if (argc != 1) {
655 error("rcp: ambiguous target\n");
656 exit(1);
657 }
658 targ = *argv;
659 if (targetshouldbedirectory)
660 verifydir(targ);
661 (void)write(rem, "", 1);
662 if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
663 targisdir = 1;
664 for (first = 1;; first = 0) {
665 cp = buf;
666 if (read(rem, cp, 1) <= 0)
667 return;
668 if (*cp++ == '\n')
669 SCREWUP("unexpected <newline>");
670 do {
671 if (read(rem, &ch, sizeof(ch)) != sizeof(ch))
672 SCREWUP("lost connection");
673 *cp++ = ch;
674 } while (cp < &buf[BUFSIZ - 1] && ch != '\n');
675 *cp = 0;
676
677 if (buf[0] == '\01' || buf[0] == '\02') {
678 if (iamremote == 0)
679 (void)write(2, buf + 1, (int)strlen(buf + 1));
680 if (buf[0] == '\02')
681 exit(1);
682 errs++;
683 continue;
684 }
685 if (buf[0] == 'E') {
686 (void)write(rem, "", 1);
687 return;
688 }
689
690 if (ch == '\n')
691 *--cp = 0;
692
693#define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
694 cp = buf;
695 if (*cp == 'T') {
696 setimes++;
697 cp++;
698 getnum(mtime.tv_sec);
699 if (*cp++ != ' ')
700 SCREWUP("mtime.sec not delimited");
701 getnum(mtime.tv_usec);
702 if (*cp++ != ' ')
703 SCREWUP("mtime.usec not delimited");
704 getnum(atime.tv_sec);
705 if (*cp++ != ' ')
706 SCREWUP("atime.sec not delimited");
707 getnum(atime.tv_usec);
708 if (*cp++ != '\0')
709 SCREWUP("atime.usec not delimited");
710 (void)write(rem, "", 1);
711 continue;
712 }
713 if (*cp != 'C' && *cp != 'D') {
714 /*
715 * Check for the case "rcp remote:foo\* local:bar".
716 * In this case, the line "No match." can be returned
717 * by the shell before the rcp command on the remote is
718 * executed so the ^Aerror_message convention isn't
719 * followed.
720 */
721 if (first) {
722 error("%s\n", cp);
723 exit(1);
724 }
725 SCREWUP("expected control record");
726 }
727 mode = 0;
728 for (++cp; cp < buf + 5; cp++) {
729 if (*cp < '0' || *cp > '7')
730 SCREWUP("bad mode");
731 mode = (mode << 3) | (*cp - '0');
732 }
733 if (*cp++ != ' ')
734 SCREWUP("mode not delimited");
735 size = 0;
736 while (isdigit(*cp))
737 size = size * 10 + (*cp++ - '0');
738 if (*cp++ != ' ')
739 SCREWUP("size not delimited");
740 if (targisdir) {
741 static char *namebuf;
742 static int cursize;
743 size_t need;
744
745 need = strlen(targ) + strlen(cp) + 250;
746 if (need > cursize) {
747 if (!(namebuf = malloc(need)))
748 error("out of memory\n");
749 }
750 (void)snprintf(namebuf, need, "%s%s%s", targ,
751 *targ ? "/" : "", cp);
752 np = namebuf;
753 }
754 else
755 np = targ;
756 exists = stat(np, &stb) == 0;
757 if (buf[0] == 'D') {
758 if (exists) {
759 if ((stb.st_mode&S_IFMT) != S_IFDIR) {
760 errno = ENOTDIR;
761 goto bad;
762 }
763 if (pflag)
764 (void)chmod(np, mode);
765 } else if (mkdir(np, mode) < 0)
766 goto bad;
767 vect[0] = np;
768 sink(1, vect);
769 if (setimes) {
770 setimes = 0;
771 if (utimes(np, tv) < 0)
772 error("rcp: can't set times on %s: %s\n",
773 np, strerror(errno));
774 }
775 continue;
776 }
777 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
778bad: error("rcp: %s: %s\n", np, strerror(errno));
779 continue;
780 }
781 if (exists && pflag)
782 (void)fchmod(ofd, mode);
783 (void)write(rem, "", 1);
784 if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == 0) {
785 (void)close(ofd);
786 continue;
787 }
788 cp = bp->buf;
789 count = 0;
790 wrerr = NO;
791 for (i = 0; i < size; i += BUFSIZ) {
792 amt = BUFSIZ;
793 if (i + amt > size)
794 amt = size - i;
795 count += amt;
796 do {
797 j = read(rem, cp, amt);
798 if (j <= 0) {
799 error("rcp: %s\n",
800 j ? strerror(errno) :
801 "dropped connection");
802 exit(1);
803 }
804 amt -= j;
805 cp += j;
806 } while (amt > 0);
807 if (count == bp->cnt) {
808 if (wrerr == NO &&
809 write(ofd, bp->buf, count) != count)
810 wrerr = YES;
811 count = 0;
812 cp = bp->buf;
813 }
814 }
815 if (count != 0 && wrerr == NO &&
816 write(ofd, bp->buf, count) != count)
817 wrerr = YES;
818 if (ftruncate(ofd, size)) {
819 error("rcp: can't truncate %s: %s\n", np,
820 strerror(errno));
821 wrerr = DISPLAYED;
822 }
823 (void)close(ofd);
824 (void)response();
825 if (setimes && wrerr == NO) {
826 setimes = 0;
827 if (utimes(np, tv) < 0) {
828 error("rcp: can't set times on %s: %s\n",
829 np, strerror(errno));
830 wrerr = DISPLAYED;
831 }
832 }
833 switch(wrerr) {
834 case YES:
835 error("rcp: %s: %s\n", np, strerror(errno));
836 break;
837 case NO:
838 (void)write(rem, "", 1);
839 break;
840 case DISPLAYED:
841 break;
842 }
843 }
844screwup:
845 error("rcp: protocol screwup: %s\n", why);
846 exit(1);
847}
848
849BUF *
850allocbuf(bp, fd, blksize)
851 BUF *bp;
852 int fd, blksize;
853{
854 struct stat stb;
855 size_t size;
856
857 if (fstat(fd, &stb) < 0) {
858 error("rcp: fstat: %s\n", strerror(errno));
859 return(0);
860 }
861 size = roundup(stb.st_blksize, blksize);
862 if (size == 0)
863 size = blksize;
864 if (bp->cnt < size) {
865 if (bp->buf != 0)
866 free(bp->buf);
867 bp->buf = malloc(size);
868 if (!bp->buf) {
869 error("rcp: malloc: out of memory\n");
870 return(0);
871 }
872 }
873 bp->cnt = size;
874 return(bp);
875}
876
877/* VARARGS1 */
878error(fmt, a1, a2, a3)
879 char *fmt;
880 int a1, a2, a3;
881{
882 static FILE *fp;
883
884 ++errs;
885 if (!fp && !(fp = fdopen(rem, "w")))
886 return;
887 (void)fprintf(fp, "%c", 0x01);
888 (void)fprintf(fp, fmt, a1, a2, a3);
889 (void)fflush(fp);
890 if (!iamremote)
891 (void)fprintf(stderr, fmt, a1, a2, a3);
892}
893
894nospace()
895{
896 (void)fprintf(stderr, "rcp: out of memory.\n");
897 exit(1);
898}
899
900
901usage()
902{
903#ifdef KERBEROS
904#ifdef CRYPT
905 (void)fprintf(stderr, "%s\n\t%s\n",
906 "usage: rcp [-k realm] [-px] f1 f2",
907 "or: rcp [-k realm] [-rpx] f1 ... fn directory");
908#else
909 (void)fprintf(stderr, "%s\n\t%s\n",
910 "usage: rcp [-k realm] [-p] f1 f2",
911 "or: rcp [-k realm] [-rp] f1 ... fn directory");
912#endif
913#else
914 (void)fprintf(stderr,
915 "usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn directory\n");
916#endif
917 exit(1);
918}
919
920#ifdef KERBEROS
921old_warning(str)
922 char *str;
923{
924 (void)fprintf(stderr, "rcp: warning: %s, using standard rcp\n", str);
925}
926
927int
928kerberos(host, bp, locuser, user)
929
930 char **host, *bp, *locuser, *user;
931{
932 struct servent *sp;
933
934again:
935 if (use_kerberos) {
936 rem = KSUCCESS;
937 errno = 0;
938 if (dest_realm == NULL)
939 dest_realm = krb_realmofhost(*host);
940
941#ifdef CRYPT
942 if (doencrypt)
943 rem = krcmd_mutual(
944 host, port,
945 user, bp, 0,
946 dest_realm,
947 &cred, schedule);
948 else
949#endif
950 rem = krcmd(
951 host, port,
952 user, bp, 0, dest_realm);
953
954 if (rem < 0) {
955 use_kerberos = 0;
956 sp = getservbyname("shell", "tcp");
957 if (sp == NULL) {
958 (void)fprintf(stderr,
959 "rcp: unknown service shell/tcp\n");
960 exit(1);
961 }
962 if (errno == ECONNREFUSED)
963 old_warning(
964 "remote host doesn't support Kerberos");
965
966 if (errno == ENOENT)
967 old_warning(
968 "Can't provide Kerberos auth data");
969 port = sp->s_port;
970 goto again;
971 }
972 } else {
973#ifdef CRYPT
974 if (doencrypt) {
975 fprintf(stderr,
976 "The -x option requires Kerberos authentication\n");
977 exit(1);
978 }
979#endif
980 rem = rcmd(host, sp->s_port, locuser, user, bp, 0);
981 }
982 return(rem);
983}
984#endif /* KERBEROS */