1 /* $OpenBSD: sftp.c,v 1.158 2013/11/20 20:54:10 deraadt Exp $ */
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
25 #include <sys/param.h>
26 #include <sys/socket.h>
28 #ifdef HAVE_SYS_STATVFS_H
29 #include <sys/statvfs.h>
47 typedef void EditLine;
62 #include "pathnames.h"
67 #include "sftp-common.h"
68 #include "sftp-client.h"
70 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
71 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
73 /* File to read commands from */
76 /* Are we in batchfile mode? */
79 /* PID of ssh transport process */
80 static pid_t sshpid = -1;
82 /* Suppress diagnositic messages */
85 /* This is set to 0 if the progressmeter is not desired. */
88 /* When this option is set, we always recursively download/upload directories */
91 /* When this option is set, we resume download if possible */
94 /* When this option is set, the file transfers will always preserve times */
97 /* When this option is set, transfers will have fsync() called on each file */
100 /* SIGINT received during command processing */
101 volatile sig_atomic_t interrupted = 0;
103 /* I wish qsort() took a separate ctx for the comparison function...*/
106 /* Context used for commandline completion */
107 struct complete_ctx {
108 struct sftp_conn *conn;
112 int remote_glob(struct sftp_conn *, const char *, int,
113 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
115 extern char *__progname;
117 /* Separators for interactive commands */
118 #define WHITESPACE " \t\r\n"
121 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
122 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
123 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
124 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
125 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
126 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
127 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
128 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
129 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
131 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
132 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
134 /* Commands for interactive mode */
170 /* Type of completion */
175 static const struct CMD cmds[] = {
176 { "bye", I_QUIT, NOARGS },
177 { "cd", I_CHDIR, REMOTE },
178 { "chdir", I_CHDIR, REMOTE },
179 { "chgrp", I_CHGRP, REMOTE },
180 { "chmod", I_CHMOD, REMOTE },
181 { "chown", I_CHOWN, REMOTE },
182 { "df", I_DF, REMOTE },
183 { "dir", I_LS, REMOTE },
184 { "exit", I_QUIT, NOARGS },
185 { "get", I_GET, REMOTE },
186 { "help", I_HELP, NOARGS },
187 { "lcd", I_LCHDIR, LOCAL },
188 { "lchdir", I_LCHDIR, LOCAL },
189 { "lls", I_LLS, LOCAL },
190 { "lmkdir", I_LMKDIR, LOCAL },
191 { "ln", I_LINK, REMOTE },
192 { "lpwd", I_LPWD, LOCAL },
193 { "ls", I_LS, REMOTE },
194 { "lumask", I_LUMASK, NOARGS },
195 { "mkdir", I_MKDIR, REMOTE },
196 { "mget", I_GET, REMOTE },
197 { "mput", I_PUT, LOCAL },
198 { "progress", I_PROGRESS, NOARGS },
199 { "put", I_PUT, LOCAL },
200 { "pwd", I_PWD, REMOTE },
201 { "quit", I_QUIT, NOARGS },
202 { "reget", I_REGET, REMOTE },
203 { "rename", I_RENAME, REMOTE },
204 { "rm", I_RM, REMOTE },
205 { "rmdir", I_RMDIR, REMOTE },
206 { "symlink", I_SYMLINK, REMOTE },
207 { "version", I_VERSION, NOARGS },
208 { "!", I_SHELL, NOARGS },
209 { "?", I_HELP, NOARGS },
213 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
220 kill(sshpid, SIGTERM);
221 waitpid(sshpid, NULL, 0);
229 cmd_interrupt(int signo)
231 const char msg[] = "\rInterrupt \n";
232 int olderrno = errno;
234 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
242 printf("Available commands:\n"
244 "cd path Change remote directory to 'path'\n"
245 "chgrp grp path Change group of file 'path' to 'grp'\n"
246 "chmod mode path Change permissions of file 'path' to 'mode'\n"
247 "chown own path Change owner of file 'path' to 'own'\n"
248 "df [-hi] [path] Display statistics for current directory or\n"
249 " filesystem containing 'path'\n"
251 "get [-Ppr] remote [local] Download file\n"
252 "reget remote [local] Resume download file\n"
253 "help Display this help text\n"
254 "lcd path Change local directory to 'path'\n"
255 "lls [ls-options [path]] Display local directory listing\n"
256 "lmkdir path Create local directory\n"
257 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
258 "lpwd Print local working directory\n"
259 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
260 "lumask umask Set local umask to 'umask'\n"
261 "mkdir path Create remote directory\n"
262 "progress Toggle display of progress meter\n"
263 "put [-Ppr] local [remote] Upload file\n"
264 "pwd Display remote working directory\n"
266 "rename oldpath newpath Rename remote file\n"
267 "rm path Delete remote file\n"
268 "rmdir path Remove remote directory\n"
269 "symlink oldpath newpath Symlink remote file\n"
270 "version Show SFTP version\n"
271 "!command Execute 'command' in local shell\n"
272 "! Escape to local shell\n"
273 "? Synonym for help\n");
277 local_do_shell(const char *args)
286 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
287 shell = _PATH_BSHELL;
289 if ((pid = fork()) == -1)
290 fatal("Couldn't fork: %s", strerror(errno));
293 /* XXX: child has pipe fds to ssh subproc open - issue? */
295 debug3("Executing %s -c \"%s\"", shell, args);
296 execl(shell, shell, "-c", args, (char *)NULL);
298 debug3("Executing %s", shell);
299 execl(shell, shell, (char *)NULL);
301 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
305 while (waitpid(pid, &status, 0) == -1)
307 fatal("Couldn't wait for child: %s", strerror(errno));
308 if (!WIFEXITED(status))
309 error("Shell exited abnormally");
310 else if (WEXITSTATUS(status))
311 error("Shell exited with status %d", WEXITSTATUS(status));
315 local_do_ls(const char *args)
318 local_do_shell(_PATH_LS);
320 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
321 char *buf = xmalloc(len);
323 /* XXX: quoting - rip quoting code from ftp? */
324 snprintf(buf, len, _PATH_LS " %s", args);
330 /* Strip one path (usually the pwd) from the start of another */
332 path_strip(char *path, char *strip)
337 return (xstrdup(path));
340 if (strncmp(path, strip, len) == 0) {
341 if (strip[len - 1] != '/' && path[len] == '/')
343 return (xstrdup(path + len));
346 return (xstrdup(path));
350 make_absolute(char *p, char *pwd)
355 if (p && p[0] != '/') {
356 abs_str = path_append(pwd, p);
364 parse_getput_flags(const char *cmd, char **argv, int argc,
365 int *aflag, int *fflag, int *pflag, int *rflag)
367 extern int opterr, optind, optopt, optreset;
370 optind = optreset = 1;
373 *aflag = *fflag = *rflag = *pflag = 0;
374 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
391 error("%s: Invalid flag -%c", cmd, optopt);
400 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
402 extern int opterr, optind, optopt, optreset;
405 optind = optreset = 1;
409 while ((ch = getopt(argc, argv, "s")) != -1) {
415 error("%s: Invalid flag -%c", cmd, optopt);
424 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
426 extern int opterr, optind, optopt, optreset;
429 optind = optreset = 1;
433 while ((ch = getopt(argc, argv, "l")) != -1) {
439 error("%s: Invalid flag -%c", cmd, optopt);
448 parse_ls_flags(char **argv, int argc, int *lflag)
450 extern int opterr, optind, optopt, optreset;
453 optind = optreset = 1;
456 *lflag = LS_NAME_SORT;
457 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
460 *lflag &= ~VIEW_FLAGS;
461 *lflag |= LS_SHORT_VIEW;
464 *lflag &= ~SORT_FLAGS;
465 *lflag |= LS_SIZE_SORT;
468 *lflag |= LS_SHOW_ALL;
471 *lflag &= ~SORT_FLAGS;
474 *lflag |= LS_SI_UNITS;
477 *lflag &= ~LS_SHORT_VIEW;
478 *lflag |= LS_LONG_VIEW;
481 *lflag &= ~LS_SHORT_VIEW;
482 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
485 *lflag |= LS_REVERSE_SORT;
488 *lflag &= ~SORT_FLAGS;
489 *lflag |= LS_TIME_SORT;
492 error("ls: Invalid flag -%c", optopt);
501 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
503 extern int opterr, optind, optopt, optreset;
506 optind = optreset = 1;
510 while ((ch = getopt(argc, argv, "hi")) != -1) {
519 error("%s: Invalid flag -%c", cmd, optopt);
528 parse_no_flags(const char *cmd, char **argv, int argc)
530 extern int opterr, optind, optopt, optreset;
533 optind = optreset = 1;
536 while ((ch = getopt(argc, argv, "")) != -1) {
539 error("%s: Invalid flag -%c", cmd, optopt);
552 /* XXX: report errors? */
553 if (stat(path, &sb) == -1)
556 return(S_ISDIR(sb.st_mode));
560 remote_is_dir(struct sftp_conn *conn, char *path)
564 /* XXX: report errors? */
565 if ((a = do_stat(conn, path, 1)) == NULL)
567 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
569 return(S_ISDIR(a->perm));
572 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
574 pathname_is_dir(char *pathname)
576 size_t l = strlen(pathname);
578 return l > 0 && pathname[l - 1] == '/';
582 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
583 int pflag, int rflag, int resume, int fflag)
585 char *abs_src = NULL;
586 char *abs_dst = NULL;
588 char *filename, *tmp=NULL;
591 abs_src = xstrdup(src);
592 abs_src = make_absolute(abs_src, pwd);
593 memset(&g, 0, sizeof(g));
595 debug3("Looking up %s", abs_src);
596 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
597 error("File \"%s\" not found.", abs_src);
603 * If multiple matches then dst must be a directory or
606 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
607 error("Multiple source paths, but destination "
608 "\"%s\" is not a directory", dst);
613 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
614 tmp = xstrdup(g.gl_pathv[i]);
615 if ((filename = basename(tmp)) == NULL) {
616 error("basename %s: %s", tmp, strerror(errno));
622 if (g.gl_matchc == 1 && dst) {
624 abs_dst = path_append(dst, filename);
626 abs_dst = xstrdup(dst);
629 abs_dst = path_append(dst, filename);
631 abs_dst = xstrdup(filename);
635 resume |= global_aflag;
636 if (!quiet && resume)
637 printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
638 else if (!quiet && !resume)
639 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
640 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
641 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
642 pflag || global_pflag, 1, resume,
643 fflag || global_fflag) == -1)
646 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
647 pflag || global_pflag, resume,
648 fflag || global_fflag) == -1)
662 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
663 int pflag, int rflag, int fflag)
665 char *tmp_dst = NULL;
666 char *abs_dst = NULL;
667 char *tmp = NULL, *filename = NULL;
670 int i, dst_is_dir = 1;
674 tmp_dst = xstrdup(dst);
675 tmp_dst = make_absolute(tmp_dst, pwd);
678 memset(&g, 0, sizeof(g));
679 debug3("Looking up %s", src);
680 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
681 error("File \"%s\" not found.", src);
686 /* If we aren't fetching to pwd then stash this status for later */
688 dst_is_dir = remote_is_dir(conn, tmp_dst);
690 /* If multiple matches, dst may be directory or unspecified */
691 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
692 error("Multiple paths match, but destination "
693 "\"%s\" is not a directory", tmp_dst);
698 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
699 if (stat(g.gl_pathv[i], &sb) == -1) {
701 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
705 tmp = xstrdup(g.gl_pathv[i]);
706 if ((filename = basename(tmp)) == NULL) {
707 error("basename %s: %s", tmp, strerror(errno));
713 if (g.gl_matchc == 1 && tmp_dst) {
714 /* If directory specified, append filename */
716 abs_dst = path_append(tmp_dst, filename);
718 abs_dst = xstrdup(tmp_dst);
719 } else if (tmp_dst) {
720 abs_dst = path_append(tmp_dst, filename);
722 abs_dst = make_absolute(xstrdup(filename), pwd);
727 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
728 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
729 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
730 pflag || global_pflag, 1,
731 fflag || global_fflag) == -1)
734 if (do_upload(conn, g.gl_pathv[i], abs_dst,
735 pflag || global_pflag,
736 fflag || global_fflag) == -1)
749 sdirent_comp(const void *aa, const void *bb)
751 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
752 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
753 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
755 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
756 if (sort_flag & LS_NAME_SORT)
757 return (rmul * strcmp(a->filename, b->filename));
758 else if (sort_flag & LS_TIME_SORT)
759 return (rmul * NCMP(a->a.mtime, b->a.mtime));
760 else if (sort_flag & LS_SIZE_SORT)
761 return (rmul * NCMP(a->a.size, b->a.size));
763 fatal("Unknown ls sort type");
766 /* sftp ls.1 replacement for directories */
768 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
771 u_int c = 1, colspace = 0, columns = 1;
774 if ((n = do_readdir(conn, path, &d)) != 0)
777 if (!(lflag & LS_SHORT_VIEW)) {
778 u_int m = 0, width = 80;
782 /* Count entries for sort and find longest filename */
783 for (n = 0; d[n] != NULL; n++) {
784 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
785 m = MAX(m, strlen(d[n]->filename));
788 /* Add any subpath that also needs to be counted */
789 tmp = path_strip(path, strip_path);
793 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
796 columns = width / (m + 2);
797 columns = MAX(columns, 1);
798 colspace = width / columns;
799 colspace = MIN(colspace, width);
802 if (lflag & SORT_FLAGS) {
803 for (n = 0; d[n] != NULL; n++)
804 ; /* count entries */
805 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
806 qsort(d, n, sizeof(*d), sdirent_comp);
809 for (n = 0; d[n] != NULL && !interrupted; n++) {
812 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
815 tmp = path_append(path, d[n]->filename);
816 fname = path_strip(tmp, strip_path);
819 if (lflag & LS_LONG_VIEW) {
820 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
824 memset(&sb, 0, sizeof(sb));
825 attrib_to_stat(&d[n]->a, &sb);
826 lname = ls_file(fname, &sb, 1,
827 (lflag & LS_SI_UNITS));
828 printf("%s\n", lname);
831 printf("%s\n", d[n]->longname);
833 printf("%-*s", colspace, fname);
844 if (!(lflag & LS_LONG_VIEW) && (c != 1))
847 free_sftp_dirents(d);
851 /* sftp ls.1 replacement which handles path globs */
853 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
860 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
862 memset(&g, 0, sizeof(g));
864 if (remote_glob(conn, path,
865 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
867 (g.gl_pathc && !g.gl_matchc)) {
870 error("Can't ls: \"%s\" not found", path);
878 * If the glob returns a single match and it is a directory,
879 * then just list its contents.
881 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
882 S_ISDIR(g.gl_statv[0]->st_mode)) {
883 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
888 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
891 if (!(lflag & LS_SHORT_VIEW)) {
892 /* Count entries for sort and find longest filename */
893 for (i = 0; g.gl_pathv[i]; i++)
894 m = MAX(m, strlen(g.gl_pathv[i]));
896 columns = width / (m + 2);
897 columns = MAX(columns, 1);
898 colspace = width / columns;
901 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
902 fname = path_strip(g.gl_pathv[i], strip_path);
903 if (lflag & LS_LONG_VIEW) {
904 if (g.gl_statv[i] == NULL) {
905 error("no stat information for %s", fname);
908 lname = ls_file(fname, g.gl_statv[i], 1,
909 (lflag & LS_SI_UNITS));
910 printf("%s\n", lname);
913 printf("%-*s", colspace, fname);
923 if (!(lflag & LS_LONG_VIEW) && (c != 1))
934 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
936 struct sftp_statvfs st;
937 char s_used[FMT_SCALED_STRSIZE];
938 char s_avail[FMT_SCALED_STRSIZE];
939 char s_root[FMT_SCALED_STRSIZE];
940 char s_total[FMT_SCALED_STRSIZE];
941 unsigned long long ffree;
943 if (do_statvfs(conn, path, &st, 1) == -1)
946 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
947 printf(" Inodes Used Avail "
948 "(root) %%Capacity\n");
949 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
950 (unsigned long long)st.f_files,
951 (unsigned long long)(st.f_files - st.f_ffree),
952 (unsigned long long)st.f_favail,
953 (unsigned long long)st.f_ffree, ffree);
955 strlcpy(s_used, "error", sizeof(s_used));
956 strlcpy(s_avail, "error", sizeof(s_avail));
957 strlcpy(s_root, "error", sizeof(s_root));
958 strlcpy(s_total, "error", sizeof(s_total));
959 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
960 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
961 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
962 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
963 printf(" Size Used Avail (root) %%Capacity\n");
964 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
965 s_total, s_used, s_avail, s_root,
966 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
969 printf(" Size Used Avail "
970 "(root) %%Capacity\n");
971 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
972 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
973 (unsigned long long)(st.f_frsize *
974 (st.f_blocks - st.f_bfree) / 1024),
975 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
976 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
977 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
984 * Undo escaping of glob sequences in place. Used to undo extra escaping
985 * applied in makeargv() when the string is destined for a function that
989 undo_glob_escape(char *s)
1024 * Split a string into an argument vector using sh(1)-style quoting,
1025 * comment and escaping rules, but with some tweaks to handle glob(3)
1027 * The "sloppy" flag allows for recovery from missing terminating quote, for
1028 * use in parsing incomplete commandlines during tab autocompletion.
1030 * Returns NULL on error or a NULL-terminated array of arguments.
1032 * If "lastquote" is not NULL, the quoting character used for the last
1033 * argument is placed in *lastquote ("\0", "'" or "\"").
1035 * If "terminated" is not NULL, *terminated will be set to 1 when the
1036 * last argument's quote has been properly terminated or 0 otherwise.
1037 * This parameter is only of use if "sloppy" is set.
1040 #define MAXARGLEN 8192
1042 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1047 static char argvs[MAXARGLEN];
1048 static char *argv[MAXARGS + 1];
1049 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1052 if (strlen(arg) > sizeof(argvs) - 1) {
1054 error("string too long");
1057 if (terminated != NULL)
1059 if (lastquote != NULL)
1064 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1065 error("Too many arguments.");
1068 if (isspace((unsigned char)arg[i])) {
1069 if (state == MA_UNQUOTED) {
1070 /* Terminate current argument */
1074 } else if (state != MA_START)
1075 argvs[j++] = arg[i];
1076 } else if (arg[i] == '"' || arg[i] == '\'') {
1077 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1078 if (state == MA_START) {
1079 argv[argc] = argvs + j;
1081 if (lastquote != NULL)
1082 *lastquote = arg[i];
1083 } else if (state == MA_UNQUOTED)
1085 else if (state == q)
1086 state = MA_UNQUOTED;
1088 argvs[j++] = arg[i];
1089 } else if (arg[i] == '\\') {
1090 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1091 quot = state == MA_SQUOTE ? '\'' : '"';
1092 /* Unescape quote we are in */
1093 /* XXX support \n and friends? */
1094 if (arg[i + 1] == quot) {
1096 argvs[j++] = arg[i];
1097 } else if (arg[i + 1] == '?' ||
1098 arg[i + 1] == '[' || arg[i + 1] == '*') {
1100 * Special case for sftp: append
1101 * double-escaped glob sequence -
1102 * glob will undo one level of
1103 * escaping. NB. string can grow here.
1105 if (j >= sizeof(argvs) - 5)
1106 goto args_too_longs;
1108 argvs[j++] = arg[i++];
1110 argvs[j++] = arg[i];
1112 argvs[j++] = arg[i++];
1113 argvs[j++] = arg[i];
1116 if (state == MA_START) {
1117 argv[argc] = argvs + j;
1118 state = MA_UNQUOTED;
1119 if (lastquote != NULL)
1122 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1123 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1125 * Special case for sftp: append
1126 * escaped glob sequence -
1127 * glob will undo one level of
1130 argvs[j++] = arg[i++];
1131 argvs[j++] = arg[i];
1133 /* Unescape everything */
1134 /* XXX support \n and friends? */
1136 argvs[j++] = arg[i];
1139 } else if (arg[i] == '#') {
1140 if (state == MA_SQUOTE || state == MA_DQUOTE)
1141 argvs[j++] = arg[i];
1144 } else if (arg[i] == '\0') {
1145 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1147 state = MA_UNQUOTED;
1148 if (terminated != NULL)
1152 error("Unterminated quoted argument");
1156 if (state == MA_UNQUOTED) {
1162 if (state == MA_START) {
1163 argv[argc] = argvs + j;
1164 state = MA_UNQUOTED;
1165 if (lastquote != NULL)
1168 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1169 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1171 * Special case for sftp: escape quoted
1172 * glob(3) wildcards. NB. string can grow
1175 if (j >= sizeof(argvs) - 3)
1176 goto args_too_longs;
1178 argvs[j++] = arg[i];
1180 argvs[j++] = arg[i];
1189 parse_args(const char **cpp, int *ignore_errors, int *aflag, int *fflag,
1190 int *hflag, int *iflag, int *lflag, int *pflag, int *rflag, int *sflag,
1191 unsigned long *n_arg, char **path1, char **path2)
1193 const char *cmd, *cp = *cpp;
1197 int i, cmdnum, optidx, argc;
1199 /* Skip leading whitespace */
1200 cp = cp + strspn(cp, WHITESPACE);
1202 /* Check for leading '-' (disable error processing) */
1207 cp = cp + strspn(cp, WHITESPACE);
1210 /* Ignore blank lines and lines which begin with comment '#' char */
1211 if (*cp == '\0' || *cp == '#')
1214 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1217 /* Figure out which command we have */
1218 for (i = 0; cmds[i].c != NULL; i++) {
1219 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1229 } else if (cmdnum == -1) {
1230 error("Invalid command.");
1234 /* Get arguments and parse flags */
1235 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1236 *rflag = *sflag = 0;
1237 *path1 = *path2 = NULL;
1243 if ((optidx = parse_getput_flags(cmd, argv, argc,
1244 aflag, fflag, pflag, rflag)) == -1)
1246 /* Get first pathname (mandatory) */
1247 if (argc - optidx < 1) {
1248 error("You must specify at least one path after a "
1249 "%s command.", cmd);
1252 *path1 = xstrdup(argv[optidx]);
1253 /* Get second pathname (optional) */
1254 if (argc - optidx > 1) {
1255 *path2 = xstrdup(argv[optidx + 1]);
1256 /* Destination is not globbed */
1257 undo_glob_escape(*path2);
1259 if (*aflag && cmdnum == I_PUT) {
1260 /* XXX implement resume for uploads */
1261 error("Resume is not supported for uploads");
1266 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1268 goto parse_two_paths;
1270 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1272 goto parse_two_paths;
1274 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1277 if (argc - optidx < 2) {
1278 error("You must specify two paths after a %s "
1282 *path1 = xstrdup(argv[optidx]);
1283 *path2 = xstrdup(argv[optidx + 1]);
1284 /* Paths are not globbed */
1285 undo_glob_escape(*path1);
1286 undo_glob_escape(*path2);
1294 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1296 /* Get pathname (mandatory) */
1297 if (argc - optidx < 1) {
1298 error("You must specify a path after a %s command.",
1302 *path1 = xstrdup(argv[optidx]);
1303 /* Only "rm" globs */
1305 undo_glob_escape(*path1);
1308 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1311 /* Default to current directory if no path specified */
1312 if (argc - optidx < 1)
1315 *path1 = xstrdup(argv[optidx]);
1316 undo_glob_escape(*path1);
1320 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1322 /* Path is optional */
1323 if (argc - optidx > 0)
1324 *path1 = xstrdup(argv[optidx]);
1327 /* Skip ls command and following whitespace */
1328 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1330 /* Uses the rest of the line */
1337 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1339 /* Get numeric arg (mandatory) */
1340 if (argc - optidx < 1)
1343 l = strtol(argv[optidx], &cp2, base);
1344 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1345 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1348 error("You must supply a numeric argument "
1349 "to the %s command.", cmd);
1353 if (cmdnum == I_LUMASK)
1355 /* Get pathname (mandatory) */
1356 if (argc - optidx < 2) {
1357 error("You must specify a path after a %s command.",
1361 *path1 = xstrdup(argv[optidx + 1]);
1369 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1373 fatal("Command not implemented");
1381 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1384 char *path1, *path2, *tmp;
1385 int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1386 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1388 unsigned long n_arg = 0;
1390 char path_buf[MAXPATHLEN];
1394 path1 = path2 = NULL;
1395 cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1396 &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1397 if (ignore_errors != 0)
1400 memset(&g, 0, sizeof(g));
1402 /* Perform command */
1408 /* Unrecognized command */
1415 err = process_get(conn, path1, path2, *pwd, pflag,
1416 rflag, aflag, fflag);
1419 err = process_put(conn, path1, path2, *pwd, pflag,
1423 path1 = make_absolute(path1, *pwd);
1424 path2 = make_absolute(path2, *pwd);
1425 err = do_rename(conn, path1, path2, lflag);
1431 path1 = make_absolute(path1, *pwd);
1432 path2 = make_absolute(path2, *pwd);
1433 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1436 path1 = make_absolute(path1, *pwd);
1437 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1438 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1440 printf("Removing %s\n", g.gl_pathv[i]);
1441 err = do_rm(conn, g.gl_pathv[i]);
1442 if (err != 0 && err_abort)
1447 path1 = make_absolute(path1, *pwd);
1449 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1451 err = do_mkdir(conn, path1, &a, 1);
1454 path1 = make_absolute(path1, *pwd);
1455 err = do_rmdir(conn, path1);
1458 path1 = make_absolute(path1, *pwd);
1459 if ((tmp = do_realpath(conn, path1)) == NULL) {
1463 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1468 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1469 error("Can't change directory: Can't check target");
1474 if (!S_ISDIR(aa->perm)) {
1475 error("Can't change directory: \"%s\" is not "
1476 "a directory", tmp);
1486 do_ls_dir(conn, *pwd, *pwd, lflag);
1490 /* Strip pwd off beginning of non-absolute paths */
1495 path1 = make_absolute(path1, *pwd);
1496 err = do_globbed_ls(conn, path1, tmp, lflag);
1499 /* Default to current directory if no path specified */
1501 path1 = xstrdup(*pwd);
1502 path1 = make_absolute(path1, *pwd);
1503 err = do_df(conn, path1, hflag, iflag);
1506 if (chdir(path1) == -1) {
1507 error("Couldn't change local directory to "
1508 "\"%s\": %s", path1, strerror(errno));
1513 if (mkdir(path1, 0777) == -1) {
1514 error("Couldn't create local directory "
1515 "\"%s\": %s", path1, strerror(errno));
1523 local_do_shell(cmd);
1527 printf("Local umask: %03lo\n", n_arg);
1530 path1 = make_absolute(path1, *pwd);
1532 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1534 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1535 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1537 printf("Changing mode on %s\n", g.gl_pathv[i]);
1538 err = do_setstat(conn, g.gl_pathv[i], &a);
1539 if (err != 0 && err_abort)
1545 path1 = make_absolute(path1, *pwd);
1546 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1547 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1548 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1555 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1556 error("Can't get current ownership of "
1557 "remote file \"%s\"", g.gl_pathv[i]);
1564 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1565 if (cmdnum == I_CHOWN) {
1567 printf("Changing owner on %s\n",
1572 printf("Changing group on %s\n",
1576 err = do_setstat(conn, g.gl_pathv[i], aa);
1577 if (err != 0 && err_abort)
1582 printf("Remote working directory: %s\n", *pwd);
1585 if (!getcwd(path_buf, sizeof(path_buf))) {
1586 error("Couldn't get local cwd: %s", strerror(errno));
1590 printf("Local working directory: %s\n", path_buf);
1593 /* Processed below */
1599 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1602 showprogress = !showprogress;
1604 printf("Progress meter enabled\n");
1606 printf("Progress meter disabled\n");
1609 fatal("%d is not implemented", cmdnum);
1617 /* If an unignored error occurs in batch mode we should abort. */
1618 if (err_abort && err != 0)
1620 else if (cmdnum == I_QUIT)
1628 prompt(EditLine *el)
1633 /* Display entries in 'list' after skipping the first 'len' chars */
1635 complete_display(char **list, u_int len)
1637 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1641 /* Count entries for sort and find longest */
1642 for (y = 0; list[y]; y++)
1643 m = MAX(m, strlen(list[y]));
1645 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1648 m = m > len ? m - len : 0;
1649 columns = width / (m + 2);
1650 columns = MAX(columns, 1);
1651 colspace = width / columns;
1652 colspace = MIN(colspace, width);
1656 for (y = 0; list[y]; y++) {
1657 llen = strlen(list[y]);
1658 tmp = llen > len ? list[y] + len : "";
1659 printf("%-*s", colspace, tmp);
1670 * Given a "list" of words that begin with a common prefix of "word",
1671 * attempt to find an autocompletion to extends "word" by the next
1672 * characters common to all entries in "list".
1675 complete_ambiguous(const char *word, char **list, size_t count)
1681 u_int y, matchlen = strlen(list[0]);
1683 /* Find length of common stem */
1684 for (y = 1; list[y]; y++) {
1687 for (x = 0; x < matchlen; x++)
1688 if (list[0][x] != list[y][x])
1694 if (matchlen > strlen(word)) {
1695 char *tmp = xstrdup(list[0]);
1697 tmp[matchlen] = '\0';
1702 return xstrdup(word);
1705 /* Autocomplete a sftp command */
1707 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1710 u_int y, count = 0, cmdlen, tmplen;
1711 char *tmp, **list, argterm[3];
1714 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1716 /* No command specified: display all available commands */
1718 for (y = 0; cmds[y].c; y++)
1719 list[count++] = xstrdup(cmds[y].c);
1722 complete_display(list, 0);
1724 for (y = 0; list[y] != NULL; y++)
1730 /* Prepare subset of commands that start with "cmd" */
1731 cmdlen = strlen(cmd);
1732 for (y = 0; cmds[y].c; y++) {
1733 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1734 list[count++] = xstrdup(cmds[y].c);
1743 /* Complete ambigious command */
1744 tmp = complete_ambiguous(cmd, list, count);
1746 complete_display(list, 0);
1748 for (y = 0; list[y]; y++)
1753 tmplen = strlen(tmp);
1754 cmdlen = strlen(cmd);
1755 /* If cmd may be extended then do so */
1756 if (tmplen > cmdlen)
1757 if (el_insertstr(el, tmp + cmdlen) == -1)
1758 fatal("el_insertstr failed.");
1760 /* Terminate argument cleanly */
1764 argterm[y++] = quote;
1765 if (lastarg || *(lf->cursor) != ' ')
1768 if (y > 0 && el_insertstr(el, argterm) == -1)
1769 fatal("el_insertstr failed.");
1778 * Determine whether a particular sftp command's arguments (if any)
1779 * represent local or remote files.
1782 complete_is_remote(char *cmd) {
1788 for (i = 0; cmds[i].c; i++) {
1789 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1796 /* Autocomplete a filename "file" */
1798 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1799 char *file, int remote, int lastarg, char quote, int terminated)
1802 char *tmp, *tmp2, ins[8];
1803 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1807 /* Glob from "file" location */
1811 xasprintf(&tmp, "%s*", file);
1813 /* Check if the path is absolute. */
1814 isabs = tmp[0] == '/';
1816 memset(&g, 0, sizeof(g));
1817 if (remote != LOCAL) {
1818 tmp = make_absolute(tmp, remote_path);
1819 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1821 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1823 /* Determine length of pwd so we can trim completion display */
1824 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1825 /* Terminate counting on first unescaped glob metacharacter */
1826 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1827 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1831 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1833 if (tmp[tmplen] == '/')
1834 pwdlen = tmplen + 1; /* track last seen '/' */
1838 if (g.gl_matchc == 0)
1841 if (g.gl_matchc > 1)
1842 complete_display(g.gl_pathv, pwdlen);
1845 /* Don't try to extend globs */
1846 if (file == NULL || hadglob)
1849 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1850 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1856 tmplen = strlen(tmp);
1857 filelen = strlen(file);
1859 /* Count the number of escaped characters in the input string. */
1861 for (i = 0; i < filelen; i++) {
1862 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1869 if (tmplen > (filelen - cesc)) {
1870 tmp2 = tmp + filelen - cesc;
1872 /* quote argument on way out */
1873 for (i = 0; i < len; i += clen) {
1874 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1875 (size_t)clen > sizeof(ins) - 2)
1876 fatal("invalid multibyte character");
1878 memcpy(ins + 1, tmp2 + i, clen);
1879 ins[clen + 1] = '\0';
1889 if (quote == '\0' || tmp2[i] == quote) {
1890 if (el_insertstr(el, ins) == -1)
1891 fatal("el_insertstr "
1897 if (el_insertstr(el, ins + 1) == -1)
1898 fatal("el_insertstr failed.");
1905 if (g.gl_matchc == 1) {
1909 if (*(lf->cursor - 1) != '/' &&
1910 (lastarg || *(lf->cursor) != ' '))
1913 if (i > 0 && el_insertstr(el, ins) == -1)
1914 fatal("el_insertstr failed.");
1923 /* tab-completion hook function, called via libedit */
1924 static unsigned char
1925 complete(EditLine *el, int ch)
1927 char **argv, *line, quote;
1929 u_int cursor, len, terminated, ret = CC_ERROR;
1931 struct complete_ctx *complete_ctx;
1934 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1935 fatal("%s: el_get failed", __func__);
1937 /* Figure out which argument the cursor points to */
1938 cursor = lf->cursor - lf->buffer;
1939 line = (char *)xmalloc(cursor + 1);
1940 memcpy(line, lf->buffer, cursor);
1941 line[cursor] = '\0';
1942 argv = makeargv(line, &carg, 1, "e, &terminated);
1945 /* Get all the arguments on the line */
1946 len = lf->lastchar - lf->buffer;
1947 line = (char *)xmalloc(len + 1);
1948 memcpy(line, lf->buffer, len);
1950 argv = makeargv(line, &argc, 1, NULL, NULL);
1952 /* Ensure cursor is at EOL or a argument boundary */
1953 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1954 line[cursor] != '\n') {
1960 /* Show all available commands */
1961 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1963 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1964 /* Handle the command parsing */
1965 if (complete_cmd_parse(el, argv[0], argc == carg,
1966 quote, terminated) != 0)
1968 } else if (carg >= 1) {
1969 /* Handle file parsing */
1970 int remote = complete_is_remote(argv[0]);
1971 char *filematch = NULL;
1973 if (carg > 1 && line[cursor-1] != ' ')
1974 filematch = argv[carg - 1];
1977 complete_match(el, complete_ctx->conn,
1978 *complete_ctx->remote_pathp, filematch,
1979 remote, carg == argc, quote, terminated) != 0)
1986 #endif /* USE_LIBEDIT */
1989 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1994 int err, interactive;
1995 EditLine *el = NULL;
1999 extern char *__progname;
2000 struct complete_ctx complete_ctx;
2002 if (!batchmode && isatty(STDIN_FILENO)) {
2003 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2004 fatal("Couldn't initialise editline");
2005 if ((hl = history_init()) == NULL)
2006 fatal("Couldn't initialise editline history");
2007 history(hl, &hev, H_SETSIZE, 100);
2008 el_set(el, EL_HIST, history, hl);
2010 el_set(el, EL_PROMPT, prompt);
2011 el_set(el, EL_EDITOR, "emacs");
2012 el_set(el, EL_TERMINAL, NULL);
2013 el_set(el, EL_SIGNAL, 1);
2014 el_source(el, NULL);
2016 /* Tab Completion */
2017 el_set(el, EL_ADDFN, "ftp-complete",
2018 "Context sensitive argument completion", complete);
2019 complete_ctx.conn = conn;
2020 complete_ctx.remote_pathp = &remote_path;
2021 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2022 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2023 /* enable ctrl-left-arrow and ctrl-right-arrow */
2024 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2025 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2026 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2027 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2028 /* make ^w match ksh behaviour */
2029 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2031 #endif /* USE_LIBEDIT */
2033 remote_path = do_realpath(conn, ".");
2034 if (remote_path == NULL)
2037 if (file1 != NULL) {
2038 dir = xstrdup(file1);
2039 dir = make_absolute(dir, remote_path);
2041 if (remote_is_dir(conn, dir) && file2 == NULL) {
2043 printf("Changing to: %s\n", dir);
2044 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2045 if (parse_dispatch_command(conn, cmd,
2046 &remote_path, 1) != 0) {
2053 /* XXX this is wrong wrt quoting */
2054 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2055 global_aflag ? " -a" : "", dir,
2056 file2 == NULL ? "" : " ",
2057 file2 == NULL ? "" : file2);
2058 err = parse_dispatch_command(conn, cmd,
2071 interactive = !batchmode && isatty(STDIN_FILENO);
2076 signal(SIGINT, SIG_IGN);
2081 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2086 if (!interactive) { /* Echo command */
2087 printf("sftp> %s", cmd);
2088 if (strlen(cmd) > 0 &&
2089 cmd[strlen(cmd) - 1] != '\n')
2097 if ((line = el_gets(el, &count)) == NULL ||
2102 history(hl, &hev, H_ENTER, line);
2103 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2104 fprintf(stderr, "Error: input line too long\n");
2107 #endif /* USE_LIBEDIT */
2110 cp = strrchr(cmd, '\n');
2114 /* Handle user interrupts gracefully during commands */
2116 signal(SIGINT, cmd_interrupt);
2118 err = parse_dispatch_command(conn, cmd, &remote_path,
2129 #endif /* USE_LIBEDIT */
2131 /* err == 1 signifies normal "quit" exit */
2132 return (err >= 0 ? 0 : -1);
2136 connect_to_server(char *path, char **args, int *in, int *out)
2141 int pin[2], pout[2];
2143 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2144 fatal("pipe: %s", strerror(errno));
2149 #else /* USE_PIPES */
2152 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2153 fatal("socketpair: %s", strerror(errno));
2154 *in = *out = inout[0];
2155 c_in = c_out = inout[1];
2156 #endif /* USE_PIPES */
2158 if ((sshpid = fork()) == -1)
2159 fatal("fork: %s", strerror(errno));
2160 else if (sshpid == 0) {
2161 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2162 (dup2(c_out, STDOUT_FILENO) == -1)) {
2163 fprintf(stderr, "dup2: %s\n", strerror(errno));
2172 * The underlying ssh is in the same process group, so we must
2173 * ignore SIGINT if we want to gracefully abort commands,
2174 * otherwise the signal will make it to the ssh process and
2175 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2176 * underlying ssh, it must *not* ignore that signal.
2178 signal(SIGINT, SIG_IGN);
2179 signal(SIGTERM, SIG_DFL);
2181 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2185 signal(SIGTERM, killchild);
2186 signal(SIGINT, killchild);
2187 signal(SIGHUP, killchild);
2195 extern char *__progname;
2198 "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2199 " [-D sftp_server_path] [-F ssh_config] "
2200 "[-i identity_file] [-l limit]\n"
2201 " [-o ssh_option] [-P port] [-R num_requests] "
2203 " [-s subsystem | sftp_server] host\n"
2204 " %s [user@]host[:file ...]\n"
2205 " %s [user@]host[:dir[/]]\n"
2206 " %s -b batchfile [user@]host\n",
2207 __progname, __progname, __progname, __progname);
2212 main(int argc, char **argv)
2214 int in, out, ch, err;
2215 char *host = NULL, *userhost, *cp, *file2 = NULL;
2216 int debug_level = 0, sshver = 2;
2217 char *file1 = NULL, *sftp_server = NULL;
2218 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2220 LogLevel ll = SYSLOG_LEVEL_INFO;
2223 extern char *optarg;
2224 struct sftp_conn *conn;
2225 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2226 size_t num_requests = DEFAULT_NUM_REQUESTS;
2227 long long limit_kbps = 0;
2229 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2231 setlocale(LC_CTYPE, "");
2233 __progname = ssh_get_progname(argv[0]);
2234 memset(&args, '\0', sizeof(args));
2236 addargs(&args, "%s", ssh_program);
2237 addargs(&args, "-oForwardX11 no");
2238 addargs(&args, "-oForwardAgent no");
2239 addargs(&args, "-oPermitLocalCommand no");
2240 addargs(&args, "-oClearAllForwardings yes");
2242 ll = SYSLOG_LEVEL_INFO;
2245 while ((ch = getopt(argc, argv,
2246 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2248 /* Passed through to ssh(1) */
2252 addargs(&args, "-%c", ch);
2254 /* Passed through to ssh(1) with argument */
2259 addargs(&args, "-%c", ch);
2260 addargs(&args, "%s", optarg);
2263 ll = SYSLOG_LEVEL_ERROR;
2266 addargs(&args, "-%c", ch);
2269 addargs(&args, "-oPort %s", optarg);
2272 if (debug_level < 3) {
2273 addargs(&args, "-v");
2274 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2280 if (sftp_server == NULL)
2281 sftp_server = _PATH_SFTP_SERVER;
2290 copy_buffer_len = strtol(optarg, &cp, 10);
2291 if (copy_buffer_len == 0 || *cp != '\0')
2292 fatal("Invalid buffer size \"%s\"", optarg);
2296 fatal("Batch file already specified.");
2298 /* Allow "-" as stdin */
2299 if (strcmp(optarg, "-") != 0 &&
2300 (infile = fopen(optarg, "r")) == NULL)
2301 fatal("%s (%s).", strerror(errno), optarg);
2303 quiet = batchmode = 1;
2304 addargs(&args, "-obatchmode yes");
2313 sftp_direct = optarg;
2316 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2320 limit_kbps *= 1024; /* kbps */
2326 num_requests = strtol(optarg, &cp, 10);
2327 if (num_requests == 0 || *cp != '\0')
2328 fatal("Invalid number of requests \"%s\"",
2332 sftp_server = optarg;
2335 ssh_program = optarg;
2336 replacearg(&args, 0, "%s", ssh_program);
2344 if (!isatty(STDERR_FILENO))
2347 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2349 if (sftp_direct == NULL) {
2350 if (optind == argc || argc > (optind + 2))
2353 userhost = xstrdup(argv[optind]);
2354 file2 = argv[optind+1];
2356 if ((host = strrchr(userhost, '@')) == NULL)
2361 fprintf(stderr, "Missing username\n");
2364 addargs(&args, "-l");
2365 addargs(&args, "%s", userhost);
2368 if ((cp = colon(host)) != NULL) {
2373 host = cleanhostname(host);
2375 fprintf(stderr, "Missing hostname\n");
2379 addargs(&args, "-oProtocol %d", sshver);
2381 /* no subsystem if the server-spec contains a '/' */
2382 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2383 addargs(&args, "-s");
2385 addargs(&args, "--");
2386 addargs(&args, "%s", host);
2387 addargs(&args, "%s", (sftp_server != NULL ?
2388 sftp_server : "sftp"));
2390 connect_to_server(ssh_program, args.list, &in, &out);
2393 addargs(&args, "sftp-server");
2395 connect_to_server(sftp_direct, args.list, &in, &out);
2399 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2401 fatal("Couldn't initialise connection to server");
2404 if (sftp_direct == NULL)
2405 fprintf(stderr, "Connected to %s.\n", host);
2407 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2410 err = interactive_loop(conn, file1, file2);
2412 #if !defined(USE_PIPES)
2413 shutdown(in, SHUT_RDWR);
2414 shutdown(out, SHUT_RDWR);
2422 while (waitpid(sshpid, NULL, 0) == -1)
2424 fatal("Couldn't wait for ssh process: %s",
2427 exit(err == 0 ? 0 : 1);