mkfs: symlink support
[minix.git] / commands / rcp / rcp.c
blob933b2b7197eff72a6294bb110241860f0a2b41c7
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 */
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10 All rights reserved.\n";
11 #endif /* not lint */
13 #ifndef lint
14 static char sccsid[] = "@(#)rcp.c 1.1 87/12/21 SMI"; /* from UCB 5.3 6/8/85"*/
15 #endif /* not lint */
18 * rcp
21 #undef _MINIX
22 #define NAMESERVER
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>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/wait.h>
37 #include <dirent.h>
38 #include <fcntl.h>
39 #include <pwd.h>
40 #include <signal.h>
41 #include <unistd.h>
43 #include <net/gen/netdb.h>
44 #include <net/netlib.h>
46 #if __STDC__
47 #define PROTO(func, args) func args
48 #else
49 #define PROTO(func, args) func ()
50 #endif /* __STDC__ */
52 PROTO (int main, (int argc, char *argv[]));
53 PROTO (void lostconn, (int sig));
54 PROTO (void error, (char *fmt, ...) );
55 PROTO (int response, (void) );
56 PROTO (void source, (int argc, char *argv[]) );
57 PROTO (void sink, (int argc, char *argv[]) );
58 PROTO (void usage, (void) );
59 PROTO (char *colon, (char *cp) );
60 PROTO (int okname, (char *cp0) );
61 PROTO (int susystem, (char *s) );
62 PROTO (void verifydir, (char *cp) );
63 PROTO (void rsource, (char *name, struct stat *statp) );
64 PROTO (struct buffer *allocbuf, (struct buffer *bp, int fd, int blksize) );
66 #define vfork fork
68 int rem;
69 int errs;
70 int errno;
71 int iamremote, targetshouldbedirectory;
72 int iamrecursive;
73 int myuid; /* uid of invoker */
74 int pflag;
75 struct passwd *pwd;
76 int userid;
77 int port;
79 struct buffer {
80 int cnt;
81 char *buf;
85 #define ga() (void) write(rem, "", 1)
87 main(argc, argv)
88 int argc;
89 char **argv;
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;
101 sp = getservbyname("shell", "tcp");
102 if (sp == NULL) {
103 fprintf(stderr, "rcp: shell/tcp: unknown service\n");
104 exit(1);
106 port = sp->s_port;
107 pwd = getpwuid(userid = getuid());
108 if (pwd == 0) {
109 fprintf(stderr, "who are you?\n");
110 exit(1);
113 #ifdef NOT_DEF
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.
119 myuid = getuid();
120 if (setruid(0) < 0) {
121 perror("setruid root");
122 exit(1);
124 seteuid(myuid);
125 #endif
127 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
128 (*argv)++;
129 while (**argv) switch (*(*argv)++) {
131 case 'r':
132 iamrecursive++;
133 break;
135 case 'p': /* preserve mtimes and atimes */
136 pflag++;
137 break;
139 /* The rest of these are not for users. */
140 case 'd':
141 targetshouldbedirectory = 1;
142 break;
144 case 'f': /* "from" */
145 iamremote = 1;
146 (void) response();
147 source(--argc, ++argv);
148 exit(errs);
150 case 't': /* "to" */
151 iamremote = 1;
152 sink(--argc, ++argv);
153 exit(errs);
155 default:
156 usage();
157 exit(1);
160 if (iamremote)
162 close(2);
163 open("/dev/tty", 2);
166 if (argc < 2) {
167 usage();
168 exit(1);
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;
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);
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);
268 source(1, argv+i);
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
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);
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;
330 sink(1, argv+argc-1);
331 (void) close(rem);
332 rem = -1;
336 exit(errs);
339 void
340 verifydir(cp)
341 char *cp;
343 struct stat stb;
345 if (stat(cp, &stb) >= 0) {
346 if ((stb.st_mode & S_IFMT) == S_IFDIR)
347 return;
348 errno = ENOTDIR;
350 error("rcp: %s: %s.\n", cp, strerror(errno));
351 exit(1);
354 char *
355 colon(cp)
356 char *cp;
359 while (*cp) {
360 if (*cp == ':')
361 return (cp);
362 if (*cp == '/')
363 return (0);
364 cp++;
366 return (0);
371 okname(cp0)
372 char *cp0;
374 register char *cp = cp0;
375 register int c;
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);
386 bad:
387 fprintf(stderr, "rcp: invalid user name %s\n", cp0);
388 return (0);
392 susystem(s)
393 char *s;
395 int status, pid, w;
396 register void PROTO ((*istat), (int) ), PROTO ((*qstat), (int) );
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);
405 istat = signal(SIGINT, SIG_IGN);
406 qstat = signal(SIGQUIT, SIG_IGN);
407 while ((w = wait(&status)) != pid && w != -1)
409 if (w == -1)
410 status = -1;
411 (void) signal(SIGINT, istat);
412 (void) signal(SIGQUIT, qstat);
413 return (status);
416 void
417 source(argc, argv)
418 int argc;
419 char **argv;
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];
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;
435 if (fstat(f, &stb) < 0)
436 goto notreg;
437 switch (stb.st_mode&S_IFMT) {
439 case S_IFREG:
440 break;
442 case S_IFDIR:
443 if (iamrecursive) {
444 (void) close(f);
445 rsource(name, &stb);
446 continue;
448 /* fall into ... */
449 default:
450 notreg:
451 (void) close(f);
452 error("rcp: %s: not a plain file\n", name);
453 continue;
455 last = strrchr(name, '/');
456 if (last == 0)
457 last = name;
458 else
459 last++;
460 if (pflag) {
462 * Make it compatible with possible future
463 * versions expecting microseconds.
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;
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;
480 if ((bp = allocbuf(&buffer, f, BUFSIZ)) == 0) {
481 (void) close(f);
482 continue;
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);
493 (void) close(f);
494 if (sizerr == 0)
495 ga();
496 else
497 error("rcp: %s: file changed size\n", name);
498 (void) response();
503 void
504 rsource(name, statp)
505 char *name;
506 struct stat *statp;
508 DIR *d = opendir(name);
509 char *last;
510 struct dirent *dp;
511 char buf[BUFSIZ];
512 char *bufv[1];
514 if (d == 0) {
515 error("rcp: %s: %s\n", name, strerror(errno));
516 return;
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;
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;
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;
547 (void) sprintf(buf, "%s/%s", name, dp->d_name);
548 bufv[0] = buf;
549 source(1, bufv);
551 closedir(d);
552 (void) write(rem, "E\n", 2);
553 (void) response();
557 response()
559 char resp, c, rbuf[BUFSIZ], *cp = rbuf;
561 if (read(rem, &resp, 1) != 1)
562 lostconn(0);
563 switch (resp) {
565 case 0: /* ok */
566 return (0);
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);
585 /*NOTREACHED*/
588 void
589 lostconn(sig)
590 int sig;
593 if (iamremote == 0)
594 fprintf(stderr, "rcp: lost connection\n");
595 exit(1);
598 void
599 sink(argc, argv)
600 int argc;
601 char **argv;
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; }
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);
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;
654 *--cp = 0;
655 cp = cmdbuf;
656 if (*cp == 'E') {
657 ga();
658 return;
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;
680 if (*cp != 'C' && *cp != 'D') {
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.
688 if (first) {
689 error("%s\n", cp);
690 exit(1);
692 SCREWUP("expected control record");
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');
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;
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));
732 continue;
734 if ((of = creat(nambuf, mode)) < 0) {
735 bad:
736 error("rcp: %s: %s\n", nambuf, strerror(errno));
737 continue;
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;
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;
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));
780 if (wrerr)
781 error("rcp: %s: %s\n", nambuf, strerror(errno));
782 else
783 ga();
785 screwup:
786 error("rcp: protocol screwup: %s\n", whopp);
787 exit(1);
790 struct buffer *
791 allocbuf(bp, fd, blksize)
792 struct buffer *bp;
793 int fd, blksize;
795 struct stat stb;
796 int size;
798 if (fstat(fd, &stb) < 0) {
799 error("rcp: fstat: %s\n", strerror(errno));
800 return ((struct buffer *)0);
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);
817 bp->cnt = size;
818 return (bp);
821 /*VARARGS1*/
822 #if __STDC__
823 void
824 error (char *fmt, ...)
825 #else
826 error(fmt)
827 char *fmt;
828 #endif
830 char buf[BUFSIZ], *cp = buf;
831 va_list ap;
833 va_start(ap, fmt);
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));
844 void
845 usage()
847 fprintf(stderr, "Usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n");