1511b41d2SMark Murray /* 2b66f2d16SKris Kennaway * scp - secure remote copy. This is basically patched BSD rcp which 3b66f2d16SKris Kennaway * uses ssh to do the data transfer (instead of using rcmd). 4511b41d2SMark Murray * 5b66f2d16SKris Kennaway * NOTE: This version should NOT be suid root. (This uses ssh to 6b66f2d16SKris Kennaway * do the transfer and ssh has the necessary privileges.) 7511b41d2SMark Murray * 8511b41d2SMark Murray * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> 9511b41d2SMark Murray * 10b66f2d16SKris Kennaway * As far as I am concerned, the code I have written for this software 11b66f2d16SKris Kennaway * can be used freely for any purpose. Any derived versions of this 12b66f2d16SKris Kennaway * software must be clearly marked as such, and if the derived work is 13b66f2d16SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 14b66f2d16SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 15b66f2d16SKris Kennaway */ 16b66f2d16SKris Kennaway /* 17b66f2d16SKris Kennaway * Copyright (c) 1999 Theo de Raadt. All rights reserved. 18b66f2d16SKris Kennaway * Copyright (c) 1999 Aaron Campbell. All rights reserved. 19b66f2d16SKris Kennaway * 20b66f2d16SKris Kennaway * Redistribution and use in source and binary forms, with or without 21b66f2d16SKris Kennaway * modification, are permitted provided that the following conditions 22b66f2d16SKris Kennaway * are met: 23b66f2d16SKris Kennaway * 1. Redistributions of source code must retain the above copyright 24b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer. 25b66f2d16SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 26b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer in the 27b66f2d16SKris Kennaway * documentation and/or other materials provided with the distribution. 28b66f2d16SKris Kennaway * 29b66f2d16SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 30b66f2d16SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 31b66f2d16SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 32b66f2d16SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 33b66f2d16SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 34b66f2d16SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35b66f2d16SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36b66f2d16SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37b66f2d16SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 38b66f2d16SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39511b41d2SMark Murray */ 40511b41d2SMark Murray 41511b41d2SMark Murray /* 42b66f2d16SKris Kennaway * Parts from: 43b66f2d16SKris Kennaway * 44511b41d2SMark Murray * Copyright (c) 1983, 1990, 1992, 1993, 1995 45511b41d2SMark Murray * The Regents of the University of California. All rights reserved. 46511b41d2SMark Murray * 47511b41d2SMark Murray * Redistribution and use in source and binary forms, with or without 48511b41d2SMark Murray * modification, are permitted provided that the following conditions 49511b41d2SMark Murray * are met: 50511b41d2SMark Murray * 1. Redistributions of source code must retain the above copyright 51511b41d2SMark Murray * notice, this list of conditions and the following disclaimer. 52511b41d2SMark Murray * 2. Redistributions in binary form must reproduce the above copyright 53511b41d2SMark Murray * notice, this list of conditions and the following disclaimer in the 54511b41d2SMark Murray * documentation and/or other materials provided with the distribution. 55511b41d2SMark Murray * 3. All advertising materials mentioning features or use of this software 56511b41d2SMark Murray * must display the following acknowledgement: 57511b41d2SMark Murray * This product includes software developed by the University of 58511b41d2SMark Murray * California, Berkeley and its contributors. 59511b41d2SMark Murray * 4. Neither the name of the University nor the names of its contributors 60511b41d2SMark Murray * may be used to endorse or promote products derived from this software 61511b41d2SMark Murray * without specific prior written permission. 62511b41d2SMark Murray * 63511b41d2SMark Murray * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 64511b41d2SMark Murray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 65511b41d2SMark Murray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 66511b41d2SMark Murray * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 67511b41d2SMark Murray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 68511b41d2SMark Murray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 69511b41d2SMark Murray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 70511b41d2SMark Murray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 71511b41d2SMark Murray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 72511b41d2SMark Murray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 73511b41d2SMark Murray * SUCH DAMAGE. 74511b41d2SMark Murray * 75511b41d2SMark Murray */ 76511b41d2SMark Murray 77511b41d2SMark Murray #include "includes.h" 78b66f2d16SKris Kennaway RCSID("$OpenBSD: scp.c,v 1.39 2000/09/07 20:53:00 markus Exp $"); 79511b41d2SMark Murray 80511b41d2SMark Murray #include "ssh.h" 81511b41d2SMark Murray #include "xmalloc.h" 82511b41d2SMark Murray #include <utime.h> 83511b41d2SMark Murray 84511b41d2SMark Murray #define _PATH_CP "cp" 85511b41d2SMark Murray 86511b41d2SMark Murray /* For progressmeter() -- number of seconds before xfer considered "stalled" */ 87511b41d2SMark Murray #define STALLTIME 5 88511b41d2SMark Murray 89511b41d2SMark Murray /* Visual statistics about files as they are transferred. */ 90511b41d2SMark Murray void progressmeter(int); 91511b41d2SMark Murray 92511b41d2SMark Murray /* Returns width of the terminal (for progress meter calculations). */ 93511b41d2SMark Murray int getttywidth(void); 94b66f2d16SKris Kennaway int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc); 95511b41d2SMark Murray 96511b41d2SMark Murray /* Time a transfer started. */ 97511b41d2SMark Murray static struct timeval start; 98511b41d2SMark Murray 99511b41d2SMark Murray /* Number of bytes of current file transferred so far. */ 100511b41d2SMark Murray volatile unsigned long statbytes; 101511b41d2SMark Murray 102511b41d2SMark Murray /* Total size of current file. */ 103511b41d2SMark Murray off_t totalbytes = 0; 104511b41d2SMark Murray 105511b41d2SMark Murray /* Name of current file being transferred. */ 106511b41d2SMark Murray char *curfile; 107511b41d2SMark Murray 108511b41d2SMark Murray /* This is set to non-zero if IPv4 is desired. */ 109511b41d2SMark Murray int IPv4 = 0; 110511b41d2SMark Murray 111511b41d2SMark Murray /* This is set to non-zero if IPv6 is desired. */ 112511b41d2SMark Murray int IPv6 = 0; 113511b41d2SMark Murray 114511b41d2SMark Murray /* This is set to non-zero to enable verbose mode. */ 115511b41d2SMark Murray int verbose_mode = 0; 116511b41d2SMark Murray 117511b41d2SMark Murray /* This is set to non-zero if compression is desired. */ 118511b41d2SMark Murray int compress = 0; 119511b41d2SMark Murray 120511b41d2SMark Murray /* This is set to zero if the progressmeter is not desired. */ 121511b41d2SMark Murray int showprogress = 1; 122511b41d2SMark Murray 123511b41d2SMark Murray /* This is set to non-zero if running in batch mode (that is, password 124511b41d2SMark Murray and passphrase queries are not allowed). */ 125511b41d2SMark Murray int batchmode = 0; 126511b41d2SMark Murray 127511b41d2SMark Murray /* This is set to the cipher type string if given on the command line. */ 128511b41d2SMark Murray char *cipher = NULL; 129511b41d2SMark Murray 130511b41d2SMark Murray /* This is set to the RSA authentication identity file name if given on 131511b41d2SMark Murray the command line. */ 132511b41d2SMark Murray char *identity = NULL; 133511b41d2SMark Murray 134511b41d2SMark Murray /* This is the port to use in contacting the remote site (is non-NULL). */ 135511b41d2SMark Murray char *port = NULL; 136511b41d2SMark Murray 137b66f2d16SKris Kennaway /* This is the program to execute for the secured connection. ("ssh" or -S) */ 138b66f2d16SKris Kennaway char *ssh_program = SSH_PROGRAM; 139b66f2d16SKris Kennaway 140511b41d2SMark Murray /* 141511b41d2SMark Murray * This function executes the given command as the specified user on the 142511b41d2SMark Murray * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 143511b41d2SMark Murray * assigns the input and output file descriptors on success. 144511b41d2SMark Murray */ 145511b41d2SMark Murray 146511b41d2SMark Murray int 147b66f2d16SKris Kennaway do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) 148511b41d2SMark Murray { 149511b41d2SMark Murray int pin[2], pout[2], reserved[2]; 150511b41d2SMark Murray 151511b41d2SMark Murray if (verbose_mode) 152511b41d2SMark Murray fprintf(stderr, "Executing: host %s, user %s, command %s\n", 153511b41d2SMark Murray host, remuser ? remuser : "(unspecified)", cmd); 154511b41d2SMark Murray 155511b41d2SMark Murray /* 156511b41d2SMark Murray * Reserve two descriptors so that the real pipes won't get 157511b41d2SMark Murray * descriptors 0 and 1 because that will screw up dup2 below. 158511b41d2SMark Murray */ 159511b41d2SMark Murray pipe(reserved); 160511b41d2SMark Murray 161511b41d2SMark Murray /* Create a socket pair for communicating with ssh. */ 162511b41d2SMark Murray if (pipe(pin) < 0) 163511b41d2SMark Murray fatal("pipe: %s", strerror(errno)); 164511b41d2SMark Murray if (pipe(pout) < 0) 165511b41d2SMark Murray fatal("pipe: %s", strerror(errno)); 166511b41d2SMark Murray 167511b41d2SMark Murray /* Free the reserved descriptors. */ 168511b41d2SMark Murray close(reserved[0]); 169511b41d2SMark Murray close(reserved[1]); 170511b41d2SMark Murray 171511b41d2SMark Murray /* For a child to execute the command on the remote host using ssh. */ 172511b41d2SMark Murray if (fork() == 0) { 173b66f2d16SKris Kennaway char *args[100]; /* XXX careful */ 174511b41d2SMark Murray unsigned int i; 175511b41d2SMark Murray 176511b41d2SMark Murray /* Child. */ 177511b41d2SMark Murray close(pin[1]); 178511b41d2SMark Murray close(pout[0]); 179511b41d2SMark Murray dup2(pin[0], 0); 180511b41d2SMark Murray dup2(pout[1], 1); 181511b41d2SMark Murray close(pin[0]); 182511b41d2SMark Murray close(pout[1]); 183511b41d2SMark Murray 184511b41d2SMark Murray i = 0; 185b66f2d16SKris Kennaway args[i++] = ssh_program; 186511b41d2SMark Murray args[i++] = "-x"; 187511b41d2SMark Murray args[i++] = "-oFallBackToRsh no"; 188511b41d2SMark Murray if (IPv4) 189511b41d2SMark Murray args[i++] = "-4"; 190511b41d2SMark Murray if (IPv6) 191511b41d2SMark Murray args[i++] = "-6"; 192511b41d2SMark Murray if (verbose_mode) 193511b41d2SMark Murray args[i++] = "-v"; 194511b41d2SMark Murray if (compress) 195511b41d2SMark Murray args[i++] = "-C"; 196511b41d2SMark Murray if (batchmode) 197511b41d2SMark Murray args[i++] = "-oBatchMode yes"; 198511b41d2SMark Murray if (cipher != NULL) { 199511b41d2SMark Murray args[i++] = "-c"; 200511b41d2SMark Murray args[i++] = cipher; 201511b41d2SMark Murray } 202511b41d2SMark Murray if (identity != NULL) { 203511b41d2SMark Murray args[i++] = "-i"; 204511b41d2SMark Murray args[i++] = identity; 205511b41d2SMark Murray } 206511b41d2SMark Murray if (port != NULL) { 207511b41d2SMark Murray args[i++] = "-p"; 208511b41d2SMark Murray args[i++] = port; 209511b41d2SMark Murray } 210511b41d2SMark Murray if (remuser != NULL) { 211511b41d2SMark Murray args[i++] = "-l"; 212511b41d2SMark Murray args[i++] = remuser; 213511b41d2SMark Murray } 214511b41d2SMark Murray args[i++] = host; 215511b41d2SMark Murray args[i++] = cmd; 216511b41d2SMark Murray args[i++] = NULL; 217511b41d2SMark Murray 218b66f2d16SKris Kennaway execvp(ssh_program, args); 219b66f2d16SKris Kennaway perror(ssh_program); 220511b41d2SMark Murray exit(1); 221511b41d2SMark Murray } 222511b41d2SMark Murray /* Parent. Close the other side, and return the local side. */ 223511b41d2SMark Murray close(pin[0]); 224511b41d2SMark Murray *fdout = pin[1]; 225511b41d2SMark Murray close(pout[1]); 226511b41d2SMark Murray *fdin = pout[0]; 227511b41d2SMark Murray return 0; 228511b41d2SMark Murray } 229511b41d2SMark Murray 230511b41d2SMark Murray void 231511b41d2SMark Murray fatal(const char *fmt,...) 232511b41d2SMark Murray { 233511b41d2SMark Murray va_list ap; 234511b41d2SMark Murray char buf[1024]; 235511b41d2SMark Murray 236511b41d2SMark Murray va_start(ap, fmt); 237511b41d2SMark Murray vsnprintf(buf, sizeof(buf), fmt, ap); 238511b41d2SMark Murray va_end(ap); 239511b41d2SMark Murray fprintf(stderr, "%s\n", buf); 240511b41d2SMark Murray exit(255); 241511b41d2SMark Murray } 242511b41d2SMark Murray 243511b41d2SMark Murray typedef struct { 244511b41d2SMark Murray int cnt; 245511b41d2SMark Murray char *buf; 246511b41d2SMark Murray } BUF; 247511b41d2SMark Murray 248511b41d2SMark Murray extern int iamremote; 249511b41d2SMark Murray 250511b41d2SMark Murray BUF *allocbuf(BUF *, int, int); 251511b41d2SMark Murray char *colon(char *); 252511b41d2SMark Murray void lostconn(int); 253511b41d2SMark Murray void nospace(void); 254511b41d2SMark Murray int okname(char *); 255511b41d2SMark Murray void run_err(const char *,...); 256511b41d2SMark Murray void verifydir(char *); 257511b41d2SMark Murray 258511b41d2SMark Murray struct passwd *pwd; 259511b41d2SMark Murray uid_t userid; 260511b41d2SMark Murray int errs, remin, remout; 261511b41d2SMark Murray int pflag, iamremote, iamrecursive, targetshouldbedirectory; 262511b41d2SMark Murray 263511b41d2SMark Murray #define CMDNEEDS 64 264511b41d2SMark Murray char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 265511b41d2SMark Murray 266511b41d2SMark Murray int response(void); 267511b41d2SMark Murray void rsource(char *, struct stat *); 268511b41d2SMark Murray void sink(int, char *[]); 269511b41d2SMark Murray void source(int, char *[]); 270511b41d2SMark Murray void tolocal(int, char *[]); 271511b41d2SMark Murray void toremote(char *, int, char *[]); 272511b41d2SMark Murray void usage(void); 273511b41d2SMark Murray 274511b41d2SMark Murray int 275511b41d2SMark Murray main(argc, argv) 276511b41d2SMark Murray int argc; 277511b41d2SMark Murray char *argv[]; 278511b41d2SMark Murray { 279511b41d2SMark Murray int ch, fflag, tflag; 280511b41d2SMark Murray char *targ; 281511b41d2SMark Murray extern char *optarg; 282511b41d2SMark Murray extern int optind; 283511b41d2SMark Murray 284511b41d2SMark Murray fflag = tflag = 0; 285b66f2d16SKris Kennaway while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:")) != EOF) 286511b41d2SMark Murray switch (ch) { 287511b41d2SMark Murray /* User-visible flags. */ 288511b41d2SMark Murray case '4': 289511b41d2SMark Murray IPv4 = 1; 290511b41d2SMark Murray break; 291511b41d2SMark Murray case '6': 292511b41d2SMark Murray IPv6 = 1; 293511b41d2SMark Murray break; 294511b41d2SMark Murray case 'p': 295511b41d2SMark Murray pflag = 1; 296511b41d2SMark Murray break; 297511b41d2SMark Murray case 'P': 298511b41d2SMark Murray port = optarg; 299511b41d2SMark Murray break; 300511b41d2SMark Murray case 'r': 301511b41d2SMark Murray iamrecursive = 1; 302511b41d2SMark Murray break; 303b66f2d16SKris Kennaway case 'S': 304b66f2d16SKris Kennaway ssh_program = optarg; 305b66f2d16SKris Kennaway break; 306b66f2d16SKris Kennaway 307511b41d2SMark Murray /* Server options. */ 308511b41d2SMark Murray case 'd': 309511b41d2SMark Murray targetshouldbedirectory = 1; 310511b41d2SMark Murray break; 311511b41d2SMark Murray case 'f': /* "from" */ 312511b41d2SMark Murray iamremote = 1; 313511b41d2SMark Murray fflag = 1; 314511b41d2SMark Murray break; 315511b41d2SMark Murray case 't': /* "to" */ 316511b41d2SMark Murray iamremote = 1; 317511b41d2SMark Murray tflag = 1; 318511b41d2SMark Murray break; 319511b41d2SMark Murray case 'c': 320511b41d2SMark Murray cipher = optarg; 321511b41d2SMark Murray break; 322511b41d2SMark Murray case 'i': 323511b41d2SMark Murray identity = optarg; 324511b41d2SMark Murray break; 325511b41d2SMark Murray case 'v': 326511b41d2SMark Murray verbose_mode = 1; 327511b41d2SMark Murray break; 328511b41d2SMark Murray case 'B': 329511b41d2SMark Murray batchmode = 1; 330511b41d2SMark Murray break; 331511b41d2SMark Murray case 'C': 332511b41d2SMark Murray compress = 1; 333511b41d2SMark Murray break; 334511b41d2SMark Murray case 'q': 335511b41d2SMark Murray showprogress = 0; 336511b41d2SMark Murray break; 337511b41d2SMark Murray case '?': 338511b41d2SMark Murray default: 339511b41d2SMark Murray usage(); 340511b41d2SMark Murray } 341511b41d2SMark Murray argc -= optind; 342511b41d2SMark Murray argv += optind; 343511b41d2SMark Murray 344511b41d2SMark Murray if ((pwd = getpwuid(userid = getuid())) == NULL) 345511b41d2SMark Murray fatal("unknown user %d", (int) userid); 346511b41d2SMark Murray 347511b41d2SMark Murray if (!isatty(STDERR_FILENO)) 348511b41d2SMark Murray showprogress = 0; 349511b41d2SMark Murray 350511b41d2SMark Murray remin = STDIN_FILENO; 351511b41d2SMark Murray remout = STDOUT_FILENO; 352511b41d2SMark Murray 353511b41d2SMark Murray if (fflag) { 354511b41d2SMark Murray /* Follow "protocol", send data. */ 355511b41d2SMark Murray (void) response(); 356511b41d2SMark Murray source(argc, argv); 357511b41d2SMark Murray exit(errs != 0); 358511b41d2SMark Murray } 359511b41d2SMark Murray if (tflag) { 360511b41d2SMark Murray /* Receive data. */ 361511b41d2SMark Murray sink(argc, argv); 362511b41d2SMark Murray exit(errs != 0); 363511b41d2SMark Murray } 364511b41d2SMark Murray if (argc < 2) 365511b41d2SMark Murray usage(); 366511b41d2SMark Murray if (argc > 2) 367511b41d2SMark Murray targetshouldbedirectory = 1; 368511b41d2SMark Murray 369511b41d2SMark Murray remin = remout = -1; 370511b41d2SMark Murray /* Command to be executed on remote system using "ssh". */ 371511b41d2SMark Murray (void) sprintf(cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", 372511b41d2SMark Murray iamrecursive ? " -r" : "", pflag ? " -p" : "", 373511b41d2SMark Murray targetshouldbedirectory ? " -d" : ""); 374511b41d2SMark Murray 375511b41d2SMark Murray (void) signal(SIGPIPE, lostconn); 376511b41d2SMark Murray 377511b41d2SMark Murray if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 378511b41d2SMark Murray toremote(targ, argc, argv); 379511b41d2SMark Murray else { 380511b41d2SMark Murray tolocal(argc, argv); /* Dest is local host. */ 381511b41d2SMark Murray if (targetshouldbedirectory) 382511b41d2SMark Murray verifydir(argv[argc - 1]); 383511b41d2SMark Murray } 384511b41d2SMark Murray exit(errs != 0); 385511b41d2SMark Murray } 386511b41d2SMark Murray 387511b41d2SMark Murray char * 388511b41d2SMark Murray cleanhostname(host) 389511b41d2SMark Murray char *host; 390511b41d2SMark Murray { 391511b41d2SMark Murray if (*host == '[' && host[strlen(host) - 1] == ']') { 392511b41d2SMark Murray host[strlen(host) - 1] = '\0'; 393511b41d2SMark Murray return (host + 1); 394511b41d2SMark Murray } else 395511b41d2SMark Murray return host; 396511b41d2SMark Murray } 397511b41d2SMark Murray 398511b41d2SMark Murray void 399511b41d2SMark Murray toremote(targ, argc, argv) 400511b41d2SMark Murray char *targ, *argv[]; 401511b41d2SMark Murray int argc; 402511b41d2SMark Murray { 403511b41d2SMark Murray int i, len; 404511b41d2SMark Murray char *bp, *host, *src, *suser, *thost, *tuser; 405511b41d2SMark Murray 406511b41d2SMark Murray *targ++ = 0; 407511b41d2SMark Murray if (*targ == 0) 408511b41d2SMark Murray targ = "."; 409511b41d2SMark Murray 410511b41d2SMark Murray if ((thost = strchr(argv[argc - 1], '@'))) { 411511b41d2SMark Murray /* user@host */ 412511b41d2SMark Murray *thost++ = 0; 413511b41d2SMark Murray tuser = argv[argc - 1]; 414511b41d2SMark Murray if (*tuser == '\0') 415511b41d2SMark Murray tuser = NULL; 416511b41d2SMark Murray else if (!okname(tuser)) 417511b41d2SMark Murray exit(1); 418511b41d2SMark Murray } else { 419511b41d2SMark Murray thost = argv[argc - 1]; 420511b41d2SMark Murray tuser = NULL; 421511b41d2SMark Murray } 422511b41d2SMark Murray 423511b41d2SMark Murray for (i = 0; i < argc - 1; i++) { 424511b41d2SMark Murray src = colon(argv[i]); 425511b41d2SMark Murray if (src) { /* remote to remote */ 426511b41d2SMark Murray *src++ = 0; 427511b41d2SMark Murray if (*src == 0) 428511b41d2SMark Murray src = "."; 429511b41d2SMark Murray host = strchr(argv[i], '@'); 430b66f2d16SKris Kennaway len = strlen(ssh_program) + strlen(argv[i]) + 431511b41d2SMark Murray strlen(src) + (tuser ? strlen(tuser) : 0) + 432511b41d2SMark Murray strlen(thost) + strlen(targ) + CMDNEEDS + 32; 433511b41d2SMark Murray bp = xmalloc(len); 434511b41d2SMark Murray if (host) { 435511b41d2SMark Murray *host++ = 0; 436511b41d2SMark Murray host = cleanhostname(host); 437511b41d2SMark Murray suser = argv[i]; 438511b41d2SMark Murray if (*suser == '\0') 439511b41d2SMark Murray suser = pwd->pw_name; 440511b41d2SMark Murray else if (!okname(suser)) 441511b41d2SMark Murray continue; 442511b41d2SMark Murray (void) sprintf(bp, 443511b41d2SMark Murray "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'", 444b66f2d16SKris Kennaway ssh_program, verbose_mode ? " -v" : "", 445511b41d2SMark Murray suser, host, cmd, src, 446511b41d2SMark Murray tuser ? tuser : "", tuser ? "@" : "", 447511b41d2SMark Murray thost, targ); 448511b41d2SMark Murray } else { 449511b41d2SMark Murray host = cleanhostname(argv[i]); 450511b41d2SMark Murray (void) sprintf(bp, 451511b41d2SMark Murray "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'", 452b66f2d16SKris Kennaway ssh_program, verbose_mode ? " -v" : "", 453511b41d2SMark Murray host, cmd, src, 454511b41d2SMark Murray tuser ? tuser : "", tuser ? "@" : "", 455511b41d2SMark Murray thost, targ); 456511b41d2SMark Murray } 457511b41d2SMark Murray if (verbose_mode) 458511b41d2SMark Murray fprintf(stderr, "Executing: %s\n", bp); 459511b41d2SMark Murray (void) system(bp); 460511b41d2SMark Murray (void) xfree(bp); 461511b41d2SMark Murray } else { /* local to remote */ 462511b41d2SMark Murray if (remin == -1) { 463511b41d2SMark Murray len = strlen(targ) + CMDNEEDS + 20; 464511b41d2SMark Murray bp = xmalloc(len); 465511b41d2SMark Murray (void) sprintf(bp, "%s -t %s", cmd, targ); 466511b41d2SMark Murray host = cleanhostname(thost); 467b66f2d16SKris Kennaway if (do_cmd(host, tuser, bp, &remin, 468b66f2d16SKris Kennaway &remout, argc) < 0) 469511b41d2SMark Murray exit(1); 470511b41d2SMark Murray if (response() < 0) 471511b41d2SMark Murray exit(1); 472511b41d2SMark Murray (void) xfree(bp); 473511b41d2SMark Murray } 474511b41d2SMark Murray source(1, argv + i); 475511b41d2SMark Murray } 476511b41d2SMark Murray } 477511b41d2SMark Murray } 478511b41d2SMark Murray 479511b41d2SMark Murray void 480511b41d2SMark Murray tolocal(argc, argv) 481511b41d2SMark Murray int argc; 482511b41d2SMark Murray char *argv[]; 483511b41d2SMark Murray { 484511b41d2SMark Murray int i, len; 485511b41d2SMark Murray char *bp, *host, *src, *suser; 486511b41d2SMark Murray 487511b41d2SMark Murray for (i = 0; i < argc - 1; i++) { 488511b41d2SMark Murray if (!(src = colon(argv[i]))) { /* Local to local. */ 489511b41d2SMark Murray len = strlen(_PATH_CP) + strlen(argv[i]) + 490511b41d2SMark Murray strlen(argv[argc - 1]) + 20; 491511b41d2SMark Murray bp = xmalloc(len); 492511b41d2SMark Murray (void) sprintf(bp, "exec %s%s%s %s %s", _PATH_CP, 493511b41d2SMark Murray iamrecursive ? " -r" : "", pflag ? " -p" : "", 494511b41d2SMark Murray argv[i], argv[argc - 1]); 495511b41d2SMark Murray if (verbose_mode) 496511b41d2SMark Murray fprintf(stderr, "Executing: %s\n", bp); 497511b41d2SMark Murray if (system(bp)) 498511b41d2SMark Murray ++errs; 499511b41d2SMark Murray (void) xfree(bp); 500511b41d2SMark Murray continue; 501511b41d2SMark Murray } 502511b41d2SMark Murray *src++ = 0; 503511b41d2SMark Murray if (*src == 0) 504511b41d2SMark Murray src = "."; 505511b41d2SMark Murray if ((host = strchr(argv[i], '@')) == NULL) { 506511b41d2SMark Murray host = argv[i]; 507511b41d2SMark Murray suser = NULL; 508511b41d2SMark Murray } else { 509511b41d2SMark Murray *host++ = 0; 510511b41d2SMark Murray suser = argv[i]; 511511b41d2SMark Murray if (*suser == '\0') 512511b41d2SMark Murray suser = pwd->pw_name; 513511b41d2SMark Murray else if (!okname(suser)) 514511b41d2SMark Murray continue; 515511b41d2SMark Murray } 516511b41d2SMark Murray host = cleanhostname(host); 517511b41d2SMark Murray len = strlen(src) + CMDNEEDS + 20; 518511b41d2SMark Murray bp = xmalloc(len); 519511b41d2SMark Murray (void) sprintf(bp, "%s -f %s", cmd, src); 520b66f2d16SKris Kennaway if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) { 521511b41d2SMark Murray (void) xfree(bp); 522511b41d2SMark Murray ++errs; 523511b41d2SMark Murray continue; 524511b41d2SMark Murray } 525511b41d2SMark Murray xfree(bp); 526511b41d2SMark Murray sink(1, argv + argc - 1); 527511b41d2SMark Murray (void) close(remin); 528511b41d2SMark Murray remin = remout = -1; 529511b41d2SMark Murray } 530511b41d2SMark Murray } 531511b41d2SMark Murray 532511b41d2SMark Murray void 533511b41d2SMark Murray source(argc, argv) 534511b41d2SMark Murray int argc; 535511b41d2SMark Murray char *argv[]; 536511b41d2SMark Murray { 537511b41d2SMark Murray struct stat stb; 538511b41d2SMark Murray static BUF buffer; 539511b41d2SMark Murray BUF *bp; 540511b41d2SMark Murray off_t i; 541511b41d2SMark Murray int amt, fd, haderr, indx, result; 542511b41d2SMark Murray char *last, *name, buf[2048]; 543511b41d2SMark Murray 544511b41d2SMark Murray for (indx = 0; indx < argc; ++indx) { 545511b41d2SMark Murray name = argv[indx]; 546511b41d2SMark Murray statbytes = 0; 547511b41d2SMark Murray if ((fd = open(name, O_RDONLY, 0)) < 0) 548511b41d2SMark Murray goto syserr; 549511b41d2SMark Murray if (fstat(fd, &stb) < 0) { 550511b41d2SMark Murray syserr: run_err("%s: %s", name, strerror(errno)); 551511b41d2SMark Murray goto next; 552511b41d2SMark Murray } 553511b41d2SMark Murray switch (stb.st_mode & S_IFMT) { 554511b41d2SMark Murray case S_IFREG: 555511b41d2SMark Murray break; 556511b41d2SMark Murray case S_IFDIR: 557511b41d2SMark Murray if (iamrecursive) { 558511b41d2SMark Murray rsource(name, &stb); 559511b41d2SMark Murray goto next; 560511b41d2SMark Murray } 561511b41d2SMark Murray /* FALLTHROUGH */ 562511b41d2SMark Murray default: 563511b41d2SMark Murray run_err("%s: not a regular file", name); 564511b41d2SMark Murray goto next; 565511b41d2SMark Murray } 566511b41d2SMark Murray if ((last = strrchr(name, '/')) == NULL) 567511b41d2SMark Murray last = name; 568511b41d2SMark Murray else 569511b41d2SMark Murray ++last; 570511b41d2SMark Murray curfile = last; 571511b41d2SMark Murray if (pflag) { 572511b41d2SMark Murray /* 573511b41d2SMark Murray * Make it compatible with possible future 574511b41d2SMark Murray * versions expecting microseconds. 575511b41d2SMark Murray */ 576511b41d2SMark Murray (void) sprintf(buf, "T%lu 0 %lu 0\n", 577511b41d2SMark Murray (unsigned long) stb.st_mtime, 578511b41d2SMark Murray (unsigned long) stb.st_atime); 579a04a10f8SKris Kennaway (void) atomicio(write, remout, buf, strlen(buf)); 580511b41d2SMark Murray if (response() < 0) 581511b41d2SMark Murray goto next; 582511b41d2SMark Murray } 583511b41d2SMark Murray #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 584511b41d2SMark Murray (void) sprintf(buf, "C%04o %lu %s\n", 585511b41d2SMark Murray (unsigned int) (stb.st_mode & FILEMODEMASK), 586511b41d2SMark Murray (unsigned long) stb.st_size, 587511b41d2SMark Murray last); 588511b41d2SMark Murray if (verbose_mode) { 589511b41d2SMark Murray fprintf(stderr, "Sending file modes: %s", buf); 590511b41d2SMark Murray fflush(stderr); 591511b41d2SMark Murray } 592a04a10f8SKris Kennaway (void) atomicio(write, remout, buf, strlen(buf)); 593511b41d2SMark Murray if (response() < 0) 594511b41d2SMark Murray goto next; 595511b41d2SMark Murray if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { 596511b41d2SMark Murray next: (void) close(fd); 597511b41d2SMark Murray continue; 598511b41d2SMark Murray } 599511b41d2SMark Murray if (showprogress) { 600511b41d2SMark Murray totalbytes = stb.st_size; 601511b41d2SMark Murray progressmeter(-1); 602511b41d2SMark Murray } 603511b41d2SMark Murray /* Keep writing after an error so that we stay sync'd up. */ 604511b41d2SMark Murray for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 605511b41d2SMark Murray amt = bp->cnt; 606511b41d2SMark Murray if (i + amt > stb.st_size) 607511b41d2SMark Murray amt = stb.st_size - i; 608511b41d2SMark Murray if (!haderr) { 609a04a10f8SKris Kennaway result = atomicio(read, fd, bp->buf, amt); 610511b41d2SMark Murray if (result != amt) 611511b41d2SMark Murray haderr = result >= 0 ? EIO : errno; 612511b41d2SMark Murray } 613511b41d2SMark Murray if (haderr) 614a04a10f8SKris Kennaway (void) atomicio(write, remout, bp->buf, amt); 615511b41d2SMark Murray else { 616a04a10f8SKris Kennaway result = atomicio(write, remout, bp->buf, amt); 617511b41d2SMark Murray if (result != amt) 618511b41d2SMark Murray haderr = result >= 0 ? EIO : errno; 619511b41d2SMark Murray statbytes += result; 620511b41d2SMark Murray } 621511b41d2SMark Murray } 622511b41d2SMark Murray if (showprogress) 623511b41d2SMark Murray progressmeter(1); 624511b41d2SMark Murray 625511b41d2SMark Murray if (close(fd) < 0 && !haderr) 626511b41d2SMark Murray haderr = errno; 627511b41d2SMark Murray if (!haderr) 628a04a10f8SKris Kennaway (void) atomicio(write, remout, "", 1); 629511b41d2SMark Murray else 630511b41d2SMark Murray run_err("%s: %s", name, strerror(haderr)); 631511b41d2SMark Murray (void) response(); 632511b41d2SMark Murray } 633511b41d2SMark Murray } 634511b41d2SMark Murray 635511b41d2SMark Murray void 636511b41d2SMark Murray rsource(name, statp) 637511b41d2SMark Murray char *name; 638511b41d2SMark Murray struct stat *statp; 639511b41d2SMark Murray { 640511b41d2SMark Murray DIR *dirp; 641511b41d2SMark Murray struct dirent *dp; 642511b41d2SMark Murray char *last, *vect[1], path[1100]; 643511b41d2SMark Murray 644511b41d2SMark Murray if (!(dirp = opendir(name))) { 645511b41d2SMark Murray run_err("%s: %s", name, strerror(errno)); 646511b41d2SMark Murray return; 647511b41d2SMark Murray } 648511b41d2SMark Murray last = strrchr(name, '/'); 649511b41d2SMark Murray if (last == 0) 650511b41d2SMark Murray last = name; 651511b41d2SMark Murray else 652511b41d2SMark Murray last++; 653511b41d2SMark Murray if (pflag) { 654511b41d2SMark Murray (void) sprintf(path, "T%lu 0 %lu 0\n", 655511b41d2SMark Murray (unsigned long) statp->st_mtime, 656511b41d2SMark Murray (unsigned long) statp->st_atime); 657a04a10f8SKris Kennaway (void) atomicio(write, remout, path, strlen(path)); 658511b41d2SMark Murray if (response() < 0) { 659511b41d2SMark Murray closedir(dirp); 660511b41d2SMark Murray return; 661511b41d2SMark Murray } 662511b41d2SMark Murray } 663511b41d2SMark Murray (void) sprintf(path, "D%04o %d %.1024s\n", 664b66f2d16SKris Kennaway (unsigned int) (statp->st_mode & FILEMODEMASK), 0, last); 665511b41d2SMark Murray if (verbose_mode) 666511b41d2SMark Murray fprintf(stderr, "Entering directory: %s", path); 667a04a10f8SKris Kennaway (void) atomicio(write, remout, path, strlen(path)); 668511b41d2SMark Murray if (response() < 0) { 669511b41d2SMark Murray closedir(dirp); 670511b41d2SMark Murray return; 671511b41d2SMark Murray } 672511b41d2SMark Murray while ((dp = readdir(dirp))) { 673511b41d2SMark Murray if (dp->d_ino == 0) 674511b41d2SMark Murray continue; 675511b41d2SMark Murray if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 676511b41d2SMark Murray continue; 677511b41d2SMark Murray if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { 678511b41d2SMark Murray run_err("%s/%s: name too long", name, dp->d_name); 679511b41d2SMark Murray continue; 680511b41d2SMark Murray } 681511b41d2SMark Murray (void) sprintf(path, "%s/%s", name, dp->d_name); 682511b41d2SMark Murray vect[0] = path; 683511b41d2SMark Murray source(1, vect); 684511b41d2SMark Murray } 685511b41d2SMark Murray (void) closedir(dirp); 686a04a10f8SKris Kennaway (void) atomicio(write, remout, "E\n", 2); 687511b41d2SMark Murray (void) response(); 688511b41d2SMark Murray } 689511b41d2SMark Murray 690511b41d2SMark Murray void 691511b41d2SMark Murray sink(argc, argv) 692511b41d2SMark Murray int argc; 693511b41d2SMark Murray char *argv[]; 694511b41d2SMark Murray { 695511b41d2SMark Murray static BUF buffer; 696511b41d2SMark Murray struct stat stb; 697511b41d2SMark Murray enum { 698511b41d2SMark Murray YES, NO, DISPLAYED 699511b41d2SMark Murray } wrerr; 700511b41d2SMark Murray BUF *bp; 701511b41d2SMark Murray off_t i, j; 702511b41d2SMark Murray int amt, count, exists, first, mask, mode, ofd, omode; 703b66f2d16SKris Kennaway off_t size; 704b66f2d16SKris Kennaway int setimes, targisdir, wrerrno = 0; 705511b41d2SMark Murray char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; 706511b41d2SMark Murray struct utimbuf ut; 707511b41d2SMark Murray int dummy_usec; 708511b41d2SMark Murray 709511b41d2SMark Murray #define SCREWUP(str) { why = str; goto screwup; } 710511b41d2SMark Murray 711511b41d2SMark Murray setimes = targisdir = 0; 712511b41d2SMark Murray mask = umask(0); 713511b41d2SMark Murray if (!pflag) 714511b41d2SMark Murray (void) umask(mask); 715511b41d2SMark Murray if (argc != 1) { 716511b41d2SMark Murray run_err("ambiguous target"); 717511b41d2SMark Murray exit(1); 718511b41d2SMark Murray } 719511b41d2SMark Murray targ = *argv; 720511b41d2SMark Murray if (targetshouldbedirectory) 721511b41d2SMark Murray verifydir(targ); 722511b41d2SMark Murray 723a04a10f8SKris Kennaway (void) atomicio(write, remout, "", 1); 724511b41d2SMark Murray if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 725511b41d2SMark Murray targisdir = 1; 726511b41d2SMark Murray for (first = 1;; first = 0) { 727511b41d2SMark Murray cp = buf; 728a04a10f8SKris Kennaway if (atomicio(read, remin, cp, 1) <= 0) 729511b41d2SMark Murray return; 730511b41d2SMark Murray if (*cp++ == '\n') 731511b41d2SMark Murray SCREWUP("unexpected <newline>"); 732511b41d2SMark Murray do { 733a04a10f8SKris Kennaway if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 734511b41d2SMark Murray SCREWUP("lost connection"); 735511b41d2SMark Murray *cp++ = ch; 736511b41d2SMark Murray } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 737511b41d2SMark Murray *cp = 0; 738511b41d2SMark Murray 739511b41d2SMark Murray if (buf[0] == '\01' || buf[0] == '\02') { 740511b41d2SMark Murray if (iamremote == 0) 741a04a10f8SKris Kennaway (void) atomicio(write, STDERR_FILENO, 742511b41d2SMark Murray buf + 1, strlen(buf + 1)); 743511b41d2SMark Murray if (buf[0] == '\02') 744511b41d2SMark Murray exit(1); 745511b41d2SMark Murray ++errs; 746511b41d2SMark Murray continue; 747511b41d2SMark Murray } 748511b41d2SMark Murray if (buf[0] == 'E') { 749a04a10f8SKris Kennaway (void) atomicio(write, remout, "", 1); 750511b41d2SMark Murray return; 751511b41d2SMark Murray } 752511b41d2SMark Murray if (ch == '\n') 753511b41d2SMark Murray *--cp = 0; 754511b41d2SMark Murray 755511b41d2SMark Murray #define getnum(t) (t) = 0; \ 756511b41d2SMark Murray while (*cp >= '0' && *cp <= '9') (t) = (t) * 10 + (*cp++ - '0'); 757511b41d2SMark Murray cp = buf; 758511b41d2SMark Murray if (*cp == 'T') { 759511b41d2SMark Murray setimes++; 760511b41d2SMark Murray cp++; 761511b41d2SMark Murray getnum(ut.modtime); 762511b41d2SMark Murray if (*cp++ != ' ') 763511b41d2SMark Murray SCREWUP("mtime.sec not delimited"); 764511b41d2SMark Murray getnum(dummy_usec); 765511b41d2SMark Murray if (*cp++ != ' ') 766511b41d2SMark Murray SCREWUP("mtime.usec not delimited"); 767511b41d2SMark Murray getnum(ut.actime); 768511b41d2SMark Murray if (*cp++ != ' ') 769511b41d2SMark Murray SCREWUP("atime.sec not delimited"); 770511b41d2SMark Murray getnum(dummy_usec); 771511b41d2SMark Murray if (*cp++ != '\0') 772511b41d2SMark Murray SCREWUP("atime.usec not delimited"); 773a04a10f8SKris Kennaway (void) atomicio(write, remout, "", 1); 774511b41d2SMark Murray continue; 775511b41d2SMark Murray } 776511b41d2SMark Murray if (*cp != 'C' && *cp != 'D') { 777511b41d2SMark Murray /* 778511b41d2SMark Murray * Check for the case "rcp remote:foo\* local:bar". 779511b41d2SMark Murray * In this case, the line "No match." can be returned 780511b41d2SMark Murray * by the shell before the rcp command on the remote is 781511b41d2SMark Murray * executed so the ^Aerror_message convention isn't 782511b41d2SMark Murray * followed. 783511b41d2SMark Murray */ 784511b41d2SMark Murray if (first) { 785511b41d2SMark Murray run_err("%s", cp); 786511b41d2SMark Murray exit(1); 787511b41d2SMark Murray } 788511b41d2SMark Murray SCREWUP("expected control record"); 789511b41d2SMark Murray } 790511b41d2SMark Murray mode = 0; 791511b41d2SMark Murray for (++cp; cp < buf + 5; cp++) { 792511b41d2SMark Murray if (*cp < '0' || *cp > '7') 793511b41d2SMark Murray SCREWUP("bad mode"); 794511b41d2SMark Murray mode = (mode << 3) | (*cp - '0'); 795511b41d2SMark Murray } 796511b41d2SMark Murray if (*cp++ != ' ') 797511b41d2SMark Murray SCREWUP("mode not delimited"); 798511b41d2SMark Murray 799511b41d2SMark Murray for (size = 0; *cp >= '0' && *cp <= '9';) 800511b41d2SMark Murray size = size * 10 + (*cp++ - '0'); 801511b41d2SMark Murray if (*cp++ != ' ') 802511b41d2SMark Murray SCREWUP("size not delimited"); 803511b41d2SMark Murray if (targisdir) { 804511b41d2SMark Murray static char *namebuf; 805511b41d2SMark Murray static int cursize; 806511b41d2SMark Murray size_t need; 807511b41d2SMark Murray 808511b41d2SMark Murray need = strlen(targ) + strlen(cp) + 250; 809511b41d2SMark Murray if (need > cursize) 810511b41d2SMark Murray namebuf = xmalloc(need); 811511b41d2SMark Murray (void) sprintf(namebuf, "%s%s%s", targ, 812511b41d2SMark Murray *targ ? "/" : "", cp); 813511b41d2SMark Murray np = namebuf; 814511b41d2SMark Murray } else 815511b41d2SMark Murray np = targ; 816511b41d2SMark Murray curfile = cp; 817511b41d2SMark Murray exists = stat(np, &stb) == 0; 818511b41d2SMark Murray if (buf[0] == 'D') { 819511b41d2SMark Murray int mod_flag = pflag; 820511b41d2SMark Murray if (exists) { 821511b41d2SMark Murray if (!S_ISDIR(stb.st_mode)) { 822511b41d2SMark Murray errno = ENOTDIR; 823511b41d2SMark Murray goto bad; 824511b41d2SMark Murray } 825511b41d2SMark Murray if (pflag) 826511b41d2SMark Murray (void) chmod(np, mode); 827511b41d2SMark Murray } else { 828511b41d2SMark Murray /* Handle copying from a read-only 829511b41d2SMark Murray directory */ 830511b41d2SMark Murray mod_flag = 1; 831511b41d2SMark Murray if (mkdir(np, mode | S_IRWXU) < 0) 832511b41d2SMark Murray goto bad; 833511b41d2SMark Murray } 834511b41d2SMark Murray vect[0] = np; 835511b41d2SMark Murray sink(1, vect); 836511b41d2SMark Murray if (setimes) { 837511b41d2SMark Murray setimes = 0; 838511b41d2SMark Murray if (utime(np, &ut) < 0) 839511b41d2SMark Murray run_err("%s: set times: %s", 840511b41d2SMark Murray np, strerror(errno)); 841511b41d2SMark Murray } 842511b41d2SMark Murray if (mod_flag) 843511b41d2SMark Murray (void) chmod(np, mode); 844511b41d2SMark Murray continue; 845511b41d2SMark Murray } 846511b41d2SMark Murray omode = mode; 847511b41d2SMark Murray mode |= S_IWRITE; 848511b41d2SMark Murray if ((ofd = open(np, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) { 849511b41d2SMark Murray bad: run_err("%s: %s", np, strerror(errno)); 850511b41d2SMark Murray continue; 851511b41d2SMark Murray } 852a04a10f8SKris Kennaway (void) atomicio(write, remout, "", 1); 853511b41d2SMark Murray if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { 854511b41d2SMark Murray (void) close(ofd); 855511b41d2SMark Murray continue; 856511b41d2SMark Murray } 857511b41d2SMark Murray cp = bp->buf; 858511b41d2SMark Murray wrerr = NO; 859511b41d2SMark Murray 860511b41d2SMark Murray if (showprogress) { 861511b41d2SMark Murray totalbytes = size; 862511b41d2SMark Murray progressmeter(-1); 863511b41d2SMark Murray } 864511b41d2SMark Murray statbytes = 0; 865511b41d2SMark Murray for (count = i = 0; i < size; i += 4096) { 866511b41d2SMark Murray amt = 4096; 867511b41d2SMark Murray if (i + amt > size) 868511b41d2SMark Murray amt = size - i; 869511b41d2SMark Murray count += amt; 870511b41d2SMark Murray do { 871a04a10f8SKris Kennaway j = atomicio(read, remin, cp, amt); 872511b41d2SMark Murray if (j <= 0) { 873511b41d2SMark Murray run_err("%s", j ? strerror(errno) : 874511b41d2SMark Murray "dropped connection"); 875511b41d2SMark Murray exit(1); 876511b41d2SMark Murray } 877511b41d2SMark Murray amt -= j; 878511b41d2SMark Murray cp += j; 879511b41d2SMark Murray statbytes += j; 880511b41d2SMark Murray } while (amt > 0); 881511b41d2SMark Murray if (count == bp->cnt) { 882511b41d2SMark Murray /* Keep reading so we stay sync'd up. */ 883511b41d2SMark Murray if (wrerr == NO) { 884a04a10f8SKris Kennaway j = atomicio(write, ofd, bp->buf, count); 885511b41d2SMark Murray if (j != count) { 886511b41d2SMark Murray wrerr = YES; 887511b41d2SMark Murray wrerrno = j >= 0 ? EIO : errno; 888511b41d2SMark Murray } 889511b41d2SMark Murray } 890511b41d2SMark Murray count = 0; 891511b41d2SMark Murray cp = bp->buf; 892511b41d2SMark Murray } 893511b41d2SMark Murray } 894511b41d2SMark Murray if (showprogress) 895511b41d2SMark Murray progressmeter(1); 896511b41d2SMark Murray if (count != 0 && wrerr == NO && 897a04a10f8SKris Kennaway (j = atomicio(write, ofd, bp->buf, count)) != count) { 898511b41d2SMark Murray wrerr = YES; 899511b41d2SMark Murray wrerrno = j >= 0 ? EIO : errno; 900511b41d2SMark Murray } 901511b41d2SMark Murray #if 0 902511b41d2SMark Murray if (ftruncate(ofd, size)) { 903511b41d2SMark Murray run_err("%s: truncate: %s", np, strerror(errno)); 904511b41d2SMark Murray wrerr = DISPLAYED; 905511b41d2SMark Murray } 906511b41d2SMark Murray #endif 907511b41d2SMark Murray if (pflag) { 908511b41d2SMark Murray if (exists || omode != mode) 909511b41d2SMark Murray if (fchmod(ofd, omode)) 910511b41d2SMark Murray run_err("%s: set mode: %s", 911511b41d2SMark Murray np, strerror(errno)); 912511b41d2SMark Murray } else { 913511b41d2SMark Murray if (!exists && omode != mode) 914511b41d2SMark Murray if (fchmod(ofd, omode & ~mask)) 915511b41d2SMark Murray run_err("%s: set mode: %s", 916511b41d2SMark Murray np, strerror(errno)); 917511b41d2SMark Murray } 918b66f2d16SKris Kennaway if (close(ofd) == -1) { 919b66f2d16SKris Kennaway wrerr = YES; 920b66f2d16SKris Kennaway wrerrno = errno; 921b66f2d16SKris Kennaway } 922511b41d2SMark Murray (void) response(); 923511b41d2SMark Murray if (setimes && wrerr == NO) { 924511b41d2SMark Murray setimes = 0; 925511b41d2SMark Murray if (utime(np, &ut) < 0) { 926511b41d2SMark Murray run_err("%s: set times: %s", 927511b41d2SMark Murray np, strerror(errno)); 928511b41d2SMark Murray wrerr = DISPLAYED; 929511b41d2SMark Murray } 930511b41d2SMark Murray } 931511b41d2SMark Murray switch (wrerr) { 932511b41d2SMark Murray case YES: 933511b41d2SMark Murray run_err("%s: %s", np, strerror(wrerrno)); 934511b41d2SMark Murray break; 935511b41d2SMark Murray case NO: 936a04a10f8SKris Kennaway (void) atomicio(write, remout, "", 1); 937511b41d2SMark Murray break; 938511b41d2SMark Murray case DISPLAYED: 939511b41d2SMark Murray break; 940511b41d2SMark Murray } 941511b41d2SMark Murray } 942511b41d2SMark Murray screwup: 943511b41d2SMark Murray run_err("protocol error: %s", why); 944511b41d2SMark Murray exit(1); 945511b41d2SMark Murray } 946511b41d2SMark Murray 947511b41d2SMark Murray int 948511b41d2SMark Murray response() 949511b41d2SMark Murray { 950511b41d2SMark Murray char ch, *cp, resp, rbuf[2048]; 951511b41d2SMark Murray 952a04a10f8SKris Kennaway if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) 953511b41d2SMark Murray lostconn(0); 954511b41d2SMark Murray 955511b41d2SMark Murray cp = rbuf; 956511b41d2SMark Murray switch (resp) { 957511b41d2SMark Murray case 0: /* ok */ 958511b41d2SMark Murray return (0); 959511b41d2SMark Murray default: 960511b41d2SMark Murray *cp++ = resp; 961511b41d2SMark Murray /* FALLTHROUGH */ 962511b41d2SMark Murray case 1: /* error, followed by error msg */ 963511b41d2SMark Murray case 2: /* fatal error, "" */ 964511b41d2SMark Murray do { 965a04a10f8SKris Kennaway if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 966511b41d2SMark Murray lostconn(0); 967511b41d2SMark Murray *cp++ = ch; 968511b41d2SMark Murray } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 969511b41d2SMark Murray 970511b41d2SMark Murray if (!iamremote) 971a04a10f8SKris Kennaway (void) atomicio(write, STDERR_FILENO, rbuf, cp - rbuf); 972511b41d2SMark Murray ++errs; 973511b41d2SMark Murray if (resp == 1) 974511b41d2SMark Murray return (-1); 975511b41d2SMark Murray exit(1); 976511b41d2SMark Murray } 977511b41d2SMark Murray /* NOTREACHED */ 978511b41d2SMark Murray } 979511b41d2SMark Murray 980511b41d2SMark Murray void 981511b41d2SMark Murray usage() 982511b41d2SMark Murray { 983b66f2d16SKris Kennaway (void) fprintf(stderr, "usage: scp " 984b66f2d16SKris Kennaway "[-pqrvC46] [-S ssh] [-P port] [-c cipher] [-i identity] f1 f2; or:\n" 985b66f2d16SKris Kennaway " scp [options] f1 ... fn directory\n"); 986511b41d2SMark Murray exit(1); 987511b41d2SMark Murray } 988511b41d2SMark Murray 989511b41d2SMark Murray void 990511b41d2SMark Murray run_err(const char *fmt,...) 991511b41d2SMark Murray { 992511b41d2SMark Murray static FILE *fp; 993511b41d2SMark Murray va_list ap; 994511b41d2SMark Murray va_start(ap, fmt); 995511b41d2SMark Murray 996511b41d2SMark Murray ++errs; 997511b41d2SMark Murray if (fp == NULL && !(fp = fdopen(remout, "w"))) 998511b41d2SMark Murray return; 999511b41d2SMark Murray (void) fprintf(fp, "%c", 0x01); 1000511b41d2SMark Murray (void) fprintf(fp, "scp: "); 1001511b41d2SMark Murray (void) vfprintf(fp, fmt, ap); 1002511b41d2SMark Murray (void) fprintf(fp, "\n"); 1003511b41d2SMark Murray (void) fflush(fp); 1004511b41d2SMark Murray 1005511b41d2SMark Murray if (!iamremote) { 1006511b41d2SMark Murray vfprintf(stderr, fmt, ap); 1007511b41d2SMark Murray fprintf(stderr, "\n"); 1008511b41d2SMark Murray } 1009511b41d2SMark Murray va_end(ap); 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 1121a8f6863aSKris Kennaway updateprogressmeter(int ignore) 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 { 1212a04a10f8SKris Kennaway if (flag != 1) 1213a04a10f8SKris Kennaway remaining = 1214a04a10f8SKris Kennaway (int)(totalbytes / (statbytes / elapsed) - elapsed); 1215a04a10f8SKris Kennaway else 1216a04a10f8SKris Kennaway remaining = elapsed; 1217a04a10f8SKris Kennaway 1218511b41d2SMark Murray i = remaining / 3600; 1219511b41d2SMark Murray if (i) 1220511b41d2SMark Murray snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1221511b41d2SMark Murray "%2d:", i); 1222511b41d2SMark Murray else 1223511b41d2SMark Murray snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1224511b41d2SMark Murray " "); 1225511b41d2SMark Murray i = remaining % 3600; 1226511b41d2SMark Murray snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 1227a04a10f8SKris Kennaway "%02d:%02d%s", i / 60, i % 60, 1228a04a10f8SKris Kennaway (flag != 1) ? " ETA" : " "); 1229511b41d2SMark Murray } 1230511b41d2SMark Murray atomicio(write, fileno(stdout), buf, strlen(buf)); 1231511b41d2SMark Murray 1232511b41d2SMark Murray if (flag == -1) { 1233a8f6863aSKris Kennaway signal(SIGALRM, updateprogressmeter); 1234511b41d2SMark Murray alarmtimer(1); 1235511b41d2SMark Murray } else if (flag == 1) { 1236511b41d2SMark Murray alarmtimer(0); 1237a04a10f8SKris Kennaway atomicio(write, fileno(stdout), "\n", 1); 1238511b41d2SMark Murray statbytes = 0; 1239511b41d2SMark Murray } 1240511b41d2SMark Murray } 1241511b41d2SMark Murray 1242511b41d2SMark Murray int 1243511b41d2SMark Murray getttywidth(void) 1244511b41d2SMark Murray { 1245511b41d2SMark Murray struct winsize winsize; 1246511b41d2SMark Murray 1247511b41d2SMark Murray if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) 1248511b41d2SMark Murray return (winsize.ws_col ? winsize.ws_col : 80); 1249511b41d2SMark Murray else 1250511b41d2SMark Murray return (80); 1251511b41d2SMark Murray } 1252