1 /* $OpenBSD: sftp.c,v 1.164 2014/07/09 01:45:10 djm 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 256 /* # 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 or upload 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 */
171 /* Type of completion */
176 static const struct CMD cmds[] = {
177 { "bye", I_QUIT, NOARGS },
178 { "cd", I_CHDIR, REMOTE },
179 { "chdir", I_CHDIR, REMOTE },
180 { "chgrp", I_CHGRP, REMOTE },
181 { "chmod", I_CHMOD, REMOTE },
182 { "chown", I_CHOWN, REMOTE },
183 { "df", I_DF, REMOTE },
184 { "dir", I_LS, REMOTE },
185 { "exit", I_QUIT, NOARGS },
186 { "get", I_GET, REMOTE },
187 { "help", I_HELP, NOARGS },
188 { "lcd", I_LCHDIR, LOCAL },
189 { "lchdir", I_LCHDIR, LOCAL },
190 { "lls", I_LLS, LOCAL },
191 { "lmkdir", I_LMKDIR, LOCAL },
192 { "ln", I_LINK, REMOTE },
193 { "lpwd", I_LPWD, LOCAL },
194 { "ls", I_LS, REMOTE },
195 { "lumask", I_LUMASK, NOARGS },
196 { "mkdir", I_MKDIR, REMOTE },
197 { "mget", I_GET, REMOTE },
198 { "mput", I_PUT, LOCAL },
199 { "progress", I_PROGRESS, NOARGS },
200 { "put", I_PUT, LOCAL },
201 { "pwd", I_PWD, REMOTE },
202 { "quit", I_QUIT, NOARGS },
203 { "reget", I_REGET, REMOTE },
204 { "rename", I_RENAME, REMOTE },
205 { "reput", I_REPUT, LOCAL },
206 { "rm", I_RM, REMOTE },
207 { "rmdir", I_RMDIR, REMOTE },
208 { "symlink", I_SYMLINK, REMOTE },
209 { "version", I_VERSION, NOARGS },
210 { "!", I_SHELL, NOARGS },
211 { "?", I_HELP, NOARGS },
215 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
222 kill(sshpid, SIGTERM);
223 waitpid(sshpid, NULL, 0);
231 cmd_interrupt(int signo)
233 const char msg[] = "\rInterrupt \n";
234 int olderrno = errno;
236 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
244 printf("Available commands:\n"
246 "cd path Change remote directory to 'path'\n"
247 "chgrp grp path Change group of file 'path' to 'grp'\n"
248 "chmod mode path Change permissions of file 'path' to 'mode'\n"
249 "chown own path Change owner of file 'path' to 'own'\n"
250 "df [-hi] [path] Display statistics for current directory or\n"
251 " filesystem containing 'path'\n"
253 "get [-Ppr] remote [local] Download file\n"
254 "reget remote [local] Resume download file\n"
255 "reput [local] remote Resume upload file\n"
256 "help Display this help text\n"
257 "lcd path Change local directory to 'path'\n"
258 "lls [ls-options [path]] Display local directory listing\n"
259 "lmkdir path Create local directory\n"
260 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
261 "lpwd Print local working directory\n"
262 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
263 "lumask umask Set local umask to 'umask'\n"
264 "mkdir path Create remote directory\n"
265 "progress Toggle display of progress meter\n"
266 "put [-Ppr] local [remote] Upload file\n"
267 "pwd Display remote working directory\n"
269 "rename oldpath newpath Rename remote file\n"
270 "rm path Delete remote file\n"
271 "rmdir path Remove remote directory\n"
272 "symlink oldpath newpath Symlink remote file\n"
273 "version Show SFTP version\n"
274 "!command Execute 'command' in local shell\n"
275 "! Escape to local shell\n"
276 "? Synonym for help\n");
280 local_do_shell(const char *args)
289 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
290 shell = _PATH_BSHELL;
292 if ((pid = fork()) == -1)
293 fatal("Couldn't fork: %s", strerror(errno));
296 /* XXX: child has pipe fds to ssh subproc open - issue? */
298 debug3("Executing %s -c \"%s\"", shell, args);
299 execl(shell, shell, "-c", args, (char *)NULL);
301 debug3("Executing %s", shell);
302 execl(shell, shell, (char *)NULL);
304 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
308 while (waitpid(pid, &status, 0) == -1)
310 fatal("Couldn't wait for child: %s", strerror(errno));
311 if (!WIFEXITED(status))
312 error("Shell exited abnormally");
313 else if (WEXITSTATUS(status))
314 error("Shell exited with status %d", WEXITSTATUS(status));
318 local_do_ls(const char *args)
321 local_do_shell(_PATH_LS);
323 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
324 char *buf = xmalloc(len);
326 /* XXX: quoting - rip quoting code from ftp? */
327 snprintf(buf, len, _PATH_LS " %s", args);
333 /* Strip one path (usually the pwd) from the start of another */
335 path_strip(char *path, char *strip)
340 return (xstrdup(path));
343 if (strncmp(path, strip, len) == 0) {
344 if (strip[len - 1] != '/' && path[len] == '/')
346 return (xstrdup(path + len));
349 return (xstrdup(path));
353 make_absolute(char *p, char *pwd)
358 if (p && p[0] != '/') {
359 abs_str = path_append(pwd, p);
367 parse_getput_flags(const char *cmd, char **argv, int argc,
368 int *aflag, int *fflag, int *pflag, int *rflag)
370 extern int opterr, optind, optopt, optreset;
373 optind = optreset = 1;
376 *aflag = *fflag = *rflag = *pflag = 0;
377 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
394 error("%s: Invalid flag -%c", cmd, optopt);
403 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
405 extern int opterr, optind, optopt, optreset;
408 optind = optreset = 1;
412 while ((ch = getopt(argc, argv, "s")) != -1) {
418 error("%s: Invalid flag -%c", cmd, optopt);
427 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
429 extern int opterr, optind, optopt, optreset;
432 optind = optreset = 1;
436 while ((ch = getopt(argc, argv, "l")) != -1) {
442 error("%s: Invalid flag -%c", cmd, optopt);
451 parse_ls_flags(char **argv, int argc, int *lflag)
453 extern int opterr, optind, optopt, optreset;
456 optind = optreset = 1;
459 *lflag = LS_NAME_SORT;
460 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
463 *lflag &= ~VIEW_FLAGS;
464 *lflag |= LS_SHORT_VIEW;
467 *lflag &= ~SORT_FLAGS;
468 *lflag |= LS_SIZE_SORT;
471 *lflag |= LS_SHOW_ALL;
474 *lflag &= ~SORT_FLAGS;
477 *lflag |= LS_SI_UNITS;
480 *lflag &= ~LS_SHORT_VIEW;
481 *lflag |= LS_LONG_VIEW;
484 *lflag &= ~LS_SHORT_VIEW;
485 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
488 *lflag |= LS_REVERSE_SORT;
491 *lflag &= ~SORT_FLAGS;
492 *lflag |= LS_TIME_SORT;
495 error("ls: Invalid flag -%c", optopt);
504 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
506 extern int opterr, optind, optopt, optreset;
509 optind = optreset = 1;
513 while ((ch = getopt(argc, argv, "hi")) != -1) {
522 error("%s: Invalid flag -%c", cmd, optopt);
531 parse_no_flags(const char *cmd, char **argv, int argc)
533 extern int opterr, optind, optopt, optreset;
536 optind = optreset = 1;
539 while ((ch = getopt(argc, argv, "")) != -1) {
542 error("%s: Invalid flag -%c", cmd, optopt);
555 /* XXX: report errors? */
556 if (stat(path, &sb) == -1)
559 return(S_ISDIR(sb.st_mode));
563 remote_is_dir(struct sftp_conn *conn, char *path)
567 /* XXX: report errors? */
568 if ((a = do_stat(conn, path, 1)) == NULL)
570 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
572 return(S_ISDIR(a->perm));
575 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
577 pathname_is_dir(char *pathname)
579 size_t l = strlen(pathname);
581 return l > 0 && pathname[l - 1] == '/';
585 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
586 int pflag, int rflag, int resume, int fflag)
588 char *abs_src = NULL;
589 char *abs_dst = NULL;
591 char *filename, *tmp=NULL;
594 abs_src = xstrdup(src);
595 abs_src = make_absolute(abs_src, pwd);
596 memset(&g, 0, sizeof(g));
598 debug3("Looking up %s", abs_src);
599 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
600 if (r == GLOB_NOSPACE) {
601 error("Too many matches for \"%s\".", abs_src);
603 error("File \"%s\" not found.", abs_src);
610 * If multiple matches then dst must be a directory or
613 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
614 error("Multiple source paths, but destination "
615 "\"%s\" is not a directory", dst);
620 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
621 tmp = xstrdup(g.gl_pathv[i]);
622 if ((filename = basename(tmp)) == NULL) {
623 error("basename %s: %s", tmp, strerror(errno));
629 if (g.gl_matchc == 1 && dst) {
631 abs_dst = path_append(dst, filename);
633 abs_dst = xstrdup(dst);
636 abs_dst = path_append(dst, filename);
638 abs_dst = xstrdup(filename);
642 resume |= global_aflag;
643 if (!quiet && resume)
644 printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
645 else if (!quiet && !resume)
646 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
647 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
648 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
649 pflag || global_pflag, 1, resume,
650 fflag || global_fflag) == -1)
653 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
654 pflag || global_pflag, resume,
655 fflag || global_fflag) == -1)
669 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
670 int pflag, int rflag, int resume, int fflag)
672 char *tmp_dst = NULL;
673 char *abs_dst = NULL;
674 char *tmp = NULL, *filename = NULL;
677 int i, dst_is_dir = 1;
681 tmp_dst = xstrdup(dst);
682 tmp_dst = make_absolute(tmp_dst, pwd);
685 memset(&g, 0, sizeof(g));
686 debug3("Looking up %s", src);
687 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
688 error("File \"%s\" not found.", src);
693 /* If we aren't fetching to pwd then stash this status for later */
695 dst_is_dir = remote_is_dir(conn, tmp_dst);
697 /* If multiple matches, dst may be directory or unspecified */
698 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
699 error("Multiple paths match, but destination "
700 "\"%s\" is not a directory", tmp_dst);
705 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
706 if (stat(g.gl_pathv[i], &sb) == -1) {
708 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
712 tmp = xstrdup(g.gl_pathv[i]);
713 if ((filename = basename(tmp)) == NULL) {
714 error("basename %s: %s", tmp, strerror(errno));
720 if (g.gl_matchc == 1 && tmp_dst) {
721 /* If directory specified, append filename */
723 abs_dst = path_append(tmp_dst, filename);
725 abs_dst = xstrdup(tmp_dst);
726 } else if (tmp_dst) {
727 abs_dst = path_append(tmp_dst, filename);
729 abs_dst = make_absolute(xstrdup(filename), pwd);
733 resume |= global_aflag;
734 if (!quiet && resume)
735 printf("Resuming upload of %s to %s\n", g.gl_pathv[i],
737 else if (!quiet && !resume)
738 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
739 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
740 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
741 pflag || global_pflag, 1, resume,
742 fflag || global_fflag) == -1)
745 if (do_upload(conn, g.gl_pathv[i], abs_dst,
746 pflag || global_pflag, resume,
747 fflag || global_fflag) == -1)
760 sdirent_comp(const void *aa, const void *bb)
762 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
763 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
764 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
766 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
767 if (sort_flag & LS_NAME_SORT)
768 return (rmul * strcmp(a->filename, b->filename));
769 else if (sort_flag & LS_TIME_SORT)
770 return (rmul * NCMP(a->a.mtime, b->a.mtime));
771 else if (sort_flag & LS_SIZE_SORT)
772 return (rmul * NCMP(a->a.size, b->a.size));
774 fatal("Unknown ls sort type");
777 /* sftp ls.1 replacement for directories */
779 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
782 u_int c = 1, colspace = 0, columns = 1;
785 if ((n = do_readdir(conn, path, &d)) != 0)
788 if (!(lflag & LS_SHORT_VIEW)) {
789 u_int m = 0, width = 80;
793 /* Count entries for sort and find longest filename */
794 for (n = 0; d[n] != NULL; n++) {
795 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
796 m = MAX(m, strlen(d[n]->filename));
799 /* Add any subpath that also needs to be counted */
800 tmp = path_strip(path, strip_path);
804 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
807 columns = width / (m + 2);
808 columns = MAX(columns, 1);
809 colspace = width / columns;
810 colspace = MIN(colspace, width);
813 if (lflag & SORT_FLAGS) {
814 for (n = 0; d[n] != NULL; n++)
815 ; /* count entries */
816 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
817 qsort(d, n, sizeof(*d), sdirent_comp);
820 for (n = 0; d[n] != NULL && !interrupted; n++) {
823 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
826 tmp = path_append(path, d[n]->filename);
827 fname = path_strip(tmp, strip_path);
830 if (lflag & LS_LONG_VIEW) {
831 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
835 memset(&sb, 0, sizeof(sb));
836 attrib_to_stat(&d[n]->a, &sb);
837 lname = ls_file(fname, &sb, 1,
838 (lflag & LS_SI_UNITS));
839 printf("%s\n", lname);
842 printf("%s\n", d[n]->longname);
844 printf("%-*s", colspace, fname);
855 if (!(lflag & LS_LONG_VIEW) && (c != 1))
858 free_sftp_dirents(d);
862 /* sftp ls.1 replacement which handles path globs */
864 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
871 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
873 memset(&g, 0, sizeof(g));
875 if ((r = remote_glob(conn, path,
876 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
878 (g.gl_pathc && !g.gl_matchc)) {
881 if (r == GLOB_NOSPACE) {
882 error("Can't ls: Too many matches for \"%s\"", path);
884 error("Can't ls: \"%s\" not found", path);
893 * If the glob returns a single match and it is a directory,
894 * then just list its contents.
896 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
897 S_ISDIR(g.gl_statv[0]->st_mode)) {
898 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
903 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
906 if (!(lflag & LS_SHORT_VIEW)) {
907 /* Count entries for sort and find longest filename */
908 for (i = 0; g.gl_pathv[i]; i++)
909 m = MAX(m, strlen(g.gl_pathv[i]));
911 columns = width / (m + 2);
912 columns = MAX(columns, 1);
913 colspace = width / columns;
916 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
917 fname = path_strip(g.gl_pathv[i], strip_path);
918 if (lflag & LS_LONG_VIEW) {
919 if (g.gl_statv[i] == NULL) {
920 error("no stat information for %s", fname);
923 lname = ls_file(fname, g.gl_statv[i], 1,
924 (lflag & LS_SI_UNITS));
925 printf("%s\n", lname);
928 printf("%-*s", colspace, fname);
938 if (!(lflag & LS_LONG_VIEW) && (c != 1))
949 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
951 struct sftp_statvfs st;
952 char s_used[FMT_SCALED_STRSIZE];
953 char s_avail[FMT_SCALED_STRSIZE];
954 char s_root[FMT_SCALED_STRSIZE];
955 char s_total[FMT_SCALED_STRSIZE];
956 unsigned long long ffree;
958 if (do_statvfs(conn, path, &st, 1) == -1)
961 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
962 printf(" Inodes Used Avail "
963 "(root) %%Capacity\n");
964 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
965 (unsigned long long)st.f_files,
966 (unsigned long long)(st.f_files - st.f_ffree),
967 (unsigned long long)st.f_favail,
968 (unsigned long long)st.f_ffree, ffree);
970 strlcpy(s_used, "error", sizeof(s_used));
971 strlcpy(s_avail, "error", sizeof(s_avail));
972 strlcpy(s_root, "error", sizeof(s_root));
973 strlcpy(s_total, "error", sizeof(s_total));
974 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
975 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
976 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
977 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
978 printf(" Size Used Avail (root) %%Capacity\n");
979 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
980 s_total, s_used, s_avail, s_root,
981 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
984 printf(" Size Used Avail "
985 "(root) %%Capacity\n");
986 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
987 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
988 (unsigned long long)(st.f_frsize *
989 (st.f_blocks - st.f_bfree) / 1024),
990 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
991 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
992 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
999 * Undo escaping of glob sequences in place. Used to undo extra escaping
1000 * applied in makeargv() when the string is destined for a function that
1004 undo_glob_escape(char *s)
1039 * Split a string into an argument vector using sh(1)-style quoting,
1040 * comment and escaping rules, but with some tweaks to handle glob(3)
1042 * The "sloppy" flag allows for recovery from missing terminating quote, for
1043 * use in parsing incomplete commandlines during tab autocompletion.
1045 * Returns NULL on error or a NULL-terminated array of arguments.
1047 * If "lastquote" is not NULL, the quoting character used for the last
1048 * argument is placed in *lastquote ("\0", "'" or "\"").
1050 * If "terminated" is not NULL, *terminated will be set to 1 when the
1051 * last argument's quote has been properly terminated or 0 otherwise.
1052 * This parameter is only of use if "sloppy" is set.
1055 #define MAXARGLEN 8192
1057 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1062 static char argvs[MAXARGLEN];
1063 static char *argv[MAXARGS + 1];
1064 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1067 if (strlen(arg) > sizeof(argvs) - 1) {
1069 error("string too long");
1072 if (terminated != NULL)
1074 if (lastquote != NULL)
1079 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1080 error("Too many arguments.");
1083 if (isspace((unsigned char)arg[i])) {
1084 if (state == MA_UNQUOTED) {
1085 /* Terminate current argument */
1089 } else if (state != MA_START)
1090 argvs[j++] = arg[i];
1091 } else if (arg[i] == '"' || arg[i] == '\'') {
1092 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1093 if (state == MA_START) {
1094 argv[argc] = argvs + j;
1096 if (lastquote != NULL)
1097 *lastquote = arg[i];
1098 } else if (state == MA_UNQUOTED)
1100 else if (state == q)
1101 state = MA_UNQUOTED;
1103 argvs[j++] = arg[i];
1104 } else if (arg[i] == '\\') {
1105 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1106 quot = state == MA_SQUOTE ? '\'' : '"';
1107 /* Unescape quote we are in */
1108 /* XXX support \n and friends? */
1109 if (arg[i + 1] == quot) {
1111 argvs[j++] = arg[i];
1112 } else if (arg[i + 1] == '?' ||
1113 arg[i + 1] == '[' || arg[i + 1] == '*') {
1115 * Special case for sftp: append
1116 * double-escaped glob sequence -
1117 * glob will undo one level of
1118 * escaping. NB. string can grow here.
1120 if (j >= sizeof(argvs) - 5)
1121 goto args_too_longs;
1123 argvs[j++] = arg[i++];
1125 argvs[j++] = arg[i];
1127 argvs[j++] = arg[i++];
1128 argvs[j++] = arg[i];
1131 if (state == MA_START) {
1132 argv[argc] = argvs + j;
1133 state = MA_UNQUOTED;
1134 if (lastquote != NULL)
1137 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1138 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1140 * Special case for sftp: append
1141 * escaped glob sequence -
1142 * glob will undo one level of
1145 argvs[j++] = arg[i++];
1146 argvs[j++] = arg[i];
1148 /* Unescape everything */
1149 /* XXX support \n and friends? */
1151 argvs[j++] = arg[i];
1154 } else if (arg[i] == '#') {
1155 if (state == MA_SQUOTE || state == MA_DQUOTE)
1156 argvs[j++] = arg[i];
1159 } else if (arg[i] == '\0') {
1160 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1162 state = MA_UNQUOTED;
1163 if (terminated != NULL)
1167 error("Unterminated quoted argument");
1171 if (state == MA_UNQUOTED) {
1177 if (state == MA_START) {
1178 argv[argc] = argvs + j;
1179 state = MA_UNQUOTED;
1180 if (lastquote != NULL)
1183 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1184 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1186 * Special case for sftp: escape quoted
1187 * glob(3) wildcards. NB. string can grow
1190 if (j >= sizeof(argvs) - 3)
1191 goto args_too_longs;
1193 argvs[j++] = arg[i];
1195 argvs[j++] = arg[i];
1204 parse_args(const char **cpp, int *ignore_errors, int *aflag,
1205 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1206 int *rflag, int *sflag,
1207 unsigned long *n_arg, char **path1, char **path2)
1209 const char *cmd, *cp = *cpp;
1213 int i, cmdnum, optidx, argc;
1215 /* Skip leading whitespace */
1216 cp = cp + strspn(cp, WHITESPACE);
1218 /* Check for leading '-' (disable error processing) */
1223 cp = cp + strspn(cp, WHITESPACE);
1226 /* Ignore blank lines and lines which begin with comment '#' char */
1227 if (*cp == '\0' || *cp == '#')
1230 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1233 /* Figure out which command we have */
1234 for (i = 0; cmds[i].c != NULL; i++) {
1235 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1245 } else if (cmdnum == -1) {
1246 error("Invalid command.");
1250 /* Get arguments and parse flags */
1251 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1252 *rflag = *sflag = 0;
1253 *path1 = *path2 = NULL;
1260 if ((optidx = parse_getput_flags(cmd, argv, argc,
1261 aflag, fflag, pflag, rflag)) == -1)
1263 /* Get first pathname (mandatory) */
1264 if (argc - optidx < 1) {
1265 error("You must specify at least one path after a "
1266 "%s command.", cmd);
1269 *path1 = xstrdup(argv[optidx]);
1270 /* Get second pathname (optional) */
1271 if (argc - optidx > 1) {
1272 *path2 = xstrdup(argv[optidx + 1]);
1273 /* Destination is not globbed */
1274 undo_glob_escape(*path2);
1278 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1280 goto parse_two_paths;
1282 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1284 goto parse_two_paths;
1286 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1289 if (argc - optidx < 2) {
1290 error("You must specify two paths after a %s "
1294 *path1 = xstrdup(argv[optidx]);
1295 *path2 = xstrdup(argv[optidx + 1]);
1296 /* Paths are not globbed */
1297 undo_glob_escape(*path1);
1298 undo_glob_escape(*path2);
1306 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1308 /* Get pathname (mandatory) */
1309 if (argc - optidx < 1) {
1310 error("You must specify a path after a %s command.",
1314 *path1 = xstrdup(argv[optidx]);
1315 /* Only "rm" globs */
1317 undo_glob_escape(*path1);
1320 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1323 /* Default to current directory if no path specified */
1324 if (argc - optidx < 1)
1327 *path1 = xstrdup(argv[optidx]);
1328 undo_glob_escape(*path1);
1332 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1334 /* Path is optional */
1335 if (argc - optidx > 0)
1336 *path1 = xstrdup(argv[optidx]);
1339 /* Skip ls command and following whitespace */
1340 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1342 /* Uses the rest of the line */
1349 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1351 /* Get numeric arg (mandatory) */
1352 if (argc - optidx < 1)
1355 l = strtol(argv[optidx], &cp2, base);
1356 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1357 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1360 error("You must supply a numeric argument "
1361 "to the %s command.", cmd);
1365 if (cmdnum == I_LUMASK)
1367 /* Get pathname (mandatory) */
1368 if (argc - optidx < 2) {
1369 error("You must specify a path after a %s command.",
1373 *path1 = xstrdup(argv[optidx + 1]);
1381 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1385 fatal("Command not implemented");
1393 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1396 char *path1, *path2, *tmp;
1397 int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0,
1399 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1401 unsigned long n_arg = 0;
1403 char path_buf[MAXPATHLEN];
1407 path1 = path2 = NULL;
1408 cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1409 &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1410 if (ignore_errors != 0)
1413 memset(&g, 0, sizeof(g));
1415 /* Perform command */
1421 /* Unrecognized command */
1428 err = process_get(conn, path1, path2, *pwd, pflag,
1429 rflag, aflag, fflag);
1435 err = process_put(conn, path1, path2, *pwd, pflag,
1436 rflag, aflag, fflag);
1439 path1 = make_absolute(path1, *pwd);
1440 path2 = make_absolute(path2, *pwd);
1441 err = do_rename(conn, path1, path2, lflag);
1447 path1 = make_absolute(path1, *pwd);
1448 path2 = make_absolute(path2, *pwd);
1449 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1452 path1 = make_absolute(path1, *pwd);
1453 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1454 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1456 printf("Removing %s\n", g.gl_pathv[i]);
1457 err = do_rm(conn, g.gl_pathv[i]);
1458 if (err != 0 && err_abort)
1463 path1 = make_absolute(path1, *pwd);
1465 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1467 err = do_mkdir(conn, path1, &a, 1);
1470 path1 = make_absolute(path1, *pwd);
1471 err = do_rmdir(conn, path1);
1474 path1 = make_absolute(path1, *pwd);
1475 if ((tmp = do_realpath(conn, path1)) == NULL) {
1479 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1484 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1485 error("Can't change directory: Can't check target");
1490 if (!S_ISDIR(aa->perm)) {
1491 error("Can't change directory: \"%s\" is not "
1492 "a directory", tmp);
1502 do_ls_dir(conn, *pwd, *pwd, lflag);
1506 /* Strip pwd off beginning of non-absolute paths */
1511 path1 = make_absolute(path1, *pwd);
1512 err = do_globbed_ls(conn, path1, tmp, lflag);
1515 /* Default to current directory if no path specified */
1517 path1 = xstrdup(*pwd);
1518 path1 = make_absolute(path1, *pwd);
1519 err = do_df(conn, path1, hflag, iflag);
1522 if (chdir(path1) == -1) {
1523 error("Couldn't change local directory to "
1524 "\"%s\": %s", path1, strerror(errno));
1529 if (mkdir(path1, 0777) == -1) {
1530 error("Couldn't create local directory "
1531 "\"%s\": %s", path1, strerror(errno));
1539 local_do_shell(cmd);
1543 printf("Local umask: %03lo\n", n_arg);
1546 path1 = make_absolute(path1, *pwd);
1548 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1550 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1551 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1553 printf("Changing mode on %s\n", g.gl_pathv[i]);
1554 err = do_setstat(conn, g.gl_pathv[i], &a);
1555 if (err != 0 && err_abort)
1561 path1 = make_absolute(path1, *pwd);
1562 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1563 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1564 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1571 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1572 error("Can't get current ownership of "
1573 "remote file \"%s\"", g.gl_pathv[i]);
1580 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1581 if (cmdnum == I_CHOWN) {
1583 printf("Changing owner on %s\n",
1588 printf("Changing group on %s\n",
1592 err = do_setstat(conn, g.gl_pathv[i], aa);
1593 if (err != 0 && err_abort)
1598 printf("Remote working directory: %s\n", *pwd);
1601 if (!getcwd(path_buf, sizeof(path_buf))) {
1602 error("Couldn't get local cwd: %s", strerror(errno));
1606 printf("Local working directory: %s\n", path_buf);
1609 /* Processed below */
1615 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1618 showprogress = !showprogress;
1620 printf("Progress meter enabled\n");
1622 printf("Progress meter disabled\n");
1625 fatal("%d is not implemented", cmdnum);
1633 /* If an unignored error occurs in batch mode we should abort. */
1634 if (err_abort && err != 0)
1636 else if (cmdnum == I_QUIT)
1644 prompt(EditLine *el)
1649 /* Display entries in 'list' after skipping the first 'len' chars */
1651 complete_display(char **list, u_int len)
1653 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1657 /* Count entries for sort and find longest */
1658 for (y = 0; list[y]; y++)
1659 m = MAX(m, strlen(list[y]));
1661 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1664 m = m > len ? m - len : 0;
1665 columns = width / (m + 2);
1666 columns = MAX(columns, 1);
1667 colspace = width / columns;
1668 colspace = MIN(colspace, width);
1672 for (y = 0; list[y]; y++) {
1673 llen = strlen(list[y]);
1674 tmp = llen > len ? list[y] + len : "";
1675 printf("%-*s", colspace, tmp);
1686 * Given a "list" of words that begin with a common prefix of "word",
1687 * attempt to find an autocompletion to extends "word" by the next
1688 * characters common to all entries in "list".
1691 complete_ambiguous(const char *word, char **list, size_t count)
1697 u_int y, matchlen = strlen(list[0]);
1699 /* Find length of common stem */
1700 for (y = 1; list[y]; y++) {
1703 for (x = 0; x < matchlen; x++)
1704 if (list[0][x] != list[y][x])
1710 if (matchlen > strlen(word)) {
1711 char *tmp = xstrdup(list[0]);
1713 tmp[matchlen] = '\0';
1718 return xstrdup(word);
1721 /* Autocomplete a sftp command */
1723 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1726 u_int y, count = 0, cmdlen, tmplen;
1727 char *tmp, **list, argterm[3];
1730 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1732 /* No command specified: display all available commands */
1734 for (y = 0; cmds[y].c; y++)
1735 list[count++] = xstrdup(cmds[y].c);
1738 complete_display(list, 0);
1740 for (y = 0; list[y] != NULL; y++)
1746 /* Prepare subset of commands that start with "cmd" */
1747 cmdlen = strlen(cmd);
1748 for (y = 0; cmds[y].c; y++) {
1749 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1750 list[count++] = xstrdup(cmds[y].c);
1759 /* Complete ambigious command */
1760 tmp = complete_ambiguous(cmd, list, count);
1762 complete_display(list, 0);
1764 for (y = 0; list[y]; y++)
1769 tmplen = strlen(tmp);
1770 cmdlen = strlen(cmd);
1771 /* If cmd may be extended then do so */
1772 if (tmplen > cmdlen)
1773 if (el_insertstr(el, tmp + cmdlen) == -1)
1774 fatal("el_insertstr failed.");
1776 /* Terminate argument cleanly */
1780 argterm[y++] = quote;
1781 if (lastarg || *(lf->cursor) != ' ')
1784 if (y > 0 && el_insertstr(el, argterm) == -1)
1785 fatal("el_insertstr failed.");
1794 * Determine whether a particular sftp command's arguments (if any)
1795 * represent local or remote files.
1798 complete_is_remote(char *cmd) {
1804 for (i = 0; cmds[i].c; i++) {
1805 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1812 /* Autocomplete a filename "file" */
1814 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1815 char *file, int remote, int lastarg, char quote, int terminated)
1818 char *tmp, *tmp2, ins[8];
1819 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1823 /* Glob from "file" location */
1827 xasprintf(&tmp, "%s*", file);
1829 /* Check if the path is absolute. */
1830 isabs = tmp[0] == '/';
1832 memset(&g, 0, sizeof(g));
1833 if (remote != LOCAL) {
1834 tmp = make_absolute(tmp, remote_path);
1835 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1837 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1839 /* Determine length of pwd so we can trim completion display */
1840 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1841 /* Terminate counting on first unescaped glob metacharacter */
1842 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1843 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1847 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1849 if (tmp[tmplen] == '/')
1850 pwdlen = tmplen + 1; /* track last seen '/' */
1855 if (g.gl_matchc == 0)
1858 if (g.gl_matchc > 1)
1859 complete_display(g.gl_pathv, pwdlen);
1861 /* Don't try to extend globs */
1862 if (file == NULL || hadglob)
1865 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1866 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1872 tmplen = strlen(tmp);
1873 filelen = strlen(file);
1875 /* Count the number of escaped characters in the input string. */
1877 for (i = 0; i < filelen; i++) {
1878 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1885 if (tmplen > (filelen - cesc)) {
1886 tmp2 = tmp + filelen - cesc;
1888 /* quote argument on way out */
1889 for (i = 0; i < len; i += clen) {
1890 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1891 (size_t)clen > sizeof(ins) - 2)
1892 fatal("invalid multibyte character");
1894 memcpy(ins + 1, tmp2 + i, clen);
1895 ins[clen + 1] = '\0';
1905 if (quote == '\0' || tmp2[i] == quote) {
1906 if (el_insertstr(el, ins) == -1)
1907 fatal("el_insertstr "
1913 if (el_insertstr(el, ins + 1) == -1)
1914 fatal("el_insertstr failed.");
1921 if (g.gl_matchc == 1) {
1923 if (!terminated && quote != '\0')
1925 if (*(lf->cursor - 1) != '/' &&
1926 (lastarg || *(lf->cursor) != ' '))
1929 if (i > 0 && el_insertstr(el, ins) == -1)
1930 fatal("el_insertstr failed.");
1939 /* tab-completion hook function, called via libedit */
1940 static unsigned char
1941 complete(EditLine *el, int ch)
1943 char **argv, *line, quote;
1945 u_int cursor, len, terminated, ret = CC_ERROR;
1947 struct complete_ctx *complete_ctx;
1950 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1951 fatal("%s: el_get failed", __func__);
1953 /* Figure out which argument the cursor points to */
1954 cursor = lf->cursor - lf->buffer;
1955 line = (char *)xmalloc(cursor + 1);
1956 memcpy(line, lf->buffer, cursor);
1957 line[cursor] = '\0';
1958 argv = makeargv(line, &carg, 1, "e, &terminated);
1961 /* Get all the arguments on the line */
1962 len = lf->lastchar - lf->buffer;
1963 line = (char *)xmalloc(len + 1);
1964 memcpy(line, lf->buffer, len);
1966 argv = makeargv(line, &argc, 1, NULL, NULL);
1968 /* Ensure cursor is at EOL or a argument boundary */
1969 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1970 line[cursor] != '\n') {
1976 /* Show all available commands */
1977 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1979 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1980 /* Handle the command parsing */
1981 if (complete_cmd_parse(el, argv[0], argc == carg,
1982 quote, terminated) != 0)
1984 } else if (carg >= 1) {
1985 /* Handle file parsing */
1986 int remote = complete_is_remote(argv[0]);
1987 char *filematch = NULL;
1989 if (carg > 1 && line[cursor-1] != ' ')
1990 filematch = argv[carg - 1];
1993 complete_match(el, complete_ctx->conn,
1994 *complete_ctx->remote_pathp, filematch,
1995 remote, carg == argc, quote, terminated) != 0)
2002 #endif /* USE_LIBEDIT */
2005 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2010 int err, interactive;
2011 EditLine *el = NULL;
2015 extern char *__progname;
2016 struct complete_ctx complete_ctx;
2018 if (!batchmode && isatty(STDIN_FILENO)) {
2019 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2020 fatal("Couldn't initialise editline");
2021 if ((hl = history_init()) == NULL)
2022 fatal("Couldn't initialise editline history");
2023 history(hl, &hev, H_SETSIZE, 100);
2024 el_set(el, EL_HIST, history, hl);
2026 el_set(el, EL_PROMPT, prompt);
2027 el_set(el, EL_EDITOR, "emacs");
2028 el_set(el, EL_TERMINAL, NULL);
2029 el_set(el, EL_SIGNAL, 1);
2030 el_source(el, NULL);
2032 /* Tab Completion */
2033 el_set(el, EL_ADDFN, "ftp-complete",
2034 "Context sensitive argument completion", complete);
2035 complete_ctx.conn = conn;
2036 complete_ctx.remote_pathp = &remote_path;
2037 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2038 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2039 /* enable ctrl-left-arrow and ctrl-right-arrow */
2040 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2041 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2042 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2043 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2044 /* make ^w match ksh behaviour */
2045 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2047 #endif /* USE_LIBEDIT */
2049 remote_path = do_realpath(conn, ".");
2050 if (remote_path == NULL)
2053 if (file1 != NULL) {
2054 dir = xstrdup(file1);
2055 dir = make_absolute(dir, remote_path);
2057 if (remote_is_dir(conn, dir) && file2 == NULL) {
2059 printf("Changing to: %s\n", dir);
2060 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2061 if (parse_dispatch_command(conn, cmd,
2062 &remote_path, 1) != 0) {
2069 /* XXX this is wrong wrt quoting */
2070 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2071 global_aflag ? " -a" : "", dir,
2072 file2 == NULL ? "" : " ",
2073 file2 == NULL ? "" : file2);
2074 err = parse_dispatch_command(conn, cmd,
2087 interactive = !batchmode && isatty(STDIN_FILENO);
2092 signal(SIGINT, SIG_IGN);
2097 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2102 if (!interactive) { /* Echo command */
2103 printf("sftp> %s", cmd);
2104 if (strlen(cmd) > 0 &&
2105 cmd[strlen(cmd) - 1] != '\n')
2113 if ((line = el_gets(el, &count)) == NULL ||
2118 history(hl, &hev, H_ENTER, line);
2119 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2120 fprintf(stderr, "Error: input line too long\n");
2123 #endif /* USE_LIBEDIT */
2126 cp = strrchr(cmd, '\n');
2130 /* Handle user interrupts gracefully during commands */
2132 signal(SIGINT, cmd_interrupt);
2134 err = parse_dispatch_command(conn, cmd, &remote_path,
2145 #endif /* USE_LIBEDIT */
2147 /* err == 1 signifies normal "quit" exit */
2148 return (err >= 0 ? 0 : -1);
2152 connect_to_server(char *path, char **args, int *in, int *out)
2157 int pin[2], pout[2];
2159 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2160 fatal("pipe: %s", strerror(errno));
2165 #else /* USE_PIPES */
2168 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2169 fatal("socketpair: %s", strerror(errno));
2170 *in = *out = inout[0];
2171 c_in = c_out = inout[1];
2172 #endif /* USE_PIPES */
2174 if ((sshpid = fork()) == -1)
2175 fatal("fork: %s", strerror(errno));
2176 else if (sshpid == 0) {
2177 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2178 (dup2(c_out, STDOUT_FILENO) == -1)) {
2179 fprintf(stderr, "dup2: %s\n", strerror(errno));
2188 * The underlying ssh is in the same process group, so we must
2189 * ignore SIGINT if we want to gracefully abort commands,
2190 * otherwise the signal will make it to the ssh process and
2191 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2192 * underlying ssh, it must *not* ignore that signal.
2194 signal(SIGINT, SIG_IGN);
2195 signal(SIGTERM, SIG_DFL);
2197 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2201 signal(SIGTERM, killchild);
2202 signal(SIGINT, killchild);
2203 signal(SIGHUP, killchild);
2211 extern char *__progname;
2214 "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2215 " [-D sftp_server_path] [-F ssh_config] "
2216 "[-i identity_file] [-l limit]\n"
2217 " [-o ssh_option] [-P port] [-R num_requests] "
2219 " [-s subsystem | sftp_server] host\n"
2220 " %s [user@]host[:file ...]\n"
2221 " %s [user@]host[:dir[/]]\n"
2222 " %s -b batchfile [user@]host\n",
2223 __progname, __progname, __progname, __progname);
2228 main(int argc, char **argv)
2230 int in, out, ch, err;
2231 char *host = NULL, *userhost, *cp, *file2 = NULL;
2232 int debug_level = 0, sshver = 2;
2233 char *file1 = NULL, *sftp_server = NULL;
2234 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2236 LogLevel ll = SYSLOG_LEVEL_INFO;
2239 extern char *optarg;
2240 struct sftp_conn *conn;
2241 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2242 size_t num_requests = DEFAULT_NUM_REQUESTS;
2243 long long limit_kbps = 0;
2245 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2247 setlocale(LC_CTYPE, "");
2249 __progname = ssh_get_progname(argv[0]);
2250 memset(&args, '\0', sizeof(args));
2252 addargs(&args, "%s", ssh_program);
2253 addargs(&args, "-oForwardX11 no");
2254 addargs(&args, "-oForwardAgent no");
2255 addargs(&args, "-oPermitLocalCommand no");
2256 addargs(&args, "-oClearAllForwardings yes");
2258 ll = SYSLOG_LEVEL_INFO;
2261 while ((ch = getopt(argc, argv,
2262 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2264 /* Passed through to ssh(1) */
2268 addargs(&args, "-%c", ch);
2270 /* Passed through to ssh(1) with argument */
2275 addargs(&args, "-%c", ch);
2276 addargs(&args, "%s", optarg);
2279 ll = SYSLOG_LEVEL_ERROR;
2282 addargs(&args, "-%c", ch);
2285 addargs(&args, "-oPort %s", optarg);
2288 if (debug_level < 3) {
2289 addargs(&args, "-v");
2290 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2296 if (sftp_server == NULL)
2297 sftp_server = _PATH_SFTP_SERVER;
2306 copy_buffer_len = strtol(optarg, &cp, 10);
2307 if (copy_buffer_len == 0 || *cp != '\0')
2308 fatal("Invalid buffer size \"%s\"", optarg);
2312 fatal("Batch file already specified.");
2314 /* Allow "-" as stdin */
2315 if (strcmp(optarg, "-") != 0 &&
2316 (infile = fopen(optarg, "r")) == NULL)
2317 fatal("%s (%s).", strerror(errno), optarg);
2319 quiet = batchmode = 1;
2320 addargs(&args, "-obatchmode yes");
2329 sftp_direct = optarg;
2332 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2336 limit_kbps *= 1024; /* kbps */
2342 num_requests = strtol(optarg, &cp, 10);
2343 if (num_requests == 0 || *cp != '\0')
2344 fatal("Invalid number of requests \"%s\"",
2348 sftp_server = optarg;
2351 ssh_program = optarg;
2352 replacearg(&args, 0, "%s", ssh_program);
2360 if (!isatty(STDERR_FILENO))
2363 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2365 if (sftp_direct == NULL) {
2366 if (optind == argc || argc > (optind + 2))
2369 userhost = xstrdup(argv[optind]);
2370 file2 = argv[optind+1];
2372 if ((host = strrchr(userhost, '@')) == NULL)
2377 fprintf(stderr, "Missing username\n");
2380 addargs(&args, "-l");
2381 addargs(&args, "%s", userhost);
2384 if ((cp = colon(host)) != NULL) {
2389 host = cleanhostname(host);
2391 fprintf(stderr, "Missing hostname\n");
2395 addargs(&args, "-oProtocol %d", sshver);
2397 /* no subsystem if the server-spec contains a '/' */
2398 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2399 addargs(&args, "-s");
2401 addargs(&args, "--");
2402 addargs(&args, "%s", host);
2403 addargs(&args, "%s", (sftp_server != NULL ?
2404 sftp_server : "sftp"));
2406 connect_to_server(ssh_program, args.list, &in, &out);
2409 addargs(&args, "sftp-server");
2411 connect_to_server(sftp_direct, args.list, &in, &out);
2415 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2417 fatal("Couldn't initialise connection to server");
2420 if (sftp_direct == NULL)
2421 fprintf(stderr, "Connected to %s.\n", host);
2423 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2426 err = interactive_loop(conn, file1, file2);
2428 #if !defined(USE_PIPES)
2429 shutdown(in, SHUT_RDWR);
2430 shutdown(out, SHUT_RDWR);
2438 while (waitpid(sshpid, NULL, 0) == -1)
2440 fatal("Couldn't wait for ssh process: %s",
2443 exit(err == 0 ? 0 : 1);