diff options
Diffstat (limited to 'scp.c')
-rw-r--r-- | scp.c | 335 |
1 files changed, 134 insertions, 201 deletions
@@ -75,13 +75,14 @@ | |||
75 | */ | 75 | */ |
76 | 76 | ||
77 | #include "includes.h" | 77 | #include "includes.h" |
78 | RCSID("$OpenBSD: scp.c,v 1.91 2002/06/19 00:27:55 deraadt Exp $"); | 78 | RCSID("$OpenBSD: scp.c,v 1.102 2003/03/05 22:33:43 markus Exp $"); |
79 | 79 | ||
80 | #include "xmalloc.h" | 80 | #include "xmalloc.h" |
81 | #include "atomicio.h" | 81 | #include "atomicio.h" |
82 | #include "pathnames.h" | 82 | #include "pathnames.h" |
83 | #include "log.h" | 83 | #include "log.h" |
84 | #include "misc.h" | 84 | #include "misc.h" |
85 | #include "progressmeter.h" | ||
85 | 86 | ||
86 | #ifdef HAVE___PROGNAME | 87 | #ifdef HAVE___PROGNAME |
87 | extern char *__progname; | 88 | extern char *__progname; |
@@ -89,29 +90,13 @@ extern char *__progname; | |||
89 | char *__progname; | 90 | char *__progname; |
90 | #endif | 91 | #endif |
91 | 92 | ||
92 | /* For progressmeter() -- number of seconds before xfer considered "stalled" */ | 93 | void bwlimit(int); |
93 | #define STALLTIME 5 | ||
94 | /* alarm() interval for updating progress meter */ | ||
95 | #define PROGRESSTIME 1 | ||
96 | |||
97 | /* Visual statistics about files as they are transferred. */ | ||
98 | void progressmeter(int); | ||
99 | |||
100 | /* Returns width of the terminal (for progress meter calculations). */ | ||
101 | int getttywidth(void); | ||
102 | int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc); | ||
103 | 94 | ||
104 | /* Struct for addargs */ | 95 | /* Struct for addargs */ |
105 | arglist args; | 96 | arglist args; |
106 | 97 | ||
107 | /* Time a transfer started. */ | 98 | /* Bandwidth limit */ |
108 | static struct timeval start; | 99 | off_t limitbw = 0; |
109 | |||
110 | /* Number of bytes of current file transferred so far. */ | ||
111 | volatile off_t statbytes; | ||
112 | |||
113 | /* Total size of current file. */ | ||
114 | off_t totalbytes = 0; | ||
115 | 100 | ||
116 | /* Name of current file being transferred. */ | 101 | /* Name of current file being transferred. */ |
117 | char *curfile; | 102 | char *curfile; |
@@ -125,6 +110,9 @@ int showprogress = 1; | |||
125 | /* This is the program to execute for the secured connection. ("ssh" or -S) */ | 110 | /* This is the program to execute for the secured connection. ("ssh" or -S) */ |
126 | char *ssh_program = _PATH_SSH_PROGRAM; | 111 | char *ssh_program = _PATH_SSH_PROGRAM; |
127 | 112 | ||
113 | /* This is used to store the pid of ssh_program */ | ||
114 | pid_t do_cmd_pid; | ||
115 | |||
128 | /* | 116 | /* |
129 | * This function executes the given command as the specified user on the | 117 | * This function executes the given command as the specified user on the |
130 | * given host. This returns < 0 if execution fails, and >= 0 otherwise. This | 118 | * given host. This returns < 0 if execution fails, and >= 0 otherwise. This |
@@ -159,7 +147,8 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) | |||
159 | close(reserved[1]); | 147 | close(reserved[1]); |
160 | 148 | ||
161 | /* For a child to execute the command on the remote host using ssh. */ | 149 | /* For a child to execute the command on the remote host using ssh. */ |
162 | if (fork() == 0) { | 150 | do_cmd_pid = fork(); |
151 | if (do_cmd_pid == 0) { | ||
163 | /* Child. */ | 152 | /* Child. */ |
164 | close(pin[1]); | 153 | close(pin[1]); |
165 | close(pout[0]); | 154 | close(pout[0]); |
@@ -177,6 +166,8 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) | |||
177 | execvp(ssh_program, args.list); | 166 | execvp(ssh_program, args.list); |
178 | perror(ssh_program); | 167 | perror(ssh_program); |
179 | exit(1); | 168 | exit(1); |
169 | } else if (do_cmd_pid == -1) { | ||
170 | fatal("fork: %s", strerror(errno)); | ||
180 | } | 171 | } |
181 | /* Parent. Close the other side, and return the local side. */ | 172 | /* Parent. Close the other side, and return the local side. */ |
182 | close(pin[0]); | 173 | close(pin[0]); |
@@ -219,8 +210,9 @@ main(argc, argv) | |||
219 | int argc; | 210 | int argc; |
220 | char *argv[]; | 211 | char *argv[]; |
221 | { | 212 | { |
222 | int ch, fflag, tflag; | 213 | int ch, fflag, tflag, status; |
223 | char *targ; | 214 | double speed; |
215 | char *targ, *endp; | ||
224 | extern char *optarg; | 216 | extern char *optarg; |
225 | extern int optind; | 217 | extern int optind; |
226 | 218 | ||
@@ -233,7 +225,7 @@ main(argc, argv) | |||
233 | addargs(&args, "-oClearAllForwardings yes"); | 225 | addargs(&args, "-oClearAllForwardings yes"); |
234 | 226 | ||
235 | fflag = tflag = 0; | 227 | fflag = tflag = 0; |
236 | while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q1246S:o:F:")) != -1) | 228 | while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1) |
237 | switch (ch) { | 229 | switch (ch) { |
238 | /* User-visible flags. */ | 230 | /* User-visible flags. */ |
239 | case '1': | 231 | case '1': |
@@ -255,6 +247,12 @@ main(argc, argv) | |||
255 | case 'B': | 247 | case 'B': |
256 | addargs(&args, "-oBatchmode yes"); | 248 | addargs(&args, "-oBatchmode yes"); |
257 | break; | 249 | break; |
250 | case 'l': | ||
251 | speed = strtod(optarg, &endp); | ||
252 | if (speed <= 0 || *endp != '\0') | ||
253 | usage(); | ||
254 | limitbw = speed * 1024; | ||
255 | break; | ||
258 | case 'p': | 256 | case 'p': |
259 | pflag = 1; | 257 | pflag = 1; |
260 | break; | 258 | break; |
@@ -319,6 +317,7 @@ main(argc, argv) | |||
319 | targetshouldbedirectory = 1; | 317 | targetshouldbedirectory = 1; |
320 | 318 | ||
321 | remin = remout = -1; | 319 | remin = remout = -1; |
320 | do_cmd_pid = -1; | ||
322 | /* Command to be executed on remote system using "ssh". */ | 321 | /* Command to be executed on remote system using "ssh". */ |
323 | (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", | 322 | (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", |
324 | verbose_mode ? " -v" : "", | 323 | verbose_mode ? " -v" : "", |
@@ -334,6 +333,22 @@ main(argc, argv) | |||
334 | if (targetshouldbedirectory) | 333 | if (targetshouldbedirectory) |
335 | verifydir(argv[argc - 1]); | 334 | verifydir(argv[argc - 1]); |
336 | } | 335 | } |
336 | /* | ||
337 | * Finally check the exit status of the ssh process, if one was forked | ||
338 | * and no error has occured yet | ||
339 | */ | ||
340 | if (do_cmd_pid != -1 && errs == 0) { | ||
341 | if (remin != -1) | ||
342 | (void) close(remin); | ||
343 | if (remout != -1) | ||
344 | (void) close(remout); | ||
345 | if (waitpid(do_cmd_pid, &status, 0) == -1) | ||
346 | errs = 1; | ||
347 | else { | ||
348 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) | ||
349 | errs = 1; | ||
350 | } | ||
351 | } | ||
337 | exit(errs != 0); | 352 | exit(errs != 0); |
338 | } | 353 | } |
339 | 354 | ||
@@ -349,14 +364,12 @@ toremote(targ, argc, argv) | |||
349 | if (*targ == 0) | 364 | if (*targ == 0) |
350 | targ = "."; | 365 | targ = "."; |
351 | 366 | ||
352 | if ((thost = strchr(argv[argc - 1], '@'))) { | 367 | if ((thost = strrchr(argv[argc - 1], '@'))) { |
353 | /* user@host */ | 368 | /* user@host */ |
354 | *thost++ = 0; | 369 | *thost++ = 0; |
355 | tuser = argv[argc - 1]; | 370 | tuser = argv[argc - 1]; |
356 | if (*tuser == '\0') | 371 | if (*tuser == '\0') |
357 | tuser = NULL; | 372 | tuser = NULL; |
358 | else if (!okname(tuser)) | ||
359 | exit(1); | ||
360 | } else { | 373 | } else { |
361 | thost = argv[argc - 1]; | 374 | thost = argv[argc - 1]; |
362 | tuser = NULL; | 375 | tuser = NULL; |
@@ -370,7 +383,7 @@ toremote(targ, argc, argv) | |||
370 | *src++ = 0; | 383 | *src++ = 0; |
371 | if (*src == 0) | 384 | if (*src == 0) |
372 | src = "."; | 385 | src = "."; |
373 | host = strchr(argv[i], '@'); | 386 | host = strrchr(argv[i], '@'); |
374 | len = strlen(ssh_program) + strlen(argv[i]) + | 387 | len = strlen(ssh_program) + strlen(argv[i]) + |
375 | strlen(src) + (tuser ? strlen(tuser) : 0) + | 388 | strlen(src) + (tuser ? strlen(tuser) : 0) + |
376 | strlen(thost) + strlen(targ) + | 389 | strlen(thost) + strlen(targ) + |
@@ -382,8 +395,14 @@ toremote(targ, argc, argv) | |||
382 | suser = argv[i]; | 395 | suser = argv[i]; |
383 | if (*suser == '\0') | 396 | if (*suser == '\0') |
384 | suser = pwd->pw_name; | 397 | suser = pwd->pw_name; |
385 | else if (!okname(suser)) | 398 | else if (!okname(suser)) { |
399 | xfree(bp); | ||
386 | continue; | 400 | continue; |
401 | } | ||
402 | if (tuser && !okname(tuser)) { | ||
403 | xfree(bp); | ||
404 | continue; | ||
405 | } | ||
387 | snprintf(bp, len, | 406 | snprintf(bp, len, |
388 | "%s%s %s -n " | 407 | "%s%s %s -n " |
389 | "-l %s %s %s %s '%s%s%s:%s'", | 408 | "-l %s %s %s %s '%s%s%s:%s'", |
@@ -449,7 +468,7 @@ tolocal(argc, argv) | |||
449 | *src++ = 0; | 468 | *src++ = 0; |
450 | if (*src == 0) | 469 | if (*src == 0) |
451 | src = "."; | 470 | src = "."; |
452 | if ((host = strchr(argv[i], '@')) == NULL) { | 471 | if ((host = strrchr(argv[i], '@')) == NULL) { |
453 | host = argv[i]; | 472 | host = argv[i]; |
454 | suser = NULL; | 473 | suser = NULL; |
455 | } else { | 474 | } else { |
@@ -457,8 +476,6 @@ tolocal(argc, argv) | |||
457 | suser = argv[i]; | 476 | suser = argv[i]; |
458 | if (*suser == '\0') | 477 | if (*suser == '\0') |
459 | suser = pwd->pw_name; | 478 | suser = pwd->pw_name; |
460 | else if (!okname(suser)) | ||
461 | continue; | ||
462 | } | 479 | } |
463 | host = cleanhostname(host); | 480 | host = cleanhostname(host); |
464 | len = strlen(src) + CMDNEEDS + 20; | 481 | len = strlen(src) + CMDNEEDS + 20; |
@@ -484,7 +501,7 @@ source(argc, argv) | |||
484 | struct stat stb; | 501 | struct stat stb; |
485 | static BUF buffer; | 502 | static BUF buffer; |
486 | BUF *bp; | 503 | BUF *bp; |
487 | off_t i, amt, result; | 504 | off_t i, amt, result, statbytes; |
488 | int fd, haderr, indx; | 505 | int fd, haderr, indx; |
489 | char *last, *name, buf[2048]; | 506 | char *last, *name, buf[2048]; |
490 | int len; | 507 | int len; |
@@ -549,7 +566,6 @@ syserr: run_err("%s: %s", name, strerror(errno)); | |||
549 | #endif | 566 | #endif |
550 | if (verbose_mode) { | 567 | if (verbose_mode) { |
551 | fprintf(stderr, "Sending file modes: %s", buf); | 568 | fprintf(stderr, "Sending file modes: %s", buf); |
552 | fflush(stderr); | ||
553 | } | 569 | } |
554 | (void) atomicio(write, remout, buf, strlen(buf)); | 570 | (void) atomicio(write, remout, buf, strlen(buf)); |
555 | if (response() < 0) | 571 | if (response() < 0) |
@@ -558,10 +574,8 @@ syserr: run_err("%s: %s", name, strerror(errno)); | |||
558 | next: (void) close(fd); | 574 | next: (void) close(fd); |
559 | continue; | 575 | continue; |
560 | } | 576 | } |
561 | if (showprogress) { | 577 | if (showprogress) |
562 | totalbytes = stb.st_size; | 578 | start_progress_meter(curfile, stb.st_size, &statbytes); |
563 | progressmeter(-1); | ||
564 | } | ||
565 | /* Keep writing after an error so that we stay sync'd up. */ | 579 | /* Keep writing after an error so that we stay sync'd up. */ |
566 | for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { | 580 | for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { |
567 | amt = bp->cnt; | 581 | amt = bp->cnt; |
@@ -580,9 +594,11 @@ next: (void) close(fd); | |||
580 | haderr = result >= 0 ? EIO : errno; | 594 | haderr = result >= 0 ? EIO : errno; |
581 | statbytes += result; | 595 | statbytes += result; |
582 | } | 596 | } |
597 | if (limitbw) | ||
598 | bwlimit(amt); | ||
583 | } | 599 | } |
584 | if (showprogress) | 600 | if (showprogress) |
585 | progressmeter(1); | 601 | stop_progress_meter(); |
586 | 602 | ||
587 | if (close(fd) < 0 && !haderr) | 603 | if (close(fd) < 0 && !haderr) |
588 | haderr = errno; | 604 | haderr = errno; |
@@ -650,6 +666,60 @@ rsource(name, statp) | |||
650 | } | 666 | } |
651 | 667 | ||
652 | void | 668 | void |
669 | bwlimit(int amount) | ||
670 | { | ||
671 | static struct timeval bwstart, bwend; | ||
672 | static int lamt, thresh = 16384; | ||
673 | u_int64_t wait; | ||
674 | struct timespec ts, rm; | ||
675 | |||
676 | if (!timerisset(&bwstart)) { | ||
677 | gettimeofday(&bwstart, NULL); | ||
678 | return; | ||
679 | } | ||
680 | |||
681 | lamt += amount; | ||
682 | if (lamt < thresh) | ||
683 | return; | ||
684 | |||
685 | gettimeofday(&bwend, NULL); | ||
686 | timersub(&bwend, &bwstart, &bwend); | ||
687 | if (!timerisset(&bwend)) | ||
688 | return; | ||
689 | |||
690 | lamt *= 8; | ||
691 | wait = (double)1000000L * lamt / limitbw; | ||
692 | |||
693 | bwstart.tv_sec = wait / 1000000L; | ||
694 | bwstart.tv_usec = wait % 1000000L; | ||
695 | |||
696 | if (timercmp(&bwstart, &bwend, >)) { | ||
697 | timersub(&bwstart, &bwend, &bwend); | ||
698 | |||
699 | /* Adjust the wait time */ | ||
700 | if (bwend.tv_sec) { | ||
701 | thresh /= 2; | ||
702 | if (thresh < 2048) | ||
703 | thresh = 2048; | ||
704 | } else if (bwend.tv_usec < 100) { | ||
705 | thresh *= 2; | ||
706 | if (thresh > 32768) | ||
707 | thresh = 32768; | ||
708 | } | ||
709 | |||
710 | TIMEVAL_TO_TIMESPEC(&bwend, &ts); | ||
711 | while (nanosleep(&ts, &rm) == -1) { | ||
712 | if (errno != EINTR) | ||
713 | break; | ||
714 | ts = rm; | ||
715 | } | ||
716 | } | ||
717 | |||
718 | lamt = 0; | ||
719 | gettimeofday(&bwstart, NULL); | ||
720 | } | ||
721 | |||
722 | void | ||
653 | sink(argc, argv) | 723 | sink(argc, argv) |
654 | int argc; | 724 | int argc; |
655 | char *argv[]; | 725 | char *argv[]; |
@@ -662,7 +732,7 @@ sink(argc, argv) | |||
662 | BUF *bp; | 732 | BUF *bp; |
663 | off_t i, j; | 733 | off_t i, j; |
664 | int amt, count, exists, first, mask, mode, ofd, omode; | 734 | int amt, count, exists, first, mask, mode, ofd, omode; |
665 | off_t size; | 735 | off_t size, statbytes; |
666 | int setimes, targisdir, wrerrno = 0; | 736 | int setimes, targisdir, wrerrno = 0; |
667 | char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; | 737 | char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; |
668 | struct timeval tv[2]; | 738 | struct timeval tv[2]; |
@@ -824,11 +894,9 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
824 | cp = bp->buf; | 894 | cp = bp->buf; |
825 | wrerr = NO; | 895 | wrerr = NO; |
826 | 896 | ||
827 | if (showprogress) { | ||
828 | totalbytes = size; | ||
829 | progressmeter(-1); | ||
830 | } | ||
831 | statbytes = 0; | 897 | statbytes = 0; |
898 | if (showprogress) | ||
899 | start_progress_meter(curfile, size, &statbytes); | ||
832 | for (count = i = 0; i < size; i += 4096) { | 900 | for (count = i = 0; i < size; i += 4096) { |
833 | amt = 4096; | 901 | amt = 4096; |
834 | if (i + amt > size) | 902 | if (i + amt > size) |
@@ -848,6 +916,10 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
848 | cp += j; | 916 | cp += j; |
849 | statbytes += j; | 917 | statbytes += j; |
850 | } while (amt > 0); | 918 | } while (amt > 0); |
919 | |||
920 | if (limitbw) | ||
921 | bwlimit(4096); | ||
922 | |||
851 | if (count == bp->cnt) { | 923 | if (count == bp->cnt) { |
852 | /* Keep reading so we stay sync'd up. */ | 924 | /* Keep reading so we stay sync'd up. */ |
853 | if (wrerr == NO) { | 925 | if (wrerr == NO) { |
@@ -862,13 +934,13 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
862 | } | 934 | } |
863 | } | 935 | } |
864 | if (showprogress) | 936 | if (showprogress) |
865 | progressmeter(1); | 937 | stop_progress_meter(); |
866 | if (count != 0 && wrerr == NO && | 938 | if (count != 0 && wrerr == NO && |
867 | (j = atomicio(write, ofd, bp->buf, count)) != count) { | 939 | (j = atomicio(write, ofd, bp->buf, count)) != count) { |
868 | wrerr = YES; | 940 | wrerr = YES; |
869 | wrerrno = j >= 0 ? EIO : errno; | 941 | wrerrno = j >= 0 ? EIO : errno; |
870 | } | 942 | } |
871 | if (ftruncate(ofd, size)) { | 943 | if (wrerr == NO && ftruncate(ofd, size) != 0) { |
872 | run_err("%s: truncate: %s", np, strerror(errno)); | 944 | run_err("%s: truncate: %s", np, strerror(errno)); |
873 | wrerr = DISPLAYED; | 945 | wrerr = DISPLAYED; |
874 | } | 946 | } |
@@ -957,8 +1029,8 @@ void | |||
957 | usage(void) | 1029 | usage(void) |
958 | { | 1030 | { |
959 | (void) fprintf(stderr, | 1031 | (void) fprintf(stderr, |
960 | "usage: scp [-pqrvBC46] [-F config] [-S program] [-P port]\n" | 1032 | "usage: scp [-pqrvBC1246] [-F config] [-S program] [-P port]\n" |
961 | " [-c cipher] [-i identity] [-o option]\n" | 1033 | " [-c cipher] [-i identity] [-l limit] [-o option]\n" |
962 | " [[user@]host1:]file1 [...] [[user@]host2:]file2\n"); | 1034 | " [[user@]host1:]file1 [...] [[user@]host2:]file2\n"); |
963 | exit(1); | 1035 | exit(1); |
964 | } | 1036 | } |
@@ -1015,9 +1087,18 @@ okname(cp0) | |||
1015 | c = (int)*cp; | 1087 | c = (int)*cp; |
1016 | if (c & 0200) | 1088 | if (c & 0200) |
1017 | goto bad; | 1089 | goto bad; |
1018 | if (!isalpha(c) && !isdigit(c) && | 1090 | if (!isalpha(c) && !isdigit(c)) { |
1019 | c != '_' && c != '-' && c != '.' && c != '+') | 1091 | switch (c) { |
1020 | goto bad; | 1092 | case '\'': |
1093 | case '"': | ||
1094 | case '`': | ||
1095 | case ' ': | ||
1096 | case '#': | ||
1097 | goto bad; | ||
1098 | default: | ||
1099 | break; | ||
1100 | } | ||
1101 | } | ||
1021 | } while (*++cp); | 1102 | } while (*++cp); |
1022 | return (1); | 1103 | return (1); |
1023 | 1104 | ||
@@ -1038,11 +1119,9 @@ allocbuf(bp, fd, blksize) | |||
1038 | run_err("fstat: %s", strerror(errno)); | 1119 | run_err("fstat: %s", strerror(errno)); |
1039 | return (0); | 1120 | return (0); |
1040 | } | 1121 | } |
1041 | if (stb.st_blksize == 0) | 1122 | size = roundup(stb.st_blksize, blksize); |
1123 | if (size == 0) | ||
1042 | size = blksize; | 1124 | size = blksize; |
1043 | else | ||
1044 | size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % | ||
1045 | stb.st_blksize; | ||
1046 | #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ | 1125 | #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ |
1047 | size = blksize; | 1126 | size = blksize; |
1048 | #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ | 1127 | #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ |
@@ -1068,149 +1147,3 @@ lostconn(signo) | |||
1068 | else | 1147 | else |
1069 | exit(1); | 1148 | exit(1); |
1070 | } | 1149 | } |
1071 | |||
1072 | static void | ||
1073 | updateprogressmeter(int ignore) | ||
1074 | { | ||
1075 | int save_errno = errno; | ||
1076 | |||
1077 | progressmeter(0); | ||
1078 | signal(SIGALRM, updateprogressmeter); | ||
1079 | alarm(PROGRESSTIME); | ||
1080 | errno = save_errno; | ||
1081 | } | ||
1082 | |||
1083 | static int | ||
1084 | foregroundproc(void) | ||
1085 | { | ||
1086 | static pid_t pgrp = -1; | ||
1087 | int ctty_pgrp; | ||
1088 | |||
1089 | if (pgrp == -1) | ||
1090 | pgrp = getpgrp(); | ||
1091 | |||
1092 | #ifdef HAVE_TCGETPGRP | ||
1093 | return ((ctty_pgrp = tcgetpgrp(STDOUT_FILENO)) != -1 && | ||
1094 | ctty_pgrp == pgrp); | ||
1095 | #else | ||
1096 | return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && | ||
1097 | ctty_pgrp == pgrp)); | ||
1098 | #endif | ||
1099 | } | ||
1100 | |||
1101 | void | ||
1102 | progressmeter(int flag) | ||
1103 | { | ||
1104 | static const char prefixes[] = " KMGTP"; | ||
1105 | static struct timeval lastupdate; | ||
1106 | static off_t lastsize; | ||
1107 | struct timeval now, td, wait; | ||
1108 | off_t cursize, abbrevsize; | ||
1109 | double elapsed; | ||
1110 | int ratio, barlength, i, remaining; | ||
1111 | char buf[512]; | ||
1112 | |||
1113 | if (flag == -1) { | ||
1114 | (void) gettimeofday(&start, (struct timezone *) 0); | ||
1115 | lastupdate = start; | ||
1116 | lastsize = 0; | ||
1117 | } | ||
1118 | if (foregroundproc() == 0) | ||
1119 | return; | ||
1120 | |||
1121 | (void) gettimeofday(&now, (struct timezone *) 0); | ||
1122 | cursize = statbytes; | ||
1123 | if (totalbytes != 0) { | ||
1124 | ratio = 100.0 * cursize / totalbytes; | ||
1125 | ratio = MAX(ratio, 0); | ||
1126 | ratio = MIN(ratio, 100); | ||
1127 | } else | ||
1128 | ratio = 100; | ||
1129 | |||
1130 | snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); | ||
1131 | |||
1132 | barlength = getttywidth() - 51; | ||
1133 | if (barlength > 0) { | ||
1134 | i = barlength * ratio / 100; | ||
1135 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1136 | "|%.*s%*s|", i, | ||
1137 | "*******************************************************" | ||
1138 | "*******************************************************" | ||
1139 | "*******************************************************" | ||
1140 | "*******************************************************" | ||
1141 | "*******************************************************" | ||
1142 | "*******************************************************" | ||
1143 | "*******************************************************", | ||
1144 | barlength - i, ""); | ||
1145 | } | ||
1146 | i = 0; | ||
1147 | abbrevsize = cursize; | ||
1148 | while (abbrevsize >= 100000 && i < sizeof(prefixes)) { | ||
1149 | i++; | ||
1150 | abbrevsize >>= 10; | ||
1151 | } | ||
1152 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5lu %c%c ", | ||
1153 | (unsigned long) abbrevsize, prefixes[i], | ||
1154 | prefixes[i] == ' ' ? ' ' : 'B'); | ||
1155 | |||
1156 | timersub(&now, &lastupdate, &wait); | ||
1157 | if (cursize > lastsize) { | ||
1158 | lastupdate = now; | ||
1159 | lastsize = cursize; | ||
1160 | if (wait.tv_sec >= STALLTIME) { | ||
1161 | start.tv_sec += wait.tv_sec; | ||
1162 | start.tv_usec += wait.tv_usec; | ||
1163 | } | ||
1164 | wait.tv_sec = 0; | ||
1165 | } | ||
1166 | timersub(&now, &start, &td); | ||
1167 | elapsed = td.tv_sec + (td.tv_usec / 1000000.0); | ||
1168 | |||
1169 | if (flag != 1 && | ||
1170 | (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes)) { | ||
1171 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1172 | " --:-- ETA"); | ||
1173 | } else if (wait.tv_sec >= STALLTIME) { | ||
1174 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1175 | " - stalled -"); | ||
1176 | } else { | ||
1177 | if (flag != 1) | ||
1178 | remaining = (int)(totalbytes / (statbytes / elapsed) - | ||
1179 | elapsed); | ||
1180 | else | ||
1181 | remaining = elapsed; | ||
1182 | |||
1183 | i = remaining / 3600; | ||
1184 | if (i) | ||
1185 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1186 | "%2d:", i); | ||
1187 | else | ||
1188 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1189 | " "); | ||
1190 | i = remaining % 3600; | ||
1191 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1192 | "%02d:%02d%s", i / 60, i % 60, | ||
1193 | (flag != 1) ? " ETA" : " "); | ||
1194 | } | ||
1195 | atomicio(write, fileno(stdout), buf, strlen(buf)); | ||
1196 | |||
1197 | if (flag == -1) { | ||
1198 | mysignal(SIGALRM, updateprogressmeter); | ||
1199 | alarm(PROGRESSTIME); | ||
1200 | } else if (flag == 1) { | ||
1201 | alarm(0); | ||
1202 | atomicio(write, fileno(stdout), "\n", 1); | ||
1203 | statbytes = 0; | ||
1204 | } | ||
1205 | } | ||
1206 | |||
1207 | int | ||
1208 | getttywidth(void) | ||
1209 | { | ||
1210 | struct winsize winsize; | ||
1211 | |||
1212 | if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) | ||
1213 | return (winsize.ws_col ? winsize.ws_col : 80); | ||
1214 | else | ||
1215 | return (80); | ||
1216 | } | ||