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