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