1511b41d2SMark Murray /* 2511b41d2SMark Murray * 3511b41d2SMark Murray * scp - secure remote copy. This is basically patched BSD rcp which uses ssh 4511b41d2SMark Murray * to do the data transfer (instead of using rcmd). 5511b41d2SMark Murray * 6511b41d2SMark Murray * NOTE: This version should NOT be suid root. (This uses ssh to do the transfer 7511b41d2SMark Murray * and ssh has the necessary privileges.) 8511b41d2SMark Murray * 9511b41d2SMark Murray * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> 10511b41d2SMark Murray * 11511b41d2SMark Murray */ 12511b41d2SMark Murray 13511b41d2SMark Murray /* 14511b41d2SMark Murray * Copyright (c) 1983, 1990, 1992, 1993, 1995 15511b41d2SMark Murray * The Regents of the University of California. All rights reserved. 16511b41d2SMark Murray * 17511b41d2SMark Murray * Redistribution and use in source and binary forms, with or without 18511b41d2SMark Murray * modification, are permitted provided that the following conditions 19511b41d2SMark Murray * are met: 20511b41d2SMark Murray * 1. Redistributions of source code must retain the above copyright 21511b41d2SMark Murray * notice, this list of conditions and the following disclaimer. 22511b41d2SMark Murray * 2. Redistributions in binary form must reproduce the above copyright 23511b41d2SMark Murray * notice, this list of conditions and the following disclaimer in the 24511b41d2SMark Murray * documentation and/or other materials provided with the distribution. 25511b41d2SMark Murray * 3. All advertising materials mentioning features or use of this software 26511b41d2SMark Murray * must display the following acknowledgement: 27511b41d2SMark Murray * This product includes software developed by the University of 28511b41d2SMark Murray * California, Berkeley and its contributors. 29511b41d2SMark Murray * 4. Neither the name of the University nor the names of its contributors 30511b41d2SMark Murray * may be used to endorse or promote products derived from this software 31511b41d2SMark Murray * without specific prior written permission. 32511b41d2SMark Murray * 33511b41d2SMark Murray * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 34511b41d2SMark Murray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 35511b41d2SMark Murray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 36511b41d2SMark Murray * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 37511b41d2SMark Murray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 38511b41d2SMark Murray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 39511b41d2SMark Murray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 40511b41d2SMark Murray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 41511b41d2SMark Murray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 42511b41d2SMark Murray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 43511b41d2SMark Murray * SUCH DAMAGE. 44511b41d2SMark Murray * 45511b41d2SMark Murray */ 46511b41d2SMark Murray 47511b41d2SMark Murray #include "includes.h" 48511b41d2SMark Murray RCSID("$Id: scp.c,v 1.25 2000/01/24 22:11:20 markus Exp $"); 49511b41d2SMark Murray 50511b41d2SMark Murray #include "ssh.h" 51511b41d2SMark Murray #include "xmalloc.h" 52511b41d2SMark Murray #include <utime.h> 53511b41d2SMark Murray 54511b41d2SMark Murray #define _PATH_CP "cp" 55511b41d2SMark Murray 56511b41d2SMark Murray /* For progressmeter() -- number of seconds before xfer considered "stalled" */ 57511b41d2SMark Murray #define STALLTIME 5 58511b41d2SMark Murray 59511b41d2SMark Murray /* Visual statistics about files as they are transferred. */ 60511b41d2SMark Murray void progressmeter(int); 61511b41d2SMark Murray 62511b41d2SMark Murray /* Returns width of the terminal (for progress meter calculations). */ 63511b41d2SMark Murray int getttywidth(void); 64511b41d2SMark Murray 65511b41d2SMark Murray /* Time a transfer started. */ 66511b41d2SMark Murray static struct timeval start; 67511b41d2SMark Murray 68511b41d2SMark Murray /* Number of bytes of current file transferred so far. */ 69511b41d2SMark Murray volatile unsigned long statbytes; 70511b41d2SMark Murray 71511b41d2SMark Murray /* Total size of current file. */ 72511b41d2SMark Murray off_t totalbytes = 0; 73511b41d2SMark Murray 74511b41d2SMark Murray /* Name of current file being transferred. */ 75511b41d2SMark Murray char *curfile; 76511b41d2SMark Murray 77511b41d2SMark Murray /* This is set to non-zero if IPv4 is desired. */ 78511b41d2SMark Murray int IPv4 = 0; 79511b41d2SMark Murray 80511b41d2SMark Murray /* This is set to non-zero if IPv6 is desired. */ 81511b41d2SMark Murray int IPv6 = 0; 82511b41d2SMark Murray 83511b41d2SMark Murray /* This is set to non-zero to enable verbose mode. */ 84511b41d2SMark Murray int verbose_mode = 0; 85511b41d2SMark Murray 86511b41d2SMark Murray /* This is set to non-zero if compression is desired. */ 87511b41d2SMark Murray int compress = 0; 88511b41d2SMark Murray 89511b41d2SMark Murray /* This is set to zero if the progressmeter is not desired. */ 90511b41d2SMark Murray int showprogress = 1; 91511b41d2SMark Murray 92511b41d2SMark Murray /* This is set to non-zero if running in batch mode (that is, password 93511b41d2SMark Murray and passphrase queries are not allowed). */ 94511b41d2SMark Murray int batchmode = 0; 95511b41d2SMark Murray 96511b41d2SMark Murray /* This is set to the cipher type string if given on the command line. */ 97511b41d2SMark Murray char *cipher = NULL; 98511b41d2SMark Murray 99511b41d2SMark Murray /* This is set to the RSA authentication identity file name if given on 100511b41d2SMark Murray the command line. */ 101511b41d2SMark Murray char *identity = NULL; 102511b41d2SMark Murray 103511b41d2SMark Murray /* This is the port to use in contacting the remote site (is non-NULL). */ 104511b41d2SMark Murray char *port = NULL; 105511b41d2SMark Murray 106511b41d2SMark Murray /* 107511b41d2SMark Murray * This function executes the given command as the specified user on the 108511b41d2SMark Murray * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 109511b41d2SMark Murray * assigns the input and output file descriptors on success. 110511b41d2SMark Murray */ 111511b41d2SMark Murray 112511b41d2SMark Murray int 113511b41d2SMark Murray do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 114511b41d2SMark Murray { 115511b41d2SMark Murray int pin[2], pout[2], reserved[2]; 116511b41d2SMark Murray 117511b41d2SMark Murray if (verbose_mode) 118511b41d2SMark Murray fprintf(stderr, "Executing: host %s, user %s, command %s\n", 119511b41d2SMark Murray host, remuser ? remuser : "(unspecified)", cmd); 120511b41d2SMark Murray 121511b41d2SMark Murray /* 122511b41d2SMark Murray * Reserve two descriptors so that the real pipes won't get 123511b41d2SMark Murray * descriptors 0 and 1 because that will screw up dup2 below. 124511b41d2SMark Murray */ 125511b41d2SMark Murray pipe(reserved); 126511b41d2SMark Murray 127511b41d2SMark Murray /* Create a socket pair for communicating with ssh. */ 128511b41d2SMark Murray if (pipe(pin) < 0) 129511b41d2SMark Murray fatal("pipe: %s", strerror(errno)); 130511b41d2SMark Murray if (pipe(pout) < 0) 131511b41d2SMark Murray fatal("pipe: %s", strerror(errno)); 132511b41d2SMark Murray 133511b41d2SMark Murray /* Free the reserved descriptors. */ 134511b41d2SMark Murray close(reserved[0]); 135511b41d2SMark Murray close(reserved[1]); 136511b41d2SMark Murray 137511b41d2SMark Murray /* For a child to execute the command on the remote host using ssh. */ 138511b41d2SMark Murray if (fork() == 0) { 139511b41d2SMark Murray char *args[100]; 140511b41d2SMark Murray unsigned int i; 141511b41d2SMark Murray 142511b41d2SMark Murray /* Child. */ 143511b41d2SMark Murray close(pin[1]); 144511b41d2SMark Murray close(pout[0]); 145511b41d2SMark Murray dup2(pin[0], 0); 146511b41d2SMark Murray dup2(pout[1], 1); 147511b41d2SMark Murray close(pin[0]); 148511b41d2SMark Murray close(pout[1]); 149511b41d2SMark Murray 150511b41d2SMark Murray i = 0; 151511b41d2SMark Murray args[i++] = SSH_PROGRAM; 152511b41d2SMark Murray args[i++] = "-x"; 153511b41d2SMark Murray args[i++] = "-oFallBackToRsh no"; 154511b41d2SMark Murray if (IPv4) 155511b41d2SMark Murray args[i++] = "-4"; 156511b41d2SMark Murray if (IPv6) 157511b41d2SMark Murray args[i++] = "-6"; 158511b41d2SMark Murray args[i++] = "-oFallBackToRsh no"; 159511b41d2SMark Murray if (verbose_mode) 160511b41d2SMark Murray args[i++] = "-v"; 161511b41d2SMark Murray if (compress) 162511b41d2SMark Murray args[i++] = "-C"; 163511b41d2SMark Murray if (batchmode) 164511b41d2SMark Murray args[i++] = "-oBatchMode yes"; 165511b41d2SMark Murray if (cipher != NULL) { 166511b41d2SMark Murray args[i++] = "-c"; 167511b41d2SMark Murray args[i++] = cipher; 168511b41d2SMark Murray } 169511b41d2SMark Murray if (identity != NULL) { 170511b41d2SMark Murray args[i++] = "-i"; 171511b41d2SMark Murray args[i++] = identity; 172511b41d2SMark Murray } 173511b41d2SMark Murray if (port != NULL) { 174511b41d2SMark Murray args[i++] = "-p"; 175511b41d2SMark Murray args[i++] = port; 176511b41d2SMark Murray } 177511b41d2SMark Murray if (remuser != NULL) { 178511b41d2SMark Murray args[i++] = "-l"; 179511b41d2SMark Murray args[i++] = remuser; 180511b41d2SMark Murray } 181511b41d2SMark Murray args[i++] = host; 182511b41d2SMark Murray args[i++] = cmd; 183511b41d2SMark Murray args[i++] = NULL; 184511b41d2SMark Murray 185511b41d2SMark Murray execvp(SSH_PROGRAM, args); 186511b41d2SMark Murray perror(SSH_PROGRAM); 187511b41d2SMark Murray exit(1); 188511b41d2SMark Murray } 189511b41d2SMark Murray /* Parent. Close the other side, and return the local side. */ 190511b41d2SMark Murray close(pin[0]); 191511b41d2SMark Murray *fdout = pin[1]; 192511b41d2SMark Murray close(pout[1]); 193511b41d2SMark Murray *fdin = pout[0]; 194511b41d2SMark Murray return 0; 195511b41d2SMark Murray } 196511b41d2SMark Murray 197511b41d2SMark Murray void 198511b41d2SMark Murray fatal(const char *fmt,...) 199511b41d2SMark Murray { 200511b41d2SMark Murray va_list ap; 201511b41d2SMark Murray char buf[1024]; 202511b41d2SMark Murray 203511b41d2SMark Murray va_start(ap, fmt); 204511b41d2SMark Murray vsnprintf(buf, sizeof(buf), fmt, ap); 205511b41d2SMark Murray va_end(ap); 206511b41d2SMark Murray fprintf(stderr, "%s\n", buf); 207511b41d2SMark Murray exit(255); 208511b41d2SMark Murray } 209511b41d2SMark Murray 210511b41d2SMark Murray /* This stuff used to be in BSD rcp extern.h. */ 211511b41d2SMark Murray 212511b41d2SMark Murray typedef struct { 213511b41d2SMark Murray int cnt; 214511b41d2SMark Murray char *buf; 215511b41d2SMark Murray } BUF; 216511b41d2SMark Murray 217511b41d2SMark Murray extern int iamremote; 218511b41d2SMark Murray 219511b41d2SMark Murray BUF *allocbuf(BUF *, int, int); 220511b41d2SMark Murray char *colon(char *); 221511b41d2SMark Murray void lostconn(int); 222511b41d2SMark Murray void nospace(void); 223511b41d2SMark Murray int okname(char *); 224511b41d2SMark Murray void run_err(const char *,...); 225511b41d2SMark Murray void verifydir(char *); 226511b41d2SMark Murray 227511b41d2SMark Murray /* Stuff from BSD rcp.c continues. */ 228511b41d2SMark Murray 229511b41d2SMark Murray struct passwd *pwd; 230511b41d2SMark Murray uid_t userid; 231511b41d2SMark Murray int errs, remin, remout; 232511b41d2SMark Murray int pflag, iamremote, iamrecursive, targetshouldbedirectory; 233511b41d2SMark Murray 234511b41d2SMark Murray #define CMDNEEDS 64 235511b41d2SMark Murray char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 236511b41d2SMark Murray 237511b41d2SMark Murray int response(void); 238511b41d2SMark Murray void rsource(char *, struct stat *); 239511b41d2SMark Murray void sink(int, char *[]); 240511b41d2SMark Murray void source(int, char *[]); 241511b41d2SMark Murray void tolocal(int, char *[]); 242511b41d2SMark Murray void toremote(char *, int, char *[]); 243511b41d2SMark Murray void usage(void); 244511b41d2SMark Murray 245511b41d2SMark Murray int 246511b41d2SMark Murray main(argc, argv) 247511b41d2SMark Murray int argc; 248511b41d2SMark Murray char *argv[]; 249511b41d2SMark Murray { 250511b41d2SMark Murray int ch, fflag, tflag; 251511b41d2SMark Murray char *targ; 252511b41d2SMark Murray extern char *optarg; 253511b41d2SMark Murray extern int optind; 254511b41d2SMark Murray 255511b41d2SMark Murray fflag = tflag = 0; 256511b41d2SMark Murray while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46")) != EOF) 257511b41d2SMark Murray switch (ch) { 258511b41d2SMark Murray /* User-visible flags. */ 259511b41d2SMark Murray case '4': 260511b41d2SMark Murray IPv4 = 1; 261511b41d2SMark Murray break; 262511b41d2SMark Murray case '6': 263511b41d2SMark Murray IPv6 = 1; 264511b41d2SMark Murray break; 265511b41d2SMark Murray case 'p': 266511b41d2SMark Murray pflag = 1; 267511b41d2SMark Murray break; 268511b41d2SMark Murray case 'P': 269511b41d2SMark Murray port = optarg; 270511b41d2SMark Murray break; 271511b41d2SMark Murray case 'r': 272511b41d2SMark Murray iamrecursive = 1; 273511b41d2SMark Murray break; 274511b41d2SMark Murray /* Server options. */ 275511b41d2SMark Murray case 'd': 276511b41d2SMark Murray targetshouldbedirectory = 1; 277511b41d2SMark Murray break; 278511b41d2SMark Murray case 'f': /* "from" */ 279511b41d2SMark Murray iamremote = 1; 280511b41d2SMark Murray fflag = 1; 281511b41d2SMark Murray break; 282511b41d2SMark Murray case 't': /* "to" */ 283511b41d2SMark Murray iamremote = 1; 284511b41d2SMark Murray tflag = 1; 285511b41d2SMark Murray break; 286511b41d2SMark Murray case 'c': 287511b41d2SMark Murray cipher = optarg; 288511b41d2SMark Murray break; 289511b41d2SMark Murray case 'i': 290511b41d2SMark Murray identity = optarg; 291511b41d2SMark Murray break; 292511b41d2SMark Murray case 'v': 293511b41d2SMark Murray verbose_mode = 1; 294511b41d2SMark Murray break; 295511b41d2SMark Murray case 'B': 296511b41d2SMark Murray batchmode = 1; 297511b41d2SMark Murray break; 298511b41d2SMark Murray case 'C': 299511b41d2SMark Murray compress = 1; 300511b41d2SMark Murray break; 301511b41d2SMark Murray case 'q': 302511b41d2SMark Murray showprogress = 0; 303511b41d2SMark Murray break; 304511b41d2SMark Murray case '?': 305511b41d2SMark Murray default: 306511b41d2SMark Murray usage(); 307511b41d2SMark Murray } 308511b41d2SMark Murray argc -= optind; 309511b41d2SMark Murray argv += optind; 310511b41d2SMark Murray 311511b41d2SMark Murray if ((pwd = getpwuid(userid = getuid())) == NULL) 312511b41d2SMark Murray fatal("unknown user %d", (int) userid); 313511b41d2SMark Murray 314511b41d2SMark Murray if (!isatty(STDERR_FILENO)) 315511b41d2SMark Murray showprogress = 0; 316511b41d2SMark Murray 317511b41d2SMark Murray remin = STDIN_FILENO; 318511b41d2SMark Murray remout = STDOUT_FILENO; 319511b41d2SMark Murray 320511b41d2SMark Murray if (fflag) { 321511b41d2SMark Murray /* Follow "protocol", send data. */ 322511b41d2SMark Murray (void) response(); 323511b41d2SMark Murray source(argc, argv); 324511b41d2SMark Murray exit(errs != 0); 325511b41d2SMark Murray } 326511b41d2SMark Murray if (tflag) { 327511b41d2SMark Murray /* Receive data. */ 328511b41d2SMark Murray sink(argc, argv); 329511b41d2SMark Murray exit(errs != 0); 330511b41d2SMark Murray } 331511b41d2SMark Murray if (argc < 2) 332511b41d2SMark Murray usage(); 333511b41d2SMark Murray if (argc > 2) 334511b41d2SMark Murray targetshouldbedirectory = 1; 335511b41d2SMark Murray 336511b41d2SMark Murray remin = remout = -1; 337511b41d2SMark Murray /* Command to be executed on remote system using "ssh". */ 338511b41d2SMark Murray (void) sprintf(cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", 339511b41d2SMark Murray iamrecursive ? " -r" : "", pflag ? " -p" : "", 340511b41d2SMark Murray targetshouldbedirectory ? " -d" : ""); 341511b41d2SMark Murray 342511b41d2SMark Murray (void) signal(SIGPIPE, lostconn); 343511b41d2SMark Murray 344511b41d2SMark Murray if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 345511b41d2SMark Murray toremote(targ, argc, argv); 346511b41d2SMark Murray else { 347511b41d2SMark Murray tolocal(argc, argv); /* Dest is local host. */ 348511b41d2SMark Murray if (targetshouldbedirectory) 349511b41d2SMark Murray verifydir(argv[argc - 1]); 350511b41d2SMark Murray } 351511b41d2SMark Murray exit(errs != 0); 352511b41d2SMark Murray } 353511b41d2SMark Murray 354511b41d2SMark Murray char * 355511b41d2SMark Murray cleanhostname(host) 356511b41d2SMark Murray char *host; 357511b41d2SMark Murray { 358511b41d2SMark Murray if (*host == '[' && host[strlen(host) - 1] == ']') { 359511b41d2SMark Murray host[strlen(host) - 1] = '\0'; 360511b41d2SMark Murray return (host + 1); 361511b41d2SMark Murray } else 362511b41d2SMark Murray return host; 363511b41d2SMark Murray } 364511b41d2SMark Murray 365511b41d2SMark Murray void 366511b41d2SMark Murray toremote(targ, argc, argv) 367511b41d2SMark Murray char *targ, *argv[]; 368511b41d2SMark Murray int argc; 369511b41d2SMark Murray { 370511b41d2SMark Murray int i, len; 371511b41d2SMark Murray char *bp, *host, *src, *suser, *thost, *tuser; 372511b41d2SMark Murray 373511b41d2SMark Murray *targ++ = 0; 374511b41d2SMark Murray if (*targ == 0) 375511b41d2SMark Murray targ = "."; 376511b41d2SMark Murray 377511b41d2SMark Murray if ((thost = strchr(argv[argc - 1], '@'))) { 378511b41d2SMark Murray /* user@host */ 379511b41d2SMark Murray *thost++ = 0; 380511b41d2SMark Murray tuser = argv[argc - 1]; 381511b41d2SMark Murray if (*tuser == '\0') 382511b41d2SMark Murray tuser = NULL; 383511b41d2SMark Murray else if (!okname(tuser)) 384511b41d2SMark Murray exit(1); 385511b41d2SMark Murray } else { 386511b41d2SMark Murray thost = argv[argc - 1]; 387511b41d2SMark Murray tuser = NULL; 388511b41d2SMark Murray } 389511b41d2SMark Murray 390511b41d2SMark Murray for (i = 0; i < argc - 1; i++) { 391511b41d2SMark Murray src = colon(argv[i]); 392511b41d2SMark Murray if (src) { /* remote to remote */ 393511b41d2SMark Murray *src++ = 0; 394511b41d2SMark Murray if (*src == 0) 395511b41d2SMark Murray src = "."; 396511b41d2SMark Murray host = strchr(argv[i], '@'); 397511b41d2SMark Murray len = strlen(SSH_PROGRAM) + strlen(argv[i]) + 398511b41d2SMark Murray strlen(src) + (tuser ? strlen(tuser) : 0) + 399511b41d2SMark Murray strlen(thost) + strlen(targ) + CMDNEEDS + 32; 400511b41d2SMark Murray bp = xmalloc(len); 401511b41d2SMark Murray if (host) { 402511b41d2SMark Murray *host++ = 0; 403511b41d2SMark Murray host = cleanhostname(host); 404511b41d2SMark Murray suser = argv[i]; 405511b41d2SMark Murray if (*suser == '\0') 406511b41d2SMark Murray suser = pwd->pw_name; 407511b41d2SMark Murray else if (!okname(suser)) 408511b41d2SMark Murray continue; 409511b41d2SMark Murray (void) sprintf(bp, 410511b41d2SMark Murray "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'", 411511b41d2SMark Murray SSH_PROGRAM, verbose_mode ? " -v" : "", 412511b41d2SMark Murray suser, host, cmd, src, 413511b41d2SMark Murray tuser ? tuser : "", tuser ? "@" : "", 414511b41d2SMark Murray thost, targ); 415511b41d2SMark Murray } else { 416511b41d2SMark Murray host = cleanhostname(argv[i]); 417511b41d2SMark Murray (void) sprintf(bp, 418511b41d2SMark Murray "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'", 419511b41d2SMark Murray SSH_PROGRAM, verbose_mode ? " -v" : "", 420511b41d2SMark Murray host, cmd, src, 421511b41d2SMark Murray tuser ? tuser : "", tuser ? "@" : "", 422511b41d2SMark Murray thost, targ); 423511b41d2SMark Murray } 424511b41d2SMark Murray if (verbose_mode) 425511b41d2SMark Murray fprintf(stderr, "Executing: %s\n", bp); 426511b41d2SMark Murray (void) system(bp); 427511b41d2SMark Murray (void) xfree(bp); 428511b41d2SMark Murray } else { /* local to remote */ 429511b41d2SMark Murray if (remin == -1) { 430511b41d2SMark Murray len = strlen(targ) + CMDNEEDS + 20; 431511b41d2SMark Murray bp = xmalloc(len); 432511b41d2SMark Murray (void) sprintf(bp, "%s -t %s", cmd, targ); 433511b41d2SMark Murray host = cleanhostname(thost); 434511b41d2SMark Murray if (do_cmd(host, tuser, 435511b41d2SMark Murray bp, &remin, &remout) < 0) 436511b41d2SMark Murray exit(1); 437511b41d2SMark Murray if (response() < 0) 438511b41d2SMark Murray exit(1); 439511b41d2SMark Murray (void) xfree(bp); 440511b41d2SMark Murray } 441511b41d2SMark Murray source(1, argv + i); 442511b41d2SMark Murray } 443511b41d2SMark Murray } 444511b41d2SMark Murray } 445511b41d2SMark Murray 446511b41d2SMark Murray void 447511b41d2SMark Murray tolocal(argc, argv) 448511b41d2SMark Murray int argc; 449511b41d2SMark Murray char *argv[]; 450511b41d2SMark Murray { 451511b41d2SMark Murray int i, len; 452511b41d2SMark Murray char *bp, *host, *src, *suser; 453511b41d2SMark Murray 454511b41d2SMark Murray for (i = 0; i < argc - 1; i++) { 455511b41d2SMark Murray if (!(src = colon(argv[i]))) { /* Local to local. */ 456511b41d2SMark Murray len = strlen(_PATH_CP) + strlen(argv[i]) + 457511b41d2SMark Murray strlen(argv[argc - 1]) + 20; 458511b41d2SMark Murray bp = xmalloc(len); 459511b41d2SMark Murray (void) sprintf(bp, "exec %s%s%s %s %s", _PATH_CP, 460511b41d2SMark Murray iamrecursive ? " -r" : "", pflag ? " -p" : "", 461511b41d2SMark Murray argv[i], argv[argc - 1]); 462511b41d2SMark Murray if (verbose_mode) 463511b41d2SMark Murray fprintf(stderr, "Executing: %s\n", bp); 464511b41d2SMark Murray if (system(bp)) 465511b41d2SMark Murray ++errs; 466511b41d2SMark Murray (void) xfree(bp); 467511b41d2SMark Murray continue; 468511b41d2SMark Murray } 469511b41d2SMark Murray *src++ = 0; 470511b41d2SMark Murray if (*src == 0) 471511b41d2SMark Murray src = "."; 472511b41d2SMark Murray if ((host = strchr(argv[i], '@')) == NULL) { 473511b41d2SMark Murray host = argv[i]; 474511b41d2SMark Murray suser = NULL; 475511b41d2SMark Murray } else { 476511b41d2SMark Murray *host++ = 0; 477511b41d2SMark Murray suser = argv[i]; 478511b41d2SMark Murray if (*suser == '\0') 479511b41d2SMark Murray suser = pwd->pw_name; 480511b41d2SMark Murray else if (!okname(suser)) 481511b41d2SMark Murray continue; 482511b41d2SMark Murray } 483511b41d2SMark Murray host = cleanhostname(host); 484511b41d2SMark Murray len = strlen(src) + CMDNEEDS + 20; 485511b41d2SMark Murray bp = xmalloc(len); 486511b41d2SMark Murray (void) sprintf(bp, "%s -f %s", cmd, src); 487511b41d2SMark Murray if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 488511b41d2SMark Murray (void) xfree(bp); 489511b41d2SMark Murray ++errs; 490511b41d2SMark Murray continue; 491511b41d2SMark Murray } 492511b41d2SMark Murray xfree(bp); 493511b41d2SMark Murray sink(1, argv + argc - 1); 494511b41d2SMark Murray (void) close(remin); 495511b41d2SMark Murray remin = remout = -1; 496511b41d2SMark Murray } 497511b41d2SMark Murray } 498511b41d2SMark Murray 499511b41d2SMark Murray void 500511b41d2SMark Murray source(argc, argv) 501511b41d2SMark Murray int argc; 502511b41d2SMark Murray char *argv[]; 503511b41d2SMark Murray { 504511b41d2SMark Murray struct stat stb; 505511b41d2SMark Murray static BUF buffer; 506511b41d2SMark Murray BUF *bp; 507511b41d2SMark Murray off_t i; 508511b41d2SMark Murray int amt, fd, haderr, indx, result; 509511b41d2SMark Murray char *last, *name, buf[2048]; 510511b41d2SMark Murray 511511b41d2SMark Murray for (indx = 0; indx < argc; ++indx) { 512511b41d2SMark Murray name = argv[indx]; 513511b41d2SMark Murray statbytes = 0; 514511b41d2SMark Murray if ((fd = open(name, O_RDONLY, 0)) < 0) 515511b41d2SMark Murray goto syserr; 516511b41d2SMark Murray if (fstat(fd, &stb) < 0) { 517511b41d2SMark Murray syserr: run_err("%s: %s", name, strerror(errno)); 518511b41d2SMark Murray goto next; 519511b41d2SMark Murray } 520511b41d2SMark Murray switch (stb.st_mode & S_IFMT) { 521511b41d2SMark Murray case S_IFREG: 522511b41d2SMark Murray break; 523511b41d2SMark Murray case S_IFDIR: 524511b41d2SMark Murray if (iamrecursive) { 525511b41d2SMark Murray rsource(name, &stb); 526511b41d2SMark Murray goto next; 527511b41d2SMark Murray } 528511b41d2SMark Murray /* FALLTHROUGH */ 529511b41d2SMark Murray default: 530511b41d2SMark Murray run_err("%s: not a regular file", name); 531511b41d2SMark Murray goto next; 532511b41d2SMark Murray } 533511b41d2SMark Murray if ((last = strrchr(name, '/')) == NULL) 534511b41d2SMark Murray last = name; 535511b41d2SMark Murray else 536511b41d2SMark Murray ++last; 537511b41d2SMark Murray curfile = last; 538511b41d2SMark Murray if (pflag) { 539511b41d2SMark Murray /* 540511b41d2SMark Murray * Make it compatible with possible future 541511b41d2SMark Murray * versions expecting microseconds. 542511b41d2SMark Murray */ 543511b41d2SMark Murray (void) sprintf(buf, "T%lu 0 %lu 0\n", 544511b41d2SMark Murray (unsigned long) stb.st_mtime, 545511b41d2SMark Murray (unsigned long) stb.st_atime); 546511b41d2SMark Murray (void) write(remout, buf, strlen(buf)); 547511b41d2SMark Murray if (response() < 0) 548511b41d2SMark Murray goto next; 549511b41d2SMark Murray } 550511b41d2SMark Murray #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 551511b41d2SMark Murray (void) sprintf(buf, "C%04o %lu %s\n", 552511b41d2SMark Murray (unsigned int) (stb.st_mode & FILEMODEMASK), 553511b41d2SMark Murray (unsigned long) stb.st_size, 554511b41d2SMark Murray last); 555511b41d2SMark Murray if (verbose_mode) { 556511b41d2SMark Murray fprintf(stderr, "Sending file modes: %s", buf); 557511b41d2SMark Murray fflush(stderr); 558511b41d2SMark Murray } 559511b41d2SMark Murray (void) write(remout, buf, strlen(buf)); 560511b41d2SMark Murray if (response() < 0) 561511b41d2SMark Murray goto next; 562511b41d2SMark Murray if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { 563511b41d2SMark Murray next: (void) close(fd); 564511b41d2SMark Murray continue; 565511b41d2SMark Murray } 566511b41d2SMark Murray if (showprogress) { 567511b41d2SMark Murray totalbytes = stb.st_size; 568511b41d2SMark Murray progressmeter(-1); 569511b41d2SMark Murray } 570511b41d2SMark Murray /* Keep writing after an error so that we stay sync'd up. */ 571511b41d2SMark Murray for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 572511b41d2SMark Murray amt = bp->cnt; 573511b41d2SMark Murray if (i + amt > stb.st_size) 574511b41d2SMark Murray amt = stb.st_size - i; 575511b41d2SMark Murray if (!haderr) { 576511b41d2SMark Murray result = read(fd, bp->buf, amt); 577511b41d2SMark Murray if (result != amt) 578511b41d2SMark Murray haderr = result >= 0 ? EIO : errno; 579511b41d2SMark Murray } 580511b41d2SMark Murray if (haderr) 581511b41d2SMark Murray (void) write(remout, bp->buf, amt); 582511b41d2SMark Murray else { 583511b41d2SMark Murray result = write(remout, bp->buf, amt); 584511b41d2SMark Murray if (result != amt) 585511b41d2SMark Murray haderr = result >= 0 ? EIO : errno; 586511b41d2SMark Murray statbytes += result; 587511b41d2SMark Murray } 588511b41d2SMark Murray } 589511b41d2SMark Murray if (showprogress) 590511b41d2SMark Murray progressmeter(1); 591511b41d2SMark Murray 592511b41d2SMark Murray if (close(fd) < 0 && !haderr) 593511b41d2SMark Murray haderr = errno; 594511b41d2SMark Murray if (!haderr) 595511b41d2SMark Murray (void) write(remout, "", 1); 596511b41d2SMark Murray else 597511b41d2SMark Murray run_err("%s: %s", name, strerror(haderr)); 598511b41d2SMark Murray (void) response(); 599511b41d2SMark Murray } 600511b41d2SMark Murray } 601511b41d2SMark Murray 602511b41d2SMark Murray void 603511b41d2SMark Murray rsource(name, statp) 604511b41d2SMark Murray char *name; 605511b41d2SMark Murray struct stat *statp; 606511b41d2SMark Murray { 607511b41d2SMark Murray DIR *dirp; 608511b41d2SMark Murray struct dirent *dp; 609511b41d2SMark Murray char *last, *vect[1], path[1100]; 610511b41d2SMark Murray 611511b41d2SMark Murray if (!(dirp = opendir(name))) { 612511b41d2SMark Murray run_err("%s: %s", name, strerror(errno)); 613511b41d2SMark Murray return; 614511b41d2SMark Murray } 615511b41d2SMark Murray last = strrchr(name, '/'); 616511b41d2SMark Murray if (last == 0) 617511b41d2SMark Murray last = name; 618511b41d2SMark Murray else 619511b41d2SMark Murray last++; 620511b41d2SMark Murray if (pflag) { 621511b41d2SMark Murray (void) sprintf(path, "T%lu 0 %lu 0\n", 622511b41d2SMark Murray (unsigned long) statp->st_mtime, 623511b41d2SMark Murray (unsigned long) statp->st_atime); 624511b41d2SMark Murray (void) write(remout, path, strlen(path)); 625511b41d2SMark Murray if (response() < 0) { 626511b41d2SMark Murray closedir(dirp); 627511b41d2SMark Murray return; 628511b41d2SMark Murray } 629511b41d2SMark Murray } 630511b41d2SMark Murray (void) sprintf(path, "D%04o %d %.1024s\n", 631511b41d2SMark Murray (unsigned int) (statp->st_mode & FILEMODEMASK), 632511b41d2SMark Murray 0, last); 633511b41d2SMark Murray if (verbose_mode) 634511b41d2SMark Murray fprintf(stderr, "Entering directory: %s", path); 635511b41d2SMark Murray (void) write(remout, path, strlen(path)); 636511b41d2SMark Murray if (response() < 0) { 637511b41d2SMark Murray closedir(dirp); 638511b41d2SMark Murray return; 639511b41d2SMark Murray } 640511b41d2SMark Murray while ((dp = readdir(dirp))) { 641511b41d2SMark Murray if (dp->d_ino == 0) 642511b41d2SMark Murray continue; 643511b41d2SMark Murray if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 644511b41d2SMark Murray continue; 645511b41d2SMark Murray if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { 646511b41d2SMark Murray run_err("%s/%s: name too long", name, dp->d_name); 647511b41d2SMark Murray continue; 648511b41d2SMark Murray } 649511b41d2SMark Murray (void) sprintf(path, "%s/%s", name, dp->d_name); 650511b41d2SMark Murray vect[0] = path; 651511b41d2SMark Murray source(1, vect); 652511b41d2SMark Murray } 653511b41d2SMark Murray (void) closedir(dirp); 654511b41d2SMark Murray (void) write(remout, "E\n", 2); 655511b41d2SMark Murray (void) response(); 656511b41d2SMark Murray } 657511b41d2SMark Murray 658511b41d2SMark Murray void 659511b41d2SMark Murray sink(argc, argv) 660511b41d2SMark Murray int argc; 661511b41d2SMark Murray char *argv[]; 662511b41d2SMark Murray { 663511b41d2SMark Murray static BUF buffer; 664511b41d2SMark Murray struct stat stb; 665511b41d2SMark Murray enum { 666511b41d2SMark Murray YES, NO, DISPLAYED 667511b41d2SMark Murray } wrerr; 668511b41d2SMark Murray BUF *bp; 669511b41d2SMark Murray off_t i, j; 670511b41d2SMark Murray int amt, count, exists, first, mask, mode, ofd, omode; 671511b41d2SMark Murray int setimes, size, targisdir, wrerrno = 0; 672511b41d2SMark Murray char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; 673511b41d2SMark Murray struct utimbuf ut; 674511b41d2SMark Murray int dummy_usec; 675511b41d2SMark Murray 676511b41d2SMark Murray #define SCREWUP(str) { why = str; goto screwup; } 677511b41d2SMark Murray 678511b41d2SMark Murray setimes = targisdir = 0; 679511b41d2SMark Murray mask = umask(0); 680511b41d2SMark Murray if (!pflag) 681511b41d2SMark Murray (void) umask(mask); 682511b41d2SMark Murray if (argc != 1) { 683511b41d2SMark Murray run_err("ambiguous target"); 684511b41d2SMark Murray exit(1); 685511b41d2SMark Murray } 686511b41d2SMark Murray targ = *argv; 687511b41d2SMark Murray if (targetshouldbedirectory) 688511b41d2SMark Murray verifydir(targ); 689511b41d2SMark Murray 690511b41d2SMark Murray (void) write(remout, "", 1); 691511b41d2SMark Murray if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 692511b41d2SMark Murray targisdir = 1; 693511b41d2SMark Murray for (first = 1;; first = 0) { 694511b41d2SMark Murray cp = buf; 695511b41d2SMark Murray if (read(remin, cp, 1) <= 0) 696511b41d2SMark Murray return; 697511b41d2SMark Murray if (*cp++ == '\n') 698511b41d2SMark Murray SCREWUP("unexpected <newline>"); 699511b41d2SMark Murray do { 700511b41d2SMark Murray if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 701511b41d2SMark Murray SCREWUP("lost connection"); 702511b41d2SMark Murray *cp++ = ch; 703511b41d2SMark Murray } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 704511b41d2SMark Murray *cp = 0; 705511b41d2SMark Murray 706511b41d2SMark Murray if (buf[0] == '\01' || buf[0] == '\02') { 707511b41d2SMark Murray if (iamremote == 0) 708511b41d2SMark Murray (void) write(STDERR_FILENO, 709511b41d2SMark Murray buf + 1, strlen(buf + 1)); 710511b41d2SMark Murray if (buf[0] == '\02') 711511b41d2SMark Murray exit(1); 712511b41d2SMark Murray ++errs; 713511b41d2SMark Murray continue; 714511b41d2SMark Murray } 715511b41d2SMark Murray if (buf[0] == 'E') { 716511b41d2SMark Murray (void) write(remout, "", 1); 717511b41d2SMark Murray return; 718511b41d2SMark Murray } 719511b41d2SMark Murray if (ch == '\n') 720511b41d2SMark Murray *--cp = 0; 721511b41d2SMark Murray 722511b41d2SMark Murray #define getnum(t) (t) = 0; \ 723511b41d2SMark Murray while (*cp >= '0' && *cp <= '9') (t) = (t) * 10 + (*cp++ - '0'); 724511b41d2SMark Murray cp = buf; 725511b41d2SMark Murray if (*cp == 'T') { 726511b41d2SMark Murray setimes++; 727511b41d2SMark Murray cp++; 728511b41d2SMark Murray getnum(ut.modtime); 729511b41d2SMark Murray if (*cp++ != ' ') 730511b41d2SMark Murray SCREWUP("mtime.sec not delimited"); 731511b41d2SMark Murray getnum(dummy_usec); 732511b41d2SMark Murray if (*cp++ != ' ') 733511b41d2SMark Murray SCREWUP("mtime.usec not delimited"); 734511b41d2SMark Murray getnum(ut.actime); 735511b41d2SMark Murray if (*cp++ != ' ') 736511b41d2SMark Murray SCREWUP("atime.sec not delimited"); 737511b41d2SMark Murray getnum(dummy_usec); 738511b41d2SMark Murray if (*cp++ != '\0') 739511b41d2SMark Murray SCREWUP("atime.usec not delimited"); 740511b41d2SMark Murray (void) write(remout, "", 1); 741511b41d2SMark Murray continue; 742511b41d2SMark Murray } 743511b41d2SMark Murray if (*cp != 'C' && *cp != 'D') { 744511b41d2SMark Murray /* 745511b41d2SMark Murray * Check for the case "rcp remote:foo\* local:bar". 746511b41d2SMark Murray * In this case, the line "No match." can be returned 747511b41d2SMark Murray * by the shell before the rcp command on the remote is 748511b41d2SMark Murray * executed so the ^Aerror_message convention isn't 749511b41d2SMark Murray * followed. 750511b41d2SMark Murray */ 751511b41d2SMark Murray if (first) { 752511b41d2SMark Murray run_err("%s", cp); 753511b41d2SMark Murray exit(1); 754511b41d2SMark Murray } 755511b41d2SMark Murray SCREWUP("expected control record"); 756511b41d2SMark Murray } 757511b41d2SMark Murray mode = 0; 758511b41d2SMark Murray for (++cp; cp < buf + 5; cp++) { 759511b41d2SMark Murray if (*cp < '0' || *cp > '7') 760511b41d2SMark Murray SCREWUP("bad mode"); 761511b41d2SMark Murray mode = (mode << 3) | (*cp - '0'); 762511b41d2SMark Murray } 763511b41d2SMark Murray if (*cp++ != ' ') 764511b41d2SMark Murray SCREWUP("mode not delimited"); 765511b41d2SMark Murray 766511b41d2SMark Murray for (size = 0; *cp >= '0' && *cp <= '9';) 767511b41d2SMark Murray size = size * 10 + (*cp++ - '0'); 768511b41d2SMark Murray if (*cp++ != ' ') 769511b41d2SMark Murray SCREWUP("size not delimited"); 770511b41d2SMark Murray if (targisdir) { 771511b41d2SMark Murray static char *namebuf; 772511b41d2SMark Murray static int cursize; 773511b41d2SMark Murray size_t need; 774511b41d2SMark Murray 775511b41d2SMark Murray need = strlen(targ) + strlen(cp) + 250; 776511b41d2SMark Murray if (need > cursize) 777511b41d2SMark Murray namebuf = xmalloc(need); 778511b41d2SMark Murray (void) sprintf(namebuf, "%s%s%s", targ, 779511b41d2SMark Murray *targ ? "/" : "", cp); 780511b41d2SMark Murray np = namebuf; 781511b41d2SMark Murray } else 782511b41d2SMark Murray np = targ; 783511b41d2SMark Murray curfile = cp; 784511b41d2SMark Murray exists = stat(np, &stb) == 0; 785511b41d2SMark Murray if (buf[0] == 'D') { 786511b41d2SMark Murray int mod_flag = pflag; 787511b41d2SMark Murray if (exists) { 788511b41d2SMark Murray if (!S_ISDIR(stb.st_mode)) { 789511b41d2SMark Murray errno = ENOTDIR; 790511b41d2SMark Murray goto bad; 791511b41d2SMark Murray } 792511b41d2SMark Murray if (pflag) 793511b41d2SMark Murray (void) chmod(np, mode); 794511b41d2SMark Murray } else { 795511b41d2SMark Murray /* Handle copying from a read-only 796511b41d2SMark Murray directory */ 797511b41d2SMark Murray mod_flag = 1; 798511b41d2SMark Murray if (mkdir(np, mode | S_IRWXU) < 0) 799511b41d2SMark Murray goto bad; 800511b41d2SMark Murray } 801511b41d2SMark Murray vect[0] = np; 802511b41d2SMark Murray sink(1, vect); 803511b41d2SMark Murray if (setimes) { 804511b41d2SMark Murray setimes = 0; 805511b41d2SMark Murray if (utime(np, &ut) < 0) 806511b41d2SMark Murray run_err("%s: set times: %s", 807511b41d2SMark Murray np, strerror(errno)); 808511b41d2SMark Murray } 809511b41d2SMark Murray if (mod_flag) 810511b41d2SMark Murray (void) chmod(np, mode); 811511b41d2SMark Murray continue; 812511b41d2SMark Murray } 813511b41d2SMark Murray omode = mode; 814511b41d2SMark Murray mode |= S_IWRITE; 815511b41d2SMark Murray if ((ofd = open(np, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) { 816511b41d2SMark Murray bad: run_err("%s: %s", np, strerror(errno)); 817511b41d2SMark Murray continue; 818511b41d2SMark Murray } 819511b41d2SMark Murray (void) write(remout, "", 1); 820511b41d2SMark Murray if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { 821511b41d2SMark Murray (void) close(ofd); 822511b41d2SMark Murray continue; 823511b41d2SMark Murray } 824511b41d2SMark Murray cp = bp->buf; 825511b41d2SMark Murray wrerr = NO; 826511b41d2SMark Murray 827511b41d2SMark Murray if (showprogress) { 828511b41d2SMark Murray totalbytes = size; 829511b41d2SMark Murray progressmeter(-1); 830511b41d2SMark Murray } 831511b41d2SMark Murray statbytes = 0; 832511b41d2SMark Murray for (count = i = 0; i < size; i += 4096) { 833511b41d2SMark Murray amt = 4096; 834511b41d2SMark Murray if (i + amt > size) 835511b41d2SMark Murray amt = size - i; 836511b41d2SMark Murray count += amt; 837511b41d2SMark Murray do { 838511b41d2SMark Murray j = read(remin, cp, amt); 839511b41d2SMark Murray if (j <= 0) { 840511b41d2SMark Murray run_err("%s", j ? strerror(errno) : 841511b41d2SMark Murray "dropped connection"); 842511b41d2SMark Murray exit(1); 843511b41d2SMark Murray } 844511b41d2SMark Murray amt -= j; 845511b41d2SMark Murray cp += j; 846511b41d2SMark Murray statbytes += j; 847511b41d2SMark Murray } while (amt > 0); 848511b41d2SMark Murray if (count == bp->cnt) { 849511b41d2SMark Murray /* Keep reading so we stay sync'd up. */ 850511b41d2SMark Murray if (wrerr == NO) { 851511b41d2SMark Murray j = write(ofd, bp->buf, count); 852511b41d2SMark Murray if (j != count) { 853511b41d2SMark Murray wrerr = YES; 854511b41d2SMark Murray wrerrno = j >= 0 ? EIO : errno; 855511b41d2SMark Murray } 856511b41d2SMark Murray } 857511b41d2SMark Murray count = 0; 858511b41d2SMark Murray cp = bp->buf; 859511b41d2SMark Murray } 860511b41d2SMark Murray } 861511b41d2SMark Murray if (showprogress) 862511b41d2SMark Murray progressmeter(1); 863511b41d2SMark Murray if (count != 0 && wrerr == NO && 864511b41d2SMark Murray (j = write(ofd, bp->buf, count)) != count) { 865511b41d2SMark Murray wrerr = YES; 866511b41d2SMark Murray wrerrno = j >= 0 ? EIO : errno; 867511b41d2SMark Murray } 868511b41d2SMark Murray #if 0 869511b41d2SMark Murray if (ftruncate(ofd, size)) { 870511b41d2SMark Murray run_err("%s: truncate: %s", np, strerror(errno)); 871511b41d2SMark Murray wrerr = DISPLAYED; 872511b41d2SMark Murray } 873511b41d2SMark Murray #endif 874511b41d2SMark Murray if (pflag) { 875511b41d2SMark Murray if (exists || omode != mode) 876511b41d2SMark Murray if (fchmod(ofd, omode)) 877511b41d2SMark Murray run_err("%s: set mode: %s", 878511b41d2SMark Murray np, strerror(errno)); 879511b41d2SMark Murray } else { 880511b41d2SMark Murray if (!exists && omode != mode) 881511b41d2SMark Murray if (fchmod(ofd, omode & ~mask)) 882511b41d2SMark Murray run_err("%s: set mode: %s", 883511b41d2SMark Murray np, strerror(errno)); 884511b41d2SMark Murray } 885511b41d2SMark Murray (void) close(ofd); 886511b41d2SMark Murray (void) response(); 887511b41d2SMark Murray if (setimes && wrerr == NO) { 888511b41d2SMark Murray setimes = 0; 889511b41d2SMark Murray if (utime(np, &ut) < 0) { 890511b41d2SMark Murray run_err("%s: set times: %s", 891511b41d2SMark Murray np, strerror(errno)); 892511b41d2SMark Murray wrerr = DISPLAYED; 893511b41d2SMark Murray } 894511b41d2SMark Murray } 895511b41d2SMark Murray switch (wrerr) { 896511b41d2SMark Murray case YES: 897511b41d2SMark Murray run_err("%s: %s", np, strerror(wrerrno)); 898511b41d2SMark Murray break; 899511b41d2SMark Murray case NO: 900511b41d2SMark Murray (void) write(remout, "", 1); 901511b41d2SMark Murray break; 902511b41d2SMark Murray case DISPLAYED: 903511b41d2SMark Murray break; 904511b41d2SMark Murray } 905511b41d2SMark Murray } 906511b41d2SMark Murray screwup: 907511b41d2SMark Murray run_err("protocol error: %s", why); 908511b41d2SMark Murray exit(1); 909511b41d2SMark Murray } 910511b41d2SMark Murray 911511b41d2SMark Murray int 912511b41d2SMark Murray response() 913511b41d2SMark Murray { 914511b41d2SMark Murray char ch, *cp, resp, rbuf[2048]; 915511b41d2SMark Murray 916511b41d2SMark Murray if (read(remin, &resp, sizeof(resp)) != sizeof(resp)) 917511b41d2SMark Murray lostconn(0); 918511b41d2SMark Murray 919511b41d2SMark Murray cp = rbuf; 920511b41d2SMark Murray switch (resp) { 921511b41d2SMark Murray case 0: /* ok */ 922511b41d2SMark Murray return (0); 923511b41d2SMark Murray default: 924511b41d2SMark Murray *cp++ = resp; 925511b41d2SMark Murray /* FALLTHROUGH */ 926511b41d2SMark Murray case 1: /* error, followed by error msg */ 927511b41d2SMark Murray case 2: /* fatal error, "" */ 928511b41d2SMark Murray do { 929511b41d2SMark Murray if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 930511b41d2SMark Murray lostconn(0); 931511b41d2SMark Murray *cp++ = ch; 932511b41d2SMark Murray } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 933511b41d2SMark Murray 934511b41d2SMark Murray if (!iamremote) 935511b41d2SMark Murray (void) write(STDERR_FILENO, rbuf, cp - rbuf); 936511b41d2SMark Murray ++errs; 937511b41d2SMark Murray if (resp == 1) 938511b41d2SMark Murray return (-1); 939511b41d2SMark Murray exit(1); 940511b41d2SMark Murray } 941511b41d2SMark Murray /* NOTREACHED */ 942511b41d2SMark Murray } 943511b41d2SMark Murray 944511b41d2SMark Murray void 945511b41d2SMark Murray usage() 946511b41d2SMark Murray { 947511b41d2SMark Murray (void) fprintf(stderr, 948511b41d2SMark Murray "usage: scp [-pqrvC46] [-P port] [-c cipher] [-i identity] f1 f2; or:\n scp [options] f1 ... fn directory\n"); 949511b41d2SMark Murray exit(1); 950511b41d2SMark Murray } 951511b41d2SMark Murray 952511b41d2SMark Murray void 953511b41d2SMark Murray run_err(const char *fmt,...) 954511b41d2SMark Murray { 955511b41d2SMark Murray static FILE *fp; 956511b41d2SMark Murray va_list ap; 957511b41d2SMark Murray va_start(ap, fmt); 958511b41d2SMark Murray 959511b41d2SMark Murray ++errs; 960511b41d2SMark Murray if (fp == NULL && !(fp = fdopen(remout, "w"))) 961511b41d2SMark Murray return; 962511b41d2SMark Murray (void) fprintf(fp, "%c", 0x01); 963511b41d2SMark Murray (void) fprintf(fp, "scp: "); 964511b41d2SMark Murray (void) vfprintf(fp, fmt, ap); 965511b41d2SMark Murray (void) fprintf(fp, "\n"); 966511b41d2SMark Murray (void) fflush(fp); 967511b41d2SMark Murray 968511b41d2SMark Murray if (!iamremote) { 969511b41d2SMark Murray vfprintf(stderr, fmt, ap); 970511b41d2SMark Murray fprintf(stderr, "\n"); 971511b41d2SMark Murray } 972511b41d2SMark Murray va_end(ap); 973511b41d2SMark Murray } 974511b41d2SMark Murray 975511b41d2SMark Murray /* Stuff below is from BSD rcp util.c. */ 976511b41d2SMark Murray 977511b41d2SMark Murray /*- 978511b41d2SMark Murray * Copyright (c) 1992, 1993 979511b41d2SMark Murray * The Regents of the University of California. All rights reserved. 980511b41d2SMark Murray * 981511b41d2SMark Murray * Redistribution and use in source and binary forms, with or without 982511b41d2SMark Murray * modification, are permitted provided that the following conditions 983511b41d2SMark Murray * are met: 984511b41d2SMark Murray * 1. Redistributions of source code must retain the above copyright 985511b41d2SMark Murray * notice, this list of conditions and the following disclaimer. 986511b41d2SMark Murray * 2. Redistributions in binary form must reproduce the above copyright 987511b41d2SMark Murray * notice, this list of conditions and the following disclaimer in the 988511b41d2SMark Murray * documentation and/or other materials provided with the distribution. 989511b41d2SMark Murray * 3. All advertising materials mentioning features or use of this software 990511b41d2SMark Murray * must display the following acknowledgement: 991511b41d2SMark Murray * This product includes software developed by the University of 992511b41d2SMark Murray * California, Berkeley and its contributors. 993511b41d2SMark Murray * 4. Neither the name of the University nor the names of its contributors 994511b41d2SMark Murray * may be used to endorse or promote products derived from this software 995511b41d2SMark Murray * without specific prior written permission. 996511b41d2SMark Murray * 997511b41d2SMark Murray * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 998511b41d2SMark Murray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 999511b41d2SMark Murray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1000511b41d2SMark Murray * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1001511b41d2SMark Murray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1002511b41d2SMark Murray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1003511b41d2SMark Murray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1004511b41d2SMark Murray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1005511b41d2SMark Murray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1006511b41d2SMark Murray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1007511b41d2SMark Murray * SUCH DAMAGE. 1008511b41d2SMark Murray * 1009511b41d2SMark Murray * $Id: scp.c,v 1.25 2000/01/24 22:11:20 markus Exp $ 1010511b41d2SMark Murray */ 1011511b41d2SMark Murray 1012511b41d2SMark Murray char * 1013511b41d2SMark Murray colon(cp) 1014511b41d2SMark Murray char *cp; 1015511b41d2SMark Murray { 1016511b41d2SMark Murray int flag = 0; 1017511b41d2SMark Murray 1018511b41d2SMark Murray if (*cp == ':') /* Leading colon is part of file name. */ 1019511b41d2SMark Murray return (0); 1020511b41d2SMark Murray if (*cp == '[') 1021511b41d2SMark Murray flag = 1; 1022511b41d2SMark Murray 1023511b41d2SMark Murray for (; *cp; ++cp) { 1024511b41d2SMark Murray if (*cp == '@' && *(cp+1) == '[') 1025511b41d2SMark Murray flag = 1; 1026511b41d2SMark Murray if (*cp == ']' && *(cp+1) == ':' && flag) 1027511b41d2SMark Murray return (cp+1); 1028511b41d2SMark Murray if (*cp == ':' && !flag) 1029511b41d2SMark Murray return (cp); 1030511b41d2SMark Murray if (*cp == '/') 1031511b41d2SMark Murray return (0); 1032511b41d2SMark Murray } 1033511b41d2SMark Murray return (0); 1034511b41d2SMark Murray } 1035511b41d2SMark Murray 1036511b41d2SMark Murray void 1037511b41d2SMark Murray verifydir(cp) 1038511b41d2SMark Murray char *cp; 1039511b41d2SMark Murray { 1040511b41d2SMark Murray struct stat stb; 1041511b41d2SMark Murray 1042511b41d2SMark Murray if (!stat(cp, &stb)) { 1043511b41d2SMark Murray if (S_ISDIR(stb.st_mode)) 1044511b41d2SMark Murray return; 1045511b41d2SMark Murray errno = ENOTDIR; 1046511b41d2SMark Murray } 1047511b41d2SMark Murray run_err("%s: %s", cp, strerror(errno)); 1048511b41d2SMark Murray exit(1); 1049511b41d2SMark Murray } 1050511b41d2SMark Murray 1051511b41d2SMark Murray int 1052511b41d2SMark Murray okname(cp0) 1053511b41d2SMark Murray char *cp0; 1054511b41d2SMark Murray { 1055511b41d2SMark Murray int c; 1056511b41d2SMark Murray char *cp; 1057511b41d2SMark Murray 1058511b41d2SMark Murray cp = cp0; 1059511b41d2SMark Murray do { 1060511b41d2SMark Murray c = *cp; 1061511b41d2SMark Murray if (c & 0200) 1062511b41d2SMark Murray goto bad; 1063511b41d2SMark Murray if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-' && c != '.') 1064511b41d2SMark Murray goto bad; 1065511b41d2SMark Murray } while (*++cp); 1066511b41d2SMark Murray return (1); 1067511b41d2SMark Murray 1068511b41d2SMark Murray bad: fprintf(stderr, "%s: invalid user name\n", cp0); 1069511b41d2SMark Murray return (0); 1070511b41d2SMark Murray } 1071511b41d2SMark Murray 1072511b41d2SMark Murray BUF * 1073511b41d2SMark Murray allocbuf(bp, fd, blksize) 1074511b41d2SMark Murray BUF *bp; 1075511b41d2SMark Murray int fd, blksize; 1076511b41d2SMark Murray { 1077511b41d2SMark Murray size_t size; 1078511b41d2SMark Murray struct stat stb; 1079511b41d2SMark Murray 1080511b41d2SMark Murray if (fstat(fd, &stb) < 0) { 1081511b41d2SMark Murray run_err("fstat: %s", strerror(errno)); 1082511b41d2SMark Murray return (0); 1083511b41d2SMark Murray } 1084511b41d2SMark Murray if (stb.st_blksize == 0) 1085511b41d2SMark Murray size = blksize; 1086511b41d2SMark Murray else 1087511b41d2SMark Murray size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % 1088511b41d2SMark Murray stb.st_blksize; 1089511b41d2SMark Murray if (bp->cnt >= size) 1090511b41d2SMark Murray return (bp); 1091511b41d2SMark Murray if (bp->buf == NULL) 1092511b41d2SMark Murray bp->buf = xmalloc(size); 1093511b41d2SMark Murray else 1094511b41d2SMark Murray bp->buf = xrealloc(bp->buf, size); 1095511b41d2SMark Murray bp->cnt = size; 1096511b41d2SMark Murray return (bp); 1097511b41d2SMark Murray } 1098511b41d2SMark Murray 1099511b41d2SMark Murray void 1100511b41d2SMark Murray lostconn(signo) 1101511b41d2SMark Murray int signo; 1102511b41d2SMark Murray { 1103511b41d2SMark Murray if (!iamremote) 1104511b41d2SMark Murray fprintf(stderr, "lost connection\n"); 1105511b41d2SMark Murray exit(1); 1106511b41d2SMark Murray } 1107511b41d2SMark Murray 1108511b41d2SMark Murray 1109511b41d2SMark Murray void 1110511b41d2SMark Murray alarmtimer(int wait) 1111511b41d2SMark Murray { 1112511b41d2SMark Murray struct itimerval itv; 1113511b41d2SMark Murray 1114511b41d2SMark Murray itv.it_value.tv_sec = wait; 1115511b41d2SMark Murray itv.it_value.tv_usec = 0; 1116511b41d2SMark Murray itv.it_interval = itv.it_value; 1117511b41d2SMark Murray setitimer(ITIMER_REAL, &itv, NULL); 1118511b41d2SMark Murray } 1119511b41d2SMark Murray 1120511b41d2SMark Murray void 1121511b41d2SMark Murray updateprogressmeter(void) 1122511b41d2SMark Murray { 1123511b41d2SMark Murray int save_errno = errno; 1124511b41d2SMark Murray 1125511b41d2SMark Murray progressmeter(0); 1126511b41d2SMark Murray errno = save_errno; 1127511b41d2SMark Murray } 1128511b41d2SMark Murray 1129511b41d2SMark Murray int 1130511b41d2SMark Murray foregroundproc() 1131511b41d2SMark Murray { 1132511b41d2SMark Murray static pid_t pgrp = -1; 1133511b41d2SMark Murray int ctty_pgrp; 1134511b41d2SMark Murray 1135511b41d2SMark Murray if (pgrp == -1) 1136511b41d2SMark Murray pgrp = getpgrp(); 1137511b41d2SMark Murray 1138511b41d2SMark Murray return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && 1139511b41d2SMark Murray ctty_pgrp == pgrp)); 1140511b41d2SMark Murray } 1141511b41d2SMark Murray 1142511b41d2SMark Murray void 1143511b41d2SMark Murray progressmeter(int flag) 1144511b41d2SMark Murray { 1145511b41d2SMark Murray static const char prefixes[] = " KMGTP"; 1146511b41d2SMark Murray static struct timeval lastupdate; 1147511b41d2SMark Murray static off_t lastsize; 1148511b41d2SMark Murray struct timeval now, td, wait; 1149511b41d2SMark Murray off_t cursize, abbrevsize; 1150511b41d2SMark Murray double elapsed; 1151511b41d2SMark Murray int ratio, barlength, i, remaining; 1152511b41d2SMark Murray char buf[256]; 1153511b41d2SMark Murray 1154511b41d2SMark Murray if (flag == -1) { 1155511b41d2SMark Murray (void) gettimeofday(&start, (struct timezone *) 0); 1156511b41d2SMark Murray lastupdate = start; 1157511b41d2SMark Murray lastsize = 0; 1158511b41d2SMark Murray } 1159511b41d2SMark Murray if (foregroundproc() == 0) 1160511b41d2SMark Murray return; 1161511b41d2SMark Murray 1162511b41d2SMark Murray (void) gettimeofday(&now, (struct timezone *) 0); 1163511b41d2SMark Murray cursize = statbytes; 1164511b41d2SMark Murray if (totalbytes != 0) { 1165511b41d2SMark Murray ratio = 100.0 * cursize / totalbytes; 1166511b41d2SMark Murray ratio = MAX(ratio, 0); 1167511b41d2SMark Murray ratio = MIN(ratio, 100); 1168511b41d2SMark Murray } else 1169511b41d2SMark Murray ratio = 100; 1170511b41d2SMark Murray 1171511b41d2SMark Murray snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); 1172511b41d2SMark Murray 1173511b41d2SMark Murray barlength = getttywidth() - 51; 1174511b41d2SMark Murray if (barlength > 0) { 1175511b41d2SMark Murray i = barlength * ratio / 100; 1176511b41d2SMark Murray snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1177511b41d2SMark Murray "|%.*s%*s|", i, 1178511b41d2SMark Murray "*****************************************************************************" 1179511b41d2SMark Murray "*****************************************************************************", 1180511b41d2SMark Murray barlength - i, ""); 1181511b41d2SMark Murray } 1182511b41d2SMark Murray i = 0; 1183511b41d2SMark Murray abbrevsize = cursize; 1184511b41d2SMark Murray while (abbrevsize >= 100000 && i < sizeof(prefixes)) { 1185511b41d2SMark Murray i++; 1186511b41d2SMark Murray abbrevsize >>= 10; 1187511b41d2SMark Murray } 1188511b41d2SMark Murray snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5qd %c%c ", 1189511b41d2SMark Murray (quad_t) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : 1190511b41d2SMark Murray 'B'); 1191511b41d2SMark Murray 1192511b41d2SMark Murray timersub(&now, &lastupdate, &wait); 1193511b41d2SMark Murray if (cursize > lastsize) { 1194511b41d2SMark Murray lastupdate = now; 1195511b41d2SMark Murray lastsize = cursize; 1196511b41d2SMark Murray if (wait.tv_sec >= STALLTIME) { 1197511b41d2SMark Murray start.tv_sec += wait.tv_sec; 1198511b41d2SMark Murray start.tv_usec += wait.tv_usec; 1199511b41d2SMark Murray } 1200511b41d2SMark Murray wait.tv_sec = 0; 1201511b41d2SMark Murray } 1202511b41d2SMark Murray timersub(&now, &start, &td); 1203511b41d2SMark Murray elapsed = td.tv_sec + (td.tv_usec / 1000000.0); 1204511b41d2SMark Murray 1205511b41d2SMark Murray if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes) { 1206511b41d2SMark Murray snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1207511b41d2SMark Murray " --:-- ETA"); 1208511b41d2SMark Murray } else if (wait.tv_sec >= STALLTIME) { 1209511b41d2SMark Murray snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1210511b41d2SMark Murray " - stalled -"); 1211511b41d2SMark Murray } else { 1212511b41d2SMark Murray remaining = (int) (totalbytes / (statbytes / elapsed) - elapsed); 1213511b41d2SMark Murray i = remaining / 3600; 1214511b41d2SMark Murray if (i) 1215511b41d2SMark Murray snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1216511b41d2SMark Murray "%2d:", i); 1217511b41d2SMark Murray else 1218511b41d2SMark Murray snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1219511b41d2SMark Murray " "); 1220511b41d2SMark Murray i = remaining % 3600; 1221511b41d2SMark Murray snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1222511b41d2SMark Murray "%02d:%02d ETA", i / 60, i % 60); 1223511b41d2SMark Murray } 1224511b41d2SMark Murray atomicio(write, fileno(stdout), buf, strlen(buf)); 1225511b41d2SMark Murray 1226511b41d2SMark Murray if (flag == -1) { 1227511b41d2SMark Murray signal(SIGALRM, (void *) updateprogressmeter); 1228511b41d2SMark Murray alarmtimer(1); 1229511b41d2SMark Murray } else if (flag == 1) { 1230511b41d2SMark Murray alarmtimer(0); 1231511b41d2SMark Murray write(fileno(stdout), "\n", 1); 1232511b41d2SMark Murray statbytes = 0; 1233511b41d2SMark Murray } 1234511b41d2SMark Murray } 1235511b41d2SMark Murray 1236511b41d2SMark Murray int 1237511b41d2SMark Murray getttywidth(void) 1238511b41d2SMark Murray { 1239511b41d2SMark Murray struct winsize winsize; 1240511b41d2SMark Murray 1241511b41d2SMark Murray if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) 1242511b41d2SMark Murray return (winsize.ws_col ? winsize.ws_col : 80); 1243511b41d2SMark Murray else 1244511b41d2SMark Murray return (80); 1245511b41d2SMark Murray } 1246