source: trunk/minix/commands/simple/rcp.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 16.5 KB
Line 
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
13#ifndef lint
14static char sccsid[] = "@(#)rcp.c 1.1 87/12/21 SMI"; /* from UCB 5.3 6/8/85"*/
15#endif /* not lint */
16
17/*
18 * rcp
19 */
20
21#undef _MINIX
22#define NAMESERVER
23
24#include <ctype.h>
25#include <errno.h>
26#include <stdarg.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <time.h>
31#include <utime.h>
32
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <sys/wait.h>
36
37#include <dirent.h>
38#include <fcntl.h>
39#include <pwd.h>
40#include <signal.h>
41#include <unistd.h>
42
43#include <net/gen/netdb.h>
44#include <net/netlib.h>
45
46#if __STDC__
47#define PROTO(func, args) func args
48#else
49#define PROTO(func, args) func ()
50#endif /* __STDC__ */
51
52PROTO (int main, (int argc, char *argv[]));
53PROTO (void lostconn, (int sig));
54PROTO (void error, (char *fmt, ...) );
55PROTO (int response, (void) );
56PROTO (void source, (int argc, char *argv[]) );
57PROTO (void sink, (int argc, char *argv[]) );
58PROTO (void usage, (void) );
59PROTO (char *colon, (char *cp) );
60PROTO (int okname, (char *cp0) );
61PROTO (int susystem, (char *s) );
62PROTO (void verifydir, (char *cp) );
63PROTO (void rsource, (char *name, struct stat *statp) );
64PROTO (struct buffer *allocbuf, (struct buffer *bp, int fd, int blksize) );
65
66#define vfork fork
67
68int rem;
69int errs;
70int errno;
71int iamremote, targetshouldbedirectory;
72int iamrecursive;
73int myuid; /* uid of invoker */
74int pflag;
75struct passwd *pwd;
76int userid;
77int port;
78
79struct buffer {
80 int cnt;
81 char *buf;
82};
83
84
85#define ga() (void) write(rem, "", 1)
86
87main(argc, argv)
88 int argc;
89 char **argv;
90{
91 char *targ, *host, *src;
92#ifndef NAMESERVER
93 char *suser, *tuser;
94#else /* NAMESERVER */
95 char *suser, *tuser, *thost;
96#endif /* NAMESERVER */
97 int i;
98 char buf[BUFSIZ], cmd[16];
99 struct servent *sp;
100
101 sp = getservbyname("shell", "tcp");
102 if (sp == NULL) {
103 fprintf(stderr, "rcp: shell/tcp: unknown service\n");
104 exit(1);
105 }
106 port = sp->s_port;
107 pwd = getpwuid(userid = getuid());
108 if (pwd == 0) {
109 fprintf(stderr, "who are you?\n");
110 exit(1);
111 }
112
113#ifdef NOT_DEF
114 /*
115 * This is a kludge to allow seteuid to user before touching
116 * files and seteuid root before doing rcmd so we can open
117 * the socket.
118 */
119 myuid = getuid();
120 if (setruid(0) < 0) {
121 perror("setruid root");
122 exit(1);
123 }
124 seteuid(myuid);
125#endif
126
127 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
128 (*argv)++;
129 while (**argv) switch (*(*argv)++) {
130
131 case 'r':
132 iamrecursive++;
133 break;
134
135 case 'p': /* preserve mtimes and atimes */
136 pflag++;
137 break;
138
139 /* The rest of these are not for users. */
140 case 'd':
141 targetshouldbedirectory = 1;
142 break;
143
144 case 'f': /* "from" */
145 iamremote = 1;
146 (void) response();
147 source(--argc, ++argv);
148 exit(errs);
149
150 case 't': /* "to" */
151 iamremote = 1;
152 sink(--argc, ++argv);
153 exit(errs);
154
155 default:
156 usage();
157 exit(1);
158 }
159 }
160if (iamremote)
161{
162 close(2);
163 open("/dev/tty", 2);
164}
165
166 if (argc < 2) {
167 usage();
168 exit(1);
169 }
170 rem = -1;
171 if (argc > 2)
172 targetshouldbedirectory = 1;
173 (void) sprintf(cmd, "rcp%s%s%s",
174 iamrecursive ? " -r" : "", pflag ? " -p" : "",
175 targetshouldbedirectory ? " -d" : "");
176 (void) signal(SIGPIPE, lostconn);
177 targ = colon(argv[argc - 1]);
178 if (targ) { /* ... to remote */
179 *targ++ = 0;
180 if (*targ == 0)
181 targ = ".";
182#ifndef NAMESERVER
183 tuser = strrchr(argv[argc - 1], '.');
184 if (tuser) {
185 *tuser++ = 0;
186 if (!okname(tuser))
187 exit(1);
188 } else
189 tuser = pwd->pw_name;
190#else /* NAMESERVER */
191 thost = strchr(argv[argc - 1], '@');
192 if (thost) {
193 *thost++ = 0;
194 tuser = argv[argc - 1];
195 if (*tuser == '\0')
196 tuser = pwd->pw_name;
197 else if (!okname(tuser))
198 exit(1);
199 } else {
200 thost = argv[argc - 1];
201 tuser = pwd->pw_name;
202 }
203#endif /* NAMESERVER */
204 for (i = 0; i < argc - 1; i++) {
205 src = colon(argv[i]);
206 if (src) { /* remote to remote */
207 *src++ = 0;
208 if (*src == 0)
209 src = ".";
210#ifndef NAMESERVER
211 suser = strrchr(argv[i], '.');
212 if (suser) {
213 *suser++ = 0;
214 if (!okname(suser))
215#else /* NAMESERVER */
216 host = strchr(argv[i], '@');
217 if (host) {
218 *host++ = 0;
219 suser = argv[i];
220 if (*suser == '\0')
221 suser = pwd->pw_name;
222 else if (!okname(suser))
223#endif /* NAMESERVER */
224 continue;
225#ifndef NAMESERVER
226 (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s.%s:%s'",
227 argv[i], suser, cmd, src,
228 argv[argc - 1], tuser, targ);
229 } else
230 (void) sprintf(buf, "rsh %s -n %s %s '%s.%s:%s'",
231 argv[i], cmd, src,
232 argv[argc - 1], tuser, targ);
233#else /* NAMESERVER */
234 (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s@%s:%s'",
235 host, suser, cmd, src,
236 tuser, thost, targ);
237 } else
238 (void) sprintf(buf, "rsh %s -n %s %s '%s@%s:%s'",
239 argv[i], cmd, src,
240 tuser, thost, targ);
241#endif /* NAMESERVER */
242 (void) susystem(buf);
243 } else { /* local to remote */
244 if (rem == -1) {
245 (void) sprintf(buf, "%s -t %s",
246 cmd, targ);
247#ifndef NAMESERVER
248 host = argv[argc - 1];
249#else /* NAMESERVER */
250 host = thost;
251#endif /* NAMESERVER */
252#ifdef NOT_DEF
253 if (seteuid(0) < 0) {
254 perror("seteuid root");
255 exit(1);
256 }
257#endif
258 rem = rcmd(&host, port, pwd->pw_name,
259 tuser, buf, 0);
260#ifdef NO_DEF
261 seteuid(myuid);
262#endif
263 if (rem < 0)
264 exit(1);
265 if (response() < 0)
266 exit(1);
267 }
268 source(1, argv+i);
269 }
270 }
271 } else { /* ... to local */
272 if (targetshouldbedirectory)
273 verifydir(argv[argc - 1]);
274 for (i = 0; i < argc - 1; i++) {
275 src = colon(argv[i]);
276 if (src == 0) { /* local to local */
277 (void) sprintf(buf, "cp%s%s %s %s",
278 iamrecursive ? " -r" : "",
279 pflag ? " -p" : "",
280 argv[i], argv[argc - 1]);
281 (void) susystem(buf);
282 } else { /* remote to local */
283 *src++ = 0;
284 if (*src == 0)
285 src = ".";
286#ifndef NAMESERVER
287 suser = strrchr(argv[i], '.');
288 if (suser) {
289 *suser++ = 0;
290 if (!okname(suser))
291#else /* NAMESERVER */
292 host = strchr(argv[i], '@');
293 if (host) {
294 *host++ = 0;
295 suser = argv[i];
296 if (*suser == '\0')
297 suser = pwd->pw_name;
298 else if (!okname(suser))
299#endif /* NAMESERVER */
300 continue;
301#ifndef NAMESERVER
302 } else
303#else /* NAMESERVER */
304 } else {
305 host = argv[i];
306#endif /* NAMESERVER */
307 suser = pwd->pw_name;
308#ifdef NAMESERVER
309 }
310#endif /* NAMESERVER */
311 (void) sprintf(buf, "%s -f %s", cmd, src);
312#ifndef NAMESERVER
313 host = argv[i];
314#endif /* NAMESERVER */
315#ifdef NOT_DEF
316 if (seteuid(0) < 0) {
317 perror("seteuid root");
318 exit(1);
319 }
320#endif
321 rem = rcmd(&host, port, pwd->pw_name, suser,
322 buf, 0);
323#ifdef NOT_DEF
324 seteuid(myuid);
325#endif
326 if (rem < 0) {
327 errs++;
328 continue;
329 }
330 sink(1, argv+argc-1);
331 (void) close(rem);
332 rem = -1;
333 }
334 }
335 }
336 exit(errs);
337}
338
339void
340verifydir(cp)
341 char *cp;
342{
343 struct stat stb;
344
345 if (stat(cp, &stb) >= 0) {
346 if ((stb.st_mode & S_IFMT) == S_IFDIR)
347 return;
348 errno = ENOTDIR;
349 }
350 error("rcp: %s: %s.\n", cp, strerror(errno));
351 exit(1);
352}
353
354char *
355colon(cp)
356 char *cp;
357{
358
359 while (*cp) {
360 if (*cp == ':')
361 return (cp);
362 if (*cp == '/')
363 return (0);
364 cp++;
365 }
366 return (0);
367}
368
369
370int
371okname(cp0)
372 char *cp0;
373{
374 register char *cp = cp0;
375 register int c;
376
377 do {
378 c = *cp;
379 if (c & 0200)
380 goto bad;
381 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
382 goto bad;
383 cp++;
384 } while (*cp);
385 return (1);
386bad:
387 fprintf(stderr, "rcp: invalid user name %s\n", cp0);
388 return (0);
389}
390
391int
392susystem(s)
393 char *s;
394{
395 int status, pid, w;
396 register void PROTO ((*istat), (int) ), PROTO ((*qstat), (int) );
397
398 if ((pid = vfork()) == 0) {
399#ifdef NOT_DEF
400 (void) setruid(myuid);
401#endif
402 execl("/bin/sh", "sh", "-c", s, (char *)0);
403 _exit(127);
404 }
405 istat = signal(SIGINT, SIG_IGN);
406 qstat = signal(SIGQUIT, SIG_IGN);
407 while ((w = wait(&status)) != pid && w != -1)
408 ;
409 if (w == -1)
410 status = -1;
411 (void) signal(SIGINT, istat);
412 (void) signal(SIGQUIT, qstat);
413 return (status);
414}
415
416void
417source(argc, argv)
418 int argc;
419 char **argv;
420{
421 char *last, *name;
422 struct stat stb;
423 static struct buffer buffer;
424 struct buffer *bp;
425 int x, sizerr, f, amt;
426 off_t i;
427 char buf[BUFSIZ];
428
429 for (x = 0; x < argc; x++) {
430 name = argv[x];
431 if ((f = open(name, 0)) < 0) {
432 error("rcp: %s: %s\n", name, strerror(errno));
433 continue;
434 }
435 if (fstat(f, &stb) < 0)
436 goto notreg;
437 switch (stb.st_mode&S_IFMT) {
438
439 case S_IFREG:
440 break;
441
442 case S_IFDIR:
443 if (iamrecursive) {
444 (void) close(f);
445 rsource(name, &stb);
446 continue;
447 }
448 /* fall into ... */
449 default:
450notreg:
451 (void) close(f);
452 error("rcp: %s: not a plain file\n", name);
453 continue;
454 }
455 last = strrchr(name, '/');
456 if (last == 0)
457 last = name;
458 else
459 last++;
460 if (pflag) {
461 /*
462 * Make it compatible with possible future
463 * versions expecting microseconds.
464 */
465 (void) sprintf(buf, "T%ld 0 %ld 0\n",
466 stb.st_mtime, stb.st_atime);
467 (void) write(rem, buf, strlen(buf));
468 if (response() < 0) {
469 (void) close(f);
470 continue;
471 }
472 }
473 (void) sprintf(buf, "C%04o %ld %s\n",
474 stb.st_mode&07777, stb.st_size, last);
475 (void) write(rem, buf, strlen(buf));
476 if (response() < 0) {
477 (void) close(f);
478 continue;
479 }
480 if ((bp = allocbuf(&buffer, f, BUFSIZ)) == 0) {
481 (void) close(f);
482 continue;
483 }
484 sizerr = 0;
485 for (i = 0; i < stb.st_size; i += bp->cnt) {
486 amt = bp->cnt;
487 if (i + amt > stb.st_size)
488 amt = stb.st_size - i;
489 if (sizerr == 0 && read(f, bp->buf, amt) != amt)
490 sizerr = 1;
491 (void) write(rem, bp->buf, amt);
492 }
493 (void) close(f);
494 if (sizerr == 0)
495 ga();
496 else
497 error("rcp: %s: file changed size\n", name);
498 (void) response();
499 }
500}
501
502
503void
504rsource(name, statp)
505 char *name;
506 struct stat *statp;
507{
508 DIR *d = opendir(name);
509 char *last;
510 struct dirent *dp;
511 char buf[BUFSIZ];
512 char *bufv[1];
513
514 if (d == 0) {
515 error("rcp: %s: %s\n", name, strerror(errno));
516 return;
517 }
518 last = strrchr(name, '/');
519 if (last == 0)
520 last = name;
521 else
522 last++;
523 if (pflag) {
524 (void) sprintf(buf, "T%ld 0 %ld 0\n",
525 statp->st_mtime, statp->st_atime);
526 (void) write(rem, buf, strlen(buf));
527 if (response() < 0) {
528 closedir(d);
529 return;
530 }
531 }
532 (void) sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last);
533 (void) write(rem, buf, strlen(buf));
534 if (response() < 0) {
535 closedir(d);
536 return;
537 }
538 while (dp = readdir(d)) {
539 if (dp->d_ino == 0)
540 continue;
541 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
542 continue;
543 if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
544 error("%s/%s: Name too long.\n", name, dp->d_name);
545 continue;
546 }
547 (void) sprintf(buf, "%s/%s", name, dp->d_name);
548 bufv[0] = buf;
549 source(1, bufv);
550 }
551 closedir(d);
552 (void) write(rem, "E\n", 2);
553 (void) response();
554}
555
556int
557response()
558{
559 char resp, c, rbuf[BUFSIZ], *cp = rbuf;
560
561 if (read(rem, &resp, 1) != 1)
562 lostconn(0);
563 switch (resp) {
564
565 case 0: /* ok */
566 return (0);
567
568 default:
569 *cp++ = resp;
570 /* fall into... */
571 case 1: /* error, followed by err msg */
572 case 2: /* fatal error, "" */
573 do {
574 if (read(rem, &c, 1) != 1)
575 lostconn(0);
576 *cp++ = c;
577 } while (cp < &rbuf[BUFSIZ] && c != '\n');
578 if (iamremote == 0)
579 (void) write(2, rbuf, cp - rbuf);
580 errs++;
581 if (resp == 1)
582 return (-1);
583 exit(1);
584 }
585 /*NOTREACHED*/
586}
587
588void
589lostconn(sig)
590int sig;
591{
592
593 if (iamremote == 0)
594 fprintf(stderr, "rcp: lost connection\n");
595 exit(1);
596}
597
598void
599sink(argc, argv)
600 int argc;
601 char **argv;
602{
603 off_t i, j, size;
604 char *targ, *whopp, *cp;
605 int of, mode, wrerr, exists, first, count, amt;
606 struct buffer *bp;
607 static struct buffer buffer;
608 struct stat stb;
609 int targisdir = 0;
610 int mask = umask(0);
611 char *myargv[1];
612 char cmdbuf[BUFSIZ], nambuf[BUFSIZ];
613 int setimes = 0;
614 struct utimbuf utimbuf;
615#define atime utimbuf.actime
616#define mtime utimbuf.modtime
617 time_t dummy;
618#define SCREWUP(str) { whopp = str; goto screwup; }
619
620#ifdef NOT_DEF
621 seteuid(pwd->pw_uid);
622#endif
623 if (!pflag)
624 (void) umask(mask);
625 if (argc != 1) {
626 error("rcp: ambiguous target\n");
627 exit(1);
628 }
629 targ = *argv;
630 if (targetshouldbedirectory)
631 verifydir(targ);
632 ga();
633 if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
634 targisdir = 1;
635 for (first = 1; ; first = 0) {
636 cp = cmdbuf;
637 if (read(rem, cp, 1) <= 0)
638 return;
639 if (*cp++ == '\n')
640 SCREWUP("unexpected '\\n'");
641 do {
642 if (read(rem, cp, 1) != 1)
643 SCREWUP("lost connection");
644 } while (*cp++ != '\n');
645 *cp = 0;
646 if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
647 if (iamremote == 0)
648 (void) write(2, cmdbuf+1, strlen(cmdbuf+1));
649 if (cmdbuf[0] == '\02')
650 exit(1);
651 errs++;
652 continue;
653 }
654 *--cp = 0;
655 cp = cmdbuf;
656 if (*cp == 'E') {
657 ga();
658 return;
659 }
660
661#define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
662 if (*cp == 'T') {
663 setimes++;
664 cp++;
665 getnum(mtime);
666 if (*cp++ != ' ')
667 SCREWUP("mtime.sec not delimited");
668 getnum(dummy);
669 if (*cp++ != ' ')
670 SCREWUP("mtime.usec not delimited");
671 getnum(atime);
672 if (*cp++ != ' ')
673 SCREWUP("atime.sec not delimited");
674 getnum(dummy);
675 if (*cp++ != '\0')
676 SCREWUP("atime.usec not delimited");
677 ga();
678 continue;
679 }
680 if (*cp != 'C' && *cp != 'D') {
681 /*
682 * Check for the case "rcp remote:foo\* local:bar".
683 * In this case, the line "No match." can be returned
684 * by the shell before the rcp command on the remote is
685 * executed so the ^Aerror_message convention isn't
686 * followed.
687 */
688 if (first) {
689 error("%s\n", cp);
690 exit(1);
691 }
692 SCREWUP("expected control record");
693 }
694 cp++;
695 mode = 0;
696 for (; cp < cmdbuf+5; cp++) {
697 if (*cp < '0' || *cp > '7')
698 SCREWUP("bad mode");
699 mode = (mode << 3) | (*cp - '0');
700 }
701 if (*cp++ != ' ')
702 SCREWUP("mode not delimited");
703 size = 0;
704 while (isdigit(*cp))
705 size = size * 10 + (*cp++ - '0');
706 if (*cp++ != ' ')
707 SCREWUP("size not delimited");
708 if (targisdir)
709 (void) sprintf(nambuf, "%s%s%s", targ,
710 *targ ? "/" : "", cp);
711 else
712 (void) strcpy(nambuf, targ);
713 exists = stat(nambuf, &stb) == 0;
714 if (cmdbuf[0] == 'D') {
715 if (exists) {
716 if ((stb.st_mode&S_IFMT) != S_IFDIR) {
717 errno = ENOTDIR;
718 goto bad;
719 }
720 if (pflag)
721 (void) chmod(nambuf, mode);
722 } else if (mkdir(nambuf, mode) < 0)
723 goto bad;
724 myargv[0] = nambuf;
725 sink(1, myargv);
726 if (setimes) {
727 setimes = 0;
728 if (utime(nambuf, &utimbuf) < 0)
729 error("rcp: can't set times on %s: %s\n",
730 nambuf, strerror(errno));
731 }
732 continue;
733 }
734 if ((of = creat(nambuf, mode)) < 0) {
735 bad:
736 error("rcp: %s: %s\n", nambuf, strerror(errno));
737 continue;
738 }
739 if (exists && pflag)
740 (void) chmod(nambuf, mode);
741 ga();
742 if ((bp = allocbuf(&buffer, of, BUFSIZ)) == 0) {
743 (void) close(of);
744 continue;
745 }
746 cp = bp->buf;
747 count = 0;
748 wrerr = 0;
749 for (i = 0; i < size; i += BUFSIZ) {
750 amt = BUFSIZ;
751 if (i + amt > size)
752 amt = size - i;
753 count += amt;
754 do {
755 j = read(rem, cp, amt);
756 if (j <= 0)
757 exit(1);
758 amt -= j;
759 cp += j;
760 } while (amt > 0);
761 if (count == bp->cnt) {
762 if (wrerr == 0 &&
763 write(of, bp->buf, count) != count)
764 wrerr++;
765 count = 0;
766 cp = bp->buf;
767 }
768 }
769 if (count != 0 && wrerr == 0 &&
770 write(of, bp->buf, count) != count)
771 wrerr++;
772 (void) close(of);
773 (void) response();
774 if (setimes) {
775 setimes = 0;
776 if (utime(nambuf, &utimbuf) < 0)
777 error("rcp: can't set times on %s: %s\n",
778 nambuf, strerror(errno));
779 }
780 if (wrerr)
781 error("rcp: %s: %s\n", nambuf, strerror(errno));
782 else
783 ga();
784 }
785screwup:
786 error("rcp: protocol screwup: %s\n", whopp);
787 exit(1);
788}
789
790struct buffer *
791allocbuf(bp, fd, blksize)
792 struct buffer *bp;
793 int fd, blksize;
794{
795 struct stat stb;
796 int size;
797
798 if (fstat(fd, &stb) < 0) {
799 error("rcp: fstat: %s\n", strerror(errno));
800 return ((struct buffer *)0);
801 }
802 size= 0;
803#if NOT_DEF
804 size = roundup(stb.st_blksize, blksize);
805#endif
806 if (size == 0)
807 size = blksize;
808 if (bp->cnt < size) {
809 if (bp->buf != 0)
810 free(bp->buf);
811 bp->buf = (char *)malloc((unsigned) size);
812 if (bp->buf == 0) {
813 error("rcp: malloc: out of memory\n");
814 return ((struct buffer *)0);
815 }
816 }
817 bp->cnt = size;
818 return (bp);
819}
820
821/*VARARGS1*/
822#if __STDC__
823void
824error (char *fmt, ...)
825#else
826error(fmt)
827char *fmt;
828#endif
829{
830 char buf[BUFSIZ], *cp = buf;
831 va_list ap;
832
833 va_start(ap, fmt);
834
835 errs++;
836 *cp++ = 1;
837 (void) vsprintf(cp, fmt, ap);
838 va_end(ap);
839 (void) write(rem, buf, strlen(buf));
840 if (iamremote == 0)
841 (void) write(2, buf+1, strlen(buf+1));
842}
843
844void
845usage()
846{
847 fprintf(stderr, "Usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n");
848}
Note: See TracBrowser for help on using the repository browser.