diff options
author | Damien Miller <djm@mindrot.org> | 2010-09-24 22:15:11 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2010-09-24 22:15:11 +1000 |
commit | 65e42f87fe945a2bf30d7e02358554dbaefa8a4c (patch) | |
tree | 102c10a0b5328a40c79dca19d208f0ca0c1671b5 /scp.c | |
parent | 7fe2b1fec3b364faf952828f3875b8e7eed8feb4 (diff) |
- 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@
Diffstat (limited to 'scp.c')
-rw-r--r-- | scp.c | 122 |
1 files changed, 22 insertions, 100 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: scp.c,v 1.166 2010/07/01 13:06:59 millert Exp $ */ | 1 | /* $OpenBSD: scp.c,v 1.167 2010/09/22 22:58:51 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * scp - secure remote copy. This is basically patched BSD rcp which | 3 | * scp - secure remote copy. This is basically patched BSD rcp which |
4 | * uses ssh to do the data transfer (instead of using rcmd). | 4 | * uses ssh to do the data transfer (instead of using rcmd). |
@@ -120,13 +120,12 @@ extern char *__progname; | |||
120 | 120 | ||
121 | int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); | 121 | int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); |
122 | 122 | ||
123 | void bwlimit(int); | ||
124 | |||
125 | /* Struct for addargs */ | 123 | /* Struct for addargs */ |
126 | arglist args; | 124 | arglist args; |
127 | 125 | ||
128 | /* Bandwidth limit */ | 126 | /* Bandwidth limit */ |
129 | off_t limit_rate = 0; | 127 | long long limit_kbps = 0; |
128 | struct bwlimit bwlimit; | ||
130 | 129 | ||
131 | /* Name of current file being transferred. */ | 130 | /* Name of current file being transferred. */ |
132 | char *curfile; | 131 | char *curfile; |
@@ -312,15 +311,14 @@ void sink(int, char *[]); | |||
312 | void source(int, char *[]); | 311 | void source(int, char *[]); |
313 | void tolocal(int, char *[]); | 312 | void tolocal(int, char *[]); |
314 | void toremote(char *, int, char *[]); | 313 | void toremote(char *, int, char *[]); |
315 | size_t scpio(ssize_t (*)(int, void *, size_t), int, void *, size_t, off_t *); | ||
316 | void usage(void); | 314 | void usage(void); |
317 | 315 | ||
318 | int | 316 | int |
319 | main(int argc, char **argv) | 317 | main(int argc, char **argv) |
320 | { | 318 | { |
321 | int ch, fflag, tflag, status, n; | 319 | int ch, fflag, tflag, status, n; |
322 | double speed; | 320 | char *targ, **newargv; |
323 | char *targ, *endp, **newargv; | 321 | const char *errstr; |
324 | extern char *optarg; | 322 | extern char *optarg; |
325 | extern int optind; | 323 | extern int optind; |
326 | 324 | ||
@@ -369,10 +367,12 @@ main(int argc, char **argv) | |||
369 | addargs(&args, "-oBatchmode yes"); | 367 | addargs(&args, "-oBatchmode yes"); |
370 | break; | 368 | break; |
371 | case 'l': | 369 | case 'l': |
372 | speed = strtod(optarg, &endp); | 370 | limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, |
373 | if (speed <= 0 || *endp != '\0') | 371 | &errstr); |
372 | if (errstr != NULL) | ||
374 | usage(); | 373 | usage(); |
375 | limit_rate = speed * 1024; | 374 | limit_kbps *= 1024; /* kbps */ |
375 | bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN); | ||
376 | break; | 376 | break; |
377 | case 'p': | 377 | case 'p': |
378 | pflag = 1; | 378 | pflag = 1; |
@@ -474,41 +474,16 @@ main(int argc, char **argv) | |||
474 | exit(errs != 0); | 474 | exit(errs != 0); |
475 | } | 475 | } |
476 | 476 | ||
477 | /* | 477 | /* Callback from atomicio6 to update progress meter and limit bandwidth */ |
478 | * atomicio-like wrapper that also applies bandwidth limits and updates | 478 | static int |
479 | * the progressmeter counter. | 479 | scpio(void *_cnt, size_t s) |
480 | */ | ||
481 | size_t | ||
482 | scpio(ssize_t (*f)(int, void *, size_t), int fd, void *_p, size_t l, off_t *c) | ||
483 | { | 480 | { |
484 | u_char *p = (u_char *)_p; | 481 | off_t *cnt = (off_t *)_cnt; |
485 | size_t offset; | 482 | |
486 | ssize_t r; | 483 | *cnt += s; |
487 | struct pollfd pfd; | 484 | if (limit_kbps > 0) |
488 | 485 | bandwidth_limit(&bwlimit, s); | |
489 | pfd.fd = fd; | 486 | return 0; |
490 | pfd.events = f == read ? POLLIN : POLLOUT; | ||
491 | for (offset = 0; offset < l;) { | ||
492 | r = f(fd, p + offset, l - offset); | ||
493 | if (r == 0) { | ||
494 | errno = EPIPE; | ||
495 | return offset; | ||
496 | } | ||
497 | if (r < 0) { | ||
498 | if (errno == EINTR) | ||
499 | continue; | ||
500 | if (errno == EAGAIN || errno == EWOULDBLOCK) { | ||
501 | (void)poll(&pfd, 1, -1); /* Ignore errors */ | ||
502 | continue; | ||
503 | } | ||
504 | return offset; | ||
505 | } | ||
506 | offset += (size_t)r; | ||
507 | *c += (off_t)r; | ||
508 | if (limit_rate) | ||
509 | bwlimit(r); | ||
510 | } | ||
511 | return offset; | ||
512 | } | 487 | } |
513 | 488 | ||
514 | void | 489 | void |
@@ -750,7 +725,7 @@ next: if (fd != -1) { | |||
750 | (void)atomicio(vwrite, remout, bp->buf, amt); | 725 | (void)atomicio(vwrite, remout, bp->buf, amt); |
751 | continue; | 726 | continue; |
752 | } | 727 | } |
753 | if (scpio(vwrite, remout, bp->buf, amt, | 728 | if (atomicio6(vwrite, remout, bp->buf, amt, scpio, |
754 | &statbytes) != amt) | 729 | &statbytes) != amt) |
755 | haderr = errno; | 730 | haderr = errno; |
756 | } | 731 | } |
@@ -825,60 +800,6 @@ rsource(char *name, struct stat *statp) | |||
825 | } | 800 | } |
826 | 801 | ||
827 | void | 802 | void |
828 | bwlimit(int amount) | ||
829 | { | ||
830 | static struct timeval bwstart, bwend; | ||
831 | static int lamt, thresh = 16384; | ||
832 | u_int64_t waitlen; | ||
833 | struct timespec ts, rm; | ||
834 | |||
835 | if (!timerisset(&bwstart)) { | ||
836 | gettimeofday(&bwstart, NULL); | ||
837 | return; | ||
838 | } | ||
839 | |||
840 | lamt += amount; | ||
841 | if (lamt < thresh) | ||
842 | return; | ||
843 | |||
844 | gettimeofday(&bwend, NULL); | ||
845 | timersub(&bwend, &bwstart, &bwend); | ||
846 | if (!timerisset(&bwend)) | ||
847 | return; | ||
848 | |||
849 | lamt *= 8; | ||
850 | waitlen = (double)1000000L * lamt / limit_rate; | ||
851 | |||
852 | bwstart.tv_sec = waitlen / 1000000L; | ||
853 | bwstart.tv_usec = waitlen % 1000000L; | ||
854 | |||
855 | if (timercmp(&bwstart, &bwend, >)) { | ||
856 | timersub(&bwstart, &bwend, &bwend); | ||
857 | |||
858 | /* Adjust the wait time */ | ||
859 | if (bwend.tv_sec) { | ||
860 | thresh /= 2; | ||
861 | if (thresh < 2048) | ||
862 | thresh = 2048; | ||
863 | } else if (bwend.tv_usec < 10000) { | ||
864 | thresh *= 2; | ||
865 | if (thresh > COPY_BUFLEN * 4) | ||
866 | thresh = COPY_BUFLEN * 4; | ||
867 | } | ||
868 | |||
869 | TIMEVAL_TO_TIMESPEC(&bwend, &ts); | ||
870 | while (nanosleep(&ts, &rm) == -1) { | ||
871 | if (errno != EINTR) | ||
872 | break; | ||
873 | ts = rm; | ||
874 | } | ||
875 | } | ||
876 | |||
877 | lamt = 0; | ||
878 | gettimeofday(&bwstart, NULL); | ||
879 | } | ||
880 | |||
881 | void | ||
882 | sink(int argc, char **argv) | 803 | sink(int argc, char **argv) |
883 | { | 804 | { |
884 | static BUF buffer; | 805 | static BUF buffer; |
@@ -1071,7 +992,8 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
1071 | amt = size - i; | 992 | amt = size - i; |
1072 | count += amt; | 993 | count += amt; |
1073 | do { | 994 | do { |
1074 | j = scpio(read, remin, cp, amt, &statbytes); | 995 | j = atomicio6(read, remin, cp, amt, |
996 | scpio, &statbytes); | ||
1075 | if (j == 0) { | 997 | if (j == 0) { |
1076 | run_err("%s", j != EPIPE ? | 998 | run_err("%s", j != EPIPE ? |
1077 | strerror(errno) : | 999 | strerror(errno) : |