1 /* $OpenBSD: sftp.c,v 1.225 2023/01/05 05:49:13 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/socket.h>
27 #ifdef HAVE_SYS_STATVFS_H
28 #include <sys/statvfs.h>
46 typedef void EditLine;
62 #include "pathnames.h"
69 #include "sftp-common.h"
70 #include "sftp-client.h"
71 #include "sftp-usergroup.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 */
170 const int t; /* Completion type for the first argument */
171 const int t2; /* completion type for the optional second argument */
174 /* Type of completion */
179 static const struct CMD cmds[] = {
180 { "bye", I_QUIT, NOARGS, NOARGS },
181 { "cd", I_CHDIR, REMOTE, NOARGS },
182 { "chdir", I_CHDIR, REMOTE, NOARGS },
183 { "chgrp", I_CHGRP, REMOTE, NOARGS },
184 { "chmod", I_CHMOD, REMOTE, NOARGS },
185 { "chown", I_CHOWN, REMOTE, NOARGS },
186 { "copy", I_COPY, REMOTE, LOCAL },
187 { "cp", I_COPY, REMOTE, LOCAL },
188 { "df", I_DF, REMOTE, NOARGS },
189 { "dir", I_LS, REMOTE, NOARGS },
190 { "exit", I_QUIT, NOARGS, NOARGS },
191 { "get", I_GET, REMOTE, LOCAL },
192 { "help", I_HELP, NOARGS, NOARGS },
193 { "lcd", I_LCHDIR, LOCAL, NOARGS },
194 { "lchdir", I_LCHDIR, LOCAL, NOARGS },
195 { "lls", I_LLS, LOCAL, NOARGS },
196 { "lmkdir", I_LMKDIR, LOCAL, NOARGS },
197 { "ln", I_LINK, REMOTE, REMOTE },
198 { "lpwd", I_LPWD, LOCAL, NOARGS },
199 { "ls", I_LS, REMOTE, NOARGS },
200 { "lumask", I_LUMASK, NOARGS, NOARGS },
201 { "mkdir", I_MKDIR, REMOTE, NOARGS },
202 { "mget", I_GET, REMOTE, LOCAL },
203 { "mput", I_PUT, LOCAL, REMOTE },
204 { "progress", I_PROGRESS, NOARGS, NOARGS },
205 { "put", I_PUT, LOCAL, REMOTE },
206 { "pwd", I_PWD, REMOTE, NOARGS },
207 { "quit", I_QUIT, NOARGS, NOARGS },
208 { "reget", I_REGET, REMOTE, LOCAL },
209 { "rename", I_RENAME, REMOTE, REMOTE },
210 { "reput", I_REPUT, LOCAL, REMOTE },
211 { "rm", I_RM, REMOTE, NOARGS },
212 { "rmdir", I_RMDIR, REMOTE, NOARGS },
213 { "symlink", I_SYMLINK, REMOTE, REMOTE },
214 { "version", I_VERSION, NOARGS, NOARGS },
215 { "!", I_SHELL, NOARGS, NOARGS },
216 { "?", I_HELP, NOARGS, NOARGS },
229 waitpid(pid, NULL, 0);
241 while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR)
244 kill(getpid(), SIGSTOP);
249 cmd_interrupt(int signo)
251 const char msg[] = "\rInterrupt \n";
252 int olderrno = errno;
254 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
261 read_interrupt(int signo)
268 sigchld_handler(int sig)
270 int save_errno = errno;
272 const char msg[] = "\rConnection closed. \n";
274 /* Report if ssh transport process dies. */
275 while ((pid = waitpid(sshpid, NULL, WNOHANG)) == -1 && errno == EINTR)
279 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
289 printf("Available commands:\n"
291 "cd path Change remote directory to 'path'\n"
292 "chgrp [-h] grp path Change group of file 'path' to 'grp'\n"
293 "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n"
294 "chown [-h] own path Change owner of file 'path' to 'own'\n"
295 "copy oldpath newpath Copy remote file\n"
296 "cp oldpath newpath Copy remote file\n"
297 "df [-hi] [path] Display statistics for current directory or\n"
298 " filesystem containing 'path'\n"
300 "get [-afpR] remote [local] Download file\n"
301 "help Display this help text\n"
302 "lcd path Change local directory to 'path'\n"
303 "lls [ls-options [path]] Display local directory listing\n"
304 "lmkdir path Create local directory\n"
305 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
306 "lpwd Print local working directory\n"
307 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
308 "lumask umask Set local umask to 'umask'\n"
309 "mkdir path Create remote directory\n"
310 "progress Toggle display of progress meter\n"
311 "put [-afpR] local [remote] Upload file\n"
312 "pwd Display remote working directory\n"
314 "reget [-fpR] remote [local] Resume download file\n"
315 "rename oldpath newpath Rename remote file\n"
316 "reput [-fpR] local [remote] Resume upload file\n"
317 "rm path Delete remote file\n"
318 "rmdir path Remove remote directory\n"
319 "symlink oldpath newpath Symlink remote file\n"
320 "version Show SFTP version\n"
321 "!command Execute 'command' in local shell\n"
322 "! Escape to local shell\n"
323 "? Synonym for help\n");
327 local_do_shell(const char *args)
336 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
337 shell = _PATH_BSHELL;
339 if ((pid = fork()) == -1)
340 fatal("Couldn't fork: %s", strerror(errno));
343 /* XXX: child has pipe fds to ssh subproc open - issue? */
345 debug3("Executing %s -c \"%s\"", shell, args);
346 execl(shell, shell, "-c", args, (char *)NULL);
348 debug3("Executing %s", shell);
349 execl(shell, shell, (char *)NULL);
351 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
355 while (waitpid(pid, &status, 0) == -1)
357 fatal("Couldn't wait for child: %s", strerror(errno));
358 if (!WIFEXITED(status))
359 error("Shell exited abnormally");
360 else if (WEXITSTATUS(status))
361 error("Shell exited with status %d", WEXITSTATUS(status));
365 local_do_ls(const char *args)
368 local_do_shell(_PATH_LS);
370 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
371 char *buf = xmalloc(len);
373 /* XXX: quoting - rip quoting code from ftp? */
374 snprintf(buf, len, _PATH_LS " %s", args);
380 /* Strip one path (usually the pwd) from the start of another */
382 path_strip(const char *path, const char *strip)
387 return (xstrdup(path));
390 if (strncmp(path, strip, len) == 0) {
391 if (strip[len - 1] != '/' && path[len] == '/')
393 return (xstrdup(path + len));
396 return (xstrdup(path));
400 parse_getput_flags(const char *cmd, char **argv, int argc,
401 int *aflag, int *fflag, int *pflag, int *rflag)
403 extern int opterr, optind, optopt, optreset;
406 optind = optreset = 1;
409 *aflag = *fflag = *rflag = *pflag = 0;
410 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
427 error("%s: Invalid flag -%c", cmd, optopt);
436 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
438 extern int opterr, optind, optopt, optreset;
441 optind = optreset = 1;
445 while ((ch = getopt(argc, argv, "s")) != -1) {
451 error("%s: Invalid flag -%c", cmd, optopt);
460 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
462 extern int opterr, optind, optopt, optreset;
465 optind = optreset = 1;
469 while ((ch = getopt(argc, argv, "l")) != -1) {
475 error("%s: Invalid flag -%c", cmd, optopt);
484 parse_ls_flags(char **argv, int argc, int *lflag)
486 extern int opterr, optind, optopt, optreset;
489 optind = optreset = 1;
492 *lflag = LS_NAME_SORT;
493 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
496 *lflag &= ~VIEW_FLAGS;
497 *lflag |= LS_SHORT_VIEW;
500 *lflag &= ~SORT_FLAGS;
501 *lflag |= LS_SIZE_SORT;
504 *lflag |= LS_SHOW_ALL;
507 *lflag &= ~SORT_FLAGS;
510 *lflag |= LS_SI_UNITS;
513 *lflag &= ~LS_SHORT_VIEW;
514 *lflag |= LS_LONG_VIEW;
517 *lflag &= ~LS_SHORT_VIEW;
518 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
521 *lflag |= LS_REVERSE_SORT;
524 *lflag &= ~SORT_FLAGS;
525 *lflag |= LS_TIME_SORT;
528 error("ls: Invalid flag -%c", optopt);
537 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
539 extern int opterr, optind, optopt, optreset;
542 optind = optreset = 1;
546 while ((ch = getopt(argc, argv, "hi")) != -1) {
555 error("%s: Invalid flag -%c", cmd, optopt);
564 parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag)
566 extern int opterr, optind, optopt, optreset;
569 optind = optreset = 1;
573 while ((ch = getopt(argc, argv, "h")) != -1) {
579 error("%s: Invalid flag -%c", cmd, optopt);
588 parse_no_flags(const char *cmd, char **argv, int argc)
590 extern int opterr, optind, optopt, optreset;
593 optind = optreset = 1;
596 while ((ch = getopt(argc, argv, "")) != -1) {
599 error("%s: Invalid flag -%c", cmd, optopt);
608 escape_glob(const char *s)
614 ret = xcalloc(2, len + 1);
615 for (i = o = 0; i < len; i++) {
616 if (strchr("[]?*\\", s[i]) != NULL)
625 make_absolute_pwd_glob(const char *p, const char *pwd)
629 escpwd = escape_glob(pwd);
632 ret = make_absolute(xstrdup(p), escpwd);
638 process_get(struct sftp_conn *conn, const char *src, const char *dst,
639 const char *pwd, int pflag, int rflag, int resume, int fflag)
641 char *filename, *abs_src = NULL, *abs_dst = NULL, *tmp = NULL;
645 abs_src = make_absolute_pwd_glob(src, pwd);
646 memset(&g, 0, sizeof(g));
648 debug3("Looking up %s", abs_src);
649 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
650 if (r == GLOB_NOSPACE) {
651 error("Too many matches for \"%s\".", abs_src);
653 error("File \"%s\" not found.", abs_src);
660 * If multiple matches then dst must be a directory or
663 if (g.gl_matchc > 1 && dst != NULL && !local_is_dir(dst)) {
664 error("Multiple source paths, but destination "
665 "\"%s\" is not a directory", dst);
670 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
671 tmp = xstrdup(g.gl_pathv[i]);
672 if ((filename = basename(tmp)) == NULL) {
673 error("basename %s: %s", tmp, strerror(errno));
679 if (g.gl_matchc == 1 && dst) {
680 if (local_is_dir(dst)) {
681 abs_dst = path_append(dst, filename);
683 abs_dst = xstrdup(dst);
686 abs_dst = path_append(dst, filename);
688 abs_dst = xstrdup(filename);
692 resume |= global_aflag;
693 if (!quiet && resume)
694 mprintf("Resuming %s to %s\n",
695 g.gl_pathv[i], abs_dst);
696 else if (!quiet && !resume)
697 mprintf("Fetching %s to %s\n",
698 g.gl_pathv[i], abs_dst);
699 /* XXX follow link flag */
700 if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
701 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
702 pflag || global_pflag, 1, resume,
703 fflag || global_fflag, 0, 0) == -1)
706 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
707 pflag || global_pflag, resume,
708 fflag || global_fflag, 0) == -1)
722 process_put(struct sftp_conn *conn, const char *src, const char *dst,
723 const char *pwd, int pflag, int rflag, int resume, int fflag)
725 char *tmp_dst = NULL;
726 char *abs_dst = NULL;
727 char *tmp = NULL, *filename = NULL;
730 int i, dst_is_dir = 1;
734 tmp_dst = xstrdup(dst);
735 tmp_dst = make_absolute(tmp_dst, pwd);
738 memset(&g, 0, sizeof(g));
739 debug3("Looking up %s", src);
740 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
741 error("File \"%s\" not found.", src);
746 /* If we aren't fetching to pwd then stash this status for later */
748 dst_is_dir = remote_is_dir(conn, tmp_dst);
750 /* If multiple matches, dst may be directory or unspecified */
751 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
752 error("Multiple paths match, but destination "
753 "\"%s\" is not a directory", tmp_dst);
758 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
759 if (stat(g.gl_pathv[i], &sb) == -1) {
761 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
765 tmp = xstrdup(g.gl_pathv[i]);
766 if ((filename = basename(tmp)) == NULL) {
767 error("basename %s: %s", tmp, strerror(errno));
773 if (g.gl_matchc == 1 && tmp_dst) {
774 /* If directory specified, append filename */
776 abs_dst = path_append(tmp_dst, filename);
778 abs_dst = xstrdup(tmp_dst);
779 } else if (tmp_dst) {
780 abs_dst = path_append(tmp_dst, filename);
782 abs_dst = make_absolute(xstrdup(filename), pwd);
786 resume |= global_aflag;
787 if (!quiet && resume)
788 mprintf("Resuming upload of %s to %s\n",
789 g.gl_pathv[i], abs_dst);
790 else if (!quiet && !resume)
791 mprintf("Uploading %s to %s\n",
792 g.gl_pathv[i], abs_dst);
793 /* XXX follow_link_flag */
794 if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
795 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
796 pflag || global_pflag, 1, resume,
797 fflag || global_fflag, 0, 0) == -1)
800 if (do_upload(conn, g.gl_pathv[i], abs_dst,
801 pflag || global_pflag, resume,
802 fflag || global_fflag, 0) == -1)
815 sdirent_comp(const void *aa, const void *bb)
817 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
818 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
819 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
821 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
822 if (sort_flag & LS_NAME_SORT)
823 return (rmul * strcmp(a->filename, b->filename));
824 else if (sort_flag & LS_TIME_SORT)
825 return (rmul * NCMP(a->a.mtime, b->a.mtime));
826 else if (sort_flag & LS_SIZE_SORT)
827 return (rmul * NCMP(a->a.size, b->a.size));
829 fatal("Unknown ls sort type");
832 /* sftp ls.1 replacement for directories */
834 do_ls_dir(struct sftp_conn *conn, const char *path,
835 const char *strip_path, int lflag)
838 u_int c = 1, colspace = 0, columns = 1;
841 if ((n = do_readdir(conn, path, &d)) != 0)
844 if (!(lflag & LS_SHORT_VIEW)) {
845 u_int m = 0, width = 80;
849 /* Count entries for sort and find longest filename */
850 for (n = 0; d[n] != NULL; n++) {
851 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
852 m = MAXIMUM(m, strlen(d[n]->filename));
855 /* Add any subpath that also needs to be counted */
856 tmp = path_strip(path, strip_path);
860 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
863 columns = width / (m + 2);
864 columns = MAXIMUM(columns, 1);
865 colspace = width / columns;
866 colspace = MINIMUM(colspace, width);
869 if (lflag & SORT_FLAGS) {
870 for (n = 0; d[n] != NULL; n++)
871 ; /* count entries */
872 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
873 qsort(d, n, sizeof(*d), sdirent_comp);
876 get_remote_user_groups_from_dirents(conn, d);
877 for (n = 0; d[n] != NULL && !interrupted; n++) {
880 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
883 tmp = path_append(path, d[n]->filename);
884 fname = path_strip(tmp, strip_path);
887 if (lflag & LS_LONG_VIEW) {
888 if ((lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) != 0 ||
889 can_get_users_groups_by_id(conn)) {
893 memset(&sb, 0, sizeof(sb));
894 attrib_to_stat(&d[n]->a, &sb);
895 lname = ls_file(fname, &sb, 1,
896 (lflag & LS_SI_UNITS),
897 ruser_name(sb.st_uid),
898 rgroup_name(sb.st_gid));
899 mprintf("%s\n", lname);
902 mprintf("%s\n", d[n]->longname);
904 mprintf("%-*s", colspace, fname);
915 if (!(lflag & LS_LONG_VIEW) && (c != 1))
918 free_sftp_dirents(d);
923 sglob_comp(const void *aa, const void *bb)
925 u_int a = *(const u_int *)aa;
926 u_int b = *(const u_int *)bb;
927 const char *ap = sort_glob->gl_pathv[a];
928 const char *bp = sort_glob->gl_pathv[b];
929 const struct stat *as = sort_glob->gl_statv[a];
930 const struct stat *bs = sort_glob->gl_statv[b];
931 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
933 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
934 if (sort_flag & LS_NAME_SORT)
935 return (rmul * strcmp(ap, bp));
936 else if (sort_flag & LS_TIME_SORT) {
937 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
938 if (timespeccmp(&as->st_mtim, &bs->st_mtim, ==))
940 return timespeccmp(&as->st_mtim, &bs->st_mtim, <) ?
942 #elif defined(HAVE_STRUCT_STAT_ST_MTIME)
943 return (rmul * NCMP(as->st_mtime, bs->st_mtime));
947 } else if (sort_flag & LS_SIZE_SORT)
948 return (rmul * NCMP(as->st_size, bs->st_size));
950 fatal("Unknown ls sort type");
953 /* sftp ls.1 replacement which handles path globs */
955 do_globbed_ls(struct sftp_conn *conn, const char *path,
956 const char *strip_path, int lflag)
962 u_int i, j, nentries, *indices = NULL, c = 1;
963 u_int colspace = 0, columns = 1, m = 0, width = 80;
965 memset(&g, 0, sizeof(g));
967 if ((r = remote_glob(conn, path,
968 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
970 (g.gl_pathc && !g.gl_matchc)) {
973 if (r == GLOB_NOSPACE) {
974 error("Can't ls: Too many matches for \"%s\"", path);
976 error("Can't ls: \"%s\" not found", path);
985 * If the glob returns a single match and it is a directory,
986 * then just list its contents.
988 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
989 S_ISDIR(g.gl_statv[0]->st_mode)) {
990 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
995 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
998 if (!(lflag & LS_SHORT_VIEW)) {
999 /* Count entries for sort and find longest filename */
1000 for (i = 0; g.gl_pathv[i]; i++)
1001 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
1003 columns = width / (m + 2);
1004 columns = MAXIMUM(columns, 1);
1005 colspace = width / columns;
1009 * Sorting: rather than mess with the contents of glob_t, prepare
1010 * an array of indices into it and sort that. For the usual
1011 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
1013 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
1014 ; /* count entries */
1015 indices = calloc(nentries, sizeof(*indices));
1016 for (i = 0; i < nentries; i++)
1019 if (lflag & SORT_FLAGS) {
1021 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
1022 qsort(indices, nentries, sizeof(*indices), sglob_comp);
1026 get_remote_user_groups_from_glob(conn, &g);
1027 for (j = 0; j < nentries && !interrupted; j++) {
1029 fname = path_strip(g.gl_pathv[i], strip_path);
1030 if (lflag & LS_LONG_VIEW) {
1031 if (g.gl_statv[i] == NULL) {
1032 error("no stat information for %s", fname);
1035 lname = ls_file(fname, g.gl_statv[i], 1,
1036 (lflag & LS_SI_UNITS),
1037 ruser_name(g.gl_statv[i]->st_uid),
1038 rgroup_name(g.gl_statv[i]->st_gid));
1039 mprintf("%s\n", lname);
1042 mprintf("%-*s", colspace, fname);
1052 if (!(lflag & LS_LONG_VIEW) && (c != 1))
1064 do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
1066 struct sftp_statvfs st;
1067 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
1068 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
1069 char s_icapacity[16], s_dcapacity[16];
1071 if (do_statvfs(conn, path, &st, 1) == -1)
1073 if (st.f_files == 0)
1074 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1076 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1077 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1080 if (st.f_blocks == 0)
1081 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1083 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1084 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1088 printf(" Inodes Used Avail "
1089 "(root) %%Capacity\n");
1090 printf("%11llu %11llu %11llu %11llu %s\n",
1091 (unsigned long long)st.f_files,
1092 (unsigned long long)(st.f_files - st.f_ffree),
1093 (unsigned long long)st.f_favail,
1094 (unsigned long long)st.f_ffree, s_icapacity);
1096 strlcpy(s_used, "error", sizeof(s_used));
1097 strlcpy(s_avail, "error", sizeof(s_avail));
1098 strlcpy(s_root, "error", sizeof(s_root));
1099 strlcpy(s_total, "error", sizeof(s_total));
1100 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1101 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1102 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1103 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1104 printf(" Size Used Avail (root) %%Capacity\n");
1105 printf("%7sB %7sB %7sB %7sB %s\n",
1106 s_total, s_used, s_avail, s_root, s_dcapacity);
1108 printf(" Size Used Avail "
1109 "(root) %%Capacity\n");
1110 printf("%12llu %12llu %12llu %12llu %s\n",
1111 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1112 (unsigned long long)(st.f_frsize *
1113 (st.f_blocks - st.f_bfree) / 1024),
1114 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1115 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1122 * Undo escaping of glob sequences in place. Used to undo extra escaping
1123 * applied in makeargv() when the string is destined for a function that
1127 undo_glob_escape(char *s)
1162 * Split a string into an argument vector using sh(1)-style quoting,
1163 * comment and escaping rules, but with some tweaks to handle glob(3)
1165 * The "sloppy" flag allows for recovery from missing terminating quote, for
1166 * use in parsing incomplete commandlines during tab autocompletion.
1168 * Returns NULL on error or a NULL-terminated array of arguments.
1170 * If "lastquote" is not NULL, the quoting character used for the last
1171 * argument is placed in *lastquote ("\0", "'" or "\"").
1173 * If "terminated" is not NULL, *terminated will be set to 1 when the
1174 * last argument's quote has been properly terminated or 0 otherwise.
1175 * This parameter is only of use if "sloppy" is set.
1178 #define MAXARGLEN 8192
1180 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1185 static char argvs[MAXARGLEN];
1186 static char *argv[MAXARGS + 1];
1187 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1190 if (strlen(arg) > sizeof(argvs) - 1) {
1192 error("string too long");
1195 if (terminated != NULL)
1197 if (lastquote != NULL)
1202 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1203 error("Too many arguments.");
1206 if (isspace((unsigned char)arg[i])) {
1207 if (state == MA_UNQUOTED) {
1208 /* Terminate current argument */
1212 } else if (state != MA_START)
1213 argvs[j++] = arg[i];
1214 } else if (arg[i] == '"' || arg[i] == '\'') {
1215 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1216 if (state == MA_START) {
1217 argv[argc] = argvs + j;
1219 if (lastquote != NULL)
1220 *lastquote = arg[i];
1221 } else if (state == MA_UNQUOTED)
1223 else if (state == q)
1224 state = MA_UNQUOTED;
1226 argvs[j++] = arg[i];
1227 } else if (arg[i] == '\\') {
1228 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1229 quot = state == MA_SQUOTE ? '\'' : '"';
1230 /* Unescape quote we are in */
1231 /* XXX support \n and friends? */
1232 if (arg[i + 1] == quot) {
1234 argvs[j++] = arg[i];
1235 } else if (arg[i + 1] == '?' ||
1236 arg[i + 1] == '[' || arg[i + 1] == '*') {
1238 * Special case for sftp: append
1239 * double-escaped glob sequence -
1240 * glob will undo one level of
1241 * escaping. NB. string can grow here.
1243 if (j >= sizeof(argvs) - 5)
1244 goto args_too_longs;
1246 argvs[j++] = arg[i++];
1248 argvs[j++] = arg[i];
1250 argvs[j++] = arg[i++];
1251 argvs[j++] = arg[i];
1254 if (state == MA_START) {
1255 argv[argc] = argvs + j;
1256 state = MA_UNQUOTED;
1257 if (lastquote != NULL)
1260 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1261 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1263 * Special case for sftp: append
1264 * escaped glob sequence -
1265 * glob will undo one level of
1268 argvs[j++] = arg[i++];
1269 argvs[j++] = arg[i];
1271 /* Unescape everything */
1272 /* XXX support \n and friends? */
1274 argvs[j++] = arg[i];
1277 } else if (arg[i] == '#') {
1278 if (state == MA_SQUOTE || state == MA_DQUOTE)
1279 argvs[j++] = arg[i];
1282 } else if (arg[i] == '\0') {
1283 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1285 state = MA_UNQUOTED;
1286 if (terminated != NULL)
1290 error("Unterminated quoted argument");
1294 if (state == MA_UNQUOTED) {
1300 if (state == MA_START) {
1301 argv[argc] = argvs + j;
1302 state = MA_UNQUOTED;
1303 if (lastquote != NULL)
1306 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1307 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1309 * Special case for sftp: escape quoted
1310 * glob(3) wildcards. NB. string can grow
1313 if (j >= sizeof(argvs) - 3)
1314 goto args_too_longs;
1316 argvs[j++] = arg[i];
1318 argvs[j++] = arg[i];
1327 parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
1328 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1329 int *rflag, int *sflag,
1330 unsigned long *n_arg, char **path1, char **path2)
1332 const char *cmd, *cp = *cpp;
1336 int path1_mandatory = 0, i, cmdnum, optidx, argc;
1338 /* Skip leading whitespace */
1339 cp = cp + strspn(cp, WHITESPACE);
1342 * Check for leading '-' (disable error processing) and '@' (suppress
1347 for (;*cp != '\0'; cp++) {
1350 } else if (*cp == '@') {
1353 /* all other characters terminate prefix processing */
1357 cp = cp + strspn(cp, WHITESPACE);
1359 /* Ignore blank lines and lines which begin with comment '#' char */
1360 if (*cp == '\0' || *cp == '#')
1363 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1366 /* Figure out which command we have */
1367 for (i = 0; cmds[i].c != NULL; i++) {
1368 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1378 } else if (cmdnum == -1) {
1379 error("Invalid command.");
1383 /* Get arguments and parse flags */
1384 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1385 *rflag = *sflag = 0;
1386 *path1 = *path2 = NULL;
1393 if ((optidx = parse_getput_flags(cmd, argv, argc,
1394 aflag, fflag, pflag, rflag)) == -1)
1396 /* Get first pathname (mandatory) */
1397 if (argc - optidx < 1) {
1398 error("You must specify at least one path after a "
1399 "%s command.", cmd);
1402 *path1 = xstrdup(argv[optidx]);
1403 /* Get second pathname (optional) */
1404 if (argc - optidx > 1) {
1405 *path2 = xstrdup(argv[optidx + 1]);
1406 /* Destination is not globbed */
1407 undo_glob_escape(*path2);
1411 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1413 goto parse_two_paths;
1415 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1417 goto parse_two_paths;
1419 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1421 goto parse_two_paths;
1423 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1426 if (argc - optidx < 2) {
1427 error("You must specify two paths after a %s "
1431 *path1 = xstrdup(argv[optidx]);
1432 *path2 = xstrdup(argv[optidx + 1]);
1433 /* Paths are not globbed */
1434 undo_glob_escape(*path1);
1435 undo_glob_escape(*path2);
1441 path1_mandatory = 1;
1445 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1447 /* Get pathname (mandatory) */
1448 if (argc - optidx < 1) {
1449 if (!path1_mandatory)
1450 break; /* return a NULL path1 */
1451 error("You must specify a path after a %s command.",
1455 *path1 = xstrdup(argv[optidx]);
1456 /* Only "rm" globs */
1458 undo_glob_escape(*path1);
1461 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1464 /* Default to current directory if no path specified */
1465 if (argc - optidx < 1)
1468 *path1 = xstrdup(argv[optidx]);
1469 undo_glob_escape(*path1);
1473 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1475 /* Path is optional */
1476 if (argc - optidx > 0)
1477 *path1 = xstrdup(argv[optidx]);
1480 /* Skip ls command and following whitespace */
1481 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1483 /* Uses the rest of the line */
1491 if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
1493 /* Get numeric arg (mandatory) */
1494 if (argc - optidx < 1)
1497 ll = strtoll(argv[optidx], &cp2, base);
1498 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1499 ((ll == LLONG_MIN || ll == LLONG_MAX) && errno == ERANGE) ||
1500 ll < 0 || ll > UINT32_MAX) {
1502 error("You must supply a numeric argument "
1503 "to the %s command.", cmd);
1507 if (cmdnum == I_LUMASK)
1509 /* Get pathname (mandatory) */
1510 if (argc - optidx < 2) {
1511 error("You must specify a path after a %s command.",
1515 *path1 = xstrdup(argv[optidx + 1]);
1523 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1527 fatal("Command not implemented");
1535 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1536 const char *startdir, int err_abort, int echo_command)
1538 const char *ocmd = cmd;
1539 char *path1, *path2, *tmp;
1540 int ignore_errors = 0, disable_echo = 1;
1541 int aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1542 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1544 unsigned long n_arg = 0;
1546 char path_buf[PATH_MAX];
1550 path1 = path2 = NULL;
1551 cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag,
1552 &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg,
1554 if (ignore_errors != 0)
1557 if (echo_command && !disable_echo)
1558 mprintf("sftp> %s\n", ocmd);
1560 memset(&g, 0, sizeof(g));
1562 /* Perform command */
1568 /* Unrecognized command */
1575 err = process_get(conn, path1, path2, *pwd, pflag,
1576 rflag, aflag, fflag);
1582 err = process_put(conn, path1, path2, *pwd, pflag,
1583 rflag, aflag, fflag);
1586 path1 = make_absolute(path1, *pwd);
1587 path2 = make_absolute(path2, *pwd);
1588 err = do_copy(conn, path1, path2);
1591 path1 = make_absolute(path1, *pwd);
1592 path2 = make_absolute(path2, *pwd);
1593 err = do_rename(conn, path1, path2, lflag);
1600 path1 = make_absolute(path1, *pwd);
1601 path2 = make_absolute(path2, *pwd);
1602 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1605 path1 = make_absolute_pwd_glob(path1, *pwd);
1606 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1607 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1609 mprintf("Removing %s\n", g.gl_pathv[i]);
1610 err = do_rm(conn, g.gl_pathv[i]);
1611 if (err != 0 && err_abort)
1616 path1 = make_absolute(path1, *pwd);
1618 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1620 err = do_mkdir(conn, path1, &a, 1);
1623 path1 = make_absolute(path1, *pwd);
1624 err = do_rmdir(conn, path1);
1627 if (path1 == NULL || *path1 == '\0')
1628 path1 = xstrdup(startdir);
1629 path1 = make_absolute(path1, *pwd);
1630 if ((tmp = do_realpath(conn, path1)) == NULL) {
1634 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1639 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1640 error("Can't change directory: Can't check target");
1645 if (!S_ISDIR(aa->perm)) {
1646 error("Can't change directory: \"%s\" is not "
1647 "a directory", tmp);
1657 do_ls_dir(conn, *pwd, *pwd, lflag);
1661 /* Strip pwd off beginning of non-absolute paths */
1663 if (!path_absolute(path1))
1666 path1 = make_absolute_pwd_glob(path1, *pwd);
1667 err = do_globbed_ls(conn, path1, tmp, lflag);
1670 /* Default to current directory if no path specified */
1672 path1 = xstrdup(*pwd);
1673 path1 = make_absolute(path1, *pwd);
1674 err = do_df(conn, path1, hflag, iflag);
1677 if (path1 == NULL || *path1 == '\0')
1678 path1 = xstrdup("~");
1679 tmp = tilde_expand_filename(path1, getuid());
1682 if (chdir(path1) == -1) {
1683 error("Couldn't change local directory to "
1684 "\"%s\": %s", path1, strerror(errno));
1689 if (mkdir(path1, 0777) == -1) {
1690 error("Couldn't create local directory "
1691 "\"%s\": %s", path1, strerror(errno));
1699 local_do_shell(cmd);
1703 printf("Local umask: %03lo\n", n_arg);
1706 path1 = make_absolute_pwd_glob(path1, *pwd);
1708 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1710 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1711 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1713 mprintf("Changing mode on %s\n",
1715 err = (hflag ? do_lsetstat : do_setstat)(conn,
1717 if (err != 0 && err_abort)
1723 path1 = make_absolute_pwd_glob(path1, *pwd);
1724 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1725 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1726 if (!(aa = (hflag ? do_lstat : do_stat)(conn,
1727 g.gl_pathv[i], 0))) {
1734 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1735 error("Can't get current ownership of "
1736 "remote file \"%s\"", g.gl_pathv[i]);
1743 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1744 if (cmdnum == I_CHOWN) {
1746 mprintf("Changing owner on %s\n",
1751 mprintf("Changing group on %s\n",
1755 err = (hflag ? do_lsetstat : do_setstat)(conn,
1757 if (err != 0 && err_abort)
1762 mprintf("Remote working directory: %s\n", *pwd);
1765 if (!getcwd(path_buf, sizeof(path_buf))) {
1766 error("Couldn't get local cwd: %s", strerror(errno));
1770 mprintf("Local working directory: %s\n", path_buf);
1773 /* Processed below */
1779 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1782 showprogress = !showprogress;
1784 printf("Progress meter enabled\n");
1786 printf("Progress meter disabled\n");
1789 fatal("%d is not implemented", cmdnum);
1797 /* If an unignored error occurs in batch mode we should abort. */
1798 if (err_abort && err != 0)
1800 else if (cmdnum == I_QUIT)
1808 prompt(EditLine *el)
1813 /* Display entries in 'list' after skipping the first 'len' chars */
1815 complete_display(char **list, u_int len)
1817 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1821 /* Count entries for sort and find longest */
1822 for (y = 0; list[y]; y++)
1823 m = MAXIMUM(m, strlen(list[y]));
1825 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1828 m = m > len ? m - len : 0;
1829 columns = width / (m + 2);
1830 columns = MAXIMUM(columns, 1);
1831 colspace = width / columns;
1832 colspace = MINIMUM(colspace, width);
1836 for (y = 0; list[y]; y++) {
1837 llen = strlen(list[y]);
1838 tmp = llen > len ? list[y] + len : "";
1839 mprintf("%-*s", colspace, tmp);
1850 * Given a "list" of words that begin with a common prefix of "word",
1851 * attempt to find an autocompletion to extends "word" by the next
1852 * characters common to all entries in "list".
1855 complete_ambiguous(const char *word, char **list, size_t count)
1861 u_int y, matchlen = strlen(list[0]);
1863 /* Find length of common stem */
1864 for (y = 1; list[y]; y++) {
1867 for (x = 0; x < matchlen; x++)
1868 if (list[0][x] != list[y][x])
1874 if (matchlen > strlen(word)) {
1875 char *tmp = xstrdup(list[0]);
1877 tmp[matchlen] = '\0';
1882 return xstrdup(word);
1885 /* Autocomplete a sftp command */
1887 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1890 u_int y, count = 0, cmdlen, tmplen;
1891 char *tmp, **list, argterm[3];
1894 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1896 /* No command specified: display all available commands */
1898 for (y = 0; cmds[y].c; y++)
1899 list[count++] = xstrdup(cmds[y].c);
1902 complete_display(list, 0);
1904 for (y = 0; list[y] != NULL; y++)
1910 /* Prepare subset of commands that start with "cmd" */
1911 cmdlen = strlen(cmd);
1912 for (y = 0; cmds[y].c; y++) {
1913 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1914 list[count++] = xstrdup(cmds[y].c);
1923 /* Complete ambiguous command */
1924 tmp = complete_ambiguous(cmd, list, count);
1926 complete_display(list, 0);
1928 for (y = 0; list[y]; y++)
1933 tmplen = strlen(tmp);
1934 cmdlen = strlen(cmd);
1935 /* If cmd may be extended then do so */
1936 if (tmplen > cmdlen)
1937 if (el_insertstr(el, tmp + cmdlen) == -1)
1938 fatal("el_insertstr failed.");
1940 /* Terminate argument cleanly */
1944 argterm[y++] = quote;
1945 if (lastarg || *(lf->cursor) != ' ')
1948 if (y > 0 && el_insertstr(el, argterm) == -1)
1949 fatal("el_insertstr failed.");
1958 * Determine whether a particular sftp command's arguments (if any) represent
1959 * local or remote files. The "cmdarg" argument specifies the actual argument
1960 * and accepts values 1 or 2.
1963 complete_is_remote(char *cmd, int cmdarg) {
1969 for (i = 0; cmds[i].c; i++) {
1970 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) {
1973 else if (cmdarg == 2)
1982 /* Autocomplete a filename "file" */
1984 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1985 char *file, int remote, int lastarg, char quote, int terminated)
1988 char *tmp, *tmp2, ins[8];
1989 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1993 /* Glob from "file" location */
1997 xasprintf(&tmp, "%s*", file);
1999 /* Check if the path is absolute. */
2000 isabs = path_absolute(tmp);
2002 memset(&g, 0, sizeof(g));
2003 if (remote != LOCAL) {
2004 tmp = make_absolute_pwd_glob(tmp, remote_path);
2005 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
2007 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
2009 /* Determine length of pwd so we can trim completion display */
2010 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
2011 /* Terminate counting on first unescaped glob metacharacter */
2012 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
2013 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
2017 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
2019 if (tmp[tmplen] == '/')
2020 pwdlen = tmplen + 1; /* track last seen '/' */
2025 if (g.gl_matchc == 0)
2028 if (g.gl_matchc > 1)
2029 complete_display(g.gl_pathv, pwdlen);
2031 /* Don't try to extend globs */
2032 if (file == NULL || hadglob)
2035 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
2036 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
2042 tmplen = strlen(tmp);
2043 filelen = strlen(file);
2045 /* Count the number of escaped characters in the input string. */
2047 for (i = 0; i < filelen; i++) {
2048 if (!isesc && file[i] == '\\' && i + 1 < filelen){
2055 if (tmplen > (filelen - cesc)) {
2056 tmp2 = tmp + filelen - cesc;
2058 /* quote argument on way out */
2059 for (i = 0; i < len; i += clen) {
2060 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
2061 (size_t)clen > sizeof(ins) - 2)
2062 fatal("invalid multibyte character");
2064 memcpy(ins + 1, tmp2 + i, clen);
2065 ins[clen + 1] = '\0';
2075 if (quote == '\0' || tmp2[i] == quote) {
2076 if (el_insertstr(el, ins) == -1)
2077 fatal("el_insertstr "
2083 if (el_insertstr(el, ins + 1) == -1)
2084 fatal("el_insertstr failed.");
2091 if (g.gl_matchc == 1) {
2093 if (!terminated && quote != '\0')
2095 if (*(lf->cursor - 1) != '/' &&
2096 (lastarg || *(lf->cursor) != ' '))
2099 if (i > 0 && el_insertstr(el, ins) == -1)
2100 fatal("el_insertstr failed.");
2109 /* tab-completion hook function, called via libedit */
2110 static unsigned char
2111 complete(EditLine *el, int ch)
2113 char **argv, *line, quote;
2115 u_int cursor, len, terminated, ret = CC_ERROR;
2117 struct complete_ctx *complete_ctx;
2120 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2121 fatal_f("el_get failed");
2123 /* Figure out which argument the cursor points to */
2124 cursor = lf->cursor - lf->buffer;
2125 line = xmalloc(cursor + 1);
2126 memcpy(line, lf->buffer, cursor);
2127 line[cursor] = '\0';
2128 argv = makeargv(line, &carg, 1, "e, &terminated);
2131 /* Get all the arguments on the line */
2132 len = lf->lastchar - lf->buffer;
2133 line = xmalloc(len + 1);
2134 memcpy(line, lf->buffer, len);
2136 argv = makeargv(line, &argc, 1, NULL, NULL);
2138 /* Ensure cursor is at EOL or a argument boundary */
2139 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2140 line[cursor] != '\n') {
2146 /* Show all available commands */
2147 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2149 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2150 /* Handle the command parsing */
2151 if (complete_cmd_parse(el, argv[0], argc == carg,
2152 quote, terminated) != 0)
2154 } else if (carg >= 1) {
2155 /* Handle file parsing */
2157 int i = 0, cmdarg = 0;
2158 char *filematch = NULL;
2160 if (carg > 1 && line[cursor-1] != ' ')
2161 filematch = argv[carg - 1];
2163 for (i = 1; i < carg; i++) {
2165 if (argv[i][0] != '-')
2170 * If previous argument is complete, then offer completion
2173 if (line[cursor - 1] == ' ')
2176 remote = complete_is_remote(argv[0], cmdarg);
2178 if ((remote == REMOTE || remote == LOCAL) &&
2179 complete_match(el, complete_ctx->conn,
2180 *complete_ctx->remote_pathp, filematch,
2181 remote, carg == argc, quote, terminated) != 0)
2188 #endif /* USE_LIBEDIT */
2191 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2194 char *dir = NULL, *startdir = NULL;
2196 int err, interactive;
2197 EditLine *el = NULL;
2201 extern char *__progname;
2202 struct complete_ctx complete_ctx;
2204 if (!batchmode && isatty(STDIN_FILENO)) {
2205 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2206 fatal("Couldn't initialise editline");
2207 if ((hl = history_init()) == NULL)
2208 fatal("Couldn't initialise editline history");
2209 history(hl, &hev, H_SETSIZE, 100);
2210 el_set(el, EL_HIST, history, hl);
2212 el_set(el, EL_PROMPT, prompt);
2213 el_set(el, EL_EDITOR, "emacs");
2214 el_set(el, EL_TERMINAL, NULL);
2215 el_set(el, EL_SIGNAL, 1);
2216 el_source(el, NULL);
2218 /* Tab Completion */
2219 el_set(el, EL_ADDFN, "ftp-complete",
2220 "Context sensitive argument completion", complete);
2221 complete_ctx.conn = conn;
2222 complete_ctx.remote_pathp = &remote_path;
2223 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2224 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2225 /* enable ctrl-left-arrow and ctrl-right-arrow */
2226 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2227 el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL);
2228 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2229 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2230 /* make ^w match ksh behaviour */
2231 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2233 #endif /* USE_LIBEDIT */
2235 remote_path = do_realpath(conn, ".");
2236 if (remote_path == NULL)
2238 startdir = xstrdup(remote_path);
2240 if (file1 != NULL) {
2241 dir = xstrdup(file1);
2242 dir = make_absolute(dir, remote_path);
2244 if (remote_is_dir(conn, dir) && file2 == NULL) {
2246 mprintf("Changing to: %s\n", dir);
2247 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2248 if (parse_dispatch_command(conn, cmd,
2249 &remote_path, startdir, 1, 0) != 0) {
2257 /* XXX this is wrong wrt quoting */
2258 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2259 global_aflag ? " -a" : "", dir,
2260 file2 == NULL ? "" : " ",
2261 file2 == NULL ? "" : file2);
2262 err = parse_dispatch_command(conn, cmd,
2263 &remote_path, startdir, 1, 0);
2273 setvbuf(stdout, NULL, _IOLBF, 0);
2274 setvbuf(infile, NULL, _IOLBF, 0);
2276 interactive = !batchmode && isatty(STDIN_FILENO);
2279 struct sigaction sa;
2282 memset(&sa, 0, sizeof(sa));
2283 sa.sa_handler = interactive ? read_interrupt : killchild;
2284 if (sigaction(SIGINT, &sa, NULL) == -1) {
2285 debug3("sigaction(%s): %s", strsignal(SIGINT),
2292 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2304 if ((line = el_gets(el, &count)) == NULL ||
2311 history(hl, &hev, H_ENTER, line);
2312 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2313 fprintf(stderr, "Error: input line too long\n");
2316 #endif /* USE_LIBEDIT */
2319 cmd[strcspn(cmd, "\n")] = '\0';
2321 /* Handle user interrupts gracefully during commands */
2323 ssh_signal(SIGINT, cmd_interrupt);
2325 err = parse_dispatch_command(conn, cmd, &remote_path,
2326 startdir, batchmode, !interactive && el == NULL);
2330 ssh_signal(SIGCHLD, SIG_DFL);
2338 #endif /* USE_LIBEDIT */
2340 /* err == 1 signifies normal "quit" exit */
2341 return (err >= 0 ? 0 : -1);
2345 connect_to_server(char *path, char **args, int *in, int *out)
2349 int pin[2], pout[2];
2351 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2352 fatal("pipe: %s", strerror(errno));
2357 #else /* USE_PIPES */
2360 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2361 fatal("socketpair: %s", strerror(errno));
2362 *in = *out = inout[0];
2363 c_in = c_out = inout[1];
2364 #endif /* USE_PIPES */
2366 if ((sshpid = fork()) == -1)
2367 fatal("fork: %s", strerror(errno));
2368 else if (sshpid == 0) {
2369 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2370 (dup2(c_out, STDOUT_FILENO) == -1)) {
2371 fprintf(stderr, "dup2: %s\n", strerror(errno));
2380 * The underlying ssh is in the same process group, so we must
2381 * ignore SIGINT if we want to gracefully abort commands,
2382 * otherwise the signal will make it to the ssh process and
2383 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2384 * underlying ssh, it must *not* ignore that signal.
2386 ssh_signal(SIGINT, SIG_IGN);
2387 ssh_signal(SIGTERM, SIG_DFL);
2389 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2393 ssh_signal(SIGTERM, killchild);
2394 ssh_signal(SIGINT, killchild);
2395 ssh_signal(SIGHUP, killchild);
2396 ssh_signal(SIGTSTP, suspchild);
2397 ssh_signal(SIGTTIN, suspchild);
2398 ssh_signal(SIGTTOU, suspchild);
2399 ssh_signal(SIGCHLD, sigchld_handler);
2407 extern char *__progname;
2410 "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2411 " [-D sftp_server_command] [-F ssh_config] [-i identity_file]\n"
2412 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
2413 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
2414 " [-X sftp_option] destination\n",
2420 main(int argc, char **argv)
2422 int r, in, out, ch, err, tmp, port = -1, noisy = 0;
2423 char *host = NULL, *user, *cp, **cpp, *file2 = NULL;
2424 int debug_level = 0;
2425 char *file1 = NULL, *sftp_server = NULL;
2426 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2428 LogLevel ll = SYSLOG_LEVEL_INFO;
2431 extern char *optarg;
2432 struct sftp_conn *conn;
2433 size_t copy_buffer_len = 0;
2434 size_t num_requests = 0;
2435 long long llv, limit_kbps = 0;
2437 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2441 __progname = ssh_get_progname(argv[0]);
2442 memset(&args, '\0', sizeof(args));
2444 addargs(&args, "%s", ssh_program);
2445 addargs(&args, "-oForwardX11 no");
2446 addargs(&args, "-oPermitLocalCommand no");
2447 addargs(&args, "-oClearAllForwardings yes");
2449 ll = SYSLOG_LEVEL_INFO;
2452 while ((ch = getopt(argc, argv,
2453 "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:X:")) != -1) {
2455 /* Passed through to ssh(1) */
2460 addargs(&args, "-%c", ch);
2462 /* Passed through to ssh(1) with argument */
2468 addargs(&args, "-%c", ch);
2469 addargs(&args, "%s", optarg);
2472 ll = SYSLOG_LEVEL_ERROR;
2475 addargs(&args, "-%c", ch);
2478 port = a2port(optarg);
2480 fatal("Bad port \"%s\"\n", optarg);
2483 if (debug_level < 3) {
2484 addargs(&args, "-v");
2485 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2490 fatal("SSH protocol v.1 is no longer supported");
2493 /* accept silently */
2499 copy_buffer_len = strtol(optarg, &cp, 10);
2500 if (copy_buffer_len == 0 || *cp != '\0')
2501 fatal("Invalid buffer size \"%s\"", optarg);
2505 fatal("Batch file already specified.");
2507 /* Allow "-" as stdin */
2508 if (strcmp(optarg, "-") != 0 &&
2509 (infile = fopen(optarg, "r")) == NULL)
2510 fatal("%s (%s).", strerror(errno), optarg);
2512 quiet = batchmode = 1;
2513 addargs(&args, "-obatchmode yes");
2519 noisy = 1; /* Used to clear quiet mode after getopt */
2525 sftp_direct = optarg;
2528 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2532 limit_kbps *= 1024; /* kbps */
2538 num_requests = strtol(optarg, &cp, 10);
2539 if (num_requests == 0 || *cp != '\0')
2540 fatal("Invalid number of requests \"%s\"",
2544 sftp_server = optarg;
2547 ssh_program = optarg;
2548 replacearg(&args, 0, "%s", ssh_program);
2551 /* Please keep in sync with ssh.c -X */
2552 if (strncmp(optarg, "buffer=", 7) == 0) {
2553 r = scan_scaled(optarg + 7, &llv);
2554 if (r == 0 && (llv <= 0 || llv > 256 * 1024)) {
2559 fatal("Invalid buffer size \"%s\": %s",
2560 optarg + 7, strerror(errno));
2562 copy_buffer_len = (size_t)llv;
2563 } else if (strncmp(optarg, "nrequests=", 10) == 0) {
2564 llv = strtonum(optarg + 10, 1, 256 * 1024,
2566 if (errstr != NULL) {
2567 fatal("Invalid number of requests "
2568 "\"%s\": %s", optarg + 10, errstr);
2570 num_requests = (size_t)llv;
2572 fatal("Invalid -X option");
2581 /* Do this last because we want the user to be able to override it */
2582 addargs(&args, "-oForwardAgent no");
2584 if (!isatty(STDERR_FILENO))
2590 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2592 if (sftp_direct == NULL) {
2593 if (optind == argc || argc > (optind + 2))
2597 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2606 /* Try with user, host and path. */
2607 if (parse_user_host_path(*argv, &user, &host,
2610 /* Try with user and host. */
2611 if (parse_user_host_port(*argv, &user, &host, NULL)
2614 /* Treat as a plain hostname. */
2615 host = xstrdup(*argv);
2616 host = cleanhostname(host);
2619 file2 = *(argv + 1);
2622 fprintf(stderr, "Missing hostname\n");
2627 addargs(&args, "-oPort %d", port);
2629 addargs(&args, "-l");
2630 addargs(&args, "%s", user);
2633 /* no subsystem if the server-spec contains a '/' */
2634 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2635 addargs(&args, "-s");
2637 addargs(&args, "--");
2638 addargs(&args, "%s", host);
2639 addargs(&args, "%s", (sftp_server != NULL ?
2640 sftp_server : "sftp"));
2642 connect_to_server(ssh_program, args.list, &in, &out);
2644 if ((r = argv_split(sftp_direct, &tmp, &cpp, 1)) != 0)
2645 fatal_r(r, "Parse -D arguments");
2647 fatal("No sftp server specified via -D");
2648 connect_to_server(cpp[0], cpp, &in, &out);
2649 argv_free(cpp, tmp);
2653 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2655 fatal("Couldn't initialise connection to server");
2658 if (sftp_direct == NULL)
2659 fprintf(stderr, "Connected to %s.\n", host);
2661 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2664 err = interactive_loop(conn, file1, file2);
2666 #if !defined(USE_PIPES)
2667 shutdown(in, SHUT_RDWR);
2668 shutdown(out, SHUT_RDWR);
2676 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
2678 fatal("Couldn't wait for ssh process: %s",
2681 exit(err == 0 ? 0 : 1);