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 e07de42f7..18b2597fe 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
@@ -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 */
302int
303do_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
290typedef struct { 341typedef struct {
291 size_t cnt; 342 size_t cnt;
292 char *buf; 343 char *buf;
@@ -312,15 +363,14 @@ void sink(int, char *[]);
312void source(int, char *[]); 363void source(int, char *[]);
313void tolocal(int, char *[]); 364void tolocal(int, char *[]);
314void toremote(char *, int, char *[]); 365void toremote(char *, int, char *[]);
315size_t scpio(ssize_t (*)(int, void *, size_t), int, void *, size_t, off_t *);
316void usage(void); 366void usage(void);
317 367
318int 368int
319main(int argc, char **argv) 369main(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 542static int
479 * the progressmeter counter. 543scpio(void *_cnt, size_t s)
480 */
481size_t
482scpio(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
514void 553void
@@ -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
827void 897void
828bwlimit(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
881void
882sink(int argc, char **argv) 898sink(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
1197usage(void) 1214usage(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);