let's really zero bss
[unix-history] / usr / src / bin / rcp / rcp.c
CommitLineData
22e155fc
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
9"@(#) Copyright (c) 1983 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
b7ff3bb3 13#ifndef lint
9e9252a8 14static char sccsid[] = "@(#)rcp.c 5.4 (Berkeley) %G%";
22e155fc 15#endif not lint
b7ff3bb3 16
4ca10280
SL
17/*
18 * rcp
19 */
ed0072ae 20#include <sys/param.h>
b7ff3bb3 21#include <sys/stat.h>
af86e024 22#include <sys/time.h>
b7ff3bb3 23#include <sys/ioctl.h>
94a2d2a7
SL
24
25#include <netinet/in.h>
26
27#include <stdio.h>
28#include <signal.h>
b7ff3bb3
BJ
29#include <pwd.h>
30#include <ctype.h>
92e58d95 31#include <netdb.h>
b7ff3bb3 32#include <errno.h>
4ca10280 33
b7ff3bb3
BJ
34int rem;
35char *colon(), *index(), *rindex(), *malloc(), *strcpy(), *sprintf();
36int errs;
37int lostconn();
b7ff3bb3
BJ
38int errno;
39char *sys_errlist[];
40int iamremote, targetshouldbedirectory;
41int iamrecursive;
af86e024 42int pflag;
b7ff3bb3
BJ
43struct passwd *pwd;
44struct passwd *getpwuid();
11bfe9c9
RC
45int userid;
46int port;
b7ff3bb3 47
371655cc
KM
48struct buffer {
49 int cnt;
50 char *buf;
51} *allocbuf();
52
b7ff3bb3
BJ
53/*VARARGS*/
54int error();
55
56#define ga() (void) write(rem, "", 1)
57
58main(argc, argv)
59 int argc;
60 char **argv;
61{
62 char *targ, *host, *src;
9f526ab5 63 char *suser, *tuser, *thost;
b7ff3bb3
BJ
64 int i;
65 char buf[BUFSIZ], cmd[16];
11bfe9c9 66 struct servent *sp;
92e58d95
RC
67
68 sp = getservbyname("shell", "tcp");
69 if (sp == NULL) {
70 fprintf(stderr, "rcp: shell/tcp: unknown service\n");
71 exit(1);
72 }
11bfe9c9
RC
73 port = sp->s_port;
74 pwd = getpwuid(userid = getuid());
b7ff3bb3
BJ
75 if (pwd == 0) {
76 fprintf(stderr, "who are you?\n");
77 exit(1);
78 }
af86e024
JL
79
80 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
81 (*argv)++;
82 while (**argv) switch (*(*argv)++) {
83
84 case 'r':
85 iamrecursive++;
86 break;
87
88 case 'p': /* preserve mtimes and atimes */
89 pflag++;
90 break;
91
92 /* The rest of these are not for users. */
93 case 'd':
94 targetshouldbedirectory = 1;
95 break;
96
97 case 'f': /* "from" */
98 iamremote = 1;
99 (void) response();
100 (void) setuid(userid);
101 source(--argc, ++argv);
102 exit(errs);
103
104 case 't': /* "to" */
105 iamremote = 1;
106 (void) setuid(userid);
107 sink(--argc, ++argv);
108 exit(errs);
109
110 default:
111 fprintf(stderr,
a4372a41 112 "Usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n");
af86e024
JL
113 exit(1);
114 }
b7ff3bb3
BJ
115 }
116 rem = -1;
117 if (argc > 2)
118 targetshouldbedirectory = 1;
af86e024
JL
119 (void) sprintf(cmd, "rcp%s%s%s",
120 iamrecursive ? " -r" : "", pflag ? " -p" : "",
121 targetshouldbedirectory ? " -d" : "");
122 (void) signal(SIGPIPE, lostconn);
b7ff3bb3 123 targ = colon(argv[argc - 1]);
af86e024 124 if (targ) { /* ... to remote */
b7ff3bb3 125 *targ++ = 0;
06fb72f5
SL
126 if (*targ == 0)
127 targ = ".";
9f526ab5
RC
128 thost = index(argv[argc - 1], '@');
129 if (thost) {
130 *thost++ = 0;
131 tuser = argv[argc - 1];
132 if (*tuser == '\0')
9e9252a8 133 tuser = NULL;
9f526ab5 134 else if (!okname(tuser))
b7ff3bb3 135 exit(1);
9f526ab5
RC
136 } else {
137 thost = argv[argc - 1];
9e9252a8 138 tuser = NULL;
9f526ab5 139 }
b7ff3bb3
BJ
140 for (i = 0; i < argc - 1; i++) {
141 src = colon(argv[i]);
af86e024 142 if (src) { /* remote to remote */
b7ff3bb3 143 *src++ = 0;
06fb72f5 144 if (*src == 0)
195c1de2 145 src = ".";
9f526ab5
RC
146 host = index(argv[i], '@');
147 if (host) {
148 *host++ = 0;
149 suser = argv[i];
150 if (*suser == '\0')
151 suser = pwd->pw_name;
152 else if (!okname(suser))
b7ff3bb3 153 continue;
9e9252a8
JB
154 (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s%s%s:%s'",
155 host, suser, cmd, src, tuser,
156 tuser ? "@" : "",
157 thost, targ);
b7ff3bb3 158 } else
9e9252a8
JB
159 (void) sprintf(buf, "rsh %s -n %s %s '%s%s%s:%s'",
160 argv[i], cmd, src, tuser,
161 tuser ? "@" : "",
162 thost, targ);
b7ff3bb3 163 (void) susystem(buf);
af86e024 164 } else { /* local to remote */
b7ff3bb3
BJ
165 if (rem == -1) {
166 (void) sprintf(buf, "%s -t %s",
167 cmd, targ);
9f526ab5 168 host = thost;
9e9252a8
JB
169 rem = rcmd(&host, port, pwd->pw_name,
170 tuser ? tuser : pwd->pw_name,
b7ff3bb3
BJ
171 buf, 0);
172 if (rem < 0)
173 exit(1);
174 if (response() < 0)
175 exit(1);
11bfe9c9 176 (void) setuid(userid);
b7ff3bb3
BJ
177 }
178 source(1, argv+i);
179 }
180 }
af86e024 181 } else { /* ... to local */
b7ff3bb3
BJ
182 if (targetshouldbedirectory)
183 verifydir(argv[argc - 1]);
184 for (i = 0; i < argc - 1; i++) {
185 src = colon(argv[i]);
af86e024
JL
186 if (src == 0) { /* local to local */
187 (void) sprintf(buf, "/bin/cp%s%s %s %s",
b7ff3bb3 188 iamrecursive ? " -r" : "",
af86e024 189 pflag ? " -p" : "",
b7ff3bb3
BJ
190 argv[i], argv[argc - 1]);
191 (void) susystem(buf);
af86e024 192 } else { /* remote to local */
b7ff3bb3 193 *src++ = 0;
06fb72f5
SL
194 if (*src == 0)
195 src = ".";
9f526ab5
RC
196 host = index(argv[i], '@');
197 if (host) {
198 *host++ = 0;
199 suser = argv[i];
200 if (*suser == '\0')
201 suser = pwd->pw_name;
202 else if (!okname(suser))
b7ff3bb3 203 continue;
9f526ab5
RC
204 } else {
205 host = argv[i];
b7ff3bb3 206 suser = pwd->pw_name;
9f526ab5 207 }
b7ff3bb3 208 (void) sprintf(buf, "%s -f %s", cmd, src);
11bfe9c9 209 rem = rcmd(&host, port, pwd->pw_name, suser,
b7ff3bb3
BJ
210 buf, 0);
211 if (rem < 0)
9f526ab5 212 continue;
11bfe9c9 213 (void) setreuid(0, userid);
b7ff3bb3 214 sink(1, argv+argc-1);
11bfe9c9 215 (void) setreuid(userid, 0);
b7ff3bb3
BJ
216 (void) close(rem);
217 rem = -1;
218 }
219 }
220 }
221 exit(errs);
222}
223
224verifydir(cp)
225 char *cp;
226{
227 struct stat stb;
228
11bfe9c9
RC
229 if (stat(cp, &stb) >= 0) {
230 if ((stb.st_mode & S_IFMT) == S_IFDIR)
231 return;
232 errno = ENOTDIR;
233 }
b7ff3bb3
BJ
234 error("rcp: %s: %s.\n", cp, sys_errlist[errno]);
235 exit(1);
236}
237
238char *
239colon(cp)
240 char *cp;
241{
242
243 while (*cp) {
244 if (*cp == ':')
245 return (cp);
246 if (*cp == '/')
247 return (0);
248 cp++;
249 }
250 return (0);
251}
252
253okname(cp0)
254 char *cp0;
255{
256 register char *cp = cp0;
257 register int c;
258
259 do {
260 c = *cp;
261 if (c & 0200)
262 goto bad;
263 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
264 goto bad;
265 cp++;
266 } while (*cp);
267 return (1);
268bad:
269 fprintf(stderr, "rcp: invalid user name %s\n", cp0);
270 return (0);
271}
272
48755084
S
273susystem(s)
274 char *s;
b7ff3bb3 275{
48755084
S
276 int status, pid, w;
277 register int (*istat)(), (*qstat)();
b7ff3bb3 278
48755084 279 if ((pid = vfork()) == 0) {
af86e024 280 (void) setuid(userid);
48755084
S
281 execl("/bin/sh", "sh", "-c", s, (char *)0);
282 _exit(127);
283 }
284 istat = signal(SIGINT, SIG_IGN);
285 qstat = signal(SIGQUIT, SIG_IGN);
286 while ((w = wait(&status)) != pid && w != -1)
287 ;
288 if (w == -1)
289 status = -1;
af86e024
JL
290 (void) signal(SIGINT, istat);
291 (void) signal(SIGQUIT, qstat);
48755084 292 return (status);
b7ff3bb3
BJ
293}
294
295source(argc, argv)
296 int argc;
297 char **argv;
298{
299 char *last, *name;
300 struct stat stb;
371655cc
KM
301 static struct buffer buffer;
302 struct buffer *bp;
303 int x, sizerr, f, amt;
5f10dd79 304 off_t i;
371655cc 305 char buf[BUFSIZ];
b7ff3bb3
BJ
306
307 for (x = 0; x < argc; x++) {
308 name = argv[x];
11bfe9c9 309 if ((f = open(name, 0)) < 0) {
b7ff3bb3
BJ
310 error("rcp: %s: %s\n", name, sys_errlist[errno]);
311 continue;
312 }
313 if (fstat(f, &stb) < 0)
314 goto notreg;
315 switch (stb.st_mode&S_IFMT) {
316
317 case S_IFREG:
318 break;
319
320 case S_IFDIR:
321 if (iamrecursive) {
322 (void) close(f);
af86e024 323 rsource(name, &stb);
b7ff3bb3
BJ
324 continue;
325 }
326 /* fall into ... */
327 default:
328notreg:
329 (void) close(f);
330 error("rcp: %s: not a plain file\n", name);
331 continue;
332 }
333 last = rindex(name, '/');
334 if (last == 0)
335 last = name;
336 else
337 last++;
af86e024
JL
338 if (pflag) {
339 /*
340 * Make it compatible with possible future
341 * versions expecting microseconds.
342 */
343 (void) sprintf(buf, "T%ld 0 %ld 0\n",
344 stb.st_mtime, stb.st_atime);
345 (void) write(rem, buf, strlen(buf));
346 if (response() < 0) {
347 (void) close(f);
348 continue;
349 }
350 }
351 (void) sprintf(buf, "C%04o %ld %s\n",
b7ff3bb3
BJ
352 stb.st_mode&07777, stb.st_size, last);
353 (void) write(rem, buf, strlen(buf));
354 if (response() < 0) {
355 (void) close(f);
356 continue;
357 }
371655cc
KM
358 if ((bp = allocbuf(&buffer, f, BUFSIZ)) < 0) {
359 (void) close(f);
360 continue;
361 }
b7ff3bb3 362 sizerr = 0;
371655cc
KM
363 for (i = 0; i < stb.st_size; i += bp->cnt) {
364 amt = bp->cnt;
b7ff3bb3
BJ
365 if (i + amt > stb.st_size)
366 amt = stb.st_size - i;
371655cc 367 if (sizerr == 0 && read(f, bp->buf, amt) != amt)
b7ff3bb3 368 sizerr = 1;
371655cc 369 (void) write(rem, bp->buf, amt);
b7ff3bb3
BJ
370 }
371 (void) close(f);
372 if (sizerr == 0)
373 ga();
374 else
375 error("rcp: %s: file changed size\n", name);
376 (void) response();
377 }
378}
379
06fb72f5 380#include <sys/dir.h>
b7ff3bb3 381
af86e024 382rsource(name, statp)
b7ff3bb3 383 char *name;
af86e024 384 struct stat *statp;
b7ff3bb3
BJ
385{
386 DIR *d = opendir(name);
387 char *last;
388 struct direct *dp;
389 char buf[BUFSIZ];
390 char *bufv[1];
391
392 if (d == 0) {
11bfe9c9 393 error("rcp: %s: %s\n", name, sys_errlist[errno]);
b7ff3bb3
BJ
394 return;
395 }
396 last = rindex(name, '/');
397 if (last == 0)
398 last = name;
399 else
400 last++;
af86e024
JL
401 if (pflag) {
402 (void) sprintf(buf, "T%ld 0 %ld 0\n",
403 statp->st_mtime, statp->st_atime);
404 (void) write(rem, buf, strlen(buf));
405 if (response() < 0) {
406 closedir(d);
407 return;
408 }
409 }
410 (void) sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last);
b7ff3bb3
BJ
411 (void) write(rem, buf, strlen(buf));
412 if (response() < 0) {
413 closedir(d);
414 return;
415 }
416 while (dp = readdir(d)) {
417 if (dp->d_ino == 0)
418 continue;
419 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
420 continue;
421 if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
422 error("%s/%s: Name too long.\n", name, dp->d_name);
423 continue;
424 }
425 (void) sprintf(buf, "%s/%s", name, dp->d_name);
426 bufv[0] = buf;
427 source(1, bufv);
428 }
429 closedir(d);
430 (void) write(rem, "E\n", 2);
431 (void) response();
432}
433
434response()
435{
436 char resp, c, rbuf[BUFSIZ], *cp = rbuf;
437
438 if (read(rem, &resp, 1) != 1)
439 lostconn();
440 switch (resp) {
441
af86e024 442 case 0: /* ok */
b7ff3bb3
BJ
443 return (0);
444
445 default:
446 *cp++ = resp;
447 /* fall into... */
af86e024
JL
448 case 1: /* error, followed by err msg */
449 case 2: /* fatal error, "" */
b7ff3bb3
BJ
450 do {
451 if (read(rem, &c, 1) != 1)
452 lostconn();
453 *cp++ = c;
454 } while (cp < &rbuf[BUFSIZ] && c != '\n');
455 if (iamremote == 0)
456 (void) write(2, rbuf, cp - rbuf);
457 errs++;
458 if (resp == 1)
459 return (-1);
460 exit(1);
461 }
462 /*NOTREACHED*/
463}
464
465lostconn()
466{
467
468 if (iamremote == 0)
469 fprintf(stderr, "rcp: lost connection\n");
470 exit(1);
471}
472
473sink(argc, argv)
474 int argc;
475 char **argv;
476{
371655cc
KM
477 off_t i, j;
478 char *targ, *whopp, *cp;
479 int of, mode, wrerr, exists, first, count, amt, size;
480 struct buffer *bp;
481 static struct buffer buffer;
482 struct stat stb;
483 int targisdir = 0;
b7ff3bb3
BJ
484 int mask = umask(0);
485 char *myargv[1];
af86e024
JL
486 char cmdbuf[BUFSIZ], nambuf[BUFSIZ];
487 int setimes = 0;
488 struct timeval tv[2];
489#define atime tv[0]
490#define mtime tv[1]
371655cc 491#define SCREWUP(str) { whopp = str; goto screwup; }
b7ff3bb3 492
a4372a41
JL
493 if (!pflag)
494 (void) umask(mask);
11bfe9c9 495 if (argc != 1) {
b7ff3bb3
BJ
496 error("rcp: ambiguous target\n");
497 exit(1);
498 }
499 targ = *argv;
500 if (targetshouldbedirectory)
501 verifydir(targ);
502 ga();
503 if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
504 targisdir = 1;
4550ab9e 505 for (first = 1; ; first = 0) {
b7ff3bb3
BJ
506 cp = cmdbuf;
507 if (read(rem, cp, 1) <= 0)
508 return;
509 if (*cp++ == '\n')
510 SCREWUP("unexpected '\\n'");
511 do {
512 if (read(rem, cp, 1) != 1)
513 SCREWUP("lost connection");
514 } while (*cp++ != '\n');
515 *cp = 0;
516 if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
517 if (iamremote == 0)
06fb72f5 518 (void) write(2, cmdbuf+1, strlen(cmdbuf+1));
b7ff3bb3
BJ
519 if (cmdbuf[0] == '\02')
520 exit(1);
521 errs++;
522 continue;
523 }
524 *--cp = 0;
525 cp = cmdbuf;
526 if (*cp == 'E') {
527 ga();
528 return;
529 }
af86e024
JL
530
531#define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
532 if (*cp == 'T') {
533 setimes++;
534 cp++;
535 getnum(mtime.tv_sec);
536 if (*cp++ != ' ')
537 SCREWUP("mtime.sec not delimited");
538 getnum(mtime.tv_usec);
539 if (*cp++ != ' ')
540 SCREWUP("mtime.usec not delimited");
541 getnum(atime.tv_sec);
542 if (*cp++ != ' ')
543 SCREWUP("atime.sec not delimited");
544 getnum(atime.tv_usec);
545 if (*cp++ != '\0')
546 SCREWUP("atime.usec not delimited");
547 ga();
548 continue;
549 }
4550ab9e
RC
550 if (*cp != 'C' && *cp != 'D') {
551 /*
552 * Check for the case "rcp remote:foo\* local:bar".
553 * In this case, the line "No match." can be returned
554 * by the shell before the rcp command on the remote is
555 * executed so the ^Aerror_message convention isn't
556 * followed.
557 */
558 if (first) {
559 error("%s\n", cp);
560 exit(1);
561 }
b7ff3bb3 562 SCREWUP("expected control record");
4550ab9e 563 }
b7ff3bb3
BJ
564 cp++;
565 mode = 0;
566 for (; cp < cmdbuf+5; cp++) {
567 if (*cp < '0' || *cp > '7')
568 SCREWUP("bad mode");
569 mode = (mode << 3) | (*cp - '0');
570 }
571 if (*cp++ != ' ')
572 SCREWUP("mode not delimited");
573 size = 0;
a4372a41 574 while (isdigit(*cp))
b7ff3bb3
BJ
575 size = size * 10 + (*cp++ - '0');
576 if (*cp++ != ' ')
577 SCREWUP("size not delimited");
578 if (targisdir)
579 (void) sprintf(nambuf, "%s%s%s", targ,
580 *targ ? "/" : "", cp);
581 else
582 (void) strcpy(nambuf, targ);
583 exists = stat(nambuf, &stb) == 0;
11bfe9c9
RC
584 if (cmdbuf[0] == 'D') {
585 if (exists) {
b7ff3bb3
BJ
586 if ((stb.st_mode&S_IFMT) != S_IFDIR) {
587 errno = ENOTDIR;
588 goto bad;
589 }
a4372a41
JL
590 if (pflag)
591 (void) chmod(nambuf, mode);
11bfe9c9 592 } else if (mkdir(nambuf, mode) < 0)
b7ff3bb3
BJ
593 goto bad;
594 myargv[0] = nambuf;
595 sink(1, myargv);
af86e024
JL
596 if (setimes) {
597 setimes = 0;
598 if (utimes(nambuf, tv) < 0)
599 error("rcp: can't set times on %s: %s\n",
600 nambuf, sys_errlist[errno]);
601 }
b7ff3bb3 602 continue;
11bfe9c9
RC
603 }
604 if ((of = creat(nambuf, mode)) < 0) {
b7ff3bb3 605 bad:
b7ff3bb3
BJ
606 error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
607 continue;
b7ff3bb3 608 }
a4372a41
JL
609 if (exists && pflag)
610 (void) fchmod(of, mode);
b7ff3bb3 611 ga();
371655cc 612 if ((bp = allocbuf(&buffer, of, BUFSIZ)) < 0) {
af86e024 613 (void) close(of);
371655cc
KM
614 continue;
615 }
616 cp = bp->buf;
617 count = 0;
b7ff3bb3
BJ
618 wrerr = 0;
619 for (i = 0; i < size; i += BUFSIZ) {
371655cc 620 amt = BUFSIZ;
b7ff3bb3
BJ
621 if (i + amt > size)
622 amt = size - i;
371655cc 623 count += amt;
b7ff3bb3 624 do {
371655cc 625 j = read(rem, cp, amt);
9e9252a8
JB
626 if (j <= 0) {
627 if (j == 0)
628 error("rcp: dropped connection");
629 else
630 error("rcp: %s\n",
631 sys_errlist[errno]);
b7ff3bb3 632 exit(1);
9e9252a8 633 }
b7ff3bb3
BJ
634 amt -= j;
635 cp += j;
636 } while (amt > 0);
371655cc
KM
637 if (count == bp->cnt) {
638 if (wrerr == 0 &&
639 write(of, bp->buf, count) != count)
640 wrerr++;
641 count = 0;
642 cp = bp->buf;
643 }
b7ff3bb3 644 }
371655cc
KM
645 if (count != 0 && wrerr == 0 &&
646 write(of, bp->buf, count) != count)
647 wrerr++;
b7ff3bb3
BJ
648 (void) close(of);
649 (void) response();
af86e024
JL
650 if (setimes) {
651 setimes = 0;
652 if (utimes(nambuf, tv) < 0)
653 error("rcp: can't set times on %s: %s\n",
654 nambuf, sys_errlist[errno]);
655 }
b7ff3bb3 656 if (wrerr)
af86e024 657 error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
b7ff3bb3
BJ
658 else
659 ga();
660 }
661screwup:
662 error("rcp: protocol screwup: %s\n", whopp);
663 exit(1);
664}
665
371655cc
KM
666struct buffer *
667allocbuf(bp, fd, blksize)
668 struct buffer *bp;
669 int fd, blksize;
670{
671 struct stat stb;
672 int size;
673
674 if (fstat(fd, &stb) < 0) {
675 error("rcp: fstat: %s\n", sys_errlist[errno]);
676 return ((struct buffer *)-1);
677 }
678 size = roundup(stb.st_blksize, blksize);
679 if (size == 0)
680 size = blksize;
681 if (bp->cnt < size) {
682 if (bp->buf != 0)
683 free(bp->buf);
af86e024 684 bp->buf = (char *)malloc((unsigned) size);
371655cc
KM
685 if (bp->buf == 0) {
686 error("rcp: malloc: out of memory\n");
687 return ((struct buffer *)-1);
688 }
689 }
690 bp->cnt = size;
691 return (bp);
692}
693
af86e024 694/*VARARGS1*/
b7ff3bb3
BJ
695error(fmt, a1, a2, a3, a4, a5)
696 char *fmt;
697 int a1, a2, a3, a4, a5;
698{
699 char buf[BUFSIZ], *cp = buf;
700
701 errs++;
702 *cp++ = 1;
703 (void) sprintf(cp, fmt, a1, a2, a3, a4, a5);
704 (void) write(rem, buf, strlen(buf));
705 if (iamremote == 0)
706 (void) write(2, buf+1, strlen(buf+1));
707}