1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 4*7c478bd9Sstevel@tonic-gate */ 5*7c478bd9Sstevel@tonic-gate /* 6*7c478bd9Sstevel@tonic-gate * scp - secure remote copy. This is basically patched BSD rcp which 7*7c478bd9Sstevel@tonic-gate * uses ssh to do the data transfer (instead of using rcmd). 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * NOTE: This version should NOT be suid root. (This uses ssh to 10*7c478bd9Sstevel@tonic-gate * do the transfer and ssh has the necessary privileges.) 11*7c478bd9Sstevel@tonic-gate * 12*7c478bd9Sstevel@tonic-gate * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * As far as I am concerned, the code I have written for this software 15*7c478bd9Sstevel@tonic-gate * can be used freely for any purpose. Any derived versions of this 16*7c478bd9Sstevel@tonic-gate * software must be clearly marked as such, and if the derived work is 17*7c478bd9Sstevel@tonic-gate * incompatible with the protocol description in the RFC file, it must be 18*7c478bd9Sstevel@tonic-gate * called by a name other than "ssh" or "Secure Shell". 19*7c478bd9Sstevel@tonic-gate */ 20*7c478bd9Sstevel@tonic-gate /* 21*7c478bd9Sstevel@tonic-gate * Copyright (c) 1999 Theo de Raadt. All rights reserved. 22*7c478bd9Sstevel@tonic-gate * Copyright (c) 1999 Aaron Campbell. All rights reserved. 23*7c478bd9Sstevel@tonic-gate * 24*7c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without 25*7c478bd9Sstevel@tonic-gate * modification, are permitted provided that the following conditions 26*7c478bd9Sstevel@tonic-gate * are met: 27*7c478bd9Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 28*7c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 29*7c478bd9Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 30*7c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 31*7c478bd9Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 32*7c478bd9Sstevel@tonic-gate * 33*7c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 34*7c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 35*7c478bd9Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 36*7c478bd9Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 37*7c478bd9Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 38*7c478bd9Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 39*7c478bd9Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 40*7c478bd9Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 41*7c478bd9Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 42*7c478bd9Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43*7c478bd9Sstevel@tonic-gate */ 44*7c478bd9Sstevel@tonic-gate 45*7c478bd9Sstevel@tonic-gate /* 46*7c478bd9Sstevel@tonic-gate * Parts from: 47*7c478bd9Sstevel@tonic-gate * 48*7c478bd9Sstevel@tonic-gate * Copyright (c) 1983, 1990, 1992, 1993, 1995 49*7c478bd9Sstevel@tonic-gate * The Regents of the University of California. All rights reserved. 50*7c478bd9Sstevel@tonic-gate * 51*7c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without 52*7c478bd9Sstevel@tonic-gate * modification, are permitted provided that the following conditions 53*7c478bd9Sstevel@tonic-gate * are met: 54*7c478bd9Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 55*7c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 56*7c478bd9Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 57*7c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 58*7c478bd9Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 59*7c478bd9Sstevel@tonic-gate * 3. All advertising materials mentioning features or use of this software 60*7c478bd9Sstevel@tonic-gate * must display the following acknowledgement: 61*7c478bd9Sstevel@tonic-gate * This product includes software developed by the University of 62*7c478bd9Sstevel@tonic-gate * California, Berkeley and its contributors. 63*7c478bd9Sstevel@tonic-gate * 4. Neither the name of the University nor the names of its contributors 64*7c478bd9Sstevel@tonic-gate * may be used to endorse or promote products derived from this software 65*7c478bd9Sstevel@tonic-gate * without specific prior written permission. 66*7c478bd9Sstevel@tonic-gate * 67*7c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 68*7c478bd9Sstevel@tonic-gate * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 69*7c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 70*7c478bd9Sstevel@tonic-gate * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 71*7c478bd9Sstevel@tonic-gate * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 72*7c478bd9Sstevel@tonic-gate * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 73*7c478bd9Sstevel@tonic-gate * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 74*7c478bd9Sstevel@tonic-gate * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 75*7c478bd9Sstevel@tonic-gate * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 76*7c478bd9Sstevel@tonic-gate * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 77*7c478bd9Sstevel@tonic-gate * SUCH DAMAGE. 78*7c478bd9Sstevel@tonic-gate * 79*7c478bd9Sstevel@tonic-gate */ 80*7c478bd9Sstevel@tonic-gate 81*7c478bd9Sstevel@tonic-gate #include "includes.h" 82*7c478bd9Sstevel@tonic-gate RCSID("$OpenBSD: scp.c,v 1.91 2002/06/19 00:27:55 deraadt Exp $"); 83*7c478bd9Sstevel@tonic-gate 84*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 85*7c478bd9Sstevel@tonic-gate 86*7c478bd9Sstevel@tonic-gate #include "xmalloc.h" 87*7c478bd9Sstevel@tonic-gate #include "atomicio.h" 88*7c478bd9Sstevel@tonic-gate #include "pathnames.h" 89*7c478bd9Sstevel@tonic-gate #include "log.h" 90*7c478bd9Sstevel@tonic-gate #include "misc.h" 91*7c478bd9Sstevel@tonic-gate 92*7c478bd9Sstevel@tonic-gate #ifdef HAVE___PROGNAME 93*7c478bd9Sstevel@tonic-gate extern char *__progname; 94*7c478bd9Sstevel@tonic-gate #else 95*7c478bd9Sstevel@tonic-gate char *__progname; 96*7c478bd9Sstevel@tonic-gate #endif 97*7c478bd9Sstevel@tonic-gate 98*7c478bd9Sstevel@tonic-gate /* For progressmeter() -- number of seconds before xfer considered "stalled" */ 99*7c478bd9Sstevel@tonic-gate #define STALLTIME 5 100*7c478bd9Sstevel@tonic-gate /* alarm() interval for updating progress meter */ 101*7c478bd9Sstevel@tonic-gate #define PROGRESSTIME 1 102*7c478bd9Sstevel@tonic-gate 103*7c478bd9Sstevel@tonic-gate /* Visual statistics about files as they are transferred. */ 104*7c478bd9Sstevel@tonic-gate void progressmeter(int); 105*7c478bd9Sstevel@tonic-gate 106*7c478bd9Sstevel@tonic-gate /* Returns width of the terminal (for progress meter calculations). */ 107*7c478bd9Sstevel@tonic-gate int getttywidth(void); 108*7c478bd9Sstevel@tonic-gate int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, 109*7c478bd9Sstevel@tonic-gate int argc); 110*7c478bd9Sstevel@tonic-gate 111*7c478bd9Sstevel@tonic-gate /* Struct for addargs */ 112*7c478bd9Sstevel@tonic-gate arglist args; 113*7c478bd9Sstevel@tonic-gate 114*7c478bd9Sstevel@tonic-gate /* Time a transfer started. */ 115*7c478bd9Sstevel@tonic-gate static struct timeval start; 116*7c478bd9Sstevel@tonic-gate 117*7c478bd9Sstevel@tonic-gate /* Number of bytes of current file transferred so far. */ 118*7c478bd9Sstevel@tonic-gate volatile off_t statbytes; 119*7c478bd9Sstevel@tonic-gate 120*7c478bd9Sstevel@tonic-gate /* Total size of current file. */ 121*7c478bd9Sstevel@tonic-gate off_t totalbytes = 0; 122*7c478bd9Sstevel@tonic-gate 123*7c478bd9Sstevel@tonic-gate /* Name of current file being transferred. */ 124*7c478bd9Sstevel@tonic-gate char *curfile; 125*7c478bd9Sstevel@tonic-gate 126*7c478bd9Sstevel@tonic-gate /* This is set to non-zero to enable verbose mode. */ 127*7c478bd9Sstevel@tonic-gate int verbose_mode = 0; 128*7c478bd9Sstevel@tonic-gate 129*7c478bd9Sstevel@tonic-gate /* This is set to zero if the progressmeter is not desired. */ 130*7c478bd9Sstevel@tonic-gate int showprogress = 1; 131*7c478bd9Sstevel@tonic-gate 132*7c478bd9Sstevel@tonic-gate /* This is the program to execute for the secured connection. ("ssh" or -S) */ 133*7c478bd9Sstevel@tonic-gate char *ssh_program = _PATH_SSH_PROGRAM; 134*7c478bd9Sstevel@tonic-gate 135*7c478bd9Sstevel@tonic-gate /* This is used to store the pid of ssh_program */ 136*7c478bd9Sstevel@tonic-gate static pid_t do_cmd_pid; 137*7c478bd9Sstevel@tonic-gate 138*7c478bd9Sstevel@tonic-gate /* 139*7c478bd9Sstevel@tonic-gate * This function executes the given command as the specified user on the 140*7c478bd9Sstevel@tonic-gate * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 141*7c478bd9Sstevel@tonic-gate * assigns the input and output file descriptors on success. 142*7c478bd9Sstevel@tonic-gate */ 143*7c478bd9Sstevel@tonic-gate 144*7c478bd9Sstevel@tonic-gate int 145*7c478bd9Sstevel@tonic-gate do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) 146*7c478bd9Sstevel@tonic-gate { 147*7c478bd9Sstevel@tonic-gate int pin[2], pout[2], reserved[2]; 148*7c478bd9Sstevel@tonic-gate 149*7c478bd9Sstevel@tonic-gate if (verbose_mode) 150*7c478bd9Sstevel@tonic-gate fprintf(stderr, 151*7c478bd9Sstevel@tonic-gate gettext("Executing: program %s host %s, " 152*7c478bd9Sstevel@tonic-gate "user %s, command %s\n"), 153*7c478bd9Sstevel@tonic-gate ssh_program, host, 154*7c478bd9Sstevel@tonic-gate remuser ? remuser : gettext("(unspecified)"), cmd); 155*7c478bd9Sstevel@tonic-gate 156*7c478bd9Sstevel@tonic-gate /* 157*7c478bd9Sstevel@tonic-gate * Reserve two descriptors so that the real pipes won't get 158*7c478bd9Sstevel@tonic-gate * descriptors 0 and 1 because that will screw up dup2 below. 159*7c478bd9Sstevel@tonic-gate */ 160*7c478bd9Sstevel@tonic-gate pipe(reserved); 161*7c478bd9Sstevel@tonic-gate 162*7c478bd9Sstevel@tonic-gate /* Create a socket pair for communicating with ssh. */ 163*7c478bd9Sstevel@tonic-gate if (pipe(pin) < 0) 164*7c478bd9Sstevel@tonic-gate fatal("pipe: %s", strerror(errno)); 165*7c478bd9Sstevel@tonic-gate if (pipe(pout) < 0) 166*7c478bd9Sstevel@tonic-gate fatal("pipe: %s", strerror(errno)); 167*7c478bd9Sstevel@tonic-gate 168*7c478bd9Sstevel@tonic-gate /* Free the reserved descriptors. */ 169*7c478bd9Sstevel@tonic-gate close(reserved[0]); 170*7c478bd9Sstevel@tonic-gate close(reserved[1]); 171*7c478bd9Sstevel@tonic-gate 172*7c478bd9Sstevel@tonic-gate /* For a child to execute the command on the remote host using ssh. */ 173*7c478bd9Sstevel@tonic-gate if ((do_cmd_pid = fork()) == 0) { 174*7c478bd9Sstevel@tonic-gate /* Child. */ 175*7c478bd9Sstevel@tonic-gate close(pin[1]); 176*7c478bd9Sstevel@tonic-gate close(pout[0]); 177*7c478bd9Sstevel@tonic-gate dup2(pin[0], 0); 178*7c478bd9Sstevel@tonic-gate dup2(pout[1], 1); 179*7c478bd9Sstevel@tonic-gate close(pin[0]); 180*7c478bd9Sstevel@tonic-gate close(pout[1]); 181*7c478bd9Sstevel@tonic-gate 182*7c478bd9Sstevel@tonic-gate args.list[0] = ssh_program; 183*7c478bd9Sstevel@tonic-gate if (remuser != NULL) 184*7c478bd9Sstevel@tonic-gate addargs(&args, "-l%s", remuser); 185*7c478bd9Sstevel@tonic-gate addargs(&args, "%s", host); 186*7c478bd9Sstevel@tonic-gate addargs(&args, "%s", cmd); 187*7c478bd9Sstevel@tonic-gate 188*7c478bd9Sstevel@tonic-gate execvp(ssh_program, args.list); 189*7c478bd9Sstevel@tonic-gate perror(ssh_program); 190*7c478bd9Sstevel@tonic-gate exit(1); 191*7c478bd9Sstevel@tonic-gate } else if (do_cmd_pid == (pid_t)-1) { 192*7c478bd9Sstevel@tonic-gate /* fork() failed */ 193*7c478bd9Sstevel@tonic-gate fatal("fork: %s", strerror(errno)); 194*7c478bd9Sstevel@tonic-gate } 195*7c478bd9Sstevel@tonic-gate 196*7c478bd9Sstevel@tonic-gate /* Parent. Close the other side, and return the local side. */ 197*7c478bd9Sstevel@tonic-gate close(pin[0]); 198*7c478bd9Sstevel@tonic-gate *fdout = pin[1]; 199*7c478bd9Sstevel@tonic-gate close(pout[1]); 200*7c478bd9Sstevel@tonic-gate *fdin = pout[0]; 201*7c478bd9Sstevel@tonic-gate return (0); 202*7c478bd9Sstevel@tonic-gate } 203*7c478bd9Sstevel@tonic-gate 204*7c478bd9Sstevel@tonic-gate typedef struct { 205*7c478bd9Sstevel@tonic-gate int cnt; 206*7c478bd9Sstevel@tonic-gate char *buf; 207*7c478bd9Sstevel@tonic-gate } BUF; 208*7c478bd9Sstevel@tonic-gate 209*7c478bd9Sstevel@tonic-gate BUF *allocbuf(BUF *, int, int); 210*7c478bd9Sstevel@tonic-gate void lostconn(int); 211*7c478bd9Sstevel@tonic-gate void nospace(void); 212*7c478bd9Sstevel@tonic-gate int okname(char *); 213*7c478bd9Sstevel@tonic-gate void run_err(const char *, ...); 214*7c478bd9Sstevel@tonic-gate void verifydir(char *); 215*7c478bd9Sstevel@tonic-gate 216*7c478bd9Sstevel@tonic-gate struct passwd *pwd; 217*7c478bd9Sstevel@tonic-gate uid_t userid; 218*7c478bd9Sstevel@tonic-gate int errs, remin, remout; 219*7c478bd9Sstevel@tonic-gate int pflag, iamremote, iamrecursive, targetshouldbedirectory; 220*7c478bd9Sstevel@tonic-gate 221*7c478bd9Sstevel@tonic-gate #define CMDNEEDS 64 222*7c478bd9Sstevel@tonic-gate char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 223*7c478bd9Sstevel@tonic-gate 224*7c478bd9Sstevel@tonic-gate int response(void); 225*7c478bd9Sstevel@tonic-gate void rsource(char *, struct stat *); 226*7c478bd9Sstevel@tonic-gate void sink(int, char *[]); 227*7c478bd9Sstevel@tonic-gate void source(int, char *[]); 228*7c478bd9Sstevel@tonic-gate void tolocal(int, char *[]); 229*7c478bd9Sstevel@tonic-gate void toremote(char *, int, char *[]); 230*7c478bd9Sstevel@tonic-gate void usage(void); 231*7c478bd9Sstevel@tonic-gate 232*7c478bd9Sstevel@tonic-gate int 233*7c478bd9Sstevel@tonic-gate main(argc, argv) 234*7c478bd9Sstevel@tonic-gate int argc; 235*7c478bd9Sstevel@tonic-gate char *argv[]; 236*7c478bd9Sstevel@tonic-gate { 237*7c478bd9Sstevel@tonic-gate int ch, fflag, tflag, status; 238*7c478bd9Sstevel@tonic-gate char *targ; 239*7c478bd9Sstevel@tonic-gate extern char *optarg; 240*7c478bd9Sstevel@tonic-gate extern int optind; 241*7c478bd9Sstevel@tonic-gate 242*7c478bd9Sstevel@tonic-gate __progname = get_progname(argv[0]); 243*7c478bd9Sstevel@tonic-gate 244*7c478bd9Sstevel@tonic-gate g11n_setlocale(LC_ALL, ""); 245*7c478bd9Sstevel@tonic-gate 246*7c478bd9Sstevel@tonic-gate args.list = NULL; 247*7c478bd9Sstevel@tonic-gate addargs(&args, "ssh"); /* overwritten with ssh_program */ 248*7c478bd9Sstevel@tonic-gate addargs(&args, "-x"); 249*7c478bd9Sstevel@tonic-gate addargs(&args, "-oForwardAgent no"); 250*7c478bd9Sstevel@tonic-gate addargs(&args, "-oClearAllForwardings yes"); 251*7c478bd9Sstevel@tonic-gate 252*7c478bd9Sstevel@tonic-gate fflag = tflag = 0; 253*7c478bd9Sstevel@tonic-gate while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:F:")) != -1) 254*7c478bd9Sstevel@tonic-gate switch (ch) { 255*7c478bd9Sstevel@tonic-gate /* User-visible flags. */ 256*7c478bd9Sstevel@tonic-gate case '4': 257*7c478bd9Sstevel@tonic-gate case '6': 258*7c478bd9Sstevel@tonic-gate case 'C': 259*7c478bd9Sstevel@tonic-gate addargs(&args, "-%c", ch); 260*7c478bd9Sstevel@tonic-gate break; 261*7c478bd9Sstevel@tonic-gate case 'o': 262*7c478bd9Sstevel@tonic-gate case 'c': 263*7c478bd9Sstevel@tonic-gate case 'i': 264*7c478bd9Sstevel@tonic-gate case 'F': 265*7c478bd9Sstevel@tonic-gate addargs(&args, "-%c%s", ch, optarg); 266*7c478bd9Sstevel@tonic-gate break; 267*7c478bd9Sstevel@tonic-gate case 'P': 268*7c478bd9Sstevel@tonic-gate addargs(&args, "-p%s", optarg); 269*7c478bd9Sstevel@tonic-gate break; 270*7c478bd9Sstevel@tonic-gate case 'B': 271*7c478bd9Sstevel@tonic-gate addargs(&args, "-oBatchmode yes"); 272*7c478bd9Sstevel@tonic-gate break; 273*7c478bd9Sstevel@tonic-gate case 'p': 274*7c478bd9Sstevel@tonic-gate pflag = 1; 275*7c478bd9Sstevel@tonic-gate break; 276*7c478bd9Sstevel@tonic-gate case 'r': 277*7c478bd9Sstevel@tonic-gate iamrecursive = 1; 278*7c478bd9Sstevel@tonic-gate break; 279*7c478bd9Sstevel@tonic-gate case 'S': 280*7c478bd9Sstevel@tonic-gate ssh_program = xstrdup(optarg); 281*7c478bd9Sstevel@tonic-gate break; 282*7c478bd9Sstevel@tonic-gate case 'v': 283*7c478bd9Sstevel@tonic-gate addargs(&args, "-v"); 284*7c478bd9Sstevel@tonic-gate verbose_mode = 1; 285*7c478bd9Sstevel@tonic-gate break; 286*7c478bd9Sstevel@tonic-gate case 'q': 287*7c478bd9Sstevel@tonic-gate showprogress = 0; 288*7c478bd9Sstevel@tonic-gate break; 289*7c478bd9Sstevel@tonic-gate 290*7c478bd9Sstevel@tonic-gate /* Server options. */ 291*7c478bd9Sstevel@tonic-gate case 'd': 292*7c478bd9Sstevel@tonic-gate targetshouldbedirectory = 1; 293*7c478bd9Sstevel@tonic-gate break; 294*7c478bd9Sstevel@tonic-gate case 'f': /* "from" */ 295*7c478bd9Sstevel@tonic-gate iamremote = 1; 296*7c478bd9Sstevel@tonic-gate fflag = 1; 297*7c478bd9Sstevel@tonic-gate break; 298*7c478bd9Sstevel@tonic-gate case 't': /* "to" */ 299*7c478bd9Sstevel@tonic-gate iamremote = 1; 300*7c478bd9Sstevel@tonic-gate tflag = 1; 301*7c478bd9Sstevel@tonic-gate #ifdef HAVE_CYGWIN 302*7c478bd9Sstevel@tonic-gate setmode(0, O_BINARY); 303*7c478bd9Sstevel@tonic-gate #endif 304*7c478bd9Sstevel@tonic-gate break; 305*7c478bd9Sstevel@tonic-gate default: 306*7c478bd9Sstevel@tonic-gate usage(); 307*7c478bd9Sstevel@tonic-gate } 308*7c478bd9Sstevel@tonic-gate argc -= optind; 309*7c478bd9Sstevel@tonic-gate argv += optind; 310*7c478bd9Sstevel@tonic-gate 311*7c478bd9Sstevel@tonic-gate if ((pwd = getpwuid(userid = getuid())) == NULL) 312*7c478bd9Sstevel@tonic-gate fatal("unknown user %d", (int)userid); 313*7c478bd9Sstevel@tonic-gate 314*7c478bd9Sstevel@tonic-gate if (!isatty(STDERR_FILENO)) 315*7c478bd9Sstevel@tonic-gate showprogress = 0; 316*7c478bd9Sstevel@tonic-gate 317*7c478bd9Sstevel@tonic-gate remin = STDIN_FILENO; 318*7c478bd9Sstevel@tonic-gate remout = STDOUT_FILENO; 319*7c478bd9Sstevel@tonic-gate 320*7c478bd9Sstevel@tonic-gate if (fflag) { 321*7c478bd9Sstevel@tonic-gate /* Follow "protocol", send data. */ 322*7c478bd9Sstevel@tonic-gate (void) response(); 323*7c478bd9Sstevel@tonic-gate source(argc, argv); 324*7c478bd9Sstevel@tonic-gate exit(errs != 0); 325*7c478bd9Sstevel@tonic-gate } 326*7c478bd9Sstevel@tonic-gate if (tflag) { 327*7c478bd9Sstevel@tonic-gate /* Receive data. */ 328*7c478bd9Sstevel@tonic-gate sink(argc, argv); 329*7c478bd9Sstevel@tonic-gate exit(errs != 0); 330*7c478bd9Sstevel@tonic-gate } 331*7c478bd9Sstevel@tonic-gate if (argc < 2) 332*7c478bd9Sstevel@tonic-gate usage(); 333*7c478bd9Sstevel@tonic-gate if (argc > 2) 334*7c478bd9Sstevel@tonic-gate targetshouldbedirectory = 1; 335*7c478bd9Sstevel@tonic-gate 336*7c478bd9Sstevel@tonic-gate remin = remout = -1; 337*7c478bd9Sstevel@tonic-gate do_cmd_pid = (pid_t)-1; 338*7c478bd9Sstevel@tonic-gate 339*7c478bd9Sstevel@tonic-gate /* Command to be executed on remote system using "ssh". */ 340*7c478bd9Sstevel@tonic-gate (void) snprintf(cmd, sizeof (cmd), "scp%s%s%s%s", 341*7c478bd9Sstevel@tonic-gate verbose_mode ? " -v" : "", 342*7c478bd9Sstevel@tonic-gate iamrecursive ? " -r" : "", pflag ? " -p" : "", 343*7c478bd9Sstevel@tonic-gate targetshouldbedirectory ? " -d" : ""); 344*7c478bd9Sstevel@tonic-gate 345*7c478bd9Sstevel@tonic-gate (void) signal(SIGPIPE, lostconn); 346*7c478bd9Sstevel@tonic-gate 347*7c478bd9Sstevel@tonic-gate if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 348*7c478bd9Sstevel@tonic-gate toremote(targ, argc, argv); 349*7c478bd9Sstevel@tonic-gate else { 350*7c478bd9Sstevel@tonic-gate tolocal(argc, argv); /* Dest is local host. */ 351*7c478bd9Sstevel@tonic-gate if (targetshouldbedirectory) 352*7c478bd9Sstevel@tonic-gate verifydir(argv[argc - 1]); 353*7c478bd9Sstevel@tonic-gate } 354*7c478bd9Sstevel@tonic-gate /* 355*7c478bd9Sstevel@tonic-gate * Finally check the exit status of the ssh process, if one was forked 356*7c478bd9Sstevel@tonic-gate * and no error has occurred yet 357*7c478bd9Sstevel@tonic-gate */ 358*7c478bd9Sstevel@tonic-gate if (do_cmd_pid != (pid_t)-1 && errs == 0) { 359*7c478bd9Sstevel@tonic-gate if (remin != -1) { 360*7c478bd9Sstevel@tonic-gate (void) close(remin); 361*7c478bd9Sstevel@tonic-gate } 362*7c478bd9Sstevel@tonic-gate if (remout != -1) { 363*7c478bd9Sstevel@tonic-gate (void) close(remout); 364*7c478bd9Sstevel@tonic-gate } 365*7c478bd9Sstevel@tonic-gate if (waitpid(do_cmd_pid, &status, 0) == -1) { 366*7c478bd9Sstevel@tonic-gate errs = 1; 367*7c478bd9Sstevel@tonic-gate } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 368*7c478bd9Sstevel@tonic-gate errs = 1; 369*7c478bd9Sstevel@tonic-gate } 370*7c478bd9Sstevel@tonic-gate } 371*7c478bd9Sstevel@tonic-gate 372*7c478bd9Sstevel@tonic-gate return (errs != 0); 373*7c478bd9Sstevel@tonic-gate } 374*7c478bd9Sstevel@tonic-gate 375*7c478bd9Sstevel@tonic-gate void 376*7c478bd9Sstevel@tonic-gate toremote(targ, argc, argv) 377*7c478bd9Sstevel@tonic-gate char *targ, *argv[]; 378*7c478bd9Sstevel@tonic-gate int argc; 379*7c478bd9Sstevel@tonic-gate { 380*7c478bd9Sstevel@tonic-gate int i, len; 381*7c478bd9Sstevel@tonic-gate char *bp, *host, *src, *suser, *thost, *tuser; 382*7c478bd9Sstevel@tonic-gate 383*7c478bd9Sstevel@tonic-gate *targ++ = 0; 384*7c478bd9Sstevel@tonic-gate if (*targ == 0) 385*7c478bd9Sstevel@tonic-gate targ = "."; 386*7c478bd9Sstevel@tonic-gate 387*7c478bd9Sstevel@tonic-gate if ((thost = strchr(argv[argc - 1], '@'))) { 388*7c478bd9Sstevel@tonic-gate /* user@host */ 389*7c478bd9Sstevel@tonic-gate *thost++ = 0; 390*7c478bd9Sstevel@tonic-gate tuser = argv[argc - 1]; 391*7c478bd9Sstevel@tonic-gate if (*tuser == '\0') 392*7c478bd9Sstevel@tonic-gate tuser = NULL; 393*7c478bd9Sstevel@tonic-gate else if (!okname(tuser)) 394*7c478bd9Sstevel@tonic-gate exit(1); 395*7c478bd9Sstevel@tonic-gate } else { 396*7c478bd9Sstevel@tonic-gate thost = argv[argc - 1]; 397*7c478bd9Sstevel@tonic-gate tuser = NULL; 398*7c478bd9Sstevel@tonic-gate } 399*7c478bd9Sstevel@tonic-gate 400*7c478bd9Sstevel@tonic-gate for (i = 0; i < argc - 1; i++) { 401*7c478bd9Sstevel@tonic-gate src = colon(argv[i]); 402*7c478bd9Sstevel@tonic-gate if (src) { /* remote to remote */ 403*7c478bd9Sstevel@tonic-gate static char *ssh_options = 404*7c478bd9Sstevel@tonic-gate "-x -o'ClearAllForwardings yes'"; 405*7c478bd9Sstevel@tonic-gate *src++ = 0; 406*7c478bd9Sstevel@tonic-gate if (*src == 0) 407*7c478bd9Sstevel@tonic-gate src = "."; 408*7c478bd9Sstevel@tonic-gate host = strchr(argv[i], '@'); 409*7c478bd9Sstevel@tonic-gate len = strlen(ssh_program) + strlen(argv[i]) + 410*7c478bd9Sstevel@tonic-gate strlen(src) + (tuser ? strlen(tuser) : 0) + 411*7c478bd9Sstevel@tonic-gate strlen(thost) + strlen(targ) + 412*7c478bd9Sstevel@tonic-gate strlen(ssh_options) + CMDNEEDS + 20; 413*7c478bd9Sstevel@tonic-gate bp = xmalloc(len); 414*7c478bd9Sstevel@tonic-gate if (host) { 415*7c478bd9Sstevel@tonic-gate *host++ = 0; 416*7c478bd9Sstevel@tonic-gate host = cleanhostname(host); 417*7c478bd9Sstevel@tonic-gate suser = argv[i]; 418*7c478bd9Sstevel@tonic-gate if (*suser == '\0') 419*7c478bd9Sstevel@tonic-gate suser = pwd->pw_name; 420*7c478bd9Sstevel@tonic-gate else if (!okname(suser)) 421*7c478bd9Sstevel@tonic-gate continue; 422*7c478bd9Sstevel@tonic-gate snprintf(bp, len, 423*7c478bd9Sstevel@tonic-gate "%s%s %s -n " 424*7c478bd9Sstevel@tonic-gate "-l %s %s %s %s '%s%s%s:%s'", 425*7c478bd9Sstevel@tonic-gate ssh_program, verbose_mode ? " -v" : "", 426*7c478bd9Sstevel@tonic-gate ssh_options, suser, host, cmd, src, 427*7c478bd9Sstevel@tonic-gate tuser ? tuser : "", tuser ? "@" : "", 428*7c478bd9Sstevel@tonic-gate thost, targ); 429*7c478bd9Sstevel@tonic-gate } else { 430*7c478bd9Sstevel@tonic-gate host = cleanhostname(argv[i]); 431*7c478bd9Sstevel@tonic-gate snprintf(bp, len, 432*7c478bd9Sstevel@tonic-gate "exec %s%s %s -n %s " 433*7c478bd9Sstevel@tonic-gate "%s %s '%s%s%s:%s'", 434*7c478bd9Sstevel@tonic-gate ssh_program, verbose_mode ? " -v" : "", 435*7c478bd9Sstevel@tonic-gate ssh_options, host, cmd, src, 436*7c478bd9Sstevel@tonic-gate tuser ? tuser : "", tuser ? "@" : "", 437*7c478bd9Sstevel@tonic-gate thost, targ); 438*7c478bd9Sstevel@tonic-gate } 439*7c478bd9Sstevel@tonic-gate if (verbose_mode) 440*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext("Executing: %s\n"), bp); 441*7c478bd9Sstevel@tonic-gate (void) system(bp); 442*7c478bd9Sstevel@tonic-gate (void) xfree(bp); 443*7c478bd9Sstevel@tonic-gate } else { /* local to remote */ 444*7c478bd9Sstevel@tonic-gate if (remin == -1) { 445*7c478bd9Sstevel@tonic-gate len = strlen(targ) + CMDNEEDS + 20; 446*7c478bd9Sstevel@tonic-gate bp = xmalloc(len); 447*7c478bd9Sstevel@tonic-gate (void) snprintf(bp, len, "%s -t %s", cmd, targ); 448*7c478bd9Sstevel@tonic-gate host = cleanhostname(thost); 449*7c478bd9Sstevel@tonic-gate if (do_cmd(host, tuser, bp, &remin, 450*7c478bd9Sstevel@tonic-gate &remout, argc) < 0) 451*7c478bd9Sstevel@tonic-gate exit(1); 452*7c478bd9Sstevel@tonic-gate if (response() < 0) 453*7c478bd9Sstevel@tonic-gate exit(1); 454*7c478bd9Sstevel@tonic-gate (void) xfree(bp); 455*7c478bd9Sstevel@tonic-gate } 456*7c478bd9Sstevel@tonic-gate source(1, argv + i); 457*7c478bd9Sstevel@tonic-gate } 458*7c478bd9Sstevel@tonic-gate } 459*7c478bd9Sstevel@tonic-gate } 460*7c478bd9Sstevel@tonic-gate 461*7c478bd9Sstevel@tonic-gate void 462*7c478bd9Sstevel@tonic-gate tolocal(argc, argv) 463*7c478bd9Sstevel@tonic-gate int argc; 464*7c478bd9Sstevel@tonic-gate char *argv[]; 465*7c478bd9Sstevel@tonic-gate { 466*7c478bd9Sstevel@tonic-gate int i, len; 467*7c478bd9Sstevel@tonic-gate char *bp, *host, *src, *suser; 468*7c478bd9Sstevel@tonic-gate 469*7c478bd9Sstevel@tonic-gate for (i = 0; i < argc - 1; i++) { 470*7c478bd9Sstevel@tonic-gate if (!(src = colon(argv[i]))) { /* Local to local. */ 471*7c478bd9Sstevel@tonic-gate len = strlen(_PATH_CP) + strlen(argv[i]) + 472*7c478bd9Sstevel@tonic-gate strlen(argv[argc - 1]) + 20; 473*7c478bd9Sstevel@tonic-gate bp = xmalloc(len); 474*7c478bd9Sstevel@tonic-gate (void) snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, 475*7c478bd9Sstevel@tonic-gate iamrecursive ? " -r" : "", pflag ? " -p" : "", 476*7c478bd9Sstevel@tonic-gate argv[i], argv[argc - 1]); 477*7c478bd9Sstevel@tonic-gate if (verbose_mode) 478*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext("Executing: %s\n"), bp); 479*7c478bd9Sstevel@tonic-gate if (system(bp)) 480*7c478bd9Sstevel@tonic-gate ++errs; 481*7c478bd9Sstevel@tonic-gate (void) xfree(bp); 482*7c478bd9Sstevel@tonic-gate continue; 483*7c478bd9Sstevel@tonic-gate } 484*7c478bd9Sstevel@tonic-gate *src++ = 0; 485*7c478bd9Sstevel@tonic-gate if (*src == 0) 486*7c478bd9Sstevel@tonic-gate src = "."; 487*7c478bd9Sstevel@tonic-gate if ((host = strchr(argv[i], '@')) == NULL) { 488*7c478bd9Sstevel@tonic-gate host = argv[i]; 489*7c478bd9Sstevel@tonic-gate suser = NULL; 490*7c478bd9Sstevel@tonic-gate } else { 491*7c478bd9Sstevel@tonic-gate *host++ = 0; 492*7c478bd9Sstevel@tonic-gate suser = argv[i]; 493*7c478bd9Sstevel@tonic-gate if (*suser == '\0') 494*7c478bd9Sstevel@tonic-gate suser = pwd->pw_name; 495*7c478bd9Sstevel@tonic-gate else if (!okname(suser)) 496*7c478bd9Sstevel@tonic-gate continue; 497*7c478bd9Sstevel@tonic-gate } 498*7c478bd9Sstevel@tonic-gate host = cleanhostname(host); 499*7c478bd9Sstevel@tonic-gate len = strlen(src) + CMDNEEDS + 20; 500*7c478bd9Sstevel@tonic-gate bp = xmalloc(len); 501*7c478bd9Sstevel@tonic-gate (void) snprintf(bp, len, "%s -f %s", cmd, src); 502*7c478bd9Sstevel@tonic-gate if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) { 503*7c478bd9Sstevel@tonic-gate (void) xfree(bp); 504*7c478bd9Sstevel@tonic-gate ++errs; 505*7c478bd9Sstevel@tonic-gate continue; 506*7c478bd9Sstevel@tonic-gate } 507*7c478bd9Sstevel@tonic-gate xfree(bp); 508*7c478bd9Sstevel@tonic-gate sink(1, argv + argc - 1); 509*7c478bd9Sstevel@tonic-gate (void) close(remin); 510*7c478bd9Sstevel@tonic-gate remin = remout = -1; 511*7c478bd9Sstevel@tonic-gate } 512*7c478bd9Sstevel@tonic-gate } 513*7c478bd9Sstevel@tonic-gate 514*7c478bd9Sstevel@tonic-gate void 515*7c478bd9Sstevel@tonic-gate source(argc, argv) 516*7c478bd9Sstevel@tonic-gate int argc; 517*7c478bd9Sstevel@tonic-gate char *argv[]; 518*7c478bd9Sstevel@tonic-gate { 519*7c478bd9Sstevel@tonic-gate struct stat stb; 520*7c478bd9Sstevel@tonic-gate static BUF buffer; 521*7c478bd9Sstevel@tonic-gate BUF *bp; 522*7c478bd9Sstevel@tonic-gate off_t i, amt, result; 523*7c478bd9Sstevel@tonic-gate int fd, haderr, indx; 524*7c478bd9Sstevel@tonic-gate char *last, *name, buf[2048]; 525*7c478bd9Sstevel@tonic-gate int len; 526*7c478bd9Sstevel@tonic-gate 527*7c478bd9Sstevel@tonic-gate for (indx = 0; indx < argc; ++indx) { 528*7c478bd9Sstevel@tonic-gate name = argv[indx]; 529*7c478bd9Sstevel@tonic-gate statbytes = 0; 530*7c478bd9Sstevel@tonic-gate len = strlen(name); 531*7c478bd9Sstevel@tonic-gate while (len > 1 && name[len-1] == '/') 532*7c478bd9Sstevel@tonic-gate name[--len] = '\0'; 533*7c478bd9Sstevel@tonic-gate if (strchr(name, '\n') != NULL) { 534*7c478bd9Sstevel@tonic-gate run_err("%s: skipping, filename contains a newline", 535*7c478bd9Sstevel@tonic-gate name); 536*7c478bd9Sstevel@tonic-gate goto next; 537*7c478bd9Sstevel@tonic-gate } 538*7c478bd9Sstevel@tonic-gate if ((fd = open(name, O_RDONLY, 0)) < 0) 539*7c478bd9Sstevel@tonic-gate goto syserr; 540*7c478bd9Sstevel@tonic-gate if (fstat(fd, &stb) < 0) { 541*7c478bd9Sstevel@tonic-gate syserr: run_err("%s: %s", name, strerror(errno)); 542*7c478bd9Sstevel@tonic-gate goto next; 543*7c478bd9Sstevel@tonic-gate } 544*7c478bd9Sstevel@tonic-gate switch (stb.st_mode & S_IFMT) { 545*7c478bd9Sstevel@tonic-gate case S_IFREG: 546*7c478bd9Sstevel@tonic-gate break; 547*7c478bd9Sstevel@tonic-gate case S_IFDIR: 548*7c478bd9Sstevel@tonic-gate if (iamrecursive) { 549*7c478bd9Sstevel@tonic-gate rsource(name, &stb); 550*7c478bd9Sstevel@tonic-gate goto next; 551*7c478bd9Sstevel@tonic-gate } 552*7c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 553*7c478bd9Sstevel@tonic-gate default: 554*7c478bd9Sstevel@tonic-gate run_err("%s: not a regular file", name); 555*7c478bd9Sstevel@tonic-gate goto next; 556*7c478bd9Sstevel@tonic-gate } 557*7c478bd9Sstevel@tonic-gate if ((last = strrchr(name, '/')) == NULL) 558*7c478bd9Sstevel@tonic-gate last = name; 559*7c478bd9Sstevel@tonic-gate else 560*7c478bd9Sstevel@tonic-gate ++last; 561*7c478bd9Sstevel@tonic-gate curfile = last; 562*7c478bd9Sstevel@tonic-gate if (pflag) { 563*7c478bd9Sstevel@tonic-gate /* 564*7c478bd9Sstevel@tonic-gate * Make it compatible with possible future 565*7c478bd9Sstevel@tonic-gate * versions expecting microseconds. 566*7c478bd9Sstevel@tonic-gate */ 567*7c478bd9Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "T%lu 0 %lu 0\n", 568*7c478bd9Sstevel@tonic-gate (ulong_t)stb.st_mtime, 569*7c478bd9Sstevel@tonic-gate (ulong_t)stb.st_atime); 570*7c478bd9Sstevel@tonic-gate (void) atomicio(write, remout, buf, strlen(buf)); 571*7c478bd9Sstevel@tonic-gate if (response() < 0) 572*7c478bd9Sstevel@tonic-gate goto next; 573*7c478bd9Sstevel@tonic-gate } 574*7c478bd9Sstevel@tonic-gate #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 575*7c478bd9Sstevel@tonic-gate #ifdef HAVE_LONG_LONG_INT 576*7c478bd9Sstevel@tonic-gate snprintf(buf, sizeof (buf), "C%04o %lld %s\n", 577*7c478bd9Sstevel@tonic-gate (uint_t)(stb.st_mode & FILEMODEMASK), 578*7c478bd9Sstevel@tonic-gate (long long)stb.st_size, last); 579*7c478bd9Sstevel@tonic-gate #else 580*7c478bd9Sstevel@tonic-gate /* XXX: Handle integer overflow? */ 581*7c478bd9Sstevel@tonic-gate snprintf(buf, sizeof (buf), "C%04o %lu %s\n", 582*7c478bd9Sstevel@tonic-gate (uint_t)(stb.st_mode & FILEMODEMASK), 583*7c478bd9Sstevel@tonic-gate (ulong_t)stb.st_size, last); 584*7c478bd9Sstevel@tonic-gate #endif 585*7c478bd9Sstevel@tonic-gate if (verbose_mode) { 586*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext("Sending file modes: %s"), buf); 587*7c478bd9Sstevel@tonic-gate fflush(stderr); 588*7c478bd9Sstevel@tonic-gate } 589*7c478bd9Sstevel@tonic-gate (void) atomicio(write, remout, buf, strlen(buf)); 590*7c478bd9Sstevel@tonic-gate if (response() < 0) 591*7c478bd9Sstevel@tonic-gate goto next; 592*7c478bd9Sstevel@tonic-gate if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { 593*7c478bd9Sstevel@tonic-gate next: (void) close(fd); 594*7c478bd9Sstevel@tonic-gate continue; 595*7c478bd9Sstevel@tonic-gate } 596*7c478bd9Sstevel@tonic-gate if (showprogress) { 597*7c478bd9Sstevel@tonic-gate totalbytes = stb.st_size; 598*7c478bd9Sstevel@tonic-gate progressmeter(-1); 599*7c478bd9Sstevel@tonic-gate } 600*7c478bd9Sstevel@tonic-gate /* Keep writing after an error so that we stay sync'd up. */ 601*7c478bd9Sstevel@tonic-gate for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 602*7c478bd9Sstevel@tonic-gate amt = bp->cnt; 603*7c478bd9Sstevel@tonic-gate if (i + amt > stb.st_size) 604*7c478bd9Sstevel@tonic-gate amt = stb.st_size - i; 605*7c478bd9Sstevel@tonic-gate if (!haderr) { 606*7c478bd9Sstevel@tonic-gate result = atomicio(read, fd, bp->buf, amt); 607*7c478bd9Sstevel@tonic-gate if (result != amt) 608*7c478bd9Sstevel@tonic-gate haderr = result >= 0 ? EIO : errno; 609*7c478bd9Sstevel@tonic-gate } 610*7c478bd9Sstevel@tonic-gate if (haderr) 611*7c478bd9Sstevel@tonic-gate (void) atomicio(write, remout, bp->buf, amt); 612*7c478bd9Sstevel@tonic-gate else { 613*7c478bd9Sstevel@tonic-gate result = atomicio(write, remout, bp->buf, amt); 614*7c478bd9Sstevel@tonic-gate if (result != amt) 615*7c478bd9Sstevel@tonic-gate haderr = result >= 0 ? EIO : errno; 616*7c478bd9Sstevel@tonic-gate statbytes += result; 617*7c478bd9Sstevel@tonic-gate } 618*7c478bd9Sstevel@tonic-gate } 619*7c478bd9Sstevel@tonic-gate if (showprogress) 620*7c478bd9Sstevel@tonic-gate progressmeter(1); 621*7c478bd9Sstevel@tonic-gate 622*7c478bd9Sstevel@tonic-gate if (close(fd) < 0 && !haderr) 623*7c478bd9Sstevel@tonic-gate haderr = errno; 624*7c478bd9Sstevel@tonic-gate if (!haderr) 625*7c478bd9Sstevel@tonic-gate (void) atomicio(write, remout, "", 1); 626*7c478bd9Sstevel@tonic-gate else 627*7c478bd9Sstevel@tonic-gate run_err("%s: %s", name, strerror(haderr)); 628*7c478bd9Sstevel@tonic-gate (void) response(); 629*7c478bd9Sstevel@tonic-gate } 630*7c478bd9Sstevel@tonic-gate } 631*7c478bd9Sstevel@tonic-gate 632*7c478bd9Sstevel@tonic-gate void 633*7c478bd9Sstevel@tonic-gate rsource(name, statp) 634*7c478bd9Sstevel@tonic-gate char *name; 635*7c478bd9Sstevel@tonic-gate struct stat *statp; 636*7c478bd9Sstevel@tonic-gate { 637*7c478bd9Sstevel@tonic-gate DIR *dirp; 638*7c478bd9Sstevel@tonic-gate struct dirent *dp; 639*7c478bd9Sstevel@tonic-gate char *last, *vect[1], path[1100]; 640*7c478bd9Sstevel@tonic-gate 641*7c478bd9Sstevel@tonic-gate if (!(dirp = opendir(name))) { 642*7c478bd9Sstevel@tonic-gate run_err("%s: %s", name, strerror(errno)); 643*7c478bd9Sstevel@tonic-gate return; 644*7c478bd9Sstevel@tonic-gate } 645*7c478bd9Sstevel@tonic-gate last = strrchr(name, '/'); 646*7c478bd9Sstevel@tonic-gate if (last == 0) 647*7c478bd9Sstevel@tonic-gate last = name; 648*7c478bd9Sstevel@tonic-gate else 649*7c478bd9Sstevel@tonic-gate last++; 650*7c478bd9Sstevel@tonic-gate if (pflag) { 651*7c478bd9Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "T%lu 0 %lu 0\n", 652*7c478bd9Sstevel@tonic-gate (ulong_t)statp->st_mtime, 653*7c478bd9Sstevel@tonic-gate (ulong_t)statp->st_atime); 654*7c478bd9Sstevel@tonic-gate (void) atomicio(write, remout, path, strlen(path)); 655*7c478bd9Sstevel@tonic-gate if (response() < 0) { 656*7c478bd9Sstevel@tonic-gate closedir(dirp); 657*7c478bd9Sstevel@tonic-gate return; 658*7c478bd9Sstevel@tonic-gate } 659*7c478bd9Sstevel@tonic-gate } 660*7c478bd9Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "D%04o %d %.1024s\n", 661*7c478bd9Sstevel@tonic-gate (uint_t)(statp->st_mode & FILEMODEMASK), 0, last); 662*7c478bd9Sstevel@tonic-gate if (verbose_mode) 663*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext("Entering directory: %s"), path); 664*7c478bd9Sstevel@tonic-gate (void) atomicio(write, remout, path, strlen(path)); 665*7c478bd9Sstevel@tonic-gate if (response() < 0) { 666*7c478bd9Sstevel@tonic-gate closedir(dirp); 667*7c478bd9Sstevel@tonic-gate return; 668*7c478bd9Sstevel@tonic-gate } 669*7c478bd9Sstevel@tonic-gate while ((dp = readdir(dirp)) != NULL) { 670*7c478bd9Sstevel@tonic-gate if (dp->d_ino == 0) 671*7c478bd9Sstevel@tonic-gate continue; 672*7c478bd9Sstevel@tonic-gate if ((strcmp(dp->d_name, ".") == 0) || 673*7c478bd9Sstevel@tonic-gate (strcmp(dp->d_name, "..") == 0)) 674*7c478bd9Sstevel@tonic-gate continue; 675*7c478bd9Sstevel@tonic-gate if (strlen(name) + 1 + strlen(dp->d_name) >= 676*7c478bd9Sstevel@tonic-gate sizeof (path) - 1) { 677*7c478bd9Sstevel@tonic-gate run_err("%s/%s: name too long", name, dp->d_name); 678*7c478bd9Sstevel@tonic-gate continue; 679*7c478bd9Sstevel@tonic-gate } 680*7c478bd9Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "%s/%s", name, dp->d_name); 681*7c478bd9Sstevel@tonic-gate vect[0] = path; 682*7c478bd9Sstevel@tonic-gate source(1, vect); 683*7c478bd9Sstevel@tonic-gate } 684*7c478bd9Sstevel@tonic-gate (void) closedir(dirp); 685*7c478bd9Sstevel@tonic-gate (void) atomicio(write, remout, "E\n", 2); 686*7c478bd9Sstevel@tonic-gate (void) response(); 687*7c478bd9Sstevel@tonic-gate } 688*7c478bd9Sstevel@tonic-gate 689*7c478bd9Sstevel@tonic-gate void 690*7c478bd9Sstevel@tonic-gate sink(argc, argv) 691*7c478bd9Sstevel@tonic-gate int argc; 692*7c478bd9Sstevel@tonic-gate char *argv[]; 693*7c478bd9Sstevel@tonic-gate { 694*7c478bd9Sstevel@tonic-gate static BUF buffer; 695*7c478bd9Sstevel@tonic-gate struct stat stb; 696*7c478bd9Sstevel@tonic-gate enum { 697*7c478bd9Sstevel@tonic-gate YES, NO, DISPLAYED 698*7c478bd9Sstevel@tonic-gate } wrerr; 699*7c478bd9Sstevel@tonic-gate BUF *bp; 700*7c478bd9Sstevel@tonic-gate off_t i, j; 701*7c478bd9Sstevel@tonic-gate int amt, count, exists, first, mask, mode, ofd, omode; 702*7c478bd9Sstevel@tonic-gate off_t size; 703*7c478bd9Sstevel@tonic-gate int setimes, targisdir, wrerrno = 0; 704*7c478bd9Sstevel@tonic-gate char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; 705*7c478bd9Sstevel@tonic-gate struct timeval tv[2]; 706*7c478bd9Sstevel@tonic-gate 707*7c478bd9Sstevel@tonic-gate #define atime tv[0] 708*7c478bd9Sstevel@tonic-gate #define mtime tv[1] 709*7c478bd9Sstevel@tonic-gate #define SCREWUP(str) { why = str; goto screwup; } 710*7c478bd9Sstevel@tonic-gate 711*7c478bd9Sstevel@tonic-gate setimes = targisdir = 0; 712*7c478bd9Sstevel@tonic-gate mask = umask(0); 713*7c478bd9Sstevel@tonic-gate if (!pflag) 714*7c478bd9Sstevel@tonic-gate (void) umask(mask); 715*7c478bd9Sstevel@tonic-gate if (argc != 1) { 716*7c478bd9Sstevel@tonic-gate run_err("ambiguous target"); 717*7c478bd9Sstevel@tonic-gate exit(1); 718*7c478bd9Sstevel@tonic-gate } 719*7c478bd9Sstevel@tonic-gate targ = *argv; 720*7c478bd9Sstevel@tonic-gate if (targetshouldbedirectory) 721*7c478bd9Sstevel@tonic-gate verifydir(targ); 722*7c478bd9Sstevel@tonic-gate 723*7c478bd9Sstevel@tonic-gate (void) atomicio(write, remout, "", 1); 724*7c478bd9Sstevel@tonic-gate if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 725*7c478bd9Sstevel@tonic-gate targisdir = 1; 726*7c478bd9Sstevel@tonic-gate for (first = 1; ; first = 0) { 727*7c478bd9Sstevel@tonic-gate cp = buf; 728*7c478bd9Sstevel@tonic-gate if (atomicio(read, remin, cp, 1) <= 0) 729*7c478bd9Sstevel@tonic-gate return; 730*7c478bd9Sstevel@tonic-gate if (*cp++ == '\n') 731*7c478bd9Sstevel@tonic-gate SCREWUP("unexpected <newline>") 732*7c478bd9Sstevel@tonic-gate do { 733*7c478bd9Sstevel@tonic-gate if (atomicio(read, remin, &ch, sizeof (ch)) != 734*7c478bd9Sstevel@tonic-gate sizeof (ch)) 735*7c478bd9Sstevel@tonic-gate SCREWUP("lost connection") 736*7c478bd9Sstevel@tonic-gate *cp++ = ch; 737*7c478bd9Sstevel@tonic-gate } while (cp < &buf[sizeof (buf) - 1] && ch != '\n'); 738*7c478bd9Sstevel@tonic-gate *cp = 0; 739*7c478bd9Sstevel@tonic-gate 740*7c478bd9Sstevel@tonic-gate if (buf[0] == '\01' || buf[0] == '\02') { 741*7c478bd9Sstevel@tonic-gate if (iamremote == 0) 742*7c478bd9Sstevel@tonic-gate (void) atomicio(write, STDERR_FILENO, 743*7c478bd9Sstevel@tonic-gate buf + 1, strlen(buf + 1)); 744*7c478bd9Sstevel@tonic-gate if (buf[0] == '\02') 745*7c478bd9Sstevel@tonic-gate exit(1); 746*7c478bd9Sstevel@tonic-gate ++errs; 747*7c478bd9Sstevel@tonic-gate continue; 748*7c478bd9Sstevel@tonic-gate } 749*7c478bd9Sstevel@tonic-gate if (buf[0] == 'E') { 750*7c478bd9Sstevel@tonic-gate (void) atomicio(write, remout, "", 1); 751*7c478bd9Sstevel@tonic-gate return; 752*7c478bd9Sstevel@tonic-gate } 753*7c478bd9Sstevel@tonic-gate if (ch == '\n') 754*7c478bd9Sstevel@tonic-gate *--cp = 0; 755*7c478bd9Sstevel@tonic-gate 756*7c478bd9Sstevel@tonic-gate cp = buf; 757*7c478bd9Sstevel@tonic-gate if (*cp == 'T') { 758*7c478bd9Sstevel@tonic-gate setimes++; 759*7c478bd9Sstevel@tonic-gate cp++; 760*7c478bd9Sstevel@tonic-gate mtime.tv_sec = strtol(cp, &cp, 10); 761*7c478bd9Sstevel@tonic-gate if (!cp || *cp++ != ' ') 762*7c478bd9Sstevel@tonic-gate SCREWUP("mtime.sec not delimited") 763*7c478bd9Sstevel@tonic-gate mtime.tv_usec = strtol(cp, &cp, 10); 764*7c478bd9Sstevel@tonic-gate if (!cp || *cp++ != ' ') 765*7c478bd9Sstevel@tonic-gate SCREWUP("mtime.usec not delimited") 766*7c478bd9Sstevel@tonic-gate atime.tv_sec = strtol(cp, &cp, 10); 767*7c478bd9Sstevel@tonic-gate if (!cp || *cp++ != ' ') 768*7c478bd9Sstevel@tonic-gate SCREWUP("atime.sec not delimited") 769*7c478bd9Sstevel@tonic-gate atime.tv_usec = strtol(cp, &cp, 10); 770*7c478bd9Sstevel@tonic-gate if (!cp || *cp++ != '\0') 771*7c478bd9Sstevel@tonic-gate SCREWUP("atime.usec not delimited") 772*7c478bd9Sstevel@tonic-gate (void) atomicio(write, remout, "", 1); 773*7c478bd9Sstevel@tonic-gate continue; 774*7c478bd9Sstevel@tonic-gate } 775*7c478bd9Sstevel@tonic-gate if (*cp != 'C' && *cp != 'D') { 776*7c478bd9Sstevel@tonic-gate /* 777*7c478bd9Sstevel@tonic-gate * Check for the case "rcp remote:foo\* local:bar". 778*7c478bd9Sstevel@tonic-gate * In this case, the line "No match." can be returned 779*7c478bd9Sstevel@tonic-gate * by the shell before the rcp command on the remote is 780*7c478bd9Sstevel@tonic-gate * executed so the ^Aerror_message convention isn't 781*7c478bd9Sstevel@tonic-gate * followed. 782*7c478bd9Sstevel@tonic-gate */ 783*7c478bd9Sstevel@tonic-gate if (first) { 784*7c478bd9Sstevel@tonic-gate run_err("%s", cp); 785*7c478bd9Sstevel@tonic-gate exit(1); 786*7c478bd9Sstevel@tonic-gate } 787*7c478bd9Sstevel@tonic-gate SCREWUP("expected control record") 788*7c478bd9Sstevel@tonic-gate } 789*7c478bd9Sstevel@tonic-gate mode = 0; 790*7c478bd9Sstevel@tonic-gate for (++cp; cp < buf + 5; cp++) { 791*7c478bd9Sstevel@tonic-gate if (*cp < '0' || *cp > '7') 792*7c478bd9Sstevel@tonic-gate SCREWUP("bad mode") 793*7c478bd9Sstevel@tonic-gate mode = (mode << 3) | (*cp - '0'); 794*7c478bd9Sstevel@tonic-gate } 795*7c478bd9Sstevel@tonic-gate if (*cp++ != ' ') 796*7c478bd9Sstevel@tonic-gate SCREWUP("mode not delimited") 797*7c478bd9Sstevel@tonic-gate 798*7c478bd9Sstevel@tonic-gate for (size = 0; isdigit(*cp); ) 799*7c478bd9Sstevel@tonic-gate size = size * 10 + (*cp++ - '0'); 800*7c478bd9Sstevel@tonic-gate if (*cp++ != ' ') 801*7c478bd9Sstevel@tonic-gate SCREWUP("size not delimited") 802*7c478bd9Sstevel@tonic-gate if (targisdir) { 803*7c478bd9Sstevel@tonic-gate static char *namebuf; 804*7c478bd9Sstevel@tonic-gate static int cursize; 805*7c478bd9Sstevel@tonic-gate size_t need; 806*7c478bd9Sstevel@tonic-gate 807*7c478bd9Sstevel@tonic-gate need = strlen(targ) + strlen(cp) + 250; 808*7c478bd9Sstevel@tonic-gate if (need > cursize) { 809*7c478bd9Sstevel@tonic-gate if (namebuf) 810*7c478bd9Sstevel@tonic-gate xfree(namebuf); 811*7c478bd9Sstevel@tonic-gate namebuf = xmalloc(need); 812*7c478bd9Sstevel@tonic-gate cursize = need; 813*7c478bd9Sstevel@tonic-gate } 814*7c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, need, "%s%s%s", targ, 815*7c478bd9Sstevel@tonic-gate strcmp(targ, "/") ? "/" : "", cp); 816*7c478bd9Sstevel@tonic-gate np = namebuf; 817*7c478bd9Sstevel@tonic-gate } else 818*7c478bd9Sstevel@tonic-gate np = targ; 819*7c478bd9Sstevel@tonic-gate curfile = cp; 820*7c478bd9Sstevel@tonic-gate exists = stat(np, &stb) == 0; 821*7c478bd9Sstevel@tonic-gate if (buf[0] == 'D') { 822*7c478bd9Sstevel@tonic-gate int mod_flag = pflag; 823*7c478bd9Sstevel@tonic-gate if (exists) { 824*7c478bd9Sstevel@tonic-gate if (!S_ISDIR(stb.st_mode)) { 825*7c478bd9Sstevel@tonic-gate errno = ENOTDIR; 826*7c478bd9Sstevel@tonic-gate goto bad; 827*7c478bd9Sstevel@tonic-gate } 828*7c478bd9Sstevel@tonic-gate if (pflag) 829*7c478bd9Sstevel@tonic-gate (void) chmod(np, mode); 830*7c478bd9Sstevel@tonic-gate } else { 831*7c478bd9Sstevel@tonic-gate /* 832*7c478bd9Sstevel@tonic-gate * Handle copying from a read-only 833*7c478bd9Sstevel@tonic-gate * directory 834*7c478bd9Sstevel@tonic-gate */ 835*7c478bd9Sstevel@tonic-gate mod_flag = 1; 836*7c478bd9Sstevel@tonic-gate if (mkdir(np, mode | S_IRWXU) < 0) 837*7c478bd9Sstevel@tonic-gate goto bad; 838*7c478bd9Sstevel@tonic-gate } 839*7c478bd9Sstevel@tonic-gate vect[0] = xstrdup(np); 840*7c478bd9Sstevel@tonic-gate sink(1, vect); 841*7c478bd9Sstevel@tonic-gate if (setimes) { 842*7c478bd9Sstevel@tonic-gate setimes = 0; 843*7c478bd9Sstevel@tonic-gate if (utimes(vect[0], tv) < 0) 844*7c478bd9Sstevel@tonic-gate run_err("%s: set times: %s", 845*7c478bd9Sstevel@tonic-gate vect[0], strerror(errno)); 846*7c478bd9Sstevel@tonic-gate } 847*7c478bd9Sstevel@tonic-gate if (mod_flag) 848*7c478bd9Sstevel@tonic-gate (void) chmod(vect[0], mode); 849*7c478bd9Sstevel@tonic-gate if (vect[0]) 850*7c478bd9Sstevel@tonic-gate xfree(vect[0]); 851*7c478bd9Sstevel@tonic-gate continue; 852*7c478bd9Sstevel@tonic-gate } 853*7c478bd9Sstevel@tonic-gate omode = mode; 854*7c478bd9Sstevel@tonic-gate mode |= S_IWRITE; 855*7c478bd9Sstevel@tonic-gate if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 856*7c478bd9Sstevel@tonic-gate bad: run_err("%s: %s", np, strerror(errno)); 857*7c478bd9Sstevel@tonic-gate continue; 858*7c478bd9Sstevel@tonic-gate } 859*7c478bd9Sstevel@tonic-gate (void) atomicio(write, remout, "", 1); 860*7c478bd9Sstevel@tonic-gate if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { 861*7c478bd9Sstevel@tonic-gate (void) close(ofd); 862*7c478bd9Sstevel@tonic-gate continue; 863*7c478bd9Sstevel@tonic-gate } 864*7c478bd9Sstevel@tonic-gate cp = bp->buf; 865*7c478bd9Sstevel@tonic-gate wrerr = NO; 866*7c478bd9Sstevel@tonic-gate 867*7c478bd9Sstevel@tonic-gate if (showprogress) { 868*7c478bd9Sstevel@tonic-gate totalbytes = size; 869*7c478bd9Sstevel@tonic-gate progressmeter(-1); 870*7c478bd9Sstevel@tonic-gate } 871*7c478bd9Sstevel@tonic-gate statbytes = 0; 872*7c478bd9Sstevel@tonic-gate for (count = i = 0; i < size; i += 4096) { 873*7c478bd9Sstevel@tonic-gate amt = 4096; 874*7c478bd9Sstevel@tonic-gate if (i + amt > size) 875*7c478bd9Sstevel@tonic-gate amt = size - i; 876*7c478bd9Sstevel@tonic-gate count += amt; 877*7c478bd9Sstevel@tonic-gate do { 878*7c478bd9Sstevel@tonic-gate j = read(remin, cp, amt); 879*7c478bd9Sstevel@tonic-gate if (j == -1 && (errno == EINTR || 880*7c478bd9Sstevel@tonic-gate errno == EAGAIN)) { 881*7c478bd9Sstevel@tonic-gate continue; 882*7c478bd9Sstevel@tonic-gate } else if (j <= 0) { 883*7c478bd9Sstevel@tonic-gate run_err("%s", j ? strerror(errno) : 884*7c478bd9Sstevel@tonic-gate "dropped connection"); 885*7c478bd9Sstevel@tonic-gate exit(1); 886*7c478bd9Sstevel@tonic-gate } 887*7c478bd9Sstevel@tonic-gate amt -= j; 888*7c478bd9Sstevel@tonic-gate cp += j; 889*7c478bd9Sstevel@tonic-gate statbytes += j; 890*7c478bd9Sstevel@tonic-gate } while (amt > 0); 891*7c478bd9Sstevel@tonic-gate if (count == bp->cnt) { 892*7c478bd9Sstevel@tonic-gate /* Keep reading so we stay sync'd up. */ 893*7c478bd9Sstevel@tonic-gate if (wrerr == NO) { 894*7c478bd9Sstevel@tonic-gate j = atomicio(write, ofd, bp->buf, 895*7c478bd9Sstevel@tonic-gate count); 896*7c478bd9Sstevel@tonic-gate if (j != count) { 897*7c478bd9Sstevel@tonic-gate wrerr = YES; 898*7c478bd9Sstevel@tonic-gate wrerrno = j >= 0 ? EIO : errno; 899*7c478bd9Sstevel@tonic-gate } 900*7c478bd9Sstevel@tonic-gate } 901*7c478bd9Sstevel@tonic-gate count = 0; 902*7c478bd9Sstevel@tonic-gate cp = bp->buf; 903*7c478bd9Sstevel@tonic-gate } 904*7c478bd9Sstevel@tonic-gate } 905*7c478bd9Sstevel@tonic-gate if (showprogress) 906*7c478bd9Sstevel@tonic-gate progressmeter(1); 907*7c478bd9Sstevel@tonic-gate if (count != 0 && wrerr == NO && 908*7c478bd9Sstevel@tonic-gate (j = atomicio(write, ofd, bp->buf, count)) != count) { 909*7c478bd9Sstevel@tonic-gate wrerr = YES; 910*7c478bd9Sstevel@tonic-gate wrerrno = j >= 0 ? EIO : errno; 911*7c478bd9Sstevel@tonic-gate } 912*7c478bd9Sstevel@tonic-gate if (ftruncate(ofd, size)) { 913*7c478bd9Sstevel@tonic-gate run_err("%s: truncate: %s", np, strerror(errno)); 914*7c478bd9Sstevel@tonic-gate wrerr = DISPLAYED; 915*7c478bd9Sstevel@tonic-gate } 916*7c478bd9Sstevel@tonic-gate if (pflag) { 917*7c478bd9Sstevel@tonic-gate if (exists || omode != mode) 918*7c478bd9Sstevel@tonic-gate #ifdef HAVE_FCHMOD 919*7c478bd9Sstevel@tonic-gate if (fchmod(ofd, omode)) 920*7c478bd9Sstevel@tonic-gate #else /* HAVE_FCHMOD */ 921*7c478bd9Sstevel@tonic-gate if (chmod(np, omode)) 922*7c478bd9Sstevel@tonic-gate #endif /* HAVE_FCHMOD */ 923*7c478bd9Sstevel@tonic-gate run_err("%s: set mode: %s", 924*7c478bd9Sstevel@tonic-gate np, strerror(errno)); 925*7c478bd9Sstevel@tonic-gate } else { 926*7c478bd9Sstevel@tonic-gate if (!exists && omode != mode) 927*7c478bd9Sstevel@tonic-gate #ifdef HAVE_FCHMOD 928*7c478bd9Sstevel@tonic-gate if (fchmod(ofd, omode & ~mask)) 929*7c478bd9Sstevel@tonic-gate #else /* HAVE_FCHMOD */ 930*7c478bd9Sstevel@tonic-gate if (chmod(np, omode & ~mask)) 931*7c478bd9Sstevel@tonic-gate #endif /* HAVE_FCHMOD */ 932*7c478bd9Sstevel@tonic-gate run_err("%s: set mode: %s", 933*7c478bd9Sstevel@tonic-gate np, strerror(errno)); 934*7c478bd9Sstevel@tonic-gate } 935*7c478bd9Sstevel@tonic-gate if (close(ofd) == -1) { 936*7c478bd9Sstevel@tonic-gate wrerr = YES; 937*7c478bd9Sstevel@tonic-gate wrerrno = errno; 938*7c478bd9Sstevel@tonic-gate } 939*7c478bd9Sstevel@tonic-gate (void) response(); 940*7c478bd9Sstevel@tonic-gate if (setimes && wrerr == NO) { 941*7c478bd9Sstevel@tonic-gate setimes = 0; 942*7c478bd9Sstevel@tonic-gate if (utimes(np, tv) < 0) { 943*7c478bd9Sstevel@tonic-gate run_err("%s: set times: %s", 944*7c478bd9Sstevel@tonic-gate np, strerror(errno)); 945*7c478bd9Sstevel@tonic-gate wrerr = DISPLAYED; 946*7c478bd9Sstevel@tonic-gate } 947*7c478bd9Sstevel@tonic-gate } 948*7c478bd9Sstevel@tonic-gate switch (wrerr) { 949*7c478bd9Sstevel@tonic-gate case YES: 950*7c478bd9Sstevel@tonic-gate run_err("%s: %s", np, strerror(wrerrno)); 951*7c478bd9Sstevel@tonic-gate break; 952*7c478bd9Sstevel@tonic-gate case NO: 953*7c478bd9Sstevel@tonic-gate (void) atomicio(write, remout, "", 1); 954*7c478bd9Sstevel@tonic-gate break; 955*7c478bd9Sstevel@tonic-gate case DISPLAYED: 956*7c478bd9Sstevel@tonic-gate break; 957*7c478bd9Sstevel@tonic-gate } 958*7c478bd9Sstevel@tonic-gate } 959*7c478bd9Sstevel@tonic-gate screwup: 960*7c478bd9Sstevel@tonic-gate run_err("protocol error: %s", why); 961*7c478bd9Sstevel@tonic-gate exit(1); 962*7c478bd9Sstevel@tonic-gate } 963*7c478bd9Sstevel@tonic-gate 964*7c478bd9Sstevel@tonic-gate int 965*7c478bd9Sstevel@tonic-gate response(void) 966*7c478bd9Sstevel@tonic-gate { 967*7c478bd9Sstevel@tonic-gate char ch, *cp, resp, rbuf[2048]; 968*7c478bd9Sstevel@tonic-gate 969*7c478bd9Sstevel@tonic-gate if (atomicio(read, remin, &resp, sizeof (resp)) != sizeof (resp)) 970*7c478bd9Sstevel@tonic-gate lostconn(0); 971*7c478bd9Sstevel@tonic-gate 972*7c478bd9Sstevel@tonic-gate cp = rbuf; 973*7c478bd9Sstevel@tonic-gate switch (resp) { 974*7c478bd9Sstevel@tonic-gate case 0: /* ok */ 975*7c478bd9Sstevel@tonic-gate return (0); 976*7c478bd9Sstevel@tonic-gate default: 977*7c478bd9Sstevel@tonic-gate *cp++ = resp; 978*7c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 979*7c478bd9Sstevel@tonic-gate case 1: /* error, followed by error msg */ 980*7c478bd9Sstevel@tonic-gate case 2: /* fatal error, "" */ 981*7c478bd9Sstevel@tonic-gate do { 982*7c478bd9Sstevel@tonic-gate if (atomicio(read, remin, &ch, sizeof (ch)) != 983*7c478bd9Sstevel@tonic-gate sizeof (ch)) 984*7c478bd9Sstevel@tonic-gate lostconn(0); 985*7c478bd9Sstevel@tonic-gate *cp++ = ch; 986*7c478bd9Sstevel@tonic-gate } while (cp < &rbuf[sizeof (rbuf) - 1] && ch != '\n'); 987*7c478bd9Sstevel@tonic-gate 988*7c478bd9Sstevel@tonic-gate if (!iamremote) 989*7c478bd9Sstevel@tonic-gate (void) atomicio(write, STDERR_FILENO, rbuf, cp - rbuf); 990*7c478bd9Sstevel@tonic-gate ++errs; 991*7c478bd9Sstevel@tonic-gate if (resp == 1) 992*7c478bd9Sstevel@tonic-gate return (-1); 993*7c478bd9Sstevel@tonic-gate exit(1); 994*7c478bd9Sstevel@tonic-gate } 995*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 996*7c478bd9Sstevel@tonic-gate } 997*7c478bd9Sstevel@tonic-gate 998*7c478bd9Sstevel@tonic-gate void 999*7c478bd9Sstevel@tonic-gate usage(void) 1000*7c478bd9Sstevel@tonic-gate { 1001*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 1002*7c478bd9Sstevel@tonic-gate gettext( 1003*7c478bd9Sstevel@tonic-gate "Usage: scp [-pqrvBC46] [-F config] [-S program] [-P port]\n" 1004*7c478bd9Sstevel@tonic-gate " [-c cipher] [-i identity] [-o option]\n" 1005*7c478bd9Sstevel@tonic-gate " [[user@]host1:]file1 [...] " 1006*7c478bd9Sstevel@tonic-gate "[[user@]host2:]file2\n")); 1007*7c478bd9Sstevel@tonic-gate exit(1); 1008*7c478bd9Sstevel@tonic-gate } 1009*7c478bd9Sstevel@tonic-gate 1010*7c478bd9Sstevel@tonic-gate /* PRINTFLIKE1 */ 1011*7c478bd9Sstevel@tonic-gate void 1012*7c478bd9Sstevel@tonic-gate run_err(const char *fmt, ...) 1013*7c478bd9Sstevel@tonic-gate { 1014*7c478bd9Sstevel@tonic-gate static FILE *fp; 1015*7c478bd9Sstevel@tonic-gate va_list ap; 1016*7c478bd9Sstevel@tonic-gate 1017*7c478bd9Sstevel@tonic-gate ++errs; 1018*7c478bd9Sstevel@tonic-gate if (fp == NULL && !(fp = fdopen(remout, "w"))) 1019*7c478bd9Sstevel@tonic-gate return; 1020*7c478bd9Sstevel@tonic-gate (void) fprintf(fp, "%c", 0x01); 1021*7c478bd9Sstevel@tonic-gate (void) fprintf(fp, "scp: "); 1022*7c478bd9Sstevel@tonic-gate va_start(ap, fmt); 1023*7c478bd9Sstevel@tonic-gate (void) vfprintf(fp, fmt, ap); 1024*7c478bd9Sstevel@tonic-gate va_end(ap); 1025*7c478bd9Sstevel@tonic-gate (void) fprintf(fp, "\n"); 1026*7c478bd9Sstevel@tonic-gate (void) fflush(fp); 1027*7c478bd9Sstevel@tonic-gate 1028*7c478bd9Sstevel@tonic-gate if (!iamremote) { 1029*7c478bd9Sstevel@tonic-gate va_start(ap, fmt); 1030*7c478bd9Sstevel@tonic-gate vfprintf(stderr, fmt, ap); 1031*7c478bd9Sstevel@tonic-gate va_end(ap); 1032*7c478bd9Sstevel@tonic-gate fprintf(stderr, "\n"); 1033*7c478bd9Sstevel@tonic-gate } 1034*7c478bd9Sstevel@tonic-gate } 1035*7c478bd9Sstevel@tonic-gate 1036*7c478bd9Sstevel@tonic-gate void 1037*7c478bd9Sstevel@tonic-gate verifydir(cp) 1038*7c478bd9Sstevel@tonic-gate char *cp; 1039*7c478bd9Sstevel@tonic-gate { 1040*7c478bd9Sstevel@tonic-gate struct stat stb; 1041*7c478bd9Sstevel@tonic-gate 1042*7c478bd9Sstevel@tonic-gate if (!stat(cp, &stb)) { 1043*7c478bd9Sstevel@tonic-gate if (S_ISDIR(stb.st_mode)) 1044*7c478bd9Sstevel@tonic-gate return; 1045*7c478bd9Sstevel@tonic-gate errno = ENOTDIR; 1046*7c478bd9Sstevel@tonic-gate } 1047*7c478bd9Sstevel@tonic-gate run_err("%s: %s", cp, strerror(errno)); 1048*7c478bd9Sstevel@tonic-gate exit(1); 1049*7c478bd9Sstevel@tonic-gate } 1050*7c478bd9Sstevel@tonic-gate 1051*7c478bd9Sstevel@tonic-gate int 1052*7c478bd9Sstevel@tonic-gate okname(cp0) 1053*7c478bd9Sstevel@tonic-gate char *cp0; 1054*7c478bd9Sstevel@tonic-gate { 1055*7c478bd9Sstevel@tonic-gate int c; 1056*7c478bd9Sstevel@tonic-gate char *cp; 1057*7c478bd9Sstevel@tonic-gate 1058*7c478bd9Sstevel@tonic-gate cp = cp0; 1059*7c478bd9Sstevel@tonic-gate do { 1060*7c478bd9Sstevel@tonic-gate c = (int)*cp; 1061*7c478bd9Sstevel@tonic-gate if (c & 0200) 1062*7c478bd9Sstevel@tonic-gate goto bad; 1063*7c478bd9Sstevel@tonic-gate if (!isalpha(c) && !isdigit(c) && 1064*7c478bd9Sstevel@tonic-gate c != '_' && c != '-' && c != '.' && c != '+') 1065*7c478bd9Sstevel@tonic-gate goto bad; 1066*7c478bd9Sstevel@tonic-gate } while (*++cp); 1067*7c478bd9Sstevel@tonic-gate return (1); 1068*7c478bd9Sstevel@tonic-gate 1069*7c478bd9Sstevel@tonic-gate bad: fprintf(stderr, gettext("%s: invalid user name\n"), cp0); 1070*7c478bd9Sstevel@tonic-gate return (0); 1071*7c478bd9Sstevel@tonic-gate } 1072*7c478bd9Sstevel@tonic-gate 1073*7c478bd9Sstevel@tonic-gate BUF * 1074*7c478bd9Sstevel@tonic-gate allocbuf(bp, fd, blksize) 1075*7c478bd9Sstevel@tonic-gate BUF *bp; 1076*7c478bd9Sstevel@tonic-gate int fd, blksize; 1077*7c478bd9Sstevel@tonic-gate { 1078*7c478bd9Sstevel@tonic-gate size_t size; 1079*7c478bd9Sstevel@tonic-gate #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE 1080*7c478bd9Sstevel@tonic-gate struct stat stb; 1081*7c478bd9Sstevel@tonic-gate 1082*7c478bd9Sstevel@tonic-gate if (fstat(fd, &stb) < 0) { 1083*7c478bd9Sstevel@tonic-gate run_err("fstat: %s", strerror(errno)); 1084*7c478bd9Sstevel@tonic-gate return (0); 1085*7c478bd9Sstevel@tonic-gate } 1086*7c478bd9Sstevel@tonic-gate if (stb.st_blksize == 0) 1087*7c478bd9Sstevel@tonic-gate size = blksize; 1088*7c478bd9Sstevel@tonic-gate else 1089*7c478bd9Sstevel@tonic-gate size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % 1090*7c478bd9Sstevel@tonic-gate stb.st_blksize; 1091*7c478bd9Sstevel@tonic-gate #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 1092*7c478bd9Sstevel@tonic-gate size = blksize; 1093*7c478bd9Sstevel@tonic-gate #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 1094*7c478bd9Sstevel@tonic-gate if (bp->cnt >= size) 1095*7c478bd9Sstevel@tonic-gate return (bp); 1096*7c478bd9Sstevel@tonic-gate if (bp->buf == NULL) 1097*7c478bd9Sstevel@tonic-gate bp->buf = xmalloc(size); 1098*7c478bd9Sstevel@tonic-gate else 1099*7c478bd9Sstevel@tonic-gate bp->buf = xrealloc(bp->buf, size); 1100*7c478bd9Sstevel@tonic-gate memset(bp->buf, 0, size); 1101*7c478bd9Sstevel@tonic-gate bp->cnt = size; 1102*7c478bd9Sstevel@tonic-gate return (bp); 1103*7c478bd9Sstevel@tonic-gate } 1104*7c478bd9Sstevel@tonic-gate 1105*7c478bd9Sstevel@tonic-gate void 1106*7c478bd9Sstevel@tonic-gate lostconn(signo) 1107*7c478bd9Sstevel@tonic-gate int signo; 1108*7c478bd9Sstevel@tonic-gate { 1109*7c478bd9Sstevel@tonic-gate if (!iamremote) 1110*7c478bd9Sstevel@tonic-gate write(STDERR_FILENO, "lost connection\n", 16); 1111*7c478bd9Sstevel@tonic-gate if (signo) 1112*7c478bd9Sstevel@tonic-gate _exit(1); 1113*7c478bd9Sstevel@tonic-gate else 1114*7c478bd9Sstevel@tonic-gate exit(1); 1115*7c478bd9Sstevel@tonic-gate } 1116*7c478bd9Sstevel@tonic-gate 1117*7c478bd9Sstevel@tonic-gate static void 1118*7c478bd9Sstevel@tonic-gate updateprogressmeter(int ignore) 1119*7c478bd9Sstevel@tonic-gate { 1120*7c478bd9Sstevel@tonic-gate int save_errno = errno; 1121*7c478bd9Sstevel@tonic-gate 1122*7c478bd9Sstevel@tonic-gate progressmeter(0); 1123*7c478bd9Sstevel@tonic-gate signal(SIGALRM, updateprogressmeter); 1124*7c478bd9Sstevel@tonic-gate alarm(PROGRESSTIME); 1125*7c478bd9Sstevel@tonic-gate errno = save_errno; 1126*7c478bd9Sstevel@tonic-gate } 1127*7c478bd9Sstevel@tonic-gate 1128*7c478bd9Sstevel@tonic-gate static int 1129*7c478bd9Sstevel@tonic-gate foregroundproc(void) 1130*7c478bd9Sstevel@tonic-gate { 1131*7c478bd9Sstevel@tonic-gate static pid_t pgrp = -1; 1132*7c478bd9Sstevel@tonic-gate int ctty_pgrp; 1133*7c478bd9Sstevel@tonic-gate 1134*7c478bd9Sstevel@tonic-gate if (pgrp == -1) 1135*7c478bd9Sstevel@tonic-gate pgrp = getpgrp(); 1136*7c478bd9Sstevel@tonic-gate 1137*7c478bd9Sstevel@tonic-gate #ifdef HAVE_TCGETPGRP 1138*7c478bd9Sstevel@tonic-gate return ((ctty_pgrp = tcgetpgrp(STDOUT_FILENO)) != -1 && 1139*7c478bd9Sstevel@tonic-gate ctty_pgrp == pgrp); 1140*7c478bd9Sstevel@tonic-gate #else 1141*7c478bd9Sstevel@tonic-gate return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && 1142*7c478bd9Sstevel@tonic-gate ctty_pgrp == pgrp)); 1143*7c478bd9Sstevel@tonic-gate #endif 1144*7c478bd9Sstevel@tonic-gate } 1145*7c478bd9Sstevel@tonic-gate 1146*7c478bd9Sstevel@tonic-gate void 1147*7c478bd9Sstevel@tonic-gate progressmeter(int flag) 1148*7c478bd9Sstevel@tonic-gate { 1149*7c478bd9Sstevel@tonic-gate static const char prefixes[] = " KMGTP"; 1150*7c478bd9Sstevel@tonic-gate static struct timeval lastupdate; 1151*7c478bd9Sstevel@tonic-gate static off_t lastsize; 1152*7c478bd9Sstevel@tonic-gate struct timeval now, td, wait; 1153*7c478bd9Sstevel@tonic-gate off_t cursize, abbrevsize; 1154*7c478bd9Sstevel@tonic-gate double elapsed; 1155*7c478bd9Sstevel@tonic-gate int ratio, barlength, i, remaining; 1156*7c478bd9Sstevel@tonic-gate char buf[512]; 1157*7c478bd9Sstevel@tonic-gate 1158*7c478bd9Sstevel@tonic-gate if (flag == -1) { 1159*7c478bd9Sstevel@tonic-gate (void) gettimeofday(&start, (struct timezone *)0); 1160*7c478bd9Sstevel@tonic-gate lastupdate = start; 1161*7c478bd9Sstevel@tonic-gate lastsize = 0; 1162*7c478bd9Sstevel@tonic-gate } 1163*7c478bd9Sstevel@tonic-gate if (foregroundproc() == 0) 1164*7c478bd9Sstevel@tonic-gate return; 1165*7c478bd9Sstevel@tonic-gate 1166*7c478bd9Sstevel@tonic-gate (void) gettimeofday(&now, (struct timezone *)0); 1167*7c478bd9Sstevel@tonic-gate cursize = statbytes; 1168*7c478bd9Sstevel@tonic-gate if (totalbytes != 0) { 1169*7c478bd9Sstevel@tonic-gate ratio = (int)(100.0 * cursize / totalbytes); 1170*7c478bd9Sstevel@tonic-gate ratio = MAX(ratio, 0); 1171*7c478bd9Sstevel@tonic-gate ratio = MIN(ratio, 100); 1172*7c478bd9Sstevel@tonic-gate } else 1173*7c478bd9Sstevel@tonic-gate ratio = 100; 1174*7c478bd9Sstevel@tonic-gate 1175*7c478bd9Sstevel@tonic-gate snprintf(buf, sizeof (buf), "\r%-20.20s %3d%% ", curfile, ratio); 1176*7c478bd9Sstevel@tonic-gate 1177*7c478bd9Sstevel@tonic-gate barlength = getttywidth() - 51; 1178*7c478bd9Sstevel@tonic-gate if (barlength > 0) { 1179*7c478bd9Sstevel@tonic-gate i = barlength * ratio / 100; 1180*7c478bd9Sstevel@tonic-gate snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf), 1181*7c478bd9Sstevel@tonic-gate "|%.*s%*s|", i, 1182*7c478bd9Sstevel@tonic-gate "*******************************************************" 1183*7c478bd9Sstevel@tonic-gate "*******************************************************" 1184*7c478bd9Sstevel@tonic-gate "*******************************************************" 1185*7c478bd9Sstevel@tonic-gate "*******************************************************" 1186*7c478bd9Sstevel@tonic-gate "*******************************************************" 1187*7c478bd9Sstevel@tonic-gate "*******************************************************" 1188*7c478bd9Sstevel@tonic-gate "*******************************************************", 1189*7c478bd9Sstevel@tonic-gate barlength - i, ""); 1190*7c478bd9Sstevel@tonic-gate } 1191*7c478bd9Sstevel@tonic-gate i = 0; 1192*7c478bd9Sstevel@tonic-gate abbrevsize = cursize; 1193*7c478bd9Sstevel@tonic-gate while (abbrevsize >= 100000 && i < sizeof (prefixes)) { 1194*7c478bd9Sstevel@tonic-gate i++; 1195*7c478bd9Sstevel@tonic-gate abbrevsize >>= 10; 1196*7c478bd9Sstevel@tonic-gate } 1197*7c478bd9Sstevel@tonic-gate snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf), " %5lu %c%c ", 1198*7c478bd9Sstevel@tonic-gate (unsigned long) abbrevsize, prefixes[i], 1199*7c478bd9Sstevel@tonic-gate prefixes[i] == ' ' ? ' ' : 'B'); 1200*7c478bd9Sstevel@tonic-gate 1201*7c478bd9Sstevel@tonic-gate timersub(&now, &lastupdate, &wait); 1202*7c478bd9Sstevel@tonic-gate if (cursize > lastsize) { 1203*7c478bd9Sstevel@tonic-gate lastupdate = now; 1204*7c478bd9Sstevel@tonic-gate lastsize = cursize; 1205*7c478bd9Sstevel@tonic-gate if (wait.tv_sec >= STALLTIME) { 1206*7c478bd9Sstevel@tonic-gate start.tv_sec += wait.tv_sec; 1207*7c478bd9Sstevel@tonic-gate start.tv_usec += wait.tv_usec; 1208*7c478bd9Sstevel@tonic-gate } 1209*7c478bd9Sstevel@tonic-gate wait.tv_sec = 0; 1210*7c478bd9Sstevel@tonic-gate } 1211*7c478bd9Sstevel@tonic-gate timersub(&now, &start, &td); 1212*7c478bd9Sstevel@tonic-gate elapsed = td.tv_sec + (td.tv_usec / 1000000.0); 1213*7c478bd9Sstevel@tonic-gate 1214*7c478bd9Sstevel@tonic-gate if (flag != 1 && 1215*7c478bd9Sstevel@tonic-gate (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes)) { 1216*7c478bd9Sstevel@tonic-gate snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf), 1217*7c478bd9Sstevel@tonic-gate " --:-- ETA"); 1218*7c478bd9Sstevel@tonic-gate } else if (wait.tv_sec >= STALLTIME) { 1219*7c478bd9Sstevel@tonic-gate snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf), 1220*7c478bd9Sstevel@tonic-gate " - stalled -"); 1221*7c478bd9Sstevel@tonic-gate } else { 1222*7c478bd9Sstevel@tonic-gate if (flag != 1) 1223*7c478bd9Sstevel@tonic-gate remaining = (int)(totalbytes / (statbytes / elapsed) - 1224*7c478bd9Sstevel@tonic-gate elapsed); 1225*7c478bd9Sstevel@tonic-gate else 1226*7c478bd9Sstevel@tonic-gate remaining = (int)elapsed; 1227*7c478bd9Sstevel@tonic-gate 1228*7c478bd9Sstevel@tonic-gate i = remaining / 3600; 1229*7c478bd9Sstevel@tonic-gate if (i) 1230*7c478bd9Sstevel@tonic-gate snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf), 1231*7c478bd9Sstevel@tonic-gate "%2d:", i); 1232*7c478bd9Sstevel@tonic-gate else 1233*7c478bd9Sstevel@tonic-gate snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf), 1234*7c478bd9Sstevel@tonic-gate " "); 1235*7c478bd9Sstevel@tonic-gate i = remaining % 3600; 1236*7c478bd9Sstevel@tonic-gate snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf), 1237*7c478bd9Sstevel@tonic-gate "%02d:%02d%s", i / 60, i % 60, 1238*7c478bd9Sstevel@tonic-gate (flag != 1) ? " ETA" : " "); 1239*7c478bd9Sstevel@tonic-gate } 1240*7c478bd9Sstevel@tonic-gate atomicio(write, fileno(stdout), buf, strlen(buf)); 1241*7c478bd9Sstevel@tonic-gate 1242*7c478bd9Sstevel@tonic-gate if (flag == -1) { 1243*7c478bd9Sstevel@tonic-gate mysignal(SIGALRM, updateprogressmeter); 1244*7c478bd9Sstevel@tonic-gate alarm(PROGRESSTIME); 1245*7c478bd9Sstevel@tonic-gate } else if (flag == 1) { 1246*7c478bd9Sstevel@tonic-gate alarm(0); 1247*7c478bd9Sstevel@tonic-gate atomicio(write, fileno(stdout), "\n", 1); 1248*7c478bd9Sstevel@tonic-gate statbytes = 0; 1249*7c478bd9Sstevel@tonic-gate } 1250*7c478bd9Sstevel@tonic-gate } 1251*7c478bd9Sstevel@tonic-gate 1252*7c478bd9Sstevel@tonic-gate int 1253*7c478bd9Sstevel@tonic-gate getttywidth(void) 1254*7c478bd9Sstevel@tonic-gate { 1255*7c478bd9Sstevel@tonic-gate struct winsize winsize; 1256*7c478bd9Sstevel@tonic-gate 1257*7c478bd9Sstevel@tonic-gate if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) 1258*7c478bd9Sstevel@tonic-gate return (winsize.ws_col ? winsize.ws_col : 80); 1259*7c478bd9Sstevel@tonic-gate else 1260*7c478bd9Sstevel@tonic-gate return (80); 1261*7c478bd9Sstevel@tonic-gate } 1262