summaryrefslogtreecommitdiff
path: root/scp.c
diff options
context:
space:
mode:
Diffstat (limited to 'scp.c')
-rw-r--r--scp.c241
1 files changed, 129 insertions, 112 deletions
diff --git a/scp.c b/scp.c
index b28d75eba..69344b804 100644
--- a/scp.c
+++ b/scp.c
@@ -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
121int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); 121int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
122 122int do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout);
123void bwlimit(int);
124 123
125/* Struct for addargs */ 124/* Struct for addargs */
126arglist args; 125arglist args;
126arglist remote_remote_args;
127 127
128/* Bandwidth limit */ 128/* Bandwidth limit */
129off_t limit_rate = 0; 129long long limit_kbps = 0;
130struct bwlimit bwlimit;
130 131
131/* Name of current file being transferred. */ 132/* Name of current file being transferred. */
132char *curfile; 133char *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. */
138int showprogress = 1; 139int showprogress = 1;
139 140
141/*
142 * This is set to non-zero if remote-remote copy should be piped
143 * through this process.
144 */
145int 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) */
141char *ssh_program = _PATH_SSH_PROGRAM; 148char *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 */
310int
311do_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
298typedef struct { 349typedef struct {
299 size_t cnt; 350 size_t cnt;
300 char *buf; 351 char *buf;
@@ -320,15 +371,14 @@ void sink(int, char *[]);
320void source(int, char *[]); 371void source(int, char *[]);
321void tolocal(int, char *[]); 372void tolocal(int, char *[]);
322void toremote(char *, int, char *[]); 373void toremote(char *, int, char *[]);
323size_t scpio(ssize_t (*)(int, void *, size_t), int, void *, size_t, off_t *);
324void usage(void); 374void usage(void);
325 375
326int 376int
327main(int argc, char **argv) 377main(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 550static int
487 * the progressmeter counter. 551scpio(void *_cnt, size_t s)
488 */
489size_t
490scpio(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
522void 561void
@@ -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
835void 905void
836bwlimit(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
889void
890sink(int argc, char **argv) 906sink(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
1205usage(void) 1222usage(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);