Commit 65e42f87 authored by Damien Miller's avatar Damien Miller

- djm@cvs.openbsd.org 2010/09/22 22:58:51

     [atomicio.c atomicio.h misc.c misc.h scp.c sftp-client.c]
     [sftp-client.h sftp.1 sftp.c]
     add an option per-read/write callback to atomicio

     factor out bandwidth limiting code from scp(1) into a generic bandwidth
     limiter that can be attached using the atomicio callback mechanism

     add a bandwidth limit option to sftp(1) using the above
     "very nice" markus@
parent 7fe2b1fe
......@@ -44,6 +44,16 @@
ssh_config.5: format the kexalgorithms in a more consistent
(prettier!) way
ok djm
- djm@cvs.openbsd.org 2010/09/22 22:58:51
[atomicio.c atomicio.h misc.c misc.h scp.c sftp-client.c]
[sftp-client.h sftp.1 sftp.c]
add an option per-read/write callback to atomicio
factor out bandwidth limiting code from scp(1) into a generic bandwidth
limiter that can be attached using the atomicio callback mechanism
add a bandwidth limit option to sftp(1) using the above
"very nice" markus@
20100910
- (dtucker) [openbsd-compat/port-linux.c] Check is_selinux_enabled for exact
......
/* $OpenBSD: atomicio.c,v 1.25 2007/06/25 12:02:27 dtucker Exp $ */
/* $OpenBSD: atomicio.c,v 1.26 2010/09/22 22:58:51 djm Exp $ */
/*
* Copyright (c) 2006 Damien Miller. All rights reserved.
* Copyright (c) 2005 Anil Madhavapeddy. All rights reserved.
......@@ -48,7 +48,8 @@
* ensure all of data on socket comes through. f==read || f==vwrite
*/
size_t
atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
atomicio6(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n,
int (*cb)(void *, size_t), void *cb_arg)
{
char *s = _s;
size_t pos = 0;
......@@ -73,17 +74,28 @@ atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
return pos;
default:
pos += (size_t)res;
if (cb != NULL && cb(cb_arg, (size_t)res) == -1) {
errno = EINTR;
return pos;
}
}
}
return (pos);
return pos;
}
size_t
atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
{
return atomicio6(f, fd, _s, n, NULL, NULL);
}
/*
* ensure all of data on socket comes through. f==readv || f==writev
*/
size_t
atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd,
const struct iovec *_iov, int iovcnt)
atomiciov6(ssize_t (*f) (int, const struct iovec *, int), int fd,
const struct iovec *_iov, int iovcnt,
int (*cb)(void *, size_t), void *cb_arg)
{
size_t pos = 0, rem;
ssize_t res;
......@@ -137,6 +149,17 @@ atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd,
iov[0].iov_base = ((char *)iov[0].iov_base) + rem;
iov[0].iov_len -= rem;
}
if (cb != NULL && cb(cb_arg, (size_t)res) == -1) {
errno = EINTR;
return pos;
}
}
return pos;
}
size_t
atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd,
const struct iovec *_iov, int iovcnt)
{
return atomiciov6(f, fd, _iov, iovcnt, NULL, NULL);
}
/* $OpenBSD: atomicio.h,v 1.10 2006/08/03 03:34:41 deraadt Exp $ */
/* $OpenBSD: atomicio.h,v 1.11 2010/09/22 22:58:51 djm Exp $ */
/*
* Copyright (c) 2006 Damien Miller. All rights reserved.
......@@ -32,6 +32,9 @@
/*
* Ensure all of data on socket comes through. f==read || f==vwrite
*/
size_t
atomicio6(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n,
int (*cb)(void *, size_t), void *);
size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
#define vwrite (ssize_t (*)(int, void *, size_t))write
......@@ -39,6 +42,9 @@ size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
/*
* ensure all of data on socket comes through. f==readv || f==writev
*/
size_t
atomiciov6(ssize_t (*f) (int, const struct iovec *, int), int fd,
const struct iovec *_iov, int iovcnt, int (*cb)(void *, size_t), void *);
size_t atomiciov(ssize_t (*)(int, const struct iovec *, int),
int, const struct iovec *, int);
......
/* $OpenBSD: misc.c,v 1.80 2010/07/21 02:10:58 djm Exp $ */
/* $OpenBSD: misc.c,v 1.81 2010/09/22 22:58:51 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2005,2006 Damien Miller. All rights reserved.
......@@ -860,6 +860,70 @@ timingsafe_bcmp(const void *b1, const void *b2, size_t n)
ret |= *p1++ ^ *p2++;
return (ret != 0);
}
void
bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen)
{
bw->buflen = buflen;
bw->rate = kbps;
bw->thresh = bw->rate;
bw->lamt = 0;
timerclear(&bw->bwstart);
timerclear(&bw->bwend);
}
/* Callback from read/write loop to insert bandwidth-limiting delays */
void
bandwidth_limit(struct bwlimit *bw, size_t read_len)
{
u_int64_t waitlen;
struct timespec ts, rm;
if (!timerisset(&bw->bwstart)) {
gettimeofday(&bw->bwstart, NULL);
return;
}
bw->lamt += read_len;
if (bw->lamt < bw->thresh)
return;
gettimeofday(&bw->bwend, NULL);
timersub(&bw->bwend, &bw->bwstart, &bw->bwend);
if (!timerisset(&bw->bwend))
return;
bw->lamt *= 8;
waitlen = (double)1000000L * bw->lamt / bw->rate;
bw->bwstart.tv_sec = waitlen / 1000000L;
bw->bwstart.tv_usec = waitlen % 1000000L;
if (timercmp(&bw->bwstart, &bw->bwend, >)) {
timersub(&bw->bwstart, &bw->bwend, &bw->bwend);
/* Adjust the wait time */
if (bw->bwend.tv_sec) {
bw->thresh /= 2;
if (bw->thresh < bw->buflen / 4)
bw->thresh = bw->buflen / 4;
} else if (bw->bwend.tv_usec < 10000) {
bw->thresh *= 2;
if (bw->thresh > bw->buflen * 8)
bw->thresh = bw->buflen * 8;
}
TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts);
while (nanosleep(&ts, &rm) == -1) {
if (errno != EINTR)
break;
ts = rm;
}
}
bw->lamt = 0;
gettimeofday(&bw->bwstart, NULL);
}
void
sock_set_v6only(int s)
{
......
/* $OpenBSD: misc.h,v 1.43 2010/07/13 23:13:16 djm Exp $ */
/* $OpenBSD: misc.h,v 1.44 2010/09/22 22:58:51 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
......@@ -80,6 +80,15 @@ void put_u32(void *, u_int32_t)
void put_u16(void *, u_int16_t)
__attribute__((__bounded__( __minbytes__, 1, 2)));
struct bwlimit {
size_t buflen;
u_int64_t rate, thresh, lamt;
struct timeval bwstart, bwend;
};
void bandwidth_limit_init(struct bwlimit *, u_int64_t, size_t);
void bandwidth_limit(struct bwlimit *, size_t);
/* readpass.c */
......
/* $OpenBSD: scp.c,v 1.166 2010/07/01 13:06:59 millert Exp $ */
/* $OpenBSD: scp.c,v 1.167 2010/09/22 22:58:51 djm Exp $ */
/*
* scp - secure remote copy. This is basically patched BSD rcp which
* uses ssh to do the data transfer (instead of using rcmd).
......@@ -120,13 +120,12 @@ extern char *__progname;
int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
void bwlimit(int);
/* Struct for addargs */
arglist args;
/* Bandwidth limit */
off_t limit_rate = 0;
long long limit_kbps = 0;
struct bwlimit bwlimit;
/* Name of current file being transferred. */
char *curfile;
......@@ -312,15 +311,14 @@ void sink(int, char *[]);
void source(int, char *[]);
void tolocal(int, char *[]);
void toremote(char *, int, char *[]);
size_t scpio(ssize_t (*)(int, void *, size_t), int, void *, size_t, off_t *);
void usage(void);
int
main(int argc, char **argv)
{
int ch, fflag, tflag, status, n;
double speed;
char *targ, *endp, **newargv;
char *targ, **newargv;
const char *errstr;
extern char *optarg;
extern int optind;
......@@ -369,10 +367,12 @@ main(int argc, char **argv)
addargs(&args, "-oBatchmode yes");
break;
case 'l':
speed = strtod(optarg, &endp);
if (speed <= 0 || *endp != '\0')
limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
&errstr);
if (errstr != NULL)
usage();
limit_rate = speed * 1024;
limit_kbps *= 1024; /* kbps */
bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN);
break;
case 'p':
pflag = 1;
......@@ -474,41 +474,16 @@ main(int argc, char **argv)
exit(errs != 0);
}
/*
* atomicio-like wrapper that also applies bandwidth limits and updates
* the progressmeter counter.
*/
size_t
scpio(ssize_t (*f)(int, void *, size_t), int fd, void *_p, size_t l, off_t *c)
/* Callback from atomicio6 to update progress meter and limit bandwidth */
static int
scpio(void *_cnt, size_t s)
{
u_char *p = (u_char *)_p;
size_t offset;
ssize_t r;
struct pollfd pfd;
pfd.fd = fd;
pfd.events = f == read ? POLLIN : POLLOUT;
for (offset = 0; offset < l;) {
r = f(fd, p + offset, l - offset);
if (r == 0) {
errno = EPIPE;
return offset;
}
if (r < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) {
(void)poll(&pfd, 1, -1); /* Ignore errors */
continue;
}
return offset;
}
offset += (size_t)r;
*c += (off_t)r;
if (limit_rate)
bwlimit(r);
}
return offset;
off_t *cnt = (off_t *)_cnt;
*cnt += s;
if (limit_kbps > 0)
bandwidth_limit(&bwlimit, s);
return 0;
}
void
......@@ -750,7 +725,7 @@ next: if (fd != -1) {
(void)atomicio(vwrite, remout, bp->buf, amt);
continue;
}
if (scpio(vwrite, remout, bp->buf, amt,
if (atomicio6(vwrite, remout, bp->buf, amt, scpio,
&statbytes) != amt)
haderr = errno;
}
......@@ -824,60 +799,6 @@ rsource(char *name, struct stat *statp)
(void) response();
}
void
bwlimit(int amount)
{
static struct timeval bwstart, bwend;
static int lamt, thresh = 16384;
u_int64_t waitlen;
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;
waitlen = (double)1000000L * lamt / limit_rate;
bwstart.tv_sec = waitlen / 1000000L;
bwstart.tv_usec = waitlen % 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 < 10000) {
thresh *= 2;
if (thresh > COPY_BUFLEN * 4)
thresh = COPY_BUFLEN * 4;
}
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)
{
......@@ -1071,7 +992,8 @@ bad: run_err("%s: %s", np, strerror(errno));
amt = size - i;
count += amt;
do {
j = scpio(read, remin, cp, amt, &statbytes);
j = atomicio6(read, remin, cp, amt,
scpio, &statbytes);
if (j == 0) {
run_err("%s", j != EPIPE ?
strerror(errno) :
......
This diff is collapsed.
/* $OpenBSD: sftp-client.h,v 1.18 2009/08/18 18:36:20 djm Exp $ */
/* $OpenBSD: sftp-client.h,v 1.19 2010/09/22 22:58:51 djm Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
......@@ -51,7 +51,7 @@ struct sftp_statvfs {
* Initialise a SSH filexfer connection. Returns NULL on error or
* a pointer to a initialized sftp_conn struct on success.
*/
struct sftp_conn *do_init(int, int, u_int, u_int);
struct sftp_conn *do_init(int, int, u_int, u_int, u_int64_t);
u_int sftp_proto_version(struct sftp_conn *);
......
.\" $OpenBSD: sftp.1,v 1.84 2010/09/19 21:30:05 jmc Exp $
.\" $OpenBSD: sftp.1,v 1.85 2010/09/22 22:58:51 djm Exp $
.\"
.\" Copyright (c) 2001 Damien Miller. All rights reserved.
.\"
......@@ -22,7 +22,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd $Mdocdate: September 19 2010 $
.Dd $Mdocdate: September 22 2010 $
.Dt SFTP 1
.Os
.Sh NAME
......@@ -38,6 +38,7 @@
.Op Fl D Ar sftp_server_path
.Op Fl F Ar ssh_config
.Op Fl i Ar identity_file
.Op Fl l Ar limit
.Op Fl o Ar ssh_option
.Op Fl P Ar port
.Op Fl R Ar num_requests
......@@ -159,6 +160,8 @@ Selects the file from which the identity (private key) for public key
authentication is read.
This option is directly passed to
.Xr ssh 1 .
.It Fl l Ar limit
Limits the used bandwidth, specified in Kbit/s.
.It Fl o Ar ssh_option
Can be used to pass options to
.Nm ssh
......
/* $OpenBSD: sftp.c,v 1.125 2010/06/18 00:58:39 djm Exp $ */
/* $OpenBSD: sftp.c,v 1.126 2010/09/22 22:58:51 djm Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
......@@ -2073,6 +2073,7 @@ main(int argc, char **argv)
int debug_level = 0, sshver = 2;
char *file1 = NULL, *sftp_server = NULL;
char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
const char *errstr;
LogLevel ll = SYSLOG_LEVEL_INFO;
arglist args;
extern int optind;
......@@ -2080,6 +2081,7 @@ main(int argc, char **argv)
struct sftp_conn *conn;
size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
size_t num_requests = DEFAULT_NUM_REQUESTS;
long long limit_kbps = 0;
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd();
......@@ -2097,7 +2099,7 @@ main(int argc, char **argv)
infile = stdin;
while ((ch = getopt(argc, argv,
"1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
"1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
switch (ch) {
/* Passed through to ssh(1) */
case '4':
......@@ -2158,6 +2160,13 @@ main(int argc, char **argv)
case 'D':
sftp_direct = optarg;
break;
case 'l':
limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
&errstr);
if (errstr != NULL)
usage();
limit_kbps *= 1024; /* kbps */
break;
case 'r':
global_rflag = 1;
break;
......@@ -2235,7 +2244,7 @@ main(int argc, char **argv)
}
freeargs(&args);
conn = do_init(in, out, copy_buffer_len, num_requests);
conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
if (conn == NULL)
fatal("Couldn't initialise connection to server");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment