+/* $OpenBSD: sftp.c,v 1.83 2006/07/08 21:47:12 stevesk Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
#include "includes.h"
-RCSID("$OpenBSD: sftp.c,v 1.53 2004/06/21 22:30:45 djm Exp $");
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+#ifdef USE_LIBEDIT
+#include <histedit.h>
+#else
+typedef void EditLine;
+#endif
+#include <signal.h>
#include "buffer.h"
#include "xmalloc.h"
int remote_glob(struct sftp_conn *, const char *, int,
int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
-#ifdef HAVE___PROGNAME
extern char *__progname;
-#else
-char *__progname;
-#endif
/* Separators for interactive commands */
#define WHITESPACE " \t\r\n"
#define LS_TIME_SORT 0x10 /* Sort by mtime */
#define LS_SIZE_SORT 0x20 /* Sort by file size */
#define LS_REVERSE_SORT 0x40 /* Reverse sort order */
+#define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */
#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
#define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
static void
killchild(int signo)
{
- if (sshpid > 1)
+ if (sshpid > 1) {
kill(sshpid, SIGTERM);
+ waitpid(sshpid, NULL, 0);
+ }
_exit(1);
}
cmd_interrupt(int signo)
{
const char msg[] = "\rInterrupt \n";
+ int olderrno = errno;
write(STDERR_FILENO, msg, sizeof(msg) - 1);
interrupted = 1;
+ errno = olderrno;
}
static void
if (errno != EINTR)
fatal("Couldn't wait for child: %s", strerror(errno));
if (!WIFEXITED(status))
- error("Shell exited abormally");
+ error("Shell exited abnormally");
else if (WEXITSTATUS(status))
error("Shell exited with status %d", WEXITSTATUS(status));
}
return (xstrdup(path));
len = strlen(strip);
- if (strip != NULL && strncmp(path, strip, len) == 0) {
+ if (strncmp(path, strip, len) == 0) {
if (strip[len - 1] != '/' && path[len] == '/')
len++;
return (xstrdup(path + len));
/* Check for flags */
if (cp++[0] == '-') {
- for(; strchr(WHITESPACE, *cp) == NULL; cp++) {
+ for (; strchr(WHITESPACE, *cp) == NULL; cp++) {
switch (*cp) {
case 'l':
*lflag &= ~VIEW_FLAGS;
case 'f':
*lflag &= ~SORT_FLAGS;
break;
+ case 'a':
+ *lflag |= LS_SHOW_ALL;
+ break;
default:
error("Invalid flag -%c", *cp);
return(-1);
{
const char *cp = *cpp, *end;
char quot;
- int i, j;
+ u_int i, j;
cp += strspn(cp, WHITESPACE);
if (!*cp) {
i++;
if (cp[i] != '\'' && cp[i] != '\"' &&
cp[i] != '\\') {
- error("Bad escaped character '\%c'",
+ error("Bad escaped character '\\%c'",
cp[i]);
goto fail;
}
if (g.gl_matchc == 1 && dst) {
/* If directory specified, append filename */
+ xfree(tmp);
if (is_dir(dst)) {
if (infer_path(g.gl_pathv[0], &tmp)) {
err = 1;
out:
xfree(abs_src);
- if (abs_dst)
- xfree(abs_dst);
globfree(&g);
return(err);
}
static int
do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
{
- int n, c = 1, colspace = 0, columns = 1;
+ int n;
+ u_int c = 1, colspace = 0, columns = 1;
SFTP_DIRENT **d;
if ((n = do_readdir(conn, path, &d)) != 0)
return (n);
if (!(lflag & LS_SHORT_VIEW)) {
- int m = 0, width = 80;
+ u_int m = 0, width = 80;
struct winsize ws;
char *tmp;
/* Count entries for sort and find longest filename */
- for (n = 0; d[n] != NULL; n++)
- m = MAX(m, strlen(d[n]->filename));
+ for (n = 0; d[n] != NULL; n++) {
+ if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
+ m = MAX(m, strlen(d[n]->filename));
+ }
/* Add any subpath that also needs to be counted */
tmp = path_strip(path, strip_path);
}
if (lflag & SORT_FLAGS) {
+ for (n = 0; d[n] != NULL; n++)
+ ; /* count entries */
sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
qsort(d, n, sizeof(*d), sdirent_comp);
}
for (n = 0; d[n] != NULL && !interrupted; n++) {
char *tmp, *fname;
+ if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
+ continue;
+
tmp = path_append(path, d[n]->filename);
fname = path_strip(tmp, strip_path);
xfree(tmp);
int lflag)
{
glob_t g;
- int i, c = 1, colspace = 0, columns = 1;
- Attrib *a;
+ u_int i, c = 1, colspace = 0, columns = 1;
+ Attrib *a = NULL;
memset(&g, 0, sizeof(g));
if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
- NULL, &g)) {
+ NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
+ if (g.gl_pathc)
+ globfree(&g);
error("Can't ls: \"%s\" not found", path);
return (-1);
}
goto out;
/*
- * If the glob returns a single match, which is the same as the
- * input glob, and it is a directory, then just list its contents
+ * If the glob returns a single match and it is a directory,
+ * then just list its contents.
*/
- if (g.gl_pathc == 1 &&
- strncmp(path, g.gl_pathv[0], strlen(g.gl_pathv[0]) - 1) == 0) {
- if ((a = do_lstat(conn, path, 1)) == NULL) {
+ if (g.gl_matchc == 1) {
+ if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
globfree(&g);
return (-1);
}
if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
S_ISDIR(a->perm)) {
+ int err;
+
+ err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
globfree(&g);
- return (do_ls_dir(conn, path, strip_path, lflag));
+ return (err);
}
}
if (!(lflag & LS_SHORT_VIEW)) {
- int m = 0, width = 80;
+ u_int m = 0, width = 80;
struct winsize ws;
/* Count entries for sort and find longest filename */
colspace = width / columns;
}
- for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
+ for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
char *fname;
fname = path_strip(g.gl_pathv[i], strip_path);
* that the server returns as well as the filenames.
*/
memset(&sb, 0, sizeof(sb));
- a = do_lstat(conn, g.gl_pathv[i], 1);
+ if (a == NULL)
+ a = do_lstat(conn, g.gl_pathv[i], 1);
if (a != NULL)
attrib_to_stat(a, &sb);
lname = ls_file(fname, &sb, 1);
return (0);
}
+#ifdef USE_LIBEDIT
+static char *
+prompt(EditLine *el)
+{
+ return ("sftp> ");
+}
+#endif
+
int
interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
{
char *dir = NULL;
char cmd[2048];
struct sftp_conn *conn;
- int err;
+ int err, interactive;
+ EditLine *el = NULL;
+#ifdef USE_LIBEDIT
+ History *hl = NULL;
+ HistEvent hev;
+ extern char *__progname;
+
+ if (!batchmode && isatty(STDIN_FILENO)) {
+ if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
+ fatal("Couldn't initialise editline");
+ if ((hl = history_init()) == NULL)
+ fatal("Couldn't initialise editline history");
+ history(hl, &hev, H_SETSIZE, 100);
+ el_set(el, EL_HIST, history, hl);
+
+ el_set(el, EL_PROMPT, prompt);
+ el_set(el, EL_EDITOR, "emacs");
+ el_set(el, EL_TERMINAL, NULL);
+ el_set(el, EL_SIGNAL, 1);
+ el_source(el, NULL);
+ }
+#endif /* USE_LIBEDIT */
conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests);
if (conn == NULL)
if (remote_is_dir(conn, dir) && file2 == NULL) {
printf("Changing to: %s\n", dir);
snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
- if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0)
+ if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) {
+ xfree(dir);
+ xfree(pwd);
+ xfree(conn);
return (-1);
+ }
} else {
if (file2 == NULL)
snprintf(cmd, sizeof cmd, "get %s", dir);
err = parse_dispatch_command(conn, cmd, &pwd, 1);
xfree(dir);
xfree(pwd);
+ xfree(conn);
return (err);
}
xfree(dir);
}
-#if HAVE_SETVBUF
+#if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
setvbuf(stdout, NULL, _IOLBF, 0);
setvbuf(infile, NULL, _IOLBF, 0);
#else
- setlinebuf(stdout);
- setlinebuf(infile);
+ setlinebuf(stdout);
+ setlinebuf(infile);
#endif
+ interactive = !batchmode && isatty(STDIN_FILENO);
err = 0;
for (;;) {
char *cp;
signal(SIGINT, SIG_IGN);
- printf("sftp> ");
+ if (el == NULL) {
+ if (interactive)
+ printf("sftp> ");
+ if (fgets(cmd, sizeof(cmd), infile) == NULL) {
+ if (interactive)
+ 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;
+ int count = 0;
- /* XXX: use libedit */
- if (fgets(cmd, sizeof(cmd), infile) == NULL) {
- printf("\n");
- break;
+ if ((line = el_gets(el, &count)) == NULL || count <= 0) {
+ printf("\n");
+ break;
+ }
+ history(hl, &hev, H_ENTER, line);
+ if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
+ fprintf(stderr, "Error: input line too long\n");
+ continue;
+ }
+#endif /* USE_LIBEDIT */
}
- if (batchmode) /* Echo command */
- printf("%s", cmd);
-
cp = strrchr(cmd, '\n');
if (cp)
*cp = '\0';
break;
}
xfree(pwd);
+ xfree(conn);
+
+#ifdef USE_LIBEDIT
+ if (el != NULL)
+ el_end(el);
+#endif /* USE_LIBEDIT */
/* err == 1 signifies normal "quit" exit */
return (err >= 0 ? 0 : -1);
/*
* The underlying ssh is in the same process group, so we must
- * ignore SIGINT if we want to gracefully abort commands,
- * otherwise the signal will make it to the ssh process and
+ * ignore SIGINT if we want to gracefully abort commands,
+ * otherwise the signal will make it to the ssh process and
* kill it too
*/
signal(SIGINT, SIG_IGN);
extern int optind;
extern char *optarg;
+ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
+ sanitise_stdfd();
+
__progname = ssh_get_progname(argv[0]);
+ memset(&args, '\0', sizeof(args));
args.list = NULL;
- addargs(&args, "ssh"); /* overwritten with ssh_program */
+ addargs(&args, "%s", ssh_program);
addargs(&args, "-oForwardX11 no");
addargs(&args, "-oForwardAgent no");
+ addargs(&args, "-oPermitLocalCommand no");
addargs(&args, "-oClearAllForwardings yes");
ll = SYSLOG_LEVEL_INFO;
break;
case 'S':
ssh_program = optarg;
+ replacearg(&args, 0, "%s", ssh_program);
break;
case 'b':
if (batchmode)
fatal("Batch file already specified.");
/* Allow "-" as stdin */
- if (strcmp(optarg, "-") != 0 &&
- (infile = fopen(optarg, "r")) == NULL)
+ if (strcmp(optarg, "-") != 0 &&
+ (infile = fopen(optarg, "r")) == NULL)
fatal("%s (%s).", strerror(errno), optarg);
showprogress = 0;
batchmode = 1;
+ addargs(&args, "-obatchmode yes");
break;
case 'P':
sftp_direct = optarg;
addargs(&args, "%s", host);
addargs(&args, "%s", (sftp_server != NULL ?
sftp_server : "sftp"));
- args.list[0] = ssh_program;
if (!batchmode)
fprintf(stderr, "Connecting to %s...\n", host);
fprintf(stderr, "Attaching to %s...\n", sftp_direct);
connect_to_server(sftp_direct, args.list, &in, &out);
}
+ freeargs(&args);
err = interactive_loop(in, out, file1, file2);
#if !defined(USE_PIPES)
- shutdown(in, SHUT_RDWR);
- shutdown(out, SHUT_RDWR);
+ shutdown(in, SHUT_RDWR);
+ shutdown(out, SHUT_RDWR);
#endif
close(in);