1 /* $OpenBSD: sftp.c,v 1.212 2021/09/11 09:05:50 schwarze 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;
63 #include "pathnames.h"
70 #include "sftp-common.h"
71 #include "sftp-client.h"
73 /* File to read commands from */
76 /* Are we in batchfile mode? */
79 /* PID of ssh transport process */
80 static volatile pid_t sshpid = -1;
82 /* Suppress diagnostic messages */
85 /* This is set to 0 if the progressmeter is not desired. */
88 /* When this option is set, we always recursively download/upload directories */
91 /* When this option is set, we resume download or upload if possible */
94 /* When this option is set, the file transfers will always preserve times */
97 /* When this option is set, transfers will have fsync() called on each file */
100 /* SIGINT received during command processing */
101 volatile sig_atomic_t interrupted = 0;
103 /* I wish qsort() took a separate ctx for the comparison function...*/
107 /* Context used for commandline completion */
108 struct complete_ctx {
109 struct sftp_conn *conn;
113 int remote_glob(struct sftp_conn *, const char *, int,
114 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
116 extern char *__progname;
118 /* Separators for interactive commands */
119 #define WHITESPACE " \t\r\n"
122 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
123 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
124 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
125 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
126 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
127 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
128 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
129 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
130 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
132 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
133 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
135 /* Commands for interactive mode */
172 /* Type of completion */
177 static const struct CMD cmds[] = {
178 { "bye", I_QUIT, NOARGS },
179 { "cd", I_CHDIR, REMOTE },
180 { "chdir", I_CHDIR, REMOTE },
181 { "chgrp", I_CHGRP, REMOTE },
182 { "chmod", I_CHMOD, REMOTE },
183 { "chown", I_CHOWN, REMOTE },
184 { "df", I_DF, REMOTE },
185 { "dir", I_LS, REMOTE },
186 { "exit", I_QUIT, NOARGS },
187 { "get", I_GET, REMOTE },
188 { "help", I_HELP, NOARGS },
189 { "lcd", I_LCHDIR, LOCAL },
190 { "lchdir", I_LCHDIR, LOCAL },
191 { "lls", I_LLS, LOCAL },
192 { "lmkdir", I_LMKDIR, LOCAL },
193 { "ln", I_LINK, REMOTE },
194 { "lpwd", I_LPWD, LOCAL },
195 { "ls", I_LS, REMOTE },
196 { "lumask", I_LUMASK, NOARGS },
197 { "mkdir", I_MKDIR, REMOTE },
198 { "mget", I_GET, REMOTE },
199 { "mput", I_PUT, LOCAL },
200 { "progress", I_PROGRESS, NOARGS },
201 { "put", I_PUT, LOCAL },
202 { "pwd", I_PWD, REMOTE },
203 { "quit", I_QUIT, NOARGS },
204 { "reget", I_REGET, REMOTE },
205 { "rename", I_RENAME, REMOTE },
206 { "reput", I_REPUT, LOCAL },
207 { "rm", I_RM, REMOTE },
208 { "rmdir", I_RMDIR, REMOTE },
209 { "symlink", I_SYMLINK, REMOTE },
210 { "version", I_VERSION, NOARGS },
211 { "!", I_SHELL, NOARGS },
212 { "?", I_HELP, NOARGS },
225 waitpid(pid, NULL, 0);
237 while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR)
240 kill(getpid(), SIGSTOP);
245 cmd_interrupt(int signo)
247 const char msg[] = "\rInterrupt \n";
248 int olderrno = errno;
250 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
257 read_interrupt(int signo)
264 sigchld_handler(int sig)
266 int save_errno = errno;
268 const char msg[] = "\rConnection closed. \n";
270 /* Report if ssh transport process dies. */
271 while ((pid = waitpid(sshpid, NULL, WNOHANG)) == -1 && errno == EINTR)
274 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
284 printf("Available commands:\n"
286 "cd path Change remote directory to 'path'\n"
287 "chgrp [-h] grp path Change group of file 'path' to 'grp'\n"
288 "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n"
289 "chown [-h] own path Change owner of file 'path' to 'own'\n"
290 "df [-hi] [path] Display statistics for current directory or\n"
291 " filesystem containing 'path'\n"
293 "get [-afpR] remote [local] Download file\n"
294 "help Display this help text\n"
295 "lcd path Change local directory to 'path'\n"
296 "lls [ls-options [path]] Display local directory listing\n"
297 "lmkdir path Create local directory\n"
298 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
299 "lpwd Print local working directory\n"
300 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
301 "lumask umask Set local umask to 'umask'\n"
302 "mkdir path Create remote directory\n"
303 "progress Toggle display of progress meter\n"
304 "put [-afpR] local [remote] Upload file\n"
305 "pwd Display remote working directory\n"
307 "reget [-fpR] remote [local] Resume download file\n"
308 "rename oldpath newpath Rename remote file\n"
309 "reput [-fpR] local [remote] Resume upload file\n"
310 "rm path Delete remote file\n"
311 "rmdir path Remove remote directory\n"
312 "symlink oldpath newpath Symlink remote file\n"
313 "version Show SFTP version\n"
314 "!command Execute 'command' in local shell\n"
315 "! Escape to local shell\n"
316 "? Synonym for help\n");
320 local_do_shell(const char *args)
329 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
330 shell = _PATH_BSHELL;
332 if ((pid = fork()) == -1)
333 fatal("Couldn't fork: %s", strerror(errno));
336 /* XXX: child has pipe fds to ssh subproc open - issue? */
338 debug3("Executing %s -c \"%s\"", shell, args);
339 execl(shell, shell, "-c", args, (char *)NULL);
341 debug3("Executing %s", shell);
342 execl(shell, shell, (char *)NULL);
344 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
348 while (waitpid(pid, &status, 0) == -1)
350 fatal("Couldn't wait for child: %s", strerror(errno));
351 if (!WIFEXITED(status))
352 error("Shell exited abnormally");
353 else if (WEXITSTATUS(status))
354 error("Shell exited with status %d", WEXITSTATUS(status));
358 local_do_ls(const char *args)
361 local_do_shell(_PATH_LS);
363 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
364 char *buf = xmalloc(len);
366 /* XXX: quoting - rip quoting code from ftp? */
367 snprintf(buf, len, _PATH_LS " %s", args);
373 /* Strip one path (usually the pwd) from the start of another */
375 path_strip(const char *path, const char *strip)
380 return (xstrdup(path));
383 if (strncmp(path, strip, len) == 0) {
384 if (strip[len - 1] != '/' && path[len] == '/')
386 return (xstrdup(path + len));
389 return (xstrdup(path));
393 parse_getput_flags(const char *cmd, char **argv, int argc,
394 int *aflag, int *fflag, int *pflag, int *rflag)
396 extern int opterr, optind, optopt, optreset;
399 optind = optreset = 1;
402 *aflag = *fflag = *rflag = *pflag = 0;
403 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
420 error("%s: Invalid flag -%c", cmd, optopt);
429 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
431 extern int opterr, optind, optopt, optreset;
434 optind = optreset = 1;
438 while ((ch = getopt(argc, argv, "s")) != -1) {
444 error("%s: Invalid flag -%c", cmd, optopt);
453 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
455 extern int opterr, optind, optopt, optreset;
458 optind = optreset = 1;
462 while ((ch = getopt(argc, argv, "l")) != -1) {
468 error("%s: Invalid flag -%c", cmd, optopt);
477 parse_ls_flags(char **argv, int argc, int *lflag)
479 extern int opterr, optind, optopt, optreset;
482 optind = optreset = 1;
485 *lflag = LS_NAME_SORT;
486 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
489 *lflag &= ~VIEW_FLAGS;
490 *lflag |= LS_SHORT_VIEW;
493 *lflag &= ~SORT_FLAGS;
494 *lflag |= LS_SIZE_SORT;
497 *lflag |= LS_SHOW_ALL;
500 *lflag &= ~SORT_FLAGS;
503 *lflag |= LS_SI_UNITS;
506 *lflag &= ~LS_SHORT_VIEW;
507 *lflag |= LS_LONG_VIEW;
510 *lflag &= ~LS_SHORT_VIEW;
511 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
514 *lflag |= LS_REVERSE_SORT;
517 *lflag &= ~SORT_FLAGS;
518 *lflag |= LS_TIME_SORT;
521 error("ls: Invalid flag -%c", optopt);
530 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
532 extern int opterr, optind, optopt, optreset;
535 optind = optreset = 1;
539 while ((ch = getopt(argc, argv, "hi")) != -1) {
548 error("%s: Invalid flag -%c", cmd, optopt);
557 parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag)
559 extern int opterr, optind, optopt, optreset;
562 optind = optreset = 1;
566 while ((ch = getopt(argc, argv, "h")) != -1) {
572 error("%s: Invalid flag -%c", cmd, optopt);
581 parse_no_flags(const char *cmd, char **argv, int argc)
583 extern int opterr, optind, optopt, optreset;
586 optind = optreset = 1;
589 while ((ch = getopt(argc, argv, "")) != -1) {
592 error("%s: Invalid flag -%c", cmd, optopt);
601 process_get(struct sftp_conn *conn, const char *src, const char *dst,
602 const char *pwd, int pflag, int rflag, int resume, int fflag)
604 char *abs_src = NULL;
605 char *abs_dst = NULL;
607 char *filename, *tmp=NULL;
610 abs_src = xstrdup(src);
611 abs_src = make_absolute(abs_src, pwd);
612 memset(&g, 0, sizeof(g));
614 debug3("Looking up %s", abs_src);
615 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
616 if (r == GLOB_NOSPACE) {
617 error("Too many matches for \"%s\".", abs_src);
619 error("File \"%s\" not found.", abs_src);
626 * If multiple matches then dst must be a directory or
629 if (g.gl_matchc > 1 && dst != NULL && !local_is_dir(dst)) {
630 error("Multiple source paths, but destination "
631 "\"%s\" is not a directory", dst);
636 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
637 tmp = xstrdup(g.gl_pathv[i]);
638 if ((filename = basename(tmp)) == NULL) {
639 error("basename %s: %s", tmp, strerror(errno));
645 if (g.gl_matchc == 1 && dst) {
646 if (local_is_dir(dst)) {
647 abs_dst = path_append(dst, filename);
649 abs_dst = xstrdup(dst);
652 abs_dst = path_append(dst, filename);
654 abs_dst = xstrdup(filename);
658 resume |= global_aflag;
659 if (!quiet && resume)
660 mprintf("Resuming %s to %s\n",
661 g.gl_pathv[i], abs_dst);
662 else if (!quiet && !resume)
663 mprintf("Fetching %s to %s\n",
664 g.gl_pathv[i], abs_dst);
665 /* XXX follow link flag */
666 if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
667 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
668 pflag || global_pflag, 1, resume,
669 fflag || global_fflag, 0) == -1)
672 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
673 pflag || global_pflag, resume,
674 fflag || global_fflag) == -1)
688 process_put(struct sftp_conn *conn, const char *src, const char *dst,
689 const char *pwd, int pflag, int rflag, int resume, int fflag)
691 char *tmp_dst = NULL;
692 char *abs_dst = NULL;
693 char *tmp = NULL, *filename = NULL;
696 int i, dst_is_dir = 1;
700 tmp_dst = xstrdup(dst);
701 tmp_dst = make_absolute(tmp_dst, pwd);
704 memset(&g, 0, sizeof(g));
705 debug3("Looking up %s", src);
706 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
707 error("File \"%s\" not found.", src);
712 /* If we aren't fetching to pwd then stash this status for later */
714 dst_is_dir = remote_is_dir(conn, tmp_dst);
716 /* If multiple matches, dst may be directory or unspecified */
717 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
718 error("Multiple paths match, but destination "
719 "\"%s\" is not a directory", tmp_dst);
724 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
725 if (stat(g.gl_pathv[i], &sb) == -1) {
727 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
731 tmp = xstrdup(g.gl_pathv[i]);
732 if ((filename = basename(tmp)) == NULL) {
733 error("basename %s: %s", tmp, strerror(errno));
739 if (g.gl_matchc == 1 && tmp_dst) {
740 /* If directory specified, append filename */
742 abs_dst = path_append(tmp_dst, filename);
744 abs_dst = xstrdup(tmp_dst);
745 } else if (tmp_dst) {
746 abs_dst = path_append(tmp_dst, filename);
748 abs_dst = make_absolute(xstrdup(filename), pwd);
752 resume |= global_aflag;
753 if (!quiet && resume)
754 mprintf("Resuming upload of %s to %s\n",
755 g.gl_pathv[i], abs_dst);
756 else if (!quiet && !resume)
757 mprintf("Uploading %s to %s\n",
758 g.gl_pathv[i], abs_dst);
759 /* XXX follow_link_flag */
760 if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
761 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
762 pflag || global_pflag, 1, resume,
763 fflag || global_fflag, 0) == -1)
766 if (do_upload(conn, g.gl_pathv[i], abs_dst,
767 pflag || global_pflag, resume,
768 fflag || global_fflag) == -1)
781 sdirent_comp(const void *aa, const void *bb)
783 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
784 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
785 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
787 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
788 if (sort_flag & LS_NAME_SORT)
789 return (rmul * strcmp(a->filename, b->filename));
790 else if (sort_flag & LS_TIME_SORT)
791 return (rmul * NCMP(a->a.mtime, b->a.mtime));
792 else if (sort_flag & LS_SIZE_SORT)
793 return (rmul * NCMP(a->a.size, b->a.size));
795 fatal("Unknown ls sort type");
798 /* sftp ls.1 replacement for directories */
800 do_ls_dir(struct sftp_conn *conn, const char *path,
801 const char *strip_path, int lflag)
804 u_int c = 1, colspace = 0, columns = 1;
807 if ((n = do_readdir(conn, path, &d)) != 0)
810 if (!(lflag & LS_SHORT_VIEW)) {
811 u_int m = 0, width = 80;
815 /* Count entries for sort and find longest filename */
816 for (n = 0; d[n] != NULL; n++) {
817 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
818 m = MAXIMUM(m, strlen(d[n]->filename));
821 /* Add any subpath that also needs to be counted */
822 tmp = path_strip(path, strip_path);
826 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
829 columns = width / (m + 2);
830 columns = MAXIMUM(columns, 1);
831 colspace = width / columns;
832 colspace = MINIMUM(colspace, width);
835 if (lflag & SORT_FLAGS) {
836 for (n = 0; d[n] != NULL; n++)
837 ; /* count entries */
838 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
839 qsort(d, n, sizeof(*d), sdirent_comp);
842 for (n = 0; d[n] != NULL && !interrupted; n++) {
845 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
848 tmp = path_append(path, d[n]->filename);
849 fname = path_strip(tmp, strip_path);
852 if (lflag & LS_LONG_VIEW) {
853 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
857 memset(&sb, 0, sizeof(sb));
858 attrib_to_stat(&d[n]->a, &sb);
859 lname = ls_file(fname, &sb, 1,
860 (lflag & LS_SI_UNITS));
861 mprintf("%s\n", lname);
864 mprintf("%s\n", d[n]->longname);
866 mprintf("%-*s", colspace, fname);
877 if (!(lflag & LS_LONG_VIEW) && (c != 1))
880 free_sftp_dirents(d);
885 sglob_comp(const void *aa, const void *bb)
887 u_int a = *(const u_int *)aa;
888 u_int b = *(const u_int *)bb;
889 const char *ap = sort_glob->gl_pathv[a];
890 const char *bp = sort_glob->gl_pathv[b];
891 const struct stat *as = sort_glob->gl_statv[a];
892 const struct stat *bs = sort_glob->gl_statv[b];
893 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
895 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
896 if (sort_flag & LS_NAME_SORT)
897 return (rmul * strcmp(ap, bp));
898 else if (sort_flag & LS_TIME_SORT) {
899 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
900 if (timespeccmp(&as->st_mtim, &bs->st_mtim, ==))
902 return timespeccmp(&as->st_mtim, &bs->st_mtim, <) ?
904 #elif defined(HAVE_STRUCT_STAT_ST_MTIME)
905 return (rmul * NCMP(as->st_mtime, bs->st_mtime));
909 } else if (sort_flag & LS_SIZE_SORT)
910 return (rmul * NCMP(as->st_size, bs->st_size));
912 fatal("Unknown ls sort type");
915 /* sftp ls.1 replacement which handles path globs */
917 do_globbed_ls(struct sftp_conn *conn, const char *path,
918 const char *strip_path, int lflag)
924 u_int i, j, nentries, *indices = NULL, c = 1;
925 u_int colspace = 0, columns = 1, m = 0, width = 80;
927 memset(&g, 0, sizeof(g));
929 if ((r = remote_glob(conn, path,
930 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
932 (g.gl_pathc && !g.gl_matchc)) {
935 if (r == GLOB_NOSPACE) {
936 error("Can't ls: Too many matches for \"%s\"", path);
938 error("Can't ls: \"%s\" not found", path);
947 * If the glob returns a single match and it is a directory,
948 * then just list its contents.
950 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
951 S_ISDIR(g.gl_statv[0]->st_mode)) {
952 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
957 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
960 if (!(lflag & LS_SHORT_VIEW)) {
961 /* Count entries for sort and find longest filename */
962 for (i = 0; g.gl_pathv[i]; i++)
963 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
965 columns = width / (m + 2);
966 columns = MAXIMUM(columns, 1);
967 colspace = width / columns;
971 * Sorting: rather than mess with the contents of glob_t, prepare
972 * an array of indices into it and sort that. For the usual
973 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
975 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
976 ; /* count entries */
977 indices = calloc(nentries, sizeof(*indices));
978 for (i = 0; i < nentries; i++)
981 if (lflag & SORT_FLAGS) {
983 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
984 qsort(indices, nentries, sizeof(*indices), sglob_comp);
988 for (j = 0; j < nentries && !interrupted; j++) {
990 fname = path_strip(g.gl_pathv[i], strip_path);
991 if (lflag & LS_LONG_VIEW) {
992 if (g.gl_statv[i] == NULL) {
993 error("no stat information for %s", fname);
996 lname = ls_file(fname, g.gl_statv[i], 1,
997 (lflag & LS_SI_UNITS));
998 mprintf("%s\n", lname);
1001 mprintf("%-*s", colspace, fname);
1011 if (!(lflag & LS_LONG_VIEW) && (c != 1))
1023 do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
1025 struct sftp_statvfs st;
1026 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
1027 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
1028 char s_icapacity[16], s_dcapacity[16];
1030 if (do_statvfs(conn, path, &st, 1) == -1)
1032 if (st.f_files == 0)
1033 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1035 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1036 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1039 if (st.f_blocks == 0)
1040 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1042 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1043 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1047 printf(" Inodes Used Avail "
1048 "(root) %%Capacity\n");
1049 printf("%11llu %11llu %11llu %11llu %s\n",
1050 (unsigned long long)st.f_files,
1051 (unsigned long long)(st.f_files - st.f_ffree),
1052 (unsigned long long)st.f_favail,
1053 (unsigned long long)st.f_ffree, s_icapacity);
1055 strlcpy(s_used, "error", sizeof(s_used));
1056 strlcpy(s_avail, "error", sizeof(s_avail));
1057 strlcpy(s_root, "error", sizeof(s_root));
1058 strlcpy(s_total, "error", sizeof(s_total));
1059 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1060 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1061 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1062 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1063 printf(" Size Used Avail (root) %%Capacity\n");
1064 printf("%7sB %7sB %7sB %7sB %s\n",
1065 s_total, s_used, s_avail, s_root, s_dcapacity);
1067 printf(" Size Used Avail "
1068 "(root) %%Capacity\n");
1069 printf("%12llu %12llu %12llu %12llu %s\n",
1070 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1071 (unsigned long long)(st.f_frsize *
1072 (st.f_blocks - st.f_bfree) / 1024),
1073 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1074 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1081 * Undo escaping of glob sequences in place. Used to undo extra escaping
1082 * applied in makeargv() when the string is destined for a function that
1086 undo_glob_escape(char *s)
1121 * Split a string into an argument vector using sh(1)-style quoting,
1122 * comment and escaping rules, but with some tweaks to handle glob(3)
1124 * The "sloppy" flag allows for recovery from missing terminating quote, for
1125 * use in parsing incomplete commandlines during tab autocompletion.
1127 * Returns NULL on error or a NULL-terminated array of arguments.
1129 * If "lastquote" is not NULL, the quoting character used for the last
1130 * argument is placed in *lastquote ("\0", "'" or "\"").
1132 * If "terminated" is not NULL, *terminated will be set to 1 when the
1133 * last argument's quote has been properly terminated or 0 otherwise.
1134 * This parameter is only of use if "sloppy" is set.
1137 #define MAXARGLEN 8192
1139 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1144 static char argvs[MAXARGLEN];
1145 static char *argv[MAXARGS + 1];
1146 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1149 if (strlen(arg) > sizeof(argvs) - 1) {
1151 error("string too long");
1154 if (terminated != NULL)
1156 if (lastquote != NULL)
1161 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1162 error("Too many arguments.");
1165 if (isspace((unsigned char)arg[i])) {
1166 if (state == MA_UNQUOTED) {
1167 /* Terminate current argument */
1171 } else if (state != MA_START)
1172 argvs[j++] = arg[i];
1173 } else if (arg[i] == '"' || arg[i] == '\'') {
1174 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1175 if (state == MA_START) {
1176 argv[argc] = argvs + j;
1178 if (lastquote != NULL)
1179 *lastquote = arg[i];
1180 } else if (state == MA_UNQUOTED)
1182 else if (state == q)
1183 state = MA_UNQUOTED;
1185 argvs[j++] = arg[i];
1186 } else if (arg[i] == '\\') {
1187 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1188 quot = state == MA_SQUOTE ? '\'' : '"';
1189 /* Unescape quote we are in */
1190 /* XXX support \n and friends? */
1191 if (arg[i + 1] == quot) {
1193 argvs[j++] = arg[i];
1194 } else if (arg[i + 1] == '?' ||
1195 arg[i + 1] == '[' || arg[i + 1] == '*') {
1197 * Special case for sftp: append
1198 * double-escaped glob sequence -
1199 * glob will undo one level of
1200 * escaping. NB. string can grow here.
1202 if (j >= sizeof(argvs) - 5)
1203 goto args_too_longs;
1205 argvs[j++] = arg[i++];
1207 argvs[j++] = arg[i];
1209 argvs[j++] = arg[i++];
1210 argvs[j++] = arg[i];
1213 if (state == MA_START) {
1214 argv[argc] = argvs + j;
1215 state = MA_UNQUOTED;
1216 if (lastquote != NULL)
1219 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1220 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1222 * Special case for sftp: append
1223 * escaped glob sequence -
1224 * glob will undo one level of
1227 argvs[j++] = arg[i++];
1228 argvs[j++] = arg[i];
1230 /* Unescape everything */
1231 /* XXX support \n and friends? */
1233 argvs[j++] = arg[i];
1236 } else if (arg[i] == '#') {
1237 if (state == MA_SQUOTE || state == MA_DQUOTE)
1238 argvs[j++] = arg[i];
1241 } else if (arg[i] == '\0') {
1242 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1244 state = MA_UNQUOTED;
1245 if (terminated != NULL)
1249 error("Unterminated quoted argument");
1253 if (state == MA_UNQUOTED) {
1259 if (state == MA_START) {
1260 argv[argc] = argvs + j;
1261 state = MA_UNQUOTED;
1262 if (lastquote != NULL)
1265 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1266 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1268 * Special case for sftp: escape quoted
1269 * glob(3) wildcards. NB. string can grow
1272 if (j >= sizeof(argvs) - 3)
1273 goto args_too_longs;
1275 argvs[j++] = arg[i];
1277 argvs[j++] = arg[i];
1286 parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
1287 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1288 int *rflag, int *sflag,
1289 unsigned long *n_arg, char **path1, char **path2)
1291 const char *cmd, *cp = *cpp;
1295 int path1_mandatory = 0, i, cmdnum, optidx, argc;
1297 /* Skip leading whitespace */
1298 cp = cp + strspn(cp, WHITESPACE);
1301 * Check for leading '-' (disable error processing) and '@' (suppress
1306 for (;*cp != '\0'; cp++) {
1309 } else if (*cp == '@') {
1312 /* all other characters terminate prefix processing */
1316 cp = cp + strspn(cp, WHITESPACE);
1318 /* Ignore blank lines and lines which begin with comment '#' char */
1319 if (*cp == '\0' || *cp == '#')
1322 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1325 /* Figure out which command we have */
1326 for (i = 0; cmds[i].c != NULL; i++) {
1327 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1337 } else if (cmdnum == -1) {
1338 error("Invalid command.");
1342 /* Get arguments and parse flags */
1343 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1344 *rflag = *sflag = 0;
1345 *path1 = *path2 = NULL;
1352 if ((optidx = parse_getput_flags(cmd, argv, argc,
1353 aflag, fflag, pflag, rflag)) == -1)
1355 /* Get first pathname (mandatory) */
1356 if (argc - optidx < 1) {
1357 error("You must specify at least one path after a "
1358 "%s command.", cmd);
1361 *path1 = xstrdup(argv[optidx]);
1362 /* Get second pathname (optional) */
1363 if (argc - optidx > 1) {
1364 *path2 = xstrdup(argv[optidx + 1]);
1365 /* Destination is not globbed */
1366 undo_glob_escape(*path2);
1370 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1372 goto parse_two_paths;
1374 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1376 goto parse_two_paths;
1378 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1381 if (argc - optidx < 2) {
1382 error("You must specify two paths after a %s "
1386 *path1 = xstrdup(argv[optidx]);
1387 *path2 = xstrdup(argv[optidx + 1]);
1388 /* Paths are not globbed */
1389 undo_glob_escape(*path1);
1390 undo_glob_escape(*path2);
1396 path1_mandatory = 1;
1400 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1402 /* Get pathname (mandatory) */
1403 if (argc - optidx < 1) {
1404 if (!path1_mandatory)
1405 break; /* return a NULL path1 */
1406 error("You must specify a path after a %s command.",
1410 *path1 = xstrdup(argv[optidx]);
1411 /* Only "rm" globs */
1413 undo_glob_escape(*path1);
1416 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1419 /* Default to current directory if no path specified */
1420 if (argc - optidx < 1)
1423 *path1 = xstrdup(argv[optidx]);
1424 undo_glob_escape(*path1);
1428 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1430 /* Path is optional */
1431 if (argc - optidx > 0)
1432 *path1 = xstrdup(argv[optidx]);
1435 /* Skip ls command and following whitespace */
1436 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1438 /* Uses the rest of the line */
1446 if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
1448 /* Get numeric arg (mandatory) */
1449 if (argc - optidx < 1)
1452 ll = strtoll(argv[optidx], &cp2, base);
1453 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1454 ((ll == LLONG_MIN || ll == LLONG_MAX) && errno == ERANGE) ||
1455 ll < 0 || ll > UINT32_MAX) {
1457 error("You must supply a numeric argument "
1458 "to the %s command.", cmd);
1462 if (cmdnum == I_LUMASK)
1464 /* Get pathname (mandatory) */
1465 if (argc - optidx < 2) {
1466 error("You must specify a path after a %s command.",
1470 *path1 = xstrdup(argv[optidx + 1]);
1478 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1482 fatal("Command not implemented");
1490 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1491 const char *startdir, int err_abort, int echo_command)
1493 const char *ocmd = cmd;
1494 char *path1, *path2, *tmp;
1495 int ignore_errors = 0, disable_echo = 1;
1496 int aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1497 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1499 unsigned long n_arg = 0;
1501 char path_buf[PATH_MAX];
1505 path1 = path2 = NULL;
1506 cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag,
1507 &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg,
1509 if (ignore_errors != 0)
1512 if (echo_command && !disable_echo)
1513 mprintf("sftp> %s\n", ocmd);
1515 memset(&g, 0, sizeof(g));
1517 /* Perform command */
1523 /* Unrecognized command */
1530 err = process_get(conn, path1, path2, *pwd, pflag,
1531 rflag, aflag, fflag);
1537 err = process_put(conn, path1, path2, *pwd, pflag,
1538 rflag, aflag, fflag);
1541 path1 = make_absolute(path1, *pwd);
1542 path2 = make_absolute(path2, *pwd);
1543 err = do_rename(conn, path1, path2, lflag);
1550 path1 = make_absolute(path1, *pwd);
1551 path2 = make_absolute(path2, *pwd);
1552 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1555 path1 = make_absolute(path1, *pwd);
1556 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1557 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1559 mprintf("Removing %s\n", g.gl_pathv[i]);
1560 err = do_rm(conn, g.gl_pathv[i]);
1561 if (err != 0 && err_abort)
1566 path1 = make_absolute(path1, *pwd);
1568 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1570 err = do_mkdir(conn, path1, &a, 1);
1573 path1 = make_absolute(path1, *pwd);
1574 err = do_rmdir(conn, path1);
1577 if (path1 == NULL || *path1 == '\0')
1578 path1 = xstrdup(startdir);
1579 path1 = make_absolute(path1, *pwd);
1580 if ((tmp = do_realpath(conn, path1)) == NULL) {
1584 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1589 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1590 error("Can't change directory: Can't check target");
1595 if (!S_ISDIR(aa->perm)) {
1596 error("Can't change directory: \"%s\" is not "
1597 "a directory", tmp);
1607 do_ls_dir(conn, *pwd, *pwd, lflag);
1611 /* Strip pwd off beginning of non-absolute paths */
1613 if (!path_absolute(path1))
1616 path1 = make_absolute(path1, *pwd);
1617 err = do_globbed_ls(conn, path1, tmp, lflag);
1620 /* Default to current directory if no path specified */
1622 path1 = xstrdup(*pwd);
1623 path1 = make_absolute(path1, *pwd);
1624 err = do_df(conn, path1, hflag, iflag);
1627 if (path1 == NULL || *path1 == '\0')
1628 path1 = xstrdup("~");
1629 tmp = tilde_expand_filename(path1, getuid());
1632 if (chdir(path1) == -1) {
1633 error("Couldn't change local directory to "
1634 "\"%s\": %s", path1, strerror(errno));
1639 if (mkdir(path1, 0777) == -1) {
1640 error("Couldn't create local directory "
1641 "\"%s\": %s", path1, strerror(errno));
1649 local_do_shell(cmd);
1653 printf("Local umask: %03lo\n", n_arg);
1656 path1 = make_absolute(path1, *pwd);
1658 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1660 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1661 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1663 mprintf("Changing mode on %s\n",
1665 err = (hflag ? do_lsetstat : do_setstat)(conn,
1667 if (err != 0 && err_abort)
1673 path1 = make_absolute(path1, *pwd);
1674 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1675 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1676 if (!(aa = (hflag ? do_lstat : do_stat)(conn,
1677 g.gl_pathv[i], 0))) {
1684 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1685 error("Can't get current ownership of "
1686 "remote file \"%s\"", g.gl_pathv[i]);
1693 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1694 if (cmdnum == I_CHOWN) {
1696 mprintf("Changing owner on %s\n",
1701 mprintf("Changing group on %s\n",
1705 err = (hflag ? do_lsetstat : do_setstat)(conn,
1707 if (err != 0 && err_abort)
1712 mprintf("Remote working directory: %s\n", *pwd);
1715 if (!getcwd(path_buf, sizeof(path_buf))) {
1716 error("Couldn't get local cwd: %s", strerror(errno));
1720 mprintf("Local working directory: %s\n", path_buf);
1723 /* Processed below */
1729 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1732 showprogress = !showprogress;
1734 printf("Progress meter enabled\n");
1736 printf("Progress meter disabled\n");
1739 fatal("%d is not implemented", cmdnum);
1747 /* If an unignored error occurs in batch mode we should abort. */
1748 if (err_abort && err != 0)
1750 else if (cmdnum == I_QUIT)
1758 prompt(EditLine *el)
1763 /* Display entries in 'list' after skipping the first 'len' chars */
1765 complete_display(char **list, u_int len)
1767 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1771 /* Count entries for sort and find longest */
1772 for (y = 0; list[y]; y++)
1773 m = MAXIMUM(m, strlen(list[y]));
1775 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1778 m = m > len ? m - len : 0;
1779 columns = width / (m + 2);
1780 columns = MAXIMUM(columns, 1);
1781 colspace = width / columns;
1782 colspace = MINIMUM(colspace, width);
1786 for (y = 0; list[y]; y++) {
1787 llen = strlen(list[y]);
1788 tmp = llen > len ? list[y] + len : "";
1789 mprintf("%-*s", colspace, tmp);
1800 * Given a "list" of words that begin with a common prefix of "word",
1801 * attempt to find an autocompletion to extends "word" by the next
1802 * characters common to all entries in "list".
1805 complete_ambiguous(const char *word, char **list, size_t count)
1811 u_int y, matchlen = strlen(list[0]);
1813 /* Find length of common stem */
1814 for (y = 1; list[y]; y++) {
1817 for (x = 0; x < matchlen; x++)
1818 if (list[0][x] != list[y][x])
1824 if (matchlen > strlen(word)) {
1825 char *tmp = xstrdup(list[0]);
1827 tmp[matchlen] = '\0';
1832 return xstrdup(word);
1835 /* Autocomplete a sftp command */
1837 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1840 u_int y, count = 0, cmdlen, tmplen;
1841 char *tmp, **list, argterm[3];
1844 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1846 /* No command specified: display all available commands */
1848 for (y = 0; cmds[y].c; y++)
1849 list[count++] = xstrdup(cmds[y].c);
1852 complete_display(list, 0);
1854 for (y = 0; list[y] != NULL; y++)
1860 /* Prepare subset of commands that start with "cmd" */
1861 cmdlen = strlen(cmd);
1862 for (y = 0; cmds[y].c; y++) {
1863 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1864 list[count++] = xstrdup(cmds[y].c);
1873 /* Complete ambiguous command */
1874 tmp = complete_ambiguous(cmd, list, count);
1876 complete_display(list, 0);
1878 for (y = 0; list[y]; y++)
1883 tmplen = strlen(tmp);
1884 cmdlen = strlen(cmd);
1885 /* If cmd may be extended then do so */
1886 if (tmplen > cmdlen)
1887 if (el_insertstr(el, tmp + cmdlen) == -1)
1888 fatal("el_insertstr failed.");
1890 /* Terminate argument cleanly */
1894 argterm[y++] = quote;
1895 if (lastarg || *(lf->cursor) != ' ')
1898 if (y > 0 && el_insertstr(el, argterm) == -1)
1899 fatal("el_insertstr failed.");
1908 * Determine whether a particular sftp command's arguments (if any)
1909 * represent local or remote files.
1912 complete_is_remote(char *cmd) {
1918 for (i = 0; cmds[i].c; i++) {
1919 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1926 /* Autocomplete a filename "file" */
1928 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1929 char *file, int remote, int lastarg, char quote, int terminated)
1932 char *tmp, *tmp2, ins[8];
1933 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1937 /* Glob from "file" location */
1941 xasprintf(&tmp, "%s*", file);
1943 /* Check if the path is absolute. */
1944 isabs = path_absolute(tmp);
1946 memset(&g, 0, sizeof(g));
1947 if (remote != LOCAL) {
1948 tmp = make_absolute(tmp, remote_path);
1949 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1951 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1953 /* Determine length of pwd so we can trim completion display */
1954 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1955 /* Terminate counting on first unescaped glob metacharacter */
1956 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1957 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1961 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1963 if (tmp[tmplen] == '/')
1964 pwdlen = tmplen + 1; /* track last seen '/' */
1969 if (g.gl_matchc == 0)
1972 if (g.gl_matchc > 1)
1973 complete_display(g.gl_pathv, pwdlen);
1975 /* Don't try to extend globs */
1976 if (file == NULL || hadglob)
1979 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1980 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1986 tmplen = strlen(tmp);
1987 filelen = strlen(file);
1989 /* Count the number of escaped characters in the input string. */
1991 for (i = 0; i < filelen; i++) {
1992 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1999 if (tmplen > (filelen - cesc)) {
2000 tmp2 = tmp + filelen - cesc;
2002 /* quote argument on way out */
2003 for (i = 0; i < len; i += clen) {
2004 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
2005 (size_t)clen > sizeof(ins) - 2)
2006 fatal("invalid multibyte character");
2008 memcpy(ins + 1, tmp2 + i, clen);
2009 ins[clen + 1] = '\0';
2019 if (quote == '\0' || tmp2[i] == quote) {
2020 if (el_insertstr(el, ins) == -1)
2021 fatal("el_insertstr "
2027 if (el_insertstr(el, ins + 1) == -1)
2028 fatal("el_insertstr failed.");
2035 if (g.gl_matchc == 1) {
2037 if (!terminated && quote != '\0')
2039 if (*(lf->cursor - 1) != '/' &&
2040 (lastarg || *(lf->cursor) != ' '))
2043 if (i > 0 && el_insertstr(el, ins) == -1)
2044 fatal("el_insertstr failed.");
2053 /* tab-completion hook function, called via libedit */
2054 static unsigned char
2055 complete(EditLine *el, int ch)
2057 char **argv, *line, quote;
2059 u_int cursor, len, terminated, ret = CC_ERROR;
2061 struct complete_ctx *complete_ctx;
2064 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2065 fatal_f("el_get failed");
2067 /* Figure out which argument the cursor points to */
2068 cursor = lf->cursor - lf->buffer;
2069 line = xmalloc(cursor + 1);
2070 memcpy(line, lf->buffer, cursor);
2071 line[cursor] = '\0';
2072 argv = makeargv(line, &carg, 1, "e, &terminated);
2075 /* Get all the arguments on the line */
2076 len = lf->lastchar - lf->buffer;
2077 line = xmalloc(len + 1);
2078 memcpy(line, lf->buffer, len);
2080 argv = makeargv(line, &argc, 1, NULL, NULL);
2082 /* Ensure cursor is at EOL or a argument boundary */
2083 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2084 line[cursor] != '\n') {
2090 /* Show all available commands */
2091 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2093 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2094 /* Handle the command parsing */
2095 if (complete_cmd_parse(el, argv[0], argc == carg,
2096 quote, terminated) != 0)
2098 } else if (carg >= 1) {
2099 /* Handle file parsing */
2100 int remote = complete_is_remote(argv[0]);
2101 char *filematch = NULL;
2103 if (carg > 1 && line[cursor-1] != ' ')
2104 filematch = argv[carg - 1];
2107 complete_match(el, complete_ctx->conn,
2108 *complete_ctx->remote_pathp, filematch,
2109 remote, carg == argc, quote, terminated) != 0)
2116 #endif /* USE_LIBEDIT */
2119 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2122 char *dir = NULL, *startdir = NULL;
2124 int err, interactive;
2125 EditLine *el = NULL;
2129 extern char *__progname;
2130 struct complete_ctx complete_ctx;
2132 if (!batchmode && isatty(STDIN_FILENO)) {
2133 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2134 fatal("Couldn't initialise editline");
2135 if ((hl = history_init()) == NULL)
2136 fatal("Couldn't initialise editline history");
2137 history(hl, &hev, H_SETSIZE, 100);
2138 el_set(el, EL_HIST, history, hl);
2140 el_set(el, EL_PROMPT, prompt);
2141 el_set(el, EL_EDITOR, "emacs");
2142 el_set(el, EL_TERMINAL, NULL);
2143 el_set(el, EL_SIGNAL, 1);
2144 el_source(el, NULL);
2146 /* Tab Completion */
2147 el_set(el, EL_ADDFN, "ftp-complete",
2148 "Context sensitive argument completion", complete);
2149 complete_ctx.conn = conn;
2150 complete_ctx.remote_pathp = &remote_path;
2151 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2152 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2153 /* enable ctrl-left-arrow and ctrl-right-arrow */
2154 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2155 el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL);
2156 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2157 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2158 /* make ^w match ksh behaviour */
2159 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2161 #endif /* USE_LIBEDIT */
2163 remote_path = do_realpath(conn, ".");
2164 if (remote_path == NULL)
2166 startdir = xstrdup(remote_path);
2168 if (file1 != NULL) {
2169 dir = xstrdup(file1);
2170 dir = make_absolute(dir, remote_path);
2172 if (remote_is_dir(conn, dir) && file2 == NULL) {
2174 mprintf("Changing to: %s\n", dir);
2175 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2176 if (parse_dispatch_command(conn, cmd,
2177 &remote_path, startdir, 1, 0) != 0) {
2185 /* XXX this is wrong wrt quoting */
2186 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2187 global_aflag ? " -a" : "", dir,
2188 file2 == NULL ? "" : " ",
2189 file2 == NULL ? "" : file2);
2190 err = parse_dispatch_command(conn, cmd,
2191 &remote_path, startdir, 1, 0);
2201 setvbuf(stdout, NULL, _IOLBF, 0);
2202 setvbuf(infile, NULL, _IOLBF, 0);
2204 interactive = !batchmode && isatty(STDIN_FILENO);
2207 struct sigaction sa;
2210 memset(&sa, 0, sizeof(sa));
2211 sa.sa_handler = interactive ? read_interrupt : killchild;
2212 if (sigaction(SIGINT, &sa, NULL) == -1) {
2213 debug3("sigaction(%s): %s", strsignal(SIGINT),
2220 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2232 if ((line = el_gets(el, &count)) == NULL ||
2239 history(hl, &hev, H_ENTER, line);
2240 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2241 fprintf(stderr, "Error: input line too long\n");
2244 #endif /* USE_LIBEDIT */
2247 cmd[strcspn(cmd, "\n")] = '\0';
2249 /* Handle user interrupts gracefully during commands */
2251 ssh_signal(SIGINT, cmd_interrupt);
2253 err = parse_dispatch_command(conn, cmd, &remote_path,
2254 startdir, batchmode, !interactive && el == NULL);
2258 ssh_signal(SIGCHLD, SIG_DFL);
2266 #endif /* USE_LIBEDIT */
2268 /* err == 1 signifies normal "quit" exit */
2269 return (err >= 0 ? 0 : -1);
2273 connect_to_server(char *path, char **args, int *in, int *out)
2278 int pin[2], pout[2];
2280 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2281 fatal("pipe: %s", strerror(errno));
2286 #else /* USE_PIPES */
2289 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2290 fatal("socketpair: %s", strerror(errno));
2291 *in = *out = inout[0];
2292 c_in = c_out = inout[1];
2293 #endif /* USE_PIPES */
2295 if ((sshpid = fork()) == -1)
2296 fatal("fork: %s", strerror(errno));
2297 else if (sshpid == 0) {
2298 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2299 (dup2(c_out, STDOUT_FILENO) == -1)) {
2300 fprintf(stderr, "dup2: %s\n", strerror(errno));
2309 * The underlying ssh is in the same process group, so we must
2310 * ignore SIGINT if we want to gracefully abort commands,
2311 * otherwise the signal will make it to the ssh process and
2312 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2313 * underlying ssh, it must *not* ignore that signal.
2315 ssh_signal(SIGINT, SIG_IGN);
2316 ssh_signal(SIGTERM, SIG_DFL);
2318 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2322 ssh_signal(SIGTERM, killchild);
2323 ssh_signal(SIGINT, killchild);
2324 ssh_signal(SIGHUP, killchild);
2325 ssh_signal(SIGTSTP, suspchild);
2326 ssh_signal(SIGTTIN, suspchild);
2327 ssh_signal(SIGTTOU, suspchild);
2328 ssh_signal(SIGCHLD, sigchld_handler);
2336 extern char *__progname;
2339 "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2340 " [-D sftp_server_path] [-F ssh_config] [-i identity_file]\n"
2341 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
2342 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
2349 main(int argc, char **argv)
2351 int in, out, ch, err, tmp, port = -1, noisy = 0;
2352 char *host = NULL, *user, *cp, *file2 = NULL;
2353 int debug_level = 0;
2354 char *file1 = NULL, *sftp_server = NULL;
2355 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2357 LogLevel ll = SYSLOG_LEVEL_INFO;
2360 extern char *optarg;
2361 struct sftp_conn *conn;
2362 size_t copy_buffer_len = 0;
2363 size_t num_requests = 0;
2364 long long limit_kbps = 0;
2366 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2372 __progname = ssh_get_progname(argv[0]);
2373 memset(&args, '\0', sizeof(args));
2375 addargs(&args, "%s", ssh_program);
2376 addargs(&args, "-oForwardX11 no");
2377 addargs(&args, "-oPermitLocalCommand no");
2378 addargs(&args, "-oClearAllForwardings yes");
2380 ll = SYSLOG_LEVEL_INFO;
2383 while ((ch = getopt(argc, argv,
2384 "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:")) != -1) {
2386 /* Passed through to ssh(1) */
2391 addargs(&args, "-%c", ch);
2393 /* Passed through to ssh(1) with argument */
2399 addargs(&args, "-%c", ch);
2400 addargs(&args, "%s", optarg);
2403 ll = SYSLOG_LEVEL_ERROR;
2406 addargs(&args, "-%c", ch);
2409 port = a2port(optarg);
2411 fatal("Bad port \"%s\"\n", optarg);
2414 if (debug_level < 3) {
2415 addargs(&args, "-v");
2416 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2421 fatal("SSH protocol v.1 is no longer supported");
2424 /* accept silently */
2430 copy_buffer_len = strtol(optarg, &cp, 10);
2431 if (copy_buffer_len == 0 || *cp != '\0')
2432 fatal("Invalid buffer size \"%s\"", optarg);
2436 fatal("Batch file already specified.");
2438 /* Allow "-" as stdin */
2439 if (strcmp(optarg, "-") != 0 &&
2440 (infile = fopen(optarg, "r")) == NULL)
2441 fatal("%s (%s).", strerror(errno), optarg);
2443 quiet = batchmode = 1;
2444 addargs(&args, "-obatchmode yes");
2450 noisy = 1; /* Used to clear quiet mode after getopt */
2456 sftp_direct = optarg;
2459 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2463 limit_kbps *= 1024; /* kbps */
2469 num_requests = strtol(optarg, &cp, 10);
2470 if (num_requests == 0 || *cp != '\0')
2471 fatal("Invalid number of requests \"%s\"",
2475 sftp_server = optarg;
2478 ssh_program = optarg;
2479 replacearg(&args, 0, "%s", ssh_program);
2487 /* Do this last because we want the user to be able to override it */
2488 addargs(&args, "-oForwardAgent no");
2490 if (!isatty(STDERR_FILENO))
2496 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2498 if (sftp_direct == NULL) {
2499 if (optind == argc || argc > (optind + 2))
2503 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2512 /* Try with user, host and path. */
2513 if (parse_user_host_path(*argv, &user, &host,
2516 /* Try with user and host. */
2517 if (parse_user_host_port(*argv, &user, &host, NULL)
2520 /* Treat as a plain hostname. */
2521 host = xstrdup(*argv);
2522 host = cleanhostname(host);
2525 file2 = *(argv + 1);
2528 fprintf(stderr, "Missing hostname\n");
2533 addargs(&args, "-oPort %d", port);
2535 addargs(&args, "-l");
2536 addargs(&args, "%s", user);
2539 /* no subsystem if the server-spec contains a '/' */
2540 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2541 addargs(&args, "-s");
2543 addargs(&args, "--");
2544 addargs(&args, "%s", host);
2545 addargs(&args, "%s", (sftp_server != NULL ?
2546 sftp_server : "sftp"));
2548 connect_to_server(ssh_program, args.list, &in, &out);
2551 addargs(&args, "sftp-server");
2553 connect_to_server(sftp_direct, args.list, &in, &out);
2557 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2559 fatal("Couldn't initialise connection to server");
2562 if (sftp_direct == NULL)
2563 fprintf(stderr, "Connected to %s.\n", host);
2565 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2568 err = interactive_loop(conn, file1, file2);
2570 #if !defined(USE_PIPES)
2571 shutdown(in, SHUT_RDWR);
2572 shutdown(out, SHUT_RDWR);
2580 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
2582 fatal("Couldn't wait for ssh process: %s",
2585 exit(err == 0 ? 0 : 1);