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. 55cf2b5f3bSDag-Erling Smørgrav * 3. Neither the name of the University nor the names of its contributors 56511b41d2SMark Murray * may be used to endorse or promote products derived from this software 57511b41d2SMark Murray * without specific prior written permission. 58511b41d2SMark Murray * 59511b41d2SMark Murray * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60511b41d2SMark Murray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61511b41d2SMark Murray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62511b41d2SMark Murray * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63511b41d2SMark Murray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64511b41d2SMark Murray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65511b41d2SMark Murray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66511b41d2SMark Murray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67511b41d2SMark Murray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68511b41d2SMark Murray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69511b41d2SMark Murray * SUCH DAMAGE. 70511b41d2SMark Murray * 71511b41d2SMark Murray */ 72511b41d2SMark Murray 73511b41d2SMark Murray #include "includes.h" 74aa49c926SDag-Erling Smørgrav RCSID("$OpenBSD: scp.c,v 1.121 2005/04/02 12:41:16 djm Exp $"); 75511b41d2SMark Murray 76511b41d2SMark Murray #include "xmalloc.h" 771e8db6e2SBrian Feldman #include "atomicio.h" 781e8db6e2SBrian Feldman #include "pathnames.h" 791e8db6e2SBrian Feldman #include "log.h" 80ae1f160dSDag-Erling Smørgrav #include "misc.h" 81e73e9afaSDag-Erling Smørgrav #include "progressmeter.h" 82511b41d2SMark Murray 8383d2307dSDag-Erling Smørgrav extern char *__progname; 8483d2307dSDag-Erling Smørgrav 85e73e9afaSDag-Erling Smørgrav void bwlimit(int); 86511b41d2SMark Murray 87ae1f160dSDag-Erling Smørgrav /* Struct for addargs */ 88ae1f160dSDag-Erling Smørgrav arglist args; 895b9b2fafSBrian Feldman 90e73e9afaSDag-Erling Smørgrav /* Bandwidth limit */ 911ec0d754SDag-Erling Smørgrav off_t limit_rate = 0; 92511b41d2SMark Murray 93511b41d2SMark Murray /* Name of current file being transferred. */ 94511b41d2SMark Murray char *curfile; 95511b41d2SMark Murray 96511b41d2SMark Murray /* This is set to non-zero to enable verbose mode. */ 97511b41d2SMark Murray int verbose_mode = 0; 98511b41d2SMark Murray 99511b41d2SMark Murray /* This is set to zero if the progressmeter is not desired. */ 100511b41d2SMark Murray int showprogress = 1; 101511b41d2SMark Murray 102b66f2d16SKris Kennaway /* This is the program to execute for the secured connection. ("ssh" or -S) */ 1031e8db6e2SBrian Feldman char *ssh_program = _PATH_SSH_PROGRAM; 104b66f2d16SKris Kennaway 105e73e9afaSDag-Erling Smørgrav /* This is used to store the pid of ssh_program */ 106cf2b5f3bSDag-Erling Smørgrav pid_t do_cmd_pid = -1; 107cf2b5f3bSDag-Erling Smørgrav 108cf2b5f3bSDag-Erling Smørgrav static void 109cf2b5f3bSDag-Erling Smørgrav killchild(int signo) 110cf2b5f3bSDag-Erling Smørgrav { 111aa49c926SDag-Erling Smørgrav if (do_cmd_pid > 1) { 112cf2b5f3bSDag-Erling Smørgrav kill(do_cmd_pid, signo); 113aa49c926SDag-Erling Smørgrav waitpid(do_cmd_pid, NULL, 0); 114aa49c926SDag-Erling Smørgrav } 115cf2b5f3bSDag-Erling Smørgrav 116cf2b5f3bSDag-Erling Smørgrav _exit(1); 117cf2b5f3bSDag-Erling Smørgrav } 118e73e9afaSDag-Erling Smørgrav 119511b41d2SMark Murray /* 120511b41d2SMark Murray * This function executes the given command as the specified user on the 121511b41d2SMark Murray * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 122511b41d2SMark Murray * assigns the input and output file descriptors on success. 123511b41d2SMark Murray */ 124511b41d2SMark Murray 125511b41d2SMark Murray int 126b66f2d16SKris Kennaway do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) 127511b41d2SMark Murray { 128511b41d2SMark Murray int pin[2], pout[2], reserved[2]; 129511b41d2SMark Murray 130511b41d2SMark Murray if (verbose_mode) 131ae1f160dSDag-Erling Smørgrav fprintf(stderr, 132ae1f160dSDag-Erling Smørgrav "Executing: program %s host %s, user %s, command %s\n", 133ae1f160dSDag-Erling Smørgrav ssh_program, host, 134ae1f160dSDag-Erling Smørgrav remuser ? remuser : "(unspecified)", cmd); 135511b41d2SMark Murray 136511b41d2SMark Murray /* 137511b41d2SMark Murray * Reserve two descriptors so that the real pipes won't get 138511b41d2SMark Murray * descriptors 0 and 1 because that will screw up dup2 below. 139511b41d2SMark Murray */ 140511b41d2SMark Murray pipe(reserved); 141511b41d2SMark Murray 142511b41d2SMark Murray /* Create a socket pair for communicating with ssh. */ 143511b41d2SMark Murray if (pipe(pin) < 0) 144511b41d2SMark Murray fatal("pipe: %s", strerror(errno)); 145511b41d2SMark Murray if (pipe(pout) < 0) 146511b41d2SMark Murray fatal("pipe: %s", strerror(errno)); 147511b41d2SMark Murray 148511b41d2SMark Murray /* Free the reserved descriptors. */ 149511b41d2SMark Murray close(reserved[0]); 150511b41d2SMark Murray close(reserved[1]); 151511b41d2SMark Murray 152cf2b5f3bSDag-Erling Smørgrav /* Fork a child to execute the command on the remote host using ssh. */ 153e73e9afaSDag-Erling Smørgrav do_cmd_pid = fork(); 154e73e9afaSDag-Erling Smørgrav if (do_cmd_pid == 0) { 155511b41d2SMark Murray /* Child. */ 156511b41d2SMark Murray close(pin[1]); 157511b41d2SMark Murray close(pout[0]); 158511b41d2SMark Murray dup2(pin[0], 0); 159511b41d2SMark Murray dup2(pout[1], 1); 160511b41d2SMark Murray close(pin[0]); 161511b41d2SMark Murray close(pout[1]); 162511b41d2SMark Murray 1635b9b2fafSBrian Feldman args.list[0] = ssh_program; 1645b9b2fafSBrian Feldman if (remuser != NULL) 165ae1f160dSDag-Erling Smørgrav addargs(&args, "-l%s", remuser); 166ae1f160dSDag-Erling Smørgrav addargs(&args, "%s", host); 167ae1f160dSDag-Erling Smørgrav addargs(&args, "%s", cmd); 168511b41d2SMark Murray 1695b9b2fafSBrian Feldman execvp(ssh_program, args.list); 170b66f2d16SKris Kennaway perror(ssh_program); 171511b41d2SMark Murray exit(1); 172e73e9afaSDag-Erling Smørgrav } else if (do_cmd_pid == -1) { 173e73e9afaSDag-Erling Smørgrav fatal("fork: %s", strerror(errno)); 174511b41d2SMark Murray } 175511b41d2SMark Murray /* Parent. Close the other side, and return the local side. */ 176511b41d2SMark Murray close(pin[0]); 177511b41d2SMark Murray *fdout = pin[1]; 178511b41d2SMark Murray close(pout[1]); 179511b41d2SMark Murray *fdin = pout[0]; 180cf2b5f3bSDag-Erling Smørgrav signal(SIGTERM, killchild); 181cf2b5f3bSDag-Erling Smørgrav signal(SIGINT, killchild); 182cf2b5f3bSDag-Erling Smørgrav signal(SIGHUP, killchild); 183511b41d2SMark Murray return 0; 184511b41d2SMark Murray } 185511b41d2SMark Murray 186511b41d2SMark Murray typedef struct { 187511b41d2SMark Murray int cnt; 188511b41d2SMark Murray char *buf; 189511b41d2SMark Murray } BUF; 190511b41d2SMark Murray 191511b41d2SMark Murray BUF *allocbuf(BUF *, int, int); 192511b41d2SMark Murray void lostconn(int); 193511b41d2SMark Murray void nospace(void); 194511b41d2SMark Murray int okname(char *); 195511b41d2SMark Murray void run_err(const char *,...); 196511b41d2SMark Murray void verifydir(char *); 197511b41d2SMark Murray 198511b41d2SMark Murray struct passwd *pwd; 199511b41d2SMark Murray uid_t userid; 200511b41d2SMark Murray int errs, remin, remout; 201511b41d2SMark Murray int pflag, iamremote, iamrecursive, targetshouldbedirectory; 202511b41d2SMark Murray 203511b41d2SMark Murray #define CMDNEEDS 64 204511b41d2SMark Murray char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 205511b41d2SMark Murray 206511b41d2SMark Murray int response(void); 207511b41d2SMark Murray void rsource(char *, struct stat *); 208511b41d2SMark Murray void sink(int, char *[]); 209511b41d2SMark Murray void source(int, char *[]); 210511b41d2SMark Murray void tolocal(int, char *[]); 211511b41d2SMark Murray void toremote(char *, int, char *[]); 212511b41d2SMark Murray void usage(void); 213511b41d2SMark Murray 214511b41d2SMark Murray int 215cf2b5f3bSDag-Erling Smørgrav main(int argc, char **argv) 216511b41d2SMark Murray { 217e73e9afaSDag-Erling Smørgrav int ch, fflag, tflag, status; 218e73e9afaSDag-Erling Smørgrav double speed; 219e73e9afaSDag-Erling Smørgrav char *targ, *endp; 220511b41d2SMark Murray extern char *optarg; 221511b41d2SMark Murray extern int optind; 222511b41d2SMark Murray 223cf2b5f3bSDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 22483d2307dSDag-Erling Smørgrav 2255b9b2fafSBrian Feldman args.list = NULL; 226ae1f160dSDag-Erling Smørgrav addargs(&args, "ssh"); /* overwritten with ssh_program */ 227ae1f160dSDag-Erling Smørgrav addargs(&args, "-x"); 228ae1f160dSDag-Erling Smørgrav addargs(&args, "-oForwardAgent no"); 229ae1f160dSDag-Erling Smørgrav addargs(&args, "-oClearAllForwardings yes"); 2305b9b2fafSBrian Feldman 231511b41d2SMark Murray fflag = tflag = 0; 232e73e9afaSDag-Erling Smørgrav while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1) 233511b41d2SMark Murray switch (ch) { 234511b41d2SMark Murray /* User-visible flags. */ 235e73e9afaSDag-Erling Smørgrav case '1': 236e73e9afaSDag-Erling Smørgrav case '2': 237511b41d2SMark Murray case '4': 238511b41d2SMark Murray case '6': 2395b9b2fafSBrian Feldman case 'C': 240ae1f160dSDag-Erling Smørgrav addargs(&args, "-%c", ch); 2415b9b2fafSBrian Feldman break; 2425b9b2fafSBrian Feldman case 'o': 2435b9b2fafSBrian Feldman case 'c': 2445b9b2fafSBrian Feldman case 'i': 245ae1f160dSDag-Erling Smørgrav case 'F': 246ae1f160dSDag-Erling Smørgrav addargs(&args, "-%c%s", ch, optarg); 2475b9b2fafSBrian Feldman break; 2485b9b2fafSBrian Feldman case 'P': 249ae1f160dSDag-Erling Smørgrav addargs(&args, "-p%s", optarg); 2505b9b2fafSBrian Feldman break; 2515b9b2fafSBrian Feldman case 'B': 252ae1f160dSDag-Erling Smørgrav addargs(&args, "-oBatchmode yes"); 253511b41d2SMark Murray break; 254e73e9afaSDag-Erling Smørgrav case 'l': 255e73e9afaSDag-Erling Smørgrav speed = strtod(optarg, &endp); 256e73e9afaSDag-Erling Smørgrav if (speed <= 0 || *endp != '\0') 257e73e9afaSDag-Erling Smørgrav usage(); 2581ec0d754SDag-Erling Smørgrav limit_rate = speed * 1024; 259e73e9afaSDag-Erling Smørgrav break; 260511b41d2SMark Murray case 'p': 261511b41d2SMark Murray pflag = 1; 262511b41d2SMark Murray break; 263511b41d2SMark Murray case 'r': 264511b41d2SMark Murray iamrecursive = 1; 265511b41d2SMark Murray break; 266b66f2d16SKris Kennaway case 'S': 2675b9b2fafSBrian Feldman ssh_program = xstrdup(optarg); 2685b9b2fafSBrian Feldman break; 2695b9b2fafSBrian Feldman case 'v': 270ae1f160dSDag-Erling Smørgrav addargs(&args, "-v"); 2715b9b2fafSBrian Feldman verbose_mode = 1; 2725b9b2fafSBrian Feldman break; 2735b9b2fafSBrian Feldman case 'q': 2741ec0d754SDag-Erling Smørgrav addargs(&args, "-q"); 2755b9b2fafSBrian Feldman showprogress = 0; 276b66f2d16SKris Kennaway break; 277b66f2d16SKris Kennaway 278511b41d2SMark Murray /* Server options. */ 279511b41d2SMark Murray case 'd': 280511b41d2SMark Murray targetshouldbedirectory = 1; 281511b41d2SMark Murray break; 282511b41d2SMark Murray case 'f': /* "from" */ 283511b41d2SMark Murray iamremote = 1; 284511b41d2SMark Murray fflag = 1; 285511b41d2SMark Murray break; 286511b41d2SMark Murray case 't': /* "to" */ 287511b41d2SMark Murray iamremote = 1; 288511b41d2SMark Murray tflag = 1; 28983d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 29083d2307dSDag-Erling Smørgrav setmode(0, O_BINARY); 29183d2307dSDag-Erling Smørgrav #endif 292511b41d2SMark Murray break; 293511b41d2SMark Murray default: 294511b41d2SMark Murray usage(); 295511b41d2SMark Murray } 296511b41d2SMark Murray argc -= optind; 297511b41d2SMark Murray argv += optind; 298511b41d2SMark Murray 299511b41d2SMark Murray if ((pwd = getpwuid(userid = getuid())) == NULL) 300cf2b5f3bSDag-Erling Smørgrav fatal("unknown user %u", (u_int) userid); 301511b41d2SMark Murray 302511b41d2SMark Murray if (!isatty(STDERR_FILENO)) 303511b41d2SMark Murray showprogress = 0; 304511b41d2SMark Murray 305511b41d2SMark Murray remin = STDIN_FILENO; 306511b41d2SMark Murray remout = STDOUT_FILENO; 307511b41d2SMark Murray 308511b41d2SMark Murray if (fflag) { 309511b41d2SMark Murray /* Follow "protocol", send data. */ 310511b41d2SMark Murray (void) response(); 311511b41d2SMark Murray source(argc, argv); 312511b41d2SMark Murray exit(errs != 0); 313511b41d2SMark Murray } 314511b41d2SMark Murray if (tflag) { 315511b41d2SMark Murray /* Receive data. */ 316511b41d2SMark Murray sink(argc, argv); 317511b41d2SMark Murray exit(errs != 0); 318511b41d2SMark Murray } 319511b41d2SMark Murray if (argc < 2) 320511b41d2SMark Murray usage(); 321511b41d2SMark Murray if (argc > 2) 322511b41d2SMark Murray targetshouldbedirectory = 1; 323511b41d2SMark Murray 324511b41d2SMark Murray remin = remout = -1; 325e73e9afaSDag-Erling Smørgrav do_cmd_pid = -1; 326511b41d2SMark Murray /* Command to be executed on remote system using "ssh". */ 3271e8db6e2SBrian Feldman (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", 3281e8db6e2SBrian Feldman verbose_mode ? " -v" : "", 329511b41d2SMark Murray iamrecursive ? " -r" : "", pflag ? " -p" : "", 330511b41d2SMark Murray targetshouldbedirectory ? " -d" : ""); 331511b41d2SMark Murray 332511b41d2SMark Murray (void) signal(SIGPIPE, lostconn); 333511b41d2SMark Murray 334511b41d2SMark Murray if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 335511b41d2SMark Murray toremote(targ, argc, argv); 336511b41d2SMark Murray else { 337511b41d2SMark Murray tolocal(argc, argv); /* Dest is local host. */ 338511b41d2SMark Murray if (targetshouldbedirectory) 339511b41d2SMark Murray verifydir(argv[argc - 1]); 340511b41d2SMark Murray } 341e73e9afaSDag-Erling Smørgrav /* 342e73e9afaSDag-Erling Smørgrav * Finally check the exit status of the ssh process, if one was forked 343e73e9afaSDag-Erling Smørgrav * and no error has occured yet 344e73e9afaSDag-Erling Smørgrav */ 345e73e9afaSDag-Erling Smørgrav if (do_cmd_pid != -1 && errs == 0) { 346e73e9afaSDag-Erling Smørgrav if (remin != -1) 347e73e9afaSDag-Erling Smørgrav (void) close(remin); 348e73e9afaSDag-Erling Smørgrav if (remout != -1) 349e73e9afaSDag-Erling Smørgrav (void) close(remout); 350e73e9afaSDag-Erling Smørgrav if (waitpid(do_cmd_pid, &status, 0) == -1) 351e73e9afaSDag-Erling Smørgrav errs = 1; 352e73e9afaSDag-Erling Smørgrav else { 353e73e9afaSDag-Erling Smørgrav if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 354e73e9afaSDag-Erling Smørgrav errs = 1; 355e73e9afaSDag-Erling Smørgrav } 356e73e9afaSDag-Erling Smørgrav } 357511b41d2SMark Murray exit(errs != 0); 358511b41d2SMark Murray } 359511b41d2SMark Murray 360511b41d2SMark Murray void 361cf2b5f3bSDag-Erling Smørgrav toremote(char *targ, int argc, char **argv) 362511b41d2SMark Murray { 363511b41d2SMark Murray int i, len; 364aa49c926SDag-Erling Smørgrav char *bp, *host, *src, *suser, *thost, *tuser, *arg; 365511b41d2SMark Murray 366511b41d2SMark Murray *targ++ = 0; 367511b41d2SMark Murray if (*targ == 0) 368511b41d2SMark Murray targ = "."; 369511b41d2SMark Murray 370aa49c926SDag-Erling Smørgrav arg = xstrdup(argv[argc - 1]); 371aa49c926SDag-Erling Smørgrav if ((thost = strrchr(arg, '@'))) { 372511b41d2SMark Murray /* user@host */ 373511b41d2SMark Murray *thost++ = 0; 374aa49c926SDag-Erling Smørgrav tuser = arg; 375511b41d2SMark Murray if (*tuser == '\0') 376511b41d2SMark Murray tuser = NULL; 377511b41d2SMark Murray } else { 378aa49c926SDag-Erling Smørgrav thost = arg; 379511b41d2SMark Murray tuser = NULL; 380511b41d2SMark Murray } 381511b41d2SMark Murray 382511b41d2SMark Murray for (i = 0; i < argc - 1; i++) { 383511b41d2SMark Murray src = colon(argv[i]); 384511b41d2SMark Murray if (src) { /* remote to remote */ 385ae1f160dSDag-Erling Smørgrav static char *ssh_options = 386545d5ecaSDag-Erling Smørgrav "-x -o'ClearAllForwardings yes'"; 387511b41d2SMark Murray *src++ = 0; 388511b41d2SMark Murray if (*src == 0) 389511b41d2SMark Murray src = "."; 390e73e9afaSDag-Erling Smørgrav host = strrchr(argv[i], '@'); 391b66f2d16SKris Kennaway len = strlen(ssh_program) + strlen(argv[i]) + 392511b41d2SMark Murray strlen(src) + (tuser ? strlen(tuser) : 0) + 393ae1f160dSDag-Erling Smørgrav strlen(thost) + strlen(targ) + 394ae1f160dSDag-Erling Smørgrav strlen(ssh_options) + CMDNEEDS + 20; 395511b41d2SMark Murray bp = xmalloc(len); 396511b41d2SMark Murray if (host) { 397511b41d2SMark Murray *host++ = 0; 398511b41d2SMark Murray host = cleanhostname(host); 399511b41d2SMark Murray suser = argv[i]; 400511b41d2SMark Murray if (*suser == '\0') 401511b41d2SMark Murray suser = pwd->pw_name; 402e73e9afaSDag-Erling Smørgrav else if (!okname(suser)) { 403e73e9afaSDag-Erling Smørgrav xfree(bp); 404511b41d2SMark Murray continue; 405e73e9afaSDag-Erling Smørgrav } 406e73e9afaSDag-Erling Smørgrav if (tuser && !okname(tuser)) { 407e73e9afaSDag-Erling Smørgrav xfree(bp); 408e73e9afaSDag-Erling Smørgrav continue; 409e73e9afaSDag-Erling Smørgrav } 4101e8db6e2SBrian Feldman snprintf(bp, len, 411ae1f160dSDag-Erling Smørgrav "%s%s %s -n " 4121e8db6e2SBrian Feldman "-l %s %s %s %s '%s%s%s:%s'", 413b66f2d16SKris Kennaway ssh_program, verbose_mode ? " -v" : "", 414ae1f160dSDag-Erling Smørgrav ssh_options, suser, host, cmd, src, 415511b41d2SMark Murray tuser ? tuser : "", tuser ? "@" : "", 416511b41d2SMark Murray thost, targ); 417511b41d2SMark Murray } else { 418511b41d2SMark Murray host = cleanhostname(argv[i]); 4191e8db6e2SBrian Feldman snprintf(bp, len, 420ae1f160dSDag-Erling Smørgrav "exec %s%s %s -n %s " 4211e8db6e2SBrian Feldman "%s %s '%s%s%s:%s'", 422b66f2d16SKris Kennaway ssh_program, verbose_mode ? " -v" : "", 423ae1f160dSDag-Erling Smørgrav ssh_options, host, cmd, src, 424511b41d2SMark Murray tuser ? tuser : "", tuser ? "@" : "", 425511b41d2SMark Murray thost, targ); 426511b41d2SMark Murray } 427511b41d2SMark Murray if (verbose_mode) 428511b41d2SMark Murray fprintf(stderr, "Executing: %s\n", bp); 4291ec0d754SDag-Erling Smørgrav if (system(bp) != 0) 4301ec0d754SDag-Erling Smørgrav errs = 1; 431511b41d2SMark Murray (void) xfree(bp); 432511b41d2SMark Murray } else { /* local to remote */ 433511b41d2SMark Murray if (remin == -1) { 434511b41d2SMark Murray len = strlen(targ) + CMDNEEDS + 20; 435511b41d2SMark Murray bp = xmalloc(len); 4361e8db6e2SBrian Feldman (void) snprintf(bp, len, "%s -t %s", cmd, targ); 437511b41d2SMark Murray host = cleanhostname(thost); 438b66f2d16SKris Kennaway if (do_cmd(host, tuser, bp, &remin, 439b66f2d16SKris Kennaway &remout, argc) < 0) 440511b41d2SMark Murray exit(1); 441511b41d2SMark Murray if (response() < 0) 442511b41d2SMark Murray exit(1); 443511b41d2SMark Murray (void) xfree(bp); 444511b41d2SMark Murray } 445511b41d2SMark Murray source(1, argv + i); 446511b41d2SMark Murray } 447511b41d2SMark Murray } 448511b41d2SMark Murray } 449511b41d2SMark Murray 450511b41d2SMark Murray void 451cf2b5f3bSDag-Erling Smørgrav tolocal(int argc, char **argv) 452511b41d2SMark Murray { 453511b41d2SMark Murray int i, len; 454511b41d2SMark Murray char *bp, *host, *src, *suser; 455511b41d2SMark Murray 456511b41d2SMark Murray for (i = 0; i < argc - 1; i++) { 457511b41d2SMark Murray if (!(src = colon(argv[i]))) { /* Local to local. */ 458511b41d2SMark Murray len = strlen(_PATH_CP) + strlen(argv[i]) + 459511b41d2SMark Murray strlen(argv[argc - 1]) + 20; 460511b41d2SMark Murray bp = xmalloc(len); 4611e8db6e2SBrian Feldman (void) snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, 462511b41d2SMark Murray iamrecursive ? " -r" : "", pflag ? " -p" : "", 463511b41d2SMark Murray argv[i], argv[argc - 1]); 464511b41d2SMark Murray if (verbose_mode) 465511b41d2SMark Murray fprintf(stderr, "Executing: %s\n", bp); 466511b41d2SMark Murray if (system(bp)) 467511b41d2SMark Murray ++errs; 468511b41d2SMark Murray (void) xfree(bp); 469511b41d2SMark Murray continue; 470511b41d2SMark Murray } 471511b41d2SMark Murray *src++ = 0; 472511b41d2SMark Murray if (*src == 0) 473511b41d2SMark Murray src = "."; 474e73e9afaSDag-Erling Smørgrav if ((host = strrchr(argv[i], '@')) == NULL) { 475511b41d2SMark Murray host = argv[i]; 476511b41d2SMark Murray suser = NULL; 477511b41d2SMark Murray } else { 478511b41d2SMark Murray *host++ = 0; 479511b41d2SMark Murray suser = argv[i]; 480511b41d2SMark Murray if (*suser == '\0') 481511b41d2SMark Murray suser = pwd->pw_name; 482511b41d2SMark Murray } 483511b41d2SMark Murray host = cleanhostname(host); 484511b41d2SMark Murray len = strlen(src) + CMDNEEDS + 20; 485511b41d2SMark Murray bp = xmalloc(len); 4861e8db6e2SBrian Feldman (void) snprintf(bp, len, "%s -f %s", cmd, src); 487b66f2d16SKris Kennaway if (do_cmd(host, suser, bp, &remin, &remout, argc) < 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 500cf2b5f3bSDag-Erling Smørgrav source(int argc, char **argv) 501511b41d2SMark Murray { 502511b41d2SMark Murray struct stat stb; 503511b41d2SMark Murray static BUF buffer; 504511b41d2SMark Murray BUF *bp; 505e73e9afaSDag-Erling Smørgrav off_t i, amt, result, statbytes; 5061e8db6e2SBrian Feldman int fd, haderr, indx; 507511b41d2SMark Murray char *last, *name, buf[2048]; 5081e8db6e2SBrian Feldman int len; 509511b41d2SMark Murray 510511b41d2SMark Murray for (indx = 0; indx < argc; ++indx) { 511511b41d2SMark Murray name = argv[indx]; 512511b41d2SMark Murray statbytes = 0; 5131e8db6e2SBrian Feldman len = strlen(name); 5141e8db6e2SBrian Feldman while (len > 1 && name[len-1] == '/') 5151e8db6e2SBrian Feldman name[--len] = '\0'; 516ae1f160dSDag-Erling Smørgrav if (strchr(name, '\n') != NULL) { 517ae1f160dSDag-Erling Smørgrav run_err("%s: skipping, filename contains a newline", 518ae1f160dSDag-Erling Smørgrav name); 519ae1f160dSDag-Erling Smørgrav goto next; 520ae1f160dSDag-Erling Smørgrav } 521511b41d2SMark Murray if ((fd = open(name, O_RDONLY, 0)) < 0) 522511b41d2SMark Murray goto syserr; 523511b41d2SMark Murray if (fstat(fd, &stb) < 0) { 524511b41d2SMark Murray syserr: run_err("%s: %s", name, strerror(errno)); 525511b41d2SMark Murray goto next; 526511b41d2SMark Murray } 527511b41d2SMark Murray switch (stb.st_mode & S_IFMT) { 528511b41d2SMark Murray case S_IFREG: 529511b41d2SMark Murray break; 530511b41d2SMark Murray case S_IFDIR: 531511b41d2SMark Murray if (iamrecursive) { 532511b41d2SMark Murray rsource(name, &stb); 533511b41d2SMark Murray goto next; 534511b41d2SMark Murray } 535511b41d2SMark Murray /* FALLTHROUGH */ 536511b41d2SMark Murray default: 537511b41d2SMark Murray run_err("%s: not a regular file", name); 538511b41d2SMark Murray goto next; 539511b41d2SMark Murray } 540511b41d2SMark Murray if ((last = strrchr(name, '/')) == NULL) 541511b41d2SMark Murray last = name; 542511b41d2SMark Murray else 543511b41d2SMark Murray ++last; 544511b41d2SMark Murray curfile = last; 545511b41d2SMark Murray if (pflag) { 546511b41d2SMark Murray /* 547511b41d2SMark Murray * Make it compatible with possible future 548511b41d2SMark Murray * versions expecting microseconds. 549511b41d2SMark Murray */ 5501e8db6e2SBrian Feldman (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", 5511e8db6e2SBrian Feldman (u_long) stb.st_mtime, 5521e8db6e2SBrian Feldman (u_long) stb.st_atime); 553cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, buf, strlen(buf)); 554511b41d2SMark Murray if (response() < 0) 555511b41d2SMark Murray goto next; 556511b41d2SMark Murray } 557511b41d2SMark Murray #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 5581e8db6e2SBrian Feldman snprintf(buf, sizeof buf, "C%04o %lld %s\n", 5591e8db6e2SBrian Feldman (u_int) (stb.st_mode & FILEMODEMASK), 560cf2b5f3bSDag-Erling Smørgrav (int64_t)stb.st_size, last); 561511b41d2SMark Murray if (verbose_mode) { 562511b41d2SMark Murray fprintf(stderr, "Sending file modes: %s", buf); 563511b41d2SMark Murray } 564cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, buf, strlen(buf)); 565511b41d2SMark Murray if (response() < 0) 566511b41d2SMark Murray goto next; 567511b41d2SMark Murray if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { 568511b41d2SMark Murray next: (void) close(fd); 569511b41d2SMark Murray continue; 570511b41d2SMark Murray } 571e73e9afaSDag-Erling Smørgrav if (showprogress) 572e73e9afaSDag-Erling Smørgrav start_progress_meter(curfile, stb.st_size, &statbytes); 573511b41d2SMark Murray /* Keep writing after an error so that we stay sync'd up. */ 574511b41d2SMark Murray for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 575511b41d2SMark Murray amt = bp->cnt; 576511b41d2SMark Murray if (i + amt > stb.st_size) 577511b41d2SMark Murray amt = stb.st_size - i; 578511b41d2SMark Murray if (!haderr) { 579a04a10f8SKris Kennaway result = atomicio(read, fd, bp->buf, amt); 580511b41d2SMark Murray if (result != amt) 581511b41d2SMark Murray haderr = result >= 0 ? EIO : errno; 582511b41d2SMark Murray } 583511b41d2SMark Murray if (haderr) 584cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, bp->buf, amt); 585511b41d2SMark Murray else { 586cf2b5f3bSDag-Erling Smørgrav result = atomicio(vwrite, remout, bp->buf, amt); 587511b41d2SMark Murray if (result != amt) 588511b41d2SMark Murray haderr = result >= 0 ? EIO : errno; 589511b41d2SMark Murray statbytes += result; 590511b41d2SMark Murray } 5911ec0d754SDag-Erling Smørgrav if (limit_rate) 592e73e9afaSDag-Erling Smørgrav bwlimit(amt); 593511b41d2SMark Murray } 594511b41d2SMark Murray if (showprogress) 595e73e9afaSDag-Erling Smørgrav stop_progress_meter(); 596511b41d2SMark Murray 597511b41d2SMark Murray if (close(fd) < 0 && !haderr) 598511b41d2SMark Murray haderr = errno; 599511b41d2SMark Murray if (!haderr) 600cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 601511b41d2SMark Murray else 602511b41d2SMark Murray run_err("%s: %s", name, strerror(haderr)); 603511b41d2SMark Murray (void) response(); 604511b41d2SMark Murray } 605511b41d2SMark Murray } 606511b41d2SMark Murray 607511b41d2SMark Murray void 608cf2b5f3bSDag-Erling Smørgrav rsource(char *name, struct stat *statp) 609511b41d2SMark Murray { 610511b41d2SMark Murray DIR *dirp; 611511b41d2SMark Murray struct dirent *dp; 612511b41d2SMark Murray char *last, *vect[1], path[1100]; 613511b41d2SMark Murray 614511b41d2SMark Murray if (!(dirp = opendir(name))) { 615511b41d2SMark Murray run_err("%s: %s", name, strerror(errno)); 616511b41d2SMark Murray return; 617511b41d2SMark Murray } 618511b41d2SMark Murray last = strrchr(name, '/'); 619511b41d2SMark Murray if (last == 0) 620511b41d2SMark Murray last = name; 621511b41d2SMark Murray else 622511b41d2SMark Murray last++; 623511b41d2SMark Murray if (pflag) { 6241e8db6e2SBrian Feldman (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", 6251e8db6e2SBrian Feldman (u_long) statp->st_mtime, 6261e8db6e2SBrian Feldman (u_long) statp->st_atime); 627cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, path, strlen(path)); 628511b41d2SMark Murray if (response() < 0) { 629511b41d2SMark Murray closedir(dirp); 630511b41d2SMark Murray return; 631511b41d2SMark Murray } 632511b41d2SMark Murray } 6331e8db6e2SBrian Feldman (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", 6341e8db6e2SBrian Feldman (u_int) (statp->st_mode & FILEMODEMASK), 0, last); 635511b41d2SMark Murray if (verbose_mode) 636511b41d2SMark Murray fprintf(stderr, "Entering directory: %s", path); 637cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, path, strlen(path)); 638511b41d2SMark Murray if (response() < 0) { 639511b41d2SMark Murray closedir(dirp); 640511b41d2SMark Murray return; 641511b41d2SMark Murray } 6421e8db6e2SBrian Feldman while ((dp = readdir(dirp)) != NULL) { 643511b41d2SMark Murray if (dp->d_ino == 0) 644511b41d2SMark Murray continue; 645511b41d2SMark Murray if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 646511b41d2SMark Murray continue; 647511b41d2SMark Murray if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { 648511b41d2SMark Murray run_err("%s/%s: name too long", name, dp->d_name); 649511b41d2SMark Murray continue; 650511b41d2SMark Murray } 6511e8db6e2SBrian Feldman (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); 652511b41d2SMark Murray vect[0] = path; 653511b41d2SMark Murray source(1, vect); 654511b41d2SMark Murray } 655511b41d2SMark Murray (void) closedir(dirp); 656cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "E\n", 2); 657511b41d2SMark Murray (void) response(); 658511b41d2SMark Murray } 659511b41d2SMark Murray 660511b41d2SMark Murray void 661e73e9afaSDag-Erling Smørgrav bwlimit(int amount) 662e73e9afaSDag-Erling Smørgrav { 663e73e9afaSDag-Erling Smørgrav static struct timeval bwstart, bwend; 664e73e9afaSDag-Erling Smørgrav static int lamt, thresh = 16384; 66521e764dfSDag-Erling Smørgrav u_int64_t waitlen; 666e73e9afaSDag-Erling Smørgrav struct timespec ts, rm; 667e73e9afaSDag-Erling Smørgrav 668e73e9afaSDag-Erling Smørgrav if (!timerisset(&bwstart)) { 669e73e9afaSDag-Erling Smørgrav gettimeofday(&bwstart, NULL); 670e73e9afaSDag-Erling Smørgrav return; 671e73e9afaSDag-Erling Smørgrav } 672e73e9afaSDag-Erling Smørgrav 673e73e9afaSDag-Erling Smørgrav lamt += amount; 674e73e9afaSDag-Erling Smørgrav if (lamt < thresh) 675e73e9afaSDag-Erling Smørgrav return; 676e73e9afaSDag-Erling Smørgrav 677e73e9afaSDag-Erling Smørgrav gettimeofday(&bwend, NULL); 678e73e9afaSDag-Erling Smørgrav timersub(&bwend, &bwstart, &bwend); 679e73e9afaSDag-Erling Smørgrav if (!timerisset(&bwend)) 680e73e9afaSDag-Erling Smørgrav return; 681e73e9afaSDag-Erling Smørgrav 682e73e9afaSDag-Erling Smørgrav lamt *= 8; 68321e764dfSDag-Erling Smørgrav waitlen = (double)1000000L * lamt / limit_rate; 684e73e9afaSDag-Erling Smørgrav 68521e764dfSDag-Erling Smørgrav bwstart.tv_sec = waitlen / 1000000L; 68621e764dfSDag-Erling Smørgrav bwstart.tv_usec = waitlen % 1000000L; 687e73e9afaSDag-Erling Smørgrav 688e73e9afaSDag-Erling Smørgrav if (timercmp(&bwstart, &bwend, >)) { 689e73e9afaSDag-Erling Smørgrav timersub(&bwstart, &bwend, &bwend); 690e73e9afaSDag-Erling Smørgrav 691e73e9afaSDag-Erling Smørgrav /* Adjust the wait time */ 692e73e9afaSDag-Erling Smørgrav if (bwend.tv_sec) { 693e73e9afaSDag-Erling Smørgrav thresh /= 2; 694e73e9afaSDag-Erling Smørgrav if (thresh < 2048) 695e73e9afaSDag-Erling Smørgrav thresh = 2048; 696e73e9afaSDag-Erling Smørgrav } else if (bwend.tv_usec < 100) { 697e73e9afaSDag-Erling Smørgrav thresh *= 2; 698e73e9afaSDag-Erling Smørgrav if (thresh > 32768) 699e73e9afaSDag-Erling Smørgrav thresh = 32768; 700e73e9afaSDag-Erling Smørgrav } 701e73e9afaSDag-Erling Smørgrav 702e73e9afaSDag-Erling Smørgrav TIMEVAL_TO_TIMESPEC(&bwend, &ts); 703e73e9afaSDag-Erling Smørgrav while (nanosleep(&ts, &rm) == -1) { 704e73e9afaSDag-Erling Smørgrav if (errno != EINTR) 705e73e9afaSDag-Erling Smørgrav break; 706e73e9afaSDag-Erling Smørgrav ts = rm; 707e73e9afaSDag-Erling Smørgrav } 708e73e9afaSDag-Erling Smørgrav } 709e73e9afaSDag-Erling Smørgrav 710e73e9afaSDag-Erling Smørgrav lamt = 0; 711e73e9afaSDag-Erling Smørgrav gettimeofday(&bwstart, NULL); 712e73e9afaSDag-Erling Smørgrav } 713e73e9afaSDag-Erling Smørgrav 714e73e9afaSDag-Erling Smørgrav void 715cf2b5f3bSDag-Erling Smørgrav sink(int argc, char **argv) 716511b41d2SMark Murray { 717511b41d2SMark Murray static BUF buffer; 718511b41d2SMark Murray struct stat stb; 719511b41d2SMark Murray enum { 720511b41d2SMark Murray YES, NO, DISPLAYED 721511b41d2SMark Murray } wrerr; 722511b41d2SMark Murray BUF *bp; 723511b41d2SMark Murray off_t i, j; 724511b41d2SMark Murray int amt, count, exists, first, mask, mode, ofd, omode; 725e73e9afaSDag-Erling Smørgrav off_t size, statbytes; 726b66f2d16SKris Kennaway int setimes, targisdir, wrerrno = 0; 727511b41d2SMark Murray char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; 7285b9b2fafSBrian Feldman struct timeval tv[2]; 729511b41d2SMark Murray 7301e8db6e2SBrian Feldman #define atime tv[0] 7311e8db6e2SBrian Feldman #define mtime tv[1] 732aa49c926SDag-Erling Smørgrav #define SCREWUP(str) { why = str; goto screwup; } 733511b41d2SMark Murray 734511b41d2SMark Murray setimes = targisdir = 0; 735511b41d2SMark Murray mask = umask(0); 736511b41d2SMark Murray if (!pflag) 737511b41d2SMark Murray (void) umask(mask); 738511b41d2SMark Murray if (argc != 1) { 739511b41d2SMark Murray run_err("ambiguous target"); 740511b41d2SMark Murray exit(1); 741511b41d2SMark Murray } 742511b41d2SMark Murray targ = *argv; 743511b41d2SMark Murray if (targetshouldbedirectory) 744511b41d2SMark Murray verifydir(targ); 745511b41d2SMark Murray 746cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 747511b41d2SMark Murray if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 748511b41d2SMark Murray targisdir = 1; 749511b41d2SMark Murray for (first = 1;; first = 0) { 750511b41d2SMark Murray cp = buf; 751a04a10f8SKris Kennaway if (atomicio(read, remin, cp, 1) <= 0) 752511b41d2SMark Murray return; 753511b41d2SMark Murray if (*cp++ == '\n') 754511b41d2SMark Murray SCREWUP("unexpected <newline>"); 755511b41d2SMark Murray do { 756a04a10f8SKris Kennaway if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 757511b41d2SMark Murray SCREWUP("lost connection"); 758511b41d2SMark Murray *cp++ = ch; 759511b41d2SMark Murray } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 760511b41d2SMark Murray *cp = 0; 76121e764dfSDag-Erling Smørgrav if (verbose_mode) 76221e764dfSDag-Erling Smørgrav fprintf(stderr, "Sink: %s", buf); 763511b41d2SMark Murray 764511b41d2SMark Murray if (buf[0] == '\01' || buf[0] == '\02') { 765511b41d2SMark Murray if (iamremote == 0) 766cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, STDERR_FILENO, 767511b41d2SMark Murray buf + 1, strlen(buf + 1)); 768511b41d2SMark Murray if (buf[0] == '\02') 769511b41d2SMark Murray exit(1); 770511b41d2SMark Murray ++errs; 771511b41d2SMark Murray continue; 772511b41d2SMark Murray } 773511b41d2SMark Murray if (buf[0] == 'E') { 774cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 775511b41d2SMark Murray return; 776511b41d2SMark Murray } 777511b41d2SMark Murray if (ch == '\n') 778511b41d2SMark Murray *--cp = 0; 779511b41d2SMark Murray 780511b41d2SMark Murray cp = buf; 781511b41d2SMark Murray if (*cp == 'T') { 782511b41d2SMark Murray setimes++; 783511b41d2SMark Murray cp++; 7841e8db6e2SBrian Feldman mtime.tv_sec = strtol(cp, &cp, 10); 7851e8db6e2SBrian Feldman if (!cp || *cp++ != ' ') 786511b41d2SMark Murray SCREWUP("mtime.sec not delimited"); 7871e8db6e2SBrian Feldman mtime.tv_usec = strtol(cp, &cp, 10); 7881e8db6e2SBrian Feldman if (!cp || *cp++ != ' ') 789511b41d2SMark Murray SCREWUP("mtime.usec not delimited"); 7901e8db6e2SBrian Feldman atime.tv_sec = strtol(cp, &cp, 10); 7911e8db6e2SBrian Feldman if (!cp || *cp++ != ' ') 792511b41d2SMark Murray SCREWUP("atime.sec not delimited"); 7931e8db6e2SBrian Feldman atime.tv_usec = strtol(cp, &cp, 10); 7941e8db6e2SBrian Feldman if (!cp || *cp++ != '\0') 795511b41d2SMark Murray SCREWUP("atime.usec not delimited"); 796cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 797511b41d2SMark Murray continue; 798511b41d2SMark Murray } 799511b41d2SMark Murray if (*cp != 'C' && *cp != 'D') { 800511b41d2SMark Murray /* 801511b41d2SMark Murray * Check for the case "rcp remote:foo\* local:bar". 802511b41d2SMark Murray * In this case, the line "No match." can be returned 803511b41d2SMark Murray * by the shell before the rcp command on the remote is 804511b41d2SMark Murray * executed so the ^Aerror_message convention isn't 805511b41d2SMark Murray * followed. 806511b41d2SMark Murray */ 807511b41d2SMark Murray if (first) { 808511b41d2SMark Murray run_err("%s", cp); 809511b41d2SMark Murray exit(1); 810511b41d2SMark Murray } 811511b41d2SMark Murray SCREWUP("expected control record"); 812511b41d2SMark Murray } 813511b41d2SMark Murray mode = 0; 814511b41d2SMark Murray for (++cp; cp < buf + 5; cp++) { 815511b41d2SMark Murray if (*cp < '0' || *cp > '7') 816511b41d2SMark Murray SCREWUP("bad mode"); 817511b41d2SMark Murray mode = (mode << 3) | (*cp - '0'); 818511b41d2SMark Murray } 819511b41d2SMark Murray if (*cp++ != ' ') 820511b41d2SMark Murray SCREWUP("mode not delimited"); 821511b41d2SMark Murray 8221e8db6e2SBrian Feldman for (size = 0; isdigit(*cp);) 823511b41d2SMark Murray size = size * 10 + (*cp++ - '0'); 824511b41d2SMark Murray if (*cp++ != ' ') 825511b41d2SMark Murray SCREWUP("size not delimited"); 82621e764dfSDag-Erling Smørgrav if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) { 82721e764dfSDag-Erling Smørgrav run_err("error: unexpected filename: %s", cp); 82821e764dfSDag-Erling Smørgrav exit(1); 82921e764dfSDag-Erling Smørgrav } 830511b41d2SMark Murray if (targisdir) { 831511b41d2SMark Murray static char *namebuf; 832511b41d2SMark Murray static int cursize; 833511b41d2SMark Murray size_t need; 834511b41d2SMark Murray 835511b41d2SMark Murray need = strlen(targ) + strlen(cp) + 250; 8361e8db6e2SBrian Feldman if (need > cursize) { 8371e8db6e2SBrian Feldman if (namebuf) 8381e8db6e2SBrian Feldman xfree(namebuf); 839511b41d2SMark Murray namebuf = xmalloc(need); 8401e8db6e2SBrian Feldman cursize = need; 8411e8db6e2SBrian Feldman } 8421e8db6e2SBrian Feldman (void) snprintf(namebuf, need, "%s%s%s", targ, 843545d5ecaSDag-Erling Smørgrav strcmp(targ, "/") ? "/" : "", cp); 844511b41d2SMark Murray np = namebuf; 845511b41d2SMark Murray } else 846511b41d2SMark Murray np = targ; 847511b41d2SMark Murray curfile = cp; 848511b41d2SMark Murray exists = stat(np, &stb) == 0; 849511b41d2SMark Murray if (buf[0] == 'D') { 850511b41d2SMark Murray int mod_flag = pflag; 85121e764dfSDag-Erling Smørgrav if (!iamrecursive) 85221e764dfSDag-Erling Smørgrav SCREWUP("received directory without -r"); 853511b41d2SMark Murray if (exists) { 854511b41d2SMark Murray if (!S_ISDIR(stb.st_mode)) { 855511b41d2SMark Murray errno = ENOTDIR; 856511b41d2SMark Murray goto bad; 857511b41d2SMark Murray } 858511b41d2SMark Murray if (pflag) 859511b41d2SMark Murray (void) chmod(np, mode); 860511b41d2SMark Murray } else { 861511b41d2SMark Murray /* Handle copying from a read-only 862511b41d2SMark Murray directory */ 863511b41d2SMark Murray mod_flag = 1; 864511b41d2SMark Murray if (mkdir(np, mode | S_IRWXU) < 0) 865511b41d2SMark Murray goto bad; 866511b41d2SMark Murray } 8671e8db6e2SBrian Feldman vect[0] = xstrdup(np); 868511b41d2SMark Murray sink(1, vect); 869511b41d2SMark Murray if (setimes) { 870511b41d2SMark Murray setimes = 0; 8711e8db6e2SBrian Feldman if (utimes(vect[0], tv) < 0) 872511b41d2SMark Murray run_err("%s: set times: %s", 8731e8db6e2SBrian Feldman vect[0], strerror(errno)); 874511b41d2SMark Murray } 875511b41d2SMark Murray if (mod_flag) 8761e8db6e2SBrian Feldman (void) chmod(vect[0], mode); 8771e8db6e2SBrian Feldman if (vect[0]) 8781e8db6e2SBrian Feldman xfree(vect[0]); 879511b41d2SMark Murray continue; 880511b41d2SMark Murray } 881511b41d2SMark Murray omode = mode; 882511b41d2SMark Murray mode |= S_IWRITE; 883ae1f160dSDag-Erling Smørgrav if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 884511b41d2SMark Murray bad: run_err("%s: %s", np, strerror(errno)); 885511b41d2SMark Murray continue; 886511b41d2SMark Murray } 887cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 888511b41d2SMark Murray if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { 889511b41d2SMark Murray (void) close(ofd); 890511b41d2SMark Murray continue; 891511b41d2SMark Murray } 892511b41d2SMark Murray cp = bp->buf; 893511b41d2SMark Murray wrerr = NO; 894511b41d2SMark Murray 895511b41d2SMark Murray statbytes = 0; 896e73e9afaSDag-Erling Smørgrav if (showprogress) 897e73e9afaSDag-Erling Smørgrav start_progress_meter(curfile, size, &statbytes); 898511b41d2SMark Murray for (count = i = 0; i < size; i += 4096) { 899511b41d2SMark Murray amt = 4096; 900511b41d2SMark Murray if (i + amt > size) 901511b41d2SMark Murray amt = size - i; 902511b41d2SMark Murray count += amt; 903511b41d2SMark Murray do { 90421e764dfSDag-Erling Smørgrav j = atomicio(read, remin, cp, amt); 90521e764dfSDag-Erling Smørgrav if (j <= 0) { 906511b41d2SMark Murray run_err("%s", j ? strerror(errno) : 907511b41d2SMark Murray "dropped connection"); 908511b41d2SMark Murray exit(1); 909511b41d2SMark Murray } 910511b41d2SMark Murray amt -= j; 911511b41d2SMark Murray cp += j; 912511b41d2SMark Murray statbytes += j; 913511b41d2SMark Murray } while (amt > 0); 914e73e9afaSDag-Erling Smørgrav 9151ec0d754SDag-Erling Smørgrav if (limit_rate) 916e73e9afaSDag-Erling Smørgrav bwlimit(4096); 917e73e9afaSDag-Erling Smørgrav 918511b41d2SMark Murray if (count == bp->cnt) { 919511b41d2SMark Murray /* Keep reading so we stay sync'd up. */ 920511b41d2SMark Murray if (wrerr == NO) { 921cf2b5f3bSDag-Erling Smørgrav j = atomicio(vwrite, ofd, bp->buf, count); 922511b41d2SMark Murray if (j != count) { 923511b41d2SMark Murray wrerr = YES; 924511b41d2SMark Murray wrerrno = j >= 0 ? EIO : errno; 925511b41d2SMark Murray } 926511b41d2SMark Murray } 927511b41d2SMark Murray count = 0; 928511b41d2SMark Murray cp = bp->buf; 929511b41d2SMark Murray } 930511b41d2SMark Murray } 931511b41d2SMark Murray if (showprogress) 932e73e9afaSDag-Erling Smørgrav stop_progress_meter(); 933511b41d2SMark Murray if (count != 0 && wrerr == NO && 934cf2b5f3bSDag-Erling Smørgrav (j = atomicio(vwrite, ofd, bp->buf, count)) != count) { 935511b41d2SMark Murray wrerr = YES; 936511b41d2SMark Murray wrerrno = j >= 0 ? EIO : errno; 937511b41d2SMark Murray } 938e73e9afaSDag-Erling Smørgrav if (wrerr == NO && ftruncate(ofd, size) != 0) { 939511b41d2SMark Murray run_err("%s: truncate: %s", np, strerror(errno)); 940511b41d2SMark Murray wrerr = DISPLAYED; 941511b41d2SMark Murray } 942511b41d2SMark Murray if (pflag) { 943511b41d2SMark Murray if (exists || omode != mode) 94483d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 94521e764dfSDag-Erling Smørgrav if (fchmod(ofd, omode)) { 94683d2307dSDag-Erling Smørgrav #else /* HAVE_FCHMOD */ 94721e764dfSDag-Erling Smørgrav if (chmod(np, omode)) { 94883d2307dSDag-Erling Smørgrav #endif /* HAVE_FCHMOD */ 949511b41d2SMark Murray run_err("%s: set mode: %s", 950511b41d2SMark Murray np, strerror(errno)); 95121e764dfSDag-Erling Smørgrav wrerr = DISPLAYED; 95221e764dfSDag-Erling Smørgrav } 953511b41d2SMark Murray } else { 954511b41d2SMark Murray if (!exists && omode != mode) 95583d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 95621e764dfSDag-Erling Smørgrav if (fchmod(ofd, omode & ~mask)) { 95783d2307dSDag-Erling Smørgrav #else /* HAVE_FCHMOD */ 95821e764dfSDag-Erling Smørgrav if (chmod(np, omode & ~mask)) { 95983d2307dSDag-Erling Smørgrav #endif /* HAVE_FCHMOD */ 960511b41d2SMark Murray run_err("%s: set mode: %s", 961511b41d2SMark Murray np, strerror(errno)); 96221e764dfSDag-Erling Smørgrav wrerr = DISPLAYED; 96321e764dfSDag-Erling Smørgrav } 964511b41d2SMark Murray } 965b66f2d16SKris Kennaway if (close(ofd) == -1) { 966b66f2d16SKris Kennaway wrerr = YES; 967b66f2d16SKris Kennaway wrerrno = errno; 968b66f2d16SKris Kennaway } 969511b41d2SMark Murray (void) response(); 970511b41d2SMark Murray if (setimes && wrerr == NO) { 971511b41d2SMark Murray setimes = 0; 9725b9b2fafSBrian Feldman if (utimes(np, tv) < 0) { 973511b41d2SMark Murray run_err("%s: set times: %s", 974511b41d2SMark Murray np, strerror(errno)); 975511b41d2SMark Murray wrerr = DISPLAYED; 976511b41d2SMark Murray } 977511b41d2SMark Murray } 978511b41d2SMark Murray switch (wrerr) { 979511b41d2SMark Murray case YES: 980511b41d2SMark Murray run_err("%s: %s", np, strerror(wrerrno)); 981511b41d2SMark Murray break; 982511b41d2SMark Murray case NO: 983cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 984511b41d2SMark Murray break; 985511b41d2SMark Murray case DISPLAYED: 986511b41d2SMark Murray break; 987511b41d2SMark Murray } 988511b41d2SMark Murray } 989511b41d2SMark Murray screwup: 990511b41d2SMark Murray run_err("protocol error: %s", why); 991511b41d2SMark Murray exit(1); 992511b41d2SMark Murray } 993511b41d2SMark Murray 994511b41d2SMark Murray int 995ae1f160dSDag-Erling Smørgrav response(void) 996511b41d2SMark Murray { 997511b41d2SMark Murray char ch, *cp, resp, rbuf[2048]; 998511b41d2SMark Murray 999a04a10f8SKris Kennaway if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) 1000511b41d2SMark Murray lostconn(0); 1001511b41d2SMark Murray 1002511b41d2SMark Murray cp = rbuf; 1003511b41d2SMark Murray switch (resp) { 1004511b41d2SMark Murray case 0: /* ok */ 1005511b41d2SMark Murray return (0); 1006511b41d2SMark Murray default: 1007511b41d2SMark Murray *cp++ = resp; 1008511b41d2SMark Murray /* FALLTHROUGH */ 1009511b41d2SMark Murray case 1: /* error, followed by error msg */ 1010511b41d2SMark Murray case 2: /* fatal error, "" */ 1011511b41d2SMark Murray do { 1012a04a10f8SKris Kennaway if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 1013511b41d2SMark Murray lostconn(0); 1014511b41d2SMark Murray *cp++ = ch; 1015511b41d2SMark Murray } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 1016511b41d2SMark Murray 1017511b41d2SMark Murray if (!iamremote) 1018cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); 1019511b41d2SMark Murray ++errs; 1020511b41d2SMark Murray if (resp == 1) 1021511b41d2SMark Murray return (-1); 1022511b41d2SMark Murray exit(1); 1023511b41d2SMark Murray } 1024511b41d2SMark Murray /* NOTREACHED */ 1025511b41d2SMark Murray } 1026511b41d2SMark Murray 1027511b41d2SMark Murray void 1028ae1f160dSDag-Erling Smørgrav usage(void) 1029511b41d2SMark Murray { 1030ae1f160dSDag-Erling Smørgrav (void) fprintf(stderr, 10311ec0d754SDag-Erling Smørgrav "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" 10321ec0d754SDag-Erling Smørgrav " [-l limit] [-o ssh_option] [-P port] [-S program]\n" 1033545d5ecaSDag-Erling Smørgrav " [[user@]host1:]file1 [...] [[user@]host2:]file2\n"); 1034511b41d2SMark Murray exit(1); 1035511b41d2SMark Murray } 1036511b41d2SMark Murray 1037511b41d2SMark Murray void 1038511b41d2SMark Murray run_err(const char *fmt,...) 1039511b41d2SMark Murray { 1040511b41d2SMark Murray static FILE *fp; 1041511b41d2SMark Murray va_list ap; 1042511b41d2SMark Murray 1043511b41d2SMark Murray ++errs; 1044511b41d2SMark Murray if (fp == NULL && !(fp = fdopen(remout, "w"))) 1045511b41d2SMark Murray return; 1046511b41d2SMark Murray (void) fprintf(fp, "%c", 0x01); 1047511b41d2SMark Murray (void) fprintf(fp, "scp: "); 1048ae1f160dSDag-Erling Smørgrav va_start(ap, fmt); 1049511b41d2SMark Murray (void) vfprintf(fp, fmt, ap); 1050ae1f160dSDag-Erling Smørgrav va_end(ap); 1051511b41d2SMark Murray (void) fprintf(fp, "\n"); 1052511b41d2SMark Murray (void) fflush(fp); 1053511b41d2SMark Murray 1054511b41d2SMark Murray if (!iamremote) { 1055ae1f160dSDag-Erling Smørgrav va_start(ap, fmt); 1056511b41d2SMark Murray vfprintf(stderr, fmt, ap); 1057ae1f160dSDag-Erling Smørgrav va_end(ap); 1058511b41d2SMark Murray fprintf(stderr, "\n"); 1059511b41d2SMark Murray } 1060511b41d2SMark Murray } 1061511b41d2SMark Murray 1062511b41d2SMark Murray void 1063cf2b5f3bSDag-Erling Smørgrav verifydir(char *cp) 1064511b41d2SMark Murray { 1065511b41d2SMark Murray struct stat stb; 1066511b41d2SMark Murray 1067511b41d2SMark Murray if (!stat(cp, &stb)) { 1068511b41d2SMark Murray if (S_ISDIR(stb.st_mode)) 1069511b41d2SMark Murray return; 1070511b41d2SMark Murray errno = ENOTDIR; 1071511b41d2SMark Murray } 1072511b41d2SMark Murray run_err("%s: %s", cp, strerror(errno)); 1073511b41d2SMark Murray exit(1); 1074511b41d2SMark Murray } 1075511b41d2SMark Murray 1076511b41d2SMark Murray int 1077cf2b5f3bSDag-Erling Smørgrav okname(char *cp0) 1078511b41d2SMark Murray { 1079511b41d2SMark Murray int c; 1080511b41d2SMark Murray char *cp; 1081511b41d2SMark Murray 1082511b41d2SMark Murray cp = cp0; 1083511b41d2SMark Murray do { 1084ae1f160dSDag-Erling Smørgrav c = (int)*cp; 1085511b41d2SMark Murray if (c & 0200) 1086511b41d2SMark Murray goto bad; 1087e73e9afaSDag-Erling Smørgrav if (!isalpha(c) && !isdigit(c)) { 1088e73e9afaSDag-Erling Smørgrav switch (c) { 1089e73e9afaSDag-Erling Smørgrav case '\'': 1090e73e9afaSDag-Erling Smørgrav case '"': 1091e73e9afaSDag-Erling Smørgrav case '`': 1092e73e9afaSDag-Erling Smørgrav case ' ': 1093e73e9afaSDag-Erling Smørgrav case '#': 1094511b41d2SMark Murray goto bad; 1095e73e9afaSDag-Erling Smørgrav default: 1096e73e9afaSDag-Erling Smørgrav break; 1097e73e9afaSDag-Erling Smørgrav } 1098e73e9afaSDag-Erling Smørgrav } 1099511b41d2SMark Murray } while (*++cp); 1100511b41d2SMark Murray return (1); 1101511b41d2SMark Murray 1102511b41d2SMark Murray bad: fprintf(stderr, "%s: invalid user name\n", cp0); 1103511b41d2SMark Murray return (0); 1104511b41d2SMark Murray } 1105511b41d2SMark Murray 1106511b41d2SMark Murray BUF * 1107cf2b5f3bSDag-Erling Smørgrav allocbuf(BUF *bp, int fd, int blksize) 1108511b41d2SMark Murray { 1109511b41d2SMark Murray size_t size; 111083d2307dSDag-Erling Smørgrav #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE 1111511b41d2SMark Murray struct stat stb; 1112511b41d2SMark Murray 1113511b41d2SMark Murray if (fstat(fd, &stb) < 0) { 1114511b41d2SMark Murray run_err("fstat: %s", strerror(errno)); 1115511b41d2SMark Murray return (0); 1116511b41d2SMark Murray } 1117b6fd52a0SDag-Erling Smørgrav size = roundup(stb.st_blksize, blksize); 1118e73e9afaSDag-Erling Smørgrav if (size == 0) 1119e73e9afaSDag-Erling Smørgrav size = blksize; 112083d2307dSDag-Erling Smørgrav #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 112183d2307dSDag-Erling Smørgrav size = blksize; 112283d2307dSDag-Erling Smørgrav #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 1123511b41d2SMark Murray if (bp->cnt >= size) 1124511b41d2SMark Murray return (bp); 1125511b41d2SMark Murray if (bp->buf == NULL) 1126511b41d2SMark Murray bp->buf = xmalloc(size); 1127511b41d2SMark Murray else 1128511b41d2SMark Murray bp->buf = xrealloc(bp->buf, size); 1129ae1f160dSDag-Erling Smørgrav memset(bp->buf, 0, size); 1130511b41d2SMark Murray bp->cnt = size; 1131511b41d2SMark Murray return (bp); 1132511b41d2SMark Murray } 1133511b41d2SMark Murray 1134511b41d2SMark Murray void 1135cf2b5f3bSDag-Erling Smørgrav lostconn(int signo) 1136511b41d2SMark Murray { 1137511b41d2SMark Murray if (!iamremote) 1138ae1f160dSDag-Erling Smørgrav write(STDERR_FILENO, "lost connection\n", 16); 1139ae1f160dSDag-Erling Smørgrav if (signo) 1140ae1f160dSDag-Erling Smørgrav _exit(1); 1141ae1f160dSDag-Erling Smørgrav else 1142511b41d2SMark Murray exit(1); 1143511b41d2SMark Murray } 1144