1 /* $OpenBSD: sftp.c,v 1.182 2017/11/03 03:46:52 djm Exp $ */
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
25 #include <sys/param.h>
26 #include <sys/socket.h>
28 #ifdef HAVE_SYS_STATVFS_H
29 #include <sys/statvfs.h>
47 typedef void EditLine;
64 #include "pathnames.h"
71 #include "sftp-common.h"
72 #include "sftp-client.h"
74 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
75 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
77 /* File to read commands from */
80 /* Are we in batchfile mode? */
83 /* PID of ssh transport process */
84 static pid_t sshpid = -1;
86 /* Suppress diagnositic messages */
89 /* This is set to 0 if the progressmeter is not desired. */
92 /* When this option is set, we always recursively download/upload directories */
95 /* When this option is set, we resume download or upload if possible */
98 /* When this option is set, the file transfers will always preserve times */
101 /* When this option is set, transfers will have fsync() called on each file */
102 int global_fflag = 0;
104 /* SIGINT received during command processing */
105 volatile sig_atomic_t interrupted = 0;
107 /* I wish qsort() took a separate ctx for the comparison function...*/
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);
259 printf("Available commands:\n"
261 "cd path Change remote directory to 'path'\n"
262 "chgrp grp path Change group of file 'path' to 'grp'\n"
263 "chmod mode path Change permissions of file 'path' to 'mode'\n"
264 "chown own path Change owner of file 'path' to 'own'\n"
265 "df [-hi] [path] Display statistics for current directory or\n"
266 " filesystem containing 'path'\n"
268 "get [-afPpRr] remote [local] Download file\n"
269 "reget [-fPpRr] remote [local] Resume download file\n"
270 "reput [-fPpRr] [local] remote Resume upload file\n"
271 "help Display this help text\n"
272 "lcd path Change local directory to 'path'\n"
273 "lls [ls-options [path]] Display local directory listing\n"
274 "lmkdir path Create local directory\n"
275 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
276 "lpwd Print local working directory\n"
277 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
278 "lumask umask Set local umask to 'umask'\n"
279 "mkdir path Create remote directory\n"
280 "progress Toggle display of progress meter\n"
281 "put [-afPpRr] local [remote] Upload file\n"
282 "pwd Display remote working directory\n"
284 "rename oldpath newpath Rename remote file\n"
285 "rm path Delete remote file\n"
286 "rmdir path Remove remote directory\n"
287 "symlink oldpath newpath Symlink remote file\n"
288 "version Show SFTP version\n"
289 "!command Execute 'command' in local shell\n"
290 "! Escape to local shell\n"
291 "? Synonym for help\n");
295 local_do_shell(const char *args)
304 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
305 shell = _PATH_BSHELL;
307 if ((pid = fork()) == -1)
308 fatal("Couldn't fork: %s", strerror(errno));
311 /* XXX: child has pipe fds to ssh subproc open - issue? */
313 debug3("Executing %s -c \"%s\"", shell, args);
314 execl(shell, shell, "-c", args, (char *)NULL);
316 debug3("Executing %s", shell);
317 execl(shell, shell, (char *)NULL);
319 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
323 while (waitpid(pid, &status, 0) == -1)
325 fatal("Couldn't wait for child: %s", strerror(errno));
326 if (!WIFEXITED(status))
327 error("Shell exited abnormally");
328 else if (WEXITSTATUS(status))
329 error("Shell exited with status %d", WEXITSTATUS(status));
333 local_do_ls(const char *args)
336 local_do_shell(_PATH_LS);
338 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
339 char *buf = xmalloc(len);
341 /* XXX: quoting - rip quoting code from ftp? */
342 snprintf(buf, len, _PATH_LS " %s", args);
348 /* Strip one path (usually the pwd) from the start of another */
350 path_strip(const char *path, const char *strip)
355 return (xstrdup(path));
358 if (strncmp(path, strip, len) == 0) {
359 if (strip[len - 1] != '/' && path[len] == '/')
361 return (xstrdup(path + len));
364 return (xstrdup(path));
368 make_absolute(char *p, const char *pwd)
373 if (p && p[0] != '/') {
374 abs_str = path_append(pwd, p);
382 parse_getput_flags(const char *cmd, char **argv, int argc,
383 int *aflag, int *fflag, int *pflag, int *rflag)
385 extern int opterr, optind, optopt, optreset;
388 optind = optreset = 1;
391 *aflag = *fflag = *rflag = *pflag = 0;
392 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
409 error("%s: Invalid flag -%c", cmd, optopt);
418 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
420 extern int opterr, optind, optopt, optreset;
423 optind = optreset = 1;
427 while ((ch = getopt(argc, argv, "s")) != -1) {
433 error("%s: Invalid flag -%c", cmd, optopt);
442 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
444 extern int opterr, optind, optopt, optreset;
447 optind = optreset = 1;
451 while ((ch = getopt(argc, argv, "l")) != -1) {
457 error("%s: Invalid flag -%c", cmd, optopt);
466 parse_ls_flags(char **argv, int argc, int *lflag)
468 extern int opterr, optind, optopt, optreset;
471 optind = optreset = 1;
474 *lflag = LS_NAME_SORT;
475 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
478 *lflag &= ~VIEW_FLAGS;
479 *lflag |= LS_SHORT_VIEW;
482 *lflag &= ~SORT_FLAGS;
483 *lflag |= LS_SIZE_SORT;
486 *lflag |= LS_SHOW_ALL;
489 *lflag &= ~SORT_FLAGS;
492 *lflag |= LS_SI_UNITS;
495 *lflag &= ~LS_SHORT_VIEW;
496 *lflag |= LS_LONG_VIEW;
499 *lflag &= ~LS_SHORT_VIEW;
500 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
503 *lflag |= LS_REVERSE_SORT;
506 *lflag &= ~SORT_FLAGS;
507 *lflag |= LS_TIME_SORT;
510 error("ls: Invalid flag -%c", optopt);
519 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
521 extern int opterr, optind, optopt, optreset;
524 optind = optreset = 1;
528 while ((ch = getopt(argc, argv, "hi")) != -1) {
537 error("%s: Invalid flag -%c", cmd, optopt);
546 parse_no_flags(const char *cmd, char **argv, int argc)
548 extern int opterr, optind, optopt, optreset;
551 optind = optreset = 1;
554 while ((ch = getopt(argc, argv, "")) != -1) {
557 error("%s: Invalid flag -%c", cmd, optopt);
566 is_dir(const char *path)
570 /* XXX: report errors? */
571 if (stat(path, &sb) == -1)
574 return(S_ISDIR(sb.st_mode));
578 remote_is_dir(struct sftp_conn *conn, const char *path)
582 /* XXX: report errors? */
583 if ((a = do_stat(conn, path, 1)) == NULL)
585 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
587 return(S_ISDIR(a->perm));
590 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
592 pathname_is_dir(const char *pathname)
594 size_t l = strlen(pathname);
596 return l > 0 && pathname[l - 1] == '/';
600 process_get(struct sftp_conn *conn, const char *src, const char *dst,
601 const char *pwd, int pflag, int rflag, int resume, int fflag)
603 char *abs_src = NULL;
604 char *abs_dst = NULL;
606 char *filename, *tmp=NULL;
609 abs_src = xstrdup(src);
610 abs_src = make_absolute(abs_src, pwd);
611 memset(&g, 0, sizeof(g));
613 debug3("Looking up %s", abs_src);
614 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
615 if (r == GLOB_NOSPACE) {
616 error("Too many matches for \"%s\".", abs_src);
618 error("File \"%s\" not found.", abs_src);
625 * If multiple matches then dst must be a directory or
628 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
629 error("Multiple source paths, but destination "
630 "\"%s\" is not a directory", dst);
635 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
636 tmp = xstrdup(g.gl_pathv[i]);
637 if ((filename = basename(tmp)) == NULL) {
638 error("basename %s: %s", tmp, strerror(errno));
644 if (g.gl_matchc == 1 && dst) {
646 abs_dst = path_append(dst, filename);
648 abs_dst = xstrdup(dst);
651 abs_dst = path_append(dst, filename);
653 abs_dst = xstrdup(filename);
657 resume |= global_aflag;
658 if (!quiet && resume)
659 mprintf("Resuming %s to %s\n",
660 g.gl_pathv[i], abs_dst);
661 else if (!quiet && !resume)
662 mprintf("Fetching %s to %s\n",
663 g.gl_pathv[i], abs_dst);
664 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
665 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
666 pflag || global_pflag, 1, resume,
667 fflag || global_fflag) == -1)
670 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
671 pflag || global_pflag, resume,
672 fflag || global_fflag) == -1)
686 process_put(struct sftp_conn *conn, const char *src, const char *dst,
687 const char *pwd, int pflag, int rflag, int resume, int fflag)
689 char *tmp_dst = NULL;
690 char *abs_dst = NULL;
691 char *tmp = NULL, *filename = NULL;
694 int i, dst_is_dir = 1;
698 tmp_dst = xstrdup(dst);
699 tmp_dst = make_absolute(tmp_dst, pwd);
702 memset(&g, 0, sizeof(g));
703 debug3("Looking up %s", src);
704 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
705 error("File \"%s\" not found.", src);
710 /* If we aren't fetching to pwd then stash this status for later */
712 dst_is_dir = remote_is_dir(conn, tmp_dst);
714 /* If multiple matches, dst may be directory or unspecified */
715 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
716 error("Multiple paths match, but destination "
717 "\"%s\" is not a directory", tmp_dst);
722 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
723 if (stat(g.gl_pathv[i], &sb) == -1) {
725 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
729 tmp = xstrdup(g.gl_pathv[i]);
730 if ((filename = basename(tmp)) == NULL) {
731 error("basename %s: %s", tmp, strerror(errno));
737 if (g.gl_matchc == 1 && tmp_dst) {
738 /* If directory specified, append filename */
740 abs_dst = path_append(tmp_dst, filename);
742 abs_dst = xstrdup(tmp_dst);
743 } else if (tmp_dst) {
744 abs_dst = path_append(tmp_dst, filename);
746 abs_dst = make_absolute(xstrdup(filename), pwd);
750 resume |= global_aflag;
751 if (!quiet && resume)
752 mprintf("Resuming upload of %s to %s\n",
753 g.gl_pathv[i], abs_dst);
754 else if (!quiet && !resume)
755 mprintf("Uploading %s to %s\n",
756 g.gl_pathv[i], abs_dst);
757 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
758 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
759 pflag || global_pflag, 1, resume,
760 fflag || global_fflag) == -1)
763 if (do_upload(conn, g.gl_pathv[i], abs_dst,
764 pflag || global_pflag, resume,
765 fflag || global_fflag) == -1)
778 sdirent_comp(const void *aa, const void *bb)
780 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
781 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
782 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
784 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
785 if (sort_flag & LS_NAME_SORT)
786 return (rmul * strcmp(a->filename, b->filename));
787 else if (sort_flag & LS_TIME_SORT)
788 return (rmul * NCMP(a->a.mtime, b->a.mtime));
789 else if (sort_flag & LS_SIZE_SORT)
790 return (rmul * NCMP(a->a.size, b->a.size));
792 fatal("Unknown ls sort type");
795 /* sftp ls.1 replacement for directories */
797 do_ls_dir(struct sftp_conn *conn, const char *path,
798 const char *strip_path, int lflag)
801 u_int c = 1, colspace = 0, columns = 1;
804 if ((n = do_readdir(conn, path, &d)) != 0)
807 if (!(lflag & LS_SHORT_VIEW)) {
808 u_int m = 0, width = 80;
812 /* Count entries for sort and find longest filename */
813 for (n = 0; d[n] != NULL; n++) {
814 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
815 m = MAXIMUM(m, strlen(d[n]->filename));
818 /* Add any subpath that also needs to be counted */
819 tmp = path_strip(path, strip_path);
823 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
826 columns = width / (m + 2);
827 columns = MAXIMUM(columns, 1);
828 colspace = width / columns;
829 colspace = MINIMUM(colspace, width);
832 if (lflag & SORT_FLAGS) {
833 for (n = 0; d[n] != NULL; n++)
834 ; /* count entries */
835 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
836 qsort(d, n, sizeof(*d), sdirent_comp);
839 for (n = 0; d[n] != NULL && !interrupted; n++) {
842 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
845 tmp = path_append(path, d[n]->filename);
846 fname = path_strip(tmp, strip_path);
849 if (lflag & LS_LONG_VIEW) {
850 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
854 memset(&sb, 0, sizeof(sb));
855 attrib_to_stat(&d[n]->a, &sb);
856 lname = ls_file(fname, &sb, 1,
857 (lflag & LS_SI_UNITS));
858 mprintf("%s\n", lname);
861 mprintf("%s\n", d[n]->longname);
863 mprintf("%-*s", colspace, fname);
874 if (!(lflag & LS_LONG_VIEW) && (c != 1))
877 free_sftp_dirents(d);
882 sglob_comp(const void *aa, const void *bb)
884 u_int a = *(const u_int *)aa;
885 u_int b = *(const u_int *)bb;
886 const char *ap = sort_glob->gl_pathv[a];
887 const char *bp = sort_glob->gl_pathv[b];
888 const struct stat *as = sort_glob->gl_statv[a];
889 const struct stat *bs = sort_glob->gl_statv[b];
890 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
892 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
893 if (sort_flag & LS_NAME_SORT)
894 return (rmul * strcmp(ap, bp));
895 else if (sort_flag & LS_TIME_SORT) {
896 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
897 return (rmul * timespeccmp(&as->st_mtim, &bs->st_mtim, <));
898 #elif defined(HAVE_STRUCT_STAT_ST_MTIME)
899 return (rmul * NCMP(as->st_mtime, bs->st_mtime));
903 } else if (sort_flag & LS_SIZE_SORT)
904 return (rmul * NCMP(as->st_size, bs->st_size));
906 fatal("Unknown ls sort type");
909 /* sftp ls.1 replacement which handles path globs */
911 do_globbed_ls(struct sftp_conn *conn, const char *path,
912 const char *strip_path, int lflag)
918 u_int i, j, nentries, *indices = NULL, c = 1;
919 u_int colspace = 0, columns = 1, m = 0, width = 80;
921 memset(&g, 0, sizeof(g));
923 if ((r = remote_glob(conn, path,
924 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
926 (g.gl_pathc && !g.gl_matchc)) {
929 if (r == GLOB_NOSPACE) {
930 error("Can't ls: Too many matches for \"%s\"", path);
932 error("Can't ls: \"%s\" not found", path);
941 * If the glob returns a single match and it is a directory,
942 * then just list its contents.
944 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
945 S_ISDIR(g.gl_statv[0]->st_mode)) {
946 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
951 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
954 if (!(lflag & LS_SHORT_VIEW)) {
955 /* Count entries for sort and find longest filename */
956 for (i = 0; g.gl_pathv[i]; i++)
957 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
959 columns = width / (m + 2);
960 columns = MAXIMUM(columns, 1);
961 colspace = width / columns;
965 * Sorting: rather than mess with the contents of glob_t, prepare
966 * an array of indices into it and sort that. For the usual
967 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
969 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
970 ; /* count entries */
971 indices = calloc(nentries, sizeof(*indices));
972 for (i = 0; i < nentries; i++)
975 if (lflag & SORT_FLAGS) {
977 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
978 qsort(indices, nentries, sizeof(*indices), sglob_comp);
982 for (j = 0; j < nentries && !interrupted; j++) {
984 fname = path_strip(g.gl_pathv[i], strip_path);
985 if (lflag & LS_LONG_VIEW) {
986 if (g.gl_statv[i] == NULL) {
987 error("no stat information for %s", fname);
990 lname = ls_file(fname, g.gl_statv[i], 1,
991 (lflag & LS_SI_UNITS));
992 mprintf("%s\n", lname);
995 mprintf("%-*s", colspace, fname);
1005 if (!(lflag & LS_LONG_VIEW) && (c != 1))
1017 do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
1019 struct sftp_statvfs st;
1020 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
1021 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
1022 char s_icapacity[16], s_dcapacity[16];
1024 if (do_statvfs(conn, path, &st, 1) == -1)
1026 if (st.f_files == 0)
1027 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1029 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1030 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1033 if (st.f_blocks == 0)
1034 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1036 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1037 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1041 printf(" Inodes Used Avail "
1042 "(root) %%Capacity\n");
1043 printf("%11llu %11llu %11llu %11llu %s\n",
1044 (unsigned long long)st.f_files,
1045 (unsigned long long)(st.f_files - st.f_ffree),
1046 (unsigned long long)st.f_favail,
1047 (unsigned long long)st.f_ffree, s_icapacity);
1049 strlcpy(s_used, "error", sizeof(s_used));
1050 strlcpy(s_avail, "error", sizeof(s_avail));
1051 strlcpy(s_root, "error", sizeof(s_root));
1052 strlcpy(s_total, "error", sizeof(s_total));
1053 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1054 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1055 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1056 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1057 printf(" Size Used Avail (root) %%Capacity\n");
1058 printf("%7sB %7sB %7sB %7sB %s\n",
1059 s_total, s_used, s_avail, s_root, s_dcapacity);
1061 printf(" Size Used Avail "
1062 "(root) %%Capacity\n");
1063 printf("%12llu %12llu %12llu %12llu %s\n",
1064 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1065 (unsigned long long)(st.f_frsize *
1066 (st.f_blocks - st.f_bfree) / 1024),
1067 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1068 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1075 * Undo escaping of glob sequences in place. Used to undo extra escaping
1076 * applied in makeargv() when the string is destined for a function that
1080 undo_glob_escape(char *s)
1115 * Split a string into an argument vector using sh(1)-style quoting,
1116 * comment and escaping rules, but with some tweaks to handle glob(3)
1118 * The "sloppy" flag allows for recovery from missing terminating quote, for
1119 * use in parsing incomplete commandlines during tab autocompletion.
1121 * Returns NULL on error or a NULL-terminated array of arguments.
1123 * If "lastquote" is not NULL, the quoting character used for the last
1124 * argument is placed in *lastquote ("\0", "'" or "\"").
1126 * If "terminated" is not NULL, *terminated will be set to 1 when the
1127 * last argument's quote has been properly terminated or 0 otherwise.
1128 * This parameter is only of use if "sloppy" is set.
1131 #define MAXARGLEN 8192
1133 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1138 static char argvs[MAXARGLEN];
1139 static char *argv[MAXARGS + 1];
1140 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1143 if (strlen(arg) > sizeof(argvs) - 1) {
1145 error("string too long");
1148 if (terminated != NULL)
1150 if (lastquote != NULL)
1155 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1156 error("Too many arguments.");
1159 if (isspace((unsigned char)arg[i])) {
1160 if (state == MA_UNQUOTED) {
1161 /* Terminate current argument */
1165 } else if (state != MA_START)
1166 argvs[j++] = arg[i];
1167 } else if (arg[i] == '"' || arg[i] == '\'') {
1168 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1169 if (state == MA_START) {
1170 argv[argc] = argvs + j;
1172 if (lastquote != NULL)
1173 *lastquote = arg[i];
1174 } else if (state == MA_UNQUOTED)
1176 else if (state == q)
1177 state = MA_UNQUOTED;
1179 argvs[j++] = arg[i];
1180 } else if (arg[i] == '\\') {
1181 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1182 quot = state == MA_SQUOTE ? '\'' : '"';
1183 /* Unescape quote we are in */
1184 /* XXX support \n and friends? */
1185 if (arg[i + 1] == quot) {
1187 argvs[j++] = arg[i];
1188 } else if (arg[i + 1] == '?' ||
1189 arg[i + 1] == '[' || arg[i + 1] == '*') {
1191 * Special case for sftp: append
1192 * double-escaped glob sequence -
1193 * glob will undo one level of
1194 * escaping. NB. string can grow here.
1196 if (j >= sizeof(argvs) - 5)
1197 goto args_too_longs;
1199 argvs[j++] = arg[i++];
1201 argvs[j++] = arg[i];
1203 argvs[j++] = arg[i++];
1204 argvs[j++] = arg[i];
1207 if (state == MA_START) {
1208 argv[argc] = argvs + j;
1209 state = MA_UNQUOTED;
1210 if (lastquote != NULL)
1213 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1214 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1216 * Special case for sftp: append
1217 * escaped glob sequence -
1218 * glob will undo one level of
1221 argvs[j++] = arg[i++];
1222 argvs[j++] = arg[i];
1224 /* Unescape everything */
1225 /* XXX support \n and friends? */
1227 argvs[j++] = arg[i];
1230 } else if (arg[i] == '#') {
1231 if (state == MA_SQUOTE || state == MA_DQUOTE)
1232 argvs[j++] = arg[i];
1235 } else if (arg[i] == '\0') {
1236 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1238 state = MA_UNQUOTED;
1239 if (terminated != NULL)
1243 error("Unterminated quoted argument");
1247 if (state == MA_UNQUOTED) {
1253 if (state == MA_START) {
1254 argv[argc] = argvs + j;
1255 state = MA_UNQUOTED;
1256 if (lastquote != NULL)
1259 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1260 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1262 * Special case for sftp: escape quoted
1263 * glob(3) wildcards. NB. string can grow
1266 if (j >= sizeof(argvs) - 3)
1267 goto args_too_longs;
1269 argvs[j++] = arg[i];
1271 argvs[j++] = arg[i];
1280 parse_args(const char **cpp, int *ignore_errors, int *aflag,
1281 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1282 int *rflag, int *sflag,
1283 unsigned long *n_arg, char **path1, char **path2)
1285 const char *cmd, *cp = *cpp;
1289 int path1_mandatory = 0, i, cmdnum, optidx, argc;
1291 /* Skip leading whitespace */
1292 cp = cp + strspn(cp, WHITESPACE);
1294 /* Check for leading '-' (disable error processing) */
1299 cp = cp + strspn(cp, WHITESPACE);
1302 /* Ignore blank lines and lines which begin with comment '#' char */
1303 if (*cp == '\0' || *cp == '#')
1306 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1309 /* Figure out which command we have */
1310 for (i = 0; cmds[i].c != NULL; i++) {
1311 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1321 } else if (cmdnum == -1) {
1322 error("Invalid command.");
1326 /* Get arguments and parse flags */
1327 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1328 *rflag = *sflag = 0;
1329 *path1 = *path2 = NULL;
1336 if ((optidx = parse_getput_flags(cmd, argv, argc,
1337 aflag, fflag, pflag, rflag)) == -1)
1339 /* Get first pathname (mandatory) */
1340 if (argc - optidx < 1) {
1341 error("You must specify at least one path after a "
1342 "%s command.", cmd);
1345 *path1 = xstrdup(argv[optidx]);
1346 /* Get second pathname (optional) */
1347 if (argc - optidx > 1) {
1348 *path2 = xstrdup(argv[optidx + 1]);
1349 /* Destination is not globbed */
1350 undo_glob_escape(*path2);
1354 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1356 goto parse_two_paths;
1358 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1360 goto parse_two_paths;
1362 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1365 if (argc - optidx < 2) {
1366 error("You must specify two paths after a %s "
1370 *path1 = xstrdup(argv[optidx]);
1371 *path2 = xstrdup(argv[optidx + 1]);
1372 /* Paths are not globbed */
1373 undo_glob_escape(*path1);
1374 undo_glob_escape(*path2);
1380 path1_mandatory = 1;
1384 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1386 /* Get pathname (mandatory) */
1387 if (argc - optidx < 1) {
1388 if (!path1_mandatory)
1389 break; /* return a NULL path1 */
1390 error("You must specify a path after a %s command.",
1394 *path1 = xstrdup(argv[optidx]);
1395 /* Only "rm" globs */
1397 undo_glob_escape(*path1);
1400 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1403 /* Default to current directory if no path specified */
1404 if (argc - optidx < 1)
1407 *path1 = xstrdup(argv[optidx]);
1408 undo_glob_escape(*path1);
1412 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1414 /* Path is optional */
1415 if (argc - optidx > 0)
1416 *path1 = xstrdup(argv[optidx]);
1419 /* Skip ls command and following whitespace */
1420 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1422 /* Uses the rest of the line */
1429 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1431 /* Get numeric arg (mandatory) */
1432 if (argc - optidx < 1)
1435 l = strtol(argv[optidx], &cp2, base);
1436 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1437 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1440 error("You must supply a numeric argument "
1441 "to the %s command.", cmd);
1445 if (cmdnum == I_LUMASK)
1447 /* Get pathname (mandatory) */
1448 if (argc - optidx < 2) {
1449 error("You must specify a path after a %s command.",
1453 *path1 = xstrdup(argv[optidx + 1]);
1461 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1465 fatal("Command not implemented");
1473 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1474 const char *startdir, int err_abort)
1476 char *path1, *path2, *tmp;
1477 int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0,
1479 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1481 unsigned long n_arg = 0;
1483 char path_buf[PATH_MAX];
1487 path1 = path2 = NULL;
1488 cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1489 &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1490 if (ignore_errors != 0)
1493 memset(&g, 0, sizeof(g));
1495 /* Perform command */
1501 /* Unrecognized command */
1508 err = process_get(conn, path1, path2, *pwd, pflag,
1509 rflag, aflag, fflag);
1515 err = process_put(conn, path1, path2, *pwd, pflag,
1516 rflag, aflag, fflag);
1519 path1 = make_absolute(path1, *pwd);
1520 path2 = make_absolute(path2, *pwd);
1521 err = do_rename(conn, path1, path2, lflag);
1527 path1 = make_absolute(path1, *pwd);
1528 path2 = make_absolute(path2, *pwd);
1529 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1532 path1 = make_absolute(path1, *pwd);
1533 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1534 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1536 mprintf("Removing %s\n", g.gl_pathv[i]);
1537 err = do_rm(conn, g.gl_pathv[i]);
1538 if (err != 0 && err_abort)
1543 path1 = make_absolute(path1, *pwd);
1545 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1547 err = do_mkdir(conn, path1, &a, 1);
1550 path1 = make_absolute(path1, *pwd);
1551 err = do_rmdir(conn, path1);
1554 if (path1 == NULL || *path1 == '\0')
1555 path1 = xstrdup(startdir);
1556 path1 = make_absolute(path1, *pwd);
1557 if ((tmp = do_realpath(conn, path1)) == NULL) {
1561 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1566 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1567 error("Can't change directory: Can't check target");
1572 if (!S_ISDIR(aa->perm)) {
1573 error("Can't change directory: \"%s\" is not "
1574 "a directory", tmp);
1584 do_ls_dir(conn, *pwd, *pwd, lflag);
1588 /* Strip pwd off beginning of non-absolute paths */
1593 path1 = make_absolute(path1, *pwd);
1594 err = do_globbed_ls(conn, path1, tmp, lflag);
1597 /* Default to current directory if no path specified */
1599 path1 = xstrdup(*pwd);
1600 path1 = make_absolute(path1, *pwd);
1601 err = do_df(conn, path1, hflag, iflag);
1604 if (path1 == NULL || *path1 == '\0')
1605 path1 = xstrdup("~");
1606 tmp = tilde_expand_filename(path1, getuid());
1609 if (chdir(path1) == -1) {
1610 error("Couldn't change local directory to "
1611 "\"%s\": %s", path1, strerror(errno));
1616 if (mkdir(path1, 0777) == -1) {
1617 error("Couldn't create local directory "
1618 "\"%s\": %s", path1, strerror(errno));
1626 local_do_shell(cmd);
1630 printf("Local umask: %03lo\n", n_arg);
1633 path1 = make_absolute(path1, *pwd);
1635 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1637 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1638 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1640 mprintf("Changing mode on %s\n",
1642 err = do_setstat(conn, g.gl_pathv[i], &a);
1643 if (err != 0 && err_abort)
1649 path1 = make_absolute(path1, *pwd);
1650 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1651 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1652 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1659 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1660 error("Can't get current ownership of "
1661 "remote file \"%s\"", g.gl_pathv[i]);
1668 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1669 if (cmdnum == I_CHOWN) {
1671 mprintf("Changing owner on %s\n",
1676 mprintf("Changing group on %s\n",
1680 err = do_setstat(conn, g.gl_pathv[i], aa);
1681 if (err != 0 && err_abort)
1686 mprintf("Remote working directory: %s\n", *pwd);
1689 if (!getcwd(path_buf, sizeof(path_buf))) {
1690 error("Couldn't get local cwd: %s", strerror(errno));
1694 mprintf("Local working directory: %s\n", path_buf);
1697 /* Processed below */
1703 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1706 showprogress = !showprogress;
1708 printf("Progress meter enabled\n");
1710 printf("Progress meter disabled\n");
1713 fatal("%d is not implemented", cmdnum);
1721 /* If an unignored error occurs in batch mode we should abort. */
1722 if (err_abort && err != 0)
1724 else if (cmdnum == I_QUIT)
1732 prompt(EditLine *el)
1737 /* Display entries in 'list' after skipping the first 'len' chars */
1739 complete_display(char **list, u_int len)
1741 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1745 /* Count entries for sort and find longest */
1746 for (y = 0; list[y]; y++)
1747 m = MAXIMUM(m, strlen(list[y]));
1749 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1752 m = m > len ? m - len : 0;
1753 columns = width / (m + 2);
1754 columns = MAXIMUM(columns, 1);
1755 colspace = width / columns;
1756 colspace = MINIMUM(colspace, width);
1760 for (y = 0; list[y]; y++) {
1761 llen = strlen(list[y]);
1762 tmp = llen > len ? list[y] + len : "";
1763 mprintf("%-*s", colspace, tmp);
1774 * Given a "list" of words that begin with a common prefix of "word",
1775 * attempt to find an autocompletion to extends "word" by the next
1776 * characters common to all entries in "list".
1779 complete_ambiguous(const char *word, char **list, size_t count)
1785 u_int y, matchlen = strlen(list[0]);
1787 /* Find length of common stem */
1788 for (y = 1; list[y]; y++) {
1791 for (x = 0; x < matchlen; x++)
1792 if (list[0][x] != list[y][x])
1798 if (matchlen > strlen(word)) {
1799 char *tmp = xstrdup(list[0]);
1801 tmp[matchlen] = '\0';
1806 return xstrdup(word);
1809 /* Autocomplete a sftp command */
1811 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1814 u_int y, count = 0, cmdlen, tmplen;
1815 char *tmp, **list, argterm[3];
1818 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1820 /* No command specified: display all available commands */
1822 for (y = 0; cmds[y].c; y++)
1823 list[count++] = xstrdup(cmds[y].c);
1826 complete_display(list, 0);
1828 for (y = 0; list[y] != NULL; y++)
1834 /* Prepare subset of commands that start with "cmd" */
1835 cmdlen = strlen(cmd);
1836 for (y = 0; cmds[y].c; y++) {
1837 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1838 list[count++] = xstrdup(cmds[y].c);
1847 /* Complete ambigious command */
1848 tmp = complete_ambiguous(cmd, list, count);
1850 complete_display(list, 0);
1852 for (y = 0; list[y]; y++)
1857 tmplen = strlen(tmp);
1858 cmdlen = strlen(cmd);
1859 /* If cmd may be extended then do so */
1860 if (tmplen > cmdlen)
1861 if (el_insertstr(el, tmp + cmdlen) == -1)
1862 fatal("el_insertstr failed.");
1864 /* Terminate argument cleanly */
1868 argterm[y++] = quote;
1869 if (lastarg || *(lf->cursor) != ' ')
1872 if (y > 0 && el_insertstr(el, argterm) == -1)
1873 fatal("el_insertstr failed.");
1882 * Determine whether a particular sftp command's arguments (if any)
1883 * represent local or remote files.
1886 complete_is_remote(char *cmd) {
1892 for (i = 0; cmds[i].c; i++) {
1893 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1900 /* Autocomplete a filename "file" */
1902 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1903 char *file, int remote, int lastarg, char quote, int terminated)
1906 char *tmp, *tmp2, ins[8];
1907 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1911 /* Glob from "file" location */
1915 xasprintf(&tmp, "%s*", file);
1917 /* Check if the path is absolute. */
1918 isabs = tmp[0] == '/';
1920 memset(&g, 0, sizeof(g));
1921 if (remote != LOCAL) {
1922 tmp = make_absolute(tmp, remote_path);
1923 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1925 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1927 /* Determine length of pwd so we can trim completion display */
1928 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1929 /* Terminate counting on first unescaped glob metacharacter */
1930 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1931 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1935 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1937 if (tmp[tmplen] == '/')
1938 pwdlen = tmplen + 1; /* track last seen '/' */
1943 if (g.gl_matchc == 0)
1946 if (g.gl_matchc > 1)
1947 complete_display(g.gl_pathv, pwdlen);
1949 /* Don't try to extend globs */
1950 if (file == NULL || hadglob)
1953 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1954 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1960 tmplen = strlen(tmp);
1961 filelen = strlen(file);
1963 /* Count the number of escaped characters in the input string. */
1965 for (i = 0; i < filelen; i++) {
1966 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1973 if (tmplen > (filelen - cesc)) {
1974 tmp2 = tmp + filelen - cesc;
1976 /* quote argument on way out */
1977 for (i = 0; i < len; i += clen) {
1978 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1979 (size_t)clen > sizeof(ins) - 2)
1980 fatal("invalid multibyte character");
1982 memcpy(ins + 1, tmp2 + i, clen);
1983 ins[clen + 1] = '\0';
1993 if (quote == '\0' || tmp2[i] == quote) {
1994 if (el_insertstr(el, ins) == -1)
1995 fatal("el_insertstr "
2001 if (el_insertstr(el, ins + 1) == -1)
2002 fatal("el_insertstr failed.");
2009 if (g.gl_matchc == 1) {
2011 if (!terminated && quote != '\0')
2013 if (*(lf->cursor - 1) != '/' &&
2014 (lastarg || *(lf->cursor) != ' '))
2017 if (i > 0 && el_insertstr(el, ins) == -1)
2018 fatal("el_insertstr failed.");
2027 /* tab-completion hook function, called via libedit */
2028 static unsigned char
2029 complete(EditLine *el, int ch)
2031 char **argv, *line, quote;
2033 u_int cursor, len, terminated, ret = CC_ERROR;
2035 struct complete_ctx *complete_ctx;
2038 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2039 fatal("%s: el_get failed", __func__);
2041 /* Figure out which argument the cursor points to */
2042 cursor = lf->cursor - lf->buffer;
2043 line = xmalloc(cursor + 1);
2044 memcpy(line, lf->buffer, cursor);
2045 line[cursor] = '\0';
2046 argv = makeargv(line, &carg, 1, "e, &terminated);
2049 /* Get all the arguments on the line */
2050 len = lf->lastchar - lf->buffer;
2051 line = xmalloc(len + 1);
2052 memcpy(line, lf->buffer, len);
2054 argv = makeargv(line, &argc, 1, NULL, NULL);
2056 /* Ensure cursor is at EOL or a argument boundary */
2057 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2058 line[cursor] != '\n') {
2064 /* Show all available commands */
2065 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2067 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2068 /* Handle the command parsing */
2069 if (complete_cmd_parse(el, argv[0], argc == carg,
2070 quote, terminated) != 0)
2072 } else if (carg >= 1) {
2073 /* Handle file parsing */
2074 int remote = complete_is_remote(argv[0]);
2075 char *filematch = NULL;
2077 if (carg > 1 && line[cursor-1] != ' ')
2078 filematch = argv[carg - 1];
2081 complete_match(el, complete_ctx->conn,
2082 *complete_ctx->remote_pathp, filematch,
2083 remote, carg == argc, quote, terminated) != 0)
2090 #endif /* USE_LIBEDIT */
2093 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2096 char *dir = NULL, *startdir = NULL;
2098 int err, interactive;
2099 EditLine *el = NULL;
2103 extern char *__progname;
2104 struct complete_ctx complete_ctx;
2106 if (!batchmode && isatty(STDIN_FILENO)) {
2107 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2108 fatal("Couldn't initialise editline");
2109 if ((hl = history_init()) == NULL)
2110 fatal("Couldn't initialise editline history");
2111 history(hl, &hev, H_SETSIZE, 100);
2112 el_set(el, EL_HIST, history, hl);
2114 el_set(el, EL_PROMPT, prompt);
2115 el_set(el, EL_EDITOR, "emacs");
2116 el_set(el, EL_TERMINAL, NULL);
2117 el_set(el, EL_SIGNAL, 1);
2118 el_source(el, NULL);
2120 /* Tab Completion */
2121 el_set(el, EL_ADDFN, "ftp-complete",
2122 "Context sensitive argument completion", complete);
2123 complete_ctx.conn = conn;
2124 complete_ctx.remote_pathp = &remote_path;
2125 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2126 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2127 /* enable ctrl-left-arrow and ctrl-right-arrow */
2128 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2129 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2130 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2131 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2132 /* make ^w match ksh behaviour */
2133 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2135 #endif /* USE_LIBEDIT */
2137 remote_path = do_realpath(conn, ".");
2138 if (remote_path == NULL)
2140 startdir = xstrdup(remote_path);
2142 if (file1 != NULL) {
2143 dir = xstrdup(file1);
2144 dir = make_absolute(dir, remote_path);
2146 if (remote_is_dir(conn, dir) && file2 == NULL) {
2148 mprintf("Changing to: %s\n", dir);
2149 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2150 if (parse_dispatch_command(conn, cmd,
2151 &remote_path, startdir, 1) != 0) {
2159 /* XXX this is wrong wrt quoting */
2160 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2161 global_aflag ? " -a" : "", dir,
2162 file2 == NULL ? "" : " ",
2163 file2 == NULL ? "" : file2);
2164 err = parse_dispatch_command(conn, cmd,
2165 &remote_path, startdir, 1);
2175 setvbuf(stdout, NULL, _IOLBF, 0);
2176 setvbuf(infile, NULL, _IOLBF, 0);
2178 interactive = !batchmode && isatty(STDIN_FILENO);
2183 signal(SIGINT, SIG_IGN);
2188 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2193 if (!interactive) { /* Echo command */
2194 mprintf("sftp> %s", cmd);
2195 if (strlen(cmd) > 0 &&
2196 cmd[strlen(cmd) - 1] != '\n')
2204 if ((line = el_gets(el, &count)) == NULL ||
2209 history(hl, &hev, H_ENTER, line);
2210 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2211 fprintf(stderr, "Error: input line too long\n");
2214 #endif /* USE_LIBEDIT */
2217 cp = strrchr(cmd, '\n');
2221 /* Handle user interrupts gracefully during commands */
2223 signal(SIGINT, cmd_interrupt);
2225 err = parse_dispatch_command(conn, cmd, &remote_path,
2226 startdir, batchmode);
2237 #endif /* USE_LIBEDIT */
2239 /* err == 1 signifies normal "quit" exit */
2240 return (err >= 0 ? 0 : -1);
2244 connect_to_server(char *path, char **args, int *in, int *out)
2249 int pin[2], pout[2];
2251 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2252 fatal("pipe: %s", strerror(errno));
2257 #else /* USE_PIPES */
2260 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2261 fatal("socketpair: %s", strerror(errno));
2262 *in = *out = inout[0];
2263 c_in = c_out = inout[1];
2264 #endif /* USE_PIPES */
2266 if ((sshpid = fork()) == -1)
2267 fatal("fork: %s", strerror(errno));
2268 else if (sshpid == 0) {
2269 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2270 (dup2(c_out, STDOUT_FILENO) == -1)) {
2271 fprintf(stderr, "dup2: %s\n", strerror(errno));
2280 * The underlying ssh is in the same process group, so we must
2281 * ignore SIGINT if we want to gracefully abort commands,
2282 * otherwise the signal will make it to the ssh process and
2283 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2284 * underlying ssh, it must *not* ignore that signal.
2286 signal(SIGINT, SIG_IGN);
2287 signal(SIGTERM, SIG_DFL);
2289 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2293 signal(SIGTERM, killchild);
2294 signal(SIGINT, killchild);
2295 signal(SIGHUP, killchild);
2296 signal(SIGTSTP, suspchild);
2297 signal(SIGTTIN, suspchild);
2298 signal(SIGTTOU, suspchild);
2306 extern char *__progname;
2309 "usage: %s [-46aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2310 " [-D sftp_server_path] [-F ssh_config] "
2311 "[-i identity_file] [-l limit]\n"
2312 " [-o ssh_option] [-P port] [-R num_requests] "
2314 " [-s subsystem | sftp_server] destination\n",
2320 main(int argc, char **argv)
2322 int in, out, ch, err, tmp, port = -1;
2323 char *host = NULL, *user, *cp, *file2 = NULL;
2324 int debug_level = 0, sshver = 2;
2325 char *file1 = NULL, *sftp_server = NULL;
2326 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2328 LogLevel ll = SYSLOG_LEVEL_INFO;
2331 extern char *optarg;
2332 struct sftp_conn *conn;
2333 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2334 size_t num_requests = DEFAULT_NUM_REQUESTS;
2335 long long limit_kbps = 0;
2337 ssh_malloc_init(); /* must be called before any mallocs */
2338 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2342 __progname = ssh_get_progname(argv[0]);
2343 memset(&args, '\0', sizeof(args));
2345 addargs(&args, "%s", ssh_program);
2346 addargs(&args, "-oForwardX11 no");
2347 addargs(&args, "-oForwardAgent no");
2348 addargs(&args, "-oPermitLocalCommand no");
2349 addargs(&args, "-oClearAllForwardings yes");
2351 ll = SYSLOG_LEVEL_INFO;
2354 while ((ch = getopt(argc, argv,
2355 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2357 /* Passed through to ssh(1) */
2361 addargs(&args, "-%c", ch);
2363 /* Passed through to ssh(1) with argument */
2368 addargs(&args, "-%c", ch);
2369 addargs(&args, "%s", optarg);
2372 ll = SYSLOG_LEVEL_ERROR;
2375 addargs(&args, "-%c", ch);
2378 port = a2port(optarg);
2380 fatal("Bad port \"%s\"\n", optarg);
2383 if (debug_level < 3) {
2384 addargs(&args, "-v");
2385 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2391 if (sftp_server == NULL)
2392 sftp_server = _PATH_SFTP_SERVER;
2401 copy_buffer_len = strtol(optarg, &cp, 10);
2402 if (copy_buffer_len == 0 || *cp != '\0')
2403 fatal("Invalid buffer size \"%s\"", optarg);
2407 fatal("Batch file already specified.");
2409 /* Allow "-" as stdin */
2410 if (strcmp(optarg, "-") != 0 &&
2411 (infile = fopen(optarg, "r")) == NULL)
2412 fatal("%s (%s).", strerror(errno), optarg);
2414 quiet = batchmode = 1;
2415 addargs(&args, "-obatchmode yes");
2424 sftp_direct = optarg;
2427 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2431 limit_kbps *= 1024; /* kbps */
2437 num_requests = strtol(optarg, &cp, 10);
2438 if (num_requests == 0 || *cp != '\0')
2439 fatal("Invalid number of requests \"%s\"",
2443 sftp_server = optarg;
2446 ssh_program = optarg;
2447 replacearg(&args, 0, "%s", ssh_program);
2455 if (!isatty(STDERR_FILENO))
2458 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2460 if (sftp_direct == NULL) {
2461 if (optind == argc || argc > (optind + 2))
2465 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2474 if (parse_user_host_path(*argv, &user, &host,
2476 /* Treat as a plain hostname. */
2477 host = xstrdup(*argv);
2478 host = cleanhostname(host);
2482 file2 = *(argv + 1);
2485 fprintf(stderr, "Missing hostname\n");
2490 addargs(&args, "-oPort %d", port);
2492 addargs(&args, "-l");
2493 addargs(&args, "%s", user);
2495 addargs(&args, "-oProtocol %d", sshver);
2497 /* no subsystem if the server-spec contains a '/' */
2498 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2499 addargs(&args, "-s");
2501 addargs(&args, "--");
2502 addargs(&args, "%s", host);
2503 addargs(&args, "%s", (sftp_server != NULL ?
2504 sftp_server : "sftp"));
2506 connect_to_server(ssh_program, args.list, &in, &out);
2509 addargs(&args, "sftp-server");
2511 connect_to_server(sftp_direct, args.list, &in, &out);
2515 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2517 fatal("Couldn't initialise connection to server");
2520 if (sftp_direct == NULL)
2521 fprintf(stderr, "Connected to %s.\n", host);
2523 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2526 err = interactive_loop(conn, file1, file2);
2528 #if !defined(USE_PIPES)
2529 shutdown(in, SHUT_RDWR);
2530 shutdown(out, SHUT_RDWR);
2538 while (waitpid(sshpid, NULL, 0) == -1)
2540 fatal("Couldn't wait for ssh process: %s",
2543 exit(err == 0 ? 0 : 1);