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