summaryrefslogtreecommitdiff
path: root/sftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'sftp.c')
-rw-r--r--sftp.c482
1 files changed, 321 insertions, 161 deletions
diff --git a/sftp.c b/sftp.c
index cb9b967e..2799e4a1 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp.c,v 1.170 2015/01/20 23:14:00 deraadt Exp $ */
+/* $OpenBSD: sftp.c,v 1.200 2020/04/03 05:53:52 jmc Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
@@ -17,7 +17,6 @@
#include "includes.h"
-#include <sys/param.h> /* MIN MAX */
#include <sys/types.h>
#include <sys/ioctl.h>
#ifdef HAVE_SYS_STAT_H
@@ -49,11 +48,11 @@ typedef void EditLine;
#endif
#include <limits.h>
#include <signal.h>
+#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include <stdarg.h>
#ifdef HAVE_UTIL_H
# include <util.h>
@@ -63,6 +62,7 @@ typedef void EditLine;
#include "log.h"
#include "pathnames.h"
#include "misc.h"
+#include "utf8.h"
#include "sftp.h"
#include "ssherr.h"
@@ -80,7 +80,7 @@ FILE* infile;
int batchmode = 0;
/* PID of ssh transport process */
-static pid_t sshpid = -1;
+static volatile pid_t sshpid = -1;
/* Suppress diagnositic messages */
int quiet = 0;
@@ -105,6 +105,7 @@ volatile sig_atomic_t interrupted = 0;
/* I wish qsort() took a separate ctx for the comparison function...*/
int sort_flag;
+glob_t *sort_glob;
/* Context used for commandline completion */
struct complete_ctx {
@@ -215,15 +216,16 @@ static const struct CMD cmds[] = {
{ NULL, -1, -1 }
};
-int interactive_loop(struct sftp_conn *, char *file1, char *file2);
-
/* ARGSUSED */
static void
killchild(int signo)
{
- if (sshpid > 1) {
- kill(sshpid, SIGTERM);
- waitpid(sshpid, NULL, 0);
+ pid_t pid;
+
+ pid = sshpid;
+ if (pid > 1) {
+ kill(pid, SIGTERM);
+ waitpid(pid, NULL, 0);
}
_exit(1);
@@ -231,6 +233,18 @@ killchild(int signo)
/* ARGSUSED */
static void
+suspchild(int signo)
+{
+ if (sshpid > 1) {
+ kill(sshpid, signo);
+ while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR)
+ continue;
+ }
+ kill(getpid(), SIGSTOP);
+}
+
+/* ARGSUSED */
+static void
cmd_interrupt(int signo)
{
const char msg[] = "\rInterrupt \n";
@@ -241,21 +255,38 @@ cmd_interrupt(int signo)
errno = olderrno;
}
+/*ARGSUSED*/
+static void
+sigchld_handler(int sig)
+{
+ int save_errno = errno;
+ pid_t pid;
+ const char msg[] = "\rConnection closed. \n";
+
+ /* Report if ssh transport process dies. */
+ while ((pid = waitpid(sshpid, NULL, WNOHANG)) == -1 && errno == EINTR)
+ continue;
+ if (pid == sshpid) {
+ (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
+ sshpid = -1;
+ }
+
+ errno = save_errno;
+}
+
static void
help(void)
{
printf("Available commands:\n"
"bye Quit sftp\n"
"cd path Change remote directory to 'path'\n"
- "chgrp grp path Change group of file 'path' to 'grp'\n"
- "chmod mode path Change permissions of file 'path' to 'mode'\n"
- "chown own path Change owner of file 'path' to 'own'\n"
+ "chgrp [-h] grp path Change group of file 'path' to 'grp'\n"
+ "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n"
+ "chown [-h] own path Change owner of file 'path' to 'own'\n"
"df [-hi] [path] Display statistics for current directory or\n"
" filesystem containing 'path'\n"
"exit Quit sftp\n"
- "get [-afPpRr] remote [local] Download file\n"
- "reget [-fPpRr] remote [local] Resume download file\n"
- "reput [-fPpRr] [local] remote Resume upload file\n"
+ "get [-afpR] remote [local] Download file\n"
"help Display this help text\n"
"lcd path Change local directory to 'path'\n"
"lls [ls-options [path]] Display local directory listing\n"
@@ -266,10 +297,12 @@ help(void)
"lumask umask Set local umask to 'umask'\n"
"mkdir path Create remote directory\n"
"progress Toggle display of progress meter\n"
- "put [-afPpRr] local [remote] Upload file\n"
+ "put [-afpR] local [remote] Upload file\n"
"pwd Display remote working directory\n"
"quit Quit sftp\n"
+ "reget [-fpR] remote [local] Resume download file\n"
"rename oldpath newpath Rename remote file\n"
+ "reput [-fpR] local [remote] Resume upload file\n"
"rm path Delete remote file\n"
"rmdir path Remove remote directory\n"
"symlink oldpath newpath Symlink remote file\n"
@@ -335,7 +368,7 @@ local_do_ls(const char *args)
/* Strip one path (usually the pwd) from the start of another */
static char *
-path_strip(char *path, char *strip)
+path_strip(const char *path, const char *strip)
{
size_t len;
@@ -353,12 +386,12 @@ path_strip(char *path, char *strip)
}
static char *
-make_absolute(char *p, char *pwd)
+make_absolute(char *p, const char *pwd)
{
char *abs_str;
/* Derelativise */
- if (p && p[0] != '/') {
+ if (p && !path_absolute(p)) {
abs_str = path_append(pwd, p);
free(p);
return(abs_str);
@@ -531,6 +564,30 @@ parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
}
static int
+parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag)
+{
+ extern int opterr, optind, optopt, optreset;
+ int ch;
+
+ optind = optreset = 1;
+ opterr = 0;
+
+ *hflag = 0;
+ while ((ch = getopt(argc, argv, "h")) != -1) {
+ switch (ch) {
+ case 'h':
+ *hflag = 1;
+ break;
+ default:
+ error("%s: Invalid flag -%c", cmd, optopt);
+ return -1;
+ }
+ }
+
+ return optind;
+}
+
+static int
parse_no_flags(const char *cmd, char **argv, int argc)
{
extern int opterr, optind, optopt, optreset;
@@ -551,7 +608,7 @@ parse_no_flags(const char *cmd, char **argv, int argc)
}
static int
-is_dir(char *path)
+is_dir(const char *path)
{
struct stat sb;
@@ -563,7 +620,7 @@ is_dir(char *path)
}
static int
-remote_is_dir(struct sftp_conn *conn, char *path)
+remote_is_dir(struct sftp_conn *conn, const char *path)
{
Attrib *a;
@@ -577,7 +634,7 @@ remote_is_dir(struct sftp_conn *conn, char *path)
/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
static int
-pathname_is_dir(char *pathname)
+pathname_is_dir(const char *pathname)
{
size_t l = strlen(pathname);
@@ -585,8 +642,8 @@ pathname_is_dir(char *pathname)
}
static int
-process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
- int pflag, int rflag, int resume, int fflag)
+process_get(struct sftp_conn *conn, const char *src, const char *dst,
+ const char *pwd, int pflag, int rflag, int resume, int fflag)
{
char *abs_src = NULL;
char *abs_dst = NULL;
@@ -644,9 +701,11 @@ process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
resume |= global_aflag;
if (!quiet && resume)
- printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
+ mprintf("Resuming %s to %s\n",
+ g.gl_pathv[i], abs_dst);
else if (!quiet && !resume)
- printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
+ mprintf("Fetching %s to %s\n",
+ g.gl_pathv[i], abs_dst);
if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
pflag || global_pflag, 1, resume,
@@ -669,8 +728,8 @@ out:
}
static int
-process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
- int pflag, int rflag, int resume, int fflag)
+process_put(struct sftp_conn *conn, const char *src, const char *dst,
+ const char *pwd, int pflag, int rflag, int resume, int fflag)
{
char *tmp_dst = NULL;
char *abs_dst = NULL;
@@ -735,10 +794,11 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
resume |= global_aflag;
if (!quiet && resume)
- printf("Resuming upload of %s to %s\n", g.gl_pathv[i],
- abs_dst);
+ mprintf("Resuming upload of %s to %s\n",
+ g.gl_pathv[i], abs_dst);
else if (!quiet && !resume)
- printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
+ mprintf("Uploading %s to %s\n",
+ g.gl_pathv[i], abs_dst);
if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
if (upload_dir(conn, g.gl_pathv[i], abs_dst,
pflag || global_pflag, 1, resume,
@@ -779,7 +839,8 @@ sdirent_comp(const void *aa, const void *bb)
/* sftp ls.1 replacement for directories */
static int
-do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
+do_ls_dir(struct sftp_conn *conn, const char *path,
+ const char *strip_path, int lflag)
{
int n;
u_int c = 1, colspace = 0, columns = 1;
@@ -796,7 +857,7 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
/* Count entries for sort and find longest filename */
for (n = 0; d[n] != NULL; n++) {
if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
- m = MAX(m, strlen(d[n]->filename));
+ m = MAXIMUM(m, strlen(d[n]->filename));
}
/* Add any subpath that also needs to be counted */
@@ -808,9 +869,9 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
width = ws.ws_col;
columns = width / (m + 2);
- columns = MAX(columns, 1);
+ columns = MAXIMUM(columns, 1);
colspace = width / columns;
- colspace = MIN(colspace, width);
+ colspace = MINIMUM(colspace, width);
}
if (lflag & SORT_FLAGS) {
@@ -839,12 +900,12 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
attrib_to_stat(&d[n]->a, &sb);
lname = ls_file(fname, &sb, 1,
(lflag & LS_SI_UNITS));
- printf("%s\n", lname);
+ mprintf("%s\n", lname);
free(lname);
} else
- printf("%s\n", d[n]->longname);
+ mprintf("%s\n", d[n]->longname);
} else {
- printf("%-*s", colspace, fname);
+ mprintf("%-*s", colspace, fname);
if (c >= columns) {
printf("\n");
c = 1;
@@ -862,16 +923,45 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
return (0);
}
+static int
+sglob_comp(const void *aa, const void *bb)
+{
+ u_int a = *(const u_int *)aa;
+ u_int b = *(const u_int *)bb;
+ const char *ap = sort_glob->gl_pathv[a];
+ const char *bp = sort_glob->gl_pathv[b];
+ const struct stat *as = sort_glob->gl_statv[a];
+ const struct stat *bs = sort_glob->gl_statv[b];
+ int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
+
+#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
+ if (sort_flag & LS_NAME_SORT)
+ return (rmul * strcmp(ap, bp));
+ else if (sort_flag & LS_TIME_SORT) {
+#if defined(HAVE_STRUCT_STAT_ST_MTIM)
+ return (rmul * timespeccmp(&as->st_mtim, &bs->st_mtim, <));
+#elif defined(HAVE_STRUCT_STAT_ST_MTIME)
+ return (rmul * NCMP(as->st_mtime, bs->st_mtime));
+#else
+ return rmul * 1;
+#endif
+ } else if (sort_flag & LS_SIZE_SORT)
+ return (rmul * NCMP(as->st_size, bs->st_size));
+
+ fatal("Unknown ls sort type");
+}
+
/* sftp ls.1 replacement which handles path globs */
static int
-do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
- int lflag)
+do_globbed_ls(struct sftp_conn *conn, const char *path,
+ const char *strip_path, int lflag)
{
char *fname, *lname;
glob_t g;
int err, r;
struct winsize ws;
- u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
+ u_int i, j, nentries, *indices = NULL, c = 1;
+ u_int colspace = 0, columns = 1, m = 0, width = 80;
memset(&g, 0, sizeof(g));
@@ -909,14 +999,33 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
if (!(lflag & LS_SHORT_VIEW)) {
/* Count entries for sort and find longest filename */
for (i = 0; g.gl_pathv[i]; i++)
- m = MAX(m, strlen(g.gl_pathv[i]));
+ m = MAXIMUM(m, strlen(g.gl_pathv[i]));
columns = width / (m + 2);
- columns = MAX(columns, 1);
+ columns = MAXIMUM(columns, 1);
colspace = width / columns;
}
- for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
+ /*
+ * Sorting: rather than mess with the contents of glob_t, prepare
+ * an array of indices into it and sort that. For the usual
+ * unsorted case, the indices are just the identity 1=1, 2=2, etc.
+ */
+ for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
+ ; /* count entries */
+ indices = calloc(nentries, sizeof(*indices));
+ for (i = 0; i < nentries; i++)
+ indices[i] = i;
+
+ if (lflag & SORT_FLAGS) {
+ sort_glob = &g;
+ sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
+ qsort(indices, nentries, sizeof(*indices), sglob_comp);
+ sort_glob = NULL;
+ }
+
+ for (j = 0; j < nentries && !interrupted; j++) {
+ i = indices[j];
fname = path_strip(g.gl_pathv[i], strip_path);
if (lflag & LS_LONG_VIEW) {
if (g.gl_statv[i] == NULL) {
@@ -925,10 +1034,10 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
}
lname = ls_file(fname, g.gl_statv[i], 1,
(lflag & LS_SI_UNITS));
- printf("%s\n", lname);
+ mprintf("%s\n", lname);
free(lname);
} else {
- printf("%-*s", colspace, fname);
+ mprintf("%-*s", colspace, fname);
if (c >= columns) {
printf("\n");
c = 1;
@@ -944,31 +1053,43 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
out:
if (g.gl_pathc)
globfree(&g);
+ free(indices);
return 0;
}
static int
-do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
+do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
{
struct sftp_statvfs st;
- char s_used[FMT_SCALED_STRSIZE];
- char s_avail[FMT_SCALED_STRSIZE];
- char s_root[FMT_SCALED_STRSIZE];
- char s_total[FMT_SCALED_STRSIZE];
- unsigned long long ffree;
+ char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
+ char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
+ char s_icapacity[16], s_dcapacity[16];
if (do_statvfs(conn, path, &st, 1) == -1)
return -1;
+ if (st.f_files == 0)
+ strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
+ else {
+ snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
+ (unsigned long long)(100 * (st.f_files - st.f_ffree) /
+ st.f_files));
+ }
+ if (st.f_blocks == 0)
+ strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
+ else {
+ snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
+ (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
+ st.f_blocks));
+ }
if (iflag) {
- ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
printf(" Inodes Used Avail "
"(root) %%Capacity\n");
- printf("%11llu %11llu %11llu %11llu %3llu%%\n",
+ printf("%11llu %11llu %11llu %11llu %s\n",
(unsigned long long)st.f_files,
(unsigned long long)(st.f_files - st.f_ffree),
(unsigned long long)st.f_favail,
- (unsigned long long)st.f_ffree, ffree);
+ (unsigned long long)st.f_ffree, s_icapacity);
} else if (hflag) {
strlcpy(s_used, "error", sizeof(s_used));
strlcpy(s_avail, "error", sizeof(s_avail));
@@ -979,21 +1100,18 @@ do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
fmt_scaled(st.f_bfree * st.f_frsize, s_root);
fmt_scaled(st.f_blocks * st.f_frsize, s_total);
printf(" Size Used Avail (root) %%Capacity\n");
- printf("%7sB %7sB %7sB %7sB %3llu%%\n",
- s_total, s_used, s_avail, s_root,
- (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
- st.f_blocks));
+ printf("%7sB %7sB %7sB %7sB %s\n",
+ s_total, s_used, s_avail, s_root, s_dcapacity);
} else {
printf(" Size Used Avail "
"(root) %%Capacity\n");
- printf("%12llu %12llu %12llu %12llu %3llu%%\n",
+ printf("%12llu %12llu %12llu %12llu %s\n",
(unsigned long long)(st.f_frsize * st.f_blocks / 1024),
(unsigned long long)(st.f_frsize *
(st.f_blocks - st.f_bfree) / 1024),
(unsigned long long)(st.f_frsize * st.f_bavail / 1024),
(unsigned long long)(st.f_frsize * st.f_bfree / 1024),
- (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
- st.f_blocks));
+ s_dcapacity);
}
return 0;
}
@@ -1204,8 +1322,8 @@ makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
}
static int
-parse_args(const char **cpp, int *ignore_errors, int *aflag,
- int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
+parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
+ int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
int *rflag, int *sflag,
unsigned long *n_arg, char **path1, char **path2)
{
@@ -1213,18 +1331,28 @@ parse_args(const char **cpp, int *ignore_errors, int *aflag,
char *cp2, **argv;
int base = 0;
long l;
- int i, cmdnum, optidx, argc;
+ int path1_mandatory = 0, i, cmdnum, optidx, argc;
/* Skip leading whitespace */
cp = cp + strspn(cp, WHITESPACE);
- /* Check for leading '-' (disable error processing) */
+ /*
+ * Check for leading '-' (disable error processing) and '@' (suppress
+ * command echo)
+ */
*ignore_errors = 0;
- if (*cp == '-') {
- *ignore_errors = 1;
- cp++;
- cp = cp + strspn(cp, WHITESPACE);
+ *disable_echo = 0;
+ for (;*cp != '\0'; cp++) {
+ if (*cp == '-') {
+ *ignore_errors = 1;
+ } else if (*cp == '@') {
+ *disable_echo = 1;
+ } else {
+ /* all other characters terminate prefix processing */
+ break;
+ }
}
+ cp = cp + strspn(cp, WHITESPACE);
/* Ignore blank lines and lines which begin with comment '#' char */
if (*cp == '\0' || *cp == '#')
@@ -1303,13 +1431,17 @@ parse_args(const char **cpp, int *ignore_errors, int *aflag,
case I_RM:
case I_MKDIR:
case I_RMDIR:
+ case I_LMKDIR:
+ path1_mandatory = 1;
+ /* FALLTHROUGH */
case I_CHDIR:
case I_LCHDIR:
- case I_LMKDIR:
if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
return -1;
/* Get pathname (mandatory) */
if (argc - optidx < 1) {
+ if (!path1_mandatory)
+ break; /* return a NULL path1 */
error("You must specify a path after a %s command.",
cmd);
return -1;
@@ -1347,9 +1479,10 @@ parse_args(const char **cpp, int *ignore_errors, int *aflag,
case I_LUMASK:
case I_CHMOD:
base = 8;
+ /* FALLTHROUGH */
case I_CHOWN:
case I_CHGRP:
- if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
+ if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
return -1;
/* Get numeric arg (mandatory) */
if (argc - optidx < 1)
@@ -1394,11 +1527,12 @@ parse_args(const char **cpp, int *ignore_errors, int *aflag,
static int
parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
- int err_abort)
+ const char *startdir, int err_abort, int echo_command)
{
+ const char *ocmd = cmd;
char *path1, *path2, *tmp;
- int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0,
- iflag = 0;
+ int ignore_errors = 0, disable_echo = 1;
+ int aflag = 0, fflag = 0, hflag = 0, iflag = 0;
int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
int cmdnum, i;
unsigned long n_arg = 0;
@@ -1408,11 +1542,15 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
glob_t g;
path1 = path2 = NULL;
- cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
- &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
+ cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag,
+ &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg,
+ &path1, &path2);
if (ignore_errors != 0)
err_abort = 0;
+ if (echo_command && !disable_echo)
+ mprintf("sftp> %s\n", ocmd);
+
memset(&g, 0, sizeof(g));
/* Perform command */
@@ -1445,6 +1583,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
break;
case I_SYMLINK:
sflag = 1;
+ /* FALLTHROUGH */
case I_LINK:
if (!sflag)
path1 = make_absolute(path1, *pwd);
@@ -1456,7 +1595,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
if (!quiet)
- printf("Removing %s\n", g.gl_pathv[i]);
+ mprintf("Removing %s\n", g.gl_pathv[i]);
err = do_rm(conn, g.gl_pathv[i]);
if (err != 0 && err_abort)
break;
@@ -1474,6 +1613,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
err = do_rmdir(conn, path1);
break;
case I_CHDIR:
+ if (path1 == NULL || *path1 == '\0')
+ path1 = xstrdup(startdir);
path1 = make_absolute(path1, *pwd);
if ((tmp = do_realpath(conn, path1)) == NULL) {
err = 1;
@@ -1508,7 +1649,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
/* Strip pwd off beginning of non-absolute paths */
tmp = NULL;
- if (*path1 != '/')
+ if (!path_absolute(path1))
tmp = *pwd;
path1 = make_absolute(path1, *pwd);
@@ -1522,6 +1663,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
err = do_df(conn, path1, hflag, iflag);
break;
case I_LCHDIR:
+ if (path1 == NULL || *path1 == '\0')
+ path1 = xstrdup("~");
tmp = tilde_expand_filename(path1, getuid());
free(path1);
path1 = tmp;
@@ -1556,8 +1699,10 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
if (!quiet)
- printf("Changing mode on %s\n", g.gl_pathv[i]);
- err = do_setstat(conn, g.gl_pathv[i], &a);
+ mprintf("Changing mode on %s\n",
+ g.gl_pathv[i]);
+ err = (hflag ? do_lsetstat : do_setstat)(conn,
+ g.gl_pathv[i], &a);
if (err != 0 && err_abort)
break;
}
@@ -1567,7 +1712,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
path1 = make_absolute(path1, *pwd);
remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
- if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
+ if (!(aa = (hflag ? do_lstat : do_stat)(conn,
+ g.gl_pathv[i], 0))) {
if (err_abort) {
err = -1;
break;
@@ -1586,22 +1732,23 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
if (cmdnum == I_CHOWN) {
if (!quiet)
- printf("Changing owner on %s\n",
+ mprintf("Changing owner on %s\n",
g.gl_pathv[i]);
aa->uid = n_arg;
} else {
if (!quiet)
- printf("Changing group on %s\n",
+ mprintf("Changing group on %s\n",
g.gl_pathv[i]);
aa->gid = n_arg;
}
- err = do_setstat(conn, g.gl_pathv[i], aa);
+ err = (hflag ? do_lsetstat : do_setstat)(conn,
+ g.gl_pathv[i], aa);
if (err != 0 && err_abort)
break;
}
break;
case I_PWD:
- printf("Remote working directory: %s\n", *pwd);
+ mprintf("Remote working directory: %s\n", *pwd);
break;
case I_LPWD:
if (!getcwd(path_buf, sizeof(path_buf))) {
@@ -1609,7 +1756,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
err = -1;
break;
}
- printf("Local working directory: %s\n", path_buf);
+ mprintf("Local working directory: %s\n", path_buf);
break;
case I_QUIT:
/* Processed below */
@@ -1662,23 +1809,23 @@ complete_display(char **list, u_int len)
/* Count entries for sort and find longest */
for (y = 0; list[y]; y++)
- m = MAX(m, strlen(list[y]));
+ m = MAXIMUM(m, strlen(list[y]));
if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
width = ws.ws_col;
m = m > len ? m - len : 0;
columns = width / (m + 2);
- columns = MAX(columns, 1);
+ columns = MAXIMUM(columns, 1);
colspace = width / columns;
- colspace = MIN(colspace, width);
+ colspace = MINIMUM(colspace, width);
printf("\n");
m = 1;
for (y = 0; list[y]; y++) {
llen = strlen(list[y]);
tmp = llen > len ? list[y] + len : "";
- printf("%-*s", colspace, tmp);
+ mprintf("%-*s", colspace, tmp);
if (m >= columns) {
printf("\n");
m = 1;
@@ -1762,7 +1909,7 @@ complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
return 0;
}
- /* Complete ambigious command */
+ /* Complete ambiguous command */
tmp = complete_ambiguous(cmd, list, count);
if (count > 1)
complete_display(list, 0);
@@ -1833,7 +1980,7 @@ complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
xasprintf(&tmp, "%s*", file);
/* Check if the path is absolute. */
- isabs = tmp[0] == '/';
+ isabs = path_absolute(tmp);
memset(&g, 0, sizeof(g));
if (remote != LOCAL) {
@@ -1958,7 +2105,7 @@ complete(EditLine *el, int ch)
/* Figure out which argument the cursor points to */
cursor = lf->cursor - lf->buffer;
- line = (char *)xmalloc(cursor + 1);
+ line = xmalloc(cursor + 1);
memcpy(line, lf->buffer, cursor);
line[cursor] = '\0';
argv = makeargv(line, &carg, 1, &quote, &terminated);
@@ -1966,7 +2113,7 @@ complete(EditLine *el, int ch)
/* Get all the arguments on the line */
len = lf->lastchar - lf->buffer;
- line = (char *)xmalloc(len + 1);
+ line = xmalloc(len + 1);
memcpy(line, lf->buffer, len);
line[len] = '\0';
argv = makeargv(line, &argc, 1, NULL, NULL);
@@ -2007,11 +2154,11 @@ complete(EditLine *el, int ch)
}
#endif /* USE_LIBEDIT */
-int
+static int
interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
{
char *remote_path;
- char *dir = NULL;
+ char *dir = NULL, *startdir = NULL;
char cmd[2048];
int err, interactive;
EditLine *el = NULL;
@@ -2044,7 +2191,7 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
/* enable ctrl-left-arrow and ctrl-right-arrow */
el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
- el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
+ el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL);
el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
/* make ^w match ksh behaviour */
@@ -2055,6 +2202,7 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
remote_path = do_realpath(conn, ".");
if (remote_path == NULL)
fatal("Need cwd");
+ startdir = xstrdup(remote_path);
if (file1 != NULL) {
dir = xstrdup(file1);
@@ -2062,11 +2210,12 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
if (remote_is_dir(conn, dir) && file2 == NULL) {
if (!quiet)
- printf("Changing to: %s\n", dir);
+ mprintf("Changing to: %s\n", dir);
snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
if (parse_dispatch_command(conn, cmd,
- &remote_path, 1) != 0) {
+ &remote_path, startdir, 1, 0) != 0) {
free(dir);
+ free(startdir);
free(remote_path);
free(conn);
return (-1);
@@ -2078,8 +2227,9 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
file2 == NULL ? "" : " ",
file2 == NULL ? "" : file2);
err = parse_dispatch_command(conn, cmd,
- &remote_path, 1);
+ &remote_path, startdir, 1, 0);
free(dir);
+ free(startdir);
free(remote_path);
free(conn);
return (err);
@@ -2093,9 +2243,7 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
interactive = !batchmode && isatty(STDIN_FILENO);
err = 0;
for (;;) {
- char *cp;
-
- signal(SIGINT, SIG_IGN);
+ ssh_signal(SIGINT, SIG_IGN);
if (el == NULL) {
if (interactive)
@@ -2105,12 +2253,6 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
printf("\n");
break;
}
- if (!interactive) { /* Echo command */
- printf("sftp> %s", cmd);
- if (strlen(cmd) > 0 &&
- cmd[strlen(cmd) - 1] != '\n')
- printf("\n");
- }
} else {
#ifdef USE_LIBEDIT
const char *line;
@@ -2129,20 +2271,20 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
#endif /* USE_LIBEDIT */
}
- cp = strrchr(cmd, '\n');
- if (cp)
- *cp = '\0';
+ cmd[strcspn(cmd, "\n")] = '\0';
/* Handle user interrupts gracefully during commands */
interrupted = 0;
- signal(SIGINT, cmd_interrupt);
+ ssh_signal(SIGINT, cmd_interrupt);
err = parse_dispatch_command(conn, cmd, &remote_path,
- batchmode);
+ startdir, batchmode, !interactive && el == NULL);
if (err != 0)
break;
}
+ ssh_signal(SIGCHLD, SIG_DFL);
free(remote_path);
+ free(startdir);
free(conn);
#ifdef USE_LIBEDIT
@@ -2197,16 +2339,20 @@ connect_to_server(char *path, char **args, int *in, int *out)
* kill it too. Contrawise, since sftp sends SIGTERMs to the
* underlying ssh, it must *not* ignore that signal.
*/
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, SIG_DFL);
+ ssh_signal(SIGINT, SIG_IGN);
+ ssh_signal(SIGTERM, SIG_DFL);
execvp(path, args);
fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
_exit(1);
}
- signal(SIGTERM, killchild);
- signal(SIGINT, killchild);
- signal(SIGHUP, killchild);
+ ssh_signal(SIGTERM, killchild);
+ ssh_signal(SIGINT, killchild);
+ ssh_signal(SIGHUP, killchild);
+ ssh_signal(SIGTSTP, suspchild);
+ ssh_signal(SIGTTIN, suspchild);
+ ssh_signal(SIGTTOU, suspchild);
+ ssh_signal(SIGCHLD, sigchld_handler);
close(c_in);
close(c_out);
}
@@ -2217,25 +2363,21 @@ usage(void)
extern char *__progname;
fprintf(stderr,
- "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
- " [-D sftp_server_path] [-F ssh_config] "
- "[-i identity_file] [-l limit]\n"
- " [-o ssh_option] [-P port] [-R num_requests] "
- "[-S program]\n"
- " [-s subsystem | sftp_server] host\n"
- " %s [user@]host[:file ...]\n"
- " %s [user@]host[:dir[/]]\n"
- " %s -b batchfile [user@]host\n",
- __progname, __progname, __progname, __progname);
+ "usage: %s [-46aCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
+ " [-D sftp_server_path] [-F ssh_config] [-i identity_file]\n"
+ " [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
+ " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
+ " destination\n",
+ __progname);
exit(1);
}
int
main(int argc, char **argv)
{
- int in, out, ch, err;
- char *host = NULL, *userhost, *cp, *file2 = NULL;
- int debug_level = 0, sshver = 2;
+ int in, out, ch, err, tmp, port = -1, noisy = 0;
+ char *host = NULL, *user, *cp, *file2 = NULL;
+ int debug_level = 0;
char *file1 = NULL, *sftp_server = NULL;
char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
const char *errstr;
@@ -2250,7 +2392,9 @@ main(int argc, char **argv)
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd();
- setlocale(LC_CTYPE, "");
+ msetlocale();
+
+ seed_rng();
__progname = ssh_get_progname(argv[0]);
memset(&args, '\0', sizeof(args));
@@ -2265,7 +2409,7 @@ main(int argc, char **argv)
infile = stdin;
while ((ch = getopt(argc, argv,
- "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
+ "1246afhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:")) != -1) {
switch (ch) {
/* Passed through to ssh(1) */
case '4':
@@ -2275,6 +2419,7 @@ main(int argc, char **argv)
break;
/* Passed through to ssh(1) with argument */
case 'F':
+ case 'J':
case 'c':
case 'i':
case 'o':
@@ -2288,7 +2433,9 @@ main(int argc, char **argv)
addargs(&args, "-%c", ch);
break;
case 'P':
- addargs(&args, "-oPort %s", optarg);
+ port = a2port(optarg);
+ if (port <= 0)
+ fatal("Bad port \"%s\"\n", optarg);
break;
case 'v':
if (debug_level < 3) {
@@ -2298,12 +2445,10 @@ main(int argc, char **argv)
debug_level++;
break;
case '1':
- sshver = 1;
- if (sftp_server == NULL)
- sftp_server = _PATH_SFTP_SERVER;
+ fatal("SSH protocol v.1 is no longer supported");
break;
case '2':
- sshver = 2;
+ /* accept silently */
break;
case 'a':
global_aflag = 1;
@@ -2328,6 +2473,9 @@ main(int argc, char **argv)
case 'f':
global_fflag = 1;
break;
+ case 'N':
+ noisy = 1; /* Used to clear quiet mode after getopt */
+ break;
case 'p':
global_pflag = 1;
break;
@@ -2366,39 +2514,51 @@ main(int argc, char **argv)
if (!isatty(STDERR_FILENO))
showprogress = 0;
+ if (noisy)
+ quiet = 0;
+
log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
if (sftp_direct == NULL) {
if (optind == argc || argc > (optind + 2))
usage();
+ argv += optind;
- userhost = xstrdup(argv[optind]);
- file2 = argv[optind+1];
-
- if ((host = strrchr(userhost, '@')) == NULL)
- host = userhost;
- else {
- *host++ = '\0';
- if (!userhost[0]) {
- fprintf(stderr, "Missing username\n");
- usage();
- }
- addargs(&args, "-l");
- addargs(&args, "%s", userhost);
- }
-
- if ((cp = colon(host)) != NULL) {
- *cp++ = '\0';
- file1 = cp;
+ switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
+ case -1:
+ usage();
+ break;
+ case 0:
+ if (tmp != -1)
+ port = tmp;
+ break;
+ default:
+ /* Try with user, host and path. */
+ if (parse_user_host_path(*argv, &user, &host,
+ &file1) == 0)
+ break;
+ /* Try with user and host. */
+ if (parse_user_host_port(*argv, &user, &host, NULL)
+ == 0)
+ break;
+ /* Treat as a plain hostname. */
+ host = xstrdup(*argv);
+ host = cleanhostname(host);
+ break;
}
+ file2 = *(argv + 1);
- host = cleanhostname(host);
if (!*host) {
fprintf(stderr, "Missing hostname\n");
usage();
}
- addargs(&args, "-oProtocol %d", sshver);
+ if (port != -1)
+ addargs(&args, "-oPort %d", port);
+ if (user != NULL) {
+ addargs(&args, "-l");
+ addargs(&args, "%s", user);
+ }
/* no subsystem if the server-spec contains a '/' */
if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
@@ -2441,7 +2601,7 @@ main(int argc, char **argv)
if (batchmode)
fclose(infile);
- while (waitpid(sshpid, NULL, 0) == -1)
+ while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
if (errno != EINTR)
fatal("Couldn't wait for ssh process: %s",
strerror(errno));