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 setprogname(argv[0]); 96 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, 97 &optind)) 98 usage (1); 99 if(help_flag) 100 usage(0); 101 if (version_flag) { 102 print_version (NULL); 103 return 0; 104 } 105 106 iamremote = (fflag || tflag); 107 108 argc -= optind; 109 argv += optind; 110 111 if ((pwd = getpwuid(userid = getuid())) == NULL) 112 errx(1, "unknown user %d", (int)userid); 113 114 remin = STDIN_FILENO; /* XXX */ 115 remout = STDOUT_FILENO; 116 117 if (fflag) { /* Follow "protocol", send data. */ 118 response(); 119 setuid(userid); 120 source(argc, argv); 121 exit(errs); 122 } 123 124 if (tflag) { /* Receive data. */ 125 setuid(userid); 126 sink(argc, argv); 127 exit(errs); 128 } 129 130 if (argc < 2) 131 usage(1); 132 if (argc > 2) 133 targetshouldbedirectory = 1; 134 135 remin = remout = -1; 136 /* Command to be executed on remote system using "rsh". */ 137 snprintf(cmd, sizeof(cmd), 138 "rcp%s%s%s", iamrecursive ? " -r" : "", 139 pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); 140 141 signal(SIGPIPE, lostconn); 142 143 if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 144 toremote(targ, argc, argv); 145 else { 146 tolocal(argc, argv); /* Dest is local host. */ 147 if (targetshouldbedirectory) 148 verifydir(argv[argc - 1]); 149 } 150 exit(errs); 151 } 152 153 void 154 toremote(char *targ, int argc, char **argv) 155 { 156 int i; 157 char *bp, *host, *src, *suser, *thost, *tuser; 158 159 *targ++ = 0; 160 if (*targ == 0) 161 targ = "."; 162 163 if ((thost = strchr(argv[argc - 1], '@'))) { 164 /* user@host */ 165 *thost++ = 0; 166 tuser = argv[argc - 1]; 167 if (*tuser == '\0') 168 tuser = NULL; 169 else if (!okname(tuser)) 170 exit(1); 171 } else { 172 thost = argv[argc - 1]; 173 tuser = NULL; 174 } 175 176 for (i = 0; i < argc - 1; i++) { 177 src = colon(argv[i]); 178 if (src) { /* remote to remote */ 179 *src++ = 0; 180 if (*src == 0) 181 src = "."; 182 host = strchr(argv[i], '@'); 183 if (host) { 184 *host++ = '\0'; 185 suser = argv[i]; 186 if (*suser == '\0') 187 suser = pwd->pw_name; 188 else if (!okname(suser)) 189 continue; 190 asprintf(&bp, 191 "%s %s -l %s -n %s %s '%s%s%s:%s'", 192 _PATH_RSH, host, suser, cmd, src, 193 tuser ? tuser : "", tuser ? "@" : "", 194 thost, targ); 195 } else { 196 asprintf(&bp, 197 "exec %s %s -n %s %s '%s%s%s:%s'", 198 _PATH_RSH, argv[i], cmd, src, 199 tuser ? tuser : "", tuser ? "@" : "", 200 thost, targ); 201 } 202 if (bp == NULL) 203 err (1, "malloc"); 204 susystem(bp, userid); 205 free(bp); 206 } else { /* local to remote */ 207 if (remin == -1) { 208 asprintf(&bp, "%s -t %s", cmd, targ); 209 if (bp == NULL) 210 err (1, "malloc"); 211 host = thost; 212 213 if (do_cmd(host, tuser, bp, &remin, &remout) < 0) 214 exit(1); 215 216 if (response() < 0) 217 exit(1); 218 free(bp); 219 setuid(userid); 220 } 221 source(1, argv+i); 222 } 223 } 224 } 225 226 void 227 tolocal(int argc, char **argv) 228 { 229 int i; 230 char *bp, *host, *src, *suser; 231 232 for (i = 0; i < argc - 1; i++) { 233 if (!(src = colon(argv[i]))) { /* Local to local. */ 234 asprintf(&bp, "exec %s%s%s %s %s", _PATH_CP, 235 iamrecursive ? " -PR" : "", pflag ? " -p" : "", 236 argv[i], argv[argc - 1]); 237 if (bp == NULL) 238 err (1, "malloc"); 239 if (susystem(bp, userid)) 240 ++errs; 241 free(bp); 242 continue; 243 } 244 *src++ = 0; 245 if (*src == 0) 246 src = "."; 247 if ((host = strchr(argv[i], '@')) == NULL) { 248 host = argv[i]; 249 suser = pwd->pw_name; 250 } else { 251 *host++ = 0; 252 suser = argv[i]; 253 if (*suser == '\0') 254 suser = pwd->pw_name; 255 else if (!okname(suser)) 256 continue; 257 } 258 asprintf(&bp, "%s -f %s", cmd, src); 259 if (bp == NULL) 260 err (1, "malloc"); 261 if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 262 free(bp); 263 ++errs; 264 continue; 265 } 266 free(bp); 267 sink(1, argv + argc - 1); 268 seteuid(0); 269 close(remin); 270 remin = remout = -1; 271 } 272 } 273 274 void 275 source(int argc, char **argv) 276 { 277 struct stat stb; 278 static BUF buffer; 279 BUF *bp; 280 off_t i; 281 int amt, fd, haderr, indx, result; 282 char *last, *name, buf[BUFSIZ]; 283 284 for (indx = 0; indx < argc; ++indx) { 285 name = argv[indx]; 286 if ((fd = open(name, O_RDONLY, 0)) < 0) 287 goto syserr; 288 if (fstat(fd, &stb)) { 289 syserr: run_err("%s: %s", name, strerror(errno)); 290 goto next; 291 } 292 switch (stb.st_mode & S_IFMT) { 293 case S_IFREG: 294 break; 295 case S_IFDIR: 296 if (iamrecursive) { 297 rsource(name, &stb); 298 goto next; 299 } 300 /* FALLTHROUGH */ 301 default: 302 run_err("%s: not a regular file", name); 303 goto next; 304 } 305 if ((last = strrchr(name, '/')) == NULL) 306 last = name; 307 else 308 ++last; 309 if (pflag) { 310 /* 311 * Make it compatible with possible future 312 * versions expecting microseconds. 313 */ 314 snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", 315 (long)stb.st_mtime, 316 (long)stb.st_atime); 317 write(remout, buf, strlen(buf)); 318 if (response() < 0) 319 goto next; 320 } 321 #define MODEMASK (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 322 snprintf(buf, sizeof(buf), "C%04o %lu %s\n", 323 stb.st_mode & MODEMASK, 324 (unsigned long)stb.st_size, 325 last); 326 write(remout, buf, strlen(buf)); 327 if (response() < 0) 328 goto next; 329 if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { 330 next: close(fd); 331 continue; 332 } 333 334 /* Keep writing after an error so that we stay sync'd up. */ 335 for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 336 amt = bp->cnt; 337 if (i + amt > stb.st_size) 338 amt = stb.st_size - i; 339 if (!haderr) { 340 result = read(fd, bp->buf, amt); 341 if (result != amt) 342 haderr = result >= 0 ? EIO : errno; 343 } 344 if (haderr) 345 write(remout, bp->buf, amt); 346 else { 347 result = write(remout, bp->buf, amt); 348 if (result != amt) 349 haderr = result >= 0 ? EIO : errno; 350 } 351 } 352 if (close(fd) && !haderr) 353 haderr = errno; 354 if (!haderr) 355 write(remout, "", 1); 356 else 357 run_err("%s: %s", name, strerror(haderr)); 358 response(); 359 } 360 } 361 362 void 363 rsource(char *name, struct stat *statp) 364 { 365 DIR *dirp; 366 struct dirent *dp; 367 char *last, *vect[1], path[MAXPATHLEN]; 368 369 if (!(dirp = opendir(name))) { 370 run_err("%s: %s", name, strerror(errno)); 371 return; 372 } 373 last = strrchr(name, '/'); 374 if (last == 0) 375 last = name; 376 else 377 last++; 378 if (pflag) { 379 snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", 380 (long)statp->st_mtime, 381 (long)statp->st_atime); 382 write(remout, path, strlen(path)); 383 if (response() < 0) { 384 closedir(dirp); 385 return; 386 } 387 } 388 snprintf(path, sizeof(path), 389 "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); 390 write(remout, path, strlen(path)); 391 if (response() < 0) { 392 closedir(dirp); 393 return; 394 } 395 while ((dp = readdir(dirp))) { 396 if (dp->d_ino == 0) 397 continue; 398 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 399 continue; 400 if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) { 401 run_err("%s/%s: name too long", name, dp->d_name); 402 continue; 403 } 404 snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); 405 vect[0] = path; 406 source(1, vect); 407 } 408 closedir(dirp); 409 write(remout, "E\n", 2); 410 response(); 411 } 412 413 void 414 sink(int argc, char **argv) 415 { 416 static BUF buffer; 417 struct stat stb; 418 struct timeval tv[2]; 419 enum { YES, NO, DISPLAYED } wrerr; 420 BUF *bp; 421 off_t i, j, size; 422 int amt, count, exists, first, mask, mode, ofd, omode; 423 int setimes, targisdir, wrerrno = 0; 424 char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ]; 425 426 #define atime tv[0] 427 #define mtime tv[1] 428 #define SCREWUP(str) { why = str; goto screwup; } 429 430 setimes = targisdir = 0; 431 mask = umask(0); 432 if (!pflag) 433 umask(mask); 434 if (argc != 1) { 435 run_err("ambiguous target"); 436 exit(1); 437 } 438 targ = *argv; 439 if (targetshouldbedirectory) 440 verifydir(targ); 441 write(remout, "", 1); 442 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 443 targisdir = 1; 444 for (first = 1;; first = 0) { 445 cp = buf; 446 if (read(remin, cp, 1) <= 0) 447 return; 448 if (*cp++ == '\n') 449 SCREWUP("unexpected <newline>"); 450 do { 451 if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 452 SCREWUP("lost connection"); 453 *cp++ = ch; 454 } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 455 *cp = 0; 456 457 if (buf[0] == '\01' || buf[0] == '\02') { 458 if (iamremote == 0) 459 write(STDERR_FILENO, 460 buf + 1, strlen(buf + 1)); 461 if (buf[0] == '\02') 462 exit(1); 463 ++errs; 464 continue; 465 } 466 if (buf[0] == 'E') { 467 write(remout, "", 1); 468 return; 469 } 470 471 if (ch == '\n') 472 *--cp = 0; 473 474 cp = buf; 475 if (*cp == 'T') { 476 setimes++; 477 cp++; 478 mtime.tv_sec = strtol(cp, &cp, 10); 479 if (!cp || *cp++ != ' ') 480 SCREWUP("mtime.sec not delimited"); 481 mtime.tv_usec = strtol(cp, &cp, 10); 482 if (!cp || *cp++ != ' ') 483 SCREWUP("mtime.usec not delimited"); 484 atime.tv_sec = strtol(cp, &cp, 10); 485 if (!cp || *cp++ != ' ') 486 SCREWUP("atime.sec not delimited"); 487 atime.tv_usec = strtol(cp, &cp, 10); 488 if (!cp || *cp++ != '\0') 489 SCREWUP("atime.usec not delimited"); 490 write(remout, "", 1); 491 continue; 492 } 493 if (*cp != 'C' && *cp != 'D') { 494 /* 495 * Check for the case "rcp remote:foo\* local:bar". 496 * In this case, the line "No match." can be returned 497 * by the shell before the rcp command on the remote is 498 * executed so the ^Aerror_message convention isn't 499 * followed. 500 */ 501 if (first) { 502 run_err("%s", cp); 503 exit(1); 504 } 505 SCREWUP("expected control record"); 506 } 507 mode = 0; 508 for (++cp; cp < buf + 5; cp++) { 509 if (*cp < '0' || *cp > '7') 510 SCREWUP("bad mode"); 511 mode = (mode << 3) | (*cp - '0'); 512 } 513 if (*cp++ != ' ') 514 SCREWUP("mode not delimited"); 515 516 for (size = 0; isdigit((unsigned char)*cp);) 517 size = size * 10 + (*cp++ - '0'); 518 if (*cp++ != ' ') 519 SCREWUP("size not delimited"); 520 if (targisdir) { 521 static char *namebuf; 522 static int cursize; 523 size_t need; 524 525 need = strlen(targ) + strlen(cp) + 250; 526 if (need > cursize) { 527 if (!(namebuf = malloc(need))) 528 run_err("%s", strerror(errno)); 529 } 530 snprintf(namebuf, need, "%s%s%s", targ, 531 *targ ? "/" : "", cp); 532 np = namebuf; 533 } else 534 np = targ; 535 exists = stat(np, &stb) == 0; 536 if (buf[0] == 'D') { 537 int mod_flag = pflag; 538 if (exists) { 539 if (!S_ISDIR(stb.st_mode)) { 540 errno = ENOTDIR; 541 goto bad; 542 } 543 if (pflag) 544 chmod(np, mode); 545 } else { 546 /* Handle copying from a read-only directory */ 547 mod_flag = 1; 548 if (mkdir(np, mode | S_IRWXU) < 0) 549 goto bad; 550 } 551 vect[0] = np; 552 sink(1, vect); 553 if (setimes) { 554 setimes = 0; 555 if (utimes(np, tv) < 0) 556 run_err("%s: set times: %s", 557 np, strerror(errno)); 558 } 559 if (mod_flag) 560 chmod(np, mode); 561 continue; 562 } 563 omode = mode; 564 mode |= S_IWRITE; 565 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 566 bad: run_err("%s: %s", np, strerror(errno)); 567 continue; 568 } 569 write(remout, "", 1); 570 if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { 571 close(ofd); 572 continue; 573 } 574 cp = bp->buf; 575 wrerr = NO; 576 for (count = i = 0; i < size; i += BUFSIZ) { 577 amt = BUFSIZ; 578 if (i + amt > size) 579 amt = size - i; 580 count += amt; 581 if((j = net_read(remin, cp, amt)) != amt) { 582 run_err("%s", j ? strerror(errno) : 583 "dropped connection"); 584 exit(1); 585 } 586 amt -= j; 587 cp += j; 588 if (count == bp->cnt) { 589 /* Keep reading so we stay sync'd up. */ 590 if (wrerr == NO) { 591 j = write(ofd, bp->buf, count); 592 if (j != count) { 593 wrerr = YES; 594 wrerrno = j >= 0 ? EIO : errno; 595 } 596 } 597 count = 0; 598 cp = bp->buf; 599 } 600 } 601 if (count != 0 && wrerr == NO && 602 (j = write(ofd, bp->buf, count)) != count) { 603 wrerr = YES; 604 wrerrno = j >= 0 ? EIO : errno; 605 } 606 if (ftruncate(ofd, size)) { 607 run_err("%s: truncate: %s", np, strerror(errno)); 608 wrerr = DISPLAYED; 609 } 610 if (pflag) { 611 if (exists || omode != mode) 612 if (fchmod(ofd, omode)) 613 run_err("%s: set mode: %s", 614 np, strerror(errno)); 615 } else { 616 if (!exists && omode != mode) 617 if (fchmod(ofd, omode & ~mask)) 618 run_err("%s: set mode: %s", 619 np, strerror(errno)); 620 } 621 close(ofd); 622 response(); 623 if (setimes && wrerr == NO) { 624 setimes = 0; 625 if (utimes(np, tv) < 0) { 626 run_err("%s: set times: %s", 627 np, strerror(errno)); 628 wrerr = DISPLAYED; 629 } 630 } 631 switch(wrerr) { 632 case YES: 633 run_err("%s: %s", np, strerror(wrerrno)); 634 break; 635 case NO: 636 write(remout, "", 1); 637 break; 638 case DISPLAYED: 639 break; 640 } 641 } 642 screwup: 643 run_err("protocol error: %s", why); 644 exit(1); 645 } 646 647 int 648 response(void) 649 { 650 char ch, *cp, resp, rbuf[BUFSIZ]; 651 652 if (read(remin, &resp, sizeof(resp)) != sizeof(resp)) 653 lostconn(0); 654 655 cp = rbuf; 656 switch(resp) { 657 case 0: /* ok */ 658 return (0); 659 default: 660 *cp++ = resp; 661 /* FALLTHROUGH */ 662 case 1: /* error, followed by error msg */ 663 case 2: /* fatal error, "" */ 664 do { 665 if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 666 lostconn(0); 667 *cp++ = ch; 668 } while (cp < &rbuf[BUFSIZ] && ch != '\n'); 669 670 if (!iamremote) 671 write(STDERR_FILENO, rbuf, cp - rbuf); 672 ++errs; 673 if (resp == 1) 674 return (-1); 675 exit(1); 676 } 677 /* NOTREACHED */ 678 } 679 680 #include <stdarg.h> 681 682 void 683 run_err(const char *fmt, ...) 684 { 685 static FILE *fp; 686 va_list ap; 687 688 ++errs; 689 if (fp == NULL && !(fp = fdopen(remout, "w"))) 690 return; 691 va_start(ap, fmt); 692 fprintf(fp, "%c", 0x01); 693 fprintf(fp, "rcp: "); 694 vfprintf(fp, fmt, ap); 695 fprintf(fp, "\n"); 696 fflush(fp); 697 va_end(ap); 698 699 if (!iamremote) { 700 va_start(ap, fmt); 701 vwarnx(fmt, ap); 702 va_end(ap); 703 } 704 } 705 706 /* 707 * This function executes the given command as the specified user on the 708 * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 709 * assigns the input and output file descriptors on success. 710 * 711 * If it cannot create necessary pipes it exits with error message. 712 */ 713 714 int 715 do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 716 { 717 int pin[2], pout[2], reserved[2]; 718 719 /* 720 * Reserve two descriptors so that the real pipes won't get 721 * descriptors 0 and 1 because that will screw up dup2 below. 722 */ 723 pipe(reserved); 724 725 /* Create a socket pair for communicating with rsh. */ 726 if (pipe(pin) < 0) { 727 perror("pipe"); 728 exit(255); 729 } 730 if (pipe(pout) < 0) { 731 perror("pipe"); 732 exit(255); 733 } 734 735 /* Free the reserved descriptors. */ 736 close(reserved[0]); 737 close(reserved[1]); 738 739 /* For a child to execute the command on the remote host using rsh. */ 740 if (fork() == 0) { 741 char *args[100]; 742 unsigned int i; 743 744 /* Child. */ 745 close(pin[1]); 746 close(pout[0]); 747 dup2(pin[0], 0); 748 dup2(pout[1], 1); 749 close(pin[0]); 750 close(pout[1]); 751 752 i = 0; 753 args[i++] = RSH_PROGRAM; 754 if (usekrb5) 755 args[i++] = "-5"; 756 if (usebroken) 757 args[i++] = "-K"; 758 if (doencrypt) 759 args[i++] = "-x"; 760 if (forwardtkt) 761 args[i++] = "-F"; 762 if (noencrypt) 763 args[i++] = "-z"; 764 if (port != NULL) { 765 args[i++] = "-p"; 766 args[i++] = port; 767 } 768 if (remuser != NULL) { 769 args[i++] = "-l"; 770 args[i++] = remuser; 771 } 772 args[i++] = host; 773 args[i++] = cmd; 774 args[i++] = NULL; 775 776 execvp(RSH_PROGRAM, args); 777 perror(RSH_PROGRAM); 778 exit(1); 779 } 780 /* Parent. Close the other side, and return the local side. */ 781 close(pin[0]); 782 *fdout = pin[1]; 783 close(pout[1]); 784 *fdin = pout[0]; 785 return 0; 786 } 787