=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/scp.c,v retrieving revision 1.88.2.2 retrieving revision 1.89 diff -u -r1.88.2.2 -r1.89 --- src/usr.bin/ssh/scp.c 2003/04/03 22:35:17 1.88.2.2 +++ src/usr.bin/ssh/scp.c 2002/06/08 12:36:53 1.89 @@ -75,25 +75,38 @@ */ #include "includes.h" -RCSID("$OpenBSD: scp.c,v 1.88.2.2 2003/04/03 22:35:17 miod Exp $"); +RCSID("$OpenBSD: scp.c,v 1.89 2002/06/08 12:36:53 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; @@ -106,9 +119,6 @@ /* This is the program to execute for the secured connection. ("ssh" or -S) */ char *ssh_program = _PATH_SSH_PROGRAM; -/* This is used to store the pid of ssh_program */ -pid_t do_cmd_pid; - /* * This function executes the given command as the specified user on the * given host. This returns < 0 if execution fails, and >= 0 otherwise. This @@ -143,8 +153,7 @@ close(reserved[1]); /* For a child to execute the command on the remote host using ssh. */ - do_cmd_pid = fork(); - if (do_cmd_pid == 0) { + if (fork() == 0) { /* Child. */ close(pin[1]); close(pout[0]); @@ -162,8 +171,6 @@ execvp(ssh_program, args.list); perror(ssh_program); exit(1); - } else if (do_cmd_pid == -1) { - fatal("fork: %s", strerror(errno)); } /* Parent. Close the other side, and return the local side. */ close(pin[0]); @@ -206,24 +213,21 @@ int argc; char *argv[]; { - int ch, fflag, tflag, status; - double speed; - char *targ, *endp; + int ch, fflag, tflag; + char *targ; extern char *optarg; extern int optind; args.list = NULL; - addargs(&args, "ssh"); /* overwritten with ssh_program */ + addargs(&args, "ssh"); /* overwritten with ssh_program */ addargs(&args, "-x"); addargs(&args, "-oForwardAgent no"); 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': @@ -241,12 +245,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; @@ -308,7 +306,6 @@ targetshouldbedirectory = 1; remin = remout = -1; - do_cmd_pid = -1; /* Command to be executed on remote system using "ssh". */ (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", @@ -324,22 +321,6 @@ if (targetshouldbedirectory) verifydir(argv[argc - 1]); } - /* - * Finally check the exit status of the ssh process, if one was forked - * and no error has occured yet - */ - if (do_cmd_pid != -1 && errs == 0) { - if (remin != -1) - (void) close(remin); - if (remout != -1) - (void) close(remout); - if (waitpid(do_cmd_pid, &status, 0) == -1) - errs = 1; - else { - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - errs = 1; - } - } exit(errs != 0); } @@ -355,12 +336,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; @@ -374,7 +357,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) + @@ -386,14 +369,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'", @@ -459,7 +436,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 { @@ -467,6 +444,8 @@ suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; + else if (!okname(suser)) + continue; } host = cleanhostname(host); len = strlen(src) + CMDNEEDS + 20; @@ -492,7 +471,7 @@ 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; @@ -550,6 +529,7 @@ (long long)stb.st_size, last); if (verbose_mode) { fprintf(stderr, "Sending file modes: %s", buf); + fflush(stderr); } (void) atomicio(write, remout, buf, strlen(buf)); if (response() < 0) @@ -558,8 +538,10 @@ 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; @@ -578,11 +560,9 @@ 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; @@ -650,60 +630,6 @@ } void -bwlimit(int amount) -{ - 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(argc, argv) int argc; char *argv[]; @@ -716,7 +642,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]; @@ -878,9 +804,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) @@ -900,10 +828,6 @@ 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) { @@ -918,13 +842,13 @@ } } if (showprogress) - stop_progress_meter(); + progressmeter(1); if (count != 0 && wrerr == NO && (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; } @@ -1005,9 +929,9 @@ usage(void) { (void) fprintf(stderr, - "usage: scp [-pqrvBC1246] [-F config] [-S program] [-P port]\n" - " [-c cipher] [-i identity] [-l limit] [-o option]\n" - " [[user@]host1:]file1 [...] [[user@]host2:]file2\n"); + "usage: scp [-pqrvBC46] [-F config] [-S ssh] [-P port] [-c cipher] [-i identity]\n" + " [-o option] f1 f2\n" + " or: scp [options] f1 ... fn directory\n"); exit(1); } @@ -1063,18 +987,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); @@ -1094,9 +1009,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) @@ -1118,4 +1035,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); }