1 /* $OpenBSD: sftp.c,v 1.177 2016/10/18 12:41:22 millert 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];
973 char s_avail[FMT_SCALED_STRSIZE];
974 char s_root[FMT_SCALED_STRSIZE];
975 char s_total[FMT_SCALED_STRSIZE];
976 unsigned long long ffree;
978 if (do_statvfs(conn, path, &st, 1) == -1)
981 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
982 printf(" Inodes Used Avail "
983 "(root) %%Capacity\n");
984 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
985 (unsigned long long)st.f_files,
986 (unsigned long long)(st.f_files - st.f_ffree),
987 (unsigned long long)st.f_favail,
988 (unsigned long long)st.f_ffree, ffree);
990 strlcpy(s_used, "error", sizeof(s_used));
991 strlcpy(s_avail, "error", sizeof(s_avail));
992 strlcpy(s_root, "error", sizeof(s_root));
993 strlcpy(s_total, "error", sizeof(s_total));
994 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
995 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
996 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
997 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
998 printf(" Size Used Avail (root) %%Capacity\n");
999 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
1000 s_total, s_used, s_avail, s_root,
1001 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1004 printf(" Size Used Avail "
1005 "(root) %%Capacity\n");
1006 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
1007 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1008 (unsigned long long)(st.f_frsize *
1009 (st.f_blocks - st.f_bfree) / 1024),
1010 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1011 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1012 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1019 * Undo escaping of glob sequences in place. Used to undo extra escaping
1020 * applied in makeargv() when the string is destined for a function that
1024 undo_glob_escape(char *s)
1059 * Split a string into an argument vector using sh(1)-style quoting,
1060 * comment and escaping rules, but with some tweaks to handle glob(3)
1062 * The "sloppy" flag allows for recovery from missing terminating quote, for
1063 * use in parsing incomplete commandlines during tab autocompletion.
1065 * Returns NULL on error or a NULL-terminated array of arguments.
1067 * If "lastquote" is not NULL, the quoting character used for the last
1068 * argument is placed in *lastquote ("\0", "'" or "\"").
1070 * If "terminated" is not NULL, *terminated will be set to 1 when the
1071 * last argument's quote has been properly terminated or 0 otherwise.
1072 * This parameter is only of use if "sloppy" is set.
1075 #define MAXARGLEN 8192
1077 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1082 static char argvs[MAXARGLEN];
1083 static char *argv[MAXARGS + 1];
1084 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1087 if (strlen(arg) > sizeof(argvs) - 1) {
1089 error("string too long");
1092 if (terminated != NULL)
1094 if (lastquote != NULL)
1099 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1100 error("Too many arguments.");
1103 if (isspace((unsigned char)arg[i])) {
1104 if (state == MA_UNQUOTED) {
1105 /* Terminate current argument */
1109 } else if (state != MA_START)
1110 argvs[j++] = arg[i];
1111 } else if (arg[i] == '"' || arg[i] == '\'') {
1112 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1113 if (state == MA_START) {
1114 argv[argc] = argvs + j;
1116 if (lastquote != NULL)
1117 *lastquote = arg[i];
1118 } else if (state == MA_UNQUOTED)
1120 else if (state == q)
1121 state = MA_UNQUOTED;
1123 argvs[j++] = arg[i];
1124 } else if (arg[i] == '\\') {
1125 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1126 quot = state == MA_SQUOTE ? '\'' : '"';
1127 /* Unescape quote we are in */
1128 /* XXX support \n and friends? */
1129 if (arg[i + 1] == quot) {
1131 argvs[j++] = arg[i];
1132 } else if (arg[i + 1] == '?' ||
1133 arg[i + 1] == '[' || arg[i + 1] == '*') {
1135 * Special case for sftp: append
1136 * double-escaped glob sequence -
1137 * glob will undo one level of
1138 * escaping. NB. string can grow here.
1140 if (j >= sizeof(argvs) - 5)
1141 goto args_too_longs;
1143 argvs[j++] = arg[i++];
1145 argvs[j++] = arg[i];
1147 argvs[j++] = arg[i++];
1148 argvs[j++] = arg[i];
1151 if (state == MA_START) {
1152 argv[argc] = argvs + j;
1153 state = MA_UNQUOTED;
1154 if (lastquote != NULL)
1157 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1158 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1160 * Special case for sftp: append
1161 * escaped glob sequence -
1162 * glob will undo one level of
1165 argvs[j++] = arg[i++];
1166 argvs[j++] = arg[i];
1168 /* Unescape everything */
1169 /* XXX support \n and friends? */
1171 argvs[j++] = arg[i];
1174 } else if (arg[i] == '#') {
1175 if (state == MA_SQUOTE || state == MA_DQUOTE)
1176 argvs[j++] = arg[i];
1179 } else if (arg[i] == '\0') {
1180 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1182 state = MA_UNQUOTED;
1183 if (terminated != NULL)
1187 error("Unterminated quoted argument");
1191 if (state == MA_UNQUOTED) {
1197 if (state == MA_START) {
1198 argv[argc] = argvs + j;
1199 state = MA_UNQUOTED;
1200 if (lastquote != NULL)
1203 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1204 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1206 * Special case for sftp: escape quoted
1207 * glob(3) wildcards. NB. string can grow
1210 if (j >= sizeof(argvs) - 3)
1211 goto args_too_longs;
1213 argvs[j++] = arg[i];
1215 argvs[j++] = arg[i];
1224 parse_args(const char **cpp, int *ignore_errors, int *aflag,
1225 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1226 int *rflag, int *sflag,
1227 unsigned long *n_arg, char **path1, char **path2)
1229 const char *cmd, *cp = *cpp;
1233 int i, cmdnum, optidx, argc;
1235 /* Skip leading whitespace */
1236 cp = cp + strspn(cp, WHITESPACE);
1238 /* Check for leading '-' (disable error processing) */
1243 cp = cp + strspn(cp, WHITESPACE);
1246 /* Ignore blank lines and lines which begin with comment '#' char */
1247 if (*cp == '\0' || *cp == '#')
1250 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1253 /* Figure out which command we have */
1254 for (i = 0; cmds[i].c != NULL; i++) {
1255 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1265 } else if (cmdnum == -1) {
1266 error("Invalid command.");
1270 /* Get arguments and parse flags */
1271 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1272 *rflag = *sflag = 0;
1273 *path1 = *path2 = NULL;
1280 if ((optidx = parse_getput_flags(cmd, argv, argc,
1281 aflag, fflag, pflag, rflag)) == -1)
1283 /* Get first pathname (mandatory) */
1284 if (argc - optidx < 1) {
1285 error("You must specify at least one path after a "
1286 "%s command.", cmd);
1289 *path1 = xstrdup(argv[optidx]);
1290 /* Get second pathname (optional) */
1291 if (argc - optidx > 1) {
1292 *path2 = xstrdup(argv[optidx + 1]);
1293 /* Destination is not globbed */
1294 undo_glob_escape(*path2);
1298 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1300 goto parse_two_paths;
1302 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1304 goto parse_two_paths;
1306 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1309 if (argc - optidx < 2) {
1310 error("You must specify two paths after a %s "
1314 *path1 = xstrdup(argv[optidx]);
1315 *path2 = xstrdup(argv[optidx + 1]);
1316 /* Paths are not globbed */
1317 undo_glob_escape(*path1);
1318 undo_glob_escape(*path2);
1326 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1328 /* Get pathname (mandatory) */
1329 if (argc - optidx < 1) {
1330 error("You must specify a path after a %s command.",
1334 *path1 = xstrdup(argv[optidx]);
1335 /* Only "rm" globs */
1337 undo_glob_escape(*path1);
1340 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1343 /* Default to current directory if no path specified */
1344 if (argc - optidx < 1)
1347 *path1 = xstrdup(argv[optidx]);
1348 undo_glob_escape(*path1);
1352 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1354 /* Path is optional */
1355 if (argc - optidx > 0)
1356 *path1 = xstrdup(argv[optidx]);
1359 /* Skip ls command and following whitespace */
1360 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1362 /* Uses the rest of the line */
1369 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1371 /* Get numeric arg (mandatory) */
1372 if (argc - optidx < 1)
1375 l = strtol(argv[optidx], &cp2, base);
1376 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1377 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1380 error("You must supply a numeric argument "
1381 "to the %s command.", cmd);
1385 if (cmdnum == I_LUMASK)
1387 /* Get pathname (mandatory) */
1388 if (argc - optidx < 2) {
1389 error("You must specify a path after a %s command.",
1393 *path1 = xstrdup(argv[optidx + 1]);
1401 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1405 fatal("Command not implemented");
1413 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1416 char *path1, *path2, *tmp;
1417 int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0,
1419 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1421 unsigned long n_arg = 0;
1423 char path_buf[PATH_MAX];
1427 path1 = path2 = NULL;
1428 cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1429 &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1430 if (ignore_errors != 0)
1433 memset(&g, 0, sizeof(g));
1435 /* Perform command */
1441 /* Unrecognized command */
1448 err = process_get(conn, path1, path2, *pwd, pflag,
1449 rflag, aflag, fflag);
1455 err = process_put(conn, path1, path2, *pwd, pflag,
1456 rflag, aflag, fflag);
1459 path1 = make_absolute(path1, *pwd);
1460 path2 = make_absolute(path2, *pwd);
1461 err = do_rename(conn, path1, path2, lflag);
1467 path1 = make_absolute(path1, *pwd);
1468 path2 = make_absolute(path2, *pwd);
1469 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1472 path1 = make_absolute(path1, *pwd);
1473 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1474 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1476 mprintf("Removing %s\n", g.gl_pathv[i]);
1477 err = do_rm(conn, g.gl_pathv[i]);
1478 if (err != 0 && err_abort)
1483 path1 = make_absolute(path1, *pwd);
1485 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1487 err = do_mkdir(conn, path1, &a, 1);
1490 path1 = make_absolute(path1, *pwd);
1491 err = do_rmdir(conn, path1);
1494 path1 = make_absolute(path1, *pwd);
1495 if ((tmp = do_realpath(conn, path1)) == NULL) {
1499 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1504 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1505 error("Can't change directory: Can't check target");
1510 if (!S_ISDIR(aa->perm)) {
1511 error("Can't change directory: \"%s\" is not "
1512 "a directory", tmp);
1522 do_ls_dir(conn, *pwd, *pwd, lflag);
1526 /* Strip pwd off beginning of non-absolute paths */
1531 path1 = make_absolute(path1, *pwd);
1532 err = do_globbed_ls(conn, path1, tmp, lflag);
1535 /* Default to current directory if no path specified */
1537 path1 = xstrdup(*pwd);
1538 path1 = make_absolute(path1, *pwd);
1539 err = do_df(conn, path1, hflag, iflag);
1542 tmp = tilde_expand_filename(path1, getuid());
1545 if (chdir(path1) == -1) {
1546 error("Couldn't change local directory to "
1547 "\"%s\": %s", path1, strerror(errno));
1552 if (mkdir(path1, 0777) == -1) {
1553 error("Couldn't create local directory "
1554 "\"%s\": %s", path1, strerror(errno));
1562 local_do_shell(cmd);
1566 printf("Local umask: %03lo\n", n_arg);
1569 path1 = make_absolute(path1, *pwd);
1571 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1573 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1574 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1576 mprintf("Changing mode on %s\n",
1578 err = do_setstat(conn, g.gl_pathv[i], &a);
1579 if (err != 0 && err_abort)
1585 path1 = make_absolute(path1, *pwd);
1586 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1587 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1588 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1595 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1596 error("Can't get current ownership of "
1597 "remote file \"%s\"", g.gl_pathv[i]);
1604 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1605 if (cmdnum == I_CHOWN) {
1607 mprintf("Changing owner on %s\n",
1612 mprintf("Changing group on %s\n",
1616 err = do_setstat(conn, g.gl_pathv[i], aa);
1617 if (err != 0 && err_abort)
1622 mprintf("Remote working directory: %s\n", *pwd);
1625 if (!getcwd(path_buf, sizeof(path_buf))) {
1626 error("Couldn't get local cwd: %s", strerror(errno));
1630 mprintf("Local working directory: %s\n", path_buf);
1633 /* Processed below */
1639 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1642 showprogress = !showprogress;
1644 printf("Progress meter enabled\n");
1646 printf("Progress meter disabled\n");
1649 fatal("%d is not implemented", cmdnum);
1657 /* If an unignored error occurs in batch mode we should abort. */
1658 if (err_abort && err != 0)
1660 else if (cmdnum == I_QUIT)
1668 prompt(EditLine *el)
1673 /* Display entries in 'list' after skipping the first 'len' chars */
1675 complete_display(char **list, u_int len)
1677 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1681 /* Count entries for sort and find longest */
1682 for (y = 0; list[y]; y++)
1683 m = MAXIMUM(m, strlen(list[y]));
1685 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1688 m = m > len ? m - len : 0;
1689 columns = width / (m + 2);
1690 columns = MAXIMUM(columns, 1);
1691 colspace = width / columns;
1692 colspace = MINIMUM(colspace, width);
1696 for (y = 0; list[y]; y++) {
1697 llen = strlen(list[y]);
1698 tmp = llen > len ? list[y] + len : "";
1699 mprintf("%-*s", colspace, tmp);
1710 * Given a "list" of words that begin with a common prefix of "word",
1711 * attempt to find an autocompletion to extends "word" by the next
1712 * characters common to all entries in "list".
1715 complete_ambiguous(const char *word, char **list, size_t count)
1721 u_int y, matchlen = strlen(list[0]);
1723 /* Find length of common stem */
1724 for (y = 1; list[y]; y++) {
1727 for (x = 0; x < matchlen; x++)
1728 if (list[0][x] != list[y][x])
1734 if (matchlen > strlen(word)) {
1735 char *tmp = xstrdup(list[0]);
1737 tmp[matchlen] = '\0';
1742 return xstrdup(word);
1745 /* Autocomplete a sftp command */
1747 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1750 u_int y, count = 0, cmdlen, tmplen;
1751 char *tmp, **list, argterm[3];
1754 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1756 /* No command specified: display all available commands */
1758 for (y = 0; cmds[y].c; y++)
1759 list[count++] = xstrdup(cmds[y].c);
1762 complete_display(list, 0);
1764 for (y = 0; list[y] != NULL; y++)
1770 /* Prepare subset of commands that start with "cmd" */
1771 cmdlen = strlen(cmd);
1772 for (y = 0; cmds[y].c; y++) {
1773 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1774 list[count++] = xstrdup(cmds[y].c);
1783 /* Complete ambigious command */
1784 tmp = complete_ambiguous(cmd, list, count);
1786 complete_display(list, 0);
1788 for (y = 0; list[y]; y++)
1793 tmplen = strlen(tmp);
1794 cmdlen = strlen(cmd);
1795 /* If cmd may be extended then do so */
1796 if (tmplen > cmdlen)
1797 if (el_insertstr(el, tmp + cmdlen) == -1)
1798 fatal("el_insertstr failed.");
1800 /* Terminate argument cleanly */
1804 argterm[y++] = quote;
1805 if (lastarg || *(lf->cursor) != ' ')
1808 if (y > 0 && el_insertstr(el, argterm) == -1)
1809 fatal("el_insertstr failed.");
1818 * Determine whether a particular sftp command's arguments (if any)
1819 * represent local or remote files.
1822 complete_is_remote(char *cmd) {
1828 for (i = 0; cmds[i].c; i++) {
1829 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1836 /* Autocomplete a filename "file" */
1838 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1839 char *file, int remote, int lastarg, char quote, int terminated)
1842 char *tmp, *tmp2, ins[8];
1843 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1847 /* Glob from "file" location */
1851 xasprintf(&tmp, "%s*", file);
1853 /* Check if the path is absolute. */
1854 isabs = tmp[0] == '/';
1856 memset(&g, 0, sizeof(g));
1857 if (remote != LOCAL) {
1858 tmp = make_absolute(tmp, remote_path);
1859 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1861 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1863 /* Determine length of pwd so we can trim completion display */
1864 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1865 /* Terminate counting on first unescaped glob metacharacter */
1866 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1867 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1871 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1873 if (tmp[tmplen] == '/')
1874 pwdlen = tmplen + 1; /* track last seen '/' */
1879 if (g.gl_matchc == 0)
1882 if (g.gl_matchc > 1)
1883 complete_display(g.gl_pathv, pwdlen);
1885 /* Don't try to extend globs */
1886 if (file == NULL || hadglob)
1889 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1890 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1896 tmplen = strlen(tmp);
1897 filelen = strlen(file);
1899 /* Count the number of escaped characters in the input string. */
1901 for (i = 0; i < filelen; i++) {
1902 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1909 if (tmplen > (filelen - cesc)) {
1910 tmp2 = tmp + filelen - cesc;
1912 /* quote argument on way out */
1913 for (i = 0; i < len; i += clen) {
1914 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1915 (size_t)clen > sizeof(ins) - 2)
1916 fatal("invalid multibyte character");
1918 memcpy(ins + 1, tmp2 + i, clen);
1919 ins[clen + 1] = '\0';
1929 if (quote == '\0' || tmp2[i] == quote) {
1930 if (el_insertstr(el, ins) == -1)
1931 fatal("el_insertstr "
1937 if (el_insertstr(el, ins + 1) == -1)
1938 fatal("el_insertstr failed.");
1945 if (g.gl_matchc == 1) {
1947 if (!terminated && quote != '\0')
1949 if (*(lf->cursor - 1) != '/' &&
1950 (lastarg || *(lf->cursor) != ' '))
1953 if (i > 0 && el_insertstr(el, ins) == -1)
1954 fatal("el_insertstr failed.");
1963 /* tab-completion hook function, called via libedit */
1964 static unsigned char
1965 complete(EditLine *el, int ch)
1967 char **argv, *line, quote;
1969 u_int cursor, len, terminated, ret = CC_ERROR;
1971 struct complete_ctx *complete_ctx;
1974 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1975 fatal("%s: el_get failed", __func__);
1977 /* Figure out which argument the cursor points to */
1978 cursor = lf->cursor - lf->buffer;
1979 line = xmalloc(cursor + 1);
1980 memcpy(line, lf->buffer, cursor);
1981 line[cursor] = '\0';
1982 argv = makeargv(line, &carg, 1, "e, &terminated);
1985 /* Get all the arguments on the line */
1986 len = lf->lastchar - lf->buffer;
1987 line = xmalloc(len + 1);
1988 memcpy(line, lf->buffer, len);
1990 argv = makeargv(line, &argc, 1, NULL, NULL);
1992 /* Ensure cursor is at EOL or a argument boundary */
1993 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1994 line[cursor] != '\n') {
2000 /* Show all available commands */
2001 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2003 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2004 /* Handle the command parsing */
2005 if (complete_cmd_parse(el, argv[0], argc == carg,
2006 quote, terminated) != 0)
2008 } else if (carg >= 1) {
2009 /* Handle file parsing */
2010 int remote = complete_is_remote(argv[0]);
2011 char *filematch = NULL;
2013 if (carg > 1 && line[cursor-1] != ' ')
2014 filematch = argv[carg - 1];
2017 complete_match(el, complete_ctx->conn,
2018 *complete_ctx->remote_pathp, filematch,
2019 remote, carg == argc, quote, terminated) != 0)
2026 #endif /* USE_LIBEDIT */
2029 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2034 int err, interactive;
2035 EditLine *el = NULL;
2039 extern char *__progname;
2040 struct complete_ctx complete_ctx;
2042 if (!batchmode && isatty(STDIN_FILENO)) {
2043 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2044 fatal("Couldn't initialise editline");
2045 if ((hl = history_init()) == NULL)
2046 fatal("Couldn't initialise editline history");
2047 history(hl, &hev, H_SETSIZE, 100);
2048 el_set(el, EL_HIST, history, hl);
2050 el_set(el, EL_PROMPT, prompt);
2051 el_set(el, EL_EDITOR, "emacs");
2052 el_set(el, EL_TERMINAL, NULL);
2053 el_set(el, EL_SIGNAL, 1);
2054 el_source(el, NULL);
2056 /* Tab Completion */
2057 el_set(el, EL_ADDFN, "ftp-complete",
2058 "Context sensitive argument completion", complete);
2059 complete_ctx.conn = conn;
2060 complete_ctx.remote_pathp = &remote_path;
2061 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2062 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2063 /* enable ctrl-left-arrow and ctrl-right-arrow */
2064 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2065 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2066 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2067 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2068 /* make ^w match ksh behaviour */
2069 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2071 #endif /* USE_LIBEDIT */
2073 remote_path = do_realpath(conn, ".");
2074 if (remote_path == NULL)
2077 if (file1 != NULL) {
2078 dir = xstrdup(file1);
2079 dir = make_absolute(dir, remote_path);
2081 if (remote_is_dir(conn, dir) && file2 == NULL) {
2083 mprintf("Changing to: %s\n", dir);
2084 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2085 if (parse_dispatch_command(conn, cmd,
2086 &remote_path, 1) != 0) {
2093 /* XXX this is wrong wrt quoting */
2094 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2095 global_aflag ? " -a" : "", dir,
2096 file2 == NULL ? "" : " ",
2097 file2 == NULL ? "" : file2);
2098 err = parse_dispatch_command(conn, cmd,
2108 setvbuf(stdout, NULL, _IOLBF, 0);
2109 setvbuf(infile, NULL, _IOLBF, 0);
2111 interactive = !batchmode && isatty(STDIN_FILENO);
2116 signal(SIGINT, SIG_IGN);
2121 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2126 if (!interactive) { /* Echo command */
2127 mprintf("sftp> %s", cmd);
2128 if (strlen(cmd) > 0 &&
2129 cmd[strlen(cmd) - 1] != '\n')
2137 if ((line = el_gets(el, &count)) == NULL ||
2142 history(hl, &hev, H_ENTER, line);
2143 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2144 fprintf(stderr, "Error: input line too long\n");
2147 #endif /* USE_LIBEDIT */
2150 cp = strrchr(cmd, '\n');
2154 /* Handle user interrupts gracefully during commands */
2156 signal(SIGINT, cmd_interrupt);
2158 err = parse_dispatch_command(conn, cmd, &remote_path,
2169 #endif /* USE_LIBEDIT */
2171 /* err == 1 signifies normal "quit" exit */
2172 return (err >= 0 ? 0 : -1);
2176 connect_to_server(char *path, char **args, int *in, int *out)
2181 int pin[2], pout[2];
2183 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2184 fatal("pipe: %s", strerror(errno));
2189 #else /* USE_PIPES */
2192 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2193 fatal("socketpair: %s", strerror(errno));
2194 *in = *out = inout[0];
2195 c_in = c_out = inout[1];
2196 #endif /* USE_PIPES */
2198 if ((sshpid = fork()) == -1)
2199 fatal("fork: %s", strerror(errno));
2200 else if (sshpid == 0) {
2201 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2202 (dup2(c_out, STDOUT_FILENO) == -1)) {
2203 fprintf(stderr, "dup2: %s\n", strerror(errno));
2212 * The underlying ssh is in the same process group, so we must
2213 * ignore SIGINT if we want to gracefully abort commands,
2214 * otherwise the signal will make it to the ssh process and
2215 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2216 * underlying ssh, it must *not* ignore that signal.
2218 signal(SIGINT, SIG_IGN);
2219 signal(SIGTERM, SIG_DFL);
2221 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2225 signal(SIGTERM, killchild);
2226 signal(SIGINT, killchild);
2227 signal(SIGHUP, killchild);
2228 signal(SIGTSTP, suspchild);
2229 signal(SIGTTIN, suspchild);
2230 signal(SIGTTOU, suspchild);
2238 extern char *__progname;
2241 "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2242 " [-D sftp_server_path] [-F ssh_config] "
2243 "[-i identity_file] [-l limit]\n"
2244 " [-o ssh_option] [-P port] [-R num_requests] "
2246 " [-s subsystem | sftp_server] host\n"
2247 " %s [user@]host[:file ...]\n"
2248 " %s [user@]host[:dir[/]]\n"
2249 " %s -b batchfile [user@]host\n",
2250 __progname, __progname, __progname, __progname);
2255 main(int argc, char **argv)
2257 int in, out, ch, err;
2258 char *host = NULL, *userhost, *cp, *file2 = NULL;
2259 int debug_level = 0, sshver = 2;
2260 char *file1 = NULL, *sftp_server = NULL;
2261 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2263 LogLevel ll = SYSLOG_LEVEL_INFO;
2266 extern char *optarg;
2267 struct sftp_conn *conn;
2268 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2269 size_t num_requests = DEFAULT_NUM_REQUESTS;
2270 long long limit_kbps = 0;
2272 ssh_malloc_init(); /* must be called before any mallocs */
2273 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2277 __progname = ssh_get_progname(argv[0]);
2278 memset(&args, '\0', sizeof(args));
2280 addargs(&args, "%s", ssh_program);
2281 addargs(&args, "-oForwardX11 no");
2282 addargs(&args, "-oForwardAgent no");
2283 addargs(&args, "-oPermitLocalCommand no");
2284 addargs(&args, "-oClearAllForwardings yes");
2286 ll = SYSLOG_LEVEL_INFO;
2289 while ((ch = getopt(argc, argv,
2290 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2292 /* Passed through to ssh(1) */
2296 addargs(&args, "-%c", ch);
2298 /* Passed through to ssh(1) with argument */
2303 addargs(&args, "-%c", ch);
2304 addargs(&args, "%s", optarg);
2307 ll = SYSLOG_LEVEL_ERROR;
2310 addargs(&args, "-%c", ch);
2313 addargs(&args, "-oPort %s", optarg);
2316 if (debug_level < 3) {
2317 addargs(&args, "-v");
2318 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2324 if (sftp_server == NULL)
2325 sftp_server = _PATH_SFTP_SERVER;
2334 copy_buffer_len = strtol(optarg, &cp, 10);
2335 if (copy_buffer_len == 0 || *cp != '\0')
2336 fatal("Invalid buffer size \"%s\"", optarg);
2340 fatal("Batch file already specified.");
2342 /* Allow "-" as stdin */
2343 if (strcmp(optarg, "-") != 0 &&
2344 (infile = fopen(optarg, "r")) == NULL)
2345 fatal("%s (%s).", strerror(errno), optarg);
2347 quiet = batchmode = 1;
2348 addargs(&args, "-obatchmode yes");
2357 sftp_direct = optarg;
2360 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2364 limit_kbps *= 1024; /* kbps */
2370 num_requests = strtol(optarg, &cp, 10);
2371 if (num_requests == 0 || *cp != '\0')
2372 fatal("Invalid number of requests \"%s\"",
2376 sftp_server = optarg;
2379 ssh_program = optarg;
2380 replacearg(&args, 0, "%s", ssh_program);
2388 if (!isatty(STDERR_FILENO))
2391 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2393 if (sftp_direct == NULL) {
2394 if (optind == argc || argc > (optind + 2))
2397 userhost = xstrdup(argv[optind]);
2398 file2 = argv[optind+1];
2400 if ((host = strrchr(userhost, '@')) == NULL)
2405 fprintf(stderr, "Missing username\n");
2408 addargs(&args, "-l");
2409 addargs(&args, "%s", userhost);
2412 if ((cp = colon(host)) != NULL) {
2417 host = cleanhostname(host);
2419 fprintf(stderr, "Missing hostname\n");
2423 addargs(&args, "-oProtocol %d", sshver);
2425 /* no subsystem if the server-spec contains a '/' */
2426 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2427 addargs(&args, "-s");
2429 addargs(&args, "--");
2430 addargs(&args, "%s", host);
2431 addargs(&args, "%s", (sftp_server != NULL ?
2432 sftp_server : "sftp"));
2434 connect_to_server(ssh_program, args.list, &in, &out);
2437 addargs(&args, "sftp-server");
2439 connect_to_server(sftp_direct, args.list, &in, &out);
2443 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2445 fatal("Couldn't initialise connection to server");
2448 if (sftp_direct == NULL)
2449 fprintf(stderr, "Connected to %s.\n", host);
2451 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2454 err = interactive_loop(conn, file1, file2);
2456 #if !defined(USE_PIPES)
2457 shutdown(in, SHUT_RDWR);
2458 shutdown(out, SHUT_RDWR);
2466 while (waitpid(sshpid, NULL, 0) == -1)
2468 fatal("Couldn't wait for ssh process: %s",
2471 exit(err == 0 ? 0 : 1);