Again, this has been lightly tested. I think there still are a few glitchs. 1. stole progressmeter() from scp.c - clean up and simplified a little to remove the 'flag' status. It now understands how to initialize itself and how to terminate itself. Along with a malloced status bar instead of the original fix width bar. 2. removed all initialization code from scp.c for progressmeter() and moved to updateprogressmeter(). 3. Added two callback per upload/download function in sftp-client.c. One to start the session the second to clean up after itself (same logic as in scp now). 4. fixed up sftp-int.c to pass the call backs. Note, I normally don't do callbacks. So I need someone to review to ensure I did not mungle anything up. Plus I normally don't use signal() and I need to know if in the (*func)(int) define if I can use the (int) legal without any unwanted. Again, patch against OpenBSD code. scp.c will fail to apply cleaning, but is an easy fix (just I only have my OpenBSD box around me at this moment). The sooner I get feedback on this the sooner I can get back to looking at maybe including readline/libedit support into sftp. And that should take care of most whinings about UI stuff in sftp (Most of the code to support readline is written, just needs better testing and some cleanup work). - Ben Index: misc.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/misc.c,v retrieving revision 1.15 diff -u -r1.15 misc.c --- misc.c 2002/01/24 21:09:25 1.15 +++ misc.c 2002/02/02 21:47:40 @@ -310,3 +310,135 @@ args->list[args->num++] = xstrdup(buf); args->list[args->num] = NULL; } + +/* scp/sftp progression meter (from src/usr.bin/ftp/util.c) */ +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(off_t statbytes, off_t totalbytes, char *filename) +{ +#define STALLTIME 5 /* number of seconds before xfer assumed "stalled" */ + static const char prefixes[] = " KMGTP"; + static char *progressbar = NULL, file = NULL; + static struct timeval *start = NULL, lastupdate; + static off_t lastsize; + static size_t progressbar_size = 0; + struct timeval now, td, wait; + off_t cursize, abbrevsize; + double elapsed; + int ratio, barlength, i, remaining; + char buf[256]; + + if (!start) { + start = xmalloc(sizeof(struct timeval)); + (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%% ", (filename?filename:""), ratio); + + barlength = getttywidth() - 51; + if (barlength > progressbar_size) { + progressbar_size = barlength; + progressbar = xrealloc(progressbar, barlength); + memset(progressbar, '*', barlength); + } + + if (barlength > 0) { + i = barlength * ratio / 100; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "|%.*s%*s|", i, progressbar, 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 ((totalbytes != statbytes) && + (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 (totalbytes != statbytes) + 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, + (totalbytes != statbytes) ? " ETA" : " "); + } + atomicio(write, fileno(stdout), buf, strlen(buf)); + + if (totalbytes == statbytes) { + atomicio(write, fileno(stdout), "\n", 1); + + /* Clean up for next usage */ + xfree(start); + start = NULL; + } +} + +int +getttywidth(void) +{ + struct winsize winsize; + + if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) + return (winsize.ws_col ? winsize.ws_col : 80); + else + return (80); +} Index: misc.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/misc.h,v retrieving revision 1.11 diff -u -r1.11 misc.h --- misc.h 2002/01/24 21:09:25 1.11 +++ misc.h 2002/02/02 21:47:40 @@ -30,4 +30,8 @@ int num; int nalloc; }; + void addargs(arglist *, char *, ...) __attribute__((format(printf, 2, 3))); +void progressmeter(off_t statbytes, off_t totalbytes, char *curfile); + +#define PROGRESSTIME 1 /* alarm() interval for updating progress meter */ Index: scp.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/scp.c,v retrieving revision 1.86 diff -u -r1.86 scp.c --- scp.c 2001/12/05 03:56:39 1.86 +++ scp.c 2002/02/02 21:47:40 @@ -83,24 +83,12 @@ #include "log.h" #include "misc.h" -/* For progressmeter() -- number of seconds before xfer considered "stalled" */ -#define STALLTIME 5 -/* alarm() interval for updating progress meter */ -#define PROGRESSTIME 1 - -/* Visual statistics about files as they are transferred. */ -void progressmeter(int); - -/* Returns width of the terminal (for progress meter calculations). */ -int getttywidth(void); +static void updateprogressmeter(int done); int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc); /* Struct for addargs */ arglist args; -/* Time a transfer started. */ -static struct timeval start; - /* Number of bytes of current file transferred so far. */ volatile off_t statbytes; @@ -542,7 +530,7 @@ } if (showprogress) { totalbytes = stb.st_size; - progressmeter(-1); + updateprogressmeter(0); } /* Keep writing after an error so that we stay sync'd up. */ for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { @@ -564,7 +552,7 @@ } } if (showprogress) - progressmeter(1); + updateprogressmeter(1); if (close(fd) < 0 && !haderr) haderr = errno; @@ -808,7 +796,7 @@ if (showprogress) { totalbytes = size; - progressmeter(-1); + updateprogressmeter(0); } statbytes = 0; for (count = i = 0; i < size; i += 4096) { @@ -844,7 +832,8 @@ } } if (showprogress) - progressmeter(1); + updateprogressmeter(1); + if (count != 0 && wrerr == NO && (j = atomicio(write, ofd, bp->buf, count)) != count) { wrerr = YES; @@ -1040,139 +1029,18 @@ } 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) +updateprogressmeter(int done) { - 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[256]; - - if (flag == -1) { - (void) gettimeofday(&start, (struct timezone *) 0); - lastupdate = start; - lastsize = 0; - } - if (foregroundproc() == 0) - return; + int save_errno = errno; - (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) { + + progressmeter(statbytes, totalbytes, curfile); + if (done == 0) { + signal(SIGALRM, updateprogressmeter); + alarm(PROGRESSTIME); + } else 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); + errno = save_errno; } + Index: sftp-client.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/sftp-client.c,v retrieving revision 1.19 diff -u -r1.19 sftp-client.c --- sftp-client.c 2001/12/19 07:18:56 1.19 +++ sftp-client.c 2002/02/02 21:47:41 @@ -49,6 +49,11 @@ /* Message ID */ static u_int msg_id = 1; +/* Progress Meter items */ +off_t statbytes = 0; +off_t totalbytes = 0; +char *curfile = NULL; + static void send_msg(int fd, Buffer *m) { @@ -670,7 +675,7 @@ int do_download(int fd_in, int fd_out, char *remote_path, char *local_path, - int pflag) + int pflag, void (*progressbar)(int)) { int local_fd; u_int expected_id, handle_len, mode, type, id; @@ -723,6 +728,11 @@ return(-1); } + totalbytes = a->size; + curfile = remote_path; + if (progressbar) + (progressbar)(0); + /* Read from remote and write to local */ offset = 0; for (;;) { @@ -784,6 +794,7 @@ offset += len; xfree(data); + statbytes = offset; } status = do_close(fd_in, fd_out, handle, handle_len); @@ -802,15 +813,21 @@ } done: + if (progressbar) + (progressbar)(1); close(local_fd); buffer_free(&msg); xfree(handle); + statbytes = 0; + totalbytes = 0; + curfile = NULL; + return status; } int do_upload(int fd_in, int fd_out, char *local_path, char *remote_path, - int pflag) + int pflag, void (*progressbar)(int)) { int local_fd; u_int handle_len, id; @@ -860,6 +877,10 @@ buffer_free(&msg); return(-1); } + totalbytes = a.size; + curfile = local_path; + if (progressbar) + (progressbar)(0); /* Read from local and write to remote */ offset = 0; @@ -903,6 +924,7 @@ (unsigned long long)offset); offset += len; + statbytes = offset; } if (close(local_fd) == -1) { @@ -920,8 +942,14 @@ status = do_close(fd_in, fd_out, handle, handle_len); done: + if (progressbar) + (progressbar)(1); xfree(handle); buffer_free(&msg); + statbytes = 0; + totalbytes = 0; + curfile = NULL; + return status; } Index: sftp-client.h ==================================================================RCS file: /cvs/src/usr.bin/ssh/sftp-client.h,v retrieving revision 1.6 diff -u -r1.6 sftp-client.h --- sftp-client.h 2001/06/26 06:33:01 1.6 +++ sftp-client.h 2002/02/02 21:47:41 @@ -88,16 +88,14 @@ /* Return target of symlink 'path' - caller must free result */ char *do_readlink(int, int, char *); -/* XXX: add callbacks to do_download/do_upload so we can do progress meter */ - /* * Download 'remote_path' to 'local_path'. Preserve permissions and times * if 'pflag' is set */ -int do_download(int, int, char *, char *, int); +int do_download(int, int, char *, char *, int, void (*)(int)); /* * Upload 'local_path' to 'remote_path'. Preserve permissions and times * if 'pflag' is set */ -int do_upload(int, int, char *, char *, int); +int do_upload(int, int, char *, char *, int, void (*)(int)); Index: sftp-int.c ==================================================================RCS file: /cvs/src/usr.bin/ssh/sftp-int.c,v retrieving revision 1.41 diff -u -r1.41 sftp-int.c --- sftp-int.c 2001/12/19 07:18:56 1.41 +++ sftp-int.c 2002/02/02 21:47:41 @@ -34,6 +34,7 @@ #include "xmalloc.h" #include "log.h" #include "pathnames.h" +#include "misc.h" #include "sftp.h" #include "sftp-common.h" @@ -115,6 +116,24 @@ }; static void +updateprogressmeter(int done) +{ + int save_errno = errno; + extern off_t statbytes; + extern off_t totalbytes; + extern char *curfile; + + progressmeter(statbytes, totalbytes, curfile); + if (done == 0) { + signal(SIGALRM, updateprogressmeter); + alarm(PROGRESSTIME); + } else + alarm(0); + + errno = save_errno; +} + +static void help(void) { printf("Available commands:\n"); @@ -382,8 +401,8 @@ err = -1; goto out; } - printf("Fetching %s to %s\n", g.gl_pathv[0], abs_dst); - err = do_download(in, out, g.gl_pathv[0], abs_dst, pflag); + err = do_download(in, out, g.gl_pathv[0], abs_dst, pflag, + NULL); goto out; } @@ -406,8 +425,8 @@ } else abs_dst = tmp; - printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); - if (do_download(in, out, g.gl_pathv[i], abs_dst, pflag) == -1) + if (do_download(in, out, g.gl_pathv[i], abs_dst, pflag, + updateprogressmeter) == -1) err = -1; xfree(abs_dst); abs_dst = NULL; @@ -464,8 +483,8 @@ } abs_dst = make_absolute(abs_dst, pwd); } - printf("Uploading %s to %s\n", g.gl_pathv[0], abs_dst); - err = do_upload(in, out, g.gl_pathv[0], abs_dst, pflag); + err = do_upload(in, out, g.gl_pathv[0], abs_dst, pflag, + updateprogressmeter); goto out; } @@ -488,8 +507,8 @@ } else abs_dst = make_absolute(tmp, pwd); - printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); - if (do_upload(in, out, g.gl_pathv[i], abs_dst, pflag) == -1) + if (do_upload(in, out, g.gl_pathv[i], abs_dst, pflag, + updateprogressmeter) == -1) err = -1; }