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