1 /* 2 * Copyright (c) 1983, 1990, 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "rcp_locl.h" 35 #include <getarg.h> 36 37 #define RSH_PROGRAM "rsh" 38 39 struct passwd *pwd; 40 uid_t userid; 41 int errs, remin, remout; 42 int pflag, iamremote, iamrecursive, targetshouldbedirectory; 43 int doencrypt, noencrypt; 44 int usebroken, usekrb5, forwardtkt; 45 char *port; 46 47 #define CMDNEEDS 64 48 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 49 50 int response (void); 51 void rsource (char *, struct stat *); 52 void sink (int, char *[]); 53 void source (int, char *[]); 54 void tolocal (int, char *[]); 55 void toremote (char *, int, char *[]); 56 57 int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); 58 59 static int fflag, tflag; 60 61 static int version_flag, help_flag; 62 63 struct getargs args[] = { 64 { NULL, '5', arg_flag, &usekrb5, "use Kerberos 5 authentication" }, 65 { NULL, 'F', arg_flag, &forwardtkt, "forward credentials" }, 66 { NULL, 'K', arg_flag, &usebroken, "use BSD authentication" }, 67 { NULL, 'P', arg_string, &port, "non-default port", "port" }, 68 { NULL, 'p', arg_flag, &pflag, "preserve file permissions" }, 69 { NULL, 'r', arg_flag, &iamrecursive, "recursive mode" }, 70 { NULL, 'x', arg_flag, &doencrypt, "use encryption" }, 71 { NULL, 'z', arg_flag, &noencrypt, "don't encrypt" }, 72 { NULL, 'd', arg_flag, &targetshouldbedirectory }, 73 { NULL, 'f', arg_flag, &fflag }, 74 { NULL, 't', arg_flag, &tflag }, 75 { "version", 0, arg_flag, &version_flag }, 76 { "help", 0, arg_flag, &help_flag } 77 }; 78 79 static void 80 usage (int ret) 81 { 82 arg_printusage (args, 83 sizeof(args) / sizeof(args[0]), 84 NULL, 85 "file1 file2|file... directory"); 86 exit (ret); 87 } 88 89 int 90 main(int argc, char **argv) 91 { 92 char *targ; 93 int optind = 0; 94 95 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, 96 &optind)) 97 usage (1); 98 if(help_flag) 99 usage(0); 100 if (version_flag) { 101 print_version (NULL); 102 return 0; 103 } 104 105 iamremote = (fflag || tflag); 106 107 argc -= optind; 108 argv += optind; 109 110 if ((pwd = getpwuid(userid = getuid())) == NULL) 111 errx(1, "unknown user %d", (int)userid); 112 113 remin = STDIN_FILENO; /* XXX */ 114 remout = STDOUT_FILENO; 115 116 if (fflag) { /* Follow "protocol", send data. */ 117 response(); 118 setuid(userid); 119 source(argc, argv); 120 exit(errs); 121 } 122 123 if (tflag) { /* Receive data. */ 124 setuid(userid); 125 sink(argc, argv); 126 exit(errs); 127 } 128 129 if (argc < 2) 130 usage(1); 131 if (argc > 2) 132 targetshouldbedirectory = 1; 133 134 remin = remout = -1; 135 /* Command to be executed on remote system using "rsh". */ 136 sprintf(cmd, "rcp%s%s%s", iamrecursive ? " -r" : "", 137 pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); 138 139 signal(SIGPIPE, lostconn); 140 141 if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 142 toremote(targ, argc, argv); 143 else { 144 tolocal(argc, argv); /* Dest is local host. */ 145 if (targetshouldbedirectory) 146 verifydir(argv[argc - 1]); 147 } 148 exit(errs); 149 } 150 151 void 152 toremote(char *targ, int argc, char **argv) 153 { 154 int i, len; 155 char *bp, *host, *src, *suser, *thost, *tuser; 156 157 *targ++ = 0; 158 if (*targ == 0) 159 targ = "."; 160 161 if ((thost = strchr(argv[argc - 1], '@'))) { 162 /* user@host */ 163 *thost++ = 0; 164 tuser = argv[argc - 1]; 165 if (*tuser == '\0') 166 tuser = NULL; 167 else if (!okname(tuser)) 168 exit(1); 169 } else { 170 thost = argv[argc - 1]; 171 tuser = NULL; 172 } 173 174 for (i = 0; i < argc - 1; i++) { 175 src = colon(argv[i]); 176 if (src) { /* remote to remote */ 177 *src++ = 0; 178 if (*src == 0) 179 src = "."; 180 host = strchr(argv[i], '@'); 181 len = strlen(_PATH_RSH) + strlen(argv[i]) + 182 strlen(src) + (tuser ? strlen(tuser) : 0) + 183 strlen(thost) + strlen(targ) + CMDNEEDS + 20; 184 if (!(bp = malloc(len))) 185 err(1, "malloc"); 186 if (host) { 187 *host++ = 0; 188 suser = argv[i]; 189 if (*suser == '\0') 190 suser = pwd->pw_name; 191 else if (!okname(suser)) 192 continue; 193 snprintf(bp, len, 194 "%s %s -l %s -n %s %s '%s%s%s:%s'", 195 _PATH_RSH, host, suser, cmd, src, 196 tuser ? tuser : "", tuser ? "@" : "", 197 thost, targ); 198 } else 199 snprintf(bp, len, 200 "exec %s %s -n %s %s '%s%s%s:%s'", 201 _PATH_RSH, argv[i], cmd, src, 202 tuser ? tuser : "", tuser ? "@" : "", 203 thost, targ); 204 susystem(bp, userid); 205 free(bp); 206 } else { /* local to remote */ 207 if (remin == -1) { 208 len = strlen(targ) + CMDNEEDS + 20; 209 if (!(bp = malloc(len))) 210 err(1, "malloc"); 211 snprintf(bp, len, "%s -t %s", cmd, targ); 212 host = thost; 213 214 if (do_cmd(host, tuser, bp, &remin, &remout) < 0) 215 exit(1); 216 217 if (response() < 0) 218 exit(1); 219 free(bp); 220 setuid(userid); 221 } 222 source(1, argv+i); 223 } 224 } 225 } 226 227 void 228 tolocal(int argc, char **argv) 229 { 230 int i, len; 231 char *bp, *host, *src, *suser; 232 233 for (i = 0; i < argc - 1; i++) { 234 if (!(src = colon(argv[i]))) { /* Local to local. */ 235 len = strlen(_PATH_CP) + strlen(argv[i]) + 236 strlen(argv[argc - 1]) + 20; 237 if (!(bp = malloc(len))) 238 err(1, "malloc"); 239 snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, 240 iamrecursive ? " -PR" : "", pflag ? " -p" : "", 241 argv[i], argv[argc - 1]); 242 if (susystem(bp, userid)) 243 ++errs; 244 free(bp); 245 continue; 246 } 247 *src++ = 0; 248 if (*src == 0) 249 src = "."; 250 if ((host = strchr(argv[i], '@')) == NULL) { 251 host = argv[i]; 252 suser = pwd->pw_name; 253 } else { 254 *host++ = 0; 255 suser = argv[i]; 256 if (*suser == '\0') 257 suser = pwd->pw_name; 258 else if (!okname(suser)) 259 continue; 260 } 261 len = strlen(src) + CMDNEEDS + 20; 262 if ((bp = malloc(len)) == NULL) 263 err(1, "malloc"); 264 snprintf(bp, len, "%s -f %s", cmd, src); 265 if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 266 free(bp); 267 ++errs; 268 continue; 269 } 270 free(bp); 271 sink(1, argv + argc - 1); 272 seteuid(0); 273 close(remin); 274 remin = remout = -1; 275 } 276 } 277 278 static char * 279 sizestr(off_t size) 280 { 281 static char ss[32]; 282 char *p; 283 ss[sizeof(ss) - 1] = '\0'; 284 for(p = ss + sizeof(ss) - 2; p >= ss; p--) { 285 *p = '0' + size % 10; 286 size /= 10; 287 if(size == 0) 288 break; 289 } 290 return ss; 291 } 292 293 294 void 295 source(int argc, char **argv) 296 { 297 struct stat stb; 298 static BUF buffer; 299 BUF *bp; 300 off_t i; 301 int amt, fd, haderr, indx, result; 302 char *last, *name, buf[BUFSIZ]; 303 304 for (indx = 0; indx < argc; ++indx) { 305 name = argv[indx]; 306 if ((fd = open(name, O_RDONLY, 0)) < 0) 307 goto syserr; 308 if (fstat(fd, &stb)) { 309 syserr: run_err("%s: %s", name, strerror(errno)); 310 goto next; 311 } 312 switch (stb.st_mode & S_IFMT) { 313 case S_IFREG: 314 break; 315 case S_IFDIR: 316 if (iamrecursive) { 317 rsource(name, &stb); 318 goto next; 319 } 320 /* FALLTHROUGH */ 321 default: 322 run_err("%s: not a regular file", name); 323 goto next; 324 } 325 if ((last = strrchr(name, '/')) == NULL) 326 last = name; 327 else 328 ++last; 329 if (pflag) { 330 /* 331 * Make it compatible with possible future 332 * versions expecting microseconds. 333 */ 334 snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", 335 (long)stb.st_mtime, 336 (long)stb.st_atime); 337 write(remout, buf, strlen(buf)); 338 if (response() < 0) 339 goto next; 340 } 341 #define MODEMASK (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 342 snprintf(buf, sizeof(buf), "C%04o %s %s\n", 343 stb.st_mode & MODEMASK, sizestr(stb.st_size), last); 344 write(remout, buf, strlen(buf)); 345 if (response() < 0) 346 goto next; 347 if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { 348 next: close(fd); 349 continue; 350 } 351 352 /* Keep writing after an error so that we stay sync'd up. */ 353 for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 354 amt = bp->cnt; 355 if (i + amt > stb.st_size) 356 amt = stb.st_size - i; 357 if (!haderr) { 358 result = read(fd, bp->buf, amt); 359 if (result != amt) 360 haderr = result >= 0 ? EIO : errno; 361 } 362 if (haderr) 363 write(remout, bp->buf, amt); 364 else { 365 result = write(remout, bp->buf, amt); 366 if (result != amt) 367 haderr = result >= 0 ? EIO : errno; 368 } 369 } 370 if (close(fd) && !haderr) 371 haderr = errno; 372 if (!haderr) 373 write(remout, "", 1); 374 else 375 run_err("%s: %s", name, strerror(haderr)); 376 response(); 377 } 378 } 379 380 void 381 rsource(char *name, struct stat *statp) 382 { 383 DIR *dirp; 384 struct dirent *dp; 385 char *last, *vect[1], path[MAXPATHLEN]; 386 387 if (!(dirp = opendir(name))) { 388 run_err("%s: %s", name, strerror(errno)); 389 return; 390 } 391 last = strrchr(name, '/'); 392 if (last == 0) 393 last = name; 394 else 395 last++; 396 if (pflag) { 397 snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", 398 (long)statp->st_mtime, 399 (long)statp->st_atime); 400 write(remout, path, strlen(path)); 401 if (response() < 0) { 402 closedir(dirp); 403 return; 404 } 405 } 406 snprintf(path, sizeof(path), 407 "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); 408 write(remout, path, strlen(path)); 409 if (response() < 0) { 410 closedir(dirp); 411 return; 412 } 413 while ((dp = readdir(dirp))) { 414 if (dp->d_ino == 0) 415 continue; 416 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 417 continue; 418 if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) { 419 run_err("%s/%s: name too long", name, dp->d_name); 420 continue; 421 } 422 snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); 423 vect[0] = path; 424 source(1, vect); 425 } 426 closedir(dirp); 427 write(remout, "E\n", 2); 428 response(); 429 } 430 431 void 432 sink(int argc, char **argv) 433 { 434 static BUF buffer; 435 struct stat stb; 436 struct timeval tv[2]; 437 enum { YES, NO, DISPLAYED } wrerr; 438 BUF *bp; 439 off_t i, j, size; 440 int amt, count, exists, first, mask, mode, ofd, omode; 441 int setimes, targisdir, wrerrno = 0; 442 char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ]; 443 444 #define atime tv[0] 445 #define mtime tv[1] 446 #define SCREWUP(str) { why = str; goto screwup; } 447 448 setimes = targisdir = 0; 449 mask = umask(0); 450 if (!pflag) 451 umask(mask); 452 if (argc != 1) { 453 run_err("ambiguous target"); 454 exit(1); 455 } 456 targ = *argv; 457 if (targetshouldbedirectory) 458 verifydir(targ); 459 write(remout, "", 1); 460 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 461 targisdir = 1; 462 for (first = 1;; first = 0) { 463 cp = buf; 464 if (read(remin, cp, 1) <= 0) 465 return; 466 if (*cp++ == '\n') 467 SCREWUP("unexpected <newline>"); 468 do { 469 if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 470 SCREWUP("lost connection"); 471 *cp++ = ch; 472 } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 473 *cp = 0; 474 475 if (buf[0] == '\01' || buf[0] == '\02') { 476 if (iamremote == 0) 477 write(STDERR_FILENO, 478 buf + 1, strlen(buf + 1)); 479 if (buf[0] == '\02') 480 exit(1); 481 ++errs; 482 continue; 483 } 484 if (buf[0] == 'E') { 485 write(remout, "", 1); 486 return; 487 } 488 489 if (ch == '\n') 490 *--cp = 0; 491 492 cp = buf; 493 if (*cp == 'T') { 494 setimes++; 495 cp++; 496 mtime.tv_sec = strtol(cp, &cp, 10); 497 if (!cp || *cp++ != ' ') 498 SCREWUP("mtime.sec not delimited"); 499 mtime.tv_usec = strtol(cp, &cp, 10); 500 if (!cp || *cp++ != ' ') 501 SCREWUP("mtime.usec not delimited"); 502 atime.tv_sec = strtol(cp, &cp, 10); 503 if (!cp || *cp++ != ' ') 504 SCREWUP("atime.sec not delimited"); 505 atime.tv_usec = strtol(cp, &cp, 10); 506 if (!cp || *cp++ != '\0') 507 SCREWUP("atime.usec not delimited"); 508 write(remout, "", 1); 509 continue; 510 } 511 if (*cp != 'C' && *cp != 'D') { 512 /* 513 * Check for the case "rcp remote:foo\* local:bar". 514 * In this case, the line "No match." can be returned 515 * by the shell before the rcp command on the remote is 516 * executed so the ^Aerror_message convention isn't 517 * followed. 518 */ 519 if (first) { 520 run_err("%s", cp); 521 exit(1); 522 } 523 SCREWUP("expected control record"); 524 } 525 mode = 0; 526 for (++cp; cp < buf + 5; cp++) { 527 if (*cp < '0' || *cp > '7') 528 SCREWUP("bad mode"); 529 mode = (mode << 3) | (*cp - '0'); 530 } 531 if (*cp++ != ' ') 532 SCREWUP("mode not delimited"); 533 534 for (size = 0; isdigit(*cp);) 535 size = size * 10 + (*cp++ - '0'); 536 if (*cp++ != ' ') 537 SCREWUP("size not delimited"); 538 if (targisdir) { 539 static char *namebuf; 540 static int cursize; 541 size_t need; 542 543 need = strlen(targ) + strlen(cp) + 250; 544 if (need > cursize) { 545 if (!(namebuf = malloc(need))) 546 run_err("%s", strerror(errno)); 547 } 548 snprintf(namebuf, need, "%s%s%s", targ, 549 *targ ? "/" : "", cp); 550 np = namebuf; 551 } else 552 np = targ; 553 exists = stat(np, &stb) == 0; 554 if (buf[0] == 'D') { 555 int mod_flag = pflag; 556 if (exists) { 557 if (!S_ISDIR(stb.st_mode)) { 558 errno = ENOTDIR; 559 goto bad; 560 } 561 if (pflag) 562 chmod(np, mode); 563 } else { 564 /* Handle copying from a read-only directory */ 565 mod_flag = 1; 566 if (mkdir(np, mode | S_IRWXU) < 0) 567 goto bad; 568 } 569 vect[0] = np; 570 sink(1, vect); 571 if (setimes) { 572 setimes = 0; 573 if (utimes(np, tv) < 0) 574 run_err("%s: set times: %s", 575 np, strerror(errno)); 576 } 577 if (mod_flag) 578 chmod(np, mode); 579 continue; 580 } 581 omode = mode; 582 mode |= S_IWRITE; 583 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 584 bad: run_err("%s: %s", np, strerror(errno)); 585 continue; 586 } 587 write(remout, "", 1); 588 if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { 589 close(ofd); 590 continue; 591 } 592 cp = bp->buf; 593 wrerr = NO; 594 for (count = i = 0; i < size; i += BUFSIZ) { 595 amt = BUFSIZ; 596 if (i + amt > size) 597 amt = size - i; 598 count += amt; 599 if((j = net_read(remin, cp, amt)) != amt) { 600 run_err("%s", j ? strerror(errno) : 601 "dropped connection"); 602 exit(1); 603 } 604 amt -= j; 605 cp += j; 606 if (count == bp->cnt) { 607 /* Keep reading so we stay sync'd up. */ 608 if (wrerr == NO) { 609 j = write(ofd, bp->buf, count); 610 if (j != count) { 611 wrerr = YES; 612 wrerrno = j >= 0 ? EIO : errno; 613 } 614 } 615 count = 0; 616 cp = bp->buf; 617 } 618 } 619 if (count != 0 && wrerr == NO && 620 (j = write(ofd, bp->buf, count)) != count) { 621 wrerr = YES; 622 wrerrno = j >= 0 ? EIO : errno; 623 } 624 if (ftruncate(ofd, size)) { 625 run_err("%s: truncate: %s", np, strerror(errno)); 626 wrerr = DISPLAYED; 627 } 628 if (pflag) { 629 if (exists || omode != mode) 630 if (fchmod(ofd, omode)) 631 run_err("%s: set mode: %s", 632 np, strerror(errno)); 633 } else { 634 if (!exists && omode != mode) 635 if (fchmod(ofd, omode & ~mask)) 636 run_err("%s: set mode: %s", 637 np, strerror(errno)); 638 } 639 close(ofd); 640 response(); 641 if (setimes && wrerr == NO) { 642 setimes = 0; 643 if (utimes(np, tv) < 0) { 644 run_err("%s: set times: %s", 645 np, strerror(errno)); 646 wrerr = DISPLAYED; 647 } 648 } 649 switch(wrerr) { 650 case YES: 651 run_err("%s: %s", np, strerror(wrerrno)); 652 break; 653 case NO: 654 write(remout, "", 1); 655 break; 656 case DISPLAYED: 657 break; 658 } 659 } 660 screwup: 661 run_err("protocol error: %s", why); 662 exit(1); 663 } 664 665 int 666 response(void) 667 { 668 char ch, *cp, resp, rbuf[BUFSIZ]; 669 670 if (read(remin, &resp, sizeof(resp)) != sizeof(resp)) 671 lostconn(0); 672 673 cp = rbuf; 674 switch(resp) { 675 case 0: /* ok */ 676 return (0); 677 default: 678 *cp++ = resp; 679 /* FALLTHROUGH */ 680 case 1: /* error, followed by error msg */ 681 case 2: /* fatal error, "" */ 682 do { 683 if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 684 lostconn(0); 685 *cp++ = ch; 686 } while (cp < &rbuf[BUFSIZ] && ch != '\n'); 687 688 if (!iamremote) 689 write(STDERR_FILENO, rbuf, cp - rbuf); 690 ++errs; 691 if (resp == 1) 692 return (-1); 693 exit(1); 694 } 695 /* NOTREACHED */ 696 } 697 698 #include <stdarg.h> 699 700 void 701 run_err(const char *fmt, ...) 702 { 703 static FILE *fp; 704 va_list ap; 705 va_start(ap, fmt); 706 707 ++errs; 708 if (fp == NULL && !(fp = fdopen(remout, "w"))) 709 return; 710 fprintf(fp, "%c", 0x01); 711 fprintf(fp, "rcp: "); 712 vfprintf(fp, fmt, ap); 713 fprintf(fp, "\n"); 714 fflush(fp); 715 716 if (!iamremote) 717 vwarnx(fmt, ap); 718 719 va_end(ap); 720 } 721 722 /* 723 * This function executes the given command as the specified user on the 724 * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 725 * assigns the input and output file descriptors on success. 726 * 727 * If it cannot create necessary pipes it exits with error message. 728 */ 729 730 int 731 do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 732 { 733 int pin[2], pout[2], reserved[2]; 734 735 /* 736 * Reserve two descriptors so that the real pipes won't get 737 * descriptors 0 and 1 because that will screw up dup2 below. 738 */ 739 pipe(reserved); 740 741 /* Create a socket pair for communicating with rsh. */ 742 if (pipe(pin) < 0) { 743 perror("pipe"); 744 exit(255); 745 } 746 if (pipe(pout) < 0) { 747 perror("pipe"); 748 exit(255); 749 } 750 751 /* Free the reserved descriptors. */ 752 close(reserved[0]); 753 close(reserved[1]); 754 755 /* For a child to execute the command on the remote host using rsh. */ 756 if (fork() == 0) { 757 char *args[100]; 758 unsigned int i; 759 760 /* Child. */ 761 close(pin[1]); 762 close(pout[0]); 763 dup2(pin[0], 0); 764 dup2(pout[1], 1); 765 close(pin[0]); 766 close(pout[1]); 767 768 i = 0; 769 args[i++] = RSH_PROGRAM; 770 if (usekrb5) 771 args[i++] = "-5"; 772 if (usebroken) 773 args[i++] = "-K"; 774 if (doencrypt) 775 args[i++] = "-x"; 776 if (forwardtkt) 777 args[i++] = "-F"; 778 if (noencrypt) 779 args[i++] = "-z"; 780 if (port != NULL) { 781 args[i++] = "-p"; 782 args[i++] = port; 783 } 784 if (remuser != NULL) { 785 args[i++] = "-l"; 786 args[i++] = remuser; 787 } 788 args[i++] = host; 789 args[i++] = cmd; 790 args[i++] = NULL; 791 792 execvp(RSH_PROGRAM, args); 793 perror(RSH_PROGRAM); 794 exit(1); 795 } 796 /* Parent. Close the other side, and return the local side. */ 797 close(pin[0]); 798 *fdout = pin[1]; 799 close(pout[1]); 800 *fdin = pout[0]; 801 return 0; 802 } 803