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" 741ec0d754SDag-Erling Smørgrav RCSID("$OpenBSD: scp.c,v 1.113 2003/11/23 23:21:21 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 #ifdef HAVE___PROGNAME 8483d2307dSDag-Erling Smørgrav extern char *__progname; 8583d2307dSDag-Erling Smørgrav #else 8683d2307dSDag-Erling Smørgrav char *__progname; 8783d2307dSDag-Erling Smørgrav #endif 8883d2307dSDag-Erling Smørgrav 89e73e9afaSDag-Erling Smørgrav void bwlimit(int); 90511b41d2SMark Murray 91ae1f160dSDag-Erling Smørgrav /* Struct for addargs */ 92ae1f160dSDag-Erling Smørgrav arglist args; 935b9b2fafSBrian Feldman 94e73e9afaSDag-Erling Smørgrav /* Bandwidth limit */ 951ec0d754SDag-Erling Smørgrav off_t limit_rate = 0; 96511b41d2SMark Murray 97511b41d2SMark Murray /* Name of current file being transferred. */ 98511b41d2SMark Murray char *curfile; 99511b41d2SMark Murray 100511b41d2SMark Murray /* This is set to non-zero to enable verbose mode. */ 101511b41d2SMark Murray int verbose_mode = 0; 102511b41d2SMark Murray 103511b41d2SMark Murray /* This is set to zero if the progressmeter is not desired. */ 104511b41d2SMark Murray int showprogress = 1; 105511b41d2SMark Murray 106b66f2d16SKris Kennaway /* This is the program to execute for the secured connection. ("ssh" or -S) */ 1071e8db6e2SBrian Feldman char *ssh_program = _PATH_SSH_PROGRAM; 108b66f2d16SKris Kennaway 109e73e9afaSDag-Erling Smørgrav /* This is used to store the pid of ssh_program */ 110cf2b5f3bSDag-Erling Smørgrav pid_t do_cmd_pid = -1; 111cf2b5f3bSDag-Erling Smørgrav 112cf2b5f3bSDag-Erling Smørgrav static void 113cf2b5f3bSDag-Erling Smørgrav killchild(int signo) 114cf2b5f3bSDag-Erling Smørgrav { 115cf2b5f3bSDag-Erling Smørgrav if (do_cmd_pid > 1) 116cf2b5f3bSDag-Erling Smørgrav kill(do_cmd_pid, signo); 117cf2b5f3bSDag-Erling Smørgrav 118cf2b5f3bSDag-Erling Smørgrav _exit(1); 119cf2b5f3bSDag-Erling Smørgrav } 120e73e9afaSDag-Erling Smørgrav 121511b41d2SMark Murray /* 122511b41d2SMark Murray * This function executes the given command as the specified user on the 123511b41d2SMark Murray * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 124511b41d2SMark Murray * assigns the input and output file descriptors on success. 125511b41d2SMark Murray */ 126511b41d2SMark Murray 127511b41d2SMark Murray int 128b66f2d16SKris Kennaway do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) 129511b41d2SMark Murray { 130511b41d2SMark Murray int pin[2], pout[2], reserved[2]; 131511b41d2SMark Murray 132511b41d2SMark Murray if (verbose_mode) 133ae1f160dSDag-Erling Smørgrav fprintf(stderr, 134ae1f160dSDag-Erling Smørgrav "Executing: program %s host %s, user %s, command %s\n", 135ae1f160dSDag-Erling Smørgrav ssh_program, host, 136ae1f160dSDag-Erling Smørgrav remuser ? remuser : "(unspecified)", cmd); 137511b41d2SMark Murray 138511b41d2SMark Murray /* 139511b41d2SMark Murray * Reserve two descriptors so that the real pipes won't get 140511b41d2SMark Murray * descriptors 0 and 1 because that will screw up dup2 below. 141511b41d2SMark Murray */ 142511b41d2SMark Murray pipe(reserved); 143511b41d2SMark Murray 144511b41d2SMark Murray /* Create a socket pair for communicating with ssh. */ 145511b41d2SMark Murray if (pipe(pin) < 0) 146511b41d2SMark Murray fatal("pipe: %s", strerror(errno)); 147511b41d2SMark Murray if (pipe(pout) < 0) 148511b41d2SMark Murray fatal("pipe: %s", strerror(errno)); 149511b41d2SMark Murray 150511b41d2SMark Murray /* Free the reserved descriptors. */ 151511b41d2SMark Murray close(reserved[0]); 152511b41d2SMark Murray close(reserved[1]); 153511b41d2SMark Murray 154cf2b5f3bSDag-Erling Smørgrav /* Fork a child to execute the command on the remote host using ssh. */ 155e73e9afaSDag-Erling Smørgrav do_cmd_pid = fork(); 156e73e9afaSDag-Erling Smørgrav if (do_cmd_pid == 0) { 157511b41d2SMark Murray /* Child. */ 158511b41d2SMark Murray close(pin[1]); 159511b41d2SMark Murray close(pout[0]); 160511b41d2SMark Murray dup2(pin[0], 0); 161511b41d2SMark Murray dup2(pout[1], 1); 162511b41d2SMark Murray close(pin[0]); 163511b41d2SMark Murray close(pout[1]); 164511b41d2SMark Murray 1655b9b2fafSBrian Feldman args.list[0] = ssh_program; 1665b9b2fafSBrian Feldman if (remuser != NULL) 167ae1f160dSDag-Erling Smørgrav addargs(&args, "-l%s", remuser); 168ae1f160dSDag-Erling Smørgrav addargs(&args, "%s", host); 169ae1f160dSDag-Erling Smørgrav addargs(&args, "%s", cmd); 170511b41d2SMark Murray 1715b9b2fafSBrian Feldman execvp(ssh_program, args.list); 172b66f2d16SKris Kennaway perror(ssh_program); 173511b41d2SMark Murray exit(1); 174e73e9afaSDag-Erling Smørgrav } else if (do_cmd_pid == -1) { 175e73e9afaSDag-Erling Smørgrav fatal("fork: %s", strerror(errno)); 176511b41d2SMark Murray } 177511b41d2SMark Murray /* Parent. Close the other side, and return the local side. */ 178511b41d2SMark Murray close(pin[0]); 179511b41d2SMark Murray *fdout = pin[1]; 180511b41d2SMark Murray close(pout[1]); 181511b41d2SMark Murray *fdin = pout[0]; 182cf2b5f3bSDag-Erling Smørgrav signal(SIGTERM, killchild); 183cf2b5f3bSDag-Erling Smørgrav signal(SIGINT, killchild); 184cf2b5f3bSDag-Erling Smørgrav signal(SIGHUP, killchild); 185511b41d2SMark Murray return 0; 186511b41d2SMark Murray } 187511b41d2SMark Murray 188511b41d2SMark Murray typedef struct { 189511b41d2SMark Murray int cnt; 190511b41d2SMark Murray char *buf; 191511b41d2SMark Murray } BUF; 192511b41d2SMark Murray 193511b41d2SMark Murray BUF *allocbuf(BUF *, int, int); 194511b41d2SMark Murray void lostconn(int); 195511b41d2SMark Murray void nospace(void); 196511b41d2SMark Murray int okname(char *); 197511b41d2SMark Murray void run_err(const char *,...); 198511b41d2SMark Murray void verifydir(char *); 199511b41d2SMark Murray 200511b41d2SMark Murray struct passwd *pwd; 201511b41d2SMark Murray uid_t userid; 202511b41d2SMark Murray int errs, remin, remout; 203511b41d2SMark Murray int pflag, iamremote, iamrecursive, targetshouldbedirectory; 204511b41d2SMark Murray 205511b41d2SMark Murray #define CMDNEEDS 64 206511b41d2SMark Murray char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 207511b41d2SMark Murray 208511b41d2SMark Murray int response(void); 209511b41d2SMark Murray void rsource(char *, struct stat *); 210511b41d2SMark Murray void sink(int, char *[]); 211511b41d2SMark Murray void source(int, char *[]); 212511b41d2SMark Murray void tolocal(int, char *[]); 213511b41d2SMark Murray void toremote(char *, int, char *[]); 214511b41d2SMark Murray void usage(void); 215511b41d2SMark Murray 216511b41d2SMark Murray int 217cf2b5f3bSDag-Erling Smørgrav main(int argc, char **argv) 218511b41d2SMark Murray { 219e73e9afaSDag-Erling Smørgrav int ch, fflag, tflag, status; 220e73e9afaSDag-Erling Smørgrav double speed; 221e73e9afaSDag-Erling Smørgrav char *targ, *endp; 222511b41d2SMark Murray extern char *optarg; 223511b41d2SMark Murray extern int optind; 224511b41d2SMark Murray 225cf2b5f3bSDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 22683d2307dSDag-Erling Smørgrav 2275b9b2fafSBrian Feldman args.list = NULL; 228ae1f160dSDag-Erling Smørgrav addargs(&args, "ssh"); /* overwritten with ssh_program */ 229ae1f160dSDag-Erling Smørgrav addargs(&args, "-x"); 230ae1f160dSDag-Erling Smørgrav addargs(&args, "-oForwardAgent no"); 231ae1f160dSDag-Erling Smørgrav addargs(&args, "-oClearAllForwardings yes"); 2325b9b2fafSBrian Feldman 233511b41d2SMark Murray fflag = tflag = 0; 234e73e9afaSDag-Erling Smørgrav while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1) 235511b41d2SMark Murray switch (ch) { 236511b41d2SMark Murray /* User-visible flags. */ 237e73e9afaSDag-Erling Smørgrav case '1': 238e73e9afaSDag-Erling Smørgrav case '2': 239511b41d2SMark Murray case '4': 240511b41d2SMark Murray case '6': 2415b9b2fafSBrian Feldman case 'C': 242ae1f160dSDag-Erling Smørgrav addargs(&args, "-%c", ch); 2435b9b2fafSBrian Feldman break; 2445b9b2fafSBrian Feldman case 'o': 2455b9b2fafSBrian Feldman case 'c': 2465b9b2fafSBrian Feldman case 'i': 247ae1f160dSDag-Erling Smørgrav case 'F': 248ae1f160dSDag-Erling Smørgrav addargs(&args, "-%c%s", ch, optarg); 2495b9b2fafSBrian Feldman break; 2505b9b2fafSBrian Feldman case 'P': 251ae1f160dSDag-Erling Smørgrav addargs(&args, "-p%s", optarg); 2525b9b2fafSBrian Feldman break; 2535b9b2fafSBrian Feldman case 'B': 254ae1f160dSDag-Erling Smørgrav addargs(&args, "-oBatchmode yes"); 255511b41d2SMark Murray break; 256e73e9afaSDag-Erling Smørgrav case 'l': 257e73e9afaSDag-Erling Smørgrav speed = strtod(optarg, &endp); 258e73e9afaSDag-Erling Smørgrav if (speed <= 0 || *endp != '\0') 259e73e9afaSDag-Erling Smørgrav usage(); 2601ec0d754SDag-Erling Smørgrav limit_rate = speed * 1024; 261e73e9afaSDag-Erling Smørgrav break; 262511b41d2SMark Murray case 'p': 263511b41d2SMark Murray pflag = 1; 264511b41d2SMark Murray break; 265511b41d2SMark Murray case 'r': 266511b41d2SMark Murray iamrecursive = 1; 267511b41d2SMark Murray break; 268b66f2d16SKris Kennaway case 'S': 2695b9b2fafSBrian Feldman ssh_program = xstrdup(optarg); 2705b9b2fafSBrian Feldman break; 2715b9b2fafSBrian Feldman case 'v': 272ae1f160dSDag-Erling Smørgrav addargs(&args, "-v"); 2735b9b2fafSBrian Feldman verbose_mode = 1; 2745b9b2fafSBrian Feldman break; 2755b9b2fafSBrian Feldman case 'q': 2761ec0d754SDag-Erling Smørgrav addargs(&args, "-q"); 2775b9b2fafSBrian Feldman showprogress = 0; 278b66f2d16SKris Kennaway break; 279b66f2d16SKris Kennaway 280511b41d2SMark Murray /* Server options. */ 281511b41d2SMark Murray case 'd': 282511b41d2SMark Murray targetshouldbedirectory = 1; 283511b41d2SMark Murray break; 284511b41d2SMark Murray case 'f': /* "from" */ 285511b41d2SMark Murray iamremote = 1; 286511b41d2SMark Murray fflag = 1; 287511b41d2SMark Murray break; 288511b41d2SMark Murray case 't': /* "to" */ 289511b41d2SMark Murray iamremote = 1; 290511b41d2SMark Murray tflag = 1; 29183d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 29283d2307dSDag-Erling Smørgrav setmode(0, O_BINARY); 29383d2307dSDag-Erling Smørgrav #endif 294511b41d2SMark Murray break; 295511b41d2SMark Murray default: 296511b41d2SMark Murray usage(); 297511b41d2SMark Murray } 298511b41d2SMark Murray argc -= optind; 299511b41d2SMark Murray argv += optind; 300511b41d2SMark Murray 301511b41d2SMark Murray if ((pwd = getpwuid(userid = getuid())) == NULL) 302cf2b5f3bSDag-Erling Smørgrav fatal("unknown user %u", (u_int) userid); 303511b41d2SMark Murray 304511b41d2SMark Murray if (!isatty(STDERR_FILENO)) 305511b41d2SMark Murray showprogress = 0; 306511b41d2SMark Murray 307511b41d2SMark Murray remin = STDIN_FILENO; 308511b41d2SMark Murray remout = STDOUT_FILENO; 309511b41d2SMark Murray 310511b41d2SMark Murray if (fflag) { 311511b41d2SMark Murray /* Follow "protocol", send data. */ 312511b41d2SMark Murray (void) response(); 313511b41d2SMark Murray source(argc, argv); 314511b41d2SMark Murray exit(errs != 0); 315511b41d2SMark Murray } 316511b41d2SMark Murray if (tflag) { 317511b41d2SMark Murray /* Receive data. */ 318511b41d2SMark Murray sink(argc, argv); 319511b41d2SMark Murray exit(errs != 0); 320511b41d2SMark Murray } 321511b41d2SMark Murray if (argc < 2) 322511b41d2SMark Murray usage(); 323511b41d2SMark Murray if (argc > 2) 324511b41d2SMark Murray targetshouldbedirectory = 1; 325511b41d2SMark Murray 326511b41d2SMark Murray remin = remout = -1; 327e73e9afaSDag-Erling Smørgrav do_cmd_pid = -1; 328511b41d2SMark Murray /* Command to be executed on remote system using "ssh". */ 3291e8db6e2SBrian Feldman (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", 3301e8db6e2SBrian Feldman verbose_mode ? " -v" : "", 331511b41d2SMark Murray iamrecursive ? " -r" : "", pflag ? " -p" : "", 332511b41d2SMark Murray targetshouldbedirectory ? " -d" : ""); 333511b41d2SMark Murray 334511b41d2SMark Murray (void) signal(SIGPIPE, lostconn); 335511b41d2SMark Murray 336511b41d2SMark Murray if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 337511b41d2SMark Murray toremote(targ, argc, argv); 338511b41d2SMark Murray else { 339511b41d2SMark Murray tolocal(argc, argv); /* Dest is local host. */ 340511b41d2SMark Murray if (targetshouldbedirectory) 341511b41d2SMark Murray verifydir(argv[argc - 1]); 342511b41d2SMark Murray } 343e73e9afaSDag-Erling Smørgrav /* 344e73e9afaSDag-Erling Smørgrav * Finally check the exit status of the ssh process, if one was forked 345e73e9afaSDag-Erling Smørgrav * and no error has occured yet 346e73e9afaSDag-Erling Smørgrav */ 347e73e9afaSDag-Erling Smørgrav if (do_cmd_pid != -1 && errs == 0) { 348e73e9afaSDag-Erling Smørgrav if (remin != -1) 349e73e9afaSDag-Erling Smørgrav (void) close(remin); 350e73e9afaSDag-Erling Smørgrav if (remout != -1) 351e73e9afaSDag-Erling Smørgrav (void) close(remout); 352e73e9afaSDag-Erling Smørgrav if (waitpid(do_cmd_pid, &status, 0) == -1) 353e73e9afaSDag-Erling Smørgrav errs = 1; 354e73e9afaSDag-Erling Smørgrav else { 355e73e9afaSDag-Erling Smørgrav if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 356e73e9afaSDag-Erling Smørgrav errs = 1; 357e73e9afaSDag-Erling Smørgrav } 358e73e9afaSDag-Erling Smørgrav } 359511b41d2SMark Murray exit(errs != 0); 360511b41d2SMark Murray } 361511b41d2SMark Murray 362511b41d2SMark Murray void 363cf2b5f3bSDag-Erling Smørgrav toremote(char *targ, int argc, char **argv) 364511b41d2SMark Murray { 365511b41d2SMark Murray int i, len; 366511b41d2SMark Murray char *bp, *host, *src, *suser, *thost, *tuser; 367511b41d2SMark Murray 368511b41d2SMark Murray *targ++ = 0; 369511b41d2SMark Murray if (*targ == 0) 370511b41d2SMark Murray targ = "."; 371511b41d2SMark Murray 372e73e9afaSDag-Erling Smørgrav if ((thost = strrchr(argv[argc - 1], '@'))) { 373511b41d2SMark Murray /* user@host */ 374511b41d2SMark Murray *thost++ = 0; 375511b41d2SMark Murray tuser = argv[argc - 1]; 376511b41d2SMark Murray if (*tuser == '\0') 377511b41d2SMark Murray tuser = NULL; 378511b41d2SMark Murray } else { 379511b41d2SMark Murray thost = argv[argc - 1]; 380511b41d2SMark Murray tuser = NULL; 381511b41d2SMark Murray } 382511b41d2SMark Murray 383511b41d2SMark Murray for (i = 0; i < argc - 1; i++) { 384511b41d2SMark Murray src = colon(argv[i]); 385511b41d2SMark Murray if (src) { /* remote to remote */ 386ae1f160dSDag-Erling Smørgrav static char *ssh_options = 387545d5ecaSDag-Erling Smørgrav "-x -o'ClearAllForwardings yes'"; 388511b41d2SMark Murray *src++ = 0; 389511b41d2SMark Murray if (*src == 0) 390511b41d2SMark Murray src = "."; 391e73e9afaSDag-Erling Smørgrav host = strrchr(argv[i], '@'); 392b66f2d16SKris Kennaway len = strlen(ssh_program) + strlen(argv[i]) + 393511b41d2SMark Murray strlen(src) + (tuser ? strlen(tuser) : 0) + 394ae1f160dSDag-Erling Smørgrav strlen(thost) + strlen(targ) + 395ae1f160dSDag-Erling Smørgrav strlen(ssh_options) + CMDNEEDS + 20; 396511b41d2SMark Murray bp = xmalloc(len); 397511b41d2SMark Murray if (host) { 398511b41d2SMark Murray *host++ = 0; 399511b41d2SMark Murray host = cleanhostname(host); 400511b41d2SMark Murray suser = argv[i]; 401511b41d2SMark Murray if (*suser == '\0') 402511b41d2SMark Murray suser = pwd->pw_name; 403e73e9afaSDag-Erling Smørgrav else if (!okname(suser)) { 404e73e9afaSDag-Erling Smørgrav xfree(bp); 405511b41d2SMark Murray continue; 406e73e9afaSDag-Erling Smørgrav } 407e73e9afaSDag-Erling Smørgrav if (tuser && !okname(tuser)) { 408e73e9afaSDag-Erling Smørgrav xfree(bp); 409e73e9afaSDag-Erling Smørgrav continue; 410e73e9afaSDag-Erling Smørgrav } 4111e8db6e2SBrian Feldman snprintf(bp, len, 412ae1f160dSDag-Erling Smørgrav "%s%s %s -n " 4131e8db6e2SBrian Feldman "-l %s %s %s %s '%s%s%s:%s'", 414b66f2d16SKris Kennaway ssh_program, verbose_mode ? " -v" : "", 415ae1f160dSDag-Erling Smørgrav ssh_options, suser, host, cmd, src, 416511b41d2SMark Murray tuser ? tuser : "", tuser ? "@" : "", 417511b41d2SMark Murray thost, targ); 418511b41d2SMark Murray } else { 419511b41d2SMark Murray host = cleanhostname(argv[i]); 4201e8db6e2SBrian Feldman snprintf(bp, len, 421ae1f160dSDag-Erling Smørgrav "exec %s%s %s -n %s " 4221e8db6e2SBrian Feldman "%s %s '%s%s%s:%s'", 423b66f2d16SKris Kennaway ssh_program, verbose_mode ? " -v" : "", 424ae1f160dSDag-Erling Smørgrav ssh_options, host, cmd, src, 425511b41d2SMark Murray tuser ? tuser : "", tuser ? "@" : "", 426511b41d2SMark Murray thost, targ); 427511b41d2SMark Murray } 428511b41d2SMark Murray if (verbose_mode) 429511b41d2SMark Murray fprintf(stderr, "Executing: %s\n", bp); 4301ec0d754SDag-Erling Smørgrav if (system(bp) != 0) 4311ec0d754SDag-Erling Smørgrav errs = 1; 432511b41d2SMark Murray (void) xfree(bp); 433511b41d2SMark Murray } else { /* local to remote */ 434511b41d2SMark Murray if (remin == -1) { 435511b41d2SMark Murray len = strlen(targ) + CMDNEEDS + 20; 436511b41d2SMark Murray bp = xmalloc(len); 4371e8db6e2SBrian Feldman (void) snprintf(bp, len, "%s -t %s", cmd, targ); 438511b41d2SMark Murray host = cleanhostname(thost); 439b66f2d16SKris Kennaway if (do_cmd(host, tuser, bp, &remin, 440b66f2d16SKris Kennaway &remout, argc) < 0) 441511b41d2SMark Murray exit(1); 442511b41d2SMark Murray if (response() < 0) 443511b41d2SMark Murray exit(1); 444511b41d2SMark Murray (void) xfree(bp); 445511b41d2SMark Murray } 446511b41d2SMark Murray source(1, argv + i); 447511b41d2SMark Murray } 448511b41d2SMark Murray } 449511b41d2SMark Murray } 450511b41d2SMark Murray 451511b41d2SMark Murray void 452cf2b5f3bSDag-Erling Smørgrav tolocal(int argc, char **argv) 453511b41d2SMark Murray { 454511b41d2SMark Murray int i, len; 455511b41d2SMark Murray char *bp, *host, *src, *suser; 456511b41d2SMark Murray 457511b41d2SMark Murray for (i = 0; i < argc - 1; i++) { 458511b41d2SMark Murray if (!(src = colon(argv[i]))) { /* Local to local. */ 459511b41d2SMark Murray len = strlen(_PATH_CP) + strlen(argv[i]) + 460511b41d2SMark Murray strlen(argv[argc - 1]) + 20; 461511b41d2SMark Murray bp = xmalloc(len); 4621e8db6e2SBrian Feldman (void) snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, 463511b41d2SMark Murray iamrecursive ? " -r" : "", pflag ? " -p" : "", 464511b41d2SMark Murray argv[i], argv[argc - 1]); 465511b41d2SMark Murray if (verbose_mode) 466511b41d2SMark Murray fprintf(stderr, "Executing: %s\n", bp); 467511b41d2SMark Murray if (system(bp)) 468511b41d2SMark Murray ++errs; 469511b41d2SMark Murray (void) xfree(bp); 470511b41d2SMark Murray continue; 471511b41d2SMark Murray } 472511b41d2SMark Murray *src++ = 0; 473511b41d2SMark Murray if (*src == 0) 474511b41d2SMark Murray src = "."; 475e73e9afaSDag-Erling Smørgrav if ((host = strrchr(argv[i], '@')) == NULL) { 476511b41d2SMark Murray host = argv[i]; 477511b41d2SMark Murray suser = NULL; 478511b41d2SMark Murray } else { 479511b41d2SMark Murray *host++ = 0; 480511b41d2SMark Murray suser = argv[i]; 481511b41d2SMark Murray if (*suser == '\0') 482511b41d2SMark Murray suser = pwd->pw_name; 483511b41d2SMark Murray } 484511b41d2SMark Murray host = cleanhostname(host); 485511b41d2SMark Murray len = strlen(src) + CMDNEEDS + 20; 486511b41d2SMark Murray bp = xmalloc(len); 4871e8db6e2SBrian Feldman (void) snprintf(bp, len, "%s -f %s", cmd, src); 488b66f2d16SKris Kennaway if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) { 489511b41d2SMark Murray (void) xfree(bp); 490511b41d2SMark Murray ++errs; 491511b41d2SMark Murray continue; 492511b41d2SMark Murray } 493511b41d2SMark Murray xfree(bp); 494511b41d2SMark Murray sink(1, argv + argc - 1); 495511b41d2SMark Murray (void) close(remin); 496511b41d2SMark Murray remin = remout = -1; 497511b41d2SMark Murray } 498511b41d2SMark Murray } 499511b41d2SMark Murray 500511b41d2SMark Murray void 501cf2b5f3bSDag-Erling Smørgrav source(int argc, char **argv) 502511b41d2SMark Murray { 503511b41d2SMark Murray struct stat stb; 504511b41d2SMark Murray static BUF buffer; 505511b41d2SMark Murray BUF *bp; 506e73e9afaSDag-Erling Smørgrav off_t i, amt, result, statbytes; 5071e8db6e2SBrian Feldman int fd, haderr, indx; 508511b41d2SMark Murray char *last, *name, buf[2048]; 5091e8db6e2SBrian Feldman int len; 510511b41d2SMark Murray 511511b41d2SMark Murray for (indx = 0; indx < argc; ++indx) { 512511b41d2SMark Murray name = argv[indx]; 513511b41d2SMark Murray statbytes = 0; 5141e8db6e2SBrian Feldman len = strlen(name); 5151e8db6e2SBrian Feldman while (len > 1 && name[len-1] == '/') 5161e8db6e2SBrian Feldman name[--len] = '\0'; 517ae1f160dSDag-Erling Smørgrav if (strchr(name, '\n') != NULL) { 518ae1f160dSDag-Erling Smørgrav run_err("%s: skipping, filename contains a newline", 519ae1f160dSDag-Erling Smørgrav name); 520ae1f160dSDag-Erling Smørgrav goto next; 521ae1f160dSDag-Erling Smørgrav } 522511b41d2SMark Murray if ((fd = open(name, O_RDONLY, 0)) < 0) 523511b41d2SMark Murray goto syserr; 524511b41d2SMark Murray if (fstat(fd, &stb) < 0) { 525511b41d2SMark Murray syserr: run_err("%s: %s", name, strerror(errno)); 526511b41d2SMark Murray goto next; 527511b41d2SMark Murray } 528511b41d2SMark Murray switch (stb.st_mode & S_IFMT) { 529511b41d2SMark Murray case S_IFREG: 530511b41d2SMark Murray break; 531511b41d2SMark Murray case S_IFDIR: 532511b41d2SMark Murray if (iamrecursive) { 533511b41d2SMark Murray rsource(name, &stb); 534511b41d2SMark Murray goto next; 535511b41d2SMark Murray } 536511b41d2SMark Murray /* FALLTHROUGH */ 537511b41d2SMark Murray default: 538511b41d2SMark Murray run_err("%s: not a regular file", name); 539511b41d2SMark Murray goto next; 540511b41d2SMark Murray } 541511b41d2SMark Murray if ((last = strrchr(name, '/')) == NULL) 542511b41d2SMark Murray last = name; 543511b41d2SMark Murray else 544511b41d2SMark Murray ++last; 545511b41d2SMark Murray curfile = last; 546511b41d2SMark Murray if (pflag) { 547511b41d2SMark Murray /* 548511b41d2SMark Murray * Make it compatible with possible future 549511b41d2SMark Murray * versions expecting microseconds. 550511b41d2SMark Murray */ 5511e8db6e2SBrian Feldman (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", 5521e8db6e2SBrian Feldman (u_long) stb.st_mtime, 5531e8db6e2SBrian Feldman (u_long) stb.st_atime); 554cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, buf, strlen(buf)); 555511b41d2SMark Murray if (response() < 0) 556511b41d2SMark Murray goto next; 557511b41d2SMark Murray } 558511b41d2SMark Murray #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 5591e8db6e2SBrian Feldman snprintf(buf, sizeof buf, "C%04o %lld %s\n", 5601e8db6e2SBrian Feldman (u_int) (stb.st_mode & FILEMODEMASK), 561cf2b5f3bSDag-Erling Smørgrav (int64_t)stb.st_size, last); 562511b41d2SMark Murray if (verbose_mode) { 563511b41d2SMark Murray fprintf(stderr, "Sending file modes: %s", buf); 564511b41d2SMark Murray } 565cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, buf, strlen(buf)); 566511b41d2SMark Murray if (response() < 0) 567511b41d2SMark Murray goto next; 568511b41d2SMark Murray if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { 569511b41d2SMark Murray next: (void) close(fd); 570511b41d2SMark Murray continue; 571511b41d2SMark Murray } 572e73e9afaSDag-Erling Smørgrav if (showprogress) 573e73e9afaSDag-Erling Smørgrav start_progress_meter(curfile, stb.st_size, &statbytes); 574511b41d2SMark Murray /* Keep writing after an error so that we stay sync'd up. */ 575511b41d2SMark Murray for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 576511b41d2SMark Murray amt = bp->cnt; 577511b41d2SMark Murray if (i + amt > stb.st_size) 578511b41d2SMark Murray amt = stb.st_size - i; 579511b41d2SMark Murray if (!haderr) { 580a04a10f8SKris Kennaway result = atomicio(read, fd, bp->buf, amt); 581511b41d2SMark Murray if (result != amt) 582511b41d2SMark Murray haderr = result >= 0 ? EIO : errno; 583511b41d2SMark Murray } 584511b41d2SMark Murray if (haderr) 585cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, bp->buf, amt); 586511b41d2SMark Murray else { 587cf2b5f3bSDag-Erling Smørgrav result = atomicio(vwrite, remout, bp->buf, amt); 588511b41d2SMark Murray if (result != amt) 589511b41d2SMark Murray haderr = result >= 0 ? EIO : errno; 590511b41d2SMark Murray statbytes += result; 591511b41d2SMark Murray } 5921ec0d754SDag-Erling Smørgrav if (limit_rate) 593e73e9afaSDag-Erling Smørgrav bwlimit(amt); 594511b41d2SMark Murray } 595511b41d2SMark Murray if (showprogress) 596e73e9afaSDag-Erling Smørgrav stop_progress_meter(); 597511b41d2SMark Murray 598511b41d2SMark Murray if (close(fd) < 0 && !haderr) 599511b41d2SMark Murray haderr = errno; 600511b41d2SMark Murray if (!haderr) 601cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 602511b41d2SMark Murray else 603511b41d2SMark Murray run_err("%s: %s", name, strerror(haderr)); 604511b41d2SMark Murray (void) response(); 605511b41d2SMark Murray } 606511b41d2SMark Murray } 607511b41d2SMark Murray 608511b41d2SMark Murray void 609cf2b5f3bSDag-Erling Smørgrav rsource(char *name, struct stat *statp) 610511b41d2SMark Murray { 611511b41d2SMark Murray DIR *dirp; 612511b41d2SMark Murray struct dirent *dp; 613511b41d2SMark Murray char *last, *vect[1], path[1100]; 614511b41d2SMark Murray 615511b41d2SMark Murray if (!(dirp = opendir(name))) { 616511b41d2SMark Murray run_err("%s: %s", name, strerror(errno)); 617511b41d2SMark Murray return; 618511b41d2SMark Murray } 619511b41d2SMark Murray last = strrchr(name, '/'); 620511b41d2SMark Murray if (last == 0) 621511b41d2SMark Murray last = name; 622511b41d2SMark Murray else 623511b41d2SMark Murray last++; 624511b41d2SMark Murray if (pflag) { 6251e8db6e2SBrian Feldman (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", 6261e8db6e2SBrian Feldman (u_long) statp->st_mtime, 6271e8db6e2SBrian Feldman (u_long) statp->st_atime); 628cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, path, strlen(path)); 629511b41d2SMark Murray if (response() < 0) { 630511b41d2SMark Murray closedir(dirp); 631511b41d2SMark Murray return; 632511b41d2SMark Murray } 633511b41d2SMark Murray } 6341e8db6e2SBrian Feldman (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", 6351e8db6e2SBrian Feldman (u_int) (statp->st_mode & FILEMODEMASK), 0, last); 636511b41d2SMark Murray if (verbose_mode) 637511b41d2SMark Murray fprintf(stderr, "Entering directory: %s", path); 638cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, path, strlen(path)); 639511b41d2SMark Murray if (response() < 0) { 640511b41d2SMark Murray closedir(dirp); 641511b41d2SMark Murray return; 642511b41d2SMark Murray } 6431e8db6e2SBrian Feldman while ((dp = readdir(dirp)) != NULL) { 644511b41d2SMark Murray if (dp->d_ino == 0) 645511b41d2SMark Murray continue; 646511b41d2SMark Murray if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 647511b41d2SMark Murray continue; 648511b41d2SMark Murray if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { 649511b41d2SMark Murray run_err("%s/%s: name too long", name, dp->d_name); 650511b41d2SMark Murray continue; 651511b41d2SMark Murray } 6521e8db6e2SBrian Feldman (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); 653511b41d2SMark Murray vect[0] = path; 654511b41d2SMark Murray source(1, vect); 655511b41d2SMark Murray } 656511b41d2SMark Murray (void) closedir(dirp); 657cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "E\n", 2); 658511b41d2SMark Murray (void) response(); 659511b41d2SMark Murray } 660511b41d2SMark Murray 661511b41d2SMark Murray void 662e73e9afaSDag-Erling Smørgrav bwlimit(int amount) 663e73e9afaSDag-Erling Smørgrav { 664e73e9afaSDag-Erling Smørgrav static struct timeval bwstart, bwend; 665e73e9afaSDag-Erling Smørgrav static int lamt, thresh = 16384; 666e73e9afaSDag-Erling Smørgrav u_int64_t wait; 667e73e9afaSDag-Erling Smørgrav struct timespec ts, rm; 668e73e9afaSDag-Erling Smørgrav 669e73e9afaSDag-Erling Smørgrav if (!timerisset(&bwstart)) { 670e73e9afaSDag-Erling Smørgrav gettimeofday(&bwstart, NULL); 671e73e9afaSDag-Erling Smørgrav return; 672e73e9afaSDag-Erling Smørgrav } 673e73e9afaSDag-Erling Smørgrav 674e73e9afaSDag-Erling Smørgrav lamt += amount; 675e73e9afaSDag-Erling Smørgrav if (lamt < thresh) 676e73e9afaSDag-Erling Smørgrav return; 677e73e9afaSDag-Erling Smørgrav 678e73e9afaSDag-Erling Smørgrav gettimeofday(&bwend, NULL); 679e73e9afaSDag-Erling Smørgrav timersub(&bwend, &bwstart, &bwend); 680e73e9afaSDag-Erling Smørgrav if (!timerisset(&bwend)) 681e73e9afaSDag-Erling Smørgrav return; 682e73e9afaSDag-Erling Smørgrav 683e73e9afaSDag-Erling Smørgrav lamt *= 8; 6841ec0d754SDag-Erling Smørgrav wait = (double)1000000L * lamt / limit_rate; 685e73e9afaSDag-Erling Smørgrav 686e73e9afaSDag-Erling Smørgrav bwstart.tv_sec = wait / 1000000L; 687e73e9afaSDag-Erling Smørgrav bwstart.tv_usec = wait % 1000000L; 688e73e9afaSDag-Erling Smørgrav 689e73e9afaSDag-Erling Smørgrav if (timercmp(&bwstart, &bwend, >)) { 690e73e9afaSDag-Erling Smørgrav timersub(&bwstart, &bwend, &bwend); 691e73e9afaSDag-Erling Smørgrav 692e73e9afaSDag-Erling Smørgrav /* Adjust the wait time */ 693e73e9afaSDag-Erling Smørgrav if (bwend.tv_sec) { 694e73e9afaSDag-Erling Smørgrav thresh /= 2; 695e73e9afaSDag-Erling Smørgrav if (thresh < 2048) 696e73e9afaSDag-Erling Smørgrav thresh = 2048; 697e73e9afaSDag-Erling Smørgrav } else if (bwend.tv_usec < 100) { 698e73e9afaSDag-Erling Smørgrav thresh *= 2; 699e73e9afaSDag-Erling Smørgrav if (thresh > 32768) 700e73e9afaSDag-Erling Smørgrav thresh = 32768; 701e73e9afaSDag-Erling Smørgrav } 702e73e9afaSDag-Erling Smørgrav 703e73e9afaSDag-Erling Smørgrav TIMEVAL_TO_TIMESPEC(&bwend, &ts); 704e73e9afaSDag-Erling Smørgrav while (nanosleep(&ts, &rm) == -1) { 705e73e9afaSDag-Erling Smørgrav if (errno != EINTR) 706e73e9afaSDag-Erling Smørgrav break; 707e73e9afaSDag-Erling Smørgrav ts = rm; 708e73e9afaSDag-Erling Smørgrav } 709e73e9afaSDag-Erling Smørgrav } 710e73e9afaSDag-Erling Smørgrav 711e73e9afaSDag-Erling Smørgrav lamt = 0; 712e73e9afaSDag-Erling Smørgrav gettimeofday(&bwstart, NULL); 713e73e9afaSDag-Erling Smørgrav } 714e73e9afaSDag-Erling Smørgrav 715e73e9afaSDag-Erling Smørgrav void 716cf2b5f3bSDag-Erling Smørgrav sink(int argc, char **argv) 717511b41d2SMark Murray { 718511b41d2SMark Murray static BUF buffer; 719511b41d2SMark Murray struct stat stb; 720511b41d2SMark Murray enum { 721511b41d2SMark Murray YES, NO, DISPLAYED 722511b41d2SMark Murray } wrerr; 723511b41d2SMark Murray BUF *bp; 724511b41d2SMark Murray off_t i, j; 725511b41d2SMark Murray int amt, count, exists, first, mask, mode, ofd, omode; 726e73e9afaSDag-Erling Smørgrav off_t size, statbytes; 727b66f2d16SKris Kennaway int setimes, targisdir, wrerrno = 0; 728511b41d2SMark Murray char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; 7295b9b2fafSBrian Feldman struct timeval tv[2]; 730511b41d2SMark Murray 7311e8db6e2SBrian Feldman #define atime tv[0] 7321e8db6e2SBrian Feldman #define mtime tv[1] 733ae1f160dSDag-Erling Smørgrav #define SCREWUP(str) do { why = str; goto screwup; } while (0) 734511b41d2SMark Murray 735511b41d2SMark Murray setimes = targisdir = 0; 736511b41d2SMark Murray mask = umask(0); 737511b41d2SMark Murray if (!pflag) 738511b41d2SMark Murray (void) umask(mask); 739511b41d2SMark Murray if (argc != 1) { 740511b41d2SMark Murray run_err("ambiguous target"); 741511b41d2SMark Murray exit(1); 742511b41d2SMark Murray } 743511b41d2SMark Murray targ = *argv; 744511b41d2SMark Murray if (targetshouldbedirectory) 745511b41d2SMark Murray verifydir(targ); 746511b41d2SMark Murray 747cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 748511b41d2SMark Murray if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 749511b41d2SMark Murray targisdir = 1; 750511b41d2SMark Murray for (first = 1;; first = 0) { 751511b41d2SMark Murray cp = buf; 752a04a10f8SKris Kennaway if (atomicio(read, remin, cp, 1) <= 0) 753511b41d2SMark Murray return; 754511b41d2SMark Murray if (*cp++ == '\n') 755511b41d2SMark Murray SCREWUP("unexpected <newline>"); 756511b41d2SMark Murray do { 757a04a10f8SKris Kennaway if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 758511b41d2SMark Murray SCREWUP("lost connection"); 759511b41d2SMark Murray *cp++ = ch; 760511b41d2SMark Murray } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 761511b41d2SMark Murray *cp = 0; 762511b41d2SMark Murray 763511b41d2SMark Murray if (buf[0] == '\01' || buf[0] == '\02') { 764511b41d2SMark Murray if (iamremote == 0) 765cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, STDERR_FILENO, 766511b41d2SMark Murray buf + 1, strlen(buf + 1)); 767511b41d2SMark Murray if (buf[0] == '\02') 768511b41d2SMark Murray exit(1); 769511b41d2SMark Murray ++errs; 770511b41d2SMark Murray continue; 771511b41d2SMark Murray } 772511b41d2SMark Murray if (buf[0] == 'E') { 773cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 774511b41d2SMark Murray return; 775511b41d2SMark Murray } 776511b41d2SMark Murray if (ch == '\n') 777511b41d2SMark Murray *--cp = 0; 778511b41d2SMark Murray 779511b41d2SMark Murray cp = buf; 780511b41d2SMark Murray if (*cp == 'T') { 781511b41d2SMark Murray setimes++; 782511b41d2SMark Murray cp++; 7831e8db6e2SBrian Feldman mtime.tv_sec = strtol(cp, &cp, 10); 7841e8db6e2SBrian Feldman if (!cp || *cp++ != ' ') 785511b41d2SMark Murray SCREWUP("mtime.sec not delimited"); 7861e8db6e2SBrian Feldman mtime.tv_usec = strtol(cp, &cp, 10); 7871e8db6e2SBrian Feldman if (!cp || *cp++ != ' ') 788511b41d2SMark Murray SCREWUP("mtime.usec not delimited"); 7891e8db6e2SBrian Feldman atime.tv_sec = strtol(cp, &cp, 10); 7901e8db6e2SBrian Feldman if (!cp || *cp++ != ' ') 791511b41d2SMark Murray SCREWUP("atime.sec not delimited"); 7921e8db6e2SBrian Feldman atime.tv_usec = strtol(cp, &cp, 10); 7931e8db6e2SBrian Feldman if (!cp || *cp++ != '\0') 794511b41d2SMark Murray SCREWUP("atime.usec not delimited"); 795cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 796511b41d2SMark Murray continue; 797511b41d2SMark Murray } 798511b41d2SMark Murray if (*cp != 'C' && *cp != 'D') { 799511b41d2SMark Murray /* 800511b41d2SMark Murray * Check for the case "rcp remote:foo\* local:bar". 801511b41d2SMark Murray * In this case, the line "No match." can be returned 802511b41d2SMark Murray * by the shell before the rcp command on the remote is 803511b41d2SMark Murray * executed so the ^Aerror_message convention isn't 804511b41d2SMark Murray * followed. 805511b41d2SMark Murray */ 806511b41d2SMark Murray if (first) { 807511b41d2SMark Murray run_err("%s", cp); 808511b41d2SMark Murray exit(1); 809511b41d2SMark Murray } 810511b41d2SMark Murray SCREWUP("expected control record"); 811511b41d2SMark Murray } 812511b41d2SMark Murray mode = 0; 813511b41d2SMark Murray for (++cp; cp < buf + 5; cp++) { 814511b41d2SMark Murray if (*cp < '0' || *cp > '7') 815511b41d2SMark Murray SCREWUP("bad mode"); 816511b41d2SMark Murray mode = (mode << 3) | (*cp - '0'); 817511b41d2SMark Murray } 818511b41d2SMark Murray if (*cp++ != ' ') 819511b41d2SMark Murray SCREWUP("mode not delimited"); 820511b41d2SMark Murray 8211e8db6e2SBrian Feldman for (size = 0; isdigit(*cp);) 822511b41d2SMark Murray size = size * 10 + (*cp++ - '0'); 823511b41d2SMark Murray if (*cp++ != ' ') 824511b41d2SMark Murray SCREWUP("size not delimited"); 825511b41d2SMark Murray if (targisdir) { 826511b41d2SMark Murray static char *namebuf; 827511b41d2SMark Murray static int cursize; 828511b41d2SMark Murray size_t need; 829511b41d2SMark Murray 830511b41d2SMark Murray need = strlen(targ) + strlen(cp) + 250; 8311e8db6e2SBrian Feldman if (need > cursize) { 8321e8db6e2SBrian Feldman if (namebuf) 8331e8db6e2SBrian Feldman xfree(namebuf); 834511b41d2SMark Murray namebuf = xmalloc(need); 8351e8db6e2SBrian Feldman cursize = need; 8361e8db6e2SBrian Feldman } 8371e8db6e2SBrian Feldman (void) snprintf(namebuf, need, "%s%s%s", targ, 838545d5ecaSDag-Erling Smørgrav strcmp(targ, "/") ? "/" : "", cp); 839511b41d2SMark Murray np = namebuf; 840511b41d2SMark Murray } else 841511b41d2SMark Murray np = targ; 842511b41d2SMark Murray curfile = cp; 843511b41d2SMark Murray exists = stat(np, &stb) == 0; 844511b41d2SMark Murray if (buf[0] == 'D') { 845511b41d2SMark Murray int mod_flag = pflag; 846511b41d2SMark Murray if (exists) { 847511b41d2SMark Murray if (!S_ISDIR(stb.st_mode)) { 848511b41d2SMark Murray errno = ENOTDIR; 849511b41d2SMark Murray goto bad; 850511b41d2SMark Murray } 851511b41d2SMark Murray if (pflag) 852511b41d2SMark Murray (void) chmod(np, mode); 853511b41d2SMark Murray } else { 854511b41d2SMark Murray /* Handle copying from a read-only 855511b41d2SMark Murray directory */ 856511b41d2SMark Murray mod_flag = 1; 857511b41d2SMark Murray if (mkdir(np, mode | S_IRWXU) < 0) 858511b41d2SMark Murray goto bad; 859511b41d2SMark Murray } 8601e8db6e2SBrian Feldman vect[0] = xstrdup(np); 861511b41d2SMark Murray sink(1, vect); 862511b41d2SMark Murray if (setimes) { 863511b41d2SMark Murray setimes = 0; 8641e8db6e2SBrian Feldman if (utimes(vect[0], tv) < 0) 865511b41d2SMark Murray run_err("%s: set times: %s", 8661e8db6e2SBrian Feldman vect[0], strerror(errno)); 867511b41d2SMark Murray } 868511b41d2SMark Murray if (mod_flag) 8691e8db6e2SBrian Feldman (void) chmod(vect[0], mode); 8701e8db6e2SBrian Feldman if (vect[0]) 8711e8db6e2SBrian Feldman xfree(vect[0]); 872511b41d2SMark Murray continue; 873511b41d2SMark Murray } 874511b41d2SMark Murray omode = mode; 875511b41d2SMark Murray mode |= S_IWRITE; 876ae1f160dSDag-Erling Smørgrav if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 877511b41d2SMark Murray bad: run_err("%s: %s", np, strerror(errno)); 878511b41d2SMark Murray continue; 879511b41d2SMark Murray } 880cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 881511b41d2SMark Murray if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { 882511b41d2SMark Murray (void) close(ofd); 883511b41d2SMark Murray continue; 884511b41d2SMark Murray } 885511b41d2SMark Murray cp = bp->buf; 886511b41d2SMark Murray wrerr = NO; 887511b41d2SMark Murray 888511b41d2SMark Murray statbytes = 0; 889e73e9afaSDag-Erling Smørgrav if (showprogress) 890e73e9afaSDag-Erling Smørgrav start_progress_meter(curfile, size, &statbytes); 891511b41d2SMark Murray for (count = i = 0; i < size; i += 4096) { 892511b41d2SMark Murray amt = 4096; 893511b41d2SMark Murray if (i + amt > size) 894511b41d2SMark Murray amt = size - i; 895511b41d2SMark Murray count += amt; 896511b41d2SMark Murray do { 8975b9b2fafSBrian Feldman j = read(remin, cp, amt); 898ae1f160dSDag-Erling Smørgrav if (j == -1 && (errno == EINTR || 899ae1f160dSDag-Erling Smørgrav errno == EAGAIN)) { 9005b9b2fafSBrian Feldman continue; 9015b9b2fafSBrian Feldman } else if (j <= 0) { 902511b41d2SMark Murray run_err("%s", j ? strerror(errno) : 903511b41d2SMark Murray "dropped connection"); 904511b41d2SMark Murray exit(1); 905511b41d2SMark Murray } 906511b41d2SMark Murray amt -= j; 907511b41d2SMark Murray cp += j; 908511b41d2SMark Murray statbytes += j; 909511b41d2SMark Murray } while (amt > 0); 910e73e9afaSDag-Erling Smørgrav 9111ec0d754SDag-Erling Smørgrav if (limit_rate) 912e73e9afaSDag-Erling Smørgrav bwlimit(4096); 913e73e9afaSDag-Erling Smørgrav 914511b41d2SMark Murray if (count == bp->cnt) { 915511b41d2SMark Murray /* Keep reading so we stay sync'd up. */ 916511b41d2SMark Murray if (wrerr == NO) { 917cf2b5f3bSDag-Erling Smørgrav j = atomicio(vwrite, ofd, bp->buf, count); 918511b41d2SMark Murray if (j != count) { 919511b41d2SMark Murray wrerr = YES; 920511b41d2SMark Murray wrerrno = j >= 0 ? EIO : errno; 921511b41d2SMark Murray } 922511b41d2SMark Murray } 923511b41d2SMark Murray count = 0; 924511b41d2SMark Murray cp = bp->buf; 925511b41d2SMark Murray } 926511b41d2SMark Murray } 927511b41d2SMark Murray if (showprogress) 928e73e9afaSDag-Erling Smørgrav stop_progress_meter(); 929511b41d2SMark Murray if (count != 0 && wrerr == NO && 930cf2b5f3bSDag-Erling Smørgrav (j = atomicio(vwrite, ofd, bp->buf, count)) != count) { 931511b41d2SMark Murray wrerr = YES; 932511b41d2SMark Murray wrerrno = j >= 0 ? EIO : errno; 933511b41d2SMark Murray } 934e73e9afaSDag-Erling Smørgrav if (wrerr == NO && ftruncate(ofd, size) != 0) { 935511b41d2SMark Murray run_err("%s: truncate: %s", np, strerror(errno)); 936511b41d2SMark Murray wrerr = DISPLAYED; 937511b41d2SMark Murray } 938511b41d2SMark Murray if (pflag) { 939511b41d2SMark Murray if (exists || omode != mode) 94083d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 941511b41d2SMark Murray if (fchmod(ofd, omode)) 94283d2307dSDag-Erling Smørgrav #else /* HAVE_FCHMOD */ 94383d2307dSDag-Erling Smørgrav if (chmod(np, omode)) 94483d2307dSDag-Erling Smørgrav #endif /* HAVE_FCHMOD */ 945511b41d2SMark Murray run_err("%s: set mode: %s", 946511b41d2SMark Murray np, strerror(errno)); 947511b41d2SMark Murray } else { 948511b41d2SMark Murray if (!exists && omode != mode) 94983d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 950511b41d2SMark Murray if (fchmod(ofd, omode & ~mask)) 95183d2307dSDag-Erling Smørgrav #else /* HAVE_FCHMOD */ 95283d2307dSDag-Erling Smørgrav if (chmod(np, omode & ~mask)) 95383d2307dSDag-Erling Smørgrav #endif /* HAVE_FCHMOD */ 954511b41d2SMark Murray run_err("%s: set mode: %s", 955511b41d2SMark Murray np, strerror(errno)); 956511b41d2SMark Murray } 957b66f2d16SKris Kennaway if (close(ofd) == -1) { 958b66f2d16SKris Kennaway wrerr = YES; 959b66f2d16SKris Kennaway wrerrno = errno; 960b66f2d16SKris Kennaway } 961511b41d2SMark Murray (void) response(); 962511b41d2SMark Murray if (setimes && wrerr == NO) { 963511b41d2SMark Murray setimes = 0; 9645b9b2fafSBrian Feldman if (utimes(np, tv) < 0) { 965511b41d2SMark Murray run_err("%s: set times: %s", 966511b41d2SMark Murray np, strerror(errno)); 967511b41d2SMark Murray wrerr = DISPLAYED; 968511b41d2SMark Murray } 969511b41d2SMark Murray } 970511b41d2SMark Murray switch (wrerr) { 971511b41d2SMark Murray case YES: 972511b41d2SMark Murray run_err("%s: %s", np, strerror(wrerrno)); 973511b41d2SMark Murray break; 974511b41d2SMark Murray case NO: 975cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 976511b41d2SMark Murray break; 977511b41d2SMark Murray case DISPLAYED: 978511b41d2SMark Murray break; 979511b41d2SMark Murray } 980511b41d2SMark Murray } 981511b41d2SMark Murray screwup: 982511b41d2SMark Murray run_err("protocol error: %s", why); 983511b41d2SMark Murray exit(1); 984511b41d2SMark Murray } 985511b41d2SMark Murray 986511b41d2SMark Murray int 987ae1f160dSDag-Erling Smørgrav response(void) 988511b41d2SMark Murray { 989511b41d2SMark Murray char ch, *cp, resp, rbuf[2048]; 990511b41d2SMark Murray 991a04a10f8SKris Kennaway if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) 992511b41d2SMark Murray lostconn(0); 993511b41d2SMark Murray 994511b41d2SMark Murray cp = rbuf; 995511b41d2SMark Murray switch (resp) { 996511b41d2SMark Murray case 0: /* ok */ 997511b41d2SMark Murray return (0); 998511b41d2SMark Murray default: 999511b41d2SMark Murray *cp++ = resp; 1000511b41d2SMark Murray /* FALLTHROUGH */ 1001511b41d2SMark Murray case 1: /* error, followed by error msg */ 1002511b41d2SMark Murray case 2: /* fatal error, "" */ 1003511b41d2SMark Murray do { 1004a04a10f8SKris Kennaway if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 1005511b41d2SMark Murray lostconn(0); 1006511b41d2SMark Murray *cp++ = ch; 1007511b41d2SMark Murray } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 1008511b41d2SMark Murray 1009511b41d2SMark Murray if (!iamremote) 1010cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); 1011511b41d2SMark Murray ++errs; 1012511b41d2SMark Murray if (resp == 1) 1013511b41d2SMark Murray return (-1); 1014511b41d2SMark Murray exit(1); 1015511b41d2SMark Murray } 1016511b41d2SMark Murray /* NOTREACHED */ 1017511b41d2SMark Murray } 1018511b41d2SMark Murray 1019511b41d2SMark Murray void 1020ae1f160dSDag-Erling Smørgrav usage(void) 1021511b41d2SMark Murray { 1022ae1f160dSDag-Erling Smørgrav (void) fprintf(stderr, 10231ec0d754SDag-Erling Smørgrav "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" 10241ec0d754SDag-Erling Smørgrav " [-l limit] [-o ssh_option] [-P port] [-S program]\n" 1025545d5ecaSDag-Erling Smørgrav " [[user@]host1:]file1 [...] [[user@]host2:]file2\n"); 1026511b41d2SMark Murray exit(1); 1027511b41d2SMark Murray } 1028511b41d2SMark Murray 1029511b41d2SMark Murray void 1030511b41d2SMark Murray run_err(const char *fmt,...) 1031511b41d2SMark Murray { 1032511b41d2SMark Murray static FILE *fp; 1033511b41d2SMark Murray va_list ap; 1034511b41d2SMark Murray 1035511b41d2SMark Murray ++errs; 1036511b41d2SMark Murray if (fp == NULL && !(fp = fdopen(remout, "w"))) 1037511b41d2SMark Murray return; 1038511b41d2SMark Murray (void) fprintf(fp, "%c", 0x01); 1039511b41d2SMark Murray (void) fprintf(fp, "scp: "); 1040ae1f160dSDag-Erling Smørgrav va_start(ap, fmt); 1041511b41d2SMark Murray (void) vfprintf(fp, fmt, ap); 1042ae1f160dSDag-Erling Smørgrav va_end(ap); 1043511b41d2SMark Murray (void) fprintf(fp, "\n"); 1044511b41d2SMark Murray (void) fflush(fp); 1045511b41d2SMark Murray 1046511b41d2SMark Murray if (!iamremote) { 1047ae1f160dSDag-Erling Smørgrav va_start(ap, fmt); 1048511b41d2SMark Murray vfprintf(stderr, fmt, ap); 1049ae1f160dSDag-Erling Smørgrav va_end(ap); 1050511b41d2SMark Murray fprintf(stderr, "\n"); 1051511b41d2SMark Murray } 1052511b41d2SMark Murray } 1053511b41d2SMark Murray 1054511b41d2SMark Murray void 1055cf2b5f3bSDag-Erling Smørgrav verifydir(char *cp) 1056511b41d2SMark Murray { 1057511b41d2SMark Murray struct stat stb; 1058511b41d2SMark Murray 1059511b41d2SMark Murray if (!stat(cp, &stb)) { 1060511b41d2SMark Murray if (S_ISDIR(stb.st_mode)) 1061511b41d2SMark Murray return; 1062511b41d2SMark Murray errno = ENOTDIR; 1063511b41d2SMark Murray } 1064511b41d2SMark Murray run_err("%s: %s", cp, strerror(errno)); 1065511b41d2SMark Murray exit(1); 1066511b41d2SMark Murray } 1067511b41d2SMark Murray 1068511b41d2SMark Murray int 1069cf2b5f3bSDag-Erling Smørgrav okname(char *cp0) 1070511b41d2SMark Murray { 1071511b41d2SMark Murray int c; 1072511b41d2SMark Murray char *cp; 1073511b41d2SMark Murray 1074511b41d2SMark Murray cp = cp0; 1075511b41d2SMark Murray do { 1076ae1f160dSDag-Erling Smørgrav c = (int)*cp; 1077511b41d2SMark Murray if (c & 0200) 1078511b41d2SMark Murray goto bad; 1079e73e9afaSDag-Erling Smørgrav if (!isalpha(c) && !isdigit(c)) { 1080e73e9afaSDag-Erling Smørgrav switch (c) { 1081e73e9afaSDag-Erling Smørgrav case '\'': 1082e73e9afaSDag-Erling Smørgrav case '"': 1083e73e9afaSDag-Erling Smørgrav case '`': 1084e73e9afaSDag-Erling Smørgrav case ' ': 1085e73e9afaSDag-Erling Smørgrav case '#': 1086511b41d2SMark Murray goto bad; 1087e73e9afaSDag-Erling Smørgrav default: 1088e73e9afaSDag-Erling Smørgrav break; 1089e73e9afaSDag-Erling Smørgrav } 1090e73e9afaSDag-Erling Smørgrav } 1091511b41d2SMark Murray } while (*++cp); 1092511b41d2SMark Murray return (1); 1093511b41d2SMark Murray 1094511b41d2SMark Murray bad: fprintf(stderr, "%s: invalid user name\n", cp0); 1095511b41d2SMark Murray return (0); 1096511b41d2SMark Murray } 1097511b41d2SMark Murray 1098511b41d2SMark Murray BUF * 1099cf2b5f3bSDag-Erling Smørgrav allocbuf(BUF *bp, int fd, int blksize) 1100511b41d2SMark Murray { 1101511b41d2SMark Murray size_t size; 110283d2307dSDag-Erling Smørgrav #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE 1103511b41d2SMark Murray struct stat stb; 1104511b41d2SMark Murray 1105511b41d2SMark Murray if (fstat(fd, &stb) < 0) { 1106511b41d2SMark Murray run_err("fstat: %s", strerror(errno)); 1107511b41d2SMark Murray return (0); 1108511b41d2SMark Murray } 1109b6fd52a0SDag-Erling Smørgrav size = roundup(stb.st_blksize, blksize); 1110e73e9afaSDag-Erling Smørgrav if (size == 0) 1111e73e9afaSDag-Erling Smørgrav size = blksize; 111283d2307dSDag-Erling Smørgrav #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 111383d2307dSDag-Erling Smørgrav size = blksize; 111483d2307dSDag-Erling Smørgrav #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 1115511b41d2SMark Murray if (bp->cnt >= size) 1116511b41d2SMark Murray return (bp); 1117511b41d2SMark Murray if (bp->buf == NULL) 1118511b41d2SMark Murray bp->buf = xmalloc(size); 1119511b41d2SMark Murray else 1120511b41d2SMark Murray bp->buf = xrealloc(bp->buf, size); 1121ae1f160dSDag-Erling Smørgrav memset(bp->buf, 0, size); 1122511b41d2SMark Murray bp->cnt = size; 1123511b41d2SMark Murray return (bp); 1124511b41d2SMark Murray } 1125511b41d2SMark Murray 1126511b41d2SMark Murray void 1127cf2b5f3bSDag-Erling Smørgrav lostconn(int signo) 1128511b41d2SMark Murray { 1129511b41d2SMark Murray if (!iamremote) 1130ae1f160dSDag-Erling Smørgrav write(STDERR_FILENO, "lost connection\n", 16); 1131ae1f160dSDag-Erling Smørgrav if (signo) 1132ae1f160dSDag-Erling Smørgrav _exit(1); 1133ae1f160dSDag-Erling Smørgrav else 1134511b41d2SMark Murray exit(1); 1135511b41d2SMark Murray } 1136