1 /* $OpenBSD: sftp.c,v 1.172 2016/02/15 09:47:49 dtucker 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/param.h> /* MIN MAX */
21 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #ifdef HAVE_SYS_STAT_H
24 # include <sys/stat.h>
26 #include <sys/param.h>
27 #include <sys/socket.h>
29 #ifdef HAVE_SYS_STATVFS_H
30 #include <sys/statvfs.h>
48 typedef void EditLine;
64 #include "pathnames.h"
70 #include "sftp-common.h"
71 #include "sftp-client.h"
73 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
74 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
76 /* File to read commands from */
79 /* Are we in batchfile mode? */
82 /* PID of ssh transport process */
83 static pid_t sshpid = -1;
85 /* Suppress diagnositic messages */
88 /* This is set to 0 if the progressmeter is not desired. */
91 /* When this option is set, we always recursively download/upload directories */
94 /* When this option is set, we resume download or upload if possible */
97 /* When this option is set, the file transfers will always preserve times */
100 /* When this option is set, transfers will have fsync() called on each file */
101 int global_fflag = 0;
103 /* SIGINT received during command processing */
104 volatile sig_atomic_t interrupted = 0;
106 /* I wish qsort() took a separate ctx for the comparison function...*/
109 /* Context used for commandline completion */
110 struct complete_ctx {
111 struct sftp_conn *conn;
115 int remote_glob(struct sftp_conn *, const char *, int,
116 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
118 extern char *__progname;
120 /* Separators for interactive commands */
121 #define WHITESPACE " \t\r\n"
124 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
125 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
126 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
127 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
128 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
129 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
130 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
131 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
132 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
134 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
135 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
137 /* Commands for interactive mode */
174 /* Type of completion */
179 static const struct CMD cmds[] = {
180 { "bye", I_QUIT, NOARGS },
181 { "cd", I_CHDIR, REMOTE },
182 { "chdir", I_CHDIR, REMOTE },
183 { "chgrp", I_CHGRP, REMOTE },
184 { "chmod", I_CHMOD, REMOTE },
185 { "chown", I_CHOWN, REMOTE },
186 { "df", I_DF, REMOTE },
187 { "dir", I_LS, REMOTE },
188 { "exit", I_QUIT, NOARGS },
189 { "get", I_GET, REMOTE },
190 { "help", I_HELP, NOARGS },
191 { "lcd", I_LCHDIR, LOCAL },
192 { "lchdir", I_LCHDIR, LOCAL },
193 { "lls", I_LLS, LOCAL },
194 { "lmkdir", I_LMKDIR, LOCAL },
195 { "ln", I_LINK, REMOTE },
196 { "lpwd", I_LPWD, LOCAL },
197 { "ls", I_LS, REMOTE },
198 { "lumask", I_LUMASK, NOARGS },
199 { "mkdir", I_MKDIR, REMOTE },
200 { "mget", I_GET, REMOTE },
201 { "mput", I_PUT, LOCAL },
202 { "progress", I_PROGRESS, NOARGS },
203 { "put", I_PUT, LOCAL },
204 { "pwd", I_PWD, REMOTE },
205 { "quit", I_QUIT, NOARGS },
206 { "reget", I_REGET, REMOTE },
207 { "rename", I_RENAME, REMOTE },
208 { "reput", I_REPUT, LOCAL },
209 { "rm", I_RM, REMOTE },
210 { "rmdir", I_RMDIR, REMOTE },
211 { "symlink", I_SYMLINK, REMOTE },
212 { "version", I_VERSION, NOARGS },
213 { "!", I_SHELL, NOARGS },
214 { "?", I_HELP, NOARGS },
218 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
225 kill(sshpid, SIGTERM);
226 waitpid(sshpid, NULL, 0);
234 cmd_interrupt(int signo)
236 const char msg[] = "\rInterrupt \n";
237 int olderrno = errno;
239 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
247 printf("Available commands:\n"
249 "cd path Change remote directory to 'path'\n"
250 "chgrp grp path Change group of file 'path' to 'grp'\n"
251 "chmod mode path Change permissions of file 'path' to 'mode'\n"
252 "chown own path Change owner of file 'path' to 'own'\n"
253 "df [-hi] [path] Display statistics for current directory or\n"
254 " filesystem containing 'path'\n"
256 "get [-afPpRr] remote [local] Download file\n"
257 "reget [-fPpRr] remote [local] Resume download file\n"
258 "reput [-fPpRr] [local] remote Resume upload file\n"
259 "help Display this help text\n"
260 "lcd path Change local directory to 'path'\n"
261 "lls [ls-options [path]] Display local directory listing\n"
262 "lmkdir path Create local directory\n"
263 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
264 "lpwd Print local working directory\n"
265 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
266 "lumask umask Set local umask to 'umask'\n"
267 "mkdir path Create remote directory\n"
268 "progress Toggle display of progress meter\n"
269 "put [-afPpRr] local [remote] Upload file\n"
270 "pwd Display remote working directory\n"
272 "rename oldpath newpath Rename remote file\n"
273 "rm path Delete remote file\n"
274 "rmdir path Remove remote directory\n"
275 "symlink oldpath newpath Symlink remote file\n"
276 "version Show SFTP version\n"
277 "!command Execute 'command' in local shell\n"
278 "! Escape to local shell\n"
279 "? Synonym for help\n");
283 local_do_shell(const char *args)
292 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
293 shell = _PATH_BSHELL;
295 if ((pid = fork()) == -1)
296 fatal("Couldn't fork: %s", strerror(errno));
299 /* XXX: child has pipe fds to ssh subproc open - issue? */
301 debug3("Executing %s -c \"%s\"", shell, args);
302 execl(shell, shell, "-c", args, (char *)NULL);
304 debug3("Executing %s", shell);
305 execl(shell, shell, (char *)NULL);
307 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
311 while (waitpid(pid, &status, 0) == -1)
313 fatal("Couldn't wait for child: %s", strerror(errno));
314 if (!WIFEXITED(status))
315 error("Shell exited abnormally");
316 else if (WEXITSTATUS(status))
317 error("Shell exited with status %d", WEXITSTATUS(status));
321 local_do_ls(const char *args)
324 local_do_shell(_PATH_LS);
326 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
327 char *buf = xmalloc(len);
329 /* XXX: quoting - rip quoting code from ftp? */
330 snprintf(buf, len, _PATH_LS " %s", args);
336 /* Strip one path (usually the pwd) from the start of another */
338 path_strip(char *path, char *strip)
343 return (xstrdup(path));
346 if (strncmp(path, strip, len) == 0) {
347 if (strip[len - 1] != '/' && path[len] == '/')
349 return (xstrdup(path + len));
352 return (xstrdup(path));
356 make_absolute(char *p, char *pwd)
361 if (p && p[0] != '/') {
362 abs_str = path_append(pwd, p);
370 parse_getput_flags(const char *cmd, char **argv, int argc,
371 int *aflag, int *fflag, int *pflag, int *rflag)
373 extern int opterr, optind, optopt, optreset;
376 optind = optreset = 1;
379 *aflag = *fflag = *rflag = *pflag = 0;
380 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
397 error("%s: Invalid flag -%c", cmd, optopt);
406 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
408 extern int opterr, optind, optopt, optreset;
411 optind = optreset = 1;
415 while ((ch = getopt(argc, argv, "s")) != -1) {
421 error("%s: Invalid flag -%c", cmd, optopt);
430 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
432 extern int opterr, optind, optopt, optreset;
435 optind = optreset = 1;
439 while ((ch = getopt(argc, argv, "l")) != -1) {
445 error("%s: Invalid flag -%c", cmd, optopt);
454 parse_ls_flags(char **argv, int argc, int *lflag)
456 extern int opterr, optind, optopt, optreset;
459 optind = optreset = 1;
462 *lflag = LS_NAME_SORT;
463 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
466 *lflag &= ~VIEW_FLAGS;
467 *lflag |= LS_SHORT_VIEW;
470 *lflag &= ~SORT_FLAGS;
471 *lflag |= LS_SIZE_SORT;
474 *lflag |= LS_SHOW_ALL;
477 *lflag &= ~SORT_FLAGS;
480 *lflag |= LS_SI_UNITS;
483 *lflag &= ~LS_SHORT_VIEW;
484 *lflag |= LS_LONG_VIEW;
487 *lflag &= ~LS_SHORT_VIEW;
488 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
491 *lflag |= LS_REVERSE_SORT;
494 *lflag &= ~SORT_FLAGS;
495 *lflag |= LS_TIME_SORT;
498 error("ls: Invalid flag -%c", optopt);
507 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
509 extern int opterr, optind, optopt, optreset;
512 optind = optreset = 1;
516 while ((ch = getopt(argc, argv, "hi")) != -1) {
525 error("%s: Invalid flag -%c", cmd, optopt);
534 parse_no_flags(const char *cmd, char **argv, int argc)
536 extern int opterr, optind, optopt, optreset;
539 optind = optreset = 1;
542 while ((ch = getopt(argc, argv, "")) != -1) {
545 error("%s: Invalid flag -%c", cmd, optopt);
558 /* XXX: report errors? */
559 if (stat(path, &sb) == -1)
562 return(S_ISDIR(sb.st_mode));
566 remote_is_dir(struct sftp_conn *conn, char *path)
570 /* XXX: report errors? */
571 if ((a = do_stat(conn, path, 1)) == NULL)
573 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
575 return(S_ISDIR(a->perm));
578 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
580 pathname_is_dir(char *pathname)
582 size_t l = strlen(pathname);
584 return l > 0 && pathname[l - 1] == '/';
588 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
589 int pflag, int rflag, int resume, int fflag)
591 char *abs_src = NULL;
592 char *abs_dst = NULL;
594 char *filename, *tmp=NULL;
597 abs_src = xstrdup(src);
598 abs_src = make_absolute(abs_src, pwd);
599 memset(&g, 0, sizeof(g));
601 debug3("Looking up %s", abs_src);
602 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
603 if (r == GLOB_NOSPACE) {
604 error("Too many matches for \"%s\".", abs_src);
606 error("File \"%s\" not found.", abs_src);
613 * If multiple matches then dst must be a directory or
616 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
617 error("Multiple source paths, but destination "
618 "\"%s\" is not a directory", dst);
623 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
624 tmp = xstrdup(g.gl_pathv[i]);
625 if ((filename = basename(tmp)) == NULL) {
626 error("basename %s: %s", tmp, strerror(errno));
632 if (g.gl_matchc == 1 && dst) {
634 abs_dst = path_append(dst, filename);
636 abs_dst = xstrdup(dst);
639 abs_dst = path_append(dst, filename);
641 abs_dst = xstrdup(filename);
645 resume |= global_aflag;
646 if (!quiet && resume)
647 printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
648 else if (!quiet && !resume)
649 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
650 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
651 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
652 pflag || global_pflag, 1, resume,
653 fflag || global_fflag) == -1)
656 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
657 pflag || global_pflag, resume,
658 fflag || global_fflag) == -1)
672 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
673 int pflag, int rflag, int resume, int fflag)
675 char *tmp_dst = NULL;
676 char *abs_dst = NULL;
677 char *tmp = NULL, *filename = NULL;
680 int i, dst_is_dir = 1;
684 tmp_dst = xstrdup(dst);
685 tmp_dst = make_absolute(tmp_dst, pwd);
688 memset(&g, 0, sizeof(g));
689 debug3("Looking up %s", src);
690 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
691 error("File \"%s\" not found.", src);
696 /* If we aren't fetching to pwd then stash this status for later */
698 dst_is_dir = remote_is_dir(conn, tmp_dst);
700 /* If multiple matches, dst may be directory or unspecified */
701 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
702 error("Multiple paths match, but destination "
703 "\"%s\" is not a directory", tmp_dst);
708 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
709 if (stat(g.gl_pathv[i], &sb) == -1) {
711 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
715 tmp = xstrdup(g.gl_pathv[i]);
716 if ((filename = basename(tmp)) == NULL) {
717 error("basename %s: %s", tmp, strerror(errno));
723 if (g.gl_matchc == 1 && tmp_dst) {
724 /* If directory specified, append filename */
726 abs_dst = path_append(tmp_dst, filename);
728 abs_dst = xstrdup(tmp_dst);
729 } else if (tmp_dst) {
730 abs_dst = path_append(tmp_dst, filename);
732 abs_dst = make_absolute(xstrdup(filename), pwd);
736 resume |= global_aflag;
737 if (!quiet && resume)
738 printf("Resuming upload of %s to %s\n", g.gl_pathv[i],
740 else if (!quiet && !resume)
741 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
742 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
743 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
744 pflag || global_pflag, 1, resume,
745 fflag || global_fflag) == -1)
748 if (do_upload(conn, g.gl_pathv[i], abs_dst,
749 pflag || global_pflag, resume,
750 fflag || global_fflag) == -1)
763 sdirent_comp(const void *aa, const void *bb)
765 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
766 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
767 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
769 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
770 if (sort_flag & LS_NAME_SORT)
771 return (rmul * strcmp(a->filename, b->filename));
772 else if (sort_flag & LS_TIME_SORT)
773 return (rmul * NCMP(a->a.mtime, b->a.mtime));
774 else if (sort_flag & LS_SIZE_SORT)
775 return (rmul * NCMP(a->a.size, b->a.size));
777 fatal("Unknown ls sort type");
780 /* sftp ls.1 replacement for directories */
782 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
785 u_int c = 1, colspace = 0, columns = 1;
788 if ((n = do_readdir(conn, path, &d)) != 0)
791 if (!(lflag & LS_SHORT_VIEW)) {
792 u_int m = 0, width = 80;
796 /* Count entries for sort and find longest filename */
797 for (n = 0; d[n] != NULL; n++) {
798 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
799 m = MAX(m, strlen(d[n]->filename));
802 /* Add any subpath that also needs to be counted */
803 tmp = path_strip(path, strip_path);
807 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
810 columns = width / (m + 2);
811 columns = MAX(columns, 1);
812 colspace = width / columns;
813 colspace = MIN(colspace, width);
816 if (lflag & SORT_FLAGS) {
817 for (n = 0; d[n] != NULL; n++)
818 ; /* count entries */
819 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
820 qsort(d, n, sizeof(*d), sdirent_comp);
823 for (n = 0; d[n] != NULL && !interrupted; n++) {
826 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
829 tmp = path_append(path, d[n]->filename);
830 fname = path_strip(tmp, strip_path);
833 if (lflag & LS_LONG_VIEW) {
834 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
838 memset(&sb, 0, sizeof(sb));
839 attrib_to_stat(&d[n]->a, &sb);
840 lname = ls_file(fname, &sb, 1,
841 (lflag & LS_SI_UNITS));
842 printf("%s\n", lname);
845 printf("%s\n", d[n]->longname);
847 printf("%-*s", colspace, fname);
858 if (!(lflag & LS_LONG_VIEW) && (c != 1))
861 free_sftp_dirents(d);
865 /* sftp ls.1 replacement which handles path globs */
867 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
874 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
876 memset(&g, 0, sizeof(g));
878 if ((r = remote_glob(conn, path,
879 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
881 (g.gl_pathc && !g.gl_matchc)) {
884 if (r == GLOB_NOSPACE) {
885 error("Can't ls: Too many matches for \"%s\"", path);
887 error("Can't ls: \"%s\" not found", path);
896 * If the glob returns a single match and it is a directory,
897 * then just list its contents.
899 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
900 S_ISDIR(g.gl_statv[0]->st_mode)) {
901 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
906 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
909 if (!(lflag & LS_SHORT_VIEW)) {
910 /* Count entries for sort and find longest filename */
911 for (i = 0; g.gl_pathv[i]; i++)
912 m = MAX(m, strlen(g.gl_pathv[i]));
914 columns = width / (m + 2);
915 columns = MAX(columns, 1);
916 colspace = width / columns;
919 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
920 fname = path_strip(g.gl_pathv[i], strip_path);
921 if (lflag & LS_LONG_VIEW) {
922 if (g.gl_statv[i] == NULL) {
923 error("no stat information for %s", fname);
926 lname = ls_file(fname, g.gl_statv[i], 1,
927 (lflag & LS_SI_UNITS));
928 printf("%s\n", lname);
931 printf("%-*s", colspace, fname);
941 if (!(lflag & LS_LONG_VIEW) && (c != 1))
952 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
954 struct sftp_statvfs st;
955 char s_used[FMT_SCALED_STRSIZE];
956 char s_avail[FMT_SCALED_STRSIZE];
957 char s_root[FMT_SCALED_STRSIZE];
958 char s_total[FMT_SCALED_STRSIZE];
959 unsigned long long ffree;
961 if (do_statvfs(conn, path, &st, 1) == -1)
964 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
965 printf(" Inodes Used Avail "
966 "(root) %%Capacity\n");
967 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
968 (unsigned long long)st.f_files,
969 (unsigned long long)(st.f_files - st.f_ffree),
970 (unsigned long long)st.f_favail,
971 (unsigned long long)st.f_ffree, ffree);
973 strlcpy(s_used, "error", sizeof(s_used));
974 strlcpy(s_avail, "error", sizeof(s_avail));
975 strlcpy(s_root, "error", sizeof(s_root));
976 strlcpy(s_total, "error", sizeof(s_total));
977 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
978 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
979 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
980 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
981 printf(" Size Used Avail (root) %%Capacity\n");
982 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
983 s_total, s_used, s_avail, s_root,
984 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
987 printf(" Size Used Avail "
988 "(root) %%Capacity\n");
989 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
990 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
991 (unsigned long long)(st.f_frsize *
992 (st.f_blocks - st.f_bfree) / 1024),
993 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
994 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
995 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1002 * Undo escaping of glob sequences in place. Used to undo extra escaping
1003 * applied in makeargv() when the string is destined for a function that
1007 undo_glob_escape(char *s)
1042 * Split a string into an argument vector using sh(1)-style quoting,
1043 * comment and escaping rules, but with some tweaks to handle glob(3)
1045 * The "sloppy" flag allows for recovery from missing terminating quote, for
1046 * use in parsing incomplete commandlines during tab autocompletion.
1048 * Returns NULL on error or a NULL-terminated array of arguments.
1050 * If "lastquote" is not NULL, the quoting character used for the last
1051 * argument is placed in *lastquote ("\0", "'" or "\"").
1053 * If "terminated" is not NULL, *terminated will be set to 1 when the
1054 * last argument's quote has been properly terminated or 0 otherwise.
1055 * This parameter is only of use if "sloppy" is set.
1058 #define MAXARGLEN 8192
1060 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1065 static char argvs[MAXARGLEN];
1066 static char *argv[MAXARGS + 1];
1067 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1070 if (strlen(arg) > sizeof(argvs) - 1) {
1072 error("string too long");
1075 if (terminated != NULL)
1077 if (lastquote != NULL)
1082 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1083 error("Too many arguments.");
1086 if (isspace((unsigned char)arg[i])) {
1087 if (state == MA_UNQUOTED) {
1088 /* Terminate current argument */
1092 } else if (state != MA_START)
1093 argvs[j++] = arg[i];
1094 } else if (arg[i] == '"' || arg[i] == '\'') {
1095 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1096 if (state == MA_START) {
1097 argv[argc] = argvs + j;
1099 if (lastquote != NULL)
1100 *lastquote = arg[i];
1101 } else if (state == MA_UNQUOTED)
1103 else if (state == q)
1104 state = MA_UNQUOTED;
1106 argvs[j++] = arg[i];
1107 } else if (arg[i] == '\\') {
1108 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1109 quot = state == MA_SQUOTE ? '\'' : '"';
1110 /* Unescape quote we are in */
1111 /* XXX support \n and friends? */
1112 if (arg[i + 1] == quot) {
1114 argvs[j++] = arg[i];
1115 } else if (arg[i + 1] == '?' ||
1116 arg[i + 1] == '[' || arg[i + 1] == '*') {
1118 * Special case for sftp: append
1119 * double-escaped glob sequence -
1120 * glob will undo one level of
1121 * escaping. NB. string can grow here.
1123 if (j >= sizeof(argvs) - 5)
1124 goto args_too_longs;
1126 argvs[j++] = arg[i++];
1128 argvs[j++] = arg[i];
1130 argvs[j++] = arg[i++];
1131 argvs[j++] = arg[i];
1134 if (state == MA_START) {
1135 argv[argc] = argvs + j;
1136 state = MA_UNQUOTED;
1137 if (lastquote != NULL)
1140 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1141 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1143 * Special case for sftp: append
1144 * escaped glob sequence -
1145 * glob will undo one level of
1148 argvs[j++] = arg[i++];
1149 argvs[j++] = arg[i];
1151 /* Unescape everything */
1152 /* XXX support \n and friends? */
1154 argvs[j++] = arg[i];
1157 } else if (arg[i] == '#') {
1158 if (state == MA_SQUOTE || state == MA_DQUOTE)
1159 argvs[j++] = arg[i];
1162 } else if (arg[i] == '\0') {
1163 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1165 state = MA_UNQUOTED;
1166 if (terminated != NULL)
1170 error("Unterminated quoted argument");
1174 if (state == MA_UNQUOTED) {
1180 if (state == MA_START) {
1181 argv[argc] = argvs + j;
1182 state = MA_UNQUOTED;
1183 if (lastquote != NULL)
1186 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1187 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1189 * Special case for sftp: escape quoted
1190 * glob(3) wildcards. NB. string can grow
1193 if (j >= sizeof(argvs) - 3)
1194 goto args_too_longs;
1196 argvs[j++] = arg[i];
1198 argvs[j++] = arg[i];
1207 parse_args(const char **cpp, int *ignore_errors, int *aflag,
1208 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1209 int *rflag, int *sflag,
1210 unsigned long *n_arg, char **path1, char **path2)
1212 const char *cmd, *cp = *cpp;
1216 int i, cmdnum, optidx, argc;
1218 /* Skip leading whitespace */
1219 cp = cp + strspn(cp, WHITESPACE);
1221 /* Check for leading '-' (disable error processing) */
1226 cp = cp + strspn(cp, WHITESPACE);
1229 /* Ignore blank lines and lines which begin with comment '#' char */
1230 if (*cp == '\0' || *cp == '#')
1233 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1236 /* Figure out which command we have */
1237 for (i = 0; cmds[i].c != NULL; i++) {
1238 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1248 } else if (cmdnum == -1) {
1249 error("Invalid command.");
1253 /* Get arguments and parse flags */
1254 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1255 *rflag = *sflag = 0;
1256 *path1 = *path2 = NULL;
1263 if ((optidx = parse_getput_flags(cmd, argv, argc,
1264 aflag, fflag, pflag, rflag)) == -1)
1266 /* Get first pathname (mandatory) */
1267 if (argc - optidx < 1) {
1268 error("You must specify at least one path after a "
1269 "%s command.", cmd);
1272 *path1 = xstrdup(argv[optidx]);
1273 /* Get second pathname (optional) */
1274 if (argc - optidx > 1) {
1275 *path2 = xstrdup(argv[optidx + 1]);
1276 /* Destination is not globbed */
1277 undo_glob_escape(*path2);
1281 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1283 goto parse_two_paths;
1285 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1287 goto parse_two_paths;
1289 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1292 if (argc - optidx < 2) {
1293 error("You must specify two paths after a %s "
1297 *path1 = xstrdup(argv[optidx]);
1298 *path2 = xstrdup(argv[optidx + 1]);
1299 /* Paths are not globbed */
1300 undo_glob_escape(*path1);
1301 undo_glob_escape(*path2);
1309 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1311 /* Get pathname (mandatory) */
1312 if (argc - optidx < 1) {
1313 error("You must specify a path after a %s command.",
1317 *path1 = xstrdup(argv[optidx]);
1318 /* Only "rm" globs */
1320 undo_glob_escape(*path1);
1323 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1326 /* Default to current directory if no path specified */
1327 if (argc - optidx < 1)
1330 *path1 = xstrdup(argv[optidx]);
1331 undo_glob_escape(*path1);
1335 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1337 /* Path is optional */
1338 if (argc - optidx > 0)
1339 *path1 = xstrdup(argv[optidx]);
1342 /* Skip ls command and following whitespace */
1343 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1345 /* Uses the rest of the line */
1352 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1354 /* Get numeric arg (mandatory) */
1355 if (argc - optidx < 1)
1358 l = strtol(argv[optidx], &cp2, base);
1359 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1360 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1363 error("You must supply a numeric argument "
1364 "to the %s command.", cmd);
1368 if (cmdnum == I_LUMASK)
1370 /* Get pathname (mandatory) */
1371 if (argc - optidx < 2) {
1372 error("You must specify a path after a %s command.",
1376 *path1 = xstrdup(argv[optidx + 1]);
1384 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1388 fatal("Command not implemented");
1396 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1399 char *path1, *path2, *tmp;
1400 int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0,
1402 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1404 unsigned long n_arg = 0;
1406 char path_buf[PATH_MAX];
1410 path1 = path2 = NULL;
1411 cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1412 &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1413 if (ignore_errors != 0)
1416 memset(&g, 0, sizeof(g));
1418 /* Perform command */
1424 /* Unrecognized command */
1431 err = process_get(conn, path1, path2, *pwd, pflag,
1432 rflag, aflag, fflag);
1438 err = process_put(conn, path1, path2, *pwd, pflag,
1439 rflag, aflag, fflag);
1442 path1 = make_absolute(path1, *pwd);
1443 path2 = make_absolute(path2, *pwd);
1444 err = do_rename(conn, path1, path2, lflag);
1450 path1 = make_absolute(path1, *pwd);
1451 path2 = make_absolute(path2, *pwd);
1452 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1455 path1 = make_absolute(path1, *pwd);
1456 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1457 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1459 printf("Removing %s\n", g.gl_pathv[i]);
1460 err = do_rm(conn, g.gl_pathv[i]);
1461 if (err != 0 && err_abort)
1466 path1 = make_absolute(path1, *pwd);
1468 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1470 err = do_mkdir(conn, path1, &a, 1);
1473 path1 = make_absolute(path1, *pwd);
1474 err = do_rmdir(conn, path1);
1477 path1 = make_absolute(path1, *pwd);
1478 if ((tmp = do_realpath(conn, path1)) == NULL) {
1482 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1487 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1488 error("Can't change directory: Can't check target");
1493 if (!S_ISDIR(aa->perm)) {
1494 error("Can't change directory: \"%s\" is not "
1495 "a directory", tmp);
1505 do_ls_dir(conn, *pwd, *pwd, lflag);
1509 /* Strip pwd off beginning of non-absolute paths */
1514 path1 = make_absolute(path1, *pwd);
1515 err = do_globbed_ls(conn, path1, tmp, lflag);
1518 /* Default to current directory if no path specified */
1520 path1 = xstrdup(*pwd);
1521 path1 = make_absolute(path1, *pwd);
1522 err = do_df(conn, path1, hflag, iflag);
1525 tmp = tilde_expand_filename(path1, getuid());
1528 if (chdir(path1) == -1) {
1529 error("Couldn't change local directory to "
1530 "\"%s\": %s", path1, strerror(errno));
1535 if (mkdir(path1, 0777) == -1) {
1536 error("Couldn't create local directory "
1537 "\"%s\": %s", path1, strerror(errno));
1545 local_do_shell(cmd);
1549 printf("Local umask: %03lo\n", n_arg);
1552 path1 = make_absolute(path1, *pwd);
1554 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1556 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1557 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1559 printf("Changing mode on %s\n", g.gl_pathv[i]);
1560 err = do_setstat(conn, g.gl_pathv[i], &a);
1561 if (err != 0 && err_abort)
1567 path1 = make_absolute(path1, *pwd);
1568 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1569 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1570 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1577 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1578 error("Can't get current ownership of "
1579 "remote file \"%s\"", g.gl_pathv[i]);
1586 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1587 if (cmdnum == I_CHOWN) {
1589 printf("Changing owner on %s\n",
1594 printf("Changing group on %s\n",
1598 err = do_setstat(conn, g.gl_pathv[i], aa);
1599 if (err != 0 && err_abort)
1604 printf("Remote working directory: %s\n", *pwd);
1607 if (!getcwd(path_buf, sizeof(path_buf))) {
1608 error("Couldn't get local cwd: %s", strerror(errno));
1612 printf("Local working directory: %s\n", path_buf);
1615 /* Processed below */
1621 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1624 showprogress = !showprogress;
1626 printf("Progress meter enabled\n");
1628 printf("Progress meter disabled\n");
1631 fatal("%d is not implemented", cmdnum);
1639 /* If an unignored error occurs in batch mode we should abort. */
1640 if (err_abort && err != 0)
1642 else if (cmdnum == I_QUIT)
1650 prompt(EditLine *el)
1655 /* Display entries in 'list' after skipping the first 'len' chars */
1657 complete_display(char **list, u_int len)
1659 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1663 /* Count entries for sort and find longest */
1664 for (y = 0; list[y]; y++)
1665 m = MAX(m, strlen(list[y]));
1667 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1670 m = m > len ? m - len : 0;
1671 columns = width / (m + 2);
1672 columns = MAX(columns, 1);
1673 colspace = width / columns;
1674 colspace = MIN(colspace, width);
1678 for (y = 0; list[y]; y++) {
1679 llen = strlen(list[y]);
1680 tmp = llen > len ? list[y] + len : "";
1681 printf("%-*s", colspace, tmp);
1692 * Given a "list" of words that begin with a common prefix of "word",
1693 * attempt to find an autocompletion to extends "word" by the next
1694 * characters common to all entries in "list".
1697 complete_ambiguous(const char *word, char **list, size_t count)
1703 u_int y, matchlen = strlen(list[0]);
1705 /* Find length of common stem */
1706 for (y = 1; list[y]; y++) {
1709 for (x = 0; x < matchlen; x++)
1710 if (list[0][x] != list[y][x])
1716 if (matchlen > strlen(word)) {
1717 char *tmp = xstrdup(list[0]);
1719 tmp[matchlen] = '\0';
1724 return xstrdup(word);
1727 /* Autocomplete a sftp command */
1729 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1732 u_int y, count = 0, cmdlen, tmplen;
1733 char *tmp, **list, argterm[3];
1736 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1738 /* No command specified: display all available commands */
1740 for (y = 0; cmds[y].c; y++)
1741 list[count++] = xstrdup(cmds[y].c);
1744 complete_display(list, 0);
1746 for (y = 0; list[y] != NULL; y++)
1752 /* Prepare subset of commands that start with "cmd" */
1753 cmdlen = strlen(cmd);
1754 for (y = 0; cmds[y].c; y++) {
1755 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1756 list[count++] = xstrdup(cmds[y].c);
1765 /* Complete ambigious command */
1766 tmp = complete_ambiguous(cmd, list, count);
1768 complete_display(list, 0);
1770 for (y = 0; list[y]; y++)
1775 tmplen = strlen(tmp);
1776 cmdlen = strlen(cmd);
1777 /* If cmd may be extended then do so */
1778 if (tmplen > cmdlen)
1779 if (el_insertstr(el, tmp + cmdlen) == -1)
1780 fatal("el_insertstr failed.");
1782 /* Terminate argument cleanly */
1786 argterm[y++] = quote;
1787 if (lastarg || *(lf->cursor) != ' ')
1790 if (y > 0 && el_insertstr(el, argterm) == -1)
1791 fatal("el_insertstr failed.");
1800 * Determine whether a particular sftp command's arguments (if any)
1801 * represent local or remote files.
1804 complete_is_remote(char *cmd) {
1810 for (i = 0; cmds[i].c; i++) {
1811 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1818 /* Autocomplete a filename "file" */
1820 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1821 char *file, int remote, int lastarg, char quote, int terminated)
1824 char *tmp, *tmp2, ins[8];
1825 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1829 /* Glob from "file" location */
1833 xasprintf(&tmp, "%s*", file);
1835 /* Check if the path is absolute. */
1836 isabs = tmp[0] == '/';
1838 memset(&g, 0, sizeof(g));
1839 if (remote != LOCAL) {
1840 tmp = make_absolute(tmp, remote_path);
1841 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1843 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1845 /* Determine length of pwd so we can trim completion display */
1846 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1847 /* Terminate counting on first unescaped glob metacharacter */
1848 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1849 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1853 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1855 if (tmp[tmplen] == '/')
1856 pwdlen = tmplen + 1; /* track last seen '/' */
1861 if (g.gl_matchc == 0)
1864 if (g.gl_matchc > 1)
1865 complete_display(g.gl_pathv, pwdlen);
1867 /* Don't try to extend globs */
1868 if (file == NULL || hadglob)
1871 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1872 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1878 tmplen = strlen(tmp);
1879 filelen = strlen(file);
1881 /* Count the number of escaped characters in the input string. */
1883 for (i = 0; i < filelen; i++) {
1884 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1891 if (tmplen > (filelen - cesc)) {
1892 tmp2 = tmp + filelen - cesc;
1894 /* quote argument on way out */
1895 for (i = 0; i < len; i += clen) {
1896 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1897 (size_t)clen > sizeof(ins) - 2)
1898 fatal("invalid multibyte character");
1900 memcpy(ins + 1, tmp2 + i, clen);
1901 ins[clen + 1] = '\0';
1911 if (quote == '\0' || tmp2[i] == quote) {
1912 if (el_insertstr(el, ins) == -1)
1913 fatal("el_insertstr "
1919 if (el_insertstr(el, ins + 1) == -1)
1920 fatal("el_insertstr failed.");
1927 if (g.gl_matchc == 1) {
1929 if (!terminated && quote != '\0')
1931 if (*(lf->cursor - 1) != '/' &&
1932 (lastarg || *(lf->cursor) != ' '))
1935 if (i > 0 && el_insertstr(el, ins) == -1)
1936 fatal("el_insertstr failed.");
1945 /* tab-completion hook function, called via libedit */
1946 static unsigned char
1947 complete(EditLine *el, int ch)
1949 char **argv, *line, quote;
1951 u_int cursor, len, terminated, ret = CC_ERROR;
1953 struct complete_ctx *complete_ctx;
1956 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1957 fatal("%s: el_get failed", __func__);
1959 /* Figure out which argument the cursor points to */
1960 cursor = lf->cursor - lf->buffer;
1961 line = xmalloc(cursor + 1);
1962 memcpy(line, lf->buffer, cursor);
1963 line[cursor] = '\0';
1964 argv = makeargv(line, &carg, 1, "e, &terminated);
1967 /* Get all the arguments on the line */
1968 len = lf->lastchar - lf->buffer;
1969 line = xmalloc(len + 1);
1970 memcpy(line, lf->buffer, len);
1972 argv = makeargv(line, &argc, 1, NULL, NULL);
1974 /* Ensure cursor is at EOL or a argument boundary */
1975 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1976 line[cursor] != '\n') {
1982 /* Show all available commands */
1983 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1985 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1986 /* Handle the command parsing */
1987 if (complete_cmd_parse(el, argv[0], argc == carg,
1988 quote, terminated) != 0)
1990 } else if (carg >= 1) {
1991 /* Handle file parsing */
1992 int remote = complete_is_remote(argv[0]);
1993 char *filematch = NULL;
1995 if (carg > 1 && line[cursor-1] != ' ')
1996 filematch = argv[carg - 1];
1999 complete_match(el, complete_ctx->conn,
2000 *complete_ctx->remote_pathp, filematch,
2001 remote, carg == argc, quote, terminated) != 0)
2008 #endif /* USE_LIBEDIT */
2011 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2016 int err, interactive;
2017 EditLine *el = NULL;
2021 extern char *__progname;
2022 struct complete_ctx complete_ctx;
2024 if (!batchmode && isatty(STDIN_FILENO)) {
2025 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2026 fatal("Couldn't initialise editline");
2027 if ((hl = history_init()) == NULL)
2028 fatal("Couldn't initialise editline history");
2029 history(hl, &hev, H_SETSIZE, 100);
2030 el_set(el, EL_HIST, history, hl);
2032 el_set(el, EL_PROMPT, prompt);
2033 el_set(el, EL_EDITOR, "emacs");
2034 el_set(el, EL_TERMINAL, NULL);
2035 el_set(el, EL_SIGNAL, 1);
2036 el_source(el, NULL);
2038 /* Tab Completion */
2039 el_set(el, EL_ADDFN, "ftp-complete",
2040 "Context sensitive argument completion", complete);
2041 complete_ctx.conn = conn;
2042 complete_ctx.remote_pathp = &remote_path;
2043 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2044 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2045 /* enable ctrl-left-arrow and ctrl-right-arrow */
2046 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2047 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2048 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2049 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2050 /* make ^w match ksh behaviour */
2051 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2053 #endif /* USE_LIBEDIT */
2055 remote_path = do_realpath(conn, ".");
2056 if (remote_path == NULL)
2059 if (file1 != NULL) {
2060 dir = xstrdup(file1);
2061 dir = make_absolute(dir, remote_path);
2063 if (remote_is_dir(conn, dir) && file2 == NULL) {
2065 printf("Changing to: %s\n", dir);
2066 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2067 if (parse_dispatch_command(conn, cmd,
2068 &remote_path, 1) != 0) {
2075 /* XXX this is wrong wrt quoting */
2076 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2077 global_aflag ? " -a" : "", dir,
2078 file2 == NULL ? "" : " ",
2079 file2 == NULL ? "" : file2);
2080 err = parse_dispatch_command(conn, cmd,
2090 setvbuf(stdout, NULL, _IOLBF, 0);
2091 setvbuf(infile, NULL, _IOLBF, 0);
2093 interactive = !batchmode && isatty(STDIN_FILENO);
2098 signal(SIGINT, SIG_IGN);
2103 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2108 if (!interactive) { /* Echo command */
2109 printf("sftp> %s", cmd);
2110 if (strlen(cmd) > 0 &&
2111 cmd[strlen(cmd) - 1] != '\n')
2119 if ((line = el_gets(el, &count)) == NULL ||
2124 history(hl, &hev, H_ENTER, line);
2125 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2126 fprintf(stderr, "Error: input line too long\n");
2129 #endif /* USE_LIBEDIT */
2132 cp = strrchr(cmd, '\n');
2136 /* Handle user interrupts gracefully during commands */
2138 signal(SIGINT, cmd_interrupt);
2140 err = parse_dispatch_command(conn, cmd, &remote_path,
2151 #endif /* USE_LIBEDIT */
2153 /* err == 1 signifies normal "quit" exit */
2154 return (err >= 0 ? 0 : -1);
2158 connect_to_server(char *path, char **args, int *in, int *out)
2163 int pin[2], pout[2];
2165 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2166 fatal("pipe: %s", strerror(errno));
2171 #else /* USE_PIPES */
2174 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2175 fatal("socketpair: %s", strerror(errno));
2176 *in = *out = inout[0];
2177 c_in = c_out = inout[1];
2178 #endif /* USE_PIPES */
2180 if ((sshpid = fork()) == -1)
2181 fatal("fork: %s", strerror(errno));
2182 else if (sshpid == 0) {
2183 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2184 (dup2(c_out, STDOUT_FILENO) == -1)) {
2185 fprintf(stderr, "dup2: %s\n", strerror(errno));
2194 * The underlying ssh is in the same process group, so we must
2195 * ignore SIGINT if we want to gracefully abort commands,
2196 * otherwise the signal will make it to the ssh process and
2197 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2198 * underlying ssh, it must *not* ignore that signal.
2200 signal(SIGINT, SIG_IGN);
2201 signal(SIGTERM, SIG_DFL);
2203 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2207 signal(SIGTERM, killchild);
2208 signal(SIGINT, killchild);
2209 signal(SIGHUP, killchild);
2217 extern char *__progname;
2220 "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2221 " [-D sftp_server_path] [-F ssh_config] "
2222 "[-i identity_file] [-l limit]\n"
2223 " [-o ssh_option] [-P port] [-R num_requests] "
2225 " [-s subsystem | sftp_server] host\n"
2226 " %s [user@]host[:file ...]\n"
2227 " %s [user@]host[:dir[/]]\n"
2228 " %s -b batchfile [user@]host\n",
2229 __progname, __progname, __progname, __progname);
2234 main(int argc, char **argv)
2236 int in, out, ch, err;
2237 char *host = NULL, *userhost, *cp, *file2 = NULL;
2238 int debug_level = 0, sshver = 2;
2239 char *file1 = NULL, *sftp_server = NULL;
2240 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2242 LogLevel ll = SYSLOG_LEVEL_INFO;
2245 extern char *optarg;
2246 struct sftp_conn *conn;
2247 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2248 size_t num_requests = DEFAULT_NUM_REQUESTS;
2249 long long limit_kbps = 0;
2251 ssh_malloc_init(); /* must be called before any mallocs */
2252 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2254 setlocale(LC_CTYPE, "");
2256 __progname = ssh_get_progname(argv[0]);
2257 memset(&args, '\0', sizeof(args));
2259 addargs(&args, "%s", ssh_program);
2260 addargs(&args, "-oForwardX11 no");
2261 addargs(&args, "-oForwardAgent no");
2262 addargs(&args, "-oPermitLocalCommand no");
2263 addargs(&args, "-oClearAllForwardings yes");
2265 ll = SYSLOG_LEVEL_INFO;
2268 while ((ch = getopt(argc, argv,
2269 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2271 /* Passed through to ssh(1) */
2275 addargs(&args, "-%c", ch);
2277 /* Passed through to ssh(1) with argument */
2282 addargs(&args, "-%c", ch);
2283 addargs(&args, "%s", optarg);
2286 ll = SYSLOG_LEVEL_ERROR;
2289 addargs(&args, "-%c", ch);
2292 addargs(&args, "-oPort %s", optarg);
2295 if (debug_level < 3) {
2296 addargs(&args, "-v");
2297 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2303 if (sftp_server == NULL)
2304 sftp_server = _PATH_SFTP_SERVER;
2313 copy_buffer_len = strtol(optarg, &cp, 10);
2314 if (copy_buffer_len == 0 || *cp != '\0')
2315 fatal("Invalid buffer size \"%s\"", optarg);
2319 fatal("Batch file already specified.");
2321 /* Allow "-" as stdin */
2322 if (strcmp(optarg, "-") != 0 &&
2323 (infile = fopen(optarg, "r")) == NULL)
2324 fatal("%s (%s).", strerror(errno), optarg);
2326 quiet = batchmode = 1;
2327 addargs(&args, "-obatchmode yes");
2336 sftp_direct = optarg;
2339 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2343 limit_kbps *= 1024; /* kbps */
2349 num_requests = strtol(optarg, &cp, 10);
2350 if (num_requests == 0 || *cp != '\0')
2351 fatal("Invalid number of requests \"%s\"",
2355 sftp_server = optarg;
2358 ssh_program = optarg;
2359 replacearg(&args, 0, "%s", ssh_program);
2367 if (!isatty(STDERR_FILENO))
2370 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2372 if (sftp_direct == NULL) {
2373 if (optind == argc || argc > (optind + 2))
2376 userhost = xstrdup(argv[optind]);
2377 file2 = argv[optind+1];
2379 if ((host = strrchr(userhost, '@')) == NULL)
2384 fprintf(stderr, "Missing username\n");
2387 addargs(&args, "-l");
2388 addargs(&args, "%s", userhost);
2391 if ((cp = colon(host)) != NULL) {
2396 host = cleanhostname(host);
2398 fprintf(stderr, "Missing hostname\n");
2402 addargs(&args, "-oProtocol %d", sshver);
2404 /* no subsystem if the server-spec contains a '/' */
2405 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2406 addargs(&args, "-s");
2408 addargs(&args, "--");
2409 addargs(&args, "%s", host);
2410 addargs(&args, "%s", (sftp_server != NULL ?
2411 sftp_server : "sftp"));
2413 connect_to_server(ssh_program, args.list, &in, &out);
2416 addargs(&args, "sftp-server");
2418 connect_to_server(sftp_direct, args.list, &in, &out);
2422 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2424 fatal("Couldn't initialise connection to server");
2427 if (sftp_direct == NULL)
2428 fprintf(stderr, "Connected to %s.\n", host);
2430 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2433 err = interactive_loop(conn, file1, file2);
2435 #if !defined(USE_PIPES)
2436 shutdown(in, SHUT_RDWR);
2437 shutdown(out, SHUT_RDWR);
2445 while (waitpid(sshpid, NULL, 0) == -1)
2447 fatal("Couldn't wait for ssh process: %s",
2450 exit(err == 0 ? 0 : 1);