1 /* $OpenBSD: sftp.c,v 1.186 2018/09/07 04:26:56 dtucker 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 */
1449 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1451 /* Get numeric arg (mandatory) */
1452 if (argc - optidx < 1)
1455 l = strtol(argv[optidx], &cp2, base);
1456 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1457 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1460 error("You must supply a numeric argument "
1461 "to the %s command.", cmd);
1465 if (cmdnum == I_LUMASK)
1467 /* Get pathname (mandatory) */
1468 if (argc - optidx < 2) {
1469 error("You must specify a path after a %s command.",
1473 *path1 = xstrdup(argv[optidx + 1]);
1481 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1485 fatal("Command not implemented");
1493 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1494 const char *startdir, int err_abort)
1496 char *path1, *path2, *tmp;
1497 int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0,
1499 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1501 unsigned long n_arg = 0;
1503 char path_buf[PATH_MAX];
1507 path1 = path2 = NULL;
1508 cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1509 &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1510 if (ignore_errors != 0)
1513 memset(&g, 0, sizeof(g));
1515 /* Perform command */
1521 /* Unrecognized command */
1528 err = process_get(conn, path1, path2, *pwd, pflag,
1529 rflag, aflag, fflag);
1535 err = process_put(conn, path1, path2, *pwd, pflag,
1536 rflag, aflag, fflag);
1539 path1 = make_absolute(path1, *pwd);
1540 path2 = make_absolute(path2, *pwd);
1541 err = do_rename(conn, path1, path2, lflag);
1548 path1 = make_absolute(path1, *pwd);
1549 path2 = make_absolute(path2, *pwd);
1550 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1553 path1 = make_absolute(path1, *pwd);
1554 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1555 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1557 mprintf("Removing %s\n", g.gl_pathv[i]);
1558 err = do_rm(conn, g.gl_pathv[i]);
1559 if (err != 0 && err_abort)
1564 path1 = make_absolute(path1, *pwd);
1566 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1568 err = do_mkdir(conn, path1, &a, 1);
1571 path1 = make_absolute(path1, *pwd);
1572 err = do_rmdir(conn, path1);
1575 if (path1 == NULL || *path1 == '\0')
1576 path1 = xstrdup(startdir);
1577 path1 = make_absolute(path1, *pwd);
1578 if ((tmp = do_realpath(conn, path1)) == NULL) {
1582 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1587 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1588 error("Can't change directory: Can't check target");
1593 if (!S_ISDIR(aa->perm)) {
1594 error("Can't change directory: \"%s\" is not "
1595 "a directory", tmp);
1605 do_ls_dir(conn, *pwd, *pwd, lflag);
1609 /* Strip pwd off beginning of non-absolute paths */
1614 path1 = make_absolute(path1, *pwd);
1615 err = do_globbed_ls(conn, path1, tmp, lflag);
1618 /* Default to current directory if no path specified */
1620 path1 = xstrdup(*pwd);
1621 path1 = make_absolute(path1, *pwd);
1622 err = do_df(conn, path1, hflag, iflag);
1625 if (path1 == NULL || *path1 == '\0')
1626 path1 = xstrdup("~");
1627 tmp = tilde_expand_filename(path1, getuid());
1630 if (chdir(path1) == -1) {
1631 error("Couldn't change local directory to "
1632 "\"%s\": %s", path1, strerror(errno));
1637 if (mkdir(path1, 0777) == -1) {
1638 error("Couldn't create local directory "
1639 "\"%s\": %s", path1, strerror(errno));
1647 local_do_shell(cmd);
1651 printf("Local umask: %03lo\n", n_arg);
1654 path1 = make_absolute(path1, *pwd);
1656 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1658 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1659 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1661 mprintf("Changing mode on %s\n",
1663 err = do_setstat(conn, g.gl_pathv[i], &a);
1664 if (err != 0 && err_abort)
1670 path1 = make_absolute(path1, *pwd);
1671 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1672 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1673 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1680 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1681 error("Can't get current ownership of "
1682 "remote file \"%s\"", g.gl_pathv[i]);
1689 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1690 if (cmdnum == I_CHOWN) {
1692 mprintf("Changing owner on %s\n",
1697 mprintf("Changing group on %s\n",
1701 err = do_setstat(conn, g.gl_pathv[i], aa);
1702 if (err != 0 && err_abort)
1707 mprintf("Remote working directory: %s\n", *pwd);
1710 if (!getcwd(path_buf, sizeof(path_buf))) {
1711 error("Couldn't get local cwd: %s", strerror(errno));
1715 mprintf("Local working directory: %s\n", path_buf);
1718 /* Processed below */
1724 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1727 showprogress = !showprogress;
1729 printf("Progress meter enabled\n");
1731 printf("Progress meter disabled\n");
1734 fatal("%d is not implemented", cmdnum);
1742 /* If an unignored error occurs in batch mode we should abort. */
1743 if (err_abort && err != 0)
1745 else if (cmdnum == I_QUIT)
1753 prompt(EditLine *el)
1758 /* Display entries in 'list' after skipping the first 'len' chars */
1760 complete_display(char **list, u_int len)
1762 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1766 /* Count entries for sort and find longest */
1767 for (y = 0; list[y]; y++)
1768 m = MAXIMUM(m, strlen(list[y]));
1770 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1773 m = m > len ? m - len : 0;
1774 columns = width / (m + 2);
1775 columns = MAXIMUM(columns, 1);
1776 colspace = width / columns;
1777 colspace = MINIMUM(colspace, width);
1781 for (y = 0; list[y]; y++) {
1782 llen = strlen(list[y]);
1783 tmp = llen > len ? list[y] + len : "";
1784 mprintf("%-*s", colspace, tmp);
1795 * Given a "list" of words that begin with a common prefix of "word",
1796 * attempt to find an autocompletion to extends "word" by the next
1797 * characters common to all entries in "list".
1800 complete_ambiguous(const char *word, char **list, size_t count)
1806 u_int y, matchlen = strlen(list[0]);
1808 /* Find length of common stem */
1809 for (y = 1; list[y]; y++) {
1812 for (x = 0; x < matchlen; x++)
1813 if (list[0][x] != list[y][x])
1819 if (matchlen > strlen(word)) {
1820 char *tmp = xstrdup(list[0]);
1822 tmp[matchlen] = '\0';
1827 return xstrdup(word);
1830 /* Autocomplete a sftp command */
1832 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1835 u_int y, count = 0, cmdlen, tmplen;
1836 char *tmp, **list, argterm[3];
1839 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1841 /* No command specified: display all available commands */
1843 for (y = 0; cmds[y].c; y++)
1844 list[count++] = xstrdup(cmds[y].c);
1847 complete_display(list, 0);
1849 for (y = 0; list[y] != NULL; y++)
1855 /* Prepare subset of commands that start with "cmd" */
1856 cmdlen = strlen(cmd);
1857 for (y = 0; cmds[y].c; y++) {
1858 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1859 list[count++] = xstrdup(cmds[y].c);
1868 /* Complete ambiguous command */
1869 tmp = complete_ambiguous(cmd, list, count);
1871 complete_display(list, 0);
1873 for (y = 0; list[y]; y++)
1878 tmplen = strlen(tmp);
1879 cmdlen = strlen(cmd);
1880 /* If cmd may be extended then do so */
1881 if (tmplen > cmdlen)
1882 if (el_insertstr(el, tmp + cmdlen) == -1)
1883 fatal("el_insertstr failed.");
1885 /* Terminate argument cleanly */
1889 argterm[y++] = quote;
1890 if (lastarg || *(lf->cursor) != ' ')
1893 if (y > 0 && el_insertstr(el, argterm) == -1)
1894 fatal("el_insertstr failed.");
1903 * Determine whether a particular sftp command's arguments (if any)
1904 * represent local or remote files.
1907 complete_is_remote(char *cmd) {
1913 for (i = 0; cmds[i].c; i++) {
1914 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1921 /* Autocomplete a filename "file" */
1923 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1924 char *file, int remote, int lastarg, char quote, int terminated)
1927 char *tmp, *tmp2, ins[8];
1928 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1932 /* Glob from "file" location */
1936 xasprintf(&tmp, "%s*", file);
1938 /* Check if the path is absolute. */
1939 isabs = tmp[0] == '/';
1941 memset(&g, 0, sizeof(g));
1942 if (remote != LOCAL) {
1943 tmp = make_absolute(tmp, remote_path);
1944 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1946 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1948 /* Determine length of pwd so we can trim completion display */
1949 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1950 /* Terminate counting on first unescaped glob metacharacter */
1951 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1952 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1956 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1958 if (tmp[tmplen] == '/')
1959 pwdlen = tmplen + 1; /* track last seen '/' */
1964 if (g.gl_matchc == 0)
1967 if (g.gl_matchc > 1)
1968 complete_display(g.gl_pathv, pwdlen);
1970 /* Don't try to extend globs */
1971 if (file == NULL || hadglob)
1974 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1975 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1981 tmplen = strlen(tmp);
1982 filelen = strlen(file);
1984 /* Count the number of escaped characters in the input string. */
1986 for (i = 0; i < filelen; i++) {
1987 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1994 if (tmplen > (filelen - cesc)) {
1995 tmp2 = tmp + filelen - cesc;
1997 /* quote argument on way out */
1998 for (i = 0; i < len; i += clen) {
1999 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
2000 (size_t)clen > sizeof(ins) - 2)
2001 fatal("invalid multibyte character");
2003 memcpy(ins + 1, tmp2 + i, clen);
2004 ins[clen + 1] = '\0';
2014 if (quote == '\0' || tmp2[i] == quote) {
2015 if (el_insertstr(el, ins) == -1)
2016 fatal("el_insertstr "
2022 if (el_insertstr(el, ins + 1) == -1)
2023 fatal("el_insertstr failed.");
2030 if (g.gl_matchc == 1) {
2032 if (!terminated && quote != '\0')
2034 if (*(lf->cursor - 1) != '/' &&
2035 (lastarg || *(lf->cursor) != ' '))
2038 if (i > 0 && el_insertstr(el, ins) == -1)
2039 fatal("el_insertstr failed.");
2048 /* tab-completion hook function, called via libedit */
2049 static unsigned char
2050 complete(EditLine *el, int ch)
2052 char **argv, *line, quote;
2054 u_int cursor, len, terminated, ret = CC_ERROR;
2056 struct complete_ctx *complete_ctx;
2059 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2060 fatal("%s: el_get failed", __func__);
2062 /* Figure out which argument the cursor points to */
2063 cursor = lf->cursor - lf->buffer;
2064 line = xmalloc(cursor + 1);
2065 memcpy(line, lf->buffer, cursor);
2066 line[cursor] = '\0';
2067 argv = makeargv(line, &carg, 1, "e, &terminated);
2070 /* Get all the arguments on the line */
2071 len = lf->lastchar - lf->buffer;
2072 line = xmalloc(len + 1);
2073 memcpy(line, lf->buffer, len);
2075 argv = makeargv(line, &argc, 1, NULL, NULL);
2077 /* Ensure cursor is at EOL or a argument boundary */
2078 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2079 line[cursor] != '\n') {
2085 /* Show all available commands */
2086 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2088 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2089 /* Handle the command parsing */
2090 if (complete_cmd_parse(el, argv[0], argc == carg,
2091 quote, terminated) != 0)
2093 } else if (carg >= 1) {
2094 /* Handle file parsing */
2095 int remote = complete_is_remote(argv[0]);
2096 char *filematch = NULL;
2098 if (carg > 1 && line[cursor-1] != ' ')
2099 filematch = argv[carg - 1];
2102 complete_match(el, complete_ctx->conn,
2103 *complete_ctx->remote_pathp, filematch,
2104 remote, carg == argc, quote, terminated) != 0)
2111 #endif /* USE_LIBEDIT */
2114 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2117 char *dir = NULL, *startdir = NULL;
2119 int err, interactive;
2120 EditLine *el = NULL;
2124 extern char *__progname;
2125 struct complete_ctx complete_ctx;
2127 if (!batchmode && isatty(STDIN_FILENO)) {
2128 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2129 fatal("Couldn't initialise editline");
2130 if ((hl = history_init()) == NULL)
2131 fatal("Couldn't initialise editline history");
2132 history(hl, &hev, H_SETSIZE, 100);
2133 el_set(el, EL_HIST, history, hl);
2135 el_set(el, EL_PROMPT, prompt);
2136 el_set(el, EL_EDITOR, "emacs");
2137 el_set(el, EL_TERMINAL, NULL);
2138 el_set(el, EL_SIGNAL, 1);
2139 el_source(el, NULL);
2141 /* Tab Completion */
2142 el_set(el, EL_ADDFN, "ftp-complete",
2143 "Context sensitive argument completion", complete);
2144 complete_ctx.conn = conn;
2145 complete_ctx.remote_pathp = &remote_path;
2146 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2147 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2148 /* enable ctrl-left-arrow and ctrl-right-arrow */
2149 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2150 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2151 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2152 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2153 /* make ^w match ksh behaviour */
2154 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2156 #endif /* USE_LIBEDIT */
2158 remote_path = do_realpath(conn, ".");
2159 if (remote_path == NULL)
2161 startdir = xstrdup(remote_path);
2163 if (file1 != NULL) {
2164 dir = xstrdup(file1);
2165 dir = make_absolute(dir, remote_path);
2167 if (remote_is_dir(conn, dir) && file2 == NULL) {
2169 mprintf("Changing to: %s\n", dir);
2170 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2171 if (parse_dispatch_command(conn, cmd,
2172 &remote_path, startdir, 1) != 0) {
2180 /* XXX this is wrong wrt quoting */
2181 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2182 global_aflag ? " -a" : "", dir,
2183 file2 == NULL ? "" : " ",
2184 file2 == NULL ? "" : file2);
2185 err = parse_dispatch_command(conn, cmd,
2186 &remote_path, startdir, 1);
2196 setvbuf(stdout, NULL, _IOLBF, 0);
2197 setvbuf(infile, NULL, _IOLBF, 0);
2199 interactive = !batchmode && isatty(STDIN_FILENO);
2204 signal(SIGINT, SIG_IGN);
2209 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2214 if (!interactive) { /* Echo command */
2215 mprintf("sftp> %s", cmd);
2216 if (strlen(cmd) > 0 &&
2217 cmd[strlen(cmd) - 1] != '\n')
2225 if ((line = el_gets(el, &count)) == NULL ||
2230 history(hl, &hev, H_ENTER, line);
2231 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2232 fprintf(stderr, "Error: input line too long\n");
2235 #endif /* USE_LIBEDIT */
2238 cp = strrchr(cmd, '\n');
2242 /* Handle user interrupts gracefully during commands */
2244 signal(SIGINT, cmd_interrupt);
2246 err = parse_dispatch_command(conn, cmd, &remote_path,
2247 startdir, batchmode);
2251 signal(SIGCHLD, SIG_DFL);
2259 #endif /* USE_LIBEDIT */
2261 /* err == 1 signifies normal "quit" exit */
2262 return (err >= 0 ? 0 : -1);
2266 connect_to_server(char *path, char **args, int *in, int *out)
2271 int pin[2], pout[2];
2273 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2274 fatal("pipe: %s", strerror(errno));
2279 #else /* USE_PIPES */
2282 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2283 fatal("socketpair: %s", strerror(errno));
2284 *in = *out = inout[0];
2285 c_in = c_out = inout[1];
2286 #endif /* USE_PIPES */
2288 if ((sshpid = fork()) == -1)
2289 fatal("fork: %s", strerror(errno));
2290 else if (sshpid == 0) {
2291 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2292 (dup2(c_out, STDOUT_FILENO) == -1)) {
2293 fprintf(stderr, "dup2: %s\n", strerror(errno));
2302 * The underlying ssh is in the same process group, so we must
2303 * ignore SIGINT if we want to gracefully abort commands,
2304 * otherwise the signal will make it to the ssh process and
2305 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2306 * underlying ssh, it must *not* ignore that signal.
2308 signal(SIGINT, SIG_IGN);
2309 signal(SIGTERM, SIG_DFL);
2311 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2315 signal(SIGTERM, killchild);
2316 signal(SIGINT, killchild);
2317 signal(SIGHUP, killchild);
2318 signal(SIGTSTP, suspchild);
2319 signal(SIGTTIN, suspchild);
2320 signal(SIGTTOU, suspchild);
2321 signal(SIGCHLD, sigchld_handler);
2329 extern char *__progname;
2332 "usage: %s [-46aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2333 " [-D sftp_server_path] [-F ssh_config] "
2334 "[-i identity_file] [-l limit]\n"
2335 " [-o ssh_option] [-P port] [-R num_requests] "
2337 " [-s subsystem | sftp_server] destination\n",
2343 main(int argc, char **argv)
2345 int in, out, ch, err, tmp, port = -1;
2346 char *host = NULL, *user, *cp, *file2 = NULL;
2347 int debug_level = 0, sshver = 2;
2348 char *file1 = NULL, *sftp_server = NULL;
2349 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2351 LogLevel ll = SYSLOG_LEVEL_INFO;
2354 extern char *optarg;
2355 struct sftp_conn *conn;
2356 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2357 size_t num_requests = DEFAULT_NUM_REQUESTS;
2358 long long limit_kbps = 0;
2360 ssh_malloc_init(); /* must be called before any mallocs */
2361 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2365 __progname = ssh_get_progname(argv[0]);
2366 memset(&args, '\0', sizeof(args));
2368 addargs(&args, "%s", ssh_program);
2369 addargs(&args, "-oForwardX11 no");
2370 addargs(&args, "-oForwardAgent no");
2371 addargs(&args, "-oPermitLocalCommand no");
2372 addargs(&args, "-oClearAllForwardings yes");
2374 ll = SYSLOG_LEVEL_INFO;
2377 while ((ch = getopt(argc, argv,
2378 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2380 /* Passed through to ssh(1) */
2384 addargs(&args, "-%c", ch);
2386 /* Passed through to ssh(1) with argument */
2391 addargs(&args, "-%c", ch);
2392 addargs(&args, "%s", optarg);
2395 ll = SYSLOG_LEVEL_ERROR;
2398 addargs(&args, "-%c", ch);
2401 port = a2port(optarg);
2403 fatal("Bad port \"%s\"\n", optarg);
2406 if (debug_level < 3) {
2407 addargs(&args, "-v");
2408 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2414 if (sftp_server == NULL)
2415 sftp_server = _PATH_SFTP_SERVER;
2424 copy_buffer_len = strtol(optarg, &cp, 10);
2425 if (copy_buffer_len == 0 || *cp != '\0')
2426 fatal("Invalid buffer size \"%s\"", optarg);
2430 fatal("Batch file already specified.");
2432 /* Allow "-" as stdin */
2433 if (strcmp(optarg, "-") != 0 &&
2434 (infile = fopen(optarg, "r")) == NULL)
2435 fatal("%s (%s).", strerror(errno), optarg);
2437 quiet = batchmode = 1;
2438 addargs(&args, "-obatchmode yes");
2447 sftp_direct = optarg;
2450 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2454 limit_kbps *= 1024; /* kbps */
2460 num_requests = strtol(optarg, &cp, 10);
2461 if (num_requests == 0 || *cp != '\0')
2462 fatal("Invalid number of requests \"%s\"",
2466 sftp_server = optarg;
2469 ssh_program = optarg;
2470 replacearg(&args, 0, "%s", ssh_program);
2478 if (!isatty(STDERR_FILENO))
2481 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2483 if (sftp_direct == NULL) {
2484 if (optind == argc || argc > (optind + 2))
2488 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2497 if (parse_user_host_path(*argv, &user, &host,
2499 /* Treat as a plain hostname. */
2500 host = xstrdup(*argv);
2501 host = cleanhostname(host);
2505 file2 = *(argv + 1);
2508 fprintf(stderr, "Missing hostname\n");
2513 addargs(&args, "-oPort %d", port);
2515 addargs(&args, "-l");
2516 addargs(&args, "%s", user);
2518 addargs(&args, "-oProtocol %d", sshver);
2520 /* no subsystem if the server-spec contains a '/' */
2521 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2522 addargs(&args, "-s");
2524 addargs(&args, "--");
2525 addargs(&args, "%s", host);
2526 addargs(&args, "%s", (sftp_server != NULL ?
2527 sftp_server : "sftp"));
2529 connect_to_server(ssh_program, args.list, &in, &out);
2532 addargs(&args, "sftp-server");
2534 connect_to_server(sftp_direct, args.list, &in, &out);
2538 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2540 fatal("Couldn't initialise connection to server");
2543 if (sftp_direct == NULL)
2544 fprintf(stderr, "Connected to %s.\n", host);
2546 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2549 err = interactive_loop(conn, file1, file2);
2551 #if !defined(USE_PIPES)
2552 shutdown(in, SHUT_RDWR);
2553 shutdown(out, SHUT_RDWR);
2561 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
2563 fatal("Couldn't wait for ssh process: %s",
2566 exit(err == 0 ? 0 : 1);