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