1 /* $OpenBSD: sftp.c,v 1.185 2018/04/26 14:47:03 bluhm 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 grp path Change group of file 'path' to 'grp'\n"
282 "chmod mode path Change permissions of file 'path' to 'mode'\n"
283 "chown 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 && p[0] != '/') {
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_no_flags(const char *cmd, char **argv, int argc)
567 extern int opterr, optind, optopt, optreset;
570 optind = optreset = 1;
573 while ((ch = getopt(argc, argv, "")) != -1) {
576 error("%s: Invalid flag -%c", cmd, optopt);
585 is_dir(const char *path)
589 /* XXX: report errors? */
590 if (stat(path, &sb) == -1)
593 return(S_ISDIR(sb.st_mode));
597 remote_is_dir(struct sftp_conn *conn, const char *path)
601 /* XXX: report errors? */
602 if ((a = do_stat(conn, path, 1)) == NULL)
604 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
606 return(S_ISDIR(a->perm));
609 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
611 pathname_is_dir(const char *pathname)
613 size_t l = strlen(pathname);
615 return l > 0 && pathname[l - 1] == '/';
619 process_get(struct sftp_conn *conn, const char *src, const char *dst,
620 const char *pwd, int pflag, int rflag, int resume, int fflag)
622 char *abs_src = NULL;
623 char *abs_dst = NULL;
625 char *filename, *tmp=NULL;
628 abs_src = xstrdup(src);
629 abs_src = make_absolute(abs_src, pwd);
630 memset(&g, 0, sizeof(g));
632 debug3("Looking up %s", abs_src);
633 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
634 if (r == GLOB_NOSPACE) {
635 error("Too many matches for \"%s\".", abs_src);
637 error("File \"%s\" not found.", abs_src);
644 * If multiple matches then dst must be a directory or
647 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
648 error("Multiple source paths, but destination "
649 "\"%s\" is not a directory", dst);
654 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
655 tmp = xstrdup(g.gl_pathv[i]);
656 if ((filename = basename(tmp)) == NULL) {
657 error("basename %s: %s", tmp, strerror(errno));
663 if (g.gl_matchc == 1 && dst) {
665 abs_dst = path_append(dst, filename);
667 abs_dst = xstrdup(dst);
670 abs_dst = path_append(dst, filename);
672 abs_dst = xstrdup(filename);
676 resume |= global_aflag;
677 if (!quiet && resume)
678 mprintf("Resuming %s to %s\n",
679 g.gl_pathv[i], abs_dst);
680 else if (!quiet && !resume)
681 mprintf("Fetching %s to %s\n",
682 g.gl_pathv[i], abs_dst);
683 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
684 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
685 pflag || global_pflag, 1, resume,
686 fflag || global_fflag) == -1)
689 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
690 pflag || global_pflag, resume,
691 fflag || global_fflag) == -1)
705 process_put(struct sftp_conn *conn, const char *src, const char *dst,
706 const char *pwd, int pflag, int rflag, int resume, int fflag)
708 char *tmp_dst = NULL;
709 char *abs_dst = NULL;
710 char *tmp = NULL, *filename = NULL;
713 int i, dst_is_dir = 1;
717 tmp_dst = xstrdup(dst);
718 tmp_dst = make_absolute(tmp_dst, pwd);
721 memset(&g, 0, sizeof(g));
722 debug3("Looking up %s", src);
723 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
724 error("File \"%s\" not found.", src);
729 /* If we aren't fetching to pwd then stash this status for later */
731 dst_is_dir = remote_is_dir(conn, tmp_dst);
733 /* If multiple matches, dst may be directory or unspecified */
734 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
735 error("Multiple paths match, but destination "
736 "\"%s\" is not a directory", tmp_dst);
741 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
742 if (stat(g.gl_pathv[i], &sb) == -1) {
744 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
748 tmp = xstrdup(g.gl_pathv[i]);
749 if ((filename = basename(tmp)) == NULL) {
750 error("basename %s: %s", tmp, strerror(errno));
756 if (g.gl_matchc == 1 && tmp_dst) {
757 /* If directory specified, append filename */
759 abs_dst = path_append(tmp_dst, filename);
761 abs_dst = xstrdup(tmp_dst);
762 } else if (tmp_dst) {
763 abs_dst = path_append(tmp_dst, filename);
765 abs_dst = make_absolute(xstrdup(filename), pwd);
769 resume |= global_aflag;
770 if (!quiet && resume)
771 mprintf("Resuming upload of %s to %s\n",
772 g.gl_pathv[i], abs_dst);
773 else if (!quiet && !resume)
774 mprintf("Uploading %s to %s\n",
775 g.gl_pathv[i], abs_dst);
776 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
777 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
778 pflag || global_pflag, 1, resume,
779 fflag || global_fflag) == -1)
782 if (do_upload(conn, g.gl_pathv[i], abs_dst,
783 pflag || global_pflag, resume,
784 fflag || global_fflag) == -1)
797 sdirent_comp(const void *aa, const void *bb)
799 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
800 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
801 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
803 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
804 if (sort_flag & LS_NAME_SORT)
805 return (rmul * strcmp(a->filename, b->filename));
806 else if (sort_flag & LS_TIME_SORT)
807 return (rmul * NCMP(a->a.mtime, b->a.mtime));
808 else if (sort_flag & LS_SIZE_SORT)
809 return (rmul * NCMP(a->a.size, b->a.size));
811 fatal("Unknown ls sort type");
814 /* sftp ls.1 replacement for directories */
816 do_ls_dir(struct sftp_conn *conn, const char *path,
817 const char *strip_path, int lflag)
820 u_int c = 1, colspace = 0, columns = 1;
823 if ((n = do_readdir(conn, path, &d)) != 0)
826 if (!(lflag & LS_SHORT_VIEW)) {
827 u_int m = 0, width = 80;
831 /* Count entries for sort and find longest filename */
832 for (n = 0; d[n] != NULL; n++) {
833 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
834 m = MAXIMUM(m, strlen(d[n]->filename));
837 /* Add any subpath that also needs to be counted */
838 tmp = path_strip(path, strip_path);
842 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
845 columns = width / (m + 2);
846 columns = MAXIMUM(columns, 1);
847 colspace = width / columns;
848 colspace = MINIMUM(colspace, width);
851 if (lflag & SORT_FLAGS) {
852 for (n = 0; d[n] != NULL; n++)
853 ; /* count entries */
854 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
855 qsort(d, n, sizeof(*d), sdirent_comp);
858 for (n = 0; d[n] != NULL && !interrupted; n++) {
861 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
864 tmp = path_append(path, d[n]->filename);
865 fname = path_strip(tmp, strip_path);
868 if (lflag & LS_LONG_VIEW) {
869 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
873 memset(&sb, 0, sizeof(sb));
874 attrib_to_stat(&d[n]->a, &sb);
875 lname = ls_file(fname, &sb, 1,
876 (lflag & LS_SI_UNITS));
877 mprintf("%s\n", lname);
880 mprintf("%s\n", d[n]->longname);
882 mprintf("%-*s", colspace, fname);
893 if (!(lflag & LS_LONG_VIEW) && (c != 1))
896 free_sftp_dirents(d);
901 sglob_comp(const void *aa, const void *bb)
903 u_int a = *(const u_int *)aa;
904 u_int b = *(const u_int *)bb;
905 const char *ap = sort_glob->gl_pathv[a];
906 const char *bp = sort_glob->gl_pathv[b];
907 const struct stat *as = sort_glob->gl_statv[a];
908 const struct stat *bs = sort_glob->gl_statv[b];
909 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
911 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
912 if (sort_flag & LS_NAME_SORT)
913 return (rmul * strcmp(ap, bp));
914 else if (sort_flag & LS_TIME_SORT) {
915 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
916 return (rmul * timespeccmp(&as->st_mtim, &bs->st_mtim, <));
917 #elif defined(HAVE_STRUCT_STAT_ST_MTIME)
918 return (rmul * NCMP(as->st_mtime, bs->st_mtime));
922 } else if (sort_flag & LS_SIZE_SORT)
923 return (rmul * NCMP(as->st_size, bs->st_size));
925 fatal("Unknown ls sort type");
928 /* sftp ls.1 replacement which handles path globs */
930 do_globbed_ls(struct sftp_conn *conn, const char *path,
931 const char *strip_path, int lflag)
937 u_int i, j, nentries, *indices = NULL, c = 1;
938 u_int colspace = 0, columns = 1, m = 0, width = 80;
940 memset(&g, 0, sizeof(g));
942 if ((r = remote_glob(conn, path,
943 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
945 (g.gl_pathc && !g.gl_matchc)) {
948 if (r == GLOB_NOSPACE) {
949 error("Can't ls: Too many matches for \"%s\"", path);
951 error("Can't ls: \"%s\" not found", path);
960 * If the glob returns a single match and it is a directory,
961 * then just list its contents.
963 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
964 S_ISDIR(g.gl_statv[0]->st_mode)) {
965 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
970 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
973 if (!(lflag & LS_SHORT_VIEW)) {
974 /* Count entries for sort and find longest filename */
975 for (i = 0; g.gl_pathv[i]; i++)
976 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
978 columns = width / (m + 2);
979 columns = MAXIMUM(columns, 1);
980 colspace = width / columns;
984 * Sorting: rather than mess with the contents of glob_t, prepare
985 * an array of indices into it and sort that. For the usual
986 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
988 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
989 ; /* count entries */
990 indices = calloc(nentries, sizeof(*indices));
991 for (i = 0; i < nentries; i++)
994 if (lflag & SORT_FLAGS) {
996 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
997 qsort(indices, nentries, sizeof(*indices), sglob_comp);
1001 for (j = 0; j < nentries && !interrupted; j++) {
1003 fname = path_strip(g.gl_pathv[i], strip_path);
1004 if (lflag & LS_LONG_VIEW) {
1005 if (g.gl_statv[i] == NULL) {
1006 error("no stat information for %s", fname);
1009 lname = ls_file(fname, g.gl_statv[i], 1,
1010 (lflag & LS_SI_UNITS));
1011 mprintf("%s\n", lname);
1014 mprintf("%-*s", colspace, fname);
1024 if (!(lflag & LS_LONG_VIEW) && (c != 1))
1036 do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
1038 struct sftp_statvfs st;
1039 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
1040 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
1041 char s_icapacity[16], s_dcapacity[16];
1043 if (do_statvfs(conn, path, &st, 1) == -1)
1045 if (st.f_files == 0)
1046 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1048 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1049 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1052 if (st.f_blocks == 0)
1053 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1055 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1056 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1060 printf(" Inodes Used Avail "
1061 "(root) %%Capacity\n");
1062 printf("%11llu %11llu %11llu %11llu %s\n",
1063 (unsigned long long)st.f_files,
1064 (unsigned long long)(st.f_files - st.f_ffree),
1065 (unsigned long long)st.f_favail,
1066 (unsigned long long)st.f_ffree, s_icapacity);
1068 strlcpy(s_used, "error", sizeof(s_used));
1069 strlcpy(s_avail, "error", sizeof(s_avail));
1070 strlcpy(s_root, "error", sizeof(s_root));
1071 strlcpy(s_total, "error", sizeof(s_total));
1072 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1073 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1074 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1075 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1076 printf(" Size Used Avail (root) %%Capacity\n");
1077 printf("%7sB %7sB %7sB %7sB %s\n",
1078 s_total, s_used, s_avail, s_root, s_dcapacity);
1080 printf(" Size Used Avail "
1081 "(root) %%Capacity\n");
1082 printf("%12llu %12llu %12llu %12llu %s\n",
1083 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1084 (unsigned long long)(st.f_frsize *
1085 (st.f_blocks - st.f_bfree) / 1024),
1086 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1087 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1094 * Undo escaping of glob sequences in place. Used to undo extra escaping
1095 * applied in makeargv() when the string is destined for a function that
1099 undo_glob_escape(char *s)
1134 * Split a string into an argument vector using sh(1)-style quoting,
1135 * comment and escaping rules, but with some tweaks to handle glob(3)
1137 * The "sloppy" flag allows for recovery from missing terminating quote, for
1138 * use in parsing incomplete commandlines during tab autocompletion.
1140 * Returns NULL on error or a NULL-terminated array of arguments.
1142 * If "lastquote" is not NULL, the quoting character used for the last
1143 * argument is placed in *lastquote ("\0", "'" or "\"").
1145 * If "terminated" is not NULL, *terminated will be set to 1 when the
1146 * last argument's quote has been properly terminated or 0 otherwise.
1147 * This parameter is only of use if "sloppy" is set.
1150 #define MAXARGLEN 8192
1152 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1157 static char argvs[MAXARGLEN];
1158 static char *argv[MAXARGS + 1];
1159 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1162 if (strlen(arg) > sizeof(argvs) - 1) {
1164 error("string too long");
1167 if (terminated != NULL)
1169 if (lastquote != NULL)
1174 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1175 error("Too many arguments.");
1178 if (isspace((unsigned char)arg[i])) {
1179 if (state == MA_UNQUOTED) {
1180 /* Terminate current argument */
1184 } else if (state != MA_START)
1185 argvs[j++] = arg[i];
1186 } else if (arg[i] == '"' || arg[i] == '\'') {
1187 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1188 if (state == MA_START) {
1189 argv[argc] = argvs + j;
1191 if (lastquote != NULL)
1192 *lastquote = arg[i];
1193 } else if (state == MA_UNQUOTED)
1195 else if (state == q)
1196 state = MA_UNQUOTED;
1198 argvs[j++] = arg[i];
1199 } else if (arg[i] == '\\') {
1200 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1201 quot = state == MA_SQUOTE ? '\'' : '"';
1202 /* Unescape quote we are in */
1203 /* XXX support \n and friends? */
1204 if (arg[i + 1] == quot) {
1206 argvs[j++] = arg[i];
1207 } else if (arg[i + 1] == '?' ||
1208 arg[i + 1] == '[' || arg[i + 1] == '*') {
1210 * Special case for sftp: append
1211 * double-escaped glob sequence -
1212 * glob will undo one level of
1213 * escaping. NB. string can grow here.
1215 if (j >= sizeof(argvs) - 5)
1216 goto args_too_longs;
1218 argvs[j++] = arg[i++];
1220 argvs[j++] = arg[i];
1222 argvs[j++] = arg[i++];
1223 argvs[j++] = arg[i];
1226 if (state == MA_START) {
1227 argv[argc] = argvs + j;
1228 state = MA_UNQUOTED;
1229 if (lastquote != NULL)
1232 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1233 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1235 * Special case for sftp: append
1236 * escaped glob sequence -
1237 * glob will undo one level of
1240 argvs[j++] = arg[i++];
1241 argvs[j++] = arg[i];
1243 /* Unescape everything */
1244 /* XXX support \n and friends? */
1246 argvs[j++] = arg[i];
1249 } else if (arg[i] == '#') {
1250 if (state == MA_SQUOTE || state == MA_DQUOTE)
1251 argvs[j++] = arg[i];
1254 } else if (arg[i] == '\0') {
1255 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1257 state = MA_UNQUOTED;
1258 if (terminated != NULL)
1262 error("Unterminated quoted argument");
1266 if (state == MA_UNQUOTED) {
1272 if (state == MA_START) {
1273 argv[argc] = argvs + j;
1274 state = MA_UNQUOTED;
1275 if (lastquote != NULL)
1278 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1279 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1281 * Special case for sftp: escape quoted
1282 * glob(3) wildcards. NB. string can grow
1285 if (j >= sizeof(argvs) - 3)
1286 goto args_too_longs;
1288 argvs[j++] = arg[i];
1290 argvs[j++] = arg[i];
1299 parse_args(const char **cpp, int *ignore_errors, int *aflag,
1300 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1301 int *rflag, int *sflag,
1302 unsigned long *n_arg, char **path1, char **path2)
1304 const char *cmd, *cp = *cpp;
1308 int path1_mandatory = 0, i, cmdnum, optidx, argc;
1310 /* Skip leading whitespace */
1311 cp = cp + strspn(cp, WHITESPACE);
1313 /* Check for leading '-' (disable error processing) */
1318 cp = cp + strspn(cp, WHITESPACE);
1321 /* Ignore blank lines and lines which begin with comment '#' char */
1322 if (*cp == '\0' || *cp == '#')
1325 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1328 /* Figure out which command we have */
1329 for (i = 0; cmds[i].c != NULL; i++) {
1330 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1340 } else if (cmdnum == -1) {
1341 error("Invalid command.");
1345 /* Get arguments and parse flags */
1346 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1347 *rflag = *sflag = 0;
1348 *path1 = *path2 = NULL;
1355 if ((optidx = parse_getput_flags(cmd, argv, argc,
1356 aflag, fflag, pflag, rflag)) == -1)
1358 /* Get first pathname (mandatory) */
1359 if (argc - optidx < 1) {
1360 error("You must specify at least one path after a "
1361 "%s command.", cmd);
1364 *path1 = xstrdup(argv[optidx]);
1365 /* Get second pathname (optional) */
1366 if (argc - optidx > 1) {
1367 *path2 = xstrdup(argv[optidx + 1]);
1368 /* Destination is not globbed */
1369 undo_glob_escape(*path2);
1373 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1375 goto parse_two_paths;
1377 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1379 goto parse_two_paths;
1381 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1384 if (argc - optidx < 2) {
1385 error("You must specify two paths after a %s "
1389 *path1 = xstrdup(argv[optidx]);
1390 *path2 = xstrdup(argv[optidx + 1]);
1391 /* Paths are not globbed */
1392 undo_glob_escape(*path1);
1393 undo_glob_escape(*path2);
1399 path1_mandatory = 1;
1403 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1405 /* Get pathname (mandatory) */
1406 if (argc - optidx < 1) {
1407 if (!path1_mandatory)
1408 break; /* return a NULL path1 */
1409 error("You must specify a path after a %s command.",
1413 *path1 = xstrdup(argv[optidx]);
1414 /* Only "rm" globs */
1416 undo_glob_escape(*path1);
1419 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1422 /* Default to current directory if no path specified */
1423 if (argc - optidx < 1)
1426 *path1 = xstrdup(argv[optidx]);
1427 undo_glob_escape(*path1);
1431 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1433 /* Path is optional */
1434 if (argc - optidx > 0)
1435 *path1 = xstrdup(argv[optidx]);
1438 /* Skip ls command and following whitespace */
1439 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1441 /* Uses the rest of the line */
1448 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1450 /* Get numeric arg (mandatory) */
1451 if (argc - optidx < 1)
1454 l = strtol(argv[optidx], &cp2, base);
1455 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1456 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1459 error("You must supply a numeric argument "
1460 "to the %s command.", cmd);
1464 if (cmdnum == I_LUMASK)
1466 /* Get pathname (mandatory) */
1467 if (argc - optidx < 2) {
1468 error("You must specify a path after a %s command.",
1472 *path1 = xstrdup(argv[optidx + 1]);
1480 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1484 fatal("Command not implemented");
1492 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1493 const char *startdir, int err_abort)
1495 char *path1, *path2, *tmp;
1496 int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0,
1498 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1500 unsigned long n_arg = 0;
1502 char path_buf[PATH_MAX];
1506 path1 = path2 = NULL;
1507 cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1508 &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1509 if (ignore_errors != 0)
1512 memset(&g, 0, sizeof(g));
1514 /* Perform command */
1520 /* Unrecognized command */
1527 err = process_get(conn, path1, path2, *pwd, pflag,
1528 rflag, aflag, fflag);
1534 err = process_put(conn, path1, path2, *pwd, pflag,
1535 rflag, aflag, fflag);
1538 path1 = make_absolute(path1, *pwd);
1539 path2 = make_absolute(path2, *pwd);
1540 err = do_rename(conn, path1, path2, lflag);
1546 path1 = make_absolute(path1, *pwd);
1547 path2 = make_absolute(path2, *pwd);
1548 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1551 path1 = make_absolute(path1, *pwd);
1552 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1553 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1555 mprintf("Removing %s\n", g.gl_pathv[i]);
1556 err = do_rm(conn, g.gl_pathv[i]);
1557 if (err != 0 && err_abort)
1562 path1 = make_absolute(path1, *pwd);
1564 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1566 err = do_mkdir(conn, path1, &a, 1);
1569 path1 = make_absolute(path1, *pwd);
1570 err = do_rmdir(conn, path1);
1573 if (path1 == NULL || *path1 == '\0')
1574 path1 = xstrdup(startdir);
1575 path1 = make_absolute(path1, *pwd);
1576 if ((tmp = do_realpath(conn, path1)) == NULL) {
1580 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1585 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1586 error("Can't change directory: Can't check target");
1591 if (!S_ISDIR(aa->perm)) {
1592 error("Can't change directory: \"%s\" is not "
1593 "a directory", tmp);
1603 do_ls_dir(conn, *pwd, *pwd, lflag);
1607 /* Strip pwd off beginning of non-absolute paths */
1612 path1 = make_absolute(path1, *pwd);
1613 err = do_globbed_ls(conn, path1, tmp, lflag);
1616 /* Default to current directory if no path specified */
1618 path1 = xstrdup(*pwd);
1619 path1 = make_absolute(path1, *pwd);
1620 err = do_df(conn, path1, hflag, iflag);
1623 if (path1 == NULL || *path1 == '\0')
1624 path1 = xstrdup("~");
1625 tmp = tilde_expand_filename(path1, getuid());
1628 if (chdir(path1) == -1) {
1629 error("Couldn't change local directory to "
1630 "\"%s\": %s", path1, strerror(errno));
1635 if (mkdir(path1, 0777) == -1) {
1636 error("Couldn't create local directory "
1637 "\"%s\": %s", path1, strerror(errno));
1645 local_do_shell(cmd);
1649 printf("Local umask: %03lo\n", n_arg);
1652 path1 = make_absolute(path1, *pwd);
1654 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1656 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1657 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1659 mprintf("Changing mode on %s\n",
1661 err = do_setstat(conn, g.gl_pathv[i], &a);
1662 if (err != 0 && err_abort)
1668 path1 = make_absolute(path1, *pwd);
1669 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1670 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1671 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1678 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1679 error("Can't get current ownership of "
1680 "remote file \"%s\"", g.gl_pathv[i]);
1687 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1688 if (cmdnum == I_CHOWN) {
1690 mprintf("Changing owner on %s\n",
1695 mprintf("Changing group on %s\n",
1699 err = do_setstat(conn, g.gl_pathv[i], aa);
1700 if (err != 0 && err_abort)
1705 mprintf("Remote working directory: %s\n", *pwd);
1708 if (!getcwd(path_buf, sizeof(path_buf))) {
1709 error("Couldn't get local cwd: %s", strerror(errno));
1713 mprintf("Local working directory: %s\n", path_buf);
1716 /* Processed below */
1722 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1725 showprogress = !showprogress;
1727 printf("Progress meter enabled\n");
1729 printf("Progress meter disabled\n");
1732 fatal("%d is not implemented", cmdnum);
1740 /* If an unignored error occurs in batch mode we should abort. */
1741 if (err_abort && err != 0)
1743 else if (cmdnum == I_QUIT)
1751 prompt(EditLine *el)
1756 /* Display entries in 'list' after skipping the first 'len' chars */
1758 complete_display(char **list, u_int len)
1760 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1764 /* Count entries for sort and find longest */
1765 for (y = 0; list[y]; y++)
1766 m = MAXIMUM(m, strlen(list[y]));
1768 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1771 m = m > len ? m - len : 0;
1772 columns = width / (m + 2);
1773 columns = MAXIMUM(columns, 1);
1774 colspace = width / columns;
1775 colspace = MINIMUM(colspace, width);
1779 for (y = 0; list[y]; y++) {
1780 llen = strlen(list[y]);
1781 tmp = llen > len ? list[y] + len : "";
1782 mprintf("%-*s", colspace, tmp);
1793 * Given a "list" of words that begin with a common prefix of "word",
1794 * attempt to find an autocompletion to extends "word" by the next
1795 * characters common to all entries in "list".
1798 complete_ambiguous(const char *word, char **list, size_t count)
1804 u_int y, matchlen = strlen(list[0]);
1806 /* Find length of common stem */
1807 for (y = 1; list[y]; y++) {
1810 for (x = 0; x < matchlen; x++)
1811 if (list[0][x] != list[y][x])
1817 if (matchlen > strlen(word)) {
1818 char *tmp = xstrdup(list[0]);
1820 tmp[matchlen] = '\0';
1825 return xstrdup(word);
1828 /* Autocomplete a sftp command */
1830 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1833 u_int y, count = 0, cmdlen, tmplen;
1834 char *tmp, **list, argterm[3];
1837 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1839 /* No command specified: display all available commands */
1841 for (y = 0; cmds[y].c; y++)
1842 list[count++] = xstrdup(cmds[y].c);
1845 complete_display(list, 0);
1847 for (y = 0; list[y] != NULL; y++)
1853 /* Prepare subset of commands that start with "cmd" */
1854 cmdlen = strlen(cmd);
1855 for (y = 0; cmds[y].c; y++) {
1856 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1857 list[count++] = xstrdup(cmds[y].c);
1866 /* Complete ambiguous command */
1867 tmp = complete_ambiguous(cmd, list, count);
1869 complete_display(list, 0);
1871 for (y = 0; list[y]; y++)
1876 tmplen = strlen(tmp);
1877 cmdlen = strlen(cmd);
1878 /* If cmd may be extended then do so */
1879 if (tmplen > cmdlen)
1880 if (el_insertstr(el, tmp + cmdlen) == -1)
1881 fatal("el_insertstr failed.");
1883 /* Terminate argument cleanly */
1887 argterm[y++] = quote;
1888 if (lastarg || *(lf->cursor) != ' ')
1891 if (y > 0 && el_insertstr(el, argterm) == -1)
1892 fatal("el_insertstr failed.");
1901 * Determine whether a particular sftp command's arguments (if any)
1902 * represent local or remote files.
1905 complete_is_remote(char *cmd) {
1911 for (i = 0; cmds[i].c; i++) {
1912 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1919 /* Autocomplete a filename "file" */
1921 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1922 char *file, int remote, int lastarg, char quote, int terminated)
1925 char *tmp, *tmp2, ins[8];
1926 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1930 /* Glob from "file" location */
1934 xasprintf(&tmp, "%s*", file);
1936 /* Check if the path is absolute. */
1937 isabs = tmp[0] == '/';
1939 memset(&g, 0, sizeof(g));
1940 if (remote != LOCAL) {
1941 tmp = make_absolute(tmp, remote_path);
1942 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1944 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1946 /* Determine length of pwd so we can trim completion display */
1947 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1948 /* Terminate counting on first unescaped glob metacharacter */
1949 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1950 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1954 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1956 if (tmp[tmplen] == '/')
1957 pwdlen = tmplen + 1; /* track last seen '/' */
1962 if (g.gl_matchc == 0)
1965 if (g.gl_matchc > 1)
1966 complete_display(g.gl_pathv, pwdlen);
1968 /* Don't try to extend globs */
1969 if (file == NULL || hadglob)
1972 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1973 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1979 tmplen = strlen(tmp);
1980 filelen = strlen(file);
1982 /* Count the number of escaped characters in the input string. */
1984 for (i = 0; i < filelen; i++) {
1985 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1992 if (tmplen > (filelen - cesc)) {
1993 tmp2 = tmp + filelen - cesc;
1995 /* quote argument on way out */
1996 for (i = 0; i < len; i += clen) {
1997 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1998 (size_t)clen > sizeof(ins) - 2)
1999 fatal("invalid multibyte character");
2001 memcpy(ins + 1, tmp2 + i, clen);
2002 ins[clen + 1] = '\0';
2012 if (quote == '\0' || tmp2[i] == quote) {
2013 if (el_insertstr(el, ins) == -1)
2014 fatal("el_insertstr "
2020 if (el_insertstr(el, ins + 1) == -1)
2021 fatal("el_insertstr failed.");
2028 if (g.gl_matchc == 1) {
2030 if (!terminated && quote != '\0')
2032 if (*(lf->cursor - 1) != '/' &&
2033 (lastarg || *(lf->cursor) != ' '))
2036 if (i > 0 && el_insertstr(el, ins) == -1)
2037 fatal("el_insertstr failed.");
2046 /* tab-completion hook function, called via libedit */
2047 static unsigned char
2048 complete(EditLine *el, int ch)
2050 char **argv, *line, quote;
2052 u_int cursor, len, terminated, ret = CC_ERROR;
2054 struct complete_ctx *complete_ctx;
2057 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2058 fatal("%s: el_get failed", __func__);
2060 /* Figure out which argument the cursor points to */
2061 cursor = lf->cursor - lf->buffer;
2062 line = xmalloc(cursor + 1);
2063 memcpy(line, lf->buffer, cursor);
2064 line[cursor] = '\0';
2065 argv = makeargv(line, &carg, 1, "e, &terminated);
2068 /* Get all the arguments on the line */
2069 len = lf->lastchar - lf->buffer;
2070 line = xmalloc(len + 1);
2071 memcpy(line, lf->buffer, len);
2073 argv = makeargv(line, &argc, 1, NULL, NULL);
2075 /* Ensure cursor is at EOL or a argument boundary */
2076 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2077 line[cursor] != '\n') {
2083 /* Show all available commands */
2084 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2086 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2087 /* Handle the command parsing */
2088 if (complete_cmd_parse(el, argv[0], argc == carg,
2089 quote, terminated) != 0)
2091 } else if (carg >= 1) {
2092 /* Handle file parsing */
2093 int remote = complete_is_remote(argv[0]);
2094 char *filematch = NULL;
2096 if (carg > 1 && line[cursor-1] != ' ')
2097 filematch = argv[carg - 1];
2100 complete_match(el, complete_ctx->conn,
2101 *complete_ctx->remote_pathp, filematch,
2102 remote, carg == argc, quote, terminated) != 0)
2109 #endif /* USE_LIBEDIT */
2112 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2115 char *dir = NULL, *startdir = NULL;
2117 int err, interactive;
2118 EditLine *el = NULL;
2122 extern char *__progname;
2123 struct complete_ctx complete_ctx;
2125 if (!batchmode && isatty(STDIN_FILENO)) {
2126 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2127 fatal("Couldn't initialise editline");
2128 if ((hl = history_init()) == NULL)
2129 fatal("Couldn't initialise editline history");
2130 history(hl, &hev, H_SETSIZE, 100);
2131 el_set(el, EL_HIST, history, hl);
2133 el_set(el, EL_PROMPT, prompt);
2134 el_set(el, EL_EDITOR, "emacs");
2135 el_set(el, EL_TERMINAL, NULL);
2136 el_set(el, EL_SIGNAL, 1);
2137 el_source(el, NULL);
2139 /* Tab Completion */
2140 el_set(el, EL_ADDFN, "ftp-complete",
2141 "Context sensitive argument completion", complete);
2142 complete_ctx.conn = conn;
2143 complete_ctx.remote_pathp = &remote_path;
2144 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2145 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2146 /* enable ctrl-left-arrow and ctrl-right-arrow */
2147 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2148 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2149 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2150 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2151 /* make ^w match ksh behaviour */
2152 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2154 #endif /* USE_LIBEDIT */
2156 remote_path = do_realpath(conn, ".");
2157 if (remote_path == NULL)
2159 startdir = xstrdup(remote_path);
2161 if (file1 != NULL) {
2162 dir = xstrdup(file1);
2163 dir = make_absolute(dir, remote_path);
2165 if (remote_is_dir(conn, dir) && file2 == NULL) {
2167 mprintf("Changing to: %s\n", dir);
2168 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2169 if (parse_dispatch_command(conn, cmd,
2170 &remote_path, startdir, 1) != 0) {
2178 /* XXX this is wrong wrt quoting */
2179 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2180 global_aflag ? " -a" : "", dir,
2181 file2 == NULL ? "" : " ",
2182 file2 == NULL ? "" : file2);
2183 err = parse_dispatch_command(conn, cmd,
2184 &remote_path, startdir, 1);
2194 setvbuf(stdout, NULL, _IOLBF, 0);
2195 setvbuf(infile, NULL, _IOLBF, 0);
2197 interactive = !batchmode && isatty(STDIN_FILENO);
2202 signal(SIGINT, SIG_IGN);
2207 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2212 if (!interactive) { /* Echo command */
2213 mprintf("sftp> %s", cmd);
2214 if (strlen(cmd) > 0 &&
2215 cmd[strlen(cmd) - 1] != '\n')
2223 if ((line = el_gets(el, &count)) == NULL ||
2228 history(hl, &hev, H_ENTER, line);
2229 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2230 fprintf(stderr, "Error: input line too long\n");
2233 #endif /* USE_LIBEDIT */
2236 cp = strrchr(cmd, '\n');
2240 /* Handle user interrupts gracefully during commands */
2242 signal(SIGINT, cmd_interrupt);
2244 err = parse_dispatch_command(conn, cmd, &remote_path,
2245 startdir, batchmode);
2249 signal(SIGCHLD, SIG_DFL);
2257 #endif /* USE_LIBEDIT */
2259 /* err == 1 signifies normal "quit" exit */
2260 return (err >= 0 ? 0 : -1);
2264 connect_to_server(char *path, char **args, int *in, int *out)
2269 int pin[2], pout[2];
2271 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2272 fatal("pipe: %s", strerror(errno));
2277 #else /* USE_PIPES */
2280 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2281 fatal("socketpair: %s", strerror(errno));
2282 *in = *out = inout[0];
2283 c_in = c_out = inout[1];
2284 #endif /* USE_PIPES */
2286 if ((sshpid = fork()) == -1)
2287 fatal("fork: %s", strerror(errno));
2288 else if (sshpid == 0) {
2289 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2290 (dup2(c_out, STDOUT_FILENO) == -1)) {
2291 fprintf(stderr, "dup2: %s\n", strerror(errno));
2300 * The underlying ssh is in the same process group, so we must
2301 * ignore SIGINT if we want to gracefully abort commands,
2302 * otherwise the signal will make it to the ssh process and
2303 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2304 * underlying ssh, it must *not* ignore that signal.
2306 signal(SIGINT, SIG_IGN);
2307 signal(SIGTERM, SIG_DFL);
2309 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2313 signal(SIGTERM, killchild);
2314 signal(SIGINT, killchild);
2315 signal(SIGHUP, killchild);
2316 signal(SIGTSTP, suspchild);
2317 signal(SIGTTIN, suspchild);
2318 signal(SIGTTOU, suspchild);
2319 signal(SIGCHLD, sigchld_handler);
2327 extern char *__progname;
2330 "usage: %s [-46aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2331 " [-D sftp_server_path] [-F ssh_config] "
2332 "[-i identity_file] [-l limit]\n"
2333 " [-o ssh_option] [-P port] [-R num_requests] "
2335 " [-s subsystem | sftp_server] destination\n",
2341 main(int argc, char **argv)
2343 int in, out, ch, err, tmp, port = -1;
2344 char *host = NULL, *user, *cp, *file2 = NULL;
2345 int debug_level = 0, sshver = 2;
2346 char *file1 = NULL, *sftp_server = NULL;
2347 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2349 LogLevel ll = SYSLOG_LEVEL_INFO;
2352 extern char *optarg;
2353 struct sftp_conn *conn;
2354 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2355 size_t num_requests = DEFAULT_NUM_REQUESTS;
2356 long long limit_kbps = 0;
2358 ssh_malloc_init(); /* must be called before any mallocs */
2359 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2363 __progname = ssh_get_progname(argv[0]);
2364 memset(&args, '\0', sizeof(args));
2366 addargs(&args, "%s", ssh_program);
2367 addargs(&args, "-oForwardX11 no");
2368 addargs(&args, "-oForwardAgent no");
2369 addargs(&args, "-oPermitLocalCommand no");
2370 addargs(&args, "-oClearAllForwardings yes");
2372 ll = SYSLOG_LEVEL_INFO;
2375 while ((ch = getopt(argc, argv,
2376 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2378 /* Passed through to ssh(1) */
2382 addargs(&args, "-%c", ch);
2384 /* Passed through to ssh(1) with argument */
2389 addargs(&args, "-%c", ch);
2390 addargs(&args, "%s", optarg);
2393 ll = SYSLOG_LEVEL_ERROR;
2396 addargs(&args, "-%c", ch);
2399 port = a2port(optarg);
2401 fatal("Bad port \"%s\"\n", optarg);
2404 if (debug_level < 3) {
2405 addargs(&args, "-v");
2406 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2412 if (sftp_server == NULL)
2413 sftp_server = _PATH_SFTP_SERVER;
2422 copy_buffer_len = strtol(optarg, &cp, 10);
2423 if (copy_buffer_len == 0 || *cp != '\0')
2424 fatal("Invalid buffer size \"%s\"", optarg);
2428 fatal("Batch file already specified.");
2430 /* Allow "-" as stdin */
2431 if (strcmp(optarg, "-") != 0 &&
2432 (infile = fopen(optarg, "r")) == NULL)
2433 fatal("%s (%s).", strerror(errno), optarg);
2435 quiet = batchmode = 1;
2436 addargs(&args, "-obatchmode yes");
2445 sftp_direct = optarg;
2448 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2452 limit_kbps *= 1024; /* kbps */
2458 num_requests = strtol(optarg, &cp, 10);
2459 if (num_requests == 0 || *cp != '\0')
2460 fatal("Invalid number of requests \"%s\"",
2464 sftp_server = optarg;
2467 ssh_program = optarg;
2468 replacearg(&args, 0, "%s", ssh_program);
2476 if (!isatty(STDERR_FILENO))
2479 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2481 if (sftp_direct == NULL) {
2482 if (optind == argc || argc > (optind + 2))
2486 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2495 if (parse_user_host_path(*argv, &user, &host,
2497 /* Treat as a plain hostname. */
2498 host = xstrdup(*argv);
2499 host = cleanhostname(host);
2503 file2 = *(argv + 1);
2506 fprintf(stderr, "Missing hostname\n");
2511 addargs(&args, "-oPort %d", port);
2513 addargs(&args, "-l");
2514 addargs(&args, "%s", user);
2516 addargs(&args, "-oProtocol %d", sshver);
2518 /* no subsystem if the server-spec contains a '/' */
2519 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2520 addargs(&args, "-s");
2522 addargs(&args, "--");
2523 addargs(&args, "%s", host);
2524 addargs(&args, "%s", (sftp_server != NULL ?
2525 sftp_server : "sftp"));
2527 connect_to_server(ssh_program, args.list, &in, &out);
2530 addargs(&args, "sftp-server");
2532 connect_to_server(sftp_direct, args.list, &in, &out);
2536 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2538 fatal("Couldn't initialise connection to server");
2541 if (sftp_direct == NULL)
2542 fprintf(stderr, "Connected to %s.\n", host);
2544 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2547 err = interactive_loop(conn, file1, file2);
2549 #if !defined(USE_PIPES)
2550 shutdown(in, SHUT_RDWR);
2551 shutdown(out, SHUT_RDWR);
2559 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
2561 fatal("Couldn't wait for ssh process: %s",
2564 exit(err == 0 ? 0 : 1);