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 | ||
@@ -295,6 +302,50 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) | |||
295 | return 0; | 302 | return 0; |
296 | } | 303 | } |
297 | 304 | ||
305 | /* | ||
306 | * This functions executes a command simlar to do_cmd(), but expects the | ||
307 | * input and output descriptors to be setup by a previous call to do_cmd(). | ||
308 | * This way the input and output of two commands can be connected. | ||
309 | */ | ||
310 | int | ||
311 | do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) | ||
312 | { | ||
313 | pid_t pid; | ||
314 | int status; | ||
315 | |||
316 | if (verbose_mode) | ||
317 | fprintf(stderr, | ||
318 | "Executing: 2nd program %s host %s, user %s, command %s\n", | ||
319 | ssh_program, host, | ||
320 | remuser ? remuser : "(unspecified)", cmd); | ||
321 | |||
322 | /* Fork a child to execute the command on the remote host using ssh. */ | ||
323 | pid = fork(); | ||
324 | if (pid == 0) { | ||
325 | dup2(fdin, 0); | ||
326 | dup2(fdout, 1); | ||
327 | |||
328 | replacearg(&args, 0, "%s", ssh_program); | ||
329 | if (remuser != NULL) { | ||
330 | addargs(&args, "-l"); | ||
331 | addargs(&args, "%s", remuser); | ||
332 | } | ||
333 | addargs(&args, "--"); | ||
334 | addargs(&args, "%s", host); | ||
335 | addargs(&args, "%s", cmd); | ||
336 | |||
337 | execvp(ssh_program, args.list); | ||
338 | perror(ssh_program); | ||
339 | exit(1); | ||
340 | } else if (pid == -1) { | ||
341 | fatal("fork: %s", strerror(errno)); | ||
342 | } | ||
343 | while (waitpid(pid, &status, 0) == -1) | ||
344 | if (errno != EINTR) | ||
345 | fatal("do_cmd2: waitpid: %s", strerror(errno)); | ||
346 | return 0; | ||
347 | } | ||
348 | |||
298 | typedef struct { | 349 | typedef struct { |
299 | size_t cnt; | 350 | size_t cnt; |
300 | char *buf; | 351 | char *buf; |
@@ -320,15 +371,14 @@ void sink(int, char *[]); | |||
320 | void source(int, char *[]); | 371 | void source(int, char *[]); |
321 | void tolocal(int, char *[]); | 372 | void tolocal(int, char *[]); |
322 | void toremote(char *, int, char *[]); | 373 | void toremote(char *, int, char *[]); |
323 | size_t scpio(ssize_t (*)(int, void *, size_t), int, void *, size_t, off_t *); | ||
324 | void usage(void); | 374 | void usage(void); |
325 | 375 | ||
326 | int | 376 | int |
327 | main(int argc, char **argv) | 377 | main(int argc, char **argv) |
328 | { | 378 | { |
329 | int ch, fflag, tflag, status, n; | 379 | int ch, fflag, tflag, status, n; |
330 | double speed; | 380 | char *targ, **newargv; |
331 | char *targ, *endp, **newargv; | 381 | const char *errstr; |
332 | extern char *optarg; | 382 | extern char *optarg; |
333 | extern int optind; | 383 | extern int optind; |
334 | 384 | ||
@@ -344,15 +394,16 @@ main(int argc, char **argv) | |||
344 | __progname = ssh_get_progname(argv[0]); | 394 | __progname = ssh_get_progname(argv[0]); |
345 | 395 | ||
346 | memset(&args, '\0', sizeof(args)); | 396 | memset(&args, '\0', sizeof(args)); |
347 | args.list = NULL; | 397 | memset(&remote_remote_args, '\0', sizeof(remote_remote_args)); |
398 | args.list = remote_remote_args.list = NULL; | ||
348 | addargs(&args, "%s", ssh_program); | 399 | addargs(&args, "%s", ssh_program); |
349 | addargs(&args, "-x"); | 400 | addargs(&args, "-x"); |
350 | addargs(&args, "-oForwardAgent no"); | 401 | addargs(&args, "-oForwardAgent=no"); |
351 | addargs(&args, "-oPermitLocalCommand no"); | 402 | addargs(&args, "-oPermitLocalCommand=no"); |
352 | addargs(&args, "-oClearAllForwardings yes"); | 403 | addargs(&args, "-oClearAllForwardings=yes"); |
353 | 404 | ||
354 | fflag = tflag = 0; | 405 | fflag = tflag = 0; |
355 | while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1) | 406 | while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) |
356 | switch (ch) { | 407 | switch (ch) { |
357 | /* User-visible flags. */ | 408 | /* User-visible flags. */ |
358 | case '1': | 409 | case '1': |
@@ -361,26 +412,37 @@ main(int argc, char **argv) | |||
361 | case '6': | 412 | case '6': |
362 | case 'C': | 413 | case 'C': |
363 | addargs(&args, "-%c", ch); | 414 | addargs(&args, "-%c", ch); |
415 | addargs(&remote_remote_args, "-%c", ch); | ||
416 | break; | ||
417 | case '3': | ||
418 | throughlocal = 1; | ||
364 | break; | 419 | break; |
365 | case 'o': | 420 | case 'o': |
366 | case 'c': | 421 | case 'c': |
367 | case 'i': | 422 | case 'i': |
368 | case 'F': | 423 | case 'F': |
424 | addargs(&remote_remote_args, "-%c", ch); | ||
425 | addargs(&remote_remote_args, "%s", optarg); | ||
369 | addargs(&args, "-%c", ch); | 426 | addargs(&args, "-%c", ch); |
370 | addargs(&args, "%s", optarg); | 427 | addargs(&args, "%s", optarg); |
371 | break; | 428 | break; |
372 | case 'P': | 429 | case 'P': |
430 | addargs(&remote_remote_args, "-p"); | ||
431 | addargs(&remote_remote_args, "%s", optarg); | ||
373 | addargs(&args, "-p"); | 432 | addargs(&args, "-p"); |
374 | addargs(&args, "%s", optarg); | 433 | addargs(&args, "%s", optarg); |
375 | break; | 434 | break; |
376 | case 'B': | 435 | case 'B': |
377 | addargs(&args, "-oBatchmode yes"); | 436 | addargs(&remote_remote_args, "-oBatchmode=yes"); |
437 | addargs(&args, "-oBatchmode=yes"); | ||
378 | break; | 438 | break; |
379 | case 'l': | 439 | case 'l': |
380 | speed = strtod(optarg, &endp); | 440 | limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, |
381 | if (speed <= 0 || *endp != '\0') | 441 | &errstr); |
442 | if (errstr != NULL) | ||
382 | usage(); | 443 | usage(); |
383 | limit_rate = speed * 1024; | 444 | limit_kbps *= 1024; /* kbps */ |
445 | bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN); | ||
384 | break; | 446 | break; |
385 | case 'p': | 447 | case 'p': |
386 | pflag = 1; | 448 | pflag = 1; |
@@ -393,10 +455,12 @@ main(int argc, char **argv) | |||
393 | break; | 455 | break; |
394 | case 'v': | 456 | case 'v': |
395 | addargs(&args, "-v"); | 457 | addargs(&args, "-v"); |
458 | addargs(&remote_remote_args, "-v"); | ||
396 | verbose_mode = 1; | 459 | verbose_mode = 1; |
397 | break; | 460 | break; |
398 | case 'q': | 461 | case 'q': |
399 | addargs(&args, "-q"); | 462 | addargs(&args, "-q"); |
463 | addargs(&remote_remote_args, "-q"); | ||
400 | showprogress = 0; | 464 | showprogress = 0; |
401 | break; | 465 | break; |
402 | 466 | ||
@@ -482,41 +546,16 @@ main(int argc, char **argv) | |||
482 | exit(errs != 0); | 546 | exit(errs != 0); |
483 | } | 547 | } |
484 | 548 | ||
485 | /* | 549 | /* Callback from atomicio6 to update progress meter and limit bandwidth */ |
486 | * atomicio-like wrapper that also applies bandwidth limits and updates | 550 | static int |
487 | * the progressmeter counter. | 551 | scpio(void *_cnt, size_t s) |
488 | */ | ||
489 | size_t | ||
490 | scpio(ssize_t (*f)(int, void *, size_t), int fd, void *_p, size_t l, off_t *c) | ||
491 | { | 552 | { |
492 | u_char *p = (u_char *)_p; | 553 | off_t *cnt = (off_t *)_cnt; |
493 | size_t offset; | 554 | |
494 | ssize_t r; | 555 | *cnt += s; |
495 | struct pollfd pfd; | 556 | if (limit_kbps > 0) |
496 | 557 | bandwidth_limit(&bwlimit, s); | |
497 | pfd.fd = fd; | 558 | return 0; |
498 | pfd.events = f == read ? POLLIN : POLLOUT; | ||
499 | for (offset = 0; offset < l;) { | ||
500 | r = f(fd, p + offset, l - offset); | ||
501 | if (r == 0) { | ||
502 | errno = EPIPE; | ||
503 | return offset; | ||
504 | } | ||
505 | if (r < 0) { | ||
506 | if (errno == EINTR) | ||
507 | continue; | ||
508 | if (errno == EAGAIN || errno == EWOULDBLOCK) { | ||
509 | (void)poll(&pfd, 1, -1); /* Ignore errors */ | ||
510 | continue; | ||
511 | } | ||
512 | return offset; | ||
513 | } | ||
514 | offset += (size_t)r; | ||
515 | *c += (off_t)r; | ||
516 | if (limit_rate) | ||
517 | bwlimit(r); | ||
518 | } | ||
519 | return offset; | ||
520 | } | 559 | } |
521 | 560 | ||
522 | void | 561 | void |
@@ -525,6 +564,7 @@ toremote(char *targ, int argc, char **argv) | |||
525 | char *bp, *host, *src, *suser, *thost, *tuser, *arg; | 564 | char *bp, *host, *src, *suser, *thost, *tuser, *arg; |
526 | arglist alist; | 565 | arglist alist; |
527 | int i; | 566 | int i; |
567 | u_int j; | ||
528 | 568 | ||
529 | memset(&alist, '\0', sizeof(alist)); | 569 | memset(&alist, '\0', sizeof(alist)); |
530 | alist.list = NULL; | 570 | alist.list = NULL; |
@@ -552,15 +592,45 @@ toremote(char *targ, int argc, char **argv) | |||
552 | 592 | ||
553 | for (i = 0; i < argc - 1; i++) { | 593 | for (i = 0; i < argc - 1; i++) { |
554 | src = colon(argv[i]); | 594 | src = colon(argv[i]); |
555 | if (src) { /* remote to remote */ | 595 | if (src && throughlocal) { /* extended remote to remote */ |
596 | *src++ = 0; | ||
597 | if (*src == 0) | ||
598 | src = "."; | ||
599 | host = strrchr(argv[i], '@'); | ||
600 | if (host) { | ||
601 | *host++ = 0; | ||
602 | host = cleanhostname(host); | ||
603 | suser = argv[i]; | ||
604 | if (*suser == '\0') | ||
605 | suser = pwd->pw_name; | ||
606 | else if (!okname(suser)) | ||
607 | continue; | ||
608 | } else { | ||
609 | host = cleanhostname(argv[i]); | ||
610 | suser = NULL; | ||
611 | } | ||
612 | xasprintf(&bp, "%s -f -- %s", cmd, src); | ||
613 | if (do_cmd(host, suser, bp, &remin, &remout) < 0) | ||
614 | exit(1); | ||
615 | (void) xfree(bp); | ||
616 | host = cleanhostname(thost); | ||
617 | xasprintf(&bp, "%s -t -- %s", cmd, targ); | ||
618 | if (do_cmd2(host, tuser, bp, remin, remout) < 0) | ||
619 | exit(1); | ||
620 | (void) xfree(bp); | ||
621 | (void) close(remin); | ||
622 | (void) close(remout); | ||
623 | remin = remout = -1; | ||
624 | } else if (src) { /* standard remote to remote */ | ||
556 | freeargs(&alist); | 625 | freeargs(&alist); |
557 | addargs(&alist, "%s", ssh_program); | 626 | addargs(&alist, "%s", ssh_program); |
558 | if (verbose_mode) | ||
559 | addargs(&alist, "-v"); | ||
560 | addargs(&alist, "-x"); | 627 | addargs(&alist, "-x"); |
561 | addargs(&alist, "-oClearAllForwardings yes"); | 628 | addargs(&alist, "-oClearAllForwardings=yes"); |
562 | addargs(&alist, "-n"); | 629 | addargs(&alist, "-n"); |
563 | 630 | for (j = 0; j < remote_remote_args.num; j++) { | |
631 | addargs(&alist, "%s", | ||
632 | remote_remote_args.list[j]); | ||
633 | } | ||
564 | *src++ = 0; | 634 | *src++ = 0; |
565 | if (*src == 0) | 635 | if (*src == 0) |
566 | src = "."; | 636 | src = "."; |
@@ -758,7 +828,7 @@ next: if (fd != -1) { | |||
758 | (void)atomicio(vwrite, remout, bp->buf, amt); | 828 | (void)atomicio(vwrite, remout, bp->buf, amt); |
759 | continue; | 829 | continue; |
760 | } | 830 | } |
761 | if (scpio(vwrite, remout, bp->buf, amt, | 831 | if (atomicio6(vwrite, remout, bp->buf, amt, scpio, |
762 | &statbytes) != amt) | 832 | &statbytes) != amt) |
763 | haderr = errno; | 833 | haderr = errno; |
764 | } | 834 | } |
@@ -833,60 +903,6 @@ rsource(char *name, struct stat *statp) | |||
833 | } | 903 | } |
834 | 904 | ||
835 | void | 905 | void |
836 | bwlimit(int amount) | ||
837 | { | ||
838 | static struct timeval bwstart, bwend; | ||
839 | static int lamt, thresh = 16384; | ||
840 | u_int64_t waitlen; | ||
841 | struct timespec ts, rm; | ||
842 | |||
843 | if (!timerisset(&bwstart)) { | ||
844 | gettimeofday(&bwstart, NULL); | ||
845 | return; | ||
846 | } | ||
847 | |||
848 | lamt += amount; | ||
849 | if (lamt < thresh) | ||
850 | return; | ||
851 | |||
852 | gettimeofday(&bwend, NULL); | ||
853 | timersub(&bwend, &bwstart, &bwend); | ||
854 | if (!timerisset(&bwend)) | ||
855 | return; | ||
856 | |||
857 | lamt *= 8; | ||
858 | waitlen = (double)1000000L * lamt / limit_rate; | ||
859 | |||
860 | bwstart.tv_sec = waitlen / 1000000L; | ||
861 | bwstart.tv_usec = waitlen % 1000000L; | ||
862 | |||
863 | if (timercmp(&bwstart, &bwend, >)) { | ||
864 | timersub(&bwstart, &bwend, &bwend); | ||
865 | |||
866 | /* Adjust the wait time */ | ||
867 | if (bwend.tv_sec) { | ||
868 | thresh /= 2; | ||
869 | if (thresh < 2048) | ||
870 | thresh = 2048; | ||
871 | } else if (bwend.tv_usec < 10000) { | ||
872 | thresh *= 2; | ||
873 | if (thresh > COPY_BUFLEN * 4) | ||
874 | thresh = COPY_BUFLEN * 4; | ||
875 | } | ||
876 | |||
877 | TIMEVAL_TO_TIMESPEC(&bwend, &ts); | ||
878 | while (nanosleep(&ts, &rm) == -1) { | ||
879 | if (errno != EINTR) | ||
880 | break; | ||
881 | ts = rm; | ||
882 | } | ||
883 | } | ||
884 | |||
885 | lamt = 0; | ||
886 | gettimeofday(&bwstart, NULL); | ||
887 | } | ||
888 | |||
889 | void | ||
890 | sink(int argc, char **argv) | 906 | sink(int argc, char **argv) |
891 | { | 907 | { |
892 | static BUF buffer; | 908 | static BUF buffer; |
@@ -1079,7 +1095,8 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
1079 | amt = size - i; | 1095 | amt = size - i; |
1080 | count += amt; | 1096 | count += amt; |
1081 | do { | 1097 | do { |
1082 | j = scpio(read, remin, cp, amt, &statbytes); | 1098 | j = atomicio6(read, remin, cp, amt, |
1099 | scpio, &statbytes); | ||
1083 | if (j == 0) { | 1100 | if (j == 0) { |
1084 | run_err("%s", j != EPIPE ? | 1101 | run_err("%s", j != EPIPE ? |
1085 | strerror(errno) : | 1102 | strerror(errno) : |
@@ -1205,7 +1222,7 @@ void | |||
1205 | usage(void) | 1222 | usage(void) |
1206 | { | 1223 | { |
1207 | (void) fprintf(stderr, | 1224 | (void) fprintf(stderr, |
1208 | "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" | 1225 | "usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" |
1209 | " [-l limit] [-o ssh_option] [-P port] [-S program]\n" | 1226 | " [-l limit] [-o ssh_option] [-P port] [-S program]\n" |
1210 | " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); | 1227 | " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); |
1211 | exit(1); | 1228 | exit(1); |