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