1 /* $OpenBSD: sftp.c,v 1.158 2013/11/20 20:54:10 deraadt Exp $ */
4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 #include <sys/types.h>
23 #include <sys/ioctl.h>
24 #ifdef HAVE_SYS_STAT_H
25 # include <sys/stat.h>
27 #include <sys/param.h>
28 #include <sys/socket.h>
30 #ifdef HAVE_SYS_STATVFS_H
31 #include <sys/statvfs.h>
49 typedef void EditLine;
64 #include "pathnames.h"
69 #include "sftp-common.h"
70 #include "sftp-client.h"
72 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
73 #define DEFAULT_NUM_REQUESTS 256 /* # concurrent outstanding requests */
75 /* File to read commands from */
78 /* Are we in batchfile mode? */
81 /* PID of ssh transport process */
82 static pid_t sshpid = -1;
84 /* Suppress diagnositic messages */
87 /* This is set to 0 if the progressmeter is not desired. */
90 /* When this option is set, we always recursively download/upload directories */
93 /* When this option is set, we resume download if possible */
96 /* When this option is set, the file transfers will always preserve times */
99 /* When this option is set, transfers will have fsync() called on each file */
100 int global_fflag = 0;
102 /* SIGINT received during command processing */
103 volatile sig_atomic_t interrupted = 0;
105 /* I wish qsort() took a separate ctx for the comparison function...*/
108 /* Context used for commandline completion */
109 struct complete_ctx {
110 struct sftp_conn *conn;
114 int remote_glob(struct sftp_conn *, const char *, int,
115 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
117 extern char *__progname;
119 /* Separators for interactive commands */
120 #define WHITESPACE " \t\r\n"
123 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
124 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
125 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
126 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
127 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
128 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
129 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
130 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
131 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
133 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
134 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
136 /* Commands for interactive mode */
172 /* Type of completion */
177 static const struct CMD cmds[] = {
178 { "bye", I_QUIT, NOARGS },
179 { "cd", I_CHDIR, REMOTE },
180 { "chdir", I_CHDIR, REMOTE },
181 { "chgrp", I_CHGRP, REMOTE },
182 { "chmod", I_CHMOD, REMOTE },
183 { "chown", I_CHOWN, REMOTE },
184 { "df", I_DF, REMOTE },
185 { "dir", I_LS, REMOTE },
186 { "exit", I_QUIT, NOARGS },
187 { "get", I_GET, REMOTE },
188 { "help", I_HELP, NOARGS },
189 { "lcd", I_LCHDIR, LOCAL },
190 { "lchdir", I_LCHDIR, LOCAL },
191 { "lls", I_LLS, LOCAL },
192 { "lmkdir", I_LMKDIR, LOCAL },
193 { "ln", I_LINK, REMOTE },
194 { "lpwd", I_LPWD, LOCAL },
195 { "ls", I_LS, REMOTE },
196 { "lumask", I_LUMASK, NOARGS },
197 { "mkdir", I_MKDIR, REMOTE },
198 { "mget", I_GET, REMOTE },
199 { "mput", I_PUT, LOCAL },
200 { "progress", I_PROGRESS, NOARGS },
201 { "put", I_PUT, LOCAL },
202 { "pwd", I_PWD, REMOTE },
203 { "quit", I_QUIT, NOARGS },
204 { "reget", I_REGET, REMOTE },
205 { "rename", I_RENAME, REMOTE },
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 "help Display this help text\n"
256 "lcd path Change local directory to 'path'\n"
257 "lls [ls-options [path]] Display local directory listing\n"
258 "lmkdir path Create local directory\n"
259 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
260 "lpwd Print local working directory\n"
261 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
262 "lumask umask Set local umask to 'umask'\n"
263 "mkdir path Create remote directory\n"
264 "progress Toggle display of progress meter\n"
265 "put [-Ppr] local [remote] Upload file\n"
266 "pwd Display remote working directory\n"
268 "rename oldpath newpath Rename remote file\n"
269 "rm path Delete remote file\n"
270 "rmdir path Remove remote directory\n"
271 "symlink oldpath newpath Symlink remote file\n"
272 "version Show SFTP version\n"
273 "!command Execute 'command' in local shell\n"
274 "! Escape to local shell\n"
275 "? Synonym for help\n");
279 local_do_shell(const char *args)
288 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
289 shell = _PATH_BSHELL;
291 if ((pid = fork()) == -1)
292 fatal("Couldn't fork: %s", strerror(errno));
295 /* XXX: child has pipe fds to ssh subproc open - issue? */
297 debug3("Executing %s -c \"%s\"", shell, args);
298 execl(shell, shell, "-c", args, (char *)NULL);
300 debug3("Executing %s", shell);
301 execl(shell, shell, (char *)NULL);
303 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
307 while (waitpid(pid, &status, 0) == -1)
309 fatal("Couldn't wait for child: %s", strerror(errno));
310 if (!WIFEXITED(status))
311 error("Shell exited abnormally");
312 else if (WEXITSTATUS(status))
313 error("Shell exited with status %d", WEXITSTATUS(status));
317 local_do_ls(const char *args)
320 local_do_shell(_PATH_LS);
322 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
323 char *buf = xmalloc(len);
325 /* XXX: quoting - rip quoting code from ftp? */
326 snprintf(buf, len, _PATH_LS " %s", args);
332 /* Strip one path (usually the pwd) from the start of another */
334 path_strip(char *path, char *strip)
339 return (xstrdup(path));
342 if (strncmp(path, strip, len) == 0) {
343 if (strip[len - 1] != '/' && path[len] == '/')
345 return (xstrdup(path + len));
348 return (xstrdup(path));
352 make_absolute(char *p, char *pwd)
357 if (p && p[0] != '/') {
358 abs_str = path_append(pwd, p);
366 parse_getput_flags(const char *cmd, char **argv, int argc,
367 int *aflag, int *fflag, int *pflag, int *rflag)
369 extern int opterr, optind, optopt, optreset;
372 optind = optreset = 1;
375 *aflag = *fflag = *rflag = *pflag = 0;
376 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
393 error("%s: Invalid flag -%c", cmd, optopt);
402 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
404 extern int opterr, optind, optopt, optreset;
407 optind = optreset = 1;
411 while ((ch = getopt(argc, argv, "s")) != -1) {
417 error("%s: Invalid flag -%c", cmd, optopt);
426 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
428 extern int opterr, optind, optopt, optreset;
431 optind = optreset = 1;
435 while ((ch = getopt(argc, argv, "l")) != -1) {
441 error("%s: Invalid flag -%c", cmd, optopt);
450 parse_ls_flags(char **argv, int argc, int *lflag)
452 extern int opterr, optind, optopt, optreset;
455 optind = optreset = 1;
458 *lflag = LS_NAME_SORT;
459 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
462 *lflag &= ~VIEW_FLAGS;
463 *lflag |= LS_SHORT_VIEW;
466 *lflag &= ~SORT_FLAGS;
467 *lflag |= LS_SIZE_SORT;
470 *lflag |= LS_SHOW_ALL;
473 *lflag &= ~SORT_FLAGS;
476 *lflag |= LS_SI_UNITS;
479 *lflag &= ~LS_SHORT_VIEW;
480 *lflag |= LS_LONG_VIEW;
483 *lflag &= ~LS_SHORT_VIEW;
484 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
487 *lflag |= LS_REVERSE_SORT;
490 *lflag &= ~SORT_FLAGS;
491 *lflag |= LS_TIME_SORT;
494 error("ls: Invalid flag -%c", optopt);
503 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
505 extern int opterr, optind, optopt, optreset;
508 optind = optreset = 1;
512 while ((ch = getopt(argc, argv, "hi")) != -1) {
521 error("%s: Invalid flag -%c", cmd, optopt);
530 parse_no_flags(const char *cmd, char **argv, int argc)
532 extern int opterr, optind, optopt, optreset;
535 optind = optreset = 1;
538 while ((ch = getopt(argc, argv, "")) != -1) {
541 error("%s: Invalid flag -%c", cmd, optopt);
554 /* XXX: report errors? */
555 if (stat(path, &sb) == -1)
558 return(S_ISDIR(sb.st_mode));
562 remote_is_dir(struct sftp_conn *conn, char *path)
566 /* XXX: report errors? */
567 if ((a = do_stat(conn, path, 1)) == NULL)
569 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
571 return(S_ISDIR(a->perm));
574 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
576 pathname_is_dir(char *pathname)
578 size_t l = strlen(pathname);
580 return l > 0 && pathname[l - 1] == '/';
584 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
585 int pflag, int rflag, int resume, int fflag)
587 char *abs_src = NULL;
588 char *abs_dst = NULL;
590 char *filename, *tmp=NULL;
593 abs_src = xstrdup(src);
594 abs_src = make_absolute(abs_src, pwd);
595 memset(&g, 0, sizeof(g));
597 debug3("Looking up %s", abs_src);
598 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
599 error("File \"%s\" not found.", abs_src);
605 * If multiple matches then dst must be a directory or
608 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
609 error("Multiple source paths, but destination "
610 "\"%s\" is not a directory", dst);
615 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
616 tmp = xstrdup(g.gl_pathv[i]);
617 if ((filename = basename(tmp)) == NULL) {
618 error("basename %s: %s", tmp, strerror(errno));
624 if (g.gl_matchc == 1 && dst) {
626 abs_dst = path_append(dst, filename);
628 abs_dst = xstrdup(dst);
631 abs_dst = path_append(dst, filename);
633 abs_dst = xstrdup(filename);
637 resume |= global_aflag;
638 if (!quiet && resume)
639 printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
640 else if (!quiet && !resume)
641 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
642 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
643 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
644 pflag || global_pflag, 1, resume,
645 fflag || global_fflag) == -1)
648 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
649 pflag || global_pflag, resume,
650 fflag || global_fflag) == -1)
664 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
665 int pflag, int rflag, int fflag)
667 char *tmp_dst = NULL;
668 char *abs_dst = NULL;
669 char *tmp = NULL, *filename = NULL;
672 int i, dst_is_dir = 1;
676 tmp_dst = xstrdup(dst);
677 tmp_dst = make_absolute(tmp_dst, pwd);
680 memset(&g, 0, sizeof(g));
681 debug3("Looking up %s", src);
682 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
683 error("File \"%s\" not found.", src);
688 /* If we aren't fetching to pwd then stash this status for later */
690 dst_is_dir = remote_is_dir(conn, tmp_dst);
692 /* If multiple matches, dst may be directory or unspecified */
693 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
694 error("Multiple paths match, but destination "
695 "\"%s\" is not a directory", tmp_dst);
700 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
701 if (stat(g.gl_pathv[i], &sb) == -1) {
703 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
707 tmp = xstrdup(g.gl_pathv[i]);
708 if ((filename = basename(tmp)) == NULL) {
709 error("basename %s: %s", tmp, strerror(errno));
715 if (g.gl_matchc == 1 && tmp_dst) {
716 /* If directory specified, append filename */
718 abs_dst = path_append(tmp_dst, filename);
720 abs_dst = xstrdup(tmp_dst);
721 } else if (tmp_dst) {
722 abs_dst = path_append(tmp_dst, filename);
724 abs_dst = make_absolute(xstrdup(filename), pwd);
729 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
730 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
731 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
732 pflag || global_pflag, 1,
733 fflag || global_fflag) == -1)
736 if (do_upload(conn, g.gl_pathv[i], abs_dst,
737 pflag || global_pflag,
738 fflag || global_fflag) == -1)
751 sdirent_comp(const void *aa, const void *bb)
753 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
754 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
755 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
757 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
758 if (sort_flag & LS_NAME_SORT)
759 return (rmul * strcmp(a->filename, b->filename));
760 else if (sort_flag & LS_TIME_SORT)
761 return (rmul * NCMP(a->a.mtime, b->a.mtime));
762 else if (sort_flag & LS_SIZE_SORT)
763 return (rmul * NCMP(a->a.size, b->a.size));
765 fatal("Unknown ls sort type");
768 /* sftp ls.1 replacement for directories */
770 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
773 u_int c = 1, colspace = 0, columns = 1;
776 if ((n = do_readdir(conn, path, &d)) != 0)
779 if (!(lflag & LS_SHORT_VIEW)) {
780 u_int m = 0, width = 80;
784 /* Count entries for sort and find longest filename */
785 for (n = 0; d[n] != NULL; n++) {
786 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
787 m = MAX(m, strlen(d[n]->filename));
790 /* Add any subpath that also needs to be counted */
791 tmp = path_strip(path, strip_path);
795 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
798 columns = width / (m + 2);
799 columns = MAX(columns, 1);
800 colspace = width / columns;
801 colspace = MIN(colspace, width);
804 if (lflag & SORT_FLAGS) {
805 for (n = 0; d[n] != NULL; n++)
806 ; /* count entries */
807 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
808 qsort(d, n, sizeof(*d), sdirent_comp);
811 for (n = 0; d[n] != NULL && !interrupted; n++) {
814 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
817 tmp = path_append(path, d[n]->filename);
818 fname = path_strip(tmp, strip_path);
821 if (lflag & LS_LONG_VIEW) {
822 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
826 memset(&sb, 0, sizeof(sb));
827 attrib_to_stat(&d[n]->a, &sb);
828 lname = ls_file(fname, &sb, 1,
829 (lflag & LS_SI_UNITS));
830 printf("%s\n", lname);
833 printf("%s\n", d[n]->longname);
835 printf("%-*s", colspace, fname);
846 if (!(lflag & LS_LONG_VIEW) && (c != 1))
849 free_sftp_dirents(d);
853 /* sftp ls.1 replacement which handles path globs */
855 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
862 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
864 memset(&g, 0, sizeof(g));
866 if (remote_glob(conn, path,
867 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
869 (g.gl_pathc && !g.gl_matchc)) {
872 error("Can't ls: \"%s\" not found", path);
880 * If the glob returns a single match and it is a directory,
881 * then just list its contents.
883 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
884 S_ISDIR(g.gl_statv[0]->st_mode)) {
885 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
890 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
893 if (!(lflag & LS_SHORT_VIEW)) {
894 /* Count entries for sort and find longest filename */
895 for (i = 0; g.gl_pathv[i]; i++)
896 m = MAX(m, strlen(g.gl_pathv[i]));
898 columns = width / (m + 2);
899 columns = MAX(columns, 1);
900 colspace = width / columns;
903 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
904 fname = path_strip(g.gl_pathv[i], strip_path);
905 if (lflag & LS_LONG_VIEW) {
906 if (g.gl_statv[i] == NULL) {
907 error("no stat information for %s", fname);
910 lname = ls_file(fname, g.gl_statv[i], 1,
911 (lflag & LS_SI_UNITS));
912 printf("%s\n", lname);
915 printf("%-*s", colspace, fname);
925 if (!(lflag & LS_LONG_VIEW) && (c != 1))
936 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
938 struct sftp_statvfs st;
939 char s_used[FMT_SCALED_STRSIZE];
940 char s_avail[FMT_SCALED_STRSIZE];
941 char s_root[FMT_SCALED_STRSIZE];
942 char s_total[FMT_SCALED_STRSIZE];
943 unsigned long long ffree;
945 if (do_statvfs(conn, path, &st, 1) == -1)
948 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
949 printf(" Inodes Used Avail "
950 "(root) %%Capacity\n");
951 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
952 (unsigned long long)st.f_files,
953 (unsigned long long)(st.f_files - st.f_ffree),
954 (unsigned long long)st.f_favail,
955 (unsigned long long)st.f_ffree, ffree);
957 strlcpy(s_used, "error", sizeof(s_used));
958 strlcpy(s_avail, "error", sizeof(s_avail));
959 strlcpy(s_root, "error", sizeof(s_root));
960 strlcpy(s_total, "error", sizeof(s_total));
961 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
962 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
963 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
964 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
965 printf(" Size Used Avail (root) %%Capacity\n");
966 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
967 s_total, s_used, s_avail, s_root,
968 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
971 printf(" Size Used Avail "
972 "(root) %%Capacity\n");
973 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
974 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
975 (unsigned long long)(st.f_frsize *
976 (st.f_blocks - st.f_bfree) / 1024),
977 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
978 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
979 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
986 * Undo escaping of glob sequences in place. Used to undo extra escaping
987 * applied in makeargv() when the string is destined for a function that
991 undo_glob_escape(char *s)
1026 * Split a string into an argument vector using sh(1)-style quoting,
1027 * comment and escaping rules, but with some tweaks to handle glob(3)
1029 * The "sloppy" flag allows for recovery from missing terminating quote, for
1030 * use in parsing incomplete commandlines during tab autocompletion.
1032 * Returns NULL on error or a NULL-terminated array of arguments.
1034 * If "lastquote" is not NULL, the quoting character used for the last
1035 * argument is placed in *lastquote ("\0", "'" or "\"").
1037 * If "terminated" is not NULL, *terminated will be set to 1 when the
1038 * last argument's quote has been properly terminated or 0 otherwise.
1039 * This parameter is only of use if "sloppy" is set.
1042 #define MAXARGLEN 8192
1044 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1049 static char argvs[MAXARGLEN];
1050 static char *argv[MAXARGS + 1];
1051 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1054 if (strlen(arg) > sizeof(argvs) - 1) {
1056 error("string too long");
1059 if (terminated != NULL)
1061 if (lastquote != NULL)
1066 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1067 error("Too many arguments.");
1070 if (isspace((unsigned char)arg[i])) {
1071 if (state == MA_UNQUOTED) {
1072 /* Terminate current argument */
1076 } else if (state != MA_START)
1077 argvs[j++] = arg[i];
1078 } else if (arg[i] == '"' || arg[i] == '\'') {
1079 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1080 if (state == MA_START) {
1081 argv[argc] = argvs + j;
1083 if (lastquote != NULL)
1084 *lastquote = arg[i];
1085 } else if (state == MA_UNQUOTED)
1087 else if (state == q)
1088 state = MA_UNQUOTED;
1090 argvs[j++] = arg[i];
1091 } else if (arg[i] == '\\') {
1092 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1093 quot = state == MA_SQUOTE ? '\'' : '"';
1094 /* Unescape quote we are in */
1095 /* XXX support \n and friends? */
1096 if (arg[i + 1] == quot) {
1098 argvs[j++] = arg[i];
1099 } else if (arg[i + 1] == '?' ||
1100 arg[i + 1] == '[' || arg[i + 1] == '*') {
1102 * Special case for sftp: append
1103 * double-escaped glob sequence -
1104 * glob will undo one level of
1105 * escaping. NB. string can grow here.
1107 if (j >= sizeof(argvs) - 5)
1108 goto args_too_longs;
1110 argvs[j++] = arg[i++];
1112 argvs[j++] = arg[i];
1114 argvs[j++] = arg[i++];
1115 argvs[j++] = arg[i];
1118 if (state == MA_START) {
1119 argv[argc] = argvs + j;
1120 state = MA_UNQUOTED;
1121 if (lastquote != NULL)
1124 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1125 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1127 * Special case for sftp: append
1128 * escaped glob sequence -
1129 * glob will undo one level of
1132 argvs[j++] = arg[i++];
1133 argvs[j++] = arg[i];
1135 /* Unescape everything */
1136 /* XXX support \n and friends? */
1138 argvs[j++] = arg[i];
1141 } else if (arg[i] == '#') {
1142 if (state == MA_SQUOTE || state == MA_DQUOTE)
1143 argvs[j++] = arg[i];
1146 } else if (arg[i] == '\0') {
1147 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1149 state = MA_UNQUOTED;
1150 if (terminated != NULL)
1154 error("Unterminated quoted argument");
1158 if (state == MA_UNQUOTED) {
1164 if (state == MA_START) {
1165 argv[argc] = argvs + j;
1166 state = MA_UNQUOTED;
1167 if (lastquote != NULL)
1170 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1171 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1173 * Special case for sftp: escape quoted
1174 * glob(3) wildcards. NB. string can grow
1177 if (j >= sizeof(argvs) - 3)
1178 goto args_too_longs;
1180 argvs[j++] = arg[i];
1182 argvs[j++] = arg[i];
1191 parse_args(const char **cpp, int *ignore_errors, int *aflag, int *fflag,
1192 int *hflag, int *iflag, int *lflag, int *pflag, int *rflag, int *sflag,
1193 unsigned long *n_arg, char **path1, char **path2)
1195 const char *cmd, *cp = *cpp;
1199 int i, cmdnum, optidx, argc;
1201 /* Skip leading whitespace */
1202 cp = cp + strspn(cp, WHITESPACE);
1204 /* Check for leading '-' (disable error processing) */
1209 cp = cp + strspn(cp, WHITESPACE);
1212 /* Ignore blank lines and lines which begin with comment '#' char */
1213 if (*cp == '\0' || *cp == '#')
1216 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1219 /* Figure out which command we have */
1220 for (i = 0; cmds[i].c != NULL; i++) {
1221 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1231 } else if (cmdnum == -1) {
1232 error("Invalid command.");
1236 /* Get arguments and parse flags */
1237 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1238 *rflag = *sflag = 0;
1239 *path1 = *path2 = NULL;
1245 if ((optidx = parse_getput_flags(cmd, argv, argc,
1246 aflag, fflag, pflag, rflag)) == -1)
1248 /* Get first pathname (mandatory) */
1249 if (argc - optidx < 1) {
1250 error("You must specify at least one path after a "
1251 "%s command.", cmd);
1254 *path1 = xstrdup(argv[optidx]);
1255 /* Get second pathname (optional) */
1256 if (argc - optidx > 1) {
1257 *path2 = xstrdup(argv[optidx + 1]);
1258 /* Destination is not globbed */
1259 undo_glob_escape(*path2);
1261 if (*aflag && cmdnum == I_PUT) {
1262 /* XXX implement resume for uploads */
1263 error("Resume is not supported for uploads");
1268 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1270 goto parse_two_paths;
1272 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1274 goto parse_two_paths;
1276 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1279 if (argc - optidx < 2) {
1280 error("You must specify two paths after a %s "
1284 *path1 = xstrdup(argv[optidx]);
1285 *path2 = xstrdup(argv[optidx + 1]);
1286 /* Paths are not globbed */
1287 undo_glob_escape(*path1);
1288 undo_glob_escape(*path2);
1296 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1298 /* Get pathname (mandatory) */
1299 if (argc - optidx < 1) {
1300 error("You must specify a path after a %s command.",
1304 *path1 = xstrdup(argv[optidx]);
1305 /* Only "rm" globs */
1307 undo_glob_escape(*path1);
1310 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1313 /* Default to current directory if no path specified */
1314 if (argc - optidx < 1)
1317 *path1 = xstrdup(argv[optidx]);
1318 undo_glob_escape(*path1);
1322 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1324 /* Path is optional */
1325 if (argc - optidx > 0)
1326 *path1 = xstrdup(argv[optidx]);
1329 /* Skip ls command and following whitespace */
1330 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1332 /* Uses the rest of the line */
1339 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1341 /* Get numeric arg (mandatory) */
1342 if (argc - optidx < 1)
1345 l = strtol(argv[optidx], &cp2, base);
1346 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1347 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1350 error("You must supply a numeric argument "
1351 "to the %s command.", cmd);
1355 if (cmdnum == I_LUMASK)
1357 /* Get pathname (mandatory) */
1358 if (argc - optidx < 2) {
1359 error("You must specify a path after a %s command.",
1363 *path1 = xstrdup(argv[optidx + 1]);
1371 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1375 fatal("Command not implemented");
1383 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1386 char *path1, *path2, *tmp;
1387 int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1388 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1390 unsigned long n_arg = 0;
1392 char path_buf[MAXPATHLEN];
1396 path1 = path2 = NULL;
1397 cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1398 &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1399 if (ignore_errors != 0)
1402 memset(&g, 0, sizeof(g));
1404 /* Perform command */
1410 /* Unrecognized command */
1417 err = process_get(conn, path1, path2, *pwd, pflag,
1418 rflag, aflag, fflag);
1421 err = process_put(conn, path1, path2, *pwd, pflag,
1425 path1 = make_absolute(path1, *pwd);
1426 path2 = make_absolute(path2, *pwd);
1427 err = do_rename(conn, path1, path2, lflag);
1433 path1 = make_absolute(path1, *pwd);
1434 path2 = make_absolute(path2, *pwd);
1435 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1438 path1 = make_absolute(path1, *pwd);
1439 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1440 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1442 printf("Removing %s\n", g.gl_pathv[i]);
1443 err = do_rm(conn, g.gl_pathv[i]);
1444 if (err != 0 && err_abort)
1449 path1 = make_absolute(path1, *pwd);
1451 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1453 err = do_mkdir(conn, path1, &a, 1);
1456 path1 = make_absolute(path1, *pwd);
1457 err = do_rmdir(conn, path1);
1460 path1 = make_absolute(path1, *pwd);
1461 if ((tmp = do_realpath(conn, path1)) == NULL) {
1465 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1470 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1471 error("Can't change directory: Can't check target");
1476 if (!S_ISDIR(aa->perm)) {
1477 error("Can't change directory: \"%s\" is not "
1478 "a directory", tmp);
1488 do_ls_dir(conn, *pwd, *pwd, lflag);
1492 /* Strip pwd off beginning of non-absolute paths */
1497 path1 = make_absolute(path1, *pwd);
1498 err = do_globbed_ls(conn, path1, tmp, lflag);
1501 /* Default to current directory if no path specified */
1503 path1 = xstrdup(*pwd);
1504 path1 = make_absolute(path1, *pwd);
1505 err = do_df(conn, path1, hflag, iflag);
1508 if (chdir(path1) == -1) {
1509 error("Couldn't change local directory to "
1510 "\"%s\": %s", path1, strerror(errno));
1515 if (mkdir(path1, 0777) == -1) {
1516 error("Couldn't create local directory "
1517 "\"%s\": %s", path1, strerror(errno));
1525 local_do_shell(cmd);
1529 printf("Local umask: %03lo\n", n_arg);
1532 path1 = make_absolute(path1, *pwd);
1534 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1536 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1537 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1539 printf("Changing mode on %s\n", g.gl_pathv[i]);
1540 err = do_setstat(conn, g.gl_pathv[i], &a);
1541 if (err != 0 && err_abort)
1547 path1 = make_absolute(path1, *pwd);
1548 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1549 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1550 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1557 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1558 error("Can't get current ownership of "
1559 "remote file \"%s\"", g.gl_pathv[i]);
1566 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1567 if (cmdnum == I_CHOWN) {
1569 printf("Changing owner on %s\n",
1574 printf("Changing group on %s\n",
1578 err = do_setstat(conn, g.gl_pathv[i], aa);
1579 if (err != 0 && err_abort)
1584 printf("Remote working directory: %s\n", *pwd);
1587 if (!getcwd(path_buf, sizeof(path_buf))) {
1588 error("Couldn't get local cwd: %s", strerror(errno));
1592 printf("Local working directory: %s\n", path_buf);
1595 /* Processed below */
1601 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1604 showprogress = !showprogress;
1606 printf("Progress meter enabled\n");
1608 printf("Progress meter disabled\n");
1611 fatal("%d is not implemented", cmdnum);
1619 /* If an unignored error occurs in batch mode we should abort. */
1620 if (err_abort && err != 0)
1622 else if (cmdnum == I_QUIT)
1630 prompt(EditLine *el)
1635 /* Display entries in 'list' after skipping the first 'len' chars */
1637 complete_display(char **list, u_int len)
1639 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1643 /* Count entries for sort and find longest */
1644 for (y = 0; list[y]; y++)
1645 m = MAX(m, strlen(list[y]));
1647 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1650 m = m > len ? m - len : 0;
1651 columns = width / (m + 2);
1652 columns = MAX(columns, 1);
1653 colspace = width / columns;
1654 colspace = MIN(colspace, width);
1658 for (y = 0; list[y]; y++) {
1659 llen = strlen(list[y]);
1660 tmp = llen > len ? list[y] + len : "";
1661 printf("%-*s", colspace, tmp);
1672 * Given a "list" of words that begin with a common prefix of "word",
1673 * attempt to find an autocompletion to extends "word" by the next
1674 * characters common to all entries in "list".
1677 complete_ambiguous(const char *word, char **list, size_t count)
1683 u_int y, matchlen = strlen(list[0]);
1685 /* Find length of common stem */
1686 for (y = 1; list[y]; y++) {
1689 for (x = 0; x < matchlen; x++)
1690 if (list[0][x] != list[y][x])
1696 if (matchlen > strlen(word)) {
1697 char *tmp = xstrdup(list[0]);
1699 tmp[matchlen] = '\0';
1704 return xstrdup(word);
1707 /* Autocomplete a sftp command */
1709 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1712 u_int y, count = 0, cmdlen, tmplen;
1713 char *tmp, **list, argterm[3];
1716 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1718 /* No command specified: display all available commands */
1720 for (y = 0; cmds[y].c; y++)
1721 list[count++] = xstrdup(cmds[y].c);
1724 complete_display(list, 0);
1726 for (y = 0; list[y] != NULL; y++)
1732 /* Prepare subset of commands that start with "cmd" */
1733 cmdlen = strlen(cmd);
1734 for (y = 0; cmds[y].c; y++) {
1735 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1736 list[count++] = xstrdup(cmds[y].c);
1745 /* Complete ambigious command */
1746 tmp = complete_ambiguous(cmd, list, count);
1748 complete_display(list, 0);
1750 for (y = 0; list[y]; y++)
1755 tmplen = strlen(tmp);
1756 cmdlen = strlen(cmd);
1757 /* If cmd may be extended then do so */
1758 if (tmplen > cmdlen)
1759 if (el_insertstr(el, tmp + cmdlen) == -1)
1760 fatal("el_insertstr failed.");
1762 /* Terminate argument cleanly */
1766 argterm[y++] = quote;
1767 if (lastarg || *(lf->cursor) != ' ')
1770 if (y > 0 && el_insertstr(el, argterm) == -1)
1771 fatal("el_insertstr failed.");
1780 * Determine whether a particular sftp command's arguments (if any)
1781 * represent local or remote files.
1784 complete_is_remote(char *cmd) {
1790 for (i = 0; cmds[i].c; i++) {
1791 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1798 /* Autocomplete a filename "file" */
1800 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1801 char *file, int remote, int lastarg, char quote, int terminated)
1804 char *tmp, *tmp2, ins[8];
1805 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1809 /* Glob from "file" location */
1813 xasprintf(&tmp, "%s*", file);
1815 /* Check if the path is absolute. */
1816 isabs = tmp[0] == '/';
1818 memset(&g, 0, sizeof(g));
1819 if (remote != LOCAL) {
1820 tmp = make_absolute(tmp, remote_path);
1821 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1823 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1825 /* Determine length of pwd so we can trim completion display */
1826 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1827 /* Terminate counting on first unescaped glob metacharacter */
1828 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1829 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1833 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1835 if (tmp[tmplen] == '/')
1836 pwdlen = tmplen + 1; /* track last seen '/' */
1840 if (g.gl_matchc == 0)
1843 if (g.gl_matchc > 1)
1844 complete_display(g.gl_pathv, pwdlen);
1847 /* Don't try to extend globs */
1848 if (file == NULL || hadglob)
1851 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1852 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1858 tmplen = strlen(tmp);
1859 filelen = strlen(file);
1861 /* Count the number of escaped characters in the input string. */
1863 for (i = 0; i < filelen; i++) {
1864 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1871 if (tmplen > (filelen - cesc)) {
1872 tmp2 = tmp + filelen - cesc;
1874 /* quote argument on way out */
1875 for (i = 0; i < len; i += clen) {
1876 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1877 (size_t)clen > sizeof(ins) - 2)
1878 fatal("invalid multibyte character");
1880 memcpy(ins + 1, tmp2 + i, clen);
1881 ins[clen + 1] = '\0';
1891 if (quote == '\0' || tmp2[i] == quote) {
1892 if (el_insertstr(el, ins) == -1)
1893 fatal("el_insertstr "
1899 if (el_insertstr(el, ins + 1) == -1)
1900 fatal("el_insertstr failed.");
1907 if (g.gl_matchc == 1) {
1911 if (*(lf->cursor - 1) != '/' &&
1912 (lastarg || *(lf->cursor) != ' '))
1915 if (i > 0 && el_insertstr(el, ins) == -1)
1916 fatal("el_insertstr failed.");
1925 /* tab-completion hook function, called via libedit */
1926 static unsigned char
1927 complete(EditLine *el, int ch)
1929 char **argv, *line, quote;
1931 u_int cursor, len, terminated, ret = CC_ERROR;
1933 struct complete_ctx *complete_ctx;
1936 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1937 fatal("%s: el_get failed", __func__);
1939 /* Figure out which argument the cursor points to */
1940 cursor = lf->cursor - lf->buffer;
1941 line = (char *)xmalloc(cursor + 1);
1942 memcpy(line, lf->buffer, cursor);
1943 line[cursor] = '\0';
1944 argv = makeargv(line, &carg, 1, "e, &terminated);
1947 /* Get all the arguments on the line */
1948 len = lf->lastchar - lf->buffer;
1949 line = (char *)xmalloc(len + 1);
1950 memcpy(line, lf->buffer, len);
1952 argv = makeargv(line, &argc, 1, NULL, NULL);
1954 /* Ensure cursor is at EOL or a argument boundary */
1955 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1956 line[cursor] != '\n') {
1962 /* Show all available commands */
1963 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1965 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1966 /* Handle the command parsing */
1967 if (complete_cmd_parse(el, argv[0], argc == carg,
1968 quote, terminated) != 0)
1970 } else if (carg >= 1) {
1971 /* Handle file parsing */
1972 int remote = complete_is_remote(argv[0]);
1973 char *filematch = NULL;
1975 if (carg > 1 && line[cursor-1] != ' ')
1976 filematch = argv[carg - 1];
1979 complete_match(el, complete_ctx->conn,
1980 *complete_ctx->remote_pathp, filematch,
1981 remote, carg == argc, quote, terminated) != 0)
1988 #endif /* USE_LIBEDIT */
1991 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1996 int err, interactive;
1997 EditLine *el = NULL;
2001 extern char *__progname;
2002 struct complete_ctx complete_ctx;
2004 if (!batchmode && isatty(STDIN_FILENO)) {
2005 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2006 fatal("Couldn't initialise editline");
2007 if ((hl = history_init()) == NULL)
2008 fatal("Couldn't initialise editline history");
2009 history(hl, &hev, H_SETSIZE, 100);
2010 el_set(el, EL_HIST, history, hl);
2012 el_set(el, EL_PROMPT, prompt);
2013 el_set(el, EL_EDITOR, "emacs");
2014 el_set(el, EL_TERMINAL, NULL);
2015 el_set(el, EL_SIGNAL, 1);
2016 el_source(el, NULL);
2018 /* Tab Completion */
2019 el_set(el, EL_ADDFN, "ftp-complete",
2020 "Context sensitive argument completion", complete);
2021 complete_ctx.conn = conn;
2022 complete_ctx.remote_pathp = &remote_path;
2023 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2024 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2025 /* enable ctrl-left-arrow and ctrl-right-arrow */
2026 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2027 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2028 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2029 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2030 /* make ^w match ksh behaviour */
2031 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2033 #endif /* USE_LIBEDIT */
2035 remote_path = do_realpath(conn, ".");
2036 if (remote_path == NULL)
2039 if (file1 != NULL) {
2040 dir = xstrdup(file1);
2041 dir = make_absolute(dir, remote_path);
2043 if (remote_is_dir(conn, dir) && file2 == NULL) {
2045 printf("Changing to: %s\n", dir);
2046 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2047 if (parse_dispatch_command(conn, cmd,
2048 &remote_path, 1) != 0) {
2055 /* XXX this is wrong wrt quoting */
2056 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2057 global_aflag ? " -a" : "", dir,
2058 file2 == NULL ? "" : " ",
2059 file2 == NULL ? "" : file2);
2060 err = parse_dispatch_command(conn, cmd,
2073 interactive = !batchmode && isatty(STDIN_FILENO);
2078 signal(SIGINT, SIG_IGN);
2083 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2088 if (!interactive) { /* Echo command */
2089 printf("sftp> %s", cmd);
2090 if (strlen(cmd) > 0 &&
2091 cmd[strlen(cmd) - 1] != '\n')
2099 if ((line = el_gets(el, &count)) == NULL ||
2104 history(hl, &hev, H_ENTER, line);
2105 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2106 fprintf(stderr, "Error: input line too long\n");
2109 #endif /* USE_LIBEDIT */
2112 cp = strrchr(cmd, '\n');
2116 /* Handle user interrupts gracefully during commands */
2118 signal(SIGINT, cmd_interrupt);
2120 err = parse_dispatch_command(conn, cmd, &remote_path,
2131 #endif /* USE_LIBEDIT */
2133 /* err == 1 signifies normal "quit" exit */
2134 return (err >= 0 ? 0 : -1);
2138 connect_to_server(char *path, char **args, int *in, int *out)
2143 int pin[2], pout[2];
2145 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2146 fatal("pipe: %s", strerror(errno));
2151 #else /* USE_PIPES */
2154 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2155 fatal("socketpair: %s", strerror(errno));
2156 *in = *out = inout[0];
2157 c_in = c_out = inout[1];
2158 #endif /* USE_PIPES */
2160 if ((sshpid = fork()) == -1)
2161 fatal("fork: %s", strerror(errno));
2162 else if (sshpid == 0) {
2163 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2164 (dup2(c_out, STDOUT_FILENO) == -1)) {
2165 fprintf(stderr, "dup2: %s\n", strerror(errno));
2174 * The underlying ssh is in the same process group, so we must
2175 * ignore SIGINT if we want to gracefully abort commands,
2176 * otherwise the signal will make it to the ssh process and
2177 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2178 * underlying ssh, it must *not* ignore that signal.
2180 signal(SIGINT, SIG_IGN);
2181 signal(SIGTERM, SIG_DFL);
2183 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2187 signal(SIGTERM, killchild);
2188 signal(SIGINT, killchild);
2189 signal(SIGHUP, killchild);
2197 extern char *__progname;
2200 "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2201 " [-D sftp_server_path] [-F ssh_config] "
2202 "[-i identity_file] [-l limit]\n"
2203 " [-o ssh_option] [-P port] [-R num_requests] "
2205 " [-s subsystem | sftp_server] host\n"
2206 " %s [user@]host[:file ...]\n"
2207 " %s [user@]host[:dir[/]]\n"
2208 " %s -b batchfile [user@]host\n",
2209 __progname, __progname, __progname, __progname);
2214 main(int argc, char **argv)
2216 int in, out, ch, err;
2217 char *host = NULL, *userhost, *cp, *file2 = NULL;
2218 int debug_level = 0, sshver = 2;
2219 char *file1 = NULL, *sftp_server = NULL;
2220 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2222 LogLevel ll = SYSLOG_LEVEL_INFO;
2225 extern char *optarg;
2226 struct sftp_conn *conn;
2227 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2228 size_t num_requests = DEFAULT_NUM_REQUESTS;
2229 long long limit_kbps = 0;
2231 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2233 setlocale(LC_CTYPE, "");
2235 __progname = ssh_get_progname(argv[0]);
2236 memset(&args, '\0', sizeof(args));
2238 addargs(&args, "%s", ssh_program);
2239 addargs(&args, "-oForwardX11 no");
2240 addargs(&args, "-oForwardAgent no");
2241 addargs(&args, "-oPermitLocalCommand no");
2242 addargs(&args, "-oClearAllForwardings yes");
2244 ll = SYSLOG_LEVEL_INFO;
2247 while ((ch = getopt(argc, argv,
2248 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2250 /* Passed through to ssh(1) */
2254 addargs(&args, "-%c", ch);
2256 /* Passed through to ssh(1) with argument */
2261 addargs(&args, "-%c", ch);
2262 addargs(&args, "%s", optarg);
2265 ll = SYSLOG_LEVEL_ERROR;
2268 addargs(&args, "-%c", ch);
2271 addargs(&args, "-oPort %s", optarg);
2274 if (debug_level < 3) {
2275 addargs(&args, "-v");
2276 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2282 if (sftp_server == NULL)
2283 sftp_server = _PATH_SFTP_SERVER;
2292 copy_buffer_len = strtol(optarg, &cp, 10);
2293 if (copy_buffer_len == 0 || *cp != '\0')
2294 fatal("Invalid buffer size \"%s\"", optarg);
2298 fatal("Batch file already specified.");
2300 /* Allow "-" as stdin */
2301 if (strcmp(optarg, "-") != 0 &&
2302 (infile = fopen(optarg, "r")) == NULL)
2303 fatal("%s (%s).", strerror(errno), optarg);
2305 quiet = batchmode = 1;
2306 addargs(&args, "-obatchmode yes");
2315 sftp_direct = optarg;
2318 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2322 limit_kbps *= 1024; /* kbps */
2328 num_requests = strtol(optarg, &cp, 10);
2329 if (num_requests == 0 || *cp != '\0')
2330 fatal("Invalid number of requests \"%s\"",
2334 sftp_server = optarg;
2337 ssh_program = optarg;
2338 replacearg(&args, 0, "%s", ssh_program);
2346 if (!isatty(STDERR_FILENO))
2349 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2351 if (sftp_direct == NULL) {
2352 if (optind == argc || argc > (optind + 2))
2355 userhost = xstrdup(argv[optind]);
2356 file2 = argv[optind+1];
2358 if ((host = strrchr(userhost, '@')) == NULL)
2363 fprintf(stderr, "Missing username\n");
2366 addargs(&args, "-l");
2367 addargs(&args, "%s", userhost);
2370 if ((cp = colon(host)) != NULL) {
2375 host = cleanhostname(host);
2377 fprintf(stderr, "Missing hostname\n");
2381 addargs(&args, "-oProtocol %d", sshver);
2383 /* no subsystem if the server-spec contains a '/' */
2384 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2385 addargs(&args, "-s");
2387 addargs(&args, "--");
2388 addargs(&args, "%s", host);
2389 addargs(&args, "%s", (sftp_server != NULL ?
2390 sftp_server : "sftp"));
2392 connect_to_server(ssh_program, args.list, &in, &out);
2395 addargs(&args, "sftp-server");
2397 connect_to_server(sftp_direct, args.list, &in, &out);
2401 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2403 fatal("Couldn't initialise connection to server");
2406 if (sftp_direct == NULL)
2407 fprintf(stderr, "Connected to %s.\n", host);
2409 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2412 err = interactive_loop(conn, file1, file2);
2414 #if !defined(USE_PIPES)
2415 shutdown(in, SHUT_RDWR);
2416 shutdown(out, SHUT_RDWR);
2424 while (waitpid(sshpid, NULL, 0) == -1)
2426 fatal("Couldn't wait for ssh process: %s",
2429 exit(err == 0 ? 0 : 1);