=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/scp.c,v retrieving revision 1.91 retrieving revision 1.91.2.2 diff -u -r1.91 -r1.91.2.2 --- src/usr.bin/ssh/scp.c 2002/06/19 00:27:55 1.91 +++ src/usr.bin/ssh/scp.c 2003/09/16 21:20:27 1.91.2.2 @@ -52,11 +52,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -75,38 +71,25 @@ */ #include "includes.h" -RCSID("$OpenBSD: scp.c,v 1.91 2002/06/19 00:27:55 deraadt Exp $"); +RCSID("$OpenBSD: scp.c,v 1.91.2.2 2003/09/16 21:20:27 brad Exp $"); #include "xmalloc.h" #include "atomicio.h" #include "pathnames.h" #include "log.h" #include "misc.h" +#include "progressmeter.h" -/* For progressmeter() -- number of seconds before xfer considered "stalled" */ -#define STALLTIME 5 -/* alarm() interval for updating progress meter */ -#define PROGRESSTIME 1 - -/* Visual statistics about files as they are transferred. */ -void progressmeter(int); - -/* Returns width of the terminal (for progress meter calculations). */ -int getttywidth(void); int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc); +void bwlimit(int); + /* Struct for addargs */ arglist args; -/* Time a transfer started. */ -static struct timeval start; +/* Bandwidth limit */ +off_t limit = 0; -/* Number of bytes of current file transferred so far. */ -volatile off_t statbytes; - -/* Total size of current file. */ -off_t totalbytes = 0; - /* Name of current file being transferred. */ char *curfile; @@ -119,6 +102,18 @@ /* This is the program to execute for the secured connection. ("ssh" or -S) */ char *ssh_program = _PATH_SSH_PROGRAM; +/* This is used to store the pid of ssh_program */ +pid_t do_cmd_pid = -1; + +static void +killchild(int signo) +{ + if (do_cmd_pid > 1) + kill(do_cmd_pid, signo); + + _exit(1); +} + /* * This function executes the given command as the specified user on the * given host. This returns < 0 if execution fails, and >= 0 otherwise. This @@ -152,8 +147,9 @@ close(reserved[0]); close(reserved[1]); - /* For a child to execute the command on the remote host using ssh. */ - if (fork() == 0) { + /* Fork a child to execute the command on the remote host using ssh. */ + do_cmd_pid = fork(); + if (do_cmd_pid == 0) { /* Child. */ close(pin[1]); close(pout[0]); @@ -171,12 +167,17 @@ execvp(ssh_program, args.list); perror(ssh_program); exit(1); + } else if (do_cmd_pid == -1) { + fatal("fork: %s", strerror(errno)); } /* Parent. Close the other side, and return the local side. */ close(pin[0]); *fdout = pin[1]; close(pout[1]); *fdin = pout[0]; + signal(SIGTERM, killchild); + signal(SIGINT, killchild); + signal(SIGHUP, killchild); return 0; } @@ -209,12 +210,11 @@ void usage(void); int -main(argc, argv) - int argc; - char *argv[]; +main(int argc, char **argv) { - int ch, fflag, tflag; - char *targ; + int ch, fflag, tflag, status; + double speed; + char *targ, *endp; extern char *optarg; extern int optind; @@ -225,9 +225,11 @@ addargs(&args, "-oClearAllForwardings yes"); fflag = tflag = 0; - while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:F:")) != -1) + while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1) switch (ch) { /* User-visible flags. */ + case '1': + case '2': case '4': case '6': case 'C': @@ -245,6 +247,12 @@ case 'B': addargs(&args, "-oBatchmode yes"); break; + case 'l': + speed = strtod(optarg, &endp); + if (speed <= 0 || *endp != '\0') + usage(); + limit = speed * 1024; + break; case 'p': pflag = 1; break; @@ -281,7 +289,7 @@ argv += optind; if ((pwd = getpwuid(userid = getuid())) == NULL) - fatal("unknown user %d", (int) userid); + fatal("unknown user %u", (u_int) userid); if (!isatty(STDERR_FILENO)) showprogress = 0; @@ -306,6 +314,7 @@ targetshouldbedirectory = 1; remin = remout = -1; + do_cmd_pid = -1; /* Command to be executed on remote system using "ssh". */ (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", @@ -321,13 +330,27 @@ if (targetshouldbedirectory) verifydir(argv[argc - 1]); } + /* + * Finally check the exit status of the ssh process, if one was forked + * and no error has occured yet + */ + if (do_cmd_pid != -1 && errs == 0) { + if (remin != -1) + (void) close(remin); + if (remout != -1) + (void) close(remout); + if (waitpid(do_cmd_pid, &status, 0) == -1) + errs = 1; + else { + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errs = 1; + } + } exit(errs != 0); } void -toremote(targ, argc, argv) - char *targ, *argv[]; - int argc; +toremote(char *targ, int argc, char **argv) { int i, len; char *bp, *host, *src, *suser, *thost, *tuser; @@ -336,14 +359,12 @@ if (*targ == 0) targ = "."; - if ((thost = strchr(argv[argc - 1], '@'))) { + if ((thost = strrchr(argv[argc - 1], '@'))) { /* user@host */ *thost++ = 0; tuser = argv[argc - 1]; if (*tuser == '\0') tuser = NULL; - else if (!okname(tuser)) - exit(1); } else { thost = argv[argc - 1]; tuser = NULL; @@ -357,7 +378,7 @@ *src++ = 0; if (*src == 0) src = "."; - host = strchr(argv[i], '@'); + host = strrchr(argv[i], '@'); len = strlen(ssh_program) + strlen(argv[i]) + strlen(src) + (tuser ? strlen(tuser) : 0) + strlen(thost) + strlen(targ) + @@ -369,8 +390,14 @@ suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; - else if (!okname(suser)) + else if (!okname(suser)) { + xfree(bp); continue; + } + if (tuser && !okname(tuser)) { + xfree(bp); + continue; + } snprintf(bp, len, "%s%s %s -n " "-l %s %s %s %s '%s%s%s:%s'", @@ -411,9 +438,7 @@ } void -tolocal(argc, argv) - int argc; - char *argv[]; +tolocal(int argc, char **argv) { int i, len; char *bp, *host, *src, *suser; @@ -436,7 +461,7 @@ *src++ = 0; if (*src == 0) src = "."; - if ((host = strchr(argv[i], '@')) == NULL) { + if ((host = strrchr(argv[i], '@')) == NULL) { host = argv[i]; suser = NULL; } else { @@ -444,8 +469,6 @@ suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; - else if (!okname(suser)) - continue; } host = cleanhostname(host); len = strlen(src) + CMDNEEDS + 20; @@ -464,14 +487,12 @@ } void -source(argc, argv) - int argc; - char *argv[]; +source(int argc, char **argv) { struct stat stb; static BUF buffer; BUF *bp; - off_t i, amt, result; + off_t i, amt, result, statbytes; int fd, haderr, indx; char *last, *name, buf[2048]; int len; @@ -519,7 +540,7 @@ (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", (u_long) stb.st_mtime, (u_long) stb.st_atime); - (void) atomicio(write, remout, buf, strlen(buf)); + (void) atomicio(vwrite, remout, buf, strlen(buf)); if (response() < 0) goto next; } @@ -529,19 +550,16 @@ (long long)stb.st_size, last); if (verbose_mode) { fprintf(stderr, "Sending file modes: %s", buf); - fflush(stderr); } - (void) atomicio(write, remout, buf, strlen(buf)); + (void) atomicio(vwrite, remout, buf, strlen(buf)); if (response() < 0) goto next; if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { next: (void) close(fd); continue; } - if (showprogress) { - totalbytes = stb.st_size; - progressmeter(-1); - } + if (showprogress) + start_progress_meter(curfile, stb.st_size, &statbytes); /* Keep writing after an error so that we stay sync'd up. */ for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { amt = bp->cnt; @@ -553,21 +571,23 @@ haderr = result >= 0 ? EIO : errno; } if (haderr) - (void) atomicio(write, remout, bp->buf, amt); + (void) atomicio(vwrite, remout, bp->buf, amt); else { - result = atomicio(write, remout, bp->buf, amt); + result = atomicio(vwrite, remout, bp->buf, amt); if (result != amt) haderr = result >= 0 ? EIO : errno; statbytes += result; } + if (limit) + bwlimit(amt); } if (showprogress) - progressmeter(1); + stop_progress_meter(); if (close(fd) < 0 && !haderr) haderr = errno; if (!haderr) - (void) atomicio(write, remout, "", 1); + (void) atomicio(vwrite, remout, "", 1); else run_err("%s: %s", name, strerror(haderr)); (void) response(); @@ -575,9 +595,7 @@ } void -rsource(name, statp) - char *name; - struct stat *statp; +rsource(char *name, struct stat *statp) { DIR *dirp; struct dirent *dp; @@ -596,7 +614,7 @@ (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", (u_long) statp->st_mtime, (u_long) statp->st_atime); - (void) atomicio(write, remout, path, strlen(path)); + (void) atomicio(vwrite, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; @@ -606,7 +624,7 @@ (u_int) (statp->st_mode & FILEMODEMASK), 0, last); if (verbose_mode) fprintf(stderr, "Entering directory: %s", path); - (void) atomicio(write, remout, path, strlen(path)); + (void) atomicio(vwrite, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; @@ -625,15 +643,67 @@ source(1, vect); } (void) closedir(dirp); - (void) atomicio(write, remout, "E\n", 2); + (void) atomicio(vwrite, remout, "E\n", 2); (void) response(); } void -sink(argc, argv) - int argc; - char *argv[]; +bwlimit(int amount) { + static struct timeval bwstart, bwend; + static int lamt, thresh = 16384; + u_int64_t wait; + struct timespec ts, rm; + + if (!timerisset(&bwstart)) { + gettimeofday(&bwstart, NULL); + return; + } + + lamt += amount; + if (lamt < thresh) + return; + + gettimeofday(&bwend, NULL); + timersub(&bwend, &bwstart, &bwend); + if (!timerisset(&bwend)) + return; + + lamt *= 8; + wait = (double)1000000L * lamt / limit; + + bwstart.tv_sec = wait / 1000000L; + bwstart.tv_usec = wait % 1000000L; + + if (timercmp(&bwstart, &bwend, >)) { + timersub(&bwstart, &bwend, &bwend); + + /* Adjust the wait time */ + if (bwend.tv_sec) { + thresh /= 2; + if (thresh < 2048) + thresh = 2048; + } else if (bwend.tv_usec < 100) { + thresh *= 2; + if (thresh > 32768) + thresh = 32768; + } + + TIMEVAL_TO_TIMESPEC(&bwend, &ts); + while (nanosleep(&ts, &rm) == -1) { + if (errno != EINTR) + break; + ts = rm; + } + } + + lamt = 0; + gettimeofday(&bwstart, NULL); +} + +void +sink(int argc, char **argv) +{ static BUF buffer; struct stat stb; enum { @@ -642,7 +712,7 @@ BUF *bp; off_t i, j; int amt, count, exists, first, mask, mode, ofd, omode; - off_t size; + off_t size, statbytes; int setimes, targisdir, wrerrno = 0; char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; struct timeval tv[2]; @@ -663,7 +733,7 @@ if (targetshouldbedirectory) verifydir(targ); - (void) atomicio(write, remout, "", 1); + (void) atomicio(vwrite, remout, "", 1); if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) targisdir = 1; for (first = 1;; first = 0) { @@ -681,7 +751,7 @@ if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) - (void) atomicio(write, STDERR_FILENO, + (void) atomicio(vwrite, STDERR_FILENO, buf + 1, strlen(buf + 1)); if (buf[0] == '\02') exit(1); @@ -689,7 +759,7 @@ continue; } if (buf[0] == 'E') { - (void) atomicio(write, remout, "", 1); + (void) atomicio(vwrite, remout, "", 1); return; } if (ch == '\n') @@ -711,7 +781,7 @@ atime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != '\0') SCREWUP("atime.usec not delimited"); - (void) atomicio(write, remout, "", 1); + (void) atomicio(vwrite, remout, "", 1); continue; } if (*cp != 'C' && *cp != 'D') { @@ -796,7 +866,7 @@ bad: run_err("%s: %s", np, strerror(errno)); continue; } - (void) atomicio(write, remout, "", 1); + (void) atomicio(vwrite, remout, "", 1); if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { (void) close(ofd); continue; @@ -804,11 +874,9 @@ cp = bp->buf; wrerr = NO; - if (showprogress) { - totalbytes = size; - progressmeter(-1); - } statbytes = 0; + if (showprogress) + start_progress_meter(curfile, size, &statbytes); for (count = i = 0; i < size; i += 4096) { amt = 4096; if (i + amt > size) @@ -828,10 +896,14 @@ cp += j; statbytes += j; } while (amt > 0); + + if (limit) + bwlimit(4096); + if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ if (wrerr == NO) { - j = atomicio(write, ofd, bp->buf, count); + j = atomicio(vwrite, ofd, bp->buf, count); if (j != count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; @@ -842,13 +914,13 @@ } } if (showprogress) - progressmeter(1); + stop_progress_meter(); if (count != 0 && wrerr == NO && - (j = atomicio(write, ofd, bp->buf, count)) != count) { + (j = atomicio(vwrite, ofd, bp->buf, count)) != count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; } - if (ftruncate(ofd, size)) { + if (wrerr == NO && ftruncate(ofd, size) != 0) { run_err("%s: truncate: %s", np, strerror(errno)); wrerr = DISPLAYED; } @@ -881,7 +953,7 @@ run_err("%s: %s", np, strerror(wrerrno)); break; case NO: - (void) atomicio(write, remout, "", 1); + (void) atomicio(vwrite, remout, "", 1); break; case DISPLAYED: break; @@ -916,7 +988,7 @@ } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); if (!iamremote) - (void) atomicio(write, STDERR_FILENO, rbuf, cp - rbuf); + (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); ++errs; if (resp == 1) return (-1); @@ -929,8 +1001,8 @@ usage(void) { (void) fprintf(stderr, - "usage: scp [-pqrvBC46] [-F config] [-S program] [-P port]\n" - " [-c cipher] [-i identity] [-o option]\n" + "usage: scp [-pqrvBC1246] [-F config] [-S program] [-P port]\n" + " [-c cipher] [-i identity] [-l limit] [-o option]\n" " [[user@]host1:]file1 [...] [[user@]host2:]file2\n"); exit(1); } @@ -961,8 +1033,7 @@ } void -verifydir(cp) - char *cp; +verifydir(char *cp) { struct stat stb; @@ -976,8 +1047,7 @@ } int -okname(cp0) - char *cp0; +okname(char *cp0) { int c; char *cp; @@ -987,9 +1057,18 @@ c = (int)*cp; if (c & 0200) goto bad; - if (!isalpha(c) && !isdigit(c) && - c != '_' && c != '-' && c != '.' && c != '+') - goto bad; + if (!isalpha(c) && !isdigit(c)) { + switch (c) { + case '\'': + case '"': + case '`': + case ' ': + case '#': + goto bad; + default: + break; + } + } } while (*++cp); return (1); @@ -998,9 +1077,7 @@ } BUF * -allocbuf(bp, fd, blksize) - BUF *bp; - int fd, blksize; +allocbuf(BUF *bp, int fd, int blksize) { size_t size; struct stat stb; @@ -1009,11 +1086,9 @@ run_err("fstat: %s", strerror(errno)); return (0); } - if (stb.st_blksize == 0) + size = roundup(stb.st_blksize, blksize); + if (size == 0) size = blksize; - else - size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % - stb.st_blksize; if (bp->cnt >= size) return (bp); if (bp->buf == NULL) @@ -1026,8 +1101,7 @@ } void -lostconn(signo) - int signo; +lostconn(int signo) { if (!iamremote) write(STDERR_FILENO, "lost connection\n", 16); @@ -1035,145 +1109,4 @@ _exit(1); else exit(1); -} - -static void -updateprogressmeter(int ignore) -{ - int save_errno = errno; - - progressmeter(0); - signal(SIGALRM, updateprogressmeter); - alarm(PROGRESSTIME); - errno = save_errno; -} - -static int -foregroundproc(void) -{ - static pid_t pgrp = -1; - int ctty_pgrp; - - if (pgrp == -1) - pgrp = getpgrp(); - - return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && - ctty_pgrp == pgrp)); -} - -void -progressmeter(int flag) -{ - static const char prefixes[] = " KMGTP"; - static struct timeval lastupdate; - static off_t lastsize; - struct timeval now, td, wait; - off_t cursize, abbrevsize; - double elapsed; - int ratio, barlength, i, remaining; - char buf[512]; - - if (flag == -1) { - (void) gettimeofday(&start, (struct timezone *) 0); - lastupdate = start; - lastsize = 0; - } - if (foregroundproc() == 0) - return; - - (void) gettimeofday(&now, (struct timezone *) 0); - cursize = statbytes; - if (totalbytes != 0) { - ratio = 100.0 * cursize / totalbytes; - ratio = MAX(ratio, 0); - ratio = MIN(ratio, 100); - } else - ratio = 100; - - snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); - - barlength = getttywidth() - 51; - if (barlength > 0) { - i = barlength * ratio / 100; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "|%.*s%*s|", i, - "*******************************************************" - "*******************************************************" - "*******************************************************" - "*******************************************************" - "*******************************************************" - "*******************************************************" - "*******************************************************", - barlength - i, ""); - } - i = 0; - abbrevsize = cursize; - while (abbrevsize >= 100000 && i < sizeof(prefixes)) { - i++; - abbrevsize >>= 10; - } - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5llu %c%c ", - (unsigned long long) abbrevsize, prefixes[i], - prefixes[i] == ' ' ? ' ' : 'B'); - - timersub(&now, &lastupdate, &wait); - if (cursize > lastsize) { - lastupdate = now; - lastsize = cursize; - if (wait.tv_sec >= STALLTIME) { - start.tv_sec += wait.tv_sec; - start.tv_usec += wait.tv_usec; - } - wait.tv_sec = 0; - } - timersub(&now, &start, &td); - elapsed = td.tv_sec + (td.tv_usec / 1000000.0); - - if (flag != 1 && - (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes)) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " --:-- ETA"); - } else if (wait.tv_sec >= STALLTIME) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " - stalled -"); - } else { - if (flag != 1) - remaining = (int)(totalbytes / (statbytes / elapsed) - - elapsed); - else - remaining = elapsed; - - i = remaining / 3600; - if (i) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "%2d:", i); - else - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " "); - i = remaining % 3600; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "%02d:%02d%s", i / 60, i % 60, - (flag != 1) ? " ETA" : " "); - } - atomicio(write, fileno(stdout), buf, strlen(buf)); - - if (flag == -1) { - signal(SIGALRM, updateprogressmeter); - alarm(PROGRESSTIME); - } else if (flag == 1) { - alarm(0); - atomicio(write, fileno(stdout), "\n", 1); - statbytes = 0; - } -} - -int -getttywidth(void) -{ - struct winsize winsize; - - if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) - return (winsize.ws_col ? winsize.ws_col : 80); - else - return (80); }