rcp.c revision 90926
1200SN/A/* 23294Salanb * Copyright (c) 1983, 1990, 1992, 1993 3200SN/A * The Regents of the University of California. All rights reserved. 4200SN/A * 5200SN/A * Redistribution and use in source and binary forms, with or without 6200SN/A * modification, are permitted provided that the following conditions 7553SN/A * are met: 8200SN/A * 1. Redistributions of source code must retain the above copyright 9553SN/A * notice, this list of conditions and the following disclaimer. 10200SN/A * 2. Redistributions in binary form must reproduce the above copyright 11200SN/A * notice, this list of conditions and the following disclaimer in the 12200SN/A * documentation and/or other materials provided with the distribution. 13200SN/A * 3. All advertising materials mentioning features or use of this software 14200SN/A * must display the following acknowledgement: 15200SN/A * This product includes software developed by the University of 16200SN/A * California, Berkeley and its contributors. 17200SN/A * 4. Neither the name of the University nor the names of its contributors 18200SN/A * may be used to endorse or promote products derived from this software 19200SN/A * without specific prior written permission. 20200SN/A * 21553SN/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22553SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23553SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24200SN/A * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25200SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261223Sohrstrom * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271223Sohrstrom * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28200SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29200SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30200SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31200SN/A * SUCH DAMAGE. 32200SN/A */ 33200SN/A 34200SN/A#include "rcp_locl.h" 35200SN/A#include <getarg.h> 36200SN/A 37200SN/A#define RSH_PROGRAM "rsh" 38200SN/A 39200SN/Astruct passwd *pwd; 40200SN/Auid_t userid; 41200SN/Aint errs, remin, remout; 42200SN/Aint pflag, iamremote, iamrecursive, targetshouldbedirectory; 43200SN/Aint doencrypt, noencrypt; 442020Smcimadamoreint usebroken, usekrb5, forwardtkt; 45200SN/Achar *port; 462603Sksrini 47200SN/A#define CMDNEEDS 64 48200SN/Achar cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 49200SN/A 50200SN/Aint response (void); 51200SN/Avoid rsource (char *, struct stat *); 52200SN/Avoid sink (int, char *[]); 53200SN/Avoid source (int, char *[]); 54200SN/Avoid tolocal (int, char *[]); 55200SN/Avoid toremote (char *, int, char *[]); 56200SN/A 572603Sksriniint do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); 58200SN/A 59200SN/Astatic int fflag, tflag; 60200SN/A 61200SN/Astatic int version_flag, help_flag; 62200SN/A 63200SN/Astruct getargs args[] = { 64200SN/A { NULL, '5', arg_flag, &usekrb5, "use Kerberos 5 authentication" }, 65200SN/A { NULL, 'F', arg_flag, &forwardtkt, "forward credentials" }, 66200SN/A { NULL, 'K', arg_flag, &usebroken, "use BSD authentication" }, 67200SN/A { NULL, 'P', arg_string, &port, "non-default port", "port" }, 68200SN/A { NULL, 'p', arg_flag, &pflag, "preserve file permissions" }, 69200SN/A { NULL, 'r', arg_flag, &iamrecursive, "recursive mode" }, 70200SN/A { NULL, 'x', arg_flag, &doencrypt, "use encryption" }, 71200SN/A { NULL, 'z', arg_flag, &noencrypt, "don't encrypt" }, 72200SN/A { NULL, 'd', arg_flag, &targetshouldbedirectory }, 73200SN/A { NULL, 'f', arg_flag, &fflag }, 742020Smcimadamore { NULL, 't', arg_flag, &tflag }, 752020Smcimadamore { "version", 0, arg_flag, &version_flag }, 762020Smcimadamore { "help", 0, arg_flag, &help_flag } 773294Salanb}; 782020Smcimadamore 792020Smcimadamorestatic void 803062Sjlahodausage (int ret) 813062Sjlahoda{ 822020Smcimadamore arg_printusage (args, 832020Smcimadamore sizeof(args) / sizeof(args[0]), 842020Smcimadamore NULL, 852020Smcimadamore "file1 file2|file... directory"); 862020Smcimadamore exit (ret); 872020Smcimadamore} 882020Smcimadamore 892020Smcimadamoreint 902063Svromeromain(int argc, char **argv) 912020Smcimadamore{ 922063Svromero char *targ; 932020Smcimadamore int optind = 0; 942020Smcimadamore 952020Smcimadamore setprogname(argv[0]); 962020Smcimadamore if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, 972020Smcimadamore &optind)) 982020Smcimadamore usage (1); 992020Smcimadamore if(help_flag) 1002020Smcimadamore usage(0); 101200SN/A if (version_flag) { 102200SN/A print_version (NULL); 103200SN/A return 0; 1042603Sksrini } 105200SN/A 106200SN/A iamremote = (fflag || tflag); 107200SN/A 108200SN/A argc -= optind; 109200SN/A argv += optind; 110200SN/A 111200SN/A if ((pwd = getpwuid(userid = getuid())) == NULL) 112200SN/A errx(1, "unknown user %d", (int)userid); 113200SN/A 1142603Sksrini remin = STDIN_FILENO; /* XXX */ 115200SN/A remout = STDOUT_FILENO; 116200SN/A 117200SN/A if (fflag) { /* Follow "protocol", send data. */ 118200SN/A response(); 119200SN/A setuid(userid); 120200SN/A source(argc, argv); 121200SN/A exit(errs); 122200SN/A } 1232603Sksrini 124200SN/A if (tflag) { /* Receive data. */ 125200SN/A setuid(userid); 126200SN/A sink(argc, argv); 127200SN/A exit(errs); 128200SN/A } 129200SN/A 130200SN/A if (argc < 2) 131200SN/A usage(1); 1322603Sksrini if (argc > 2) 133200SN/A targetshouldbedirectory = 1; 134200SN/A 135200SN/A remin = remout = -1; 136200SN/A /* Command to be executed on remote system using "rsh". */ 137200SN/A snprintf(cmd, sizeof(cmd), 138200SN/A "rcp%s%s%s", iamrecursive ? " -r" : "", 139200SN/A pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); 140200SN/A 141200SN/A signal(SIGPIPE, lostconn); 142200SN/A 143200SN/A if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 144200SN/A toremote(targ, argc, argv); 145200SN/A else { 146200SN/A tolocal(argc, argv); /* Dest is local host. */ 147200SN/A if (targetshouldbedirectory) 148200SN/A verifydir(argv[argc - 1]); 149200SN/A } 150200SN/A exit(errs); 151200SN/A} 152200SN/A 153200SN/Avoid 154200SN/Atoremote(char *targ, int argc, char **argv) 155200SN/A{ 156200SN/A int i; 157200SN/A char *bp, *host, *src, *suser, *thost, *tuser; 158200SN/A 159200SN/A *targ++ = 0; 160200SN/A if (*targ == 0) 161200SN/A targ = "."; 162200SN/A 163200SN/A if ((thost = strchr(argv[argc - 1], '@'))) { 164200SN/A /* user@host */ 165200SN/A *thost++ = 0; 166200SN/A tuser = argv[argc - 1]; 167200SN/A if (*tuser == '\0') 1682710Sjlahoda tuser = NULL; 1692710Sjlahoda else if (!okname(tuser)) 1703294Salanb exit(1); 171200SN/A } else { 172200SN/A thost = argv[argc - 1]; 173200SN/A tuser = NULL; 174200SN/A } 1752020Smcimadamore 176200SN/A for (i = 0; i < argc - 1; i++) { 1773294Salanb src = colon(argv[i]); 178200SN/A if (src) { /* remote to remote */ 179200SN/A *src++ = 0; 180200SN/A if (*src == 0) 181200SN/A src = "."; 182200SN/A host = strchr(argv[i], '@'); 183200SN/A if (host) { 184200SN/A *host++ = '\0'; 185200SN/A suser = argv[i]; 186200SN/A if (*suser == '\0') 187200SN/A suser = pwd->pw_name; 188200SN/A else if (!okname(suser)) 189200SN/A continue; 190200SN/A asprintf(&bp, 191200SN/A "%s %s -l %s -n %s %s '%s%s%s:%s'", 1922020Smcimadamore _PATH_RSH, host, suser, cmd, src, 193200SN/A tuser ? tuser : "", tuser ? "@" : "", 194200SN/A thost, targ); 1952020Smcimadamore } else { 1962020Smcimadamore asprintf(&bp, 197200SN/A "exec %s %s -n %s %s '%s%s%s:%s'", 198200SN/A _PATH_RSH, argv[i], cmd, src, 1992020Smcimadamore tuser ? tuser : "", tuser ? "@" : "", 2002020Smcimadamore thost, targ); 2012603Sksrini } 2022710Sjlahoda if (bp == NULL) 2032710Sjlahoda err (1, "malloc"); 2042710Sjlahoda susystem(bp, userid); 2052710Sjlahoda free(bp); 2063827Smcimadamore } else { /* local to remote */ 2073827Smcimadamore if (remin == -1) { 2083827Smcimadamore asprintf(&bp, "%s -t %s", cmd, targ); 2093827Smcimadamore if (bp == NULL) 2103827Smcimadamore err (1, "malloc"); 211200SN/A host = thost; 2122020Smcimadamore 2132020Smcimadamore if (do_cmd(host, tuser, bp, &remin, &remout) < 0) 2142020Smcimadamore exit(1); 2152020Smcimadamore 2162020Smcimadamore if (response() < 0) 2172020Smcimadamore exit(1); 2182020Smcimadamore free(bp); 2192020Smcimadamore setuid(userid); 220200SN/A } 2212710Sjlahoda source(1, argv+i); 222200SN/A } 223200SN/A } 224200SN/A} 225200SN/A 226200SN/Avoid 2272603Sksrinitolocal(int argc, char **argv) 228200SN/A{ 229200SN/A int i; 2302603Sksrini char *bp, *host, *src, *suser; 231200SN/A 2322020Smcimadamore for (i = 0; i < argc - 1; i++) { 233200SN/A if (!(src = colon(argv[i]))) { /* Local to local. */ 234200SN/A asprintf(&bp, "exec %s%s%s %s %s", _PATH_CP, 235200SN/A iamrecursive ? " -PR" : "", pflag ? " -p" : "", 236200SN/A argv[i], argv[argc - 1]); 237200SN/A if (bp == NULL) 238200SN/A err (1, "malloc"); 239200SN/A if (susystem(bp, userid)) 240200SN/A ++errs; 241200SN/A free(bp); 242200SN/A continue; 243200SN/A } 244200SN/A *src++ = 0; 245200SN/A if (*src == 0) 246200SN/A src = "."; 247200SN/A if ((host = strchr(argv[i], '@')) == NULL) { 2483827Smcimadamore host = argv[i]; 2493827Smcimadamore suser = pwd->pw_name; 2503827Smcimadamore } else { 251200SN/A *host++ = 0; 252200SN/A suser = argv[i]; 253200SN/A if (*suser == '\0') 254200SN/A suser = pwd->pw_name; 255200SN/A else if (!okname(suser)) 256200SN/A continue; 257200SN/A } 258200SN/A asprintf(&bp, "%s -f %s", cmd, src); 259200SN/A if (bp == NULL) 260200SN/A err (1, "malloc"); 261200SN/A if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 262200SN/A free(bp); 263200SN/A ++errs; 264200SN/A continue; 265200SN/A } 266200SN/A free(bp); 267200SN/A sink(1, argv + argc - 1); 268200SN/A seteuid(0); 269200SN/A close(remin); 270200SN/A remin = remout = -1; 271200SN/A } 272200SN/A} 273200SN/A 274200SN/Avoid 275200SN/Asource(int argc, char **argv) 276200SN/A{ 277200SN/A struct stat stb; 278200SN/A static BUF buffer; 279200SN/A BUF *bp; 280200SN/A off_t i; 281200SN/A int amt, fd, haderr, indx, result; 282200SN/A char *last, *name, buf[BUFSIZ]; 283200SN/A 284200SN/A for (indx = 0; indx < argc; ++indx) { 285200SN/A name = argv[indx]; 286200SN/A if ((fd = open(name, O_RDONLY, 0)) < 0) 287200SN/A goto syserr; 288200SN/A if (fstat(fd, &stb)) { 289200SN/Asyserr: run_err("%s: %s", name, strerror(errno)); 290200SN/A goto next; 291200SN/A } 292200SN/A switch (stb.st_mode & S_IFMT) { 293200SN/A case S_IFREG: 294200SN/A break; 295200SN/A case S_IFDIR: 296200SN/A if (iamrecursive) { 297200SN/A rsource(name, &stb); 298200SN/A goto next; 299200SN/A } 300200SN/A /* FALLTHROUGH */ 301200SN/A default: 302200SN/A run_err("%s: not a regular file", name); 303200SN/A goto next; 3042710Sjlahoda } 3052710Sjlahoda if ((last = strrchr(name, '/')) == NULL) 3062710Sjlahoda last = name; 3073294Salanb else 3082710Sjlahoda ++last; 309200SN/A if (pflag) { 310200SN/A /* 311200SN/A * Make it compatible with possible future 312200SN/A * versions expecting microseconds. 313200SN/A */ 314200SN/A snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", 315200SN/A (long)stb.st_mtime, 316200SN/A (long)stb.st_atime); 317200SN/A write(remout, buf, strlen(buf)); 3182603Sksrini if (response() < 0) 319200SN/A goto next; 320200SN/A } 321200SN/A#define MODEMASK (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 322200SN/A snprintf(buf, sizeof(buf), "C%04o %lu %s\n", 323200SN/A stb.st_mode & MODEMASK, 324200SN/A (unsigned long)stb.st_size, 325200SN/A last); 326200SN/A write(remout, buf, strlen(buf)); 327 if (response() < 0) 328 goto next; 329 if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { 330next: 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 362void 363rsource(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 413void 414sink(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) { 566bad: 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 } 642screwup: 643 run_err("protocol error: %s", why); 644 exit(1); 645} 646 647int 648response(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 682void 683run_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 714int 715do_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