1 /* $OpenBSD: sftp.c,v 1.178 2017/02/15 01:46:47 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;
64 #include "pathnames.h"
71 #include "sftp-common.h"
72 #include "sftp-client.h"
74 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
75 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
77 /* File to read commands from */
80 /* Are we in batchfile mode? */
83 /* PID of ssh transport process */
84 static pid_t sshpid = -1;
86 /* Suppress diagnositic messages */
89 /* This is set to 0 if the progressmeter is not desired. */
92 /* When this option is set, we always recursively download/upload directories */
95 /* When this option is set, we resume download or upload if possible */
98 /* When this option is set, the file transfers will always preserve times */
101 /* When this option is set, transfers will have fsync() called on each file */
102 int global_fflag = 0;
104 /* SIGINT received during command processing */
105 volatile sig_atomic_t interrupted = 0;
107 /* I wish qsort() took a separate ctx for the comparison function...*/
110 /* Context used for commandline completion */
111 struct complete_ctx {
112 struct sftp_conn *conn;
116 int remote_glob(struct sftp_conn *, const char *, int,
117 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
119 extern char *__progname;
121 /* Separators for interactive commands */
122 #define WHITESPACE " \t\r\n"
125 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
126 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
127 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
128 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
129 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
130 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
131 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
132 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
133 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
135 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
136 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
138 /* Commands for interactive mode */
175 /* Type of completion */
180 static const struct CMD cmds[] = {
181 { "bye", I_QUIT, NOARGS },
182 { "cd", I_CHDIR, REMOTE },
183 { "chdir", I_CHDIR, REMOTE },
184 { "chgrp", I_CHGRP, REMOTE },
185 { "chmod", I_CHMOD, REMOTE },
186 { "chown", I_CHOWN, REMOTE },
187 { "df", I_DF, REMOTE },
188 { "dir", I_LS, REMOTE },
189 { "exit", I_QUIT, NOARGS },
190 { "get", I_GET, REMOTE },
191 { "help", I_HELP, NOARGS },
192 { "lcd", I_LCHDIR, LOCAL },
193 { "lchdir", I_LCHDIR, LOCAL },
194 { "lls", I_LLS, LOCAL },
195 { "lmkdir", I_LMKDIR, LOCAL },
196 { "ln", I_LINK, REMOTE },
197 { "lpwd", I_LPWD, LOCAL },
198 { "ls", I_LS, REMOTE },
199 { "lumask", I_LUMASK, NOARGS },
200 { "mkdir", I_MKDIR, REMOTE },
201 { "mget", I_GET, REMOTE },
202 { "mput", I_PUT, LOCAL },
203 { "progress", I_PROGRESS, NOARGS },
204 { "put", I_PUT, LOCAL },
205 { "pwd", I_PWD, REMOTE },
206 { "quit", I_QUIT, NOARGS },
207 { "reget", I_REGET, REMOTE },
208 { "rename", I_RENAME, REMOTE },
209 { "reput", I_REPUT, LOCAL },
210 { "rm", I_RM, REMOTE },
211 { "rmdir", I_RMDIR, REMOTE },
212 { "symlink", I_SYMLINK, REMOTE },
213 { "version", I_VERSION, NOARGS },
214 { "!", I_SHELL, NOARGS },
215 { "?", I_HELP, NOARGS },
219 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
226 kill(sshpid, SIGTERM);
227 waitpid(sshpid, NULL, 0);
239 while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR)
242 kill(getpid(), SIGSTOP);
247 cmd_interrupt(int signo)
249 const char msg[] = "\rInterrupt \n";
250 int olderrno = errno;
252 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
260 printf("Available commands:\n"
262 "cd path Change remote directory to 'path'\n"
263 "chgrp grp path Change group of file 'path' to 'grp'\n"
264 "chmod mode path Change permissions of file 'path' to 'mode'\n"
265 "chown own path Change owner of file 'path' to 'own'\n"
266 "df [-hi] [path] Display statistics for current directory or\n"
267 " filesystem containing 'path'\n"
269 "get [-afPpRr] remote [local] Download file\n"
270 "reget [-fPpRr] remote [local] Resume download file\n"
271 "reput [-fPpRr] [local] remote Resume upload file\n"
272 "help Display this help text\n"
273 "lcd path Change local directory to 'path'\n"
274 "lls [ls-options [path]] Display local directory listing\n"
275 "lmkdir path Create local directory\n"
276 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
277 "lpwd Print local working directory\n"
278 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
279 "lumask umask Set local umask to 'umask'\n"
280 "mkdir path Create remote directory\n"
281 "progress Toggle display of progress meter\n"
282 "put [-afPpRr] local [remote] Upload file\n"
283 "pwd Display remote working directory\n"
285 "rename oldpath newpath Rename remote file\n"
286 "rm path Delete remote file\n"
287 "rmdir path Remove remote directory\n"
288 "symlink oldpath newpath Symlink remote file\n"
289 "version Show SFTP version\n"
290 "!command Execute 'command' in local shell\n"
291 "! Escape to local shell\n"
292 "? Synonym for help\n");
296 local_do_shell(const char *args)
305 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
306 shell = _PATH_BSHELL;
308 if ((pid = fork()) == -1)
309 fatal("Couldn't fork: %s", strerror(errno));
312 /* XXX: child has pipe fds to ssh subproc open - issue? */
314 debug3("Executing %s -c \"%s\"", shell, args);
315 execl(shell, shell, "-c", args, (char *)NULL);
317 debug3("Executing %s", shell);
318 execl(shell, shell, (char *)NULL);
320 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
324 while (waitpid(pid, &status, 0) == -1)
326 fatal("Couldn't wait for child: %s", strerror(errno));
327 if (!WIFEXITED(status))
328 error("Shell exited abnormally");
329 else if (WEXITSTATUS(status))
330 error("Shell exited with status %d", WEXITSTATUS(status));
334 local_do_ls(const char *args)
337 local_do_shell(_PATH_LS);
339 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
340 char *buf = xmalloc(len);
342 /* XXX: quoting - rip quoting code from ftp? */
343 snprintf(buf, len, _PATH_LS " %s", args);
349 /* Strip one path (usually the pwd) from the start of another */
351 path_strip(const char *path, const char *strip)
356 return (xstrdup(path));
359 if (strncmp(path, strip, len) == 0) {
360 if (strip[len - 1] != '/' && path[len] == '/')
362 return (xstrdup(path + len));
365 return (xstrdup(path));
369 make_absolute(char *p, const char *pwd)
374 if (p && p[0] != '/') {
375 abs_str = path_append(pwd, p);
383 parse_getput_flags(const char *cmd, char **argv, int argc,
384 int *aflag, int *fflag, int *pflag, int *rflag)
386 extern int opterr, optind, optopt, optreset;
389 optind = optreset = 1;
392 *aflag = *fflag = *rflag = *pflag = 0;
393 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
410 error("%s: Invalid flag -%c", cmd, optopt);
419 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
421 extern int opterr, optind, optopt, optreset;
424 optind = optreset = 1;
428 while ((ch = getopt(argc, argv, "s")) != -1) {
434 error("%s: Invalid flag -%c", cmd, optopt);
443 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
445 extern int opterr, optind, optopt, optreset;
448 optind = optreset = 1;
452 while ((ch = getopt(argc, argv, "l")) != -1) {
458 error("%s: Invalid flag -%c", cmd, optopt);
467 parse_ls_flags(char **argv, int argc, int *lflag)
469 extern int opterr, optind, optopt, optreset;
472 optind = optreset = 1;
475 *lflag = LS_NAME_SORT;
476 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
479 *lflag &= ~VIEW_FLAGS;
480 *lflag |= LS_SHORT_VIEW;
483 *lflag &= ~SORT_FLAGS;
484 *lflag |= LS_SIZE_SORT;
487 *lflag |= LS_SHOW_ALL;
490 *lflag &= ~SORT_FLAGS;
493 *lflag |= LS_SI_UNITS;
496 *lflag &= ~LS_SHORT_VIEW;
497 *lflag |= LS_LONG_VIEW;
500 *lflag &= ~LS_SHORT_VIEW;
501 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
504 *lflag |= LS_REVERSE_SORT;
507 *lflag &= ~SORT_FLAGS;
508 *lflag |= LS_TIME_SORT;
511 error("ls: Invalid flag -%c", optopt);
520 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
522 extern int opterr, optind, optopt, optreset;
525 optind = optreset = 1;
529 while ((ch = getopt(argc, argv, "hi")) != -1) {
538 error("%s: Invalid flag -%c", cmd, optopt);
547 parse_no_flags(const char *cmd, char **argv, int argc)
549 extern int opterr, optind, optopt, optreset;
552 optind = optreset = 1;
555 while ((ch = getopt(argc, argv, "")) != -1) {
558 error("%s: Invalid flag -%c", cmd, optopt);
567 is_dir(const char *path)
571 /* XXX: report errors? */
572 if (stat(path, &sb) == -1)
575 return(S_ISDIR(sb.st_mode));
579 remote_is_dir(struct sftp_conn *conn, const char *path)
583 /* XXX: report errors? */
584 if ((a = do_stat(conn, path, 1)) == NULL)
586 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
588 return(S_ISDIR(a->perm));
591 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
593 pathname_is_dir(const char *pathname)
595 size_t l = strlen(pathname);
597 return l > 0 && pathname[l - 1] == '/';
601 process_get(struct sftp_conn *conn, const char *src, const char *dst,
602 const char *pwd, int pflag, int rflag, int resume, int fflag)
604 char *abs_src = NULL;
605 char *abs_dst = NULL;
607 char *filename, *tmp=NULL;
610 abs_src = xstrdup(src);
611 abs_src = make_absolute(abs_src, pwd);
612 memset(&g, 0, sizeof(g));
614 debug3("Looking up %s", abs_src);
615 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
616 if (r == GLOB_NOSPACE) {
617 error("Too many matches for \"%s\".", abs_src);
619 error("File \"%s\" not found.", abs_src);
626 * If multiple matches then dst must be a directory or
629 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
630 error("Multiple source paths, but destination "
631 "\"%s\" is not a directory", dst);
636 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
637 tmp = xstrdup(g.gl_pathv[i]);
638 if ((filename = basename(tmp)) == NULL) {
639 error("basename %s: %s", tmp, strerror(errno));
645 if (g.gl_matchc == 1 && dst) {
647 abs_dst = path_append(dst, filename);
649 abs_dst = xstrdup(dst);
652 abs_dst = path_append(dst, filename);
654 abs_dst = xstrdup(filename);
658 resume |= global_aflag;
659 if (!quiet && resume)
660 mprintf("Resuming %s to %s\n",
661 g.gl_pathv[i], abs_dst);
662 else if (!quiet && !resume)
663 mprintf("Fetching %s to %s\n",
664 g.gl_pathv[i], abs_dst);
665 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
666 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
667 pflag || global_pflag, 1, resume,
668 fflag || global_fflag) == -1)
671 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
672 pflag || global_pflag, resume,
673 fflag || global_fflag) == -1)
687 process_put(struct sftp_conn *conn, const char *src, const char *dst,
688 const char *pwd, int pflag, int rflag, int resume, int fflag)
690 char *tmp_dst = NULL;
691 char *abs_dst = NULL;
692 char *tmp = NULL, *filename = NULL;
695 int i, dst_is_dir = 1;
699 tmp_dst = xstrdup(dst);
700 tmp_dst = make_absolute(tmp_dst, pwd);
703 memset(&g, 0, sizeof(g));
704 debug3("Looking up %s", src);
705 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
706 error("File \"%s\" not found.", src);
711 /* If we aren't fetching to pwd then stash this status for later */
713 dst_is_dir = remote_is_dir(conn, tmp_dst);
715 /* If multiple matches, dst may be directory or unspecified */
716 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
717 error("Multiple paths match, but destination "
718 "\"%s\" is not a directory", tmp_dst);
723 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
724 if (stat(g.gl_pathv[i], &sb) == -1) {
726 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
730 tmp = xstrdup(g.gl_pathv[i]);
731 if ((filename = basename(tmp)) == NULL) {
732 error("basename %s: %s", tmp, strerror(errno));
738 if (g.gl_matchc == 1 && tmp_dst) {
739 /* If directory specified, append filename */
741 abs_dst = path_append(tmp_dst, filename);
743 abs_dst = xstrdup(tmp_dst);
744 } else if (tmp_dst) {
745 abs_dst = path_append(tmp_dst, filename);
747 abs_dst = make_absolute(xstrdup(filename), pwd);
751 resume |= global_aflag;
752 if (!quiet && resume)
753 mprintf("Resuming upload of %s to %s\n",
754 g.gl_pathv[i], abs_dst);
755 else if (!quiet && !resume)
756 mprintf("Uploading %s to %s\n",
757 g.gl_pathv[i], abs_dst);
758 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
759 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
760 pflag || global_pflag, 1, resume,
761 fflag || global_fflag) == -1)
764 if (do_upload(conn, g.gl_pathv[i], abs_dst,
765 pflag || global_pflag, resume,
766 fflag || global_fflag) == -1)
779 sdirent_comp(const void *aa, const void *bb)
781 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
782 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
783 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
785 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
786 if (sort_flag & LS_NAME_SORT)
787 return (rmul * strcmp(a->filename, b->filename));
788 else if (sort_flag & LS_TIME_SORT)
789 return (rmul * NCMP(a->a.mtime, b->a.mtime));
790 else if (sort_flag & LS_SIZE_SORT)
791 return (rmul * NCMP(a->a.size, b->a.size));
793 fatal("Unknown ls sort type");
796 /* sftp ls.1 replacement for directories */
798 do_ls_dir(struct sftp_conn *conn, const char *path,
799 const char *strip_path, int lflag)
802 u_int c = 1, colspace = 0, columns = 1;
805 if ((n = do_readdir(conn, path, &d)) != 0)
808 if (!(lflag & LS_SHORT_VIEW)) {
809 u_int m = 0, width = 80;
813 /* Count entries for sort and find longest filename */
814 for (n = 0; d[n] != NULL; n++) {
815 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
816 m = MAXIMUM(m, strlen(d[n]->filename));
819 /* Add any subpath that also needs to be counted */
820 tmp = path_strip(path, strip_path);
824 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
827 columns = width / (m + 2);
828 columns = MAXIMUM(columns, 1);
829 colspace = width / columns;
830 colspace = MINIMUM(colspace, width);
833 if (lflag & SORT_FLAGS) {
834 for (n = 0; d[n] != NULL; n++)
835 ; /* count entries */
836 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
837 qsort(d, n, sizeof(*d), sdirent_comp);
840 for (n = 0; d[n] != NULL && !interrupted; n++) {
843 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
846 tmp = path_append(path, d[n]->filename);
847 fname = path_strip(tmp, strip_path);
850 if (lflag & LS_LONG_VIEW) {
851 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
855 memset(&sb, 0, sizeof(sb));
856 attrib_to_stat(&d[n]->a, &sb);
857 lname = ls_file(fname, &sb, 1,
858 (lflag & LS_SI_UNITS));
859 mprintf("%s\n", lname);
862 mprintf("%s\n", d[n]->longname);
864 mprintf("%-*s", colspace, fname);
875 if (!(lflag & LS_LONG_VIEW) && (c != 1))
878 free_sftp_dirents(d);
882 /* sftp ls.1 replacement which handles path globs */
884 do_globbed_ls(struct sftp_conn *conn, const char *path,
885 const char *strip_path, int lflag)
891 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
893 memset(&g, 0, sizeof(g));
895 if ((r = remote_glob(conn, path,
896 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
898 (g.gl_pathc && !g.gl_matchc)) {
901 if (r == GLOB_NOSPACE) {
902 error("Can't ls: Too many matches for \"%s\"", path);
904 error("Can't ls: \"%s\" not found", path);
913 * If the glob returns a single match and it is a directory,
914 * then just list its contents.
916 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
917 S_ISDIR(g.gl_statv[0]->st_mode)) {
918 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
923 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
926 if (!(lflag & LS_SHORT_VIEW)) {
927 /* Count entries for sort and find longest filename */
928 for (i = 0; g.gl_pathv[i]; i++)
929 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
931 columns = width / (m + 2);
932 columns = MAXIMUM(columns, 1);
933 colspace = width / columns;
936 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
937 fname = path_strip(g.gl_pathv[i], strip_path);
938 if (lflag & LS_LONG_VIEW) {
939 if (g.gl_statv[i] == NULL) {
940 error("no stat information for %s", fname);
943 lname = ls_file(fname, g.gl_statv[i], 1,
944 (lflag & LS_SI_UNITS));
945 mprintf("%s\n", lname);
948 mprintf("%-*s", colspace, fname);
958 if (!(lflag & LS_LONG_VIEW) && (c != 1))
969 do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
971 struct sftp_statvfs st;
972 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
973 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
974 char s_icapacity[16], s_dcapacity[16];
976 if (do_statvfs(conn, path, &st, 1) == -1)
979 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
981 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
982 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
985 if (st.f_blocks == 0)
986 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
988 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
989 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
993 printf(" Inodes Used Avail "
994 "(root) %%Capacity\n");
995 printf("%11llu %11llu %11llu %11llu %s\n",
996 (unsigned long long)st.f_files,
997 (unsigned long long)(st.f_files - st.f_ffree),
998 (unsigned long long)st.f_favail,
999 (unsigned long long)st.f_ffree, s_icapacity);
1001 strlcpy(s_used, "error", sizeof(s_used));
1002 strlcpy(s_avail, "error", sizeof(s_avail));
1003 strlcpy(s_root, "error", sizeof(s_root));
1004 strlcpy(s_total, "error", sizeof(s_total));
1005 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1006 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1007 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1008 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1009 printf(" Size Used Avail (root) %%Capacity\n");
1010 printf("%7sB %7sB %7sB %7sB %s\n",
1011 s_total, s_used, s_avail, s_root, s_dcapacity);
1013 printf(" Size Used Avail "
1014 "(root) %%Capacity\n");
1015 printf("%12llu %12llu %12llu %12llu %s\n",
1016 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1017 (unsigned long long)(st.f_frsize *
1018 (st.f_blocks - st.f_bfree) / 1024),
1019 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1020 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1027 * Undo escaping of glob sequences in place. Used to undo extra escaping
1028 * applied in makeargv() when the string is destined for a function that
1032 undo_glob_escape(char *s)
1067 * Split a string into an argument vector using sh(1)-style quoting,
1068 * comment and escaping rules, but with some tweaks to handle glob(3)
1070 * The "sloppy" flag allows for recovery from missing terminating quote, for
1071 * use in parsing incomplete commandlines during tab autocompletion.
1073 * Returns NULL on error or a NULL-terminated array of arguments.
1075 * If "lastquote" is not NULL, the quoting character used for the last
1076 * argument is placed in *lastquote ("\0", "'" or "\"").
1078 * If "terminated" is not NULL, *terminated will be set to 1 when the
1079 * last argument's quote has been properly terminated or 0 otherwise.
1080 * This parameter is only of use if "sloppy" is set.
1083 #define MAXARGLEN 8192
1085 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1090 static char argvs[MAXARGLEN];
1091 static char *argv[MAXARGS + 1];
1092 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1095 if (strlen(arg) > sizeof(argvs) - 1) {
1097 error("string too long");
1100 if (terminated != NULL)
1102 if (lastquote != NULL)
1107 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1108 error("Too many arguments.");
1111 if (isspace((unsigned char)arg[i])) {
1112 if (state == MA_UNQUOTED) {
1113 /* Terminate current argument */
1117 } else if (state != MA_START)
1118 argvs[j++] = arg[i];
1119 } else if (arg[i] == '"' || arg[i] == '\'') {
1120 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1121 if (state == MA_START) {
1122 argv[argc] = argvs + j;
1124 if (lastquote != NULL)
1125 *lastquote = arg[i];
1126 } else if (state == MA_UNQUOTED)
1128 else if (state == q)
1129 state = MA_UNQUOTED;
1131 argvs[j++] = arg[i];
1132 } else if (arg[i] == '\\') {
1133 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1134 quot = state == MA_SQUOTE ? '\'' : '"';
1135 /* Unescape quote we are in */
1136 /* XXX support \n and friends? */
1137 if (arg[i + 1] == quot) {
1139 argvs[j++] = arg[i];
1140 } else if (arg[i + 1] == '?' ||
1141 arg[i + 1] == '[' || arg[i + 1] == '*') {
1143 * Special case for sftp: append
1144 * double-escaped glob sequence -
1145 * glob will undo one level of
1146 * escaping. NB. string can grow here.
1148 if (j >= sizeof(argvs) - 5)
1149 goto args_too_longs;
1151 argvs[j++] = arg[i++];
1153 argvs[j++] = arg[i];
1155 argvs[j++] = arg[i++];
1156 argvs[j++] = arg[i];
1159 if (state == MA_START) {
1160 argv[argc] = argvs + j;
1161 state = MA_UNQUOTED;
1162 if (lastquote != NULL)
1165 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1166 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1168 * Special case for sftp: append
1169 * escaped glob sequence -
1170 * glob will undo one level of
1173 argvs[j++] = arg[i++];
1174 argvs[j++] = arg[i];
1176 /* Unescape everything */
1177 /* XXX support \n and friends? */
1179 argvs[j++] = arg[i];
1182 } else if (arg[i] == '#') {
1183 if (state == MA_SQUOTE || state == MA_DQUOTE)
1184 argvs[j++] = arg[i];
1187 } else if (arg[i] == '\0') {
1188 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1190 state = MA_UNQUOTED;
1191 if (terminated != NULL)
1195 error("Unterminated quoted argument");
1199 if (state == MA_UNQUOTED) {
1205 if (state == MA_START) {
1206 argv[argc] = argvs + j;
1207 state = MA_UNQUOTED;
1208 if (lastquote != NULL)
1211 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1212 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1214 * Special case for sftp: escape quoted
1215 * glob(3) wildcards. NB. string can grow
1218 if (j >= sizeof(argvs) - 3)
1219 goto args_too_longs;
1221 argvs[j++] = arg[i];
1223 argvs[j++] = arg[i];
1232 parse_args(const char **cpp, int *ignore_errors, int *aflag,
1233 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1234 int *rflag, int *sflag,
1235 unsigned long *n_arg, char **path1, char **path2)
1237 const char *cmd, *cp = *cpp;
1241 int i, cmdnum, optidx, argc;
1243 /* Skip leading whitespace */
1244 cp = cp + strspn(cp, WHITESPACE);
1246 /* Check for leading '-' (disable error processing) */
1251 cp = cp + strspn(cp, WHITESPACE);
1254 /* Ignore blank lines and lines which begin with comment '#' char */
1255 if (*cp == '\0' || *cp == '#')
1258 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1261 /* Figure out which command we have */
1262 for (i = 0; cmds[i].c != NULL; i++) {
1263 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1273 } else if (cmdnum == -1) {
1274 error("Invalid command.");
1278 /* Get arguments and parse flags */
1279 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1280 *rflag = *sflag = 0;
1281 *path1 = *path2 = NULL;
1288 if ((optidx = parse_getput_flags(cmd, argv, argc,
1289 aflag, fflag, pflag, rflag)) == -1)
1291 /* Get first pathname (mandatory) */
1292 if (argc - optidx < 1) {
1293 error("You must specify at least one path after a "
1294 "%s command.", cmd);
1297 *path1 = xstrdup(argv[optidx]);
1298 /* Get second pathname (optional) */
1299 if (argc - optidx > 1) {
1300 *path2 = xstrdup(argv[optidx + 1]);
1301 /* Destination is not globbed */
1302 undo_glob_escape(*path2);
1306 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1308 goto parse_two_paths;
1310 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1312 goto parse_two_paths;
1314 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1317 if (argc - optidx < 2) {
1318 error("You must specify two paths after a %s "
1322 *path1 = xstrdup(argv[optidx]);
1323 *path2 = xstrdup(argv[optidx + 1]);
1324 /* Paths are not globbed */
1325 undo_glob_escape(*path1);
1326 undo_glob_escape(*path2);
1334 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1336 /* Get pathname (mandatory) */
1337 if (argc - optidx < 1) {
1338 error("You must specify a path after a %s command.",
1342 *path1 = xstrdup(argv[optidx]);
1343 /* Only "rm" globs */
1345 undo_glob_escape(*path1);
1348 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1351 /* Default to current directory if no path specified */
1352 if (argc - optidx < 1)
1355 *path1 = xstrdup(argv[optidx]);
1356 undo_glob_escape(*path1);
1360 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1362 /* Path is optional */
1363 if (argc - optidx > 0)
1364 *path1 = xstrdup(argv[optidx]);
1367 /* Skip ls command and following whitespace */
1368 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1370 /* Uses the rest of the line */
1377 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1379 /* Get numeric arg (mandatory) */
1380 if (argc - optidx < 1)
1383 l = strtol(argv[optidx], &cp2, base);
1384 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1385 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1388 error("You must supply a numeric argument "
1389 "to the %s command.", cmd);
1393 if (cmdnum == I_LUMASK)
1395 /* Get pathname (mandatory) */
1396 if (argc - optidx < 2) {
1397 error("You must specify a path after a %s command.",
1401 *path1 = xstrdup(argv[optidx + 1]);
1409 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1413 fatal("Command not implemented");
1421 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1424 char *path1, *path2, *tmp;
1425 int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0,
1427 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1429 unsigned long n_arg = 0;
1431 char path_buf[PATH_MAX];
1435 path1 = path2 = NULL;
1436 cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1437 &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1438 if (ignore_errors != 0)
1441 memset(&g, 0, sizeof(g));
1443 /* Perform command */
1449 /* Unrecognized command */
1456 err = process_get(conn, path1, path2, *pwd, pflag,
1457 rflag, aflag, fflag);
1463 err = process_put(conn, path1, path2, *pwd, pflag,
1464 rflag, aflag, fflag);
1467 path1 = make_absolute(path1, *pwd);
1468 path2 = make_absolute(path2, *pwd);
1469 err = do_rename(conn, path1, path2, lflag);
1475 path1 = make_absolute(path1, *pwd);
1476 path2 = make_absolute(path2, *pwd);
1477 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1480 path1 = make_absolute(path1, *pwd);
1481 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1482 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1484 mprintf("Removing %s\n", g.gl_pathv[i]);
1485 err = do_rm(conn, g.gl_pathv[i]);
1486 if (err != 0 && err_abort)
1491 path1 = make_absolute(path1, *pwd);
1493 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1495 err = do_mkdir(conn, path1, &a, 1);
1498 path1 = make_absolute(path1, *pwd);
1499 err = do_rmdir(conn, path1);
1502 path1 = make_absolute(path1, *pwd);
1503 if ((tmp = do_realpath(conn, path1)) == NULL) {
1507 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1512 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1513 error("Can't change directory: Can't check target");
1518 if (!S_ISDIR(aa->perm)) {
1519 error("Can't change directory: \"%s\" is not "
1520 "a directory", tmp);
1530 do_ls_dir(conn, *pwd, *pwd, lflag);
1534 /* Strip pwd off beginning of non-absolute paths */
1539 path1 = make_absolute(path1, *pwd);
1540 err = do_globbed_ls(conn, path1, tmp, lflag);
1543 /* Default to current directory if no path specified */
1545 path1 = xstrdup(*pwd);
1546 path1 = make_absolute(path1, *pwd);
1547 err = do_df(conn, path1, hflag, iflag);
1550 tmp = tilde_expand_filename(path1, getuid());
1553 if (chdir(path1) == -1) {
1554 error("Couldn't change local directory to "
1555 "\"%s\": %s", path1, strerror(errno));
1560 if (mkdir(path1, 0777) == -1) {
1561 error("Couldn't create local directory "
1562 "\"%s\": %s", path1, strerror(errno));
1570 local_do_shell(cmd);
1574 printf("Local umask: %03lo\n", n_arg);
1577 path1 = make_absolute(path1, *pwd);
1579 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1581 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1582 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1584 mprintf("Changing mode on %s\n",
1586 err = do_setstat(conn, g.gl_pathv[i], &a);
1587 if (err != 0 && err_abort)
1593 path1 = make_absolute(path1, *pwd);
1594 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1595 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1596 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1603 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1604 error("Can't get current ownership of "
1605 "remote file \"%s\"", g.gl_pathv[i]);
1612 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1613 if (cmdnum == I_CHOWN) {
1615 mprintf("Changing owner on %s\n",
1620 mprintf("Changing group on %s\n",
1624 err = do_setstat(conn, g.gl_pathv[i], aa);
1625 if (err != 0 && err_abort)
1630 mprintf("Remote working directory: %s\n", *pwd);
1633 if (!getcwd(path_buf, sizeof(path_buf))) {
1634 error("Couldn't get local cwd: %s", strerror(errno));
1638 mprintf("Local working directory: %s\n", path_buf);
1641 /* Processed below */
1647 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1650 showprogress = !showprogress;
1652 printf("Progress meter enabled\n");
1654 printf("Progress meter disabled\n");
1657 fatal("%d is not implemented", cmdnum);
1665 /* If an unignored error occurs in batch mode we should abort. */
1666 if (err_abort && err != 0)
1668 else if (cmdnum == I_QUIT)
1676 prompt(EditLine *el)
1681 /* Display entries in 'list' after skipping the first 'len' chars */
1683 complete_display(char **list, u_int len)
1685 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1689 /* Count entries for sort and find longest */
1690 for (y = 0; list[y]; y++)
1691 m = MAXIMUM(m, strlen(list[y]));
1693 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1696 m = m > len ? m - len : 0;
1697 columns = width / (m + 2);
1698 columns = MAXIMUM(columns, 1);
1699 colspace = width / columns;
1700 colspace = MINIMUM(colspace, width);
1704 for (y = 0; list[y]; y++) {
1705 llen = strlen(list[y]);
1706 tmp = llen > len ? list[y] + len : "";
1707 mprintf("%-*s", colspace, tmp);
1718 * Given a "list" of words that begin with a common prefix of "word",
1719 * attempt to find an autocompletion to extends "word" by the next
1720 * characters common to all entries in "list".
1723 complete_ambiguous(const char *word, char **list, size_t count)
1729 u_int y, matchlen = strlen(list[0]);
1731 /* Find length of common stem */
1732 for (y = 1; list[y]; y++) {
1735 for (x = 0; x < matchlen; x++)
1736 if (list[0][x] != list[y][x])
1742 if (matchlen > strlen(word)) {
1743 char *tmp = xstrdup(list[0]);
1745 tmp[matchlen] = '\0';
1750 return xstrdup(word);
1753 /* Autocomplete a sftp command */
1755 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1758 u_int y, count = 0, cmdlen, tmplen;
1759 char *tmp, **list, argterm[3];
1762 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1764 /* No command specified: display all available commands */
1766 for (y = 0; cmds[y].c; y++)
1767 list[count++] = xstrdup(cmds[y].c);
1770 complete_display(list, 0);
1772 for (y = 0; list[y] != NULL; y++)
1778 /* Prepare subset of commands that start with "cmd" */
1779 cmdlen = strlen(cmd);
1780 for (y = 0; cmds[y].c; y++) {
1781 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1782 list[count++] = xstrdup(cmds[y].c);
1791 /* Complete ambigious command */
1792 tmp = complete_ambiguous(cmd, list, count);
1794 complete_display(list, 0);
1796 for (y = 0; list[y]; y++)
1801 tmplen = strlen(tmp);
1802 cmdlen = strlen(cmd);
1803 /* If cmd may be extended then do so */
1804 if (tmplen > cmdlen)
1805 if (el_insertstr(el, tmp + cmdlen) == -1)
1806 fatal("el_insertstr failed.");
1808 /* Terminate argument cleanly */
1812 argterm[y++] = quote;
1813 if (lastarg || *(lf->cursor) != ' ')
1816 if (y > 0 && el_insertstr(el, argterm) == -1)
1817 fatal("el_insertstr failed.");
1826 * Determine whether a particular sftp command's arguments (if any)
1827 * represent local or remote files.
1830 complete_is_remote(char *cmd) {
1836 for (i = 0; cmds[i].c; i++) {
1837 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1844 /* Autocomplete a filename "file" */
1846 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1847 char *file, int remote, int lastarg, char quote, int terminated)
1850 char *tmp, *tmp2, ins[8];
1851 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1855 /* Glob from "file" location */
1859 xasprintf(&tmp, "%s*", file);
1861 /* Check if the path is absolute. */
1862 isabs = tmp[0] == '/';
1864 memset(&g, 0, sizeof(g));
1865 if (remote != LOCAL) {
1866 tmp = make_absolute(tmp, remote_path);
1867 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1869 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1871 /* Determine length of pwd so we can trim completion display */
1872 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1873 /* Terminate counting on first unescaped glob metacharacter */
1874 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1875 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1879 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1881 if (tmp[tmplen] == '/')
1882 pwdlen = tmplen + 1; /* track last seen '/' */
1887 if (g.gl_matchc == 0)
1890 if (g.gl_matchc > 1)
1891 complete_display(g.gl_pathv, pwdlen);
1893 /* Don't try to extend globs */
1894 if (file == NULL || hadglob)
1897 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1898 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1904 tmplen = strlen(tmp);
1905 filelen = strlen(file);
1907 /* Count the number of escaped characters in the input string. */
1909 for (i = 0; i < filelen; i++) {
1910 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1917 if (tmplen > (filelen - cesc)) {
1918 tmp2 = tmp + filelen - cesc;
1920 /* quote argument on way out */
1921 for (i = 0; i < len; i += clen) {
1922 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1923 (size_t)clen > sizeof(ins) - 2)
1924 fatal("invalid multibyte character");
1926 memcpy(ins + 1, tmp2 + i, clen);
1927 ins[clen + 1] = '\0';
1937 if (quote == '\0' || tmp2[i] == quote) {
1938 if (el_insertstr(el, ins) == -1)
1939 fatal("el_insertstr "
1945 if (el_insertstr(el, ins + 1) == -1)
1946 fatal("el_insertstr failed.");
1953 if (g.gl_matchc == 1) {
1955 if (!terminated && quote != '\0')
1957 if (*(lf->cursor - 1) != '/' &&
1958 (lastarg || *(lf->cursor) != ' '))
1961 if (i > 0 && el_insertstr(el, ins) == -1)
1962 fatal("el_insertstr failed.");
1971 /* tab-completion hook function, called via libedit */
1972 static unsigned char
1973 complete(EditLine *el, int ch)
1975 char **argv, *line, quote;
1977 u_int cursor, len, terminated, ret = CC_ERROR;
1979 struct complete_ctx *complete_ctx;
1982 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1983 fatal("%s: el_get failed", __func__);
1985 /* Figure out which argument the cursor points to */
1986 cursor = lf->cursor - lf->buffer;
1987 line = xmalloc(cursor + 1);
1988 memcpy(line, lf->buffer, cursor);
1989 line[cursor] = '\0';
1990 argv = makeargv(line, &carg, 1, "e, &terminated);
1993 /* Get all the arguments on the line */
1994 len = lf->lastchar - lf->buffer;
1995 line = xmalloc(len + 1);
1996 memcpy(line, lf->buffer, len);
1998 argv = makeargv(line, &argc, 1, NULL, NULL);
2000 /* Ensure cursor is at EOL or a argument boundary */
2001 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2002 line[cursor] != '\n') {
2008 /* Show all available commands */
2009 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2011 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2012 /* Handle the command parsing */
2013 if (complete_cmd_parse(el, argv[0], argc == carg,
2014 quote, terminated) != 0)
2016 } else if (carg >= 1) {
2017 /* Handle file parsing */
2018 int remote = complete_is_remote(argv[0]);
2019 char *filematch = NULL;
2021 if (carg > 1 && line[cursor-1] != ' ')
2022 filematch = argv[carg - 1];
2025 complete_match(el, complete_ctx->conn,
2026 *complete_ctx->remote_pathp, filematch,
2027 remote, carg == argc, quote, terminated) != 0)
2034 #endif /* USE_LIBEDIT */
2037 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2042 int err, interactive;
2043 EditLine *el = NULL;
2047 extern char *__progname;
2048 struct complete_ctx complete_ctx;
2050 if (!batchmode && isatty(STDIN_FILENO)) {
2051 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2052 fatal("Couldn't initialise editline");
2053 if ((hl = history_init()) == NULL)
2054 fatal("Couldn't initialise editline history");
2055 history(hl, &hev, H_SETSIZE, 100);
2056 el_set(el, EL_HIST, history, hl);
2058 el_set(el, EL_PROMPT, prompt);
2059 el_set(el, EL_EDITOR, "emacs");
2060 el_set(el, EL_TERMINAL, NULL);
2061 el_set(el, EL_SIGNAL, 1);
2062 el_source(el, NULL);
2064 /* Tab Completion */
2065 el_set(el, EL_ADDFN, "ftp-complete",
2066 "Context sensitive argument completion", complete);
2067 complete_ctx.conn = conn;
2068 complete_ctx.remote_pathp = &remote_path;
2069 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2070 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2071 /* enable ctrl-left-arrow and ctrl-right-arrow */
2072 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2073 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2074 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2075 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2076 /* make ^w match ksh behaviour */
2077 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2079 #endif /* USE_LIBEDIT */
2081 remote_path = do_realpath(conn, ".");
2082 if (remote_path == NULL)
2085 if (file1 != NULL) {
2086 dir = xstrdup(file1);
2087 dir = make_absolute(dir, remote_path);
2089 if (remote_is_dir(conn, dir) && file2 == NULL) {
2091 mprintf("Changing to: %s\n", dir);
2092 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2093 if (parse_dispatch_command(conn, cmd,
2094 &remote_path, 1) != 0) {
2101 /* XXX this is wrong wrt quoting */
2102 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2103 global_aflag ? " -a" : "", dir,
2104 file2 == NULL ? "" : " ",
2105 file2 == NULL ? "" : file2);
2106 err = parse_dispatch_command(conn, cmd,
2116 setvbuf(stdout, NULL, _IOLBF, 0);
2117 setvbuf(infile, NULL, _IOLBF, 0);
2119 interactive = !batchmode && isatty(STDIN_FILENO);
2124 signal(SIGINT, SIG_IGN);
2129 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2134 if (!interactive) { /* Echo command */
2135 mprintf("sftp> %s", cmd);
2136 if (strlen(cmd) > 0 &&
2137 cmd[strlen(cmd) - 1] != '\n')
2145 if ((line = el_gets(el, &count)) == NULL ||
2150 history(hl, &hev, H_ENTER, line);
2151 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2152 fprintf(stderr, "Error: input line too long\n");
2155 #endif /* USE_LIBEDIT */
2158 cp = strrchr(cmd, '\n');
2162 /* Handle user interrupts gracefully during commands */
2164 signal(SIGINT, cmd_interrupt);
2166 err = parse_dispatch_command(conn, cmd, &remote_path,
2177 #endif /* USE_LIBEDIT */
2179 /* err == 1 signifies normal "quit" exit */
2180 return (err >= 0 ? 0 : -1);
2184 connect_to_server(char *path, char **args, int *in, int *out)
2189 int pin[2], pout[2];
2191 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2192 fatal("pipe: %s", strerror(errno));
2197 #else /* USE_PIPES */
2200 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2201 fatal("socketpair: %s", strerror(errno));
2202 *in = *out = inout[0];
2203 c_in = c_out = inout[1];
2204 #endif /* USE_PIPES */
2206 if ((sshpid = fork()) == -1)
2207 fatal("fork: %s", strerror(errno));
2208 else if (sshpid == 0) {
2209 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2210 (dup2(c_out, STDOUT_FILENO) == -1)) {
2211 fprintf(stderr, "dup2: %s\n", strerror(errno));
2220 * The underlying ssh is in the same process group, so we must
2221 * ignore SIGINT if we want to gracefully abort commands,
2222 * otherwise the signal will make it to the ssh process and
2223 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2224 * underlying ssh, it must *not* ignore that signal.
2226 signal(SIGINT, SIG_IGN);
2227 signal(SIGTERM, SIG_DFL);
2229 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2233 signal(SIGTERM, killchild);
2234 signal(SIGINT, killchild);
2235 signal(SIGHUP, killchild);
2236 signal(SIGTSTP, suspchild);
2237 signal(SIGTTIN, suspchild);
2238 signal(SIGTTOU, suspchild);
2246 extern char *__progname;
2249 "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2250 " [-D sftp_server_path] [-F ssh_config] "
2251 "[-i identity_file] [-l limit]\n"
2252 " [-o ssh_option] [-P port] [-R num_requests] "
2254 " [-s subsystem | sftp_server] host\n"
2255 " %s [user@]host[:file ...]\n"
2256 " %s [user@]host[:dir[/]]\n"
2257 " %s -b batchfile [user@]host\n",
2258 __progname, __progname, __progname, __progname);
2263 main(int argc, char **argv)
2265 int in, out, ch, err;
2266 char *host = NULL, *userhost, *cp, *file2 = NULL;
2267 int debug_level = 0, sshver = 2;
2268 char *file1 = NULL, *sftp_server = NULL;
2269 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2271 LogLevel ll = SYSLOG_LEVEL_INFO;
2274 extern char *optarg;
2275 struct sftp_conn *conn;
2276 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2277 size_t num_requests = DEFAULT_NUM_REQUESTS;
2278 long long limit_kbps = 0;
2280 ssh_malloc_init(); /* must be called before any mallocs */
2281 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2285 __progname = ssh_get_progname(argv[0]);
2286 memset(&args, '\0', sizeof(args));
2288 addargs(&args, "%s", ssh_program);
2289 addargs(&args, "-oForwardX11 no");
2290 addargs(&args, "-oForwardAgent no");
2291 addargs(&args, "-oPermitLocalCommand no");
2292 addargs(&args, "-oClearAllForwardings yes");
2294 ll = SYSLOG_LEVEL_INFO;
2297 while ((ch = getopt(argc, argv,
2298 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2300 /* Passed through to ssh(1) */
2304 addargs(&args, "-%c", ch);
2306 /* Passed through to ssh(1) with argument */
2311 addargs(&args, "-%c", ch);
2312 addargs(&args, "%s", optarg);
2315 ll = SYSLOG_LEVEL_ERROR;
2318 addargs(&args, "-%c", ch);
2321 addargs(&args, "-oPort %s", optarg);
2324 if (debug_level < 3) {
2325 addargs(&args, "-v");
2326 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2332 if (sftp_server == NULL)
2333 sftp_server = _PATH_SFTP_SERVER;
2342 copy_buffer_len = strtol(optarg, &cp, 10);
2343 if (copy_buffer_len == 0 || *cp != '\0')
2344 fatal("Invalid buffer size \"%s\"", optarg);
2348 fatal("Batch file already specified.");
2350 /* Allow "-" as stdin */
2351 if (strcmp(optarg, "-") != 0 &&
2352 (infile = fopen(optarg, "r")) == NULL)
2353 fatal("%s (%s).", strerror(errno), optarg);
2355 quiet = batchmode = 1;
2356 addargs(&args, "-obatchmode yes");
2365 sftp_direct = optarg;
2368 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2372 limit_kbps *= 1024; /* kbps */
2378 num_requests = strtol(optarg, &cp, 10);
2379 if (num_requests == 0 || *cp != '\0')
2380 fatal("Invalid number of requests \"%s\"",
2384 sftp_server = optarg;
2387 ssh_program = optarg;
2388 replacearg(&args, 0, "%s", ssh_program);
2396 if (!isatty(STDERR_FILENO))
2399 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2401 if (sftp_direct == NULL) {
2402 if (optind == argc || argc > (optind + 2))
2405 userhost = xstrdup(argv[optind]);
2406 file2 = argv[optind+1];
2408 if ((host = strrchr(userhost, '@')) == NULL)
2413 fprintf(stderr, "Missing username\n");
2416 addargs(&args, "-l");
2417 addargs(&args, "%s", userhost);
2420 if ((cp = colon(host)) != NULL) {
2425 host = cleanhostname(host);
2427 fprintf(stderr, "Missing hostname\n");
2431 addargs(&args, "-oProtocol %d", sshver);
2433 /* no subsystem if the server-spec contains a '/' */
2434 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2435 addargs(&args, "-s");
2437 addargs(&args, "--");
2438 addargs(&args, "%s", host);
2439 addargs(&args, "%s", (sftp_server != NULL ?
2440 sftp_server : "sftp"));
2442 connect_to_server(ssh_program, args.list, &in, &out);
2445 addargs(&args, "sftp-server");
2447 connect_to_server(sftp_direct, args.list, &in, &out);
2451 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2453 fatal("Couldn't initialise connection to server");
2456 if (sftp_direct == NULL)
2457 fprintf(stderr, "Connected to %s.\n", host);
2459 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2462 err = interactive_loop(conn, file1, file2);
2464 #if !defined(USE_PIPES)
2465 shutdown(in, SHUT_RDWR);
2466 shutdown(out, SHUT_RDWR);
2474 while (waitpid(sshpid, NULL, 0) == -1)
2476 fatal("Couldn't wait for ssh process: %s",
2479 exit(err == 0 ? 0 : 1);