1 /* $OpenBSD: sftp.c,v 1.234 2023/04/12 08:53:54 jsg 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 },
228 (void)waitpid(pid, NULL, 0);
239 while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR)
242 kill(getpid(), SIGSTOP);
246 cmd_interrupt(int signo)
248 const char msg[] = "\rInterrupt \n";
249 int olderrno = errno;
251 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
257 read_interrupt(int signo)
263 sigchld_handler(int sig)
265 int save_errno = errno;
267 const char msg[] = "\rConnection closed. \n";
269 /* Report if ssh transport process dies. */
270 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 "copy oldpath newpath Copy remote file\n"
291 "cp oldpath newpath Copy remote file\n"
292 "df [-hi] [path] Display statistics for current directory or\n"
293 " filesystem containing 'path'\n"
295 "get [-afpR] remote [local] Download file\n"
296 "help Display this help text\n"
297 "lcd path Change local directory to 'path'\n"
298 "lls [ls-options [path]] Display local directory listing\n"
299 "lmkdir path Create local directory\n"
300 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
301 "lpwd Print local working directory\n"
302 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
303 "lumask umask Set local umask to 'umask'\n"
304 "mkdir path Create remote directory\n"
305 "progress Toggle display of progress meter\n"
306 "put [-afpR] local [remote] Upload file\n"
307 "pwd Display remote working directory\n"
309 "reget [-fpR] remote [local] Resume download file\n"
310 "rename oldpath newpath Rename remote file\n"
311 "reput [-fpR] local [remote] Resume upload file\n"
312 "rm path Delete remote file\n"
313 "rmdir path Remove remote directory\n"
314 "symlink oldpath newpath Symlink remote file\n"
315 "version Show SFTP version\n"
316 "!command Execute 'command' in local shell\n"
317 "! Escape to local shell\n"
318 "? Synonym for help\n");
322 local_do_shell(const char *args)
331 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
332 shell = _PATH_BSHELL;
334 if ((pid = fork()) == -1)
335 fatal("Couldn't fork: %s", strerror(errno));
338 /* XXX: child has pipe fds to ssh subproc open - issue? */
340 debug3("Executing %s -c \"%s\"", shell, args);
341 execl(shell, shell, "-c", args, (char *)NULL);
343 debug3("Executing %s", shell);
344 execl(shell, shell, (char *)NULL);
346 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
350 while (waitpid(pid, &status, 0) == -1)
352 fatal("Couldn't wait for child: %s", strerror(errno));
353 if (!WIFEXITED(status))
354 error("Shell exited abnormally");
355 else if (WEXITSTATUS(status))
356 error("Shell exited with status %d", WEXITSTATUS(status));
360 local_do_ls(const char *args)
363 local_do_shell(_PATH_LS);
365 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
366 char *buf = xmalloc(len);
368 /* XXX: quoting - rip quoting code from ftp? */
369 snprintf(buf, len, _PATH_LS " %s", args);
375 /* Strip one path (usually the pwd) from the start of another */
377 path_strip(const char *path, const char *strip)
382 return (xstrdup(path));
385 if (strncmp(path, strip, len) == 0) {
386 if (strip[len - 1] != '/' && path[len] == '/')
388 return (xstrdup(path + len));
391 return (xstrdup(path));
395 parse_getput_flags(const char *cmd, char **argv, int argc,
396 int *aflag, int *fflag, int *pflag, int *rflag)
398 extern int opterr, optind, optopt, optreset;
401 optind = optreset = 1;
404 *aflag = *fflag = *rflag = *pflag = 0;
405 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
422 error("%s: Invalid flag -%c", cmd, optopt);
431 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
433 extern int opterr, optind, optopt, optreset;
436 optind = optreset = 1;
440 while ((ch = getopt(argc, argv, "s")) != -1) {
446 error("%s: Invalid flag -%c", cmd, optopt);
455 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
457 extern int opterr, optind, optopt, optreset;
460 optind = optreset = 1;
464 while ((ch = getopt(argc, argv, "l")) != -1) {
470 error("%s: Invalid flag -%c", cmd, optopt);
479 parse_ls_flags(char **argv, int argc, int *lflag)
481 extern int opterr, optind, optopt, optreset;
484 optind = optreset = 1;
487 *lflag = LS_NAME_SORT;
488 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
491 *lflag &= ~VIEW_FLAGS;
492 *lflag |= LS_SHORT_VIEW;
495 *lflag &= ~SORT_FLAGS;
496 *lflag |= LS_SIZE_SORT;
499 *lflag |= LS_SHOW_ALL;
502 *lflag &= ~SORT_FLAGS;
505 *lflag |= LS_SI_UNITS;
508 *lflag &= ~LS_SHORT_VIEW;
509 *lflag |= LS_LONG_VIEW;
512 *lflag &= ~LS_SHORT_VIEW;
513 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
516 *lflag |= LS_REVERSE_SORT;
519 *lflag &= ~SORT_FLAGS;
520 *lflag |= LS_TIME_SORT;
523 error("ls: Invalid flag -%c", optopt);
532 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
534 extern int opterr, optind, optopt, optreset;
537 optind = optreset = 1;
541 while ((ch = getopt(argc, argv, "hi")) != -1) {
550 error("%s: Invalid flag -%c", cmd, optopt);
559 parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag)
561 extern int opterr, optind, optopt, optreset;
564 optind = optreset = 1;
568 while ((ch = getopt(argc, argv, "h")) != -1) {
574 error("%s: Invalid flag -%c", cmd, optopt);
583 parse_no_flags(const char *cmd, char **argv, int argc)
585 extern int opterr, optind, optopt, optreset;
588 optind = optreset = 1;
591 while ((ch = getopt(argc, argv, "")) != -1) {
594 error("%s: Invalid flag -%c", cmd, optopt);
603 escape_glob(const char *s)
609 ret = xcalloc(2, len + 1);
610 for (i = o = 0; i < len; i++) {
611 if (strchr("[]?*\\", s[i]) != NULL)
620 * Arg p must be dynamically allocated. make_absolute will either return it
621 * or free it and allocate a new one. Caller must free returned string.
624 make_absolute_pwd_glob(char *p, const char *pwd)
628 escpwd = escape_glob(pwd);
631 ret = make_absolute(p, escpwd);
637 process_get(struct sftp_conn *conn, const char *src, const char *dst,
638 const char *pwd, int pflag, int rflag, int resume, int fflag)
640 char *filename, *abs_src = NULL, *abs_dst = NULL, *tmp = NULL;
644 abs_src = make_absolute_pwd_glob(xstrdup(src), pwd);
645 memset(&g, 0, sizeof(g));
647 debug3("Looking up %s", abs_src);
648 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
649 if (r == GLOB_NOSPACE) {
650 error("Too many matches for \"%s\".", abs_src);
652 error("File \"%s\" not found.", abs_src);
659 * If multiple matches then dst must be a directory or
662 if (g.gl_matchc > 1 && dst != NULL && !local_is_dir(dst)) {
663 error("Multiple source paths, but destination "
664 "\"%s\" is not a directory", dst);
669 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
670 tmp = xstrdup(g.gl_pathv[i]);
671 if ((filename = basename(tmp)) == NULL) {
672 error("basename %s: %s", tmp, strerror(errno));
678 if (g.gl_matchc == 1 && dst) {
679 if (local_is_dir(dst)) {
680 abs_dst = path_append(dst, filename);
682 abs_dst = xstrdup(dst);
685 abs_dst = path_append(dst, filename);
687 abs_dst = xstrdup(filename);
691 resume |= global_aflag;
692 if (!quiet && resume)
693 mprintf("Resuming %s to %s\n",
694 g.gl_pathv[i], abs_dst);
695 else if (!quiet && !resume)
696 mprintf("Fetching %s to %s\n",
697 g.gl_pathv[i], abs_dst);
698 /* XXX follow link flag */
699 if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
700 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
701 pflag || global_pflag, 1, resume,
702 fflag || global_fflag, 0, 0) == -1)
705 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
706 pflag || global_pflag, resume,
707 fflag || global_fflag, 0) == -1)
721 process_put(struct sftp_conn *conn, const char *src, const char *dst,
722 const char *pwd, int pflag, int rflag, int resume, int fflag)
724 char *tmp_dst = NULL;
725 char *abs_dst = NULL;
726 char *tmp = NULL, *filename = NULL;
729 int i, dst_is_dir = 1;
733 tmp_dst = xstrdup(dst);
734 tmp_dst = make_absolute(tmp_dst, pwd);
737 memset(&g, 0, sizeof(g));
738 debug3("Looking up %s", src);
739 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
740 error("File \"%s\" not found.", src);
745 /* If we aren't fetching to pwd then stash this status for later */
747 dst_is_dir = remote_is_dir(conn, tmp_dst);
749 /* If multiple matches, dst may be directory or unspecified */
750 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
751 error("Multiple paths match, but destination "
752 "\"%s\" is not a directory", tmp_dst);
757 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
758 if (stat(g.gl_pathv[i], &sb) == -1) {
760 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
764 tmp = xstrdup(g.gl_pathv[i]);
765 if ((filename = basename(tmp)) == NULL) {
766 error("basename %s: %s", tmp, strerror(errno));
774 if (g.gl_matchc == 1 && tmp_dst) {
775 /* If directory specified, append filename */
777 abs_dst = path_append(tmp_dst, filename);
779 abs_dst = xstrdup(tmp_dst);
780 } else if (tmp_dst) {
781 abs_dst = path_append(tmp_dst, filename);
783 abs_dst = make_absolute(xstrdup(filename), pwd);
787 resume |= global_aflag;
788 if (!quiet && resume)
789 mprintf("Resuming upload of %s to %s\n",
790 g.gl_pathv[i], abs_dst);
791 else if (!quiet && !resume)
792 mprintf("Uploading %s to %s\n",
793 g.gl_pathv[i], abs_dst);
794 /* XXX follow_link_flag */
795 if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
796 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
797 pflag || global_pflag, 1, resume,
798 fflag || global_fflag, 0, 0) == -1)
801 if (do_upload(conn, g.gl_pathv[i], abs_dst,
802 pflag || global_pflag, resume,
803 fflag || global_fflag, 0) == -1)
816 sdirent_comp(const void *aa, const void *bb)
818 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
819 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
820 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
822 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
823 if (sort_flag & LS_NAME_SORT)
824 return (rmul * strcmp(a->filename, b->filename));
825 else if (sort_flag & LS_TIME_SORT)
826 return (rmul * NCMP(a->a.mtime, b->a.mtime));
827 else if (sort_flag & LS_SIZE_SORT)
828 return (rmul * NCMP(a->a.size, b->a.size));
830 fatal("Unknown ls sort type");
833 /* sftp ls.1 replacement for directories */
835 do_ls_dir(struct sftp_conn *conn, const char *path,
836 const char *strip_path, int lflag)
839 u_int c = 1, colspace = 0, columns = 1;
842 if ((n = do_readdir(conn, path, &d)) != 0)
845 if (!(lflag & LS_SHORT_VIEW)) {
846 u_int m = 0, width = 80;
850 /* Count entries for sort and find longest filename */
851 for (n = 0; d[n] != NULL; n++) {
852 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
853 m = MAXIMUM(m, strlen(d[n]->filename));
856 /* Add any subpath that also needs to be counted */
857 tmp = path_strip(path, strip_path);
861 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
864 columns = width / (m + 2);
865 columns = MAXIMUM(columns, 1);
866 colspace = width / columns;
867 colspace = MINIMUM(colspace, width);
870 if (lflag & SORT_FLAGS) {
871 for (n = 0; d[n] != NULL; n++)
872 ; /* count entries */
873 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
874 qsort(d, n, sizeof(*d), sdirent_comp);
877 get_remote_user_groups_from_dirents(conn, d);
878 for (n = 0; d[n] != NULL && !interrupted; n++) {
881 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
884 tmp = path_append(path, d[n]->filename);
885 fname = path_strip(tmp, strip_path);
888 if (lflag & LS_LONG_VIEW) {
889 if ((lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) != 0 ||
890 can_get_users_groups_by_id(conn)) {
894 memset(&sb, 0, sizeof(sb));
895 attrib_to_stat(&d[n]->a, &sb);
896 lname = ls_file(fname, &sb, 1,
897 (lflag & LS_SI_UNITS),
898 ruser_name(sb.st_uid),
899 rgroup_name(sb.st_gid));
900 mprintf("%s\n", lname);
903 mprintf("%s\n", d[n]->longname);
905 mprintf("%-*s", colspace, fname);
916 if (!(lflag & LS_LONG_VIEW) && (c != 1))
919 free_sftp_dirents(d);
924 sglob_comp(const void *aa, const void *bb)
926 u_int a = *(const u_int *)aa;
927 u_int b = *(const u_int *)bb;
928 const char *ap = sort_glob->gl_pathv[a];
929 const char *bp = sort_glob->gl_pathv[b];
930 const struct stat *as = sort_glob->gl_statv[a];
931 const struct stat *bs = sort_glob->gl_statv[b];
932 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
934 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
935 if (sort_flag & LS_NAME_SORT)
936 return (rmul * strcmp(ap, bp));
937 else if (sort_flag & LS_TIME_SORT) {
938 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
939 if (timespeccmp(&as->st_mtim, &bs->st_mtim, ==))
941 return timespeccmp(&as->st_mtim, &bs->st_mtim, <) ?
943 #elif defined(HAVE_STRUCT_STAT_ST_MTIME)
944 return (rmul * NCMP(as->st_mtime, bs->st_mtime));
948 } else if (sort_flag & LS_SIZE_SORT)
949 return (rmul * NCMP(as->st_size, bs->st_size));
951 fatal("Unknown ls sort type");
954 /* sftp ls.1 replacement which handles path globs */
956 do_globbed_ls(struct sftp_conn *conn, const char *path,
957 const char *strip_path, int lflag)
963 u_int i, j, nentries, *indices = NULL, c = 1;
964 u_int colspace = 0, columns = 1, m = 0, width = 80;
966 memset(&g, 0, sizeof(g));
968 if ((r = remote_glob(conn, path,
969 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
971 (g.gl_pathc && !g.gl_matchc)) {
974 if (r == GLOB_NOSPACE) {
975 error("Can't ls: Too many matches for \"%s\"", path);
977 error("Can't ls: \"%s\" not found", path);
986 * If the glob returns a single match and it is a directory,
987 * then just list its contents.
989 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
990 S_ISDIR(g.gl_statv[0]->st_mode)) {
991 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
996 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
999 if (!(lflag & LS_SHORT_VIEW)) {
1000 /* Count entries for sort and find longest filename */
1001 for (i = 0; g.gl_pathv[i]; i++)
1002 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
1004 columns = width / (m + 2);
1005 columns = MAXIMUM(columns, 1);
1006 colspace = width / columns;
1010 * Sorting: rather than mess with the contents of glob_t, prepare
1011 * an array of indices into it and sort that. For the usual
1012 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
1014 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
1015 ; /* count entries */
1016 indices = xcalloc(nentries, sizeof(*indices));
1017 for (i = 0; i < nentries; i++)
1020 if (lflag & SORT_FLAGS) {
1022 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
1023 qsort(indices, nentries, sizeof(*indices), sglob_comp);
1027 get_remote_user_groups_from_glob(conn, &g);
1028 for (j = 0; j < nentries && !interrupted; j++) {
1030 fname = path_strip(g.gl_pathv[i], strip_path);
1031 if (lflag & LS_LONG_VIEW) {
1032 if (g.gl_statv[i] == NULL) {
1033 error("no stat information for %s", fname);
1037 lname = ls_file(fname, g.gl_statv[i], 1,
1038 (lflag & LS_SI_UNITS),
1039 ruser_name(g.gl_statv[i]->st_uid),
1040 rgroup_name(g.gl_statv[i]->st_gid));
1041 mprintf("%s\n", lname);
1044 mprintf("%-*s", colspace, fname);
1054 if (!(lflag & LS_LONG_VIEW) && (c != 1))
1066 do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
1068 struct sftp_statvfs st;
1069 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
1070 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
1071 char s_icapacity[16], s_dcapacity[16];
1073 if (do_statvfs(conn, path, &st, 1) == -1)
1075 if (st.f_files == 0)
1076 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1078 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1079 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1082 if (st.f_blocks == 0)
1083 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1085 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1086 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1090 printf(" Inodes Used Avail "
1091 "(root) %%Capacity\n");
1092 printf("%11llu %11llu %11llu %11llu %s\n",
1093 (unsigned long long)st.f_files,
1094 (unsigned long long)(st.f_files - st.f_ffree),
1095 (unsigned long long)st.f_favail,
1096 (unsigned long long)st.f_ffree, s_icapacity);
1098 strlcpy(s_used, "error", sizeof(s_used));
1099 strlcpy(s_avail, "error", sizeof(s_avail));
1100 strlcpy(s_root, "error", sizeof(s_root));
1101 strlcpy(s_total, "error", sizeof(s_total));
1102 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1103 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1104 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1105 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1106 printf(" Size Used Avail (root) %%Capacity\n");
1107 printf("%7sB %7sB %7sB %7sB %s\n",
1108 s_total, s_used, s_avail, s_root, s_dcapacity);
1110 printf(" Size Used Avail "
1111 "(root) %%Capacity\n");
1112 printf("%12llu %12llu %12llu %12llu %s\n",
1113 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1114 (unsigned long long)(st.f_frsize *
1115 (st.f_blocks - st.f_bfree) / 1024),
1116 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1117 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1124 * Undo escaping of glob sequences in place. Used to undo extra escaping
1125 * applied in makeargv() when the string is destined for a function that
1129 undo_glob_escape(char *s)
1164 * Split a string into an argument vector using sh(1)-style quoting,
1165 * comment and escaping rules, but with some tweaks to handle glob(3)
1167 * The "sloppy" flag allows for recovery from missing terminating quote, for
1168 * use in parsing incomplete commandlines during tab autocompletion.
1170 * Returns NULL on error or a NULL-terminated array of arguments.
1172 * If "lastquote" is not NULL, the quoting character used for the last
1173 * argument is placed in *lastquote ("\0", "'" or "\"").
1175 * If "terminated" is not NULL, *terminated will be set to 1 when the
1176 * last argument's quote has been properly terminated or 0 otherwise.
1177 * This parameter is only of use if "sloppy" is set.
1180 #define MAXARGLEN 8192
1182 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1187 static char argvs[MAXARGLEN];
1188 static char *argv[MAXARGS + 1];
1189 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1192 if (strlen(arg) > sizeof(argvs) - 1) {
1194 error("string too long");
1197 if (terminated != NULL)
1199 if (lastquote != NULL)
1204 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1205 error("Too many arguments.");
1208 if (isspace((unsigned char)arg[i])) {
1209 if (state == MA_UNQUOTED) {
1210 /* Terminate current argument */
1214 } else if (state != MA_START)
1215 argvs[j++] = arg[i];
1216 } else if (arg[i] == '"' || arg[i] == '\'') {
1217 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1218 if (state == MA_START) {
1219 argv[argc] = argvs + j;
1221 if (lastquote != NULL)
1222 *lastquote = arg[i];
1223 } else if (state == MA_UNQUOTED)
1225 else if (state == q)
1226 state = MA_UNQUOTED;
1228 argvs[j++] = arg[i];
1229 } else if (arg[i] == '\\') {
1230 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1231 quot = state == MA_SQUOTE ? '\'' : '"';
1232 /* Unescape quote we are in */
1233 /* XXX support \n and friends? */
1234 if (arg[i + 1] == quot) {
1236 argvs[j++] = arg[i];
1237 } else if (arg[i + 1] == '?' ||
1238 arg[i + 1] == '[' || arg[i + 1] == '*') {
1240 * Special case for sftp: append
1241 * double-escaped glob sequence -
1242 * glob will undo one level of
1243 * escaping. NB. string can grow here.
1245 if (j >= sizeof(argvs) - 5)
1246 goto args_too_longs;
1248 argvs[j++] = arg[i++];
1250 argvs[j++] = arg[i];
1252 argvs[j++] = arg[i++];
1253 argvs[j++] = arg[i];
1256 if (state == MA_START) {
1257 argv[argc] = argvs + j;
1258 state = MA_UNQUOTED;
1259 if (lastquote != NULL)
1262 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1263 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1265 * Special case for sftp: append
1266 * escaped glob sequence -
1267 * glob will undo one level of
1270 argvs[j++] = arg[i++];
1271 argvs[j++] = arg[i];
1273 /* Unescape everything */
1274 /* XXX support \n and friends? */
1276 argvs[j++] = arg[i];
1279 } else if (arg[i] == '#') {
1280 if (state == MA_SQUOTE || state == MA_DQUOTE)
1281 argvs[j++] = arg[i];
1284 } else if (arg[i] == '\0') {
1285 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1287 state = MA_UNQUOTED;
1288 if (terminated != NULL)
1292 error("Unterminated quoted argument");
1296 if (state == MA_UNQUOTED) {
1302 if (state == MA_START) {
1303 argv[argc] = argvs + j;
1304 state = MA_UNQUOTED;
1305 if (lastquote != NULL)
1308 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1309 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1311 * Special case for sftp: escape quoted
1312 * glob(3) wildcards. NB. string can grow
1315 if (j >= sizeof(argvs) - 3)
1316 goto args_too_longs;
1318 argvs[j++] = arg[i];
1320 argvs[j++] = arg[i];
1329 parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
1330 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1331 int *rflag, int *sflag,
1332 unsigned long *n_arg, char **path1, char **path2)
1334 const char *cmd, *cp = *cpp;
1338 int path1_mandatory = 0, i, cmdnum, optidx, argc;
1340 /* Skip leading whitespace */
1341 cp = cp + strspn(cp, WHITESPACE);
1344 * Check for leading '-' (disable error processing) and '@' (suppress
1349 for (;*cp != '\0'; cp++) {
1352 } else if (*cp == '@') {
1355 /* all other characters terminate prefix processing */
1359 cp = cp + strspn(cp, WHITESPACE);
1361 /* Ignore blank lines and lines which begin with comment '#' char */
1362 if (*cp == '\0' || *cp == '#')
1365 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1368 /* Figure out which command we have */
1369 for (i = 0; cmds[i].c != NULL; i++) {
1370 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1380 } else if (cmdnum == -1) {
1381 error("Invalid command.");
1385 /* Get arguments and parse flags */
1386 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1387 *rflag = *sflag = 0;
1388 *path1 = *path2 = NULL;
1395 if ((optidx = parse_getput_flags(cmd, argv, argc,
1396 aflag, fflag, pflag, rflag)) == -1)
1398 /* Get first pathname (mandatory) */
1399 if (argc - optidx < 1) {
1400 error("You must specify at least one path after a "
1401 "%s command.", cmd);
1404 *path1 = xstrdup(argv[optidx]);
1405 /* Get second pathname (optional) */
1406 if (argc - optidx > 1) {
1407 *path2 = xstrdup(argv[optidx + 1]);
1408 /* Destination is not globbed */
1409 undo_glob_escape(*path2);
1413 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1415 goto parse_two_paths;
1417 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1419 goto parse_two_paths;
1421 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1423 goto parse_two_paths;
1425 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1428 if (argc - optidx < 2) {
1429 error("You must specify two paths after a %s "
1433 *path1 = xstrdup(argv[optidx]);
1434 *path2 = xstrdup(argv[optidx + 1]);
1435 /* Paths are not globbed */
1436 undo_glob_escape(*path1);
1437 undo_glob_escape(*path2);
1443 path1_mandatory = 1;
1447 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1449 /* Get pathname (mandatory) */
1450 if (argc - optidx < 1) {
1451 if (!path1_mandatory)
1452 break; /* return a NULL path1 */
1453 error("You must specify a path after a %s command.",
1457 *path1 = xstrdup(argv[optidx]);
1458 /* Only "rm" globs */
1460 undo_glob_escape(*path1);
1463 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1466 /* Default to current directory if no path specified */
1467 if (argc - optidx < 1)
1470 *path1 = xstrdup(argv[optidx]);
1471 undo_glob_escape(*path1);
1475 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1477 /* Path is optional */
1478 if (argc - optidx > 0)
1479 *path1 = xstrdup(argv[optidx]);
1482 /* Skip ls command and following whitespace */
1483 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1485 /* Uses the rest of the line */
1493 if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
1495 /* Get numeric arg (mandatory) */
1496 if (argc - optidx < 1)
1499 ll = strtoll(argv[optidx], &cp2, base);
1500 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1501 ((ll == LLONG_MIN || ll == LLONG_MAX) && errno == ERANGE) ||
1502 ll < 0 || ll > UINT32_MAX) {
1504 error("You must supply a numeric argument "
1505 "to the %s command.", cmd);
1509 if (cmdnum == I_LUMASK)
1511 /* Get pathname (mandatory) */
1512 if (argc - optidx < 2) {
1513 error("You must specify a path after a %s command.",
1517 *path1 = xstrdup(argv[optidx + 1]);
1525 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1529 fatal("Command not implemented");
1537 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1538 const char *startdir, int err_abort, int echo_command)
1540 const char *ocmd = cmd;
1541 char *path1, *path2, *tmp;
1542 int ignore_errors = 0, disable_echo = 1;
1543 int aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1544 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1546 unsigned long n_arg = 0;
1548 char path_buf[PATH_MAX];
1552 path1 = path2 = NULL;
1553 cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag,
1554 &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg,
1556 if (ignore_errors != 0)
1559 if (echo_command && !disable_echo)
1560 mprintf("sftp> %s\n", ocmd);
1562 memset(&g, 0, sizeof(g));
1564 /* Perform command */
1570 /* Unrecognized command */
1577 err = process_get(conn, path1, path2, *pwd, pflag,
1578 rflag, aflag, fflag);
1584 err = process_put(conn, path1, path2, *pwd, pflag,
1585 rflag, aflag, fflag);
1588 path1 = make_absolute(path1, *pwd);
1589 path2 = make_absolute(path2, *pwd);
1590 err = do_copy(conn, path1, path2);
1593 path1 = make_absolute(path1, *pwd);
1594 path2 = make_absolute(path2, *pwd);
1595 err = do_rename(conn, path1, path2, lflag);
1602 path1 = make_absolute(path1, *pwd);
1603 path2 = make_absolute(path2, *pwd);
1604 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1607 path1 = make_absolute_pwd_glob(path1, *pwd);
1608 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1609 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1611 mprintf("Removing %s\n", g.gl_pathv[i]);
1612 err = do_rm(conn, g.gl_pathv[i]);
1613 if (err != 0 && err_abort)
1618 path1 = make_absolute(path1, *pwd);
1620 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1622 err = do_mkdir(conn, path1, &a, 1);
1625 path1 = make_absolute(path1, *pwd);
1626 err = do_rmdir(conn, path1);
1629 if (path1 == NULL || *path1 == '\0')
1630 path1 = xstrdup(startdir);
1631 path1 = make_absolute(path1, *pwd);
1632 if ((tmp = do_realpath(conn, path1)) == NULL) {
1636 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1641 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1642 error("Can't change directory: Can't check target");
1647 if (!S_ISDIR(aa->perm)) {
1648 error("Can't change directory: \"%s\" is not "
1649 "a directory", tmp);
1659 do_ls_dir(conn, *pwd, *pwd, lflag);
1663 /* Strip pwd off beginning of non-absolute paths */
1665 if (!path_absolute(path1))
1668 path1 = make_absolute_pwd_glob(path1, *pwd);
1669 err = do_globbed_ls(conn, path1, tmp, lflag);
1672 /* Default to current directory if no path specified */
1674 path1 = xstrdup(*pwd);
1675 path1 = make_absolute(path1, *pwd);
1676 err = do_df(conn, path1, hflag, iflag);
1679 if (path1 == NULL || *path1 == '\0')
1680 path1 = xstrdup("~");
1681 tmp = tilde_expand_filename(path1, getuid());
1684 if (chdir(path1) == -1) {
1685 error("Couldn't change local directory to "
1686 "\"%s\": %s", path1, strerror(errno));
1691 if (mkdir(path1, 0777) == -1) {
1692 error("Couldn't create local directory "
1693 "\"%s\": %s", path1, strerror(errno));
1701 local_do_shell(cmd);
1705 printf("Local umask: %03lo\n", n_arg);
1708 path1 = make_absolute_pwd_glob(path1, *pwd);
1710 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1712 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1713 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1715 mprintf("Changing mode on %s\n",
1717 err = (hflag ? do_lsetstat : do_setstat)(conn,
1719 if (err != 0 && err_abort)
1725 path1 = make_absolute_pwd_glob(path1, *pwd);
1726 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1727 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1728 if (!(aa = (hflag ? do_lstat : do_stat)(conn,
1729 g.gl_pathv[i], 0))) {
1736 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1737 error("Can't get current ownership of "
1738 "remote file \"%s\"", g.gl_pathv[i]);
1745 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1746 if (cmdnum == I_CHOWN) {
1748 mprintf("Changing owner on %s\n",
1753 mprintf("Changing group on %s\n",
1757 err = (hflag ? do_lsetstat : do_setstat)(conn,
1759 if (err != 0 && err_abort)
1764 mprintf("Remote working directory: %s\n", *pwd);
1767 if (!getcwd(path_buf, sizeof(path_buf))) {
1768 error("Couldn't get local cwd: %s", strerror(errno));
1772 mprintf("Local working directory: %s\n", path_buf);
1775 /* Processed below */
1781 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1784 showprogress = !showprogress;
1786 printf("Progress meter enabled\n");
1788 printf("Progress meter disabled\n");
1791 fatal("%d is not implemented", cmdnum);
1799 /* If an unignored error occurs in batch mode we should abort. */
1800 if (err_abort && err != 0)
1802 else if (cmdnum == I_QUIT)
1810 prompt(EditLine *el)
1815 /* Display entries in 'list' after skipping the first 'len' chars */
1817 complete_display(char **list, u_int len)
1819 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1823 /* Count entries for sort and find longest */
1824 for (y = 0; list[y]; y++)
1825 m = MAXIMUM(m, strlen(list[y]));
1827 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1830 m = m > len ? m - len : 0;
1831 columns = width / (m + 2);
1832 columns = MAXIMUM(columns, 1);
1833 colspace = width / columns;
1834 colspace = MINIMUM(colspace, width);
1838 for (y = 0; list[y]; y++) {
1839 llen = strlen(list[y]);
1840 tmp = llen > len ? list[y] + len : "";
1841 mprintf("%-*s", colspace, tmp);
1852 * Given a "list" of words that begin with a common prefix of "word",
1853 * attempt to find an autocompletion to extends "word" by the next
1854 * characters common to all entries in "list".
1857 complete_ambiguous(const char *word, char **list, size_t count)
1863 u_int y, matchlen = strlen(list[0]);
1865 /* Find length of common stem */
1866 for (y = 1; list[y]; y++) {
1869 for (x = 0; x < matchlen; x++)
1870 if (list[0][x] != list[y][x])
1876 if (matchlen > strlen(word)) {
1877 char *tmp = xstrdup(list[0]);
1879 tmp[matchlen] = '\0';
1884 return xstrdup(word);
1887 /* Autocomplete a sftp command */
1889 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1892 u_int y, count = 0, cmdlen, tmplen;
1893 char *tmp, **list, argterm[3];
1896 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1898 /* No command specified: display all available commands */
1900 for (y = 0; cmds[y].c; y++)
1901 list[count++] = xstrdup(cmds[y].c);
1904 complete_display(list, 0);
1906 for (y = 0; list[y] != NULL; y++)
1912 /* Prepare subset of commands that start with "cmd" */
1913 cmdlen = strlen(cmd);
1914 for (y = 0; cmds[y].c; y++) {
1915 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1916 list[count++] = xstrdup(cmds[y].c);
1925 /* Complete ambiguous command */
1926 tmp = complete_ambiguous(cmd, list, count);
1928 complete_display(list, 0);
1930 for (y = 0; list[y]; y++)
1935 tmplen = strlen(tmp);
1936 cmdlen = strlen(cmd);
1937 /* If cmd may be extended then do so */
1938 if (tmplen > cmdlen)
1939 if (el_insertstr(el, tmp + cmdlen) == -1)
1940 fatal("el_insertstr failed.");
1942 /* Terminate argument cleanly */
1946 argterm[y++] = quote;
1947 if (lastarg || *(lf->cursor) != ' ')
1950 if (y > 0 && el_insertstr(el, argterm) == -1)
1951 fatal("el_insertstr failed.");
1960 * Determine whether a particular sftp command's arguments (if any) represent
1961 * local or remote files. The "cmdarg" argument specifies the actual argument
1962 * and accepts values 1 or 2.
1965 complete_is_remote(char *cmd, int cmdarg) {
1971 for (i = 0; cmds[i].c; i++) {
1972 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) {
1975 else if (cmdarg == 2)
1984 /* Autocomplete a filename "file" */
1986 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1987 char *file, int remote, int lastarg, char quote, int terminated)
1990 char *tmp, *tmp2, ins[8];
1991 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1995 /* Glob from "file" location */
1999 xasprintf(&tmp, "%s*", file);
2001 /* Check if the path is absolute. */
2002 isabs = path_absolute(tmp);
2004 memset(&g, 0, sizeof(g));
2005 if (remote != LOCAL) {
2006 tmp = make_absolute_pwd_glob(tmp, remote_path);
2007 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
2009 (void)glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
2011 /* Determine length of pwd so we can trim completion display */
2012 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
2013 /* Terminate counting on first unescaped glob metacharacter */
2014 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
2015 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
2019 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
2021 if (tmp[tmplen] == '/')
2022 pwdlen = tmplen + 1; /* track last seen '/' */
2027 if (g.gl_matchc == 0)
2030 if (g.gl_matchc > 1)
2031 complete_display(g.gl_pathv, pwdlen);
2033 /* Don't try to extend globs */
2034 if (file == NULL || hadglob)
2037 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
2038 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
2044 tmplen = strlen(tmp);
2045 filelen = strlen(file);
2047 /* Count the number of escaped characters in the input string. */
2049 for (i = 0; i < filelen; i++) {
2050 if (!isesc && file[i] == '\\' && i + 1 < filelen){
2057 if (tmplen > (filelen - cesc)) {
2058 tmp2 = tmp + filelen - cesc;
2060 /* quote argument on way out */
2061 for (i = 0; i < len; i += clen) {
2062 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
2063 (size_t)clen > sizeof(ins) - 2)
2064 fatal("invalid multibyte character");
2066 memcpy(ins + 1, tmp2 + i, clen);
2067 ins[clen + 1] = '\0';
2077 if (quote == '\0' || tmp2[i] == quote) {
2078 if (el_insertstr(el, ins) == -1)
2079 fatal("el_insertstr "
2085 if (el_insertstr(el, ins + 1) == -1)
2086 fatal("el_insertstr failed.");
2093 if (g.gl_matchc == 1) {
2095 if (!terminated && quote != '\0')
2097 if (*(lf->cursor - 1) != '/' &&
2098 (lastarg || *(lf->cursor) != ' '))
2101 if (i > 0 && el_insertstr(el, ins) == -1)
2102 fatal("el_insertstr failed.");
2111 /* tab-completion hook function, called via libedit */
2112 static unsigned char
2113 complete(EditLine *el, int ch)
2115 char **argv, *line, quote;
2117 u_int cursor, len, terminated, ret = CC_ERROR;
2119 struct complete_ctx *complete_ctx;
2122 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2123 fatal_f("el_get failed");
2125 /* Figure out which argument the cursor points to */
2126 cursor = lf->cursor - lf->buffer;
2127 line = xmalloc(cursor + 1);
2128 memcpy(line, lf->buffer, cursor);
2129 line[cursor] = '\0';
2130 argv = makeargv(line, &carg, 1, "e, &terminated);
2133 /* Get all the arguments on the line */
2134 len = lf->lastchar - lf->buffer;
2135 line = xmalloc(len + 1);
2136 memcpy(line, lf->buffer, len);
2138 argv = makeargv(line, &argc, 1, NULL, NULL);
2140 /* Ensure cursor is at EOL or a argument boundary */
2141 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2142 line[cursor] != '\n') {
2148 /* Show all available commands */
2149 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2151 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2152 /* Handle the command parsing */
2153 if (complete_cmd_parse(el, argv[0], argc == carg,
2154 quote, terminated) != 0)
2156 } else if (carg >= 1) {
2157 /* Handle file parsing */
2159 int i = 0, cmdarg = 0;
2160 char *filematch = NULL;
2162 if (carg > 1 && line[cursor-1] != ' ')
2163 filematch = argv[carg - 1];
2165 for (i = 1; i < carg; i++) {
2167 if (argv[i][0] != '-')
2172 * If previous argument is complete, then offer completion
2175 if (line[cursor - 1] == ' ')
2178 remote = complete_is_remote(argv[0], cmdarg);
2180 if ((remote == REMOTE || remote == LOCAL) &&
2181 complete_match(el, complete_ctx->conn,
2182 *complete_ctx->remote_pathp, filematch,
2183 remote, carg == argc, quote, terminated) != 0)
2190 #endif /* USE_LIBEDIT */
2193 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2196 char *dir = NULL, *startdir = NULL;
2198 int err, interactive;
2199 EditLine *el = NULL;
2203 extern char *__progname;
2204 struct complete_ctx complete_ctx;
2206 if (!batchmode && isatty(STDIN_FILENO)) {
2207 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2208 fatal("Couldn't initialise editline");
2209 if ((hl = history_init()) == NULL)
2210 fatal("Couldn't initialise editline history");
2211 history(hl, &hev, H_SETSIZE, 100);
2212 el_set(el, EL_HIST, history, hl);
2214 el_set(el, EL_PROMPT, prompt);
2215 el_set(el, EL_EDITOR, "emacs");
2216 el_set(el, EL_TERMINAL, NULL);
2217 el_set(el, EL_SIGNAL, 1);
2218 el_source(el, NULL);
2220 /* Tab Completion */
2221 el_set(el, EL_ADDFN, "ftp-complete",
2222 "Context sensitive argument completion", complete);
2223 complete_ctx.conn = conn;
2224 complete_ctx.remote_pathp = &remote_path;
2225 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2226 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2227 /* enable ctrl-left-arrow and ctrl-right-arrow */
2228 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2229 el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL);
2230 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2231 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2232 /* make ^w match ksh behaviour */
2233 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2235 #endif /* USE_LIBEDIT */
2237 remote_path = do_realpath(conn, ".");
2238 if (remote_path == NULL)
2240 startdir = xstrdup(remote_path);
2242 if (file1 != NULL) {
2243 dir = xstrdup(file1);
2244 dir = make_absolute(dir, remote_path);
2246 if (remote_is_dir(conn, dir) && file2 == NULL) {
2248 mprintf("Changing to: %s\n", dir);
2249 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2250 if (parse_dispatch_command(conn, cmd,
2251 &remote_path, startdir, 1, 0) != 0) {
2259 /* XXX this is wrong wrt quoting */
2260 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2261 global_aflag ? " -a" : "", dir,
2262 file2 == NULL ? "" : " ",
2263 file2 == NULL ? "" : file2);
2264 err = parse_dispatch_command(conn, cmd,
2265 &remote_path, startdir, 1, 0);
2275 setvbuf(stdout, NULL, _IOLBF, 0);
2276 setvbuf(infile, NULL, _IOLBF, 0);
2278 interactive = !batchmode && isatty(STDIN_FILENO);
2281 struct sigaction sa;
2284 memset(&sa, 0, sizeof(sa));
2285 sa.sa_handler = interactive ? read_interrupt : killchild;
2286 if (sigaction(SIGINT, &sa, NULL) == -1) {
2287 debug3("sigaction(%s): %s", strsignal(SIGINT),
2294 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2306 if ((line = el_gets(el, &count)) == NULL ||
2313 history(hl, &hev, H_ENTER, line);
2314 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2315 fprintf(stderr, "Error: input line too long\n");
2318 #endif /* USE_LIBEDIT */
2321 cmd[strcspn(cmd, "\n")] = '\0';
2323 /* Handle user interrupts gracefully during commands */
2325 ssh_signal(SIGINT, cmd_interrupt);
2327 err = parse_dispatch_command(conn, cmd, &remote_path,
2328 startdir, batchmode, !interactive && el == NULL);
2332 ssh_signal(SIGCHLD, SIG_DFL);
2340 #endif /* USE_LIBEDIT */
2342 /* err == 1 signifies normal "quit" exit */
2343 return (err >= 0 ? 0 : -1);
2347 connect_to_server(char *path, char **args, int *in, int *out)
2351 int pin[2], pout[2];
2353 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2354 fatal("pipe: %s", strerror(errno));
2359 #else /* USE_PIPES */
2362 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2363 fatal("socketpair: %s", strerror(errno));
2364 *in = *out = inout[0];
2365 c_in = c_out = inout[1];
2366 #endif /* USE_PIPES */
2368 if ((sshpid = fork()) == -1)
2369 fatal("fork: %s", strerror(errno));
2370 else if (sshpid == 0) {
2371 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2372 (dup2(c_out, STDOUT_FILENO) == -1)) {
2373 fprintf(stderr, "dup2: %s\n", strerror(errno));
2382 * The underlying ssh is in the same process group, so we must
2383 * ignore SIGINT if we want to gracefully abort commands,
2384 * otherwise the signal will make it to the ssh process and
2385 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2386 * underlying ssh, it must *not* ignore that signal.
2388 ssh_signal(SIGINT, SIG_IGN);
2389 ssh_signal(SIGTERM, SIG_DFL);
2391 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2395 ssh_signal(SIGTERM, killchild);
2396 ssh_signal(SIGINT, killchild);
2397 ssh_signal(SIGHUP, killchild);
2398 ssh_signal(SIGTSTP, suspchild);
2399 ssh_signal(SIGTTIN, suspchild);
2400 ssh_signal(SIGTTOU, suspchild);
2401 ssh_signal(SIGCHLD, sigchld_handler);
2409 extern char *__progname;
2412 "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2413 " [-D sftp_server_command] [-F ssh_config] [-i identity_file]\n"
2414 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
2415 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
2416 " [-X sftp_option] destination\n",
2422 main(int argc, char **argv)
2424 int r, in, out, ch, err, tmp, port = -1, noisy = 0;
2425 char *host = NULL, *user, *cp, **cpp, *file2 = NULL;
2426 int debug_level = 0;
2427 char *file1 = NULL, *sftp_server = NULL;
2428 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2430 LogLevel ll = SYSLOG_LEVEL_INFO;
2433 extern char *optarg;
2434 struct sftp_conn *conn;
2435 size_t copy_buffer_len = 0;
2436 size_t num_requests = 0;
2437 long long llv, limit_kbps = 0;
2439 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2443 __progname = ssh_get_progname(argv[0]);
2444 memset(&args, '\0', sizeof(args));
2446 addargs(&args, "%s", ssh_program);
2447 addargs(&args, "-oForwardX11 no");
2448 addargs(&args, "-oPermitLocalCommand no");
2449 addargs(&args, "-oClearAllForwardings yes");
2451 ll = SYSLOG_LEVEL_INFO;
2454 while ((ch = getopt(argc, argv,
2455 "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:X:")) != -1) {
2457 /* Passed through to ssh(1) */
2462 addargs(&args, "-%c", ch);
2464 /* Passed through to ssh(1) with argument */
2470 addargs(&args, "-%c", ch);
2471 addargs(&args, "%s", optarg);
2474 ll = SYSLOG_LEVEL_ERROR;
2477 addargs(&args, "-%c", ch);
2480 port = a2port(optarg);
2482 fatal("Bad port \"%s\"\n", optarg);
2485 if (debug_level < 3) {
2486 addargs(&args, "-v");
2487 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2492 fatal("SSH protocol v.1 is no longer supported");
2495 /* accept silently */
2501 copy_buffer_len = strtol(optarg, &cp, 10);
2502 if (copy_buffer_len == 0 || *cp != '\0')
2503 fatal("Invalid buffer size \"%s\"", optarg);
2507 fatal("Batch file already specified.");
2509 /* Allow "-" as stdin */
2510 if (strcmp(optarg, "-") != 0 &&
2511 (infile = fopen(optarg, "r")) == NULL)
2512 fatal("%s (%s).", strerror(errno), optarg);
2514 quiet = batchmode = 1;
2515 addargs(&args, "-obatchmode yes");
2521 noisy = 1; /* Used to clear quiet mode after getopt */
2527 sftp_direct = optarg;
2530 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2534 limit_kbps *= 1024; /* kbps */
2540 num_requests = strtol(optarg, &cp, 10);
2541 if (num_requests == 0 || *cp != '\0')
2542 fatal("Invalid number of requests \"%s\"",
2546 sftp_server = optarg;
2549 ssh_program = optarg;
2550 replacearg(&args, 0, "%s", ssh_program);
2553 /* Please keep in sync with ssh.c -X */
2554 if (strncmp(optarg, "buffer=", 7) == 0) {
2555 r = scan_scaled(optarg + 7, &llv);
2556 if (r == 0 && (llv <= 0 || llv > 256 * 1024)) {
2561 fatal("Invalid buffer size \"%s\": %s",
2562 optarg + 7, strerror(errno));
2564 copy_buffer_len = (size_t)llv;
2565 } else if (strncmp(optarg, "nrequests=", 10) == 0) {
2566 llv = strtonum(optarg + 10, 1, 256 * 1024,
2568 if (errstr != NULL) {
2569 fatal("Invalid number of requests "
2570 "\"%s\": %s", optarg + 10, errstr);
2572 num_requests = (size_t)llv;
2574 fatal("Invalid -X option");
2583 /* Do this last because we want the user to be able to override it */
2584 addargs(&args, "-oForwardAgent no");
2586 if (!isatty(STDERR_FILENO))
2592 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2594 if (sftp_direct == NULL) {
2595 if (optind == argc || argc > (optind + 2))
2599 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2608 /* Try with user, host and path. */
2609 if (parse_user_host_path(*argv, &user, &host,
2612 /* Try with user and host. */
2613 if (parse_user_host_port(*argv, &user, &host, NULL)
2616 /* Treat as a plain hostname. */
2617 host = xstrdup(*argv);
2618 host = cleanhostname(host);
2621 file2 = *(argv + 1);
2624 fprintf(stderr, "Missing hostname\n");
2629 addargs(&args, "-oPort %d", port);
2631 addargs(&args, "-l");
2632 addargs(&args, "%s", user);
2635 /* no subsystem if the server-spec contains a '/' */
2636 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2637 addargs(&args, "-s");
2639 addargs(&args, "--");
2640 addargs(&args, "%s", host);
2641 addargs(&args, "%s", (sftp_server != NULL ?
2642 sftp_server : "sftp"));
2644 connect_to_server(ssh_program, args.list, &in, &out);
2646 if ((r = argv_split(sftp_direct, &tmp, &cpp, 1)) != 0)
2647 fatal_r(r, "Parse -D arguments");
2649 fatal("No sftp server specified via -D");
2650 connect_to_server(cpp[0], cpp, &in, &out);
2651 argv_free(cpp, tmp);
2655 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2657 fatal("Couldn't initialise connection to server");
2660 if (sftp_direct == NULL)
2661 fprintf(stderr, "Connected to %s.\n", host);
2663 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2666 err = interactive_loop(conn, file1, file2);
2668 #if !defined(USE_PIPES)
2669 shutdown(in, SHUT_RDWR);
2670 shutdown(out, SHUT_RDWR);
2678 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
2680 fatal("Couldn't wait for ssh process: %s",
2683 exit(err == 0 ? 0 : 1);