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" 74cf2b5f3bSDag-Erling Smørgrav RCSID("$OpenBSD: scp.c,v 1.108 2003/07/18 01:54:25 deraadt 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 */ 95e73e9afaSDag-Erling Smørgrav off_t limitbw = 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(); 260e73e9afaSDag-Erling Smørgrav limitbw = 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': 2765b9b2fafSBrian Feldman showprogress = 0; 277b66f2d16SKris Kennaway break; 278b66f2d16SKris Kennaway 279511b41d2SMark Murray /* Server options. */ 280511b41d2SMark Murray case 'd': 281511b41d2SMark Murray targetshouldbedirectory = 1; 282511b41d2SMark Murray break; 283511b41d2SMark Murray case 'f': /* "from" */ 284511b41d2SMark Murray iamremote = 1; 285511b41d2SMark Murray fflag = 1; 286511b41d2SMark Murray break; 287511b41d2SMark Murray case 't': /* "to" */ 288511b41d2SMark Murray iamremote = 1; 289511b41d2SMark Murray tflag = 1; 29083d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 29183d2307dSDag-Erling Smørgrav setmode(0, O_BINARY); 29283d2307dSDag-Erling Smørgrav #endif 293511b41d2SMark Murray break; 294511b41d2SMark Murray default: 295511b41d2SMark Murray usage(); 296511b41d2SMark Murray } 297511b41d2SMark Murray argc -= optind; 298511b41d2SMark Murray argv += optind; 299511b41d2SMark Murray 300511b41d2SMark Murray if ((pwd = getpwuid(userid = getuid())) == NULL) 301cf2b5f3bSDag-Erling Smørgrav fatal("unknown user %u", (u_int) userid); 302511b41d2SMark Murray 303511b41d2SMark Murray if (!isatty(STDERR_FILENO)) 304511b41d2SMark Murray showprogress = 0; 305511b41d2SMark Murray 306511b41d2SMark Murray remin = STDIN_FILENO; 307511b41d2SMark Murray remout = STDOUT_FILENO; 308511b41d2SMark Murray 309511b41d2SMark Murray if (fflag) { 310511b41d2SMark Murray /* Follow "protocol", send data. */ 311511b41d2SMark Murray (void) response(); 312511b41d2SMark Murray source(argc, argv); 313511b41d2SMark Murray exit(errs != 0); 314511b41d2SMark Murray } 315511b41d2SMark Murray if (tflag) { 316511b41d2SMark Murray /* Receive data. */ 317511b41d2SMark Murray sink(argc, argv); 318511b41d2SMark Murray exit(errs != 0); 319511b41d2SMark Murray } 320511b41d2SMark Murray if (argc < 2) 321511b41d2SMark Murray usage(); 322511b41d2SMark Murray if (argc > 2) 323511b41d2SMark Murray targetshouldbedirectory = 1; 324511b41d2SMark Murray 325511b41d2SMark Murray remin = remout = -1; 326e73e9afaSDag-Erling Smørgrav do_cmd_pid = -1; 327511b41d2SMark Murray /* Command to be executed on remote system using "ssh". */ 3281e8db6e2SBrian Feldman (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", 3291e8db6e2SBrian Feldman verbose_mode ? " -v" : "", 330511b41d2SMark Murray iamrecursive ? " -r" : "", pflag ? " -p" : "", 331511b41d2SMark Murray targetshouldbedirectory ? " -d" : ""); 332511b41d2SMark Murray 333511b41d2SMark Murray (void) signal(SIGPIPE, lostconn); 334511b41d2SMark Murray 335511b41d2SMark Murray if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 336511b41d2SMark Murray toremote(targ, argc, argv); 337511b41d2SMark Murray else { 338511b41d2SMark Murray tolocal(argc, argv); /* Dest is local host. */ 339511b41d2SMark Murray if (targetshouldbedirectory) 340511b41d2SMark Murray verifydir(argv[argc - 1]); 341511b41d2SMark Murray } 342e73e9afaSDag-Erling Smørgrav /* 343e73e9afaSDag-Erling Smørgrav * Finally check the exit status of the ssh process, if one was forked 344e73e9afaSDag-Erling Smørgrav * and no error has occured yet 345e73e9afaSDag-Erling Smørgrav */ 346e73e9afaSDag-Erling Smørgrav if (do_cmd_pid != -1 && errs == 0) { 347e73e9afaSDag-Erling Smørgrav if (remin != -1) 348e73e9afaSDag-Erling Smørgrav (void) close(remin); 349e73e9afaSDag-Erling Smørgrav if (remout != -1) 350e73e9afaSDag-Erling Smørgrav (void) close(remout); 351e73e9afaSDag-Erling Smørgrav if (waitpid(do_cmd_pid, &status, 0) == -1) 352e73e9afaSDag-Erling Smørgrav errs = 1; 353e73e9afaSDag-Erling Smørgrav else { 354e73e9afaSDag-Erling Smørgrav if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 355e73e9afaSDag-Erling Smørgrav errs = 1; 356e73e9afaSDag-Erling Smørgrav } 357e73e9afaSDag-Erling Smørgrav } 358511b41d2SMark Murray exit(errs != 0); 359511b41d2SMark Murray } 360511b41d2SMark Murray 361511b41d2SMark Murray void 362cf2b5f3bSDag-Erling Smørgrav toremote(char *targ, int argc, char **argv) 363511b41d2SMark Murray { 364511b41d2SMark Murray int i, len; 365511b41d2SMark Murray char *bp, *host, *src, *suser, *thost, *tuser; 366511b41d2SMark Murray 367511b41d2SMark Murray *targ++ = 0; 368511b41d2SMark Murray if (*targ == 0) 369511b41d2SMark Murray targ = "."; 370511b41d2SMark Murray 371e73e9afaSDag-Erling Smørgrav if ((thost = strrchr(argv[argc - 1], '@'))) { 372511b41d2SMark Murray /* user@host */ 373511b41d2SMark Murray *thost++ = 0; 374511b41d2SMark Murray tuser = argv[argc - 1]; 375511b41d2SMark Murray if (*tuser == '\0') 376511b41d2SMark Murray tuser = NULL; 377511b41d2SMark Murray } else { 378511b41d2SMark Murray thost = argv[argc - 1]; 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); 429511b41d2SMark Murray (void) system(bp); 430511b41d2SMark Murray (void) xfree(bp); 431511b41d2SMark Murray } else { /* local to remote */ 432511b41d2SMark Murray if (remin == -1) { 433511b41d2SMark Murray len = strlen(targ) + CMDNEEDS + 20; 434511b41d2SMark Murray bp = xmalloc(len); 4351e8db6e2SBrian Feldman (void) snprintf(bp, len, "%s -t %s", cmd, targ); 436511b41d2SMark Murray host = cleanhostname(thost); 437b66f2d16SKris Kennaway if (do_cmd(host, tuser, bp, &remin, 438b66f2d16SKris Kennaway &remout, argc) < 0) 439511b41d2SMark Murray exit(1); 440511b41d2SMark Murray if (response() < 0) 441511b41d2SMark Murray exit(1); 442511b41d2SMark Murray (void) xfree(bp); 443511b41d2SMark Murray } 444511b41d2SMark Murray source(1, argv + i); 445511b41d2SMark Murray } 446511b41d2SMark Murray } 447511b41d2SMark Murray } 448511b41d2SMark Murray 449511b41d2SMark Murray void 450cf2b5f3bSDag-Erling Smørgrav tolocal(int argc, char **argv) 451511b41d2SMark Murray { 452511b41d2SMark Murray int i, len; 453511b41d2SMark Murray char *bp, *host, *src, *suser; 454511b41d2SMark Murray 455511b41d2SMark Murray for (i = 0; i < argc - 1; i++) { 456511b41d2SMark Murray if (!(src = colon(argv[i]))) { /* Local to local. */ 457511b41d2SMark Murray len = strlen(_PATH_CP) + strlen(argv[i]) + 458511b41d2SMark Murray strlen(argv[argc - 1]) + 20; 459511b41d2SMark Murray bp = xmalloc(len); 4601e8db6e2SBrian Feldman (void) snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, 461511b41d2SMark Murray iamrecursive ? " -r" : "", pflag ? " -p" : "", 462511b41d2SMark Murray argv[i], argv[argc - 1]); 463511b41d2SMark Murray if (verbose_mode) 464511b41d2SMark Murray fprintf(stderr, "Executing: %s\n", bp); 465511b41d2SMark Murray if (system(bp)) 466511b41d2SMark Murray ++errs; 467511b41d2SMark Murray (void) xfree(bp); 468511b41d2SMark Murray continue; 469511b41d2SMark Murray } 470511b41d2SMark Murray *src++ = 0; 471511b41d2SMark Murray if (*src == 0) 472511b41d2SMark Murray src = "."; 473e73e9afaSDag-Erling Smørgrav if ((host = strrchr(argv[i], '@')) == NULL) { 474511b41d2SMark Murray host = argv[i]; 475511b41d2SMark Murray suser = NULL; 476511b41d2SMark Murray } else { 477511b41d2SMark Murray *host++ = 0; 478511b41d2SMark Murray suser = argv[i]; 479511b41d2SMark Murray if (*suser == '\0') 480511b41d2SMark Murray suser = pwd->pw_name; 481511b41d2SMark Murray } 482511b41d2SMark Murray host = cleanhostname(host); 483511b41d2SMark Murray len = strlen(src) + CMDNEEDS + 20; 484511b41d2SMark Murray bp = xmalloc(len); 4851e8db6e2SBrian Feldman (void) snprintf(bp, len, "%s -f %s", cmd, src); 486b66f2d16SKris Kennaway if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) { 487511b41d2SMark Murray (void) xfree(bp); 488511b41d2SMark Murray ++errs; 489511b41d2SMark Murray continue; 490511b41d2SMark Murray } 491511b41d2SMark Murray xfree(bp); 492511b41d2SMark Murray sink(1, argv + argc - 1); 493511b41d2SMark Murray (void) close(remin); 494511b41d2SMark Murray remin = remout = -1; 495511b41d2SMark Murray } 496511b41d2SMark Murray } 497511b41d2SMark Murray 498511b41d2SMark Murray void 499cf2b5f3bSDag-Erling Smørgrav source(int argc, char **argv) 500511b41d2SMark Murray { 501511b41d2SMark Murray struct stat stb; 502511b41d2SMark Murray static BUF buffer; 503511b41d2SMark Murray BUF *bp; 504e73e9afaSDag-Erling Smørgrav off_t i, amt, result, statbytes; 5051e8db6e2SBrian Feldman int fd, haderr, indx; 506511b41d2SMark Murray char *last, *name, buf[2048]; 5071e8db6e2SBrian Feldman int len; 508511b41d2SMark Murray 509511b41d2SMark Murray for (indx = 0; indx < argc; ++indx) { 510511b41d2SMark Murray name = argv[indx]; 511511b41d2SMark Murray statbytes = 0; 5121e8db6e2SBrian Feldman len = strlen(name); 5131e8db6e2SBrian Feldman while (len > 1 && name[len-1] == '/') 5141e8db6e2SBrian Feldman name[--len] = '\0'; 515ae1f160dSDag-Erling Smørgrav if (strchr(name, '\n') != NULL) { 516ae1f160dSDag-Erling Smørgrav run_err("%s: skipping, filename contains a newline", 517ae1f160dSDag-Erling Smørgrav name); 518ae1f160dSDag-Erling Smørgrav goto next; 519ae1f160dSDag-Erling Smørgrav } 520511b41d2SMark Murray if ((fd = open(name, O_RDONLY, 0)) < 0) 521511b41d2SMark Murray goto syserr; 522511b41d2SMark Murray if (fstat(fd, &stb) < 0) { 523511b41d2SMark Murray syserr: run_err("%s: %s", name, strerror(errno)); 524511b41d2SMark Murray goto next; 525511b41d2SMark Murray } 526511b41d2SMark Murray switch (stb.st_mode & S_IFMT) { 527511b41d2SMark Murray case S_IFREG: 528511b41d2SMark Murray break; 529511b41d2SMark Murray case S_IFDIR: 530511b41d2SMark Murray if (iamrecursive) { 531511b41d2SMark Murray rsource(name, &stb); 532511b41d2SMark Murray goto next; 533511b41d2SMark Murray } 534511b41d2SMark Murray /* FALLTHROUGH */ 535511b41d2SMark Murray default: 536511b41d2SMark Murray run_err("%s: not a regular file", name); 537511b41d2SMark Murray goto next; 538511b41d2SMark Murray } 539511b41d2SMark Murray if ((last = strrchr(name, '/')) == NULL) 540511b41d2SMark Murray last = name; 541511b41d2SMark Murray else 542511b41d2SMark Murray ++last; 543511b41d2SMark Murray curfile = last; 544511b41d2SMark Murray if (pflag) { 545511b41d2SMark Murray /* 546511b41d2SMark Murray * Make it compatible with possible future 547511b41d2SMark Murray * versions expecting microseconds. 548511b41d2SMark Murray */ 5491e8db6e2SBrian Feldman (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", 5501e8db6e2SBrian Feldman (u_long) stb.st_mtime, 5511e8db6e2SBrian Feldman (u_long) stb.st_atime); 552cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, buf, strlen(buf)); 553511b41d2SMark Murray if (response() < 0) 554511b41d2SMark Murray goto next; 555511b41d2SMark Murray } 556511b41d2SMark Murray #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 5571e8db6e2SBrian Feldman snprintf(buf, sizeof buf, "C%04o %lld %s\n", 5581e8db6e2SBrian Feldman (u_int) (stb.st_mode & FILEMODEMASK), 559cf2b5f3bSDag-Erling Smørgrav (int64_t)stb.st_size, last); 560511b41d2SMark Murray if (verbose_mode) { 561511b41d2SMark Murray fprintf(stderr, "Sending file modes: %s", buf); 562511b41d2SMark Murray } 563cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, buf, strlen(buf)); 564511b41d2SMark Murray if (response() < 0) 565511b41d2SMark Murray goto next; 566511b41d2SMark Murray if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { 567511b41d2SMark Murray next: (void) close(fd); 568511b41d2SMark Murray continue; 569511b41d2SMark Murray } 570e73e9afaSDag-Erling Smørgrav if (showprogress) 571e73e9afaSDag-Erling Smørgrav start_progress_meter(curfile, stb.st_size, &statbytes); 572511b41d2SMark Murray /* Keep writing after an error so that we stay sync'd up. */ 573511b41d2SMark Murray for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 574511b41d2SMark Murray amt = bp->cnt; 575511b41d2SMark Murray if (i + amt > stb.st_size) 576511b41d2SMark Murray amt = stb.st_size - i; 577511b41d2SMark Murray if (!haderr) { 578a04a10f8SKris Kennaway result = atomicio(read, fd, bp->buf, amt); 579511b41d2SMark Murray if (result != amt) 580511b41d2SMark Murray haderr = result >= 0 ? EIO : errno; 581511b41d2SMark Murray } 582511b41d2SMark Murray if (haderr) 583cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, bp->buf, amt); 584511b41d2SMark Murray else { 585cf2b5f3bSDag-Erling Smørgrav result = atomicio(vwrite, remout, bp->buf, amt); 586511b41d2SMark Murray if (result != amt) 587511b41d2SMark Murray haderr = result >= 0 ? EIO : errno; 588511b41d2SMark Murray statbytes += result; 589511b41d2SMark Murray } 590e73e9afaSDag-Erling Smørgrav if (limitbw) 591e73e9afaSDag-Erling Smørgrav bwlimit(amt); 592511b41d2SMark Murray } 593511b41d2SMark Murray if (showprogress) 594e73e9afaSDag-Erling Smørgrav stop_progress_meter(); 595511b41d2SMark Murray 596511b41d2SMark Murray if (close(fd) < 0 && !haderr) 597511b41d2SMark Murray haderr = errno; 598511b41d2SMark Murray if (!haderr) 599cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 600511b41d2SMark Murray else 601511b41d2SMark Murray run_err("%s: %s", name, strerror(haderr)); 602511b41d2SMark Murray (void) response(); 603511b41d2SMark Murray } 604511b41d2SMark Murray } 605511b41d2SMark Murray 606511b41d2SMark Murray void 607cf2b5f3bSDag-Erling Smørgrav rsource(char *name, struct stat *statp) 608511b41d2SMark Murray { 609511b41d2SMark Murray DIR *dirp; 610511b41d2SMark Murray struct dirent *dp; 611511b41d2SMark Murray char *last, *vect[1], path[1100]; 612511b41d2SMark Murray 613511b41d2SMark Murray if (!(dirp = opendir(name))) { 614511b41d2SMark Murray run_err("%s: %s", name, strerror(errno)); 615511b41d2SMark Murray return; 616511b41d2SMark Murray } 617511b41d2SMark Murray last = strrchr(name, '/'); 618511b41d2SMark Murray if (last == 0) 619511b41d2SMark Murray last = name; 620511b41d2SMark Murray else 621511b41d2SMark Murray last++; 622511b41d2SMark Murray if (pflag) { 6231e8db6e2SBrian Feldman (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", 6241e8db6e2SBrian Feldman (u_long) statp->st_mtime, 6251e8db6e2SBrian Feldman (u_long) statp->st_atime); 626cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, path, strlen(path)); 627511b41d2SMark Murray if (response() < 0) { 628511b41d2SMark Murray closedir(dirp); 629511b41d2SMark Murray return; 630511b41d2SMark Murray } 631511b41d2SMark Murray } 6321e8db6e2SBrian Feldman (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", 6331e8db6e2SBrian Feldman (u_int) (statp->st_mode & FILEMODEMASK), 0, last); 634511b41d2SMark Murray if (verbose_mode) 635511b41d2SMark Murray fprintf(stderr, "Entering directory: %s", path); 636cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, path, strlen(path)); 637511b41d2SMark Murray if (response() < 0) { 638511b41d2SMark Murray closedir(dirp); 639511b41d2SMark Murray return; 640511b41d2SMark Murray } 6411e8db6e2SBrian Feldman while ((dp = readdir(dirp)) != NULL) { 642511b41d2SMark Murray if (dp->d_ino == 0) 643511b41d2SMark Murray continue; 644511b41d2SMark Murray if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 645511b41d2SMark Murray continue; 646511b41d2SMark Murray if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { 647511b41d2SMark Murray run_err("%s/%s: name too long", name, dp->d_name); 648511b41d2SMark Murray continue; 649511b41d2SMark Murray } 6501e8db6e2SBrian Feldman (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); 651511b41d2SMark Murray vect[0] = path; 652511b41d2SMark Murray source(1, vect); 653511b41d2SMark Murray } 654511b41d2SMark Murray (void) closedir(dirp); 655cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "E\n", 2); 656511b41d2SMark Murray (void) response(); 657511b41d2SMark Murray } 658511b41d2SMark Murray 659511b41d2SMark Murray void 660e73e9afaSDag-Erling Smørgrav bwlimit(int amount) 661e73e9afaSDag-Erling Smørgrav { 662e73e9afaSDag-Erling Smørgrav static struct timeval bwstart, bwend; 663e73e9afaSDag-Erling Smørgrav static int lamt, thresh = 16384; 664e73e9afaSDag-Erling Smørgrav u_int64_t wait; 665e73e9afaSDag-Erling Smørgrav struct timespec ts, rm; 666e73e9afaSDag-Erling Smørgrav 667e73e9afaSDag-Erling Smørgrav if (!timerisset(&bwstart)) { 668e73e9afaSDag-Erling Smørgrav gettimeofday(&bwstart, NULL); 669e73e9afaSDag-Erling Smørgrav return; 670e73e9afaSDag-Erling Smørgrav } 671e73e9afaSDag-Erling Smørgrav 672e73e9afaSDag-Erling Smørgrav lamt += amount; 673e73e9afaSDag-Erling Smørgrav if (lamt < thresh) 674e73e9afaSDag-Erling Smørgrav return; 675e73e9afaSDag-Erling Smørgrav 676e73e9afaSDag-Erling Smørgrav gettimeofday(&bwend, NULL); 677e73e9afaSDag-Erling Smørgrav timersub(&bwend, &bwstart, &bwend); 678e73e9afaSDag-Erling Smørgrav if (!timerisset(&bwend)) 679e73e9afaSDag-Erling Smørgrav return; 680e73e9afaSDag-Erling Smørgrav 681e73e9afaSDag-Erling Smørgrav lamt *= 8; 682e73e9afaSDag-Erling Smørgrav wait = (double)1000000L * lamt / limitbw; 683e73e9afaSDag-Erling Smørgrav 684e73e9afaSDag-Erling Smørgrav bwstart.tv_sec = wait / 1000000L; 685e73e9afaSDag-Erling Smørgrav bwstart.tv_usec = wait % 1000000L; 686e73e9afaSDag-Erling Smørgrav 687e73e9afaSDag-Erling Smørgrav if (timercmp(&bwstart, &bwend, >)) { 688e73e9afaSDag-Erling Smørgrav timersub(&bwstart, &bwend, &bwend); 689e73e9afaSDag-Erling Smørgrav 690e73e9afaSDag-Erling Smørgrav /* Adjust the wait time */ 691e73e9afaSDag-Erling Smørgrav if (bwend.tv_sec) { 692e73e9afaSDag-Erling Smørgrav thresh /= 2; 693e73e9afaSDag-Erling Smørgrav if (thresh < 2048) 694e73e9afaSDag-Erling Smørgrav thresh = 2048; 695e73e9afaSDag-Erling Smørgrav } else if (bwend.tv_usec < 100) { 696e73e9afaSDag-Erling Smørgrav thresh *= 2; 697e73e9afaSDag-Erling Smørgrav if (thresh > 32768) 698e73e9afaSDag-Erling Smørgrav thresh = 32768; 699e73e9afaSDag-Erling Smørgrav } 700e73e9afaSDag-Erling Smørgrav 701e73e9afaSDag-Erling Smørgrav TIMEVAL_TO_TIMESPEC(&bwend, &ts); 702e73e9afaSDag-Erling Smørgrav while (nanosleep(&ts, &rm) == -1) { 703e73e9afaSDag-Erling Smørgrav if (errno != EINTR) 704e73e9afaSDag-Erling Smørgrav break; 705e73e9afaSDag-Erling Smørgrav ts = rm; 706e73e9afaSDag-Erling Smørgrav } 707e73e9afaSDag-Erling Smørgrav } 708e73e9afaSDag-Erling Smørgrav 709e73e9afaSDag-Erling Smørgrav lamt = 0; 710e73e9afaSDag-Erling Smørgrav gettimeofday(&bwstart, NULL); 711e73e9afaSDag-Erling Smørgrav } 712e73e9afaSDag-Erling Smørgrav 713e73e9afaSDag-Erling Smørgrav void 714cf2b5f3bSDag-Erling Smørgrav sink(int argc, char **argv) 715511b41d2SMark Murray { 716511b41d2SMark Murray static BUF buffer; 717511b41d2SMark Murray struct stat stb; 718511b41d2SMark Murray enum { 719511b41d2SMark Murray YES, NO, DISPLAYED 720511b41d2SMark Murray } wrerr; 721511b41d2SMark Murray BUF *bp; 722511b41d2SMark Murray off_t i, j; 723511b41d2SMark Murray int amt, count, exists, first, mask, mode, ofd, omode; 724e73e9afaSDag-Erling Smørgrav off_t size, statbytes; 725b66f2d16SKris Kennaway int setimes, targisdir, wrerrno = 0; 726511b41d2SMark Murray char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; 7275b9b2fafSBrian Feldman struct timeval tv[2]; 728511b41d2SMark Murray 7291e8db6e2SBrian Feldman #define atime tv[0] 7301e8db6e2SBrian Feldman #define mtime tv[1] 731ae1f160dSDag-Erling Smørgrav #define SCREWUP(str) do { why = str; goto screwup; } while (0) 732511b41d2SMark Murray 733511b41d2SMark Murray setimes = targisdir = 0; 734511b41d2SMark Murray mask = umask(0); 735511b41d2SMark Murray if (!pflag) 736511b41d2SMark Murray (void) umask(mask); 737511b41d2SMark Murray if (argc != 1) { 738511b41d2SMark Murray run_err("ambiguous target"); 739511b41d2SMark Murray exit(1); 740511b41d2SMark Murray } 741511b41d2SMark Murray targ = *argv; 742511b41d2SMark Murray if (targetshouldbedirectory) 743511b41d2SMark Murray verifydir(targ); 744511b41d2SMark Murray 745cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 746511b41d2SMark Murray if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 747511b41d2SMark Murray targisdir = 1; 748511b41d2SMark Murray for (first = 1;; first = 0) { 749511b41d2SMark Murray cp = buf; 750a04a10f8SKris Kennaway if (atomicio(read, remin, cp, 1) <= 0) 751511b41d2SMark Murray return; 752511b41d2SMark Murray if (*cp++ == '\n') 753511b41d2SMark Murray SCREWUP("unexpected <newline>"); 754511b41d2SMark Murray do { 755a04a10f8SKris Kennaway if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 756511b41d2SMark Murray SCREWUP("lost connection"); 757511b41d2SMark Murray *cp++ = ch; 758511b41d2SMark Murray } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 759511b41d2SMark Murray *cp = 0; 760511b41d2SMark Murray 761511b41d2SMark Murray if (buf[0] == '\01' || buf[0] == '\02') { 762511b41d2SMark Murray if (iamremote == 0) 763cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, STDERR_FILENO, 764511b41d2SMark Murray buf + 1, strlen(buf + 1)); 765511b41d2SMark Murray if (buf[0] == '\02') 766511b41d2SMark Murray exit(1); 767511b41d2SMark Murray ++errs; 768511b41d2SMark Murray continue; 769511b41d2SMark Murray } 770511b41d2SMark Murray if (buf[0] == 'E') { 771cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 772511b41d2SMark Murray return; 773511b41d2SMark Murray } 774511b41d2SMark Murray if (ch == '\n') 775511b41d2SMark Murray *--cp = 0; 776511b41d2SMark Murray 777511b41d2SMark Murray cp = buf; 778511b41d2SMark Murray if (*cp == 'T') { 779511b41d2SMark Murray setimes++; 780511b41d2SMark Murray cp++; 7811e8db6e2SBrian Feldman mtime.tv_sec = strtol(cp, &cp, 10); 7821e8db6e2SBrian Feldman if (!cp || *cp++ != ' ') 783511b41d2SMark Murray SCREWUP("mtime.sec not delimited"); 7841e8db6e2SBrian Feldman mtime.tv_usec = strtol(cp, &cp, 10); 7851e8db6e2SBrian Feldman if (!cp || *cp++ != ' ') 786511b41d2SMark Murray SCREWUP("mtime.usec not delimited"); 7871e8db6e2SBrian Feldman atime.tv_sec = strtol(cp, &cp, 10); 7881e8db6e2SBrian Feldman if (!cp || *cp++ != ' ') 789511b41d2SMark Murray SCREWUP("atime.sec not delimited"); 7901e8db6e2SBrian Feldman atime.tv_usec = strtol(cp, &cp, 10); 7911e8db6e2SBrian Feldman if (!cp || *cp++ != '\0') 792511b41d2SMark Murray SCREWUP("atime.usec not delimited"); 793cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 794511b41d2SMark Murray continue; 795511b41d2SMark Murray } 796511b41d2SMark Murray if (*cp != 'C' && *cp != 'D') { 797511b41d2SMark Murray /* 798511b41d2SMark Murray * Check for the case "rcp remote:foo\* local:bar". 799511b41d2SMark Murray * In this case, the line "No match." can be returned 800511b41d2SMark Murray * by the shell before the rcp command on the remote is 801511b41d2SMark Murray * executed so the ^Aerror_message convention isn't 802511b41d2SMark Murray * followed. 803511b41d2SMark Murray */ 804511b41d2SMark Murray if (first) { 805511b41d2SMark Murray run_err("%s", cp); 806511b41d2SMark Murray exit(1); 807511b41d2SMark Murray } 808511b41d2SMark Murray SCREWUP("expected control record"); 809511b41d2SMark Murray } 810511b41d2SMark Murray mode = 0; 811511b41d2SMark Murray for (++cp; cp < buf + 5; cp++) { 812511b41d2SMark Murray if (*cp < '0' || *cp > '7') 813511b41d2SMark Murray SCREWUP("bad mode"); 814511b41d2SMark Murray mode = (mode << 3) | (*cp - '0'); 815511b41d2SMark Murray } 816511b41d2SMark Murray if (*cp++ != ' ') 817511b41d2SMark Murray SCREWUP("mode not delimited"); 818511b41d2SMark Murray 8191e8db6e2SBrian Feldman for (size = 0; isdigit(*cp);) 820511b41d2SMark Murray size = size * 10 + (*cp++ - '0'); 821511b41d2SMark Murray if (*cp++ != ' ') 822511b41d2SMark Murray SCREWUP("size not delimited"); 823511b41d2SMark Murray if (targisdir) { 824511b41d2SMark Murray static char *namebuf; 825511b41d2SMark Murray static int cursize; 826511b41d2SMark Murray size_t need; 827511b41d2SMark Murray 828511b41d2SMark Murray need = strlen(targ) + strlen(cp) + 250; 8291e8db6e2SBrian Feldman if (need > cursize) { 8301e8db6e2SBrian Feldman if (namebuf) 8311e8db6e2SBrian Feldman xfree(namebuf); 832511b41d2SMark Murray namebuf = xmalloc(need); 8331e8db6e2SBrian Feldman cursize = need; 8341e8db6e2SBrian Feldman } 8351e8db6e2SBrian Feldman (void) snprintf(namebuf, need, "%s%s%s", targ, 836545d5ecaSDag-Erling Smørgrav strcmp(targ, "/") ? "/" : "", cp); 837511b41d2SMark Murray np = namebuf; 838511b41d2SMark Murray } else 839511b41d2SMark Murray np = targ; 840511b41d2SMark Murray curfile = cp; 841511b41d2SMark Murray exists = stat(np, &stb) == 0; 842511b41d2SMark Murray if (buf[0] == 'D') { 843511b41d2SMark Murray int mod_flag = pflag; 844511b41d2SMark Murray if (exists) { 845511b41d2SMark Murray if (!S_ISDIR(stb.st_mode)) { 846511b41d2SMark Murray errno = ENOTDIR; 847511b41d2SMark Murray goto bad; 848511b41d2SMark Murray } 849511b41d2SMark Murray if (pflag) 850511b41d2SMark Murray (void) chmod(np, mode); 851511b41d2SMark Murray } else { 852511b41d2SMark Murray /* Handle copying from a read-only 853511b41d2SMark Murray directory */ 854511b41d2SMark Murray mod_flag = 1; 855511b41d2SMark Murray if (mkdir(np, mode | S_IRWXU) < 0) 856511b41d2SMark Murray goto bad; 857511b41d2SMark Murray } 8581e8db6e2SBrian Feldman vect[0] = xstrdup(np); 859511b41d2SMark Murray sink(1, vect); 860511b41d2SMark Murray if (setimes) { 861511b41d2SMark Murray setimes = 0; 8621e8db6e2SBrian Feldman if (utimes(vect[0], tv) < 0) 863511b41d2SMark Murray run_err("%s: set times: %s", 8641e8db6e2SBrian Feldman vect[0], strerror(errno)); 865511b41d2SMark Murray } 866511b41d2SMark Murray if (mod_flag) 8671e8db6e2SBrian Feldman (void) chmod(vect[0], mode); 8681e8db6e2SBrian Feldman if (vect[0]) 8691e8db6e2SBrian Feldman xfree(vect[0]); 870511b41d2SMark Murray continue; 871511b41d2SMark Murray } 872511b41d2SMark Murray omode = mode; 873511b41d2SMark Murray mode |= S_IWRITE; 874ae1f160dSDag-Erling Smørgrav if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 875511b41d2SMark Murray bad: run_err("%s: %s", np, strerror(errno)); 876511b41d2SMark Murray continue; 877511b41d2SMark Murray } 878cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 879511b41d2SMark Murray if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { 880511b41d2SMark Murray (void) close(ofd); 881511b41d2SMark Murray continue; 882511b41d2SMark Murray } 883511b41d2SMark Murray cp = bp->buf; 884511b41d2SMark Murray wrerr = NO; 885511b41d2SMark Murray 886511b41d2SMark Murray statbytes = 0; 887e73e9afaSDag-Erling Smørgrav if (showprogress) 888e73e9afaSDag-Erling Smørgrav start_progress_meter(curfile, size, &statbytes); 889511b41d2SMark Murray for (count = i = 0; i < size; i += 4096) { 890511b41d2SMark Murray amt = 4096; 891511b41d2SMark Murray if (i + amt > size) 892511b41d2SMark Murray amt = size - i; 893511b41d2SMark Murray count += amt; 894511b41d2SMark Murray do { 8955b9b2fafSBrian Feldman j = read(remin, cp, amt); 896ae1f160dSDag-Erling Smørgrav if (j == -1 && (errno == EINTR || 897ae1f160dSDag-Erling Smørgrav errno == EAGAIN)) { 8985b9b2fafSBrian Feldman continue; 8995b9b2fafSBrian Feldman } else if (j <= 0) { 900511b41d2SMark Murray run_err("%s", j ? strerror(errno) : 901511b41d2SMark Murray "dropped connection"); 902511b41d2SMark Murray exit(1); 903511b41d2SMark Murray } 904511b41d2SMark Murray amt -= j; 905511b41d2SMark Murray cp += j; 906511b41d2SMark Murray statbytes += j; 907511b41d2SMark Murray } while (amt > 0); 908e73e9afaSDag-Erling Smørgrav 909e73e9afaSDag-Erling Smørgrav if (limitbw) 910e73e9afaSDag-Erling Smørgrav bwlimit(4096); 911e73e9afaSDag-Erling Smørgrav 912511b41d2SMark Murray if (count == bp->cnt) { 913511b41d2SMark Murray /* Keep reading so we stay sync'd up. */ 914511b41d2SMark Murray if (wrerr == NO) { 915cf2b5f3bSDag-Erling Smørgrav j = atomicio(vwrite, ofd, bp->buf, count); 916511b41d2SMark Murray if (j != count) { 917511b41d2SMark Murray wrerr = YES; 918511b41d2SMark Murray wrerrno = j >= 0 ? EIO : errno; 919511b41d2SMark Murray } 920511b41d2SMark Murray } 921511b41d2SMark Murray count = 0; 922511b41d2SMark Murray cp = bp->buf; 923511b41d2SMark Murray } 924511b41d2SMark Murray } 925511b41d2SMark Murray if (showprogress) 926e73e9afaSDag-Erling Smørgrav stop_progress_meter(); 927511b41d2SMark Murray if (count != 0 && wrerr == NO && 928cf2b5f3bSDag-Erling Smørgrav (j = atomicio(vwrite, ofd, bp->buf, count)) != count) { 929511b41d2SMark Murray wrerr = YES; 930511b41d2SMark Murray wrerrno = j >= 0 ? EIO : errno; 931511b41d2SMark Murray } 932e73e9afaSDag-Erling Smørgrav if (wrerr == NO && ftruncate(ofd, size) != 0) { 933511b41d2SMark Murray run_err("%s: truncate: %s", np, strerror(errno)); 934511b41d2SMark Murray wrerr = DISPLAYED; 935511b41d2SMark Murray } 936511b41d2SMark Murray if (pflag) { 937511b41d2SMark Murray if (exists || omode != mode) 93883d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 939511b41d2SMark Murray if (fchmod(ofd, omode)) 94083d2307dSDag-Erling Smørgrav #else /* HAVE_FCHMOD */ 94183d2307dSDag-Erling Smørgrav if (chmod(np, omode)) 94283d2307dSDag-Erling Smørgrav #endif /* HAVE_FCHMOD */ 943511b41d2SMark Murray run_err("%s: set mode: %s", 944511b41d2SMark Murray np, strerror(errno)); 945511b41d2SMark Murray } else { 946511b41d2SMark Murray if (!exists && omode != mode) 94783d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 948511b41d2SMark Murray if (fchmod(ofd, omode & ~mask)) 94983d2307dSDag-Erling Smørgrav #else /* HAVE_FCHMOD */ 95083d2307dSDag-Erling Smørgrav if (chmod(np, omode & ~mask)) 95183d2307dSDag-Erling Smørgrav #endif /* HAVE_FCHMOD */ 952511b41d2SMark Murray run_err("%s: set mode: %s", 953511b41d2SMark Murray np, strerror(errno)); 954511b41d2SMark Murray } 955b66f2d16SKris Kennaway if (close(ofd) == -1) { 956b66f2d16SKris Kennaway wrerr = YES; 957b66f2d16SKris Kennaway wrerrno = errno; 958b66f2d16SKris Kennaway } 959511b41d2SMark Murray (void) response(); 960511b41d2SMark Murray if (setimes && wrerr == NO) { 961511b41d2SMark Murray setimes = 0; 9625b9b2fafSBrian Feldman if (utimes(np, tv) < 0) { 963511b41d2SMark Murray run_err("%s: set times: %s", 964511b41d2SMark Murray np, strerror(errno)); 965511b41d2SMark Murray wrerr = DISPLAYED; 966511b41d2SMark Murray } 967511b41d2SMark Murray } 968511b41d2SMark Murray switch (wrerr) { 969511b41d2SMark Murray case YES: 970511b41d2SMark Murray run_err("%s: %s", np, strerror(wrerrno)); 971511b41d2SMark Murray break; 972511b41d2SMark Murray case NO: 973cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, remout, "", 1); 974511b41d2SMark Murray break; 975511b41d2SMark Murray case DISPLAYED: 976511b41d2SMark Murray break; 977511b41d2SMark Murray } 978511b41d2SMark Murray } 979511b41d2SMark Murray screwup: 980511b41d2SMark Murray run_err("protocol error: %s", why); 981511b41d2SMark Murray exit(1); 982511b41d2SMark Murray } 983511b41d2SMark Murray 984511b41d2SMark Murray int 985ae1f160dSDag-Erling Smørgrav response(void) 986511b41d2SMark Murray { 987511b41d2SMark Murray char ch, *cp, resp, rbuf[2048]; 988511b41d2SMark Murray 989a04a10f8SKris Kennaway if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) 990511b41d2SMark Murray lostconn(0); 991511b41d2SMark Murray 992511b41d2SMark Murray cp = rbuf; 993511b41d2SMark Murray switch (resp) { 994511b41d2SMark Murray case 0: /* ok */ 995511b41d2SMark Murray return (0); 996511b41d2SMark Murray default: 997511b41d2SMark Murray *cp++ = resp; 998511b41d2SMark Murray /* FALLTHROUGH */ 999511b41d2SMark Murray case 1: /* error, followed by error msg */ 1000511b41d2SMark Murray case 2: /* fatal error, "" */ 1001511b41d2SMark Murray do { 1002a04a10f8SKris Kennaway if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 1003511b41d2SMark Murray lostconn(0); 1004511b41d2SMark Murray *cp++ = ch; 1005511b41d2SMark Murray } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 1006511b41d2SMark Murray 1007511b41d2SMark Murray if (!iamremote) 1008cf2b5f3bSDag-Erling Smørgrav (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); 1009511b41d2SMark Murray ++errs; 1010511b41d2SMark Murray if (resp == 1) 1011511b41d2SMark Murray return (-1); 1012511b41d2SMark Murray exit(1); 1013511b41d2SMark Murray } 1014511b41d2SMark Murray /* NOTREACHED */ 1015511b41d2SMark Murray } 1016511b41d2SMark Murray 1017511b41d2SMark Murray void 1018ae1f160dSDag-Erling Smørgrav usage(void) 1019511b41d2SMark Murray { 1020ae1f160dSDag-Erling Smørgrav (void) fprintf(stderr, 1021e73e9afaSDag-Erling Smørgrav "usage: scp [-pqrvBC1246] [-F config] [-S program] [-P port]\n" 1022e73e9afaSDag-Erling Smørgrav " [-c cipher] [-i identity] [-l limit] [-o option]\n" 1023545d5ecaSDag-Erling Smørgrav " [[user@]host1:]file1 [...] [[user@]host2:]file2\n"); 1024511b41d2SMark Murray exit(1); 1025511b41d2SMark Murray } 1026511b41d2SMark Murray 1027511b41d2SMark Murray void 1028511b41d2SMark Murray run_err(const char *fmt,...) 1029511b41d2SMark Murray { 1030511b41d2SMark Murray static FILE *fp; 1031511b41d2SMark Murray va_list ap; 1032511b41d2SMark Murray 1033511b41d2SMark Murray ++errs; 1034511b41d2SMark Murray if (fp == NULL && !(fp = fdopen(remout, "w"))) 1035511b41d2SMark Murray return; 1036511b41d2SMark Murray (void) fprintf(fp, "%c", 0x01); 1037511b41d2SMark Murray (void) fprintf(fp, "scp: "); 1038ae1f160dSDag-Erling Smørgrav va_start(ap, fmt); 1039511b41d2SMark Murray (void) vfprintf(fp, fmt, ap); 1040ae1f160dSDag-Erling Smørgrav va_end(ap); 1041511b41d2SMark Murray (void) fprintf(fp, "\n"); 1042511b41d2SMark Murray (void) fflush(fp); 1043511b41d2SMark Murray 1044511b41d2SMark Murray if (!iamremote) { 1045ae1f160dSDag-Erling Smørgrav va_start(ap, fmt); 1046511b41d2SMark Murray vfprintf(stderr, fmt, ap); 1047ae1f160dSDag-Erling Smørgrav va_end(ap); 1048511b41d2SMark Murray fprintf(stderr, "\n"); 1049511b41d2SMark Murray } 1050511b41d2SMark Murray } 1051511b41d2SMark Murray 1052511b41d2SMark Murray void 1053cf2b5f3bSDag-Erling Smørgrav verifydir(char *cp) 1054511b41d2SMark Murray { 1055511b41d2SMark Murray struct stat stb; 1056511b41d2SMark Murray 1057511b41d2SMark Murray if (!stat(cp, &stb)) { 1058511b41d2SMark Murray if (S_ISDIR(stb.st_mode)) 1059511b41d2SMark Murray return; 1060511b41d2SMark Murray errno = ENOTDIR; 1061511b41d2SMark Murray } 1062511b41d2SMark Murray run_err("%s: %s", cp, strerror(errno)); 1063511b41d2SMark Murray exit(1); 1064511b41d2SMark Murray } 1065511b41d2SMark Murray 1066511b41d2SMark Murray int 1067cf2b5f3bSDag-Erling Smørgrav okname(char *cp0) 1068511b41d2SMark Murray { 1069511b41d2SMark Murray int c; 1070511b41d2SMark Murray char *cp; 1071511b41d2SMark Murray 1072511b41d2SMark Murray cp = cp0; 1073511b41d2SMark Murray do { 1074ae1f160dSDag-Erling Smørgrav c = (int)*cp; 1075511b41d2SMark Murray if (c & 0200) 1076511b41d2SMark Murray goto bad; 1077e73e9afaSDag-Erling Smørgrav if (!isalpha(c) && !isdigit(c)) { 1078e73e9afaSDag-Erling Smørgrav switch (c) { 1079e73e9afaSDag-Erling Smørgrav case '\'': 1080e73e9afaSDag-Erling Smørgrav case '"': 1081e73e9afaSDag-Erling Smørgrav case '`': 1082e73e9afaSDag-Erling Smørgrav case ' ': 1083e73e9afaSDag-Erling Smørgrav case '#': 1084511b41d2SMark Murray goto bad; 1085e73e9afaSDag-Erling Smørgrav default: 1086e73e9afaSDag-Erling Smørgrav break; 1087e73e9afaSDag-Erling Smørgrav } 1088e73e9afaSDag-Erling Smørgrav } 1089511b41d2SMark Murray } while (*++cp); 1090511b41d2SMark Murray return (1); 1091511b41d2SMark Murray 1092511b41d2SMark Murray bad: fprintf(stderr, "%s: invalid user name\n", cp0); 1093511b41d2SMark Murray return (0); 1094511b41d2SMark Murray } 1095511b41d2SMark Murray 1096511b41d2SMark Murray BUF * 1097cf2b5f3bSDag-Erling Smørgrav allocbuf(BUF *bp, int fd, int blksize) 1098511b41d2SMark Murray { 1099511b41d2SMark Murray size_t size; 110083d2307dSDag-Erling Smørgrav #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE 1101511b41d2SMark Murray struct stat stb; 1102511b41d2SMark Murray 1103511b41d2SMark Murray if (fstat(fd, &stb) < 0) { 1104511b41d2SMark Murray run_err("fstat: %s", strerror(errno)); 1105511b41d2SMark Murray return (0); 1106511b41d2SMark Murray } 1107b6fd52a0SDag-Erling Smørgrav size = roundup(stb.st_blksize, blksize); 1108e73e9afaSDag-Erling Smørgrav if (size == 0) 1109e73e9afaSDag-Erling Smørgrav size = blksize; 111083d2307dSDag-Erling Smørgrav #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 111183d2307dSDag-Erling Smørgrav size = blksize; 111283d2307dSDag-Erling Smørgrav #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 1113511b41d2SMark Murray if (bp->cnt >= size) 1114511b41d2SMark Murray return (bp); 1115511b41d2SMark Murray if (bp->buf == NULL) 1116511b41d2SMark Murray bp->buf = xmalloc(size); 1117511b41d2SMark Murray else 1118511b41d2SMark Murray bp->buf = xrealloc(bp->buf, size); 1119ae1f160dSDag-Erling Smørgrav memset(bp->buf, 0, size); 1120511b41d2SMark Murray bp->cnt = size; 1121511b41d2SMark Murray return (bp); 1122511b41d2SMark Murray } 1123511b41d2SMark Murray 1124511b41d2SMark Murray void 1125cf2b5f3bSDag-Erling Smørgrav lostconn(int signo) 1126511b41d2SMark Murray { 1127511b41d2SMark Murray if (!iamremote) 1128ae1f160dSDag-Erling Smørgrav write(STDERR_FILENO, "lost connection\n", 16); 1129ae1f160dSDag-Erling Smørgrav if (signo) 1130ae1f160dSDag-Erling Smørgrav _exit(1); 1131ae1f160dSDag-Erling Smørgrav else 1132511b41d2SMark Murray exit(1); 1133511b41d2SMark Murray } 1134