version 1.88, 2002/04/06 18:24:09 |
version 1.88.2.2, 2003/04/03 22:35:17 |
|
|
#include "pathnames.h" |
#include "pathnames.h" |
#include "log.h" |
#include "log.h" |
#include "misc.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); |
int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc); |
|
|
|
void bwlimit(int); |
|
|
/* Struct for addargs */ |
/* Struct for addargs */ |
arglist args; |
arglist args; |
|
|
/* Time a transfer started. */ |
/* Bandwidth limit */ |
static struct timeval start; |
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. */ |
/* Name of current file being transferred. */ |
char *curfile; |
char *curfile; |
|
|
|
|
/* This is the program to execute for the secured connection. ("ssh" or -S) */ |
/* This is the program to execute for the secured connection. ("ssh" or -S) */ |
char *ssh_program = _PATH_SSH_PROGRAM; |
char *ssh_program = _PATH_SSH_PROGRAM; |
|
|
|
/* This is used to store the pid of ssh_program */ |
|
pid_t do_cmd_pid; |
|
|
/* |
/* |
* This function executes the given command as the specified user on the |
* This function executes the given command as the specified user on the |
* given host. This returns < 0 if execution fails, and >= 0 otherwise. This |
* given host. This returns < 0 if execution fails, and >= 0 otherwise. This |
|
|
close(reserved[1]); |
close(reserved[1]); |
|
|
/* For a child to execute the command on the remote host using ssh. */ |
/* For a child to execute the command on the remote host using ssh. */ |
if (fork() == 0) { |
do_cmd_pid = fork(); |
|
if (do_cmd_pid == 0) { |
/* Child. */ |
/* Child. */ |
close(pin[1]); |
close(pin[1]); |
close(pout[0]); |
close(pout[0]); |
|
|
execvp(ssh_program, args.list); |
execvp(ssh_program, args.list); |
perror(ssh_program); |
perror(ssh_program); |
exit(1); |
exit(1); |
|
} else if (do_cmd_pid == -1) { |
|
fatal("fork: %s", strerror(errno)); |
} |
} |
/* Parent. Close the other side, and return the local side. */ |
/* Parent. Close the other side, and return the local side. */ |
close(pin[0]); |
close(pin[0]); |
|
|
int argc; |
int argc; |
char *argv[]; |
char *argv[]; |
{ |
{ |
int ch, fflag, tflag; |
int ch, fflag, tflag, status; |
char *targ; |
double speed; |
|
char *targ, *endp; |
extern char *optarg; |
extern char *optarg; |
extern int optind; |
extern int optind; |
|
|
args.list = NULL; |
args.list = NULL; |
addargs(&args, "ssh"); /* overwritten with ssh_program */ |
addargs(&args, "ssh"); /* overwritten with ssh_program */ |
addargs(&args, "-x"); |
addargs(&args, "-x"); |
addargs(&args, "-oForwardAgent no"); |
addargs(&args, "-oForwardAgent no"); |
addargs(&args, "-oFallBackToRsh no"); |
|
addargs(&args, "-oClearAllForwardings yes"); |
addargs(&args, "-oClearAllForwardings yes"); |
|
|
fflag = tflag = 0; |
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) { |
switch (ch) { |
/* User-visible flags. */ |
/* User-visible flags. */ |
|
case '1': |
|
case '2': |
case '4': |
case '4': |
case '6': |
case '6': |
case 'C': |
case 'C': |
|
|
case 'B': |
case 'B': |
addargs(&args, "-oBatchmode yes"); |
addargs(&args, "-oBatchmode yes"); |
break; |
break; |
|
case 'l': |
|
speed = strtod(optarg, &endp); |
|
if (speed <= 0 || *endp != '\0') |
|
usage(); |
|
limit = speed * 1024; |
|
break; |
case 'p': |
case 'p': |
pflag = 1; |
pflag = 1; |
break; |
break; |
|
|
targetshouldbedirectory = 1; |
targetshouldbedirectory = 1; |
|
|
remin = remout = -1; |
remin = remout = -1; |
|
do_cmd_pid = -1; |
/* Command to be executed on remote system using "ssh". */ |
/* Command to be executed on remote system using "ssh". */ |
(void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", |
(void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", |
verbose_mode ? " -v" : "", |
verbose_mode ? " -v" : "", |
|
|
if (targetshouldbedirectory) |
if (targetshouldbedirectory) |
verifydir(argv[argc - 1]); |
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); |
exit(errs != 0); |
} |
} |
|
|
|
|
if (*targ == 0) |
if (*targ == 0) |
targ = "."; |
targ = "."; |
|
|
if ((thost = strchr(argv[argc - 1], '@'))) { |
if ((thost = strrchr(argv[argc - 1], '@'))) { |
/* user@host */ |
/* user@host */ |
*thost++ = 0; |
*thost++ = 0; |
tuser = argv[argc - 1]; |
tuser = argv[argc - 1]; |
if (*tuser == '\0') |
if (*tuser == '\0') |
tuser = NULL; |
tuser = NULL; |
else if (!okname(tuser)) |
|
exit(1); |
|
} else { |
} else { |
thost = argv[argc - 1]; |
thost = argv[argc - 1]; |
tuser = NULL; |
tuser = NULL; |
|
|
src = colon(argv[i]); |
src = colon(argv[i]); |
if (src) { /* remote to remote */ |
if (src) { /* remote to remote */ |
static char *ssh_options = |
static char *ssh_options = |
"-x -o'FallBackToRsh no' " |
"-x -o'ClearAllForwardings yes'"; |
"-o'ClearAllForwardings yes'"; |
|
*src++ = 0; |
*src++ = 0; |
if (*src == 0) |
if (*src == 0) |
src = "."; |
src = "."; |
host = strchr(argv[i], '@'); |
host = strrchr(argv[i], '@'); |
len = strlen(ssh_program) + strlen(argv[i]) + |
len = strlen(ssh_program) + strlen(argv[i]) + |
strlen(src) + (tuser ? strlen(tuser) : 0) + |
strlen(src) + (tuser ? strlen(tuser) : 0) + |
strlen(thost) + strlen(targ) + |
strlen(thost) + strlen(targ) + |
|
|
suser = argv[i]; |
suser = argv[i]; |
if (*suser == '\0') |
if (*suser == '\0') |
suser = pwd->pw_name; |
suser = pwd->pw_name; |
else if (!okname(suser)) |
else if (!okname(suser)) { |
|
xfree(bp); |
continue; |
continue; |
|
} |
|
if (tuser && !okname(tuser)) { |
|
xfree(bp); |
|
continue; |
|
} |
snprintf(bp, len, |
snprintf(bp, len, |
"%s%s %s -n " |
"%s%s %s -n " |
"-l %s %s %s %s '%s%s%s:%s'", |
"-l %s %s %s %s '%s%s%s:%s'", |
|
|
*src++ = 0; |
*src++ = 0; |
if (*src == 0) |
if (*src == 0) |
src = "."; |
src = "."; |
if ((host = strchr(argv[i], '@')) == NULL) { |
if ((host = strrchr(argv[i], '@')) == NULL) { |
host = argv[i]; |
host = argv[i]; |
suser = NULL; |
suser = NULL; |
} else { |
} else { |
|
|
suser = argv[i]; |
suser = argv[i]; |
if (*suser == '\0') |
if (*suser == '\0') |
suser = pwd->pw_name; |
suser = pwd->pw_name; |
else if (!okname(suser)) |
|
continue; |
|
} |
} |
host = cleanhostname(host); |
host = cleanhostname(host); |
len = strlen(src) + CMDNEEDS + 20; |
len = strlen(src) + CMDNEEDS + 20; |
|
|
struct stat stb; |
struct stat stb; |
static BUF buffer; |
static BUF buffer; |
BUF *bp; |
BUF *bp; |
off_t i, amt, result; |
off_t i, amt, result, statbytes; |
int fd, haderr, indx; |
int fd, haderr, indx; |
char *last, *name, buf[2048]; |
char *last, *name, buf[2048]; |
int len; |
int len; |
|
|
(long long)stb.st_size, last); |
(long long)stb.st_size, last); |
if (verbose_mode) { |
if (verbose_mode) { |
fprintf(stderr, "Sending file modes: %s", buf); |
fprintf(stderr, "Sending file modes: %s", buf); |
fflush(stderr); |
|
} |
} |
(void) atomicio(write, remout, buf, strlen(buf)); |
(void) atomicio(write, remout, buf, strlen(buf)); |
if (response() < 0) |
if (response() < 0) |
|
|
next: (void) close(fd); |
next: (void) close(fd); |
continue; |
continue; |
} |
} |
if (showprogress) { |
if (showprogress) |
totalbytes = stb.st_size; |
start_progress_meter(curfile, stb.st_size, &statbytes); |
progressmeter(-1); |
|
} |
|
/* Keep writing after an error so that we stay sync'd up. */ |
/* Keep writing after an error so that we stay sync'd up. */ |
for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { |
for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { |
amt = bp->cnt; |
amt = bp->cnt; |
|
|
haderr = result >= 0 ? EIO : errno; |
haderr = result >= 0 ? EIO : errno; |
statbytes += result; |
statbytes += result; |
} |
} |
|
if (limit) |
|
bwlimit(amt); |
} |
} |
if (showprogress) |
if (showprogress) |
progressmeter(1); |
stop_progress_meter(); |
|
|
if (close(fd) < 0 && !haderr) |
if (close(fd) < 0 && !haderr) |
haderr = errno; |
haderr = errno; |
|
|
} |
} |
|
|
void |
void |
|
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(argc, argv) |
sink(argc, argv) |
int argc; |
int argc; |
char *argv[]; |
char *argv[]; |
|
|
BUF *bp; |
BUF *bp; |
off_t i, j; |
off_t i, j; |
int amt, count, exists, first, mask, mode, ofd, omode; |
int amt, count, exists, first, mask, mode, ofd, omode; |
off_t size; |
off_t size, statbytes; |
int setimes, targisdir, wrerrno = 0; |
int setimes, targisdir, wrerrno = 0; |
char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; |
char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; |
struct timeval tv[2]; |
struct timeval tv[2]; |
|
|
cp = bp->buf; |
cp = bp->buf; |
wrerr = NO; |
wrerr = NO; |
|
|
if (showprogress) { |
|
totalbytes = size; |
|
progressmeter(-1); |
|
} |
|
statbytes = 0; |
statbytes = 0; |
|
if (showprogress) |
|
start_progress_meter(curfile, size, &statbytes); |
for (count = i = 0; i < size; i += 4096) { |
for (count = i = 0; i < size; i += 4096) { |
amt = 4096; |
amt = 4096; |
if (i + amt > size) |
if (i + amt > size) |
|
|
cp += j; |
cp += j; |
statbytes += j; |
statbytes += j; |
} while (amt > 0); |
} while (amt > 0); |
|
|
|
if (limit) |
|
bwlimit(4096); |
|
|
if (count == bp->cnt) { |
if (count == bp->cnt) { |
/* Keep reading so we stay sync'd up. */ |
/* Keep reading so we stay sync'd up. */ |
if (wrerr == NO) { |
if (wrerr == NO) { |
|
|
} |
} |
} |
} |
if (showprogress) |
if (showprogress) |
progressmeter(1); |
stop_progress_meter(); |
if (count != 0 && wrerr == NO && |
if (count != 0 && wrerr == NO && |
(j = atomicio(write, ofd, bp->buf, count)) != count) { |
(j = atomicio(write, ofd, bp->buf, count)) != count) { |
wrerr = YES; |
wrerr = YES; |
wrerrno = j >= 0 ? EIO : errno; |
wrerrno = j >= 0 ? EIO : errno; |
} |
} |
if (ftruncate(ofd, size)) { |
if (wrerr == NO && ftruncate(ofd, size) != 0) { |
run_err("%s: truncate: %s", np, strerror(errno)); |
run_err("%s: truncate: %s", np, strerror(errno)); |
wrerr = DISPLAYED; |
wrerr = DISPLAYED; |
} |
} |
|
|
usage(void) |
usage(void) |
{ |
{ |
(void) fprintf(stderr, |
(void) fprintf(stderr, |
"usage: scp [-pqrvBC46] [-F config] [-S ssh] [-P port] [-c cipher] [-i identity]\n" |
"usage: scp [-pqrvBC1246] [-F config] [-S program] [-P port]\n" |
" [-o option] f1 f2\n" |
" [-c cipher] [-i identity] [-l limit] [-o option]\n" |
" or: scp [options] f1 ... fn directory\n"); |
" [[user@]host1:]file1 [...] [[user@]host2:]file2\n"); |
exit(1); |
exit(1); |
} |
} |
|
|
|
|
c = (int)*cp; |
c = (int)*cp; |
if (c & 0200) |
if (c & 0200) |
goto bad; |
goto bad; |
if (!isalpha(c) && !isdigit(c) && |
if (!isalpha(c) && !isdigit(c)) { |
c != '_' && c != '-' && c != '.' && c != '+') |
switch (c) { |
goto bad; |
case '\'': |
|
case '"': |
|
case '`': |
|
case ' ': |
|
case '#': |
|
goto bad; |
|
default: |
|
break; |
|
} |
|
} |
} while (*++cp); |
} while (*++cp); |
return (1); |
return (1); |
|
|
|
|
run_err("fstat: %s", strerror(errno)); |
run_err("fstat: %s", strerror(errno)); |
return (0); |
return (0); |
} |
} |
if (stb.st_blksize == 0) |
size = roundup(stb.st_blksize, blksize); |
|
if (size == 0) |
size = blksize; |
size = blksize; |
else |
|
size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % |
|
stb.st_blksize; |
|
if (bp->cnt >= size) |
if (bp->cnt >= size) |
return (bp); |
return (bp); |
if (bp->buf == NULL) |
if (bp->buf == NULL) |
|
|
_exit(1); |
_exit(1); |
else |
else |
exit(1); |
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); |
|
} |
} |