=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/scp.c,v retrieving revision 1.91.2.2 retrieving revision 1.92 diff -u -r1.91.2.2 -r1.92 --- src/usr.bin/ssh/scp.c 2003/09/16 21:20:27 1.91.2.2 +++ src/usr.bin/ssh/scp.c 2002/11/07 22:35:38 1.92 @@ -52,7 +52,11 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -71,25 +75,38 @@ */ #include "includes.h" -RCSID("$OpenBSD: scp.c,v 1.91.2.2 2003/09/16 21:20:27 brad Exp $"); +RCSID("$OpenBSD: scp.c,v 1.92 2002/11/07 22:35:38 markus Exp $"); #include "xmalloc.h" #include "atomicio.h" #include "pathnames.h" #include "log.h" #include "misc.h" -#include "progressmeter.h" -int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc); +/* For progressmeter() -- number of seconds before xfer considered "stalled" */ +#define STALLTIME 5 +/* alarm() interval for updating progress meter */ +#define PROGRESSTIME 1 -void bwlimit(int); +/* Visual statistics about files as they are transferred. */ +void progressmeter(int); +/* Returns width of the terminal (for progress meter calculations). */ +int getttywidth(void); +int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc); + /* Struct for addargs */ arglist args; -/* Bandwidth limit */ -off_t limit = 0; +/* Time a transfer started. */ +static struct timeval start; +/* Number of bytes of current file transferred so far. */ +volatile off_t statbytes; + +/* Total size of current file. */ +off_t totalbytes = 0; + /* Name of current file being transferred. */ char *curfile; @@ -103,17 +120,8 @@ char *ssh_program = _PATH_SSH_PROGRAM; /* This is used to store the pid of ssh_program */ -pid_t do_cmd_pid = -1; +pid_t do_cmd_pid; -static void -killchild(int signo) -{ - if (do_cmd_pid > 1) - kill(do_cmd_pid, signo); - - _exit(1); -} - /* * This function executes the given command as the specified user on the * given host. This returns < 0 if execution fails, and >= 0 otherwise. This @@ -147,7 +155,7 @@ close(reserved[0]); close(reserved[1]); - /* Fork a child to execute the command on the remote host using ssh. */ + /* For a child to execute the command on the remote host using ssh. */ do_cmd_pid = fork(); if (do_cmd_pid == 0) { /* Child. */ @@ -175,9 +183,6 @@ *fdout = pin[1]; close(pout[1]); *fdin = pout[0]; - signal(SIGTERM, killchild); - signal(SIGINT, killchild); - signal(SIGHUP, killchild); return 0; } @@ -210,11 +215,12 @@ void usage(void); int -main(int argc, char **argv) +main(argc, argv) + int argc; + char *argv[]; { int ch, fflag, tflag, status; - double speed; - char *targ, *endp; + char *targ; extern char *optarg; extern int optind; @@ -225,11 +231,9 @@ addargs(&args, "-oClearAllForwardings yes"); fflag = tflag = 0; - while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1) + while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:F:")) != -1) switch (ch) { /* User-visible flags. */ - case '1': - case '2': case '4': case '6': case 'C': @@ -247,12 +251,6 @@ case 'B': addargs(&args, "-oBatchmode yes"); break; - case 'l': - speed = strtod(optarg, &endp); - if (speed <= 0 || *endp != '\0') - usage(); - limit = speed * 1024; - break; case 'p': pflag = 1; break; @@ -289,7 +287,7 @@ argv += optind; if ((pwd = getpwuid(userid = getuid())) == NULL) - fatal("unknown user %u", (u_int) userid); + fatal("unknown user %d", (int) userid); if (!isatty(STDERR_FILENO)) showprogress = 0; @@ -350,7 +348,9 @@ } void -toremote(char *targ, int argc, char **argv) +toremote(targ, argc, argv) + char *targ, *argv[]; + int argc; { int i, len; char *bp, *host, *src, *suser, *thost, *tuser; @@ -359,12 +359,14 @@ if (*targ == 0) targ = "."; - if ((thost = strrchr(argv[argc - 1], '@'))) { + if ((thost = strchr(argv[argc - 1], '@'))) { /* user@host */ *thost++ = 0; tuser = argv[argc - 1]; if (*tuser == '\0') tuser = NULL; + else if (!okname(tuser)) + exit(1); } else { thost = argv[argc - 1]; tuser = NULL; @@ -378,7 +380,7 @@ *src++ = 0; if (*src == 0) src = "."; - host = strrchr(argv[i], '@'); + host = strchr(argv[i], '@'); len = strlen(ssh_program) + strlen(argv[i]) + strlen(src) + (tuser ? strlen(tuser) : 0) + strlen(thost) + strlen(targ) + @@ -390,14 +392,8 @@ suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; - else if (!okname(suser)) { - xfree(bp); + else if (!okname(suser)) continue; - } - if (tuser && !okname(tuser)) { - xfree(bp); - continue; - } snprintf(bp, len, "%s%s %s -n " "-l %s %s %s %s '%s%s%s:%s'", @@ -438,7 +434,9 @@ } void -tolocal(int argc, char **argv) +tolocal(argc, argv) + int argc; + char *argv[]; { int i, len; char *bp, *host, *src, *suser; @@ -461,7 +459,7 @@ *src++ = 0; if (*src == 0) src = "."; - if ((host = strrchr(argv[i], '@')) == NULL) { + if ((host = strchr(argv[i], '@')) == NULL) { host = argv[i]; suser = NULL; } else { @@ -469,6 +467,8 @@ suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; + else if (!okname(suser)) + continue; } host = cleanhostname(host); len = strlen(src) + CMDNEEDS + 20; @@ -487,12 +487,14 @@ } void -source(int argc, char **argv) +source(argc, argv) + int argc; + char *argv[]; { struct stat stb; static BUF buffer; BUF *bp; - off_t i, amt, result, statbytes; + off_t i, amt, result; int fd, haderr, indx; char *last, *name, buf[2048]; int len; @@ -540,7 +542,7 @@ (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", (u_long) stb.st_mtime, (u_long) stb.st_atime); - (void) atomicio(vwrite, remout, buf, strlen(buf)); + (void) atomicio(write, remout, buf, strlen(buf)); if (response() < 0) goto next; } @@ -550,16 +552,19 @@ (long long)stb.st_size, last); if (verbose_mode) { fprintf(stderr, "Sending file modes: %s", buf); + fflush(stderr); } - (void) atomicio(vwrite, remout, buf, strlen(buf)); + (void) atomicio(write, remout, buf, strlen(buf)); if (response() < 0) goto next; if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { next: (void) close(fd); continue; } - if (showprogress) - start_progress_meter(curfile, stb.st_size, &statbytes); + if (showprogress) { + totalbytes = stb.st_size; + progressmeter(-1); + } /* Keep writing after an error so that we stay sync'd up. */ for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { amt = bp->cnt; @@ -571,23 +576,21 @@ haderr = result >= 0 ? EIO : errno; } if (haderr) - (void) atomicio(vwrite, remout, bp->buf, amt); + (void) atomicio(write, remout, bp->buf, amt); else { - result = atomicio(vwrite, remout, bp->buf, amt); + result = atomicio(write, remout, bp->buf, amt); if (result != amt) haderr = result >= 0 ? EIO : errno; statbytes += result; } - if (limit) - bwlimit(amt); } if (showprogress) - stop_progress_meter(); + progressmeter(1); if (close(fd) < 0 && !haderr) haderr = errno; if (!haderr) - (void) atomicio(vwrite, remout, "", 1); + (void) atomicio(write, remout, "", 1); else run_err("%s: %s", name, strerror(haderr)); (void) response(); @@ -595,7 +598,9 @@ } void -rsource(char *name, struct stat *statp) +rsource(name, statp) + char *name; + struct stat *statp; { DIR *dirp; struct dirent *dp; @@ -614,7 +619,7 @@ (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", (u_long) statp->st_mtime, (u_long) statp->st_atime); - (void) atomicio(vwrite, remout, path, strlen(path)); + (void) atomicio(write, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; @@ -624,7 +629,7 @@ (u_int) (statp->st_mode & FILEMODEMASK), 0, last); if (verbose_mode) fprintf(stderr, "Entering directory: %s", path); - (void) atomicio(vwrite, remout, path, strlen(path)); + (void) atomicio(write, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; @@ -643,67 +648,15 @@ source(1, vect); } (void) closedir(dirp); - (void) atomicio(vwrite, remout, "E\n", 2); + (void) atomicio(write, remout, "E\n", 2); (void) response(); } void -bwlimit(int amount) +sink(argc, argv) + int argc; + char *argv[]; { - static struct timeval bwstart, bwend; - static int lamt, thresh = 16384; - u_int64_t wait; - struct timespec ts, rm; - - if (!timerisset(&bwstart)) { - gettimeofday(&bwstart, NULL); - return; - } - - lamt += amount; - if (lamt < thresh) - return; - - gettimeofday(&bwend, NULL); - timersub(&bwend, &bwstart, &bwend); - if (!timerisset(&bwend)) - return; - - lamt *= 8; - wait = (double)1000000L * lamt / limit; - - bwstart.tv_sec = wait / 1000000L; - bwstart.tv_usec = wait % 1000000L; - - if (timercmp(&bwstart, &bwend, >)) { - timersub(&bwstart, &bwend, &bwend); - - /* Adjust the wait time */ - if (bwend.tv_sec) { - thresh /= 2; - if (thresh < 2048) - thresh = 2048; - } else if (bwend.tv_usec < 100) { - thresh *= 2; - if (thresh > 32768) - thresh = 32768; - } - - TIMEVAL_TO_TIMESPEC(&bwend, &ts); - while (nanosleep(&ts, &rm) == -1) { - if (errno != EINTR) - break; - ts = rm; - } - } - - lamt = 0; - gettimeofday(&bwstart, NULL); -} - -void -sink(int argc, char **argv) -{ static BUF buffer; struct stat stb; enum { @@ -712,7 +665,7 @@ BUF *bp; off_t i, j; int amt, count, exists, first, mask, mode, ofd, omode; - off_t size, statbytes; + off_t size; int setimes, targisdir, wrerrno = 0; char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; struct timeval tv[2]; @@ -733,7 +686,7 @@ if (targetshouldbedirectory) verifydir(targ); - (void) atomicio(vwrite, remout, "", 1); + (void) atomicio(write, remout, "", 1); if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) targisdir = 1; for (first = 1;; first = 0) { @@ -751,7 +704,7 @@ if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) - (void) atomicio(vwrite, STDERR_FILENO, + (void) atomicio(write, STDERR_FILENO, buf + 1, strlen(buf + 1)); if (buf[0] == '\02') exit(1); @@ -759,7 +712,7 @@ continue; } if (buf[0] == 'E') { - (void) atomicio(vwrite, remout, "", 1); + (void) atomicio(write, remout, "", 1); return; } if (ch == '\n') @@ -781,7 +734,7 @@ atime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != '\0') SCREWUP("atime.usec not delimited"); - (void) atomicio(vwrite, remout, "", 1); + (void) atomicio(write, remout, "", 1); continue; } if (*cp != 'C' && *cp != 'D') { @@ -866,7 +819,7 @@ bad: run_err("%s: %s", np, strerror(errno)); continue; } - (void) atomicio(vwrite, remout, "", 1); + (void) atomicio(write, remout, "", 1); if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { (void) close(ofd); continue; @@ -874,9 +827,11 @@ cp = bp->buf; wrerr = NO; + if (showprogress) { + totalbytes = size; + progressmeter(-1); + } statbytes = 0; - if (showprogress) - start_progress_meter(curfile, size, &statbytes); for (count = i = 0; i < size; i += 4096) { amt = 4096; if (i + amt > size) @@ -896,14 +851,10 @@ cp += j; statbytes += j; } while (amt > 0); - - if (limit) - bwlimit(4096); - if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ if (wrerr == NO) { - j = atomicio(vwrite, ofd, bp->buf, count); + j = atomicio(write, ofd, bp->buf, count); if (j != count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; @@ -914,13 +865,13 @@ } } if (showprogress) - stop_progress_meter(); + progressmeter(1); if (count != 0 && wrerr == NO && - (j = atomicio(vwrite, ofd, bp->buf, count)) != count) { + (j = atomicio(write, ofd, bp->buf, count)) != count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; } - if (wrerr == NO && ftruncate(ofd, size) != 0) { + if (ftruncate(ofd, size)) { run_err("%s: truncate: %s", np, strerror(errno)); wrerr = DISPLAYED; } @@ -953,7 +904,7 @@ run_err("%s: %s", np, strerror(wrerrno)); break; case NO: - (void) atomicio(vwrite, remout, "", 1); + (void) atomicio(write, remout, "", 1); break; case DISPLAYED: break; @@ -988,7 +939,7 @@ } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); if (!iamremote) - (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); + (void) atomicio(write, STDERR_FILENO, rbuf, cp - rbuf); ++errs; if (resp == 1) return (-1); @@ -1001,8 +952,8 @@ usage(void) { (void) fprintf(stderr, - "usage: scp [-pqrvBC1246] [-F config] [-S program] [-P port]\n" - " [-c cipher] [-i identity] [-l limit] [-o option]\n" + "usage: scp [-pqrvBC46] [-F config] [-S program] [-P port]\n" + " [-c cipher] [-i identity] [-o option]\n" " [[user@]host1:]file1 [...] [[user@]host2:]file2\n"); exit(1); } @@ -1033,7 +984,8 @@ } void -verifydir(char *cp) +verifydir(cp) + char *cp; { struct stat stb; @@ -1047,7 +999,8 @@ } int -okname(char *cp0) +okname(cp0) + char *cp0; { int c; char *cp; @@ -1057,18 +1010,9 @@ c = (int)*cp; if (c & 0200) goto bad; - if (!isalpha(c) && !isdigit(c)) { - switch (c) { - case '\'': - case '"': - case '`': - case ' ': - case '#': - goto bad; - default: - break; - } - } + if (!isalpha(c) && !isdigit(c) && + c != '_' && c != '-' && c != '.' && c != '+') + goto bad; } while (*++cp); return (1); @@ -1077,7 +1021,9 @@ } BUF * -allocbuf(BUF *bp, int fd, int blksize) +allocbuf(bp, fd, blksize) + BUF *bp; + int fd, blksize; { size_t size; struct stat stb; @@ -1086,9 +1032,11 @@ run_err("fstat: %s", strerror(errno)); return (0); } - size = roundup(stb.st_blksize, blksize); - if (size == 0) + if (stb.st_blksize == 0) size = blksize; + else + size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % + stb.st_blksize; if (bp->cnt >= size) return (bp); if (bp->buf == NULL) @@ -1101,7 +1049,8 @@ } void -lostconn(int signo) +lostconn(signo) + int signo; { if (!iamremote) write(STDERR_FILENO, "lost connection\n", 16); @@ -1109,4 +1058,145 @@ _exit(1); else exit(1); +} + +static void +updateprogressmeter(int ignore) +{ + int save_errno = errno; + + progressmeter(0); + signal(SIGALRM, updateprogressmeter); + alarm(PROGRESSTIME); + errno = save_errno; +} + +static int +foregroundproc(void) +{ + static pid_t pgrp = -1; + int ctty_pgrp; + + if (pgrp == -1) + pgrp = getpgrp(); + + return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && + ctty_pgrp == pgrp)); +} + +void +progressmeter(int flag) +{ + static const char prefixes[] = " KMGTP"; + static struct timeval lastupdate; + static off_t lastsize; + struct timeval now, td, wait; + off_t cursize, abbrevsize; + double elapsed; + int ratio, barlength, i, remaining; + char buf[512]; + + if (flag == -1) { + (void) gettimeofday(&start, (struct timezone *) 0); + lastupdate = start; + lastsize = 0; + } + if (foregroundproc() == 0) + return; + + (void) gettimeofday(&now, (struct timezone *) 0); + cursize = statbytes; + if (totalbytes != 0) { + ratio = 100.0 * cursize / totalbytes; + ratio = MAX(ratio, 0); + ratio = MIN(ratio, 100); + } else + ratio = 100; + + snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); + + barlength = getttywidth() - 51; + if (barlength > 0) { + i = barlength * ratio / 100; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "|%.*s%*s|", i, + "*******************************************************" + "*******************************************************" + "*******************************************************" + "*******************************************************" + "*******************************************************" + "*******************************************************" + "*******************************************************", + barlength - i, ""); + } + i = 0; + abbrevsize = cursize; + while (abbrevsize >= 100000 && i < sizeof(prefixes)) { + i++; + abbrevsize >>= 10; + } + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5llu %c%c ", + (unsigned long long) abbrevsize, prefixes[i], + prefixes[i] == ' ' ? ' ' : 'B'); + + timersub(&now, &lastupdate, &wait); + if (cursize > lastsize) { + lastupdate = now; + lastsize = cursize; + if (wait.tv_sec >= STALLTIME) { + start.tv_sec += wait.tv_sec; + start.tv_usec += wait.tv_usec; + } + wait.tv_sec = 0; + } + timersub(&now, &start, &td); + elapsed = td.tv_sec + (td.tv_usec / 1000000.0); + + if (flag != 1 && + (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes)) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " --:-- ETA"); + } else if (wait.tv_sec >= STALLTIME) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " - stalled -"); + } else { + if (flag != 1) + remaining = (int)(totalbytes / (statbytes / elapsed) - + elapsed); + else + remaining = elapsed; + + i = remaining / 3600; + if (i) + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "%2d:", i); + else + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " "); + i = remaining % 3600; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "%02d:%02d%s", i / 60, i % 60, + (flag != 1) ? " ETA" : " "); + } + atomicio(write, fileno(stdout), buf, strlen(buf)); + + if (flag == -1) { + signal(SIGALRM, updateprogressmeter); + alarm(PROGRESSTIME); + } else if (flag == 1) { + alarm(0); + atomicio(write, fileno(stdout), "\n", 1); + statbytes = 0; + } +} + +int +getttywidth(void) +{ + struct winsize winsize; + + if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) + return (winsize.ws_col ? winsize.ws_col : 80); + else + return (80); }