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