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