Version two of progressbar for scp/sftp

mouring mouring at etoh.eviladmin.org
Sun Feb 3 08:29:05 EST 2002


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;
 	}
 



More information about the openssh-unix-dev mailing list