diff options
Diffstat (limited to 'scp.c')
-rw-r--r-- | scp.c | 241 |
1 files changed, 129 insertions, 112 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.170 2010/12/09 14:13:33 jmc 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). |
@@ -119,14 +119,15 @@ extern char *__progname; | |||
119 | #define COPY_BUFLEN 16384 | 119 | #define COPY_BUFLEN 16384 |
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 | int do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout); | |
123 | void bwlimit(int); | ||
124 | 123 | ||
125 | /* Struct for addargs */ | 124 | /* Struct for addargs */ |
126 | arglist args; | 125 | arglist args; |
126 | arglist remote_remote_args; | ||
127 | 127 | ||
128 | /* Bandwidth limit */ | 128 | /* Bandwidth limit */ |
129 | off_t limit_rate = 0; | 129 | long long limit_kbps = 0; |
130 | struct bwlimit bwlimit; | ||
130 | 131 | ||
131 | /* Name of current file being transferred. */ | 132 | /* Name of current file being transferred. */ |
132 | char *curfile; | 133 | char *curfile; |
@@ -137,6 +138,12 @@ int verbose_mode = 0; | |||
137 | /* This is set to zero if the progressmeter is not desired. */ | 138 | /* This is set to zero if the progressmeter is not desired. */ |
138 | int showprogress = 1; | 139 | int showprogress = 1; |
139 | 140 | ||
141 | /* | ||
142 | * This is set to non-zero if remote-remote copy should be piped | ||
143 | * through this process. | ||
144 | */ | ||
145 | int throughlocal = 0; | ||
146 | |||
140 | /* This is the program to execute for the secured connection. ("ssh" or -S) */ | 147 | /* This is the program to execute for the secured connection. ("ssh" or -S) */ |
141 | char *ssh_program = _PATH_SSH_PROGRAM; | 148 | char *ssh_program = _PATH_SSH_PROGRAM; |
142 | 149 | ||
@@ -287,6 +294,50 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) | |||
287 | return 0; | 294 | return 0; |
288 | } | 295 | } |
289 | 296 | ||
297 | /* | ||
298 | * This functions executes a command simlar to do_cmd(), but expects the | ||
299 | * input and output descriptors to be setup by a previous call to do_cmd(). | ||
300 | * This way the input and output of two commands can be connected. | ||
301 | */ | ||
302 | int | ||
303 | do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) | ||
304 | { | ||
305 | pid_t pid; | ||
306 | int status; | ||
307 | |||
308 | if (verbose_mode) | ||
309 | fprintf(stderr, | ||
310 | "Executing: 2nd program %s host %s, user %s, command %s\n", | ||
311 | ssh_program, host, | ||
312 | remuser ? remuser : "(unspecified)", cmd); | ||
313 | |||
314 | /* Fork a child to execute the command on the remote host using ssh. */ | ||
315 | pid = fork(); | ||
316 | if (pid == 0) { | ||
317 | dup2(fdin, 0); | ||
318 | dup2(fdout, 1); | ||
319 | |||
320 | replacearg(&args, 0, "%s", ssh_program); | ||
321 | if (remuser != NULL) { | ||
322 | addargs(&args, "-l"); | ||
323 | addargs(&args, "%s", remuser); | ||
324 | } | ||
325 | addargs(&args, "--"); | ||
326 | addargs(&args, "%s", host); | ||
327 | addargs(&args, "%s", cmd); | ||
328 | |||
329 | execvp(ssh_program, args.list); | ||
330 | perror(ssh_program); | ||
331 | exit(1); | ||
332 | } else if (pid == -1) { | ||
333 | fatal("fork: %s", strerror(errno)); | ||
334 | } | ||
335 | while (waitpid(pid, &status, 0) == -1) | ||
336 | if (errno != EINTR) | ||
337 | fatal("do_cmd2: waitpid: %s", strerror(errno)); | ||
338 | return 0; | ||
339 | } | ||
340 | |||
290 | typedef struct { | 341 | typedef struct { |
291 | size_t cnt; | 342 | size_t cnt; |
292 | char *buf; | 343 | char *buf; |
@@ -312,15 +363,14 @@ void sink(int, char *[]); | |||
312 | void source(int, char *[]); | 363 | void source(int, char *[]); |
313 | void tolocal(int, char *[]); | 364 | void tolocal(int, char *[]); |
314 | void toremote(char *, int, char *[]); | 365 | 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); | 366 | void usage(void); |
317 | 367 | ||
318 | int | 368 | int |
319 | main(int argc, char **argv) | 369 | main(int argc, char **argv) |
320 | { | 370 | { |
321 | int ch, fflag, tflag, status, n; | 371 | int ch, fflag, tflag, status, n; |
322 | double speed; | 372 | char *targ, **newargv; |
323 | char *targ, *endp, **newargv; | 373 | const char *errstr; |
324 | extern char *optarg; | 374 | extern char *optarg; |
325 | extern int optind; | 375 | extern int optind; |
326 | 376 | ||
@@ -336,15 +386,16 @@ main(int argc, char **argv) | |||
336 | __progname = ssh_get_progname(argv[0]); | 386 | __progname = ssh_get_progname(argv[0]); |
337 | 387 | ||
338 | memset(&args, '\0', sizeof(args)); | 388 | memset(&args, '\0', sizeof(args)); |
339 | args.list = NULL; | 389 | memset(&remote_remote_args, '\0', sizeof(remote_remote_args)); |
390 | args.list = remote_remote_args.list = NULL; | ||
340 | addargs(&args, "%s", ssh_program); | 391 | addargs(&args, "%s", ssh_program); |
341 | addargs(&args, "-x"); | 392 | addargs(&args, "-x"); |
342 | addargs(&args, "-oForwardAgent no"); | 393 | addargs(&args, "-oForwardAgent=no"); |
343 | addargs(&args, "-oPermitLocalCommand no"); | 394 | addargs(&args, "-oPermitLocalCommand=no"); |
344 | addargs(&args, "-oClearAllForwardings yes"); | 395 | addargs(&args, "-oClearAllForwardings=yes"); |
345 | 396 | ||
346 | fflag = tflag = 0; | 397 | fflag = tflag = 0; |
347 | while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1) | 398 | while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) |
348 | switch (ch) { | 399 | switch (ch) { |
349 | /* User-visible flags. */ | 400 | /* User-visible flags. */ |
350 | case '1': | 401 | case '1': |
@@ -353,26 +404,37 @@ main(int argc, char **argv) | |||
353 | case '6': | 404 | case '6': |
354 | case 'C': | 405 | case 'C': |
355 | addargs(&args, "-%c", ch); | 406 | addargs(&args, "-%c", ch); |
407 | addargs(&remote_remote_args, "-%c", ch); | ||
408 | break; | ||
409 | case '3': | ||
410 | throughlocal = 1; | ||
356 | break; | 411 | break; |
357 | case 'o': | 412 | case 'o': |
358 | case 'c': | 413 | case 'c': |
359 | case 'i': | 414 | case 'i': |
360 | case 'F': | 415 | case 'F': |
416 | addargs(&remote_remote_args, "-%c", ch); | ||
417 | addargs(&remote_remote_args, "%s", optarg); | ||
361 | addargs(&args, "-%c", ch); | 418 | addargs(&args, "-%c", ch); |
362 | addargs(&args, "%s", optarg); | 419 | addargs(&args, "%s", optarg); |
363 | break; | 420 | break; |
364 | case 'P': | 421 | case 'P': |
422 | addargs(&remote_remote_args, "-p"); | ||
423 | addargs(&remote_remote_args, "%s", optarg); | ||
365 | addargs(&args, "-p"); | 424 | addargs(&args, "-p"); |
366 | addargs(&args, "%s", optarg); | 425 | addargs(&args, "%s", optarg); |
367 | break; | 426 | break; |
368 | case 'B': | 427 | case 'B': |
369 | addargs(&args, "-oBatchmode yes"); | 428 | addargs(&remote_remote_args, "-oBatchmode=yes"); |
429 | addargs(&args, "-oBatchmode=yes"); | ||
370 | break; | 430 | break; |
371 | case 'l': | 431 | case 'l': |
372 | speed = strtod(optarg, &endp); | 432 | limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, |
373 | if (speed <= 0 || *endp != '\0') | 433 | &errstr); |
434 | if (errstr != NULL) | ||
374 | usage(); | 435 | usage(); |
375 | limit_rate = speed * 1024; | 436 | limit_kbps *= 1024; /* kbps */ |
437 | bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN); | ||
376 | break; | 438 | break; |
377 | case 'p': | 439 | case 'p': |
378 | pflag = 1; | 440 | pflag = 1; |
@@ -385,10 +447,12 @@ main(int argc, char **argv) | |||
385 | break; | 447 | break; |
386 | case 'v': | 448 | case 'v': |
387 | addargs(&args, "-v"); | 449 | addargs(&args, "-v"); |
450 | addargs(&remote_remote_args, "-v"); | ||
388 | verbose_mode = 1; | 451 | verbose_mode = 1; |
389 | break; | 452 | break; |
390 | case 'q': | 453 | case 'q': |
391 | addargs(&args, "-q"); | 454 | addargs(&args, "-q"); |
455 | addargs(&remote_remote_args, "-q"); | ||
392 | showprogress = 0; | 456 | showprogress = 0; |
393 | break; | 457 | break; |
394 | 458 | ||
@@ -474,41 +538,16 @@ main(int argc, char **argv) | |||
474 | exit(errs != 0); | 538 | exit(errs != 0); |
475 | } | 539 | } |
476 | 540 | ||
477 | /* | 541 | /* Callback from atomicio6 to update progress meter and limit bandwidth */ |
478 | * atomicio-like wrapper that also applies bandwidth limits and updates | 542 | static int |
479 | * the progressmeter counter. | 543 | 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 | { | 544 | { |
484 | u_char *p = (u_char *)_p; | 545 | off_t *cnt = (off_t *)_cnt; |
485 | size_t offset; | 546 | |
486 | ssize_t r; | 547 | *cnt += s; |
487 | struct pollfd pfd; | 548 | if (limit_kbps > 0) |
488 | 549 | bandwidth_limit(&bwlimit, s); | |
489 | pfd.fd = fd; | 550 | 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 | } | 551 | } |
513 | 552 | ||
514 | void | 553 | void |
@@ -517,6 +556,7 @@ toremote(char *targ, int argc, char **argv) | |||
517 | char *bp, *host, *src, *suser, *thost, *tuser, *arg; | 556 | char *bp, *host, *src, *suser, *thost, *tuser, *arg; |
518 | arglist alist; | 557 | arglist alist; |
519 | int i; | 558 | int i; |
559 | u_int j; | ||
520 | 560 | ||
521 | memset(&alist, '\0', sizeof(alist)); | 561 | memset(&alist, '\0', sizeof(alist)); |
522 | alist.list = NULL; | 562 | alist.list = NULL; |
@@ -544,15 +584,45 @@ toremote(char *targ, int argc, char **argv) | |||
544 | 584 | ||
545 | for (i = 0; i < argc - 1; i++) { | 585 | for (i = 0; i < argc - 1; i++) { |
546 | src = colon(argv[i]); | 586 | src = colon(argv[i]); |
547 | if (src) { /* remote to remote */ | 587 | if (src && throughlocal) { /* extended remote to remote */ |
588 | *src++ = 0; | ||
589 | if (*src == 0) | ||
590 | src = "."; | ||
591 | host = strrchr(argv[i], '@'); | ||
592 | if (host) { | ||
593 | *host++ = 0; | ||
594 | host = cleanhostname(host); | ||
595 | suser = argv[i]; | ||
596 | if (*suser == '\0') | ||
597 | suser = pwd->pw_name; | ||
598 | else if (!okname(suser)) | ||
599 | continue; | ||
600 | } else { | ||
601 | host = cleanhostname(argv[i]); | ||
602 | suser = NULL; | ||
603 | } | ||
604 | xasprintf(&bp, "%s -f -- %s", cmd, src); | ||
605 | if (do_cmd(host, suser, bp, &remin, &remout) < 0) | ||
606 | exit(1); | ||
607 | (void) xfree(bp); | ||
608 | host = cleanhostname(thost); | ||
609 | xasprintf(&bp, "%s -t -- %s", cmd, targ); | ||
610 | if (do_cmd2(host, tuser, bp, remin, remout) < 0) | ||
611 | exit(1); | ||
612 | (void) xfree(bp); | ||
613 | (void) close(remin); | ||
614 | (void) close(remout); | ||
615 | remin = remout = -1; | ||
616 | } else if (src) { /* standard remote to remote */ | ||
548 | freeargs(&alist); | 617 | freeargs(&alist); |
549 | addargs(&alist, "%s", ssh_program); | 618 | addargs(&alist, "%s", ssh_program); |
550 | if (verbose_mode) | ||
551 | addargs(&alist, "-v"); | ||
552 | addargs(&alist, "-x"); | 619 | addargs(&alist, "-x"); |
553 | addargs(&alist, "-oClearAllForwardings yes"); | 620 | addargs(&alist, "-oClearAllForwardings=yes"); |
554 | addargs(&alist, "-n"); | 621 | addargs(&alist, "-n"); |
555 | 622 | for (j = 0; j < remote_remote_args.num; j++) { | |
623 | addargs(&alist, "%s", | ||
624 | remote_remote_args.list[j]); | ||
625 | } | ||
556 | *src++ = 0; | 626 | *src++ = 0; |
557 | if (*src == 0) | 627 | if (*src == 0) |
558 | src = "."; | 628 | src = "."; |
@@ -750,7 +820,7 @@ next: if (fd != -1) { | |||
750 | (void)atomicio(vwrite, remout, bp->buf, amt); | 820 | (void)atomicio(vwrite, remout, bp->buf, amt); |
751 | continue; | 821 | continue; |
752 | } | 822 | } |
753 | if (scpio(vwrite, remout, bp->buf, amt, | 823 | if (atomicio6(vwrite, remout, bp->buf, amt, scpio, |
754 | &statbytes) != amt) | 824 | &statbytes) != amt) |
755 | haderr = errno; | 825 | haderr = errno; |
756 | } | 826 | } |
@@ -825,60 +895,6 @@ rsource(char *name, struct stat *statp) | |||
825 | } | 895 | } |
826 | 896 | ||
827 | void | 897 | 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) | 898 | sink(int argc, char **argv) |
883 | { | 899 | { |
884 | static BUF buffer; | 900 | static BUF buffer; |
@@ -1071,7 +1087,8 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
1071 | amt = size - i; | 1087 | amt = size - i; |
1072 | count += amt; | 1088 | count += amt; |
1073 | do { | 1089 | do { |
1074 | j = scpio(read, remin, cp, amt, &statbytes); | 1090 | j = atomicio6(read, remin, cp, amt, |
1091 | scpio, &statbytes); | ||
1075 | if (j == 0) { | 1092 | if (j == 0) { |
1076 | run_err("%s", j != EPIPE ? | 1093 | run_err("%s", j != EPIPE ? |
1077 | strerror(errno) : | 1094 | strerror(errno) : |
@@ -1197,7 +1214,7 @@ void | |||
1197 | usage(void) | 1214 | usage(void) |
1198 | { | 1215 | { |
1199 | (void) fprintf(stderr, | 1216 | (void) fprintf(stderr, |
1200 | "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" | 1217 | "usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" |
1201 | " [-l limit] [-o ssh_option] [-P port] [-S program]\n" | 1218 | " [-l limit] [-o ssh_option] [-P port] [-S program]\n" |
1202 | " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); | 1219 | " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); |
1203 | exit(1); | 1220 | exit(1); |