diff options
Diffstat (limited to 'scp.c')
-rw-r--r-- | scp.c | 539 |
1 files changed, 476 insertions, 63 deletions
@@ -1,4 +1,4 @@ -/* $OpenBSD: scp.c,v 1.210 2020/05/06 20:57:38 djm Exp $ */ +/* $OpenBSD: scp.c,v 1.239 2021/09/20 06:53:56 djm Exp $ */ /* * scp - secure remote copy. This is basically patched BSD rcp which * uses ssh to do the data transfer (instead of using rcmd). @@ -97,6 +97,14 @@ #ifdef HAVE_FNMATCH_H #include <fnmatch.h> #endif +#ifdef USE_SYSTEM_GLOB +# include <glob.h> +#else +# include "openbsd-compat/glob.h" +#endif +#ifdef HAVE_LIBGEN_H +#include <libgen.h> +#endif #include <limits.h> #include <locale.h> #include <pwd.h> @@ -123,12 +131,15 @@ #include "progressmeter.h" #include "utf8.h" +#include "sftp-common.h" +#include "sftp-client.h" + extern char *__progname; #define COPY_BUFLEN 16384 -int do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout); -int do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout); +int do_cmd(char *, char *, char *, int, int, char *, int *, int *, pid_t *); +int do_cmd2(char *, char *, int, char *, int, int); /* Struct for addargs */ arglist args; @@ -143,6 +154,7 @@ char *curfile; /* This is set to non-zero to enable verbose mode. */ int verbose_mode = 0; +LogLevel log_level = SYSLOG_LEVEL_INFO; /* This is set to zero if the progressmeter is not desired. */ int showprogress = 1; @@ -151,7 +163,7 @@ int showprogress = 1; * This is set to non-zero if remote-remote copy should be piped * through this process. */ -int throughlocal = 0; +int throughlocal = 1; /* Non-standard port to use for the ssh connection or -1. */ int sshport = -1; @@ -161,6 +173,13 @@ char *ssh_program = _PATH_SSH_PROGRAM; /* This is used to store the pid of ssh_program */ pid_t do_cmd_pid = -1; +pid_t do_cmd_pid2 = -1; + +/* Needed for sftp */ +volatile sig_atomic_t interrupted = 0; + +int remote_glob(struct sftp_conn *, const char *, int, + int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ static void killchild(int signo) @@ -169,6 +188,10 @@ killchild(int signo) kill(do_cmd_pid, signo ? signo : SIGTERM); waitpid(do_cmd_pid, NULL, 0); } + if (do_cmd_pid2 > 1) { + kill(do_cmd_pid2, signo ? signo : SIGTERM); + waitpid(do_cmd_pid2, NULL, 0); + } if (signo) _exit(1); @@ -176,19 +199,26 @@ killchild(int signo) } static void -suspchild(int signo) +suspone(int pid, int signo) { int status; - if (do_cmd_pid > 1) { - kill(do_cmd_pid, signo); - while (waitpid(do_cmd_pid, &status, WUNTRACED) == -1 && + if (pid > 1) { + kill(pid, signo); + while (waitpid(pid, &status, WUNTRACED) == -1 && errno == EINTR) ; - kill(getpid(), SIGSTOP); } } +static void +suspchild(int signo) +{ + suspone(do_cmd_pid, signo); + suspone(do_cmd_pid2, signo); + kill(getpid(), SIGSTOP); +} + static int do_local_cmd(arglist *a) { @@ -238,14 +268,15 @@ do_local_cmd(arglist *a) */ int -do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) +do_cmd(char *program, char *host, char *remuser, int port, int subsystem, + char *cmd, int *fdin, int *fdout, pid_t *pid) { int pin[2], pout[2], reserved[2]; if (verbose_mode) fmprintf(stderr, "Executing: program %s host %s, user %s, command %s\n", - ssh_program, host, + program, host, remuser ? remuser : "(unspecified)", cmd); if (port == -1) @@ -273,8 +304,8 @@ do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) ssh_signal(SIGTTOU, suspchild); /* Fork a child to execute the command on the remote host using ssh. */ - do_cmd_pid = fork(); - if (do_cmd_pid == 0) { + *pid = fork(); + if (*pid == 0) { /* Child. */ close(pin[1]); close(pout[0]); @@ -283,7 +314,7 @@ do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) close(pin[0]); close(pout[1]); - replacearg(&args, 0, "%s", ssh_program); + replacearg(&args, 0, "%s", program); if (port != -1) { addargs(&args, "-p"); addargs(&args, "%d", port); @@ -292,14 +323,16 @@ do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) addargs(&args, "-l"); addargs(&args, "%s", remuser); } + if (subsystem) + addargs(&args, "-s"); addargs(&args, "--"); addargs(&args, "%s", host); addargs(&args, "%s", cmd); - execvp(ssh_program, args.list); - perror(ssh_program); + execvp(program, args.list); + perror(program); exit(1); - } else if (do_cmd_pid == -1) { + } else if (*pid == -1) { fatal("fork: %s", strerror(errno)); } /* Parent. Close the other side, and return the local side. */ @@ -319,10 +352,11 @@ do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) * This way the input and output of two commands can be connected. */ int -do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout) +do_cmd2(char *host, char *remuser, int port, char *cmd, + int fdin, int fdout) { - pid_t pid; int status; + pid_t pid; if (verbose_mode) fmprintf(stderr, @@ -373,34 +407,49 @@ typedef struct { BUF *allocbuf(BUF *, int, int); void lostconn(int); int okname(char *); -void run_err(const char *,...); -int note_err(const char *,...); +void run_err(const char *,...) + __attribute__((__format__ (printf, 1, 2))) + __attribute__((__nonnull__ (1))); +int note_err(const char *,...) + __attribute__((__format__ (printf, 1, 2))); void verifydir(char *); struct passwd *pwd; uid_t userid; -int errs, remin, remout; +int errs, remin, remout, remin2, remout2; int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory; #define CMDNEEDS 64 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ +enum scp_mode_e { + MODE_SCP, + MODE_SFTP +}; + int response(void); void rsource(char *, struct stat *); void sink(int, char *[], const char *); void source(int, char *[]); -void tolocal(int, char *[]); -void toremote(int, char *[]); +void tolocal(int, char *[], enum scp_mode_e, char *sftp_direct); +void toremote(int, char *[], enum scp_mode_e, char *sftp_direct); void usage(void); +void source_sftp(int, char *, char *, struct sftp_conn *); +void sink_sftp(int, char *, const char *, struct sftp_conn *); +void throughlocal_sftp(struct sftp_conn *, struct sftp_conn *, + char *, char *); + int main(int argc, char **argv) { int ch, fflag, tflag, status, n; - char **newargv; + char **newargv, *argv0; const char *errstr; extern char *optarg; extern int optind; + enum scp_mode_e mode = MODE_SCP; + char *sftp_direct = NULL; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); @@ -410,6 +459,7 @@ main(int argc, char **argv) msetlocale(); /* Copy argv, because we modify it */ + argv0 = argv[0]; newargv = xcalloc(MAXIMUM(argc + 1, 1), sizeof(*newargv)); for (n = 0; n < argc; n++) newargv[n] = xstrdup(argv[n]); @@ -417,12 +467,13 @@ main(int argc, char **argv) __progname = ssh_get_progname(argv[0]); + log_init(argv0, log_level, SYSLOG_FACILITY_USER, 2); + memset(&args, '\0', sizeof(args)); memset(&remote_remote_args, '\0', sizeof(remote_remote_args)); args.list = remote_remote_args.list = NULL; addargs(&args, "%s", ssh_program); addargs(&args, "-x"); - addargs(&args, "-oForwardAgent=no"); addargs(&args, "-oPermitLocalCommand=no"); addargs(&args, "-oClearAllForwardings=yes"); addargs(&args, "-oRemoteCommand=none"); @@ -430,7 +481,7 @@ main(int argc, char **argv) fflag = Tflag = tflag = 0; while ((ch = getopt(argc, argv, - "dfl:prtTvBCc:i:P:q12346S:o:F:J:")) != -1) { + "12346ABCTdfOpqRrstvD:F:J:M:P:S:c:i:l:o:")) != -1) { switch (ch) { /* User-visible flags. */ case '1': @@ -439,15 +490,22 @@ main(int argc, char **argv) case '2': /* Ignored */ break; + case 'A': case '4': case '6': case 'C': addargs(&args, "-%c", ch); addargs(&remote_remote_args, "-%c", ch); break; + case 'D': + sftp_direct = optarg; + break; case '3': throughlocal = 1; break; + case 'R': + throughlocal = 0; + break; case 'o': case 'c': case 'i': @@ -458,6 +516,12 @@ main(int argc, char **argv) addargs(&args, "-%c", ch); addargs(&args, "%s", optarg); break; + case 'O': + mode = MODE_SCP; + break; + case 's': + mode = MODE_SFTP; + break; case 'P': sshport = a2port(optarg); if (sshport <= 0) @@ -487,6 +551,10 @@ main(int argc, char **argv) case 'v': addargs(&args, "-v"); addargs(&remote_remote_args, "-v"); + if (verbose_mode == 0) + log_level = SYSLOG_LEVEL_DEBUG1; + else if (log_level < SYSLOG_LEVEL_DEBUG3) + log_level++; verbose_mode = 1; break; case 'q': @@ -520,6 +588,14 @@ main(int argc, char **argv) argc -= optind; argv += optind; + log_init(argv0, log_level, SYSLOG_FACILITY_USER, 2); + + /* Do this last because we want the user to be able to override it */ + addargs(&args, "-oForwardAgent=no"); + + if (iamremote) + mode = MODE_SCP; + if ((pwd = getpwuid(userid = getuid())) == NULL) fatal("unknown user %u", (u_int) userid); @@ -566,17 +642,17 @@ main(int argc, char **argv) (void) ssh_signal(SIGPIPE, lostconn); if (colon(argv[argc - 1])) /* Dest is remote host. */ - toremote(argc, argv); + toremote(argc, argv, mode, sftp_direct); else { if (targetshouldbedirectory) verifydir(argv[argc - 1]); - tolocal(argc, argv); /* Dest is local host. */ + tolocal(argc, argv, mode, sftp_direct); /* Dest is local host. */ } /* * Finally check the exit status of the ssh process, if one was forked * and no error has occurred yet */ - if (do_cmd_pid != -1 && errs == 0) { + if (do_cmd_pid != -1 && (mode == MODE_SFTP || errs == 0)) { if (remin != -1) (void) close(remin); if (remout != -1) @@ -624,7 +700,7 @@ do_times(int fd, int verb, const struct stat *sb) static int parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp, - char **pathp) + char **pathp) { int r; @@ -842,7 +918,7 @@ brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp) goto fail; } if (invalid) - fatal("%s: invalid brace pattern \"%s\"", __func__, cp); + fatal_f("invalid brace pattern \"%s\"", cp); if (expanded) { /* * Current entry expanded to new entries on the @@ -881,14 +957,34 @@ brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp) return ret; } +static struct sftp_conn * +do_sftp_connect(char *host, char *user, int port, char *sftp_direct, + int *reminp, int *remoutp, int *pidp) +{ + if (sftp_direct == NULL) { + if (do_cmd(ssh_program, host, user, port, 1, "sftp", + reminp, remoutp, pidp) < 0) + return NULL; + + } else { + args.list = NULL; + addargs(&args, "sftp-server"); + if (do_cmd(sftp_direct, host, NULL, -1, 0, "sftp", + reminp, remoutp, pidp) < 0) + return NULL; + } + return do_init(*reminp, *remoutp, 32768, 64, limit_kbps); +} + void -toremote(int argc, char **argv) +toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct) { char *suser = NULL, *host = NULL, *src = NULL; char *bp, *tuser, *thost, *targ; int sport = -1, tport = -1; + struct sftp_conn *conn = NULL, *conn2 = NULL; arglist alist; - int i, r; + int i, r, status; u_int j; memset(&alist, '\0', sizeof(alist)); @@ -909,10 +1005,6 @@ toremote(int argc, char **argv) goto out; } } - if (tuser != NULL && !okname(tuser)) { - ++errs; - goto out; - } /* Parse source files */ for (i = 0; i < argc - 1; i++) { @@ -933,24 +1025,77 @@ toremote(int argc, char **argv) continue; } if (host && throughlocal) { /* extended remote to remote */ - xasprintf(&bp, "%s -f %s%s", cmd, - *src == '-' ? "-- " : "", src); - if (do_cmd(host, suser, sport, bp, &remin, &remout) < 0) - exit(1); - free(bp); - xasprintf(&bp, "%s -t %s%s", cmd, - *targ == '-' ? "-- " : "", targ); - if (do_cmd2(thost, tuser, tport, bp, remin, remout) < 0) - exit(1); - free(bp); - (void) close(remin); - (void) close(remout); - remin = remout = -1; + if (mode == MODE_SFTP) { + if (remin == -1) { + /* Connect to dest now */ + conn = do_sftp_connect(thost, tuser, + tport, sftp_direct, + &remin, &remout, &do_cmd_pid); + if (conn == NULL) { + fatal("Unable to open " + "destination connection"); + } + debug3_f("origin in %d out %d pid %ld", + remin, remout, (long)do_cmd_pid); + } + /* + * XXX remember suser/host/sport and only + * reconnect if they change between arguments. + * would save reconnections for cases like + * scp -3 hosta:/foo hosta:/bar hostb: + */ + /* Connect to origin now */ + conn2 = do_sftp_connect(host, suser, + sport, sftp_direct, + &remin2, &remout2, &do_cmd_pid2); + if (conn2 == NULL) { + fatal("Unable to open " + "source connection"); + } + debug3_f("destination in %d out %d pid %ld", + remin2, remout2, (long)do_cmd_pid2); + throughlocal_sftp(conn2, conn, src, targ); + (void) close(remin2); + (void) close(remout2); + remin2 = remout2 = -1; + if (waitpid(do_cmd_pid2, &status, 0) == -1) + ++errs; + else if (!WIFEXITED(status) || + WEXITSTATUS(status) != 0) + ++errs; + do_cmd_pid2 = -1; + continue; + } else { + xasprintf(&bp, "%s -f %s%s", cmd, + *src == '-' ? "-- " : "", src); + if (do_cmd(ssh_program, host, suser, sport, 0, + bp, &remin, &remout, &do_cmd_pid) < 0) + exit(1); + free(bp); + xasprintf(&bp, "%s -t %s%s", cmd, + *targ == '-' ? "-- " : "", targ); + if (do_cmd2(thost, tuser, tport, bp, + remin, remout) < 0) + exit(1); + free(bp); + (void) close(remin); + (void) close(remout); + remin = remout = -1; + } } else if (host) { /* standard remote to remote */ + /* + * Second remote user is passed to first remote side + * via scp command-line. Ensure it contains no obvious + * shell characters. + */ + if (tuser != NULL && !okname(tuser)) { + ++errs; + continue; + } if (tport != -1 && tport != SSH_DEFAULT_PORT) { /* This would require the remote support URIs */ fatal("target port not supported with two " - "remote hosts without the -3 option"); + "remote hosts and the -R option"); } freeargs(&alist); @@ -981,11 +1126,28 @@ toremote(int argc, char **argv) if (do_local_cmd(&alist) != 0) errs = 1; } else { /* local to remote */ + if (mode == MODE_SFTP) { + if (remin == -1) { + /* Connect to remote now */ + conn = do_sftp_connect(thost, tuser, + tport, sftp_direct, + &remin, &remout, &do_cmd_pid); + if (conn == NULL) { + fatal("Unable to open sftp " + "connection"); + } + } + + /* The protocol */ + source_sftp(1, argv[i], targ, conn); + continue; + } + /* SCP */ if (remin == -1) { xasprintf(&bp, "%s -t %s%s", cmd, *targ == '-' ? "-- " : "", targ); - if (do_cmd(thost, tuser, tport, bp, &remin, - &remout) < 0) + if (do_cmd(ssh_program, thost, tuser, tport, 0, + bp, &remin, &remout, &do_cmd_pid) < 0) exit(1); if (response() < 0) exit(1); @@ -995,6 +1157,8 @@ toremote(int argc, char **argv) } } out: + if (mode == MODE_SFTP) + free(conn); free(tuser); free(thost); free(targ); @@ -1004,10 +1168,11 @@ out: } void -tolocal(int argc, char **argv) +tolocal(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct) { char *bp, *host = NULL, *src = NULL, *suser = NULL; arglist alist; + struct sftp_conn *conn = NULL; int i, r, sport = -1; memset(&alist, '\0', sizeof(alist)); @@ -1044,9 +1209,29 @@ tolocal(int argc, char **argv) continue; } /* Remote to local. */ + if (mode == MODE_SFTP) { + conn = do_sftp_connect(host, suser, sport, + sftp_direct, &remin, &remout, &do_cmd_pid); + if (conn == NULL) { + error("sftp connection failed"); + ++errs; + continue; + } + + /* The protocol */ + sink_sftp(1, argv[argc - 1], src, conn); + + free(conn); + (void) close(remin); + (void) close(remout); + remin = remout = -1; + continue; + } + /* SCP */ xasprintf(&bp, "%s -f %s%s", cmd, *src == '-' ? "-- " : "", src); - if (do_cmd(host, suser, sport, bp, &remin, &remout) < 0) { + if (do_cmd(ssh_program, host, suser, sport, 0, bp, + &remin, &remout, &do_cmd_pid) < 0) { free(bp); ++errs; continue; @@ -1061,6 +1246,69 @@ tolocal(int argc, char **argv) free(src); } +/* Prepare remote path, handling ~ by assuming cwd is the homedir */ +static char * +prepare_remote_path(struct sftp_conn *conn, const char *path) +{ + /* Handle ~ prefixed paths */ + if (*path != '~') + return xstrdup(path); + if (*path == '\0' || strcmp(path, "~") == 0) + return xstrdup("."); + if (strncmp(path, "~/", 2) == 0) + return xstrdup(path + 2); + if (can_expand_path(conn)) + return do_expand_path(conn, path); + /* No protocol extension */ + error("server expand-path extension is required " + "for ~user paths in SFTP mode"); + return NULL; +} + +void +source_sftp(int argc, char *src, char *targ, struct sftp_conn *conn) +{ + char *target = NULL, *filename = NULL, *abs_dst = NULL; + int target_is_dir; + + if ((filename = basename(src)) == NULL) + fatal("basename %s: %s", src, strerror(errno)); + + /* + * No need to glob here - the local shell already took care of + * the expansions + */ + if ((target = prepare_remote_path(conn, targ)) == NULL) + cleanup_exit(255); + target_is_dir = remote_is_dir(conn, target); + if (targetshouldbedirectory && !target_is_dir) { + fatal("Target is not a directory, but more files selected " + "for upload"); + } + if (target_is_dir) + abs_dst = path_append(target, filename); + else { + abs_dst = target; + target = NULL; + } + debug3_f("copying local %s to remote %s", src, abs_dst); + + if (local_is_dir(src) && iamrecursive) { + if (upload_dir(conn, src, abs_dst, pflag, + SFTP_PROGRESS_ONLY, 0, 0, 1) != 0) { + error("failed to upload directory %s to %s", + src, abs_dst); + errs = 1; + } + } else if (do_upload(conn, src, abs_dst, pflag, 0, 0) != 0) { + error("failed to upload file %s to %s", src, abs_dst); + errs = 1; + } + + free(abs_dst); + free(target); +} + void source(int argc, char **argv) { @@ -1222,6 +1470,81 @@ rsource(char *name, struct stat *statp) (void) response(); } +void +sink_sftp(int argc, char *dst, const char *src, struct sftp_conn *conn) +{ + char *abs_src = NULL; + char *abs_dst = NULL; + glob_t g; + char *filename, *tmp = NULL; + int i, r, err = 0; + + memset(&g, 0, sizeof(g)); + /* + * Here, we need remote glob as SFTP can not depend on remote shell + * expansions + */ + + if ((abs_src = prepare_remote_path(conn, src)) == NULL) { + err = -1; + goto out; + } + + debug3_f("copying remote %s to local %s", abs_src, dst); + if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) { + if (r == GLOB_NOSPACE) + error("%s: too many glob matches", abs_src); + else + error("%s: %s", abs_src, strerror(ENOENT)); + err = -1; + goto out; + } + + if (g.gl_matchc > 1 && !local_is_dir(dst)) { + error("Multiple files match pattern, but destination " + "\"%s\" is not a directory", dst); + err = -1; + goto out; + } + + for (i = 0; g.gl_pathv[i] && !interrupted; i++) { + tmp = xstrdup(g.gl_pathv[i]); + if ((filename = basename(tmp)) == NULL) { + error("basename %s: %s", tmp, strerror(errno)); + err = -1; + goto out; + } + + if (local_is_dir(dst)) + abs_dst = path_append(dst, filename); + else + abs_dst = xstrdup(dst); + + debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); + if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { + if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, + pflag, SFTP_PROGRESS_ONLY, 0, 0, 1) == -1) + err = -1; + } else { + if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, + pflag, 0, 0) == -1) + err = -1; + } + free(abs_dst); + abs_dst = NULL; + free(tmp); + tmp = NULL; + } + +out: + free(abs_src); + free(tmp); + globfree(&g); + if (err == -1) + errs = 1; +} + + #define TYPE_OVERFLOW(type, val) \ ((sizeof(type) == 4 && (val) > INT32_MAX) || \ (sizeof(type) == 8 && (val) > INT64_MAX) || \ @@ -1273,7 +1596,7 @@ sink(int argc, char **argv, const char *src) * the requested destination file glob. */ if (brace_expand(src, &patterns, &npatterns) != 0) - fatal("%s: could not expand pattern", __func__); + fatal_f("could not expand pattern"); } for (first = 1;; first = 0) { cp = buf; @@ -1417,8 +1740,7 @@ sink(int argc, char **argv, const char *src) if (pflag) (void) chmod(np, mode); } else { - /* Handle copying from a read-only - directory */ + /* Handle copying from a read-only directory */ mod_flag = 1; if (mkdir(np, mode | S_IRWXU) == -1) goto bad; @@ -1520,7 +1842,7 @@ bad: run_err("%s: %s", np, strerror(errno)); } } if (close(ofd) == -1) - note_err(np, "%s: close: %s", np, strerror(errno)); + note_err("%s: close: %s", np, strerror(errno)); (void) response(); if (showprogress) stop_progress_meter(); @@ -1548,6 +1870,79 @@ screwup: exit(1); } +void +throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to, + char *src, char *targ) +{ + char *target = NULL, *filename = NULL, *abs_dst = NULL; + char *abs_src = NULL, *tmp = NULL; + glob_t g; + int i, r, targetisdir, err = 0; + + if ((filename = basename(src)) == NULL) + fatal("basename %s: %s", src, strerror(errno)); + + if ((abs_src = prepare_remote_path(from, src)) == NULL || + (target = prepare_remote_path(to, targ)) == NULL) + cleanup_exit(255); + memset(&g, 0, sizeof(g)); + + targetisdir = remote_is_dir(to, target); + if (!targetisdir && targetshouldbedirectory) { + error("%s: destination is not a directory", target); + err = -1; + goto out; + } + + debug3_f("copying remote %s to remote %s", abs_src, target); + if ((r = remote_glob(from, abs_src, GLOB_MARK, NULL, &g)) != 0) { + if (r == GLOB_NOSPACE) + error("%s: too many glob matches", abs_src); + else + error("%s: %s", abs_src, strerror(ENOENT)); + err = -1; + goto out; + } + + for (i = 0; g.gl_pathv[i] && !interrupted; i++) { + tmp = xstrdup(g.gl_pathv[i]); + if ((filename = basename(tmp)) == NULL) { + error("basename %s: %s", tmp, strerror(errno)); + err = -1; + goto out; + } + + if (targetisdir) + abs_dst = path_append(target, filename); + else + abs_dst = xstrdup(target); + + debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); + if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { + if (crossload_dir(from, to, g.gl_pathv[i], abs_dst, + NULL, pflag, SFTP_PROGRESS_ONLY, 1) == -1) + err = -1; + } else { + if (do_crossload(from, to, g.gl_pathv[i], abs_dst, NULL, + pflag) == -1) + err = -1; + } + free(abs_dst); + abs_dst = NULL; + free(tmp); + tmp = NULL; + } + +out: + free(abs_src); + free(abs_dst); + free(target); + free(tmp); + globfree(&g); + if (err == -1) + errs = 1; +} + int response(void) { @@ -1590,9 +1985,9 @@ void usage(void) { (void) fprintf(stderr, - "usage: scp [-346BCpqrTv] [-c cipher] [-F ssh_config] [-i identity_file]\n" - " [-J destination] [-l limit] [-o ssh_option] [-P port]\n" - " [-S program] source ... target\n"); + "usage: scp [-346ABCOpqRrsTv] [-c cipher] [-D sftp_server_path] [-F ssh_config]\n" + " [-i identity_file] [-J destination] [-l limit]\n" + " [-o ssh_option] [-P port] [-S program] source ... target\n"); exit(1); } @@ -1731,3 +2126,21 @@ lostconn(int signo) else exit(1); } + +void +cleanup_exit(int i) +{ + if (remin > 0) + close(remin); + if (remout > 0) + close(remout); + if (remin2 > 0) + close(remin2); + if (remout2 > 0) + close(remout2); + if (do_cmd_pid > 0) + waitpid(do_cmd_pid, NULL, 0); + if (do_cmd_pid2 > 0) + waitpid(do_cmd_pid2, NULL, 0); + exit(i); +} |