1 /* $OpenBSD: sftp.c,v 1.190 2019/01/21 22:50:42 tb 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 volatile 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...*/
111 /* Context used for commandline completion */
112 struct complete_ctx {
113 struct sftp_conn *conn;
117 int remote_glob(struct sftp_conn *, const char *, int,
118 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
120 extern char *__progname;
122 /* Separators for interactive commands */
123 #define WHITESPACE " \t\r\n"
126 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
127 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
128 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
129 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
130 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
131 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
132 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
133 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
134 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
136 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
137 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
139 /* Commands for interactive mode */
176 /* Type of completion */
181 static const struct CMD cmds[] = {
182 { "bye", I_QUIT, NOARGS },
183 { "cd", I_CHDIR, REMOTE },
184 { "chdir", I_CHDIR, REMOTE },
185 { "chgrp", I_CHGRP, REMOTE },
186 { "chmod", I_CHMOD, REMOTE },
187 { "chown", I_CHOWN, REMOTE },
188 { "df", I_DF, REMOTE },
189 { "dir", I_LS, REMOTE },
190 { "exit", I_QUIT, NOARGS },
191 { "get", I_GET, REMOTE },
192 { "help", I_HELP, NOARGS },
193 { "lcd", I_LCHDIR, LOCAL },
194 { "lchdir", I_LCHDIR, LOCAL },
195 { "lls", I_LLS, LOCAL },
196 { "lmkdir", I_LMKDIR, LOCAL },
197 { "ln", I_LINK, REMOTE },
198 { "lpwd", I_LPWD, LOCAL },
199 { "ls", I_LS, REMOTE },
200 { "lumask", I_LUMASK, NOARGS },
201 { "mkdir", I_MKDIR, REMOTE },
202 { "mget", I_GET, REMOTE },
203 { "mput", I_PUT, LOCAL },
204 { "progress", I_PROGRESS, NOARGS },
205 { "put", I_PUT, LOCAL },
206 { "pwd", I_PWD, REMOTE },
207 { "quit", I_QUIT, NOARGS },
208 { "reget", I_REGET, REMOTE },
209 { "rename", I_RENAME, REMOTE },
210 { "reput", I_REPUT, LOCAL },
211 { "rm", I_RM, REMOTE },
212 { "rmdir", I_RMDIR, REMOTE },
213 { "symlink", I_SYMLINK, REMOTE },
214 { "version", I_VERSION, NOARGS },
215 { "!", I_SHELL, NOARGS },
216 { "?", I_HELP, NOARGS },
225 kill(sshpid, SIGTERM);
226 waitpid(sshpid, NULL, 0);
238 while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR)
241 kill(getpid(), SIGSTOP);
246 cmd_interrupt(int signo)
248 const char msg[] = "\rInterrupt \n";
249 int olderrno = errno;
251 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
258 sigchld_handler(int sig)
260 int save_errno = errno;
262 const char msg[] = "\rConnection closed. \n";
264 /* Report if ssh transport process dies. */
265 while ((pid = waitpid(sshpid, NULL, WNOHANG)) == -1 && errno == EINTR)
268 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
278 printf("Available commands:\n"
280 "cd path Change remote directory to 'path'\n"
281 "chgrp [-h] grp path Change group of file 'path' to 'grp'\n"
282 "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n"
283 "chown [-h] own path Change owner of file 'path' to 'own'\n"
284 "df [-hi] [path] Display statistics for current directory or\n"
285 " filesystem containing 'path'\n"
287 "get [-afPpRr] remote [local] Download file\n"
288 "reget [-fPpRr] remote [local] Resume download file\n"
289 "reput [-fPpRr] [local] remote Resume upload file\n"
290 "help Display this help text\n"
291 "lcd path Change local directory to 'path'\n"
292 "lls [ls-options [path]] Display local directory listing\n"
293 "lmkdir path Create local directory\n"
294 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
295 "lpwd Print local working directory\n"
296 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
297 "lumask umask Set local umask to 'umask'\n"
298 "mkdir path Create remote directory\n"
299 "progress Toggle display of progress meter\n"
300 "put [-afPpRr] local [remote] Upload file\n"
301 "pwd Display remote working directory\n"
303 "rename oldpath newpath Rename remote file\n"
304 "rm path Delete remote file\n"
305 "rmdir path Remove remote directory\n"
306 "symlink oldpath newpath Symlink remote file\n"
307 "version Show SFTP version\n"
308 "!command Execute 'command' in local shell\n"
309 "! Escape to local shell\n"
310 "? Synonym for help\n");
314 local_do_shell(const char *args)
323 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
324 shell = _PATH_BSHELL;
326 if ((pid = fork()) == -1)
327 fatal("Couldn't fork: %s", strerror(errno));
330 /* XXX: child has pipe fds to ssh subproc open - issue? */
332 debug3("Executing %s -c \"%s\"", shell, args);
333 execl(shell, shell, "-c", args, (char *)NULL);
335 debug3("Executing %s", shell);
336 execl(shell, shell, (char *)NULL);
338 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
342 while (waitpid(pid, &status, 0) == -1)
344 fatal("Couldn't wait for child: %s", strerror(errno));
345 if (!WIFEXITED(status))
346 error("Shell exited abnormally");
347 else if (WEXITSTATUS(status))
348 error("Shell exited with status %d", WEXITSTATUS(status));
352 local_do_ls(const char *args)
355 local_do_shell(_PATH_LS);
357 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
358 char *buf = xmalloc(len);
360 /* XXX: quoting - rip quoting code from ftp? */
361 snprintf(buf, len, _PATH_LS " %s", args);
367 /* Strip one path (usually the pwd) from the start of another */
369 path_strip(const char *path, const char *strip)
374 return (xstrdup(path));
377 if (strncmp(path, strip, len) == 0) {
378 if (strip[len - 1] != '/' && path[len] == '/')
380 return (xstrdup(path + len));
383 return (xstrdup(path));
387 make_absolute(char *p, const char *pwd)
392 if (p && !path_absolute(p)) {
393 abs_str = path_append(pwd, p);
401 parse_getput_flags(const char *cmd, char **argv, int argc,
402 int *aflag, int *fflag, int *pflag, int *rflag)
404 extern int opterr, optind, optopt, optreset;
407 optind = optreset = 1;
410 *aflag = *fflag = *rflag = *pflag = 0;
411 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
428 error("%s: Invalid flag -%c", cmd, optopt);
437 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
439 extern int opterr, optind, optopt, optreset;
442 optind = optreset = 1;
446 while ((ch = getopt(argc, argv, "s")) != -1) {
452 error("%s: Invalid flag -%c", cmd, optopt);
461 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
463 extern int opterr, optind, optopt, optreset;
466 optind = optreset = 1;
470 while ((ch = getopt(argc, argv, "l")) != -1) {
476 error("%s: Invalid flag -%c", cmd, optopt);
485 parse_ls_flags(char **argv, int argc, int *lflag)
487 extern int opterr, optind, optopt, optreset;
490 optind = optreset = 1;
493 *lflag = LS_NAME_SORT;
494 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
497 *lflag &= ~VIEW_FLAGS;
498 *lflag |= LS_SHORT_VIEW;
501 *lflag &= ~SORT_FLAGS;
502 *lflag |= LS_SIZE_SORT;
505 *lflag |= LS_SHOW_ALL;
508 *lflag &= ~SORT_FLAGS;
511 *lflag |= LS_SI_UNITS;
514 *lflag &= ~LS_SHORT_VIEW;
515 *lflag |= LS_LONG_VIEW;
518 *lflag &= ~LS_SHORT_VIEW;
519 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
522 *lflag |= LS_REVERSE_SORT;
525 *lflag &= ~SORT_FLAGS;
526 *lflag |= LS_TIME_SORT;
529 error("ls: Invalid flag -%c", optopt);
538 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
540 extern int opterr, optind, optopt, optreset;
543 optind = optreset = 1;
547 while ((ch = getopt(argc, argv, "hi")) != -1) {
556 error("%s: Invalid flag -%c", cmd, optopt);
565 parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag)
567 extern int opterr, optind, optopt, optreset;
570 optind = optreset = 1;
574 while ((ch = getopt(argc, argv, "h")) != -1) {
580 error("%s: Invalid flag -%c", cmd, optopt);
589 parse_no_flags(const char *cmd, char **argv, int argc)
591 extern int opterr, optind, optopt, optreset;
594 optind = optreset = 1;
597 while ((ch = getopt(argc, argv, "")) != -1) {
600 error("%s: Invalid flag -%c", cmd, optopt);
609 is_dir(const char *path)
613 /* XXX: report errors? */
614 if (stat(path, &sb) == -1)
617 return(S_ISDIR(sb.st_mode));
621 remote_is_dir(struct sftp_conn *conn, const char *path)
625 /* XXX: report errors? */
626 if ((a = do_stat(conn, path, 1)) == NULL)
628 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
630 return(S_ISDIR(a->perm));
633 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
635 pathname_is_dir(const char *pathname)
637 size_t l = strlen(pathname);
639 return l > 0 && pathname[l - 1] == '/';
643 process_get(struct sftp_conn *conn, const char *src, const char *dst,
644 const char *pwd, int pflag, int rflag, int resume, int fflag)
646 char *abs_src = NULL;
647 char *abs_dst = NULL;
649 char *filename, *tmp=NULL;
652 abs_src = xstrdup(src);
653 abs_src = make_absolute(abs_src, pwd);
654 memset(&g, 0, sizeof(g));
656 debug3("Looking up %s", abs_src);
657 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
658 if (r == GLOB_NOSPACE) {
659 error("Too many matches for \"%s\".", abs_src);
661 error("File \"%s\" not found.", abs_src);
668 * If multiple matches then dst must be a directory or
671 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
672 error("Multiple source paths, but destination "
673 "\"%s\" is not a directory", dst);
678 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
679 tmp = xstrdup(g.gl_pathv[i]);
680 if ((filename = basename(tmp)) == NULL) {
681 error("basename %s: %s", tmp, strerror(errno));
687 if (g.gl_matchc == 1 && dst) {
689 abs_dst = path_append(dst, filename);
691 abs_dst = xstrdup(dst);
694 abs_dst = path_append(dst, filename);
696 abs_dst = xstrdup(filename);
700 resume |= global_aflag;
701 if (!quiet && resume)
702 mprintf("Resuming %s to %s\n",
703 g.gl_pathv[i], abs_dst);
704 else if (!quiet && !resume)
705 mprintf("Fetching %s to %s\n",
706 g.gl_pathv[i], abs_dst);
707 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
708 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
709 pflag || global_pflag, 1, resume,
710 fflag || global_fflag) == -1)
713 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
714 pflag || global_pflag, resume,
715 fflag || global_fflag) == -1)
729 process_put(struct sftp_conn *conn, const char *src, const char *dst,
730 const char *pwd, int pflag, int rflag, int resume, int fflag)
732 char *tmp_dst = NULL;
733 char *abs_dst = NULL;
734 char *tmp = NULL, *filename = NULL;
737 int i, dst_is_dir = 1;
741 tmp_dst = xstrdup(dst);
742 tmp_dst = make_absolute(tmp_dst, pwd);
745 memset(&g, 0, sizeof(g));
746 debug3("Looking up %s", src);
747 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
748 error("File \"%s\" not found.", src);
753 /* If we aren't fetching to pwd then stash this status for later */
755 dst_is_dir = remote_is_dir(conn, tmp_dst);
757 /* If multiple matches, dst may be directory or unspecified */
758 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
759 error("Multiple paths match, but destination "
760 "\"%s\" is not a directory", tmp_dst);
765 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
766 if (stat(g.gl_pathv[i], &sb) == -1) {
768 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
772 tmp = xstrdup(g.gl_pathv[i]);
773 if ((filename = basename(tmp)) == NULL) {
774 error("basename %s: %s", tmp, strerror(errno));
780 if (g.gl_matchc == 1 && tmp_dst) {
781 /* If directory specified, append filename */
783 abs_dst = path_append(tmp_dst, filename);
785 abs_dst = xstrdup(tmp_dst);
786 } else if (tmp_dst) {
787 abs_dst = path_append(tmp_dst, filename);
789 abs_dst = make_absolute(xstrdup(filename), pwd);
793 resume |= global_aflag;
794 if (!quiet && resume)
795 mprintf("Resuming upload of %s to %s\n",
796 g.gl_pathv[i], abs_dst);
797 else if (!quiet && !resume)
798 mprintf("Uploading %s to %s\n",
799 g.gl_pathv[i], abs_dst);
800 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
801 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
802 pflag || global_pflag, 1, resume,
803 fflag || global_fflag) == -1)
806 if (do_upload(conn, g.gl_pathv[i], abs_dst,
807 pflag || global_pflag, resume,
808 fflag || global_fflag) == -1)
821 sdirent_comp(const void *aa, const void *bb)
823 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
824 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
825 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
827 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
828 if (sort_flag & LS_NAME_SORT)
829 return (rmul * strcmp(a->filename, b->filename));
830 else if (sort_flag & LS_TIME_SORT)
831 return (rmul * NCMP(a->a.mtime, b->a.mtime));
832 else if (sort_flag & LS_SIZE_SORT)
833 return (rmul * NCMP(a->a.size, b->a.size));
835 fatal("Unknown ls sort type");
838 /* sftp ls.1 replacement for directories */
840 do_ls_dir(struct sftp_conn *conn, const char *path,
841 const char *strip_path, int lflag)
844 u_int c = 1, colspace = 0, columns = 1;
847 if ((n = do_readdir(conn, path, &d)) != 0)
850 if (!(lflag & LS_SHORT_VIEW)) {
851 u_int m = 0, width = 80;
855 /* Count entries for sort and find longest filename */
856 for (n = 0; d[n] != NULL; n++) {
857 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
858 m = MAXIMUM(m, strlen(d[n]->filename));
861 /* Add any subpath that also needs to be counted */
862 tmp = path_strip(path, strip_path);
866 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
869 columns = width / (m + 2);
870 columns = MAXIMUM(columns, 1);
871 colspace = width / columns;
872 colspace = MINIMUM(colspace, width);
875 if (lflag & SORT_FLAGS) {
876 for (n = 0; d[n] != NULL; n++)
877 ; /* count entries */
878 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
879 qsort(d, n, sizeof(*d), sdirent_comp);
882 for (n = 0; d[n] != NULL && !interrupted; n++) {
885 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
888 tmp = path_append(path, d[n]->filename);
889 fname = path_strip(tmp, strip_path);
892 if (lflag & LS_LONG_VIEW) {
893 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
897 memset(&sb, 0, sizeof(sb));
898 attrib_to_stat(&d[n]->a, &sb);
899 lname = ls_file(fname, &sb, 1,
900 (lflag & LS_SI_UNITS));
901 mprintf("%s\n", lname);
904 mprintf("%s\n", d[n]->longname);
906 mprintf("%-*s", colspace, fname);
917 if (!(lflag & LS_LONG_VIEW) && (c != 1))
920 free_sftp_dirents(d);
925 sglob_comp(const void *aa, const void *bb)
927 u_int a = *(const u_int *)aa;
928 u_int b = *(const u_int *)bb;
929 const char *ap = sort_glob->gl_pathv[a];
930 const char *bp = sort_glob->gl_pathv[b];
931 const struct stat *as = sort_glob->gl_statv[a];
932 const struct stat *bs = sort_glob->gl_statv[b];
933 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
935 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
936 if (sort_flag & LS_NAME_SORT)
937 return (rmul * strcmp(ap, bp));
938 else if (sort_flag & LS_TIME_SORT) {
939 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
940 return (rmul * timespeccmp(&as->st_mtim, &bs->st_mtim, <));
941 #elif defined(HAVE_STRUCT_STAT_ST_MTIME)
942 return (rmul * NCMP(as->st_mtime, bs->st_mtime));
946 } else if (sort_flag & LS_SIZE_SORT)
947 return (rmul * NCMP(as->st_size, bs->st_size));
949 fatal("Unknown ls sort type");
952 /* sftp ls.1 replacement which handles path globs */
954 do_globbed_ls(struct sftp_conn *conn, const char *path,
955 const char *strip_path, int lflag)
961 u_int i, j, nentries, *indices = NULL, c = 1;
962 u_int colspace = 0, columns = 1, m = 0, width = 80;
964 memset(&g, 0, sizeof(g));
966 if ((r = remote_glob(conn, path,
967 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
969 (g.gl_pathc && !g.gl_matchc)) {
972 if (r == GLOB_NOSPACE) {
973 error("Can't ls: Too many matches for \"%s\"", path);
975 error("Can't ls: \"%s\" not found", path);
984 * If the glob returns a single match and it is a directory,
985 * then just list its contents.
987 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
988 S_ISDIR(g.gl_statv[0]->st_mode)) {
989 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
994 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
997 if (!(lflag & LS_SHORT_VIEW)) {
998 /* Count entries for sort and find longest filename */
999 for (i = 0; g.gl_pathv[i]; i++)
1000 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
1002 columns = width / (m + 2);
1003 columns = MAXIMUM(columns, 1);
1004 colspace = width / columns;
1008 * Sorting: rather than mess with the contents of glob_t, prepare
1009 * an array of indices into it and sort that. For the usual
1010 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
1012 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
1013 ; /* count entries */
1014 indices = calloc(nentries, sizeof(*indices));
1015 for (i = 0; i < nentries; i++)
1018 if (lflag & SORT_FLAGS) {
1020 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
1021 qsort(indices, nentries, sizeof(*indices), sglob_comp);
1025 for (j = 0; j < nentries && !interrupted; j++) {
1027 fname = path_strip(g.gl_pathv[i], strip_path);
1028 if (lflag & LS_LONG_VIEW) {
1029 if (g.gl_statv[i] == NULL) {
1030 error("no stat information for %s", fname);
1033 lname = ls_file(fname, g.gl_statv[i], 1,
1034 (lflag & LS_SI_UNITS));
1035 mprintf("%s\n", lname);
1038 mprintf("%-*s", colspace, fname);
1048 if (!(lflag & LS_LONG_VIEW) && (c != 1))
1060 do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
1062 struct sftp_statvfs st;
1063 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
1064 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
1065 char s_icapacity[16], s_dcapacity[16];
1067 if (do_statvfs(conn, path, &st, 1) == -1)
1069 if (st.f_files == 0)
1070 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1072 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1073 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1076 if (st.f_blocks == 0)
1077 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1079 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1080 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1084 printf(" Inodes Used Avail "
1085 "(root) %%Capacity\n");
1086 printf("%11llu %11llu %11llu %11llu %s\n",
1087 (unsigned long long)st.f_files,
1088 (unsigned long long)(st.f_files - st.f_ffree),
1089 (unsigned long long)st.f_favail,
1090 (unsigned long long)st.f_ffree, s_icapacity);
1092 strlcpy(s_used, "error", sizeof(s_used));
1093 strlcpy(s_avail, "error", sizeof(s_avail));
1094 strlcpy(s_root, "error", sizeof(s_root));
1095 strlcpy(s_total, "error", sizeof(s_total));
1096 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1097 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1098 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1099 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1100 printf(" Size Used Avail (root) %%Capacity\n");
1101 printf("%7sB %7sB %7sB %7sB %s\n",
1102 s_total, s_used, s_avail, s_root, s_dcapacity);
1104 printf(" Size Used Avail "
1105 "(root) %%Capacity\n");
1106 printf("%12llu %12llu %12llu %12llu %s\n",
1107 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1108 (unsigned long long)(st.f_frsize *
1109 (st.f_blocks - st.f_bfree) / 1024),
1110 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1111 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1118 * Undo escaping of glob sequences in place. Used to undo extra escaping
1119 * applied in makeargv() when the string is destined for a function that
1123 undo_glob_escape(char *s)
1158 * Split a string into an argument vector using sh(1)-style quoting,
1159 * comment and escaping rules, but with some tweaks to handle glob(3)
1161 * The "sloppy" flag allows for recovery from missing terminating quote, for
1162 * use in parsing incomplete commandlines during tab autocompletion.
1164 * Returns NULL on error or a NULL-terminated array of arguments.
1166 * If "lastquote" is not NULL, the quoting character used for the last
1167 * argument is placed in *lastquote ("\0", "'" or "\"").
1169 * If "terminated" is not NULL, *terminated will be set to 1 when the
1170 * last argument's quote has been properly terminated or 0 otherwise.
1171 * This parameter is only of use if "sloppy" is set.
1174 #define MAXARGLEN 8192
1176 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1181 static char argvs[MAXARGLEN];
1182 static char *argv[MAXARGS + 1];
1183 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1186 if (strlen(arg) > sizeof(argvs) - 1) {
1188 error("string too long");
1191 if (terminated != NULL)
1193 if (lastquote != NULL)
1198 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1199 error("Too many arguments.");
1202 if (isspace((unsigned char)arg[i])) {
1203 if (state == MA_UNQUOTED) {
1204 /* Terminate current argument */
1208 } else if (state != MA_START)
1209 argvs[j++] = arg[i];
1210 } else if (arg[i] == '"' || arg[i] == '\'') {
1211 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1212 if (state == MA_START) {
1213 argv[argc] = argvs + j;
1215 if (lastquote != NULL)
1216 *lastquote = arg[i];
1217 } else if (state == MA_UNQUOTED)
1219 else if (state == q)
1220 state = MA_UNQUOTED;
1222 argvs[j++] = arg[i];
1223 } else if (arg[i] == '\\') {
1224 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1225 quot = state == MA_SQUOTE ? '\'' : '"';
1226 /* Unescape quote we are in */
1227 /* XXX support \n and friends? */
1228 if (arg[i + 1] == quot) {
1230 argvs[j++] = arg[i];
1231 } else if (arg[i + 1] == '?' ||
1232 arg[i + 1] == '[' || arg[i + 1] == '*') {
1234 * Special case for sftp: append
1235 * double-escaped glob sequence -
1236 * glob will undo one level of
1237 * escaping. NB. string can grow here.
1239 if (j >= sizeof(argvs) - 5)
1240 goto args_too_longs;
1242 argvs[j++] = arg[i++];
1244 argvs[j++] = arg[i];
1246 argvs[j++] = arg[i++];
1247 argvs[j++] = arg[i];
1250 if (state == MA_START) {
1251 argv[argc] = argvs + j;
1252 state = MA_UNQUOTED;
1253 if (lastquote != NULL)
1256 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1257 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1259 * Special case for sftp: append
1260 * escaped glob sequence -
1261 * glob will undo one level of
1264 argvs[j++] = arg[i++];
1265 argvs[j++] = arg[i];
1267 /* Unescape everything */
1268 /* XXX support \n and friends? */
1270 argvs[j++] = arg[i];
1273 } else if (arg[i] == '#') {
1274 if (state == MA_SQUOTE || state == MA_DQUOTE)
1275 argvs[j++] = arg[i];
1278 } else if (arg[i] == '\0') {
1279 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1281 state = MA_UNQUOTED;
1282 if (terminated != NULL)
1286 error("Unterminated quoted argument");
1290 if (state == MA_UNQUOTED) {
1296 if (state == MA_START) {
1297 argv[argc] = argvs + j;
1298 state = MA_UNQUOTED;
1299 if (lastquote != NULL)
1302 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1303 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1305 * Special case for sftp: escape quoted
1306 * glob(3) wildcards. NB. string can grow
1309 if (j >= sizeof(argvs) - 3)
1310 goto args_too_longs;
1312 argvs[j++] = arg[i];
1314 argvs[j++] = arg[i];
1323 parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
1324 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1325 int *rflag, int *sflag,
1326 unsigned long *n_arg, char **path1, char **path2)
1328 const char *cmd, *cp = *cpp;
1332 int path1_mandatory = 0, i, cmdnum, optidx, argc;
1334 /* Skip leading whitespace */
1335 cp = cp + strspn(cp, WHITESPACE);
1338 * Check for leading '-' (disable error processing) and '@' (suppress
1343 for (;*cp != '\0'; cp++) {
1346 } else if (*cp == '@') {
1349 /* all other characters terminate prefix processing */
1353 cp = cp + strspn(cp, WHITESPACE);
1355 /* Ignore blank lines and lines which begin with comment '#' char */
1356 if (*cp == '\0' || *cp == '#')
1359 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1362 /* Figure out which command we have */
1363 for (i = 0; cmds[i].c != NULL; i++) {
1364 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1374 } else if (cmdnum == -1) {
1375 error("Invalid command.");
1379 /* Get arguments and parse flags */
1380 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1381 *rflag = *sflag = 0;
1382 *path1 = *path2 = NULL;
1389 if ((optidx = parse_getput_flags(cmd, argv, argc,
1390 aflag, fflag, pflag, rflag)) == -1)
1392 /* Get first pathname (mandatory) */
1393 if (argc - optidx < 1) {
1394 error("You must specify at least one path after a "
1395 "%s command.", cmd);
1398 *path1 = xstrdup(argv[optidx]);
1399 /* Get second pathname (optional) */
1400 if (argc - optidx > 1) {
1401 *path2 = xstrdup(argv[optidx + 1]);
1402 /* Destination is not globbed */
1403 undo_glob_escape(*path2);
1407 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1409 goto parse_two_paths;
1411 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1413 goto parse_two_paths;
1415 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1418 if (argc - optidx < 2) {
1419 error("You must specify two paths after a %s "
1423 *path1 = xstrdup(argv[optidx]);
1424 *path2 = xstrdup(argv[optidx + 1]);
1425 /* Paths are not globbed */
1426 undo_glob_escape(*path1);
1427 undo_glob_escape(*path2);
1433 path1_mandatory = 1;
1437 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1439 /* Get pathname (mandatory) */
1440 if (argc - optidx < 1) {
1441 if (!path1_mandatory)
1442 break; /* return a NULL path1 */
1443 error("You must specify a path after a %s command.",
1447 *path1 = xstrdup(argv[optidx]);
1448 /* Only "rm" globs */
1450 undo_glob_escape(*path1);
1453 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1456 /* Default to current directory if no path specified */
1457 if (argc - optidx < 1)
1460 *path1 = xstrdup(argv[optidx]);
1461 undo_glob_escape(*path1);
1465 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1467 /* Path is optional */
1468 if (argc - optidx > 0)
1469 *path1 = xstrdup(argv[optidx]);
1472 /* Skip ls command and following whitespace */
1473 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1475 /* Uses the rest of the line */
1483 if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
1485 /* Get numeric arg (mandatory) */
1486 if (argc - optidx < 1)
1489 l = strtol(argv[optidx], &cp2, base);
1490 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1491 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1494 error("You must supply a numeric argument "
1495 "to the %s command.", cmd);
1499 if (cmdnum == I_LUMASK)
1501 /* Get pathname (mandatory) */
1502 if (argc - optidx < 2) {
1503 error("You must specify a path after a %s command.",
1507 *path1 = xstrdup(argv[optidx + 1]);
1515 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1519 fatal("Command not implemented");
1527 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1528 const char *startdir, int err_abort, int echo_command)
1530 const char *ocmd = cmd;
1531 char *path1, *path2, *tmp;
1532 int ignore_errors = 0, disable_echo = 1;
1533 int aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1534 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1536 unsigned long n_arg = 0;
1538 char path_buf[PATH_MAX];
1542 path1 = path2 = NULL;
1543 cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag,
1544 &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg,
1546 if (ignore_errors != 0)
1549 if (echo_command && !disable_echo)
1550 mprintf("sftp> %s\n", ocmd);
1552 memset(&g, 0, sizeof(g));
1554 /* Perform command */
1560 /* Unrecognized command */
1567 err = process_get(conn, path1, path2, *pwd, pflag,
1568 rflag, aflag, fflag);
1574 err = process_put(conn, path1, path2, *pwd, pflag,
1575 rflag, aflag, fflag);
1578 path1 = make_absolute(path1, *pwd);
1579 path2 = make_absolute(path2, *pwd);
1580 err = do_rename(conn, path1, path2, lflag);
1587 path1 = make_absolute(path1, *pwd);
1588 path2 = make_absolute(path2, *pwd);
1589 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1592 path1 = make_absolute(path1, *pwd);
1593 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1594 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1596 mprintf("Removing %s\n", g.gl_pathv[i]);
1597 err = do_rm(conn, g.gl_pathv[i]);
1598 if (err != 0 && err_abort)
1603 path1 = make_absolute(path1, *pwd);
1605 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1607 err = do_mkdir(conn, path1, &a, 1);
1610 path1 = make_absolute(path1, *pwd);
1611 err = do_rmdir(conn, path1);
1614 if (path1 == NULL || *path1 == '\0')
1615 path1 = xstrdup(startdir);
1616 path1 = make_absolute(path1, *pwd);
1617 if ((tmp = do_realpath(conn, path1)) == NULL) {
1621 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1626 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1627 error("Can't change directory: Can't check target");
1632 if (!S_ISDIR(aa->perm)) {
1633 error("Can't change directory: \"%s\" is not "
1634 "a directory", tmp);
1644 do_ls_dir(conn, *pwd, *pwd, lflag);
1648 /* Strip pwd off beginning of non-absolute paths */
1650 if (!path_absolute(path1))
1653 path1 = make_absolute(path1, *pwd);
1654 err = do_globbed_ls(conn, path1, tmp, lflag);
1657 /* Default to current directory if no path specified */
1659 path1 = xstrdup(*pwd);
1660 path1 = make_absolute(path1, *pwd);
1661 err = do_df(conn, path1, hflag, iflag);
1664 if (path1 == NULL || *path1 == '\0')
1665 path1 = xstrdup("~");
1666 tmp = tilde_expand_filename(path1, getuid());
1669 if (chdir(path1) == -1) {
1670 error("Couldn't change local directory to "
1671 "\"%s\": %s", path1, strerror(errno));
1676 if (mkdir(path1, 0777) == -1) {
1677 error("Couldn't create local directory "
1678 "\"%s\": %s", path1, strerror(errno));
1686 local_do_shell(cmd);
1690 printf("Local umask: %03lo\n", n_arg);
1693 path1 = make_absolute(path1, *pwd);
1695 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1697 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1698 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1700 mprintf("Changing mode on %s\n",
1702 err = (hflag ? do_lsetstat : do_setstat)(conn,
1704 if (err != 0 && err_abort)
1710 path1 = make_absolute(path1, *pwd);
1711 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1712 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1713 if (!(aa = (hflag ? do_lstat : do_stat)(conn,
1714 g.gl_pathv[i], 0))) {
1721 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1722 error("Can't get current ownership of "
1723 "remote file \"%s\"", g.gl_pathv[i]);
1730 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1731 if (cmdnum == I_CHOWN) {
1733 mprintf("Changing owner on %s\n",
1738 mprintf("Changing group on %s\n",
1742 err = (hflag ? do_lsetstat : do_setstat)(conn,
1744 if (err != 0 && err_abort)
1749 mprintf("Remote working directory: %s\n", *pwd);
1752 if (!getcwd(path_buf, sizeof(path_buf))) {
1753 error("Couldn't get local cwd: %s", strerror(errno));
1757 mprintf("Local working directory: %s\n", path_buf);
1760 /* Processed below */
1766 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1769 showprogress = !showprogress;
1771 printf("Progress meter enabled\n");
1773 printf("Progress meter disabled\n");
1776 fatal("%d is not implemented", cmdnum);
1784 /* If an unignored error occurs in batch mode we should abort. */
1785 if (err_abort && err != 0)
1787 else if (cmdnum == I_QUIT)
1795 prompt(EditLine *el)
1800 /* Display entries in 'list' after skipping the first 'len' chars */
1802 complete_display(char **list, u_int len)
1804 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1808 /* Count entries for sort and find longest */
1809 for (y = 0; list[y]; y++)
1810 m = MAXIMUM(m, strlen(list[y]));
1812 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1815 m = m > len ? m - len : 0;
1816 columns = width / (m + 2);
1817 columns = MAXIMUM(columns, 1);
1818 colspace = width / columns;
1819 colspace = MINIMUM(colspace, width);
1823 for (y = 0; list[y]; y++) {
1824 llen = strlen(list[y]);
1825 tmp = llen > len ? list[y] + len : "";
1826 mprintf("%-*s", colspace, tmp);
1837 * Given a "list" of words that begin with a common prefix of "word",
1838 * attempt to find an autocompletion to extends "word" by the next
1839 * characters common to all entries in "list".
1842 complete_ambiguous(const char *word, char **list, size_t count)
1848 u_int y, matchlen = strlen(list[0]);
1850 /* Find length of common stem */
1851 for (y = 1; list[y]; y++) {
1854 for (x = 0; x < matchlen; x++)
1855 if (list[0][x] != list[y][x])
1861 if (matchlen > strlen(word)) {
1862 char *tmp = xstrdup(list[0]);
1864 tmp[matchlen] = '\0';
1869 return xstrdup(word);
1872 /* Autocomplete a sftp command */
1874 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1877 u_int y, count = 0, cmdlen, tmplen;
1878 char *tmp, **list, argterm[3];
1881 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1883 /* No command specified: display all available commands */
1885 for (y = 0; cmds[y].c; y++)
1886 list[count++] = xstrdup(cmds[y].c);
1889 complete_display(list, 0);
1891 for (y = 0; list[y] != NULL; y++)
1897 /* Prepare subset of commands that start with "cmd" */
1898 cmdlen = strlen(cmd);
1899 for (y = 0; cmds[y].c; y++) {
1900 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1901 list[count++] = xstrdup(cmds[y].c);
1910 /* Complete ambiguous command */
1911 tmp = complete_ambiguous(cmd, list, count);
1913 complete_display(list, 0);
1915 for (y = 0; list[y]; y++)
1920 tmplen = strlen(tmp);
1921 cmdlen = strlen(cmd);
1922 /* If cmd may be extended then do so */
1923 if (tmplen > cmdlen)
1924 if (el_insertstr(el, tmp + cmdlen) == -1)
1925 fatal("el_insertstr failed.");
1927 /* Terminate argument cleanly */
1931 argterm[y++] = quote;
1932 if (lastarg || *(lf->cursor) != ' ')
1935 if (y > 0 && el_insertstr(el, argterm) == -1)
1936 fatal("el_insertstr failed.");
1945 * Determine whether a particular sftp command's arguments (if any)
1946 * represent local or remote files.
1949 complete_is_remote(char *cmd) {
1955 for (i = 0; cmds[i].c; i++) {
1956 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1963 /* Autocomplete a filename "file" */
1965 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1966 char *file, int remote, int lastarg, char quote, int terminated)
1969 char *tmp, *tmp2, ins[8];
1970 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1974 /* Glob from "file" location */
1978 xasprintf(&tmp, "%s*", file);
1980 /* Check if the path is absolute. */
1981 isabs = path_absolute(tmp);
1983 memset(&g, 0, sizeof(g));
1984 if (remote != LOCAL) {
1985 tmp = make_absolute(tmp, remote_path);
1986 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1988 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1990 /* Determine length of pwd so we can trim completion display */
1991 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1992 /* Terminate counting on first unescaped glob metacharacter */
1993 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1994 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1998 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
2000 if (tmp[tmplen] == '/')
2001 pwdlen = tmplen + 1; /* track last seen '/' */
2006 if (g.gl_matchc == 0)
2009 if (g.gl_matchc > 1)
2010 complete_display(g.gl_pathv, pwdlen);
2012 /* Don't try to extend globs */
2013 if (file == NULL || hadglob)
2016 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
2017 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
2023 tmplen = strlen(tmp);
2024 filelen = strlen(file);
2026 /* Count the number of escaped characters in the input string. */
2028 for (i = 0; i < filelen; i++) {
2029 if (!isesc && file[i] == '\\' && i + 1 < filelen){
2036 if (tmplen > (filelen - cesc)) {
2037 tmp2 = tmp + filelen - cesc;
2039 /* quote argument on way out */
2040 for (i = 0; i < len; i += clen) {
2041 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
2042 (size_t)clen > sizeof(ins) - 2)
2043 fatal("invalid multibyte character");
2045 memcpy(ins + 1, tmp2 + i, clen);
2046 ins[clen + 1] = '\0';
2056 if (quote == '\0' || tmp2[i] == quote) {
2057 if (el_insertstr(el, ins) == -1)
2058 fatal("el_insertstr "
2064 if (el_insertstr(el, ins + 1) == -1)
2065 fatal("el_insertstr failed.");
2072 if (g.gl_matchc == 1) {
2074 if (!terminated && quote != '\0')
2076 if (*(lf->cursor - 1) != '/' &&
2077 (lastarg || *(lf->cursor) != ' '))
2080 if (i > 0 && el_insertstr(el, ins) == -1)
2081 fatal("el_insertstr failed.");
2090 /* tab-completion hook function, called via libedit */
2091 static unsigned char
2092 complete(EditLine *el, int ch)
2094 char **argv, *line, quote;
2096 u_int cursor, len, terminated, ret = CC_ERROR;
2098 struct complete_ctx *complete_ctx;
2101 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2102 fatal("%s: el_get failed", __func__);
2104 /* Figure out which argument the cursor points to */
2105 cursor = lf->cursor - lf->buffer;
2106 line = xmalloc(cursor + 1);
2107 memcpy(line, lf->buffer, cursor);
2108 line[cursor] = '\0';
2109 argv = makeargv(line, &carg, 1, "e, &terminated);
2112 /* Get all the arguments on the line */
2113 len = lf->lastchar - lf->buffer;
2114 line = xmalloc(len + 1);
2115 memcpy(line, lf->buffer, len);
2117 argv = makeargv(line, &argc, 1, NULL, NULL);
2119 /* Ensure cursor is at EOL or a argument boundary */
2120 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2121 line[cursor] != '\n') {
2127 /* Show all available commands */
2128 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2130 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2131 /* Handle the command parsing */
2132 if (complete_cmd_parse(el, argv[0], argc == carg,
2133 quote, terminated) != 0)
2135 } else if (carg >= 1) {
2136 /* Handle file parsing */
2137 int remote = complete_is_remote(argv[0]);
2138 char *filematch = NULL;
2140 if (carg > 1 && line[cursor-1] != ' ')
2141 filematch = argv[carg - 1];
2144 complete_match(el, complete_ctx->conn,
2145 *complete_ctx->remote_pathp, filematch,
2146 remote, carg == argc, quote, terminated) != 0)
2153 #endif /* USE_LIBEDIT */
2156 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2159 char *dir = NULL, *startdir = NULL;
2161 int err, interactive;
2162 EditLine *el = NULL;
2166 extern char *__progname;
2167 struct complete_ctx complete_ctx;
2169 if (!batchmode && isatty(STDIN_FILENO)) {
2170 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2171 fatal("Couldn't initialise editline");
2172 if ((hl = history_init()) == NULL)
2173 fatal("Couldn't initialise editline history");
2174 history(hl, &hev, H_SETSIZE, 100);
2175 el_set(el, EL_HIST, history, hl);
2177 el_set(el, EL_PROMPT, prompt);
2178 el_set(el, EL_EDITOR, "emacs");
2179 el_set(el, EL_TERMINAL, NULL);
2180 el_set(el, EL_SIGNAL, 1);
2181 el_source(el, NULL);
2183 /* Tab Completion */
2184 el_set(el, EL_ADDFN, "ftp-complete",
2185 "Context sensitive argument completion", complete);
2186 complete_ctx.conn = conn;
2187 complete_ctx.remote_pathp = &remote_path;
2188 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2189 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2190 /* enable ctrl-left-arrow and ctrl-right-arrow */
2191 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2192 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2193 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2194 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2195 /* make ^w match ksh behaviour */
2196 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2198 #endif /* USE_LIBEDIT */
2200 remote_path = do_realpath(conn, ".");
2201 if (remote_path == NULL)
2203 startdir = xstrdup(remote_path);
2205 if (file1 != NULL) {
2206 dir = xstrdup(file1);
2207 dir = make_absolute(dir, remote_path);
2209 if (remote_is_dir(conn, dir) && file2 == NULL) {
2211 mprintf("Changing to: %s\n", dir);
2212 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2213 if (parse_dispatch_command(conn, cmd,
2214 &remote_path, startdir, 1, 0) != 0) {
2222 /* XXX this is wrong wrt quoting */
2223 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2224 global_aflag ? " -a" : "", dir,
2225 file2 == NULL ? "" : " ",
2226 file2 == NULL ? "" : file2);
2227 err = parse_dispatch_command(conn, cmd,
2228 &remote_path, startdir, 1, 0);
2238 setvbuf(stdout, NULL, _IOLBF, 0);
2239 setvbuf(infile, NULL, _IOLBF, 0);
2241 interactive = !batchmode && isatty(STDIN_FILENO);
2244 signal(SIGINT, SIG_IGN);
2249 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2259 if ((line = el_gets(el, &count)) == NULL ||
2264 history(hl, &hev, H_ENTER, line);
2265 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2266 fprintf(stderr, "Error: input line too long\n");
2269 #endif /* USE_LIBEDIT */
2272 cmd[strcspn(cmd, "\n")] = '\0';
2274 /* Handle user interrupts gracefully during commands */
2276 signal(SIGINT, cmd_interrupt);
2278 err = parse_dispatch_command(conn, cmd, &remote_path,
2279 startdir, batchmode, !interactive && el == NULL);
2283 signal(SIGCHLD, SIG_DFL);
2291 #endif /* USE_LIBEDIT */
2293 /* err == 1 signifies normal "quit" exit */
2294 return (err >= 0 ? 0 : -1);
2298 connect_to_server(char *path, char **args, int *in, int *out)
2303 int pin[2], pout[2];
2305 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2306 fatal("pipe: %s", strerror(errno));
2311 #else /* USE_PIPES */
2314 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2315 fatal("socketpair: %s", strerror(errno));
2316 *in = *out = inout[0];
2317 c_in = c_out = inout[1];
2318 #endif /* USE_PIPES */
2320 if ((sshpid = fork()) == -1)
2321 fatal("fork: %s", strerror(errno));
2322 else if (sshpid == 0) {
2323 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2324 (dup2(c_out, STDOUT_FILENO) == -1)) {
2325 fprintf(stderr, "dup2: %s\n", strerror(errno));
2334 * The underlying ssh is in the same process group, so we must
2335 * ignore SIGINT if we want to gracefully abort commands,
2336 * otherwise the signal will make it to the ssh process and
2337 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2338 * underlying ssh, it must *not* ignore that signal.
2340 signal(SIGINT, SIG_IGN);
2341 signal(SIGTERM, SIG_DFL);
2343 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2347 signal(SIGTERM, killchild);
2348 signal(SIGINT, killchild);
2349 signal(SIGHUP, killchild);
2350 signal(SIGTSTP, suspchild);
2351 signal(SIGTTIN, suspchild);
2352 signal(SIGTTOU, suspchild);
2353 signal(SIGCHLD, sigchld_handler);
2361 extern char *__progname;
2364 "usage: %s [-46aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2365 " [-D sftp_server_path] [-F ssh_config] [-i identity_file]\n"
2366 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
2367 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
2374 main(int argc, char **argv)
2376 int in, out, ch, err, tmp, port = -1;
2377 char *host = NULL, *user, *cp, *file2 = NULL;
2378 int debug_level = 0, sshver = 2;
2379 char *file1 = NULL, *sftp_server = NULL;
2380 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2382 LogLevel ll = SYSLOG_LEVEL_INFO;
2385 extern char *optarg;
2386 struct sftp_conn *conn;
2387 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2388 size_t num_requests = DEFAULT_NUM_REQUESTS;
2389 long long limit_kbps = 0;
2391 ssh_malloc_init(); /* must be called before any mallocs */
2392 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2398 __progname = ssh_get_progname(argv[0]);
2399 memset(&args, '\0', sizeof(args));
2401 addargs(&args, "%s", ssh_program);
2402 addargs(&args, "-oForwardX11 no");
2403 addargs(&args, "-oForwardAgent no");
2404 addargs(&args, "-oPermitLocalCommand no");
2405 addargs(&args, "-oClearAllForwardings yes");
2407 ll = SYSLOG_LEVEL_INFO;
2410 while ((ch = getopt(argc, argv,
2411 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:")) != -1) {
2413 /* Passed through to ssh(1) */
2417 addargs(&args, "-%c", ch);
2419 /* Passed through to ssh(1) with argument */
2425 addargs(&args, "-%c", ch);
2426 addargs(&args, "%s", optarg);
2429 ll = SYSLOG_LEVEL_ERROR;
2432 addargs(&args, "-%c", ch);
2435 port = a2port(optarg);
2437 fatal("Bad port \"%s\"\n", optarg);
2440 if (debug_level < 3) {
2441 addargs(&args, "-v");
2442 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2448 if (sftp_server == NULL)
2449 sftp_server = _PATH_SFTP_SERVER;
2458 copy_buffer_len = strtol(optarg, &cp, 10);
2459 if (copy_buffer_len == 0 || *cp != '\0')
2460 fatal("Invalid buffer size \"%s\"", optarg);
2464 fatal("Batch file already specified.");
2466 /* Allow "-" as stdin */
2467 if (strcmp(optarg, "-") != 0 &&
2468 (infile = fopen(optarg, "r")) == NULL)
2469 fatal("%s (%s).", strerror(errno), optarg);
2471 quiet = batchmode = 1;
2472 addargs(&args, "-obatchmode yes");
2481 sftp_direct = optarg;
2484 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2488 limit_kbps *= 1024; /* kbps */
2494 num_requests = strtol(optarg, &cp, 10);
2495 if (num_requests == 0 || *cp != '\0')
2496 fatal("Invalid number of requests \"%s\"",
2500 sftp_server = optarg;
2503 ssh_program = optarg;
2504 replacearg(&args, 0, "%s", ssh_program);
2512 if (!isatty(STDERR_FILENO))
2515 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2517 if (sftp_direct == NULL) {
2518 if (optind == argc || argc > (optind + 2))
2522 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2531 if (parse_user_host_path(*argv, &user, &host,
2533 /* Treat as a plain hostname. */
2534 host = xstrdup(*argv);
2535 host = cleanhostname(host);
2539 file2 = *(argv + 1);
2542 fprintf(stderr, "Missing hostname\n");
2547 addargs(&args, "-oPort %d", port);
2549 addargs(&args, "-l");
2550 addargs(&args, "%s", user);
2552 addargs(&args, "-oProtocol %d", sshver);
2554 /* no subsystem if the server-spec contains a '/' */
2555 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2556 addargs(&args, "-s");
2558 addargs(&args, "--");
2559 addargs(&args, "%s", host);
2560 addargs(&args, "%s", (sftp_server != NULL ?
2561 sftp_server : "sftp"));
2563 connect_to_server(ssh_program, args.list, &in, &out);
2566 addargs(&args, "sftp-server");
2568 connect_to_server(sftp_direct, args.list, &in, &out);
2572 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2574 fatal("Couldn't initialise connection to server");
2577 if (sftp_direct == NULL)
2578 fprintf(stderr, "Connected to %s.\n", host);
2580 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2583 err = interactive_loop(conn, file1, file2);
2585 #if !defined(USE_PIPES)
2586 shutdown(in, SHUT_RDWR);
2587 shutdown(out, SHUT_RDWR);
2595 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
2597 fatal("Couldn't wait for ssh process: %s",
2600 exit(err == 0 ? 0 : 1);