1 /* $OpenBSD: sftp.c,v 1.229 2023/03/12 09:41:18 dtucker Exp $ */
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
25 #include <sys/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 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 make_absolute_pwd_glob(const char *p, const char *pwd)
624 escpwd = escape_glob(pwd);
627 ret = make_absolute(xstrdup(p), escpwd);
633 process_get(struct sftp_conn *conn, const char *src, const char *dst,
634 const char *pwd, int pflag, int rflag, int resume, int fflag)
636 char *filename, *abs_src = NULL, *abs_dst = NULL, *tmp = NULL;
640 abs_src = make_absolute_pwd_glob(src, pwd);
641 memset(&g, 0, sizeof(g));
643 debug3("Looking up %s", abs_src);
644 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
645 if (r == GLOB_NOSPACE) {
646 error("Too many matches for \"%s\".", abs_src);
648 error("File \"%s\" not found.", abs_src);
655 * If multiple matches then dst must be a directory or
658 if (g.gl_matchc > 1 && dst != NULL && !local_is_dir(dst)) {
659 error("Multiple source paths, but destination "
660 "\"%s\" is not a directory", dst);
665 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
666 tmp = xstrdup(g.gl_pathv[i]);
667 if ((filename = basename(tmp)) == NULL) {
668 error("basename %s: %s", tmp, strerror(errno));
674 if (g.gl_matchc == 1 && dst) {
675 if (local_is_dir(dst)) {
676 abs_dst = path_append(dst, filename);
678 abs_dst = xstrdup(dst);
681 abs_dst = path_append(dst, filename);
683 abs_dst = xstrdup(filename);
687 resume |= global_aflag;
688 if (!quiet && resume)
689 mprintf("Resuming %s to %s\n",
690 g.gl_pathv[i], abs_dst);
691 else if (!quiet && !resume)
692 mprintf("Fetching %s to %s\n",
693 g.gl_pathv[i], abs_dst);
694 /* XXX follow link flag */
695 if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
696 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
697 pflag || global_pflag, 1, resume,
698 fflag || global_fflag, 0, 0) == -1)
701 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
702 pflag || global_pflag, resume,
703 fflag || global_fflag, 0) == -1)
717 process_put(struct sftp_conn *conn, const char *src, const char *dst,
718 const char *pwd, int pflag, int rflag, int resume, int fflag)
720 char *tmp_dst = NULL;
721 char *abs_dst = NULL;
722 char *tmp = NULL, *filename = NULL;
725 int i, dst_is_dir = 1;
729 tmp_dst = xstrdup(dst);
730 tmp_dst = make_absolute(tmp_dst, pwd);
733 memset(&g, 0, sizeof(g));
734 debug3("Looking up %s", src);
735 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
736 error("File \"%s\" not found.", src);
741 /* If we aren't fetching to pwd then stash this status for later */
743 dst_is_dir = remote_is_dir(conn, tmp_dst);
745 /* If multiple matches, dst may be directory or unspecified */
746 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
747 error("Multiple paths match, but destination "
748 "\"%s\" is not a directory", tmp_dst);
753 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
754 if (stat(g.gl_pathv[i], &sb) == -1) {
756 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
760 tmp = xstrdup(g.gl_pathv[i]);
761 if ((filename = basename(tmp)) == NULL) {
762 error("basename %s: %s", tmp, strerror(errno));
768 if (g.gl_matchc == 1 && tmp_dst) {
769 /* If directory specified, append filename */
771 abs_dst = path_append(tmp_dst, filename);
773 abs_dst = xstrdup(tmp_dst);
774 } else if (tmp_dst) {
775 abs_dst = path_append(tmp_dst, filename);
777 abs_dst = make_absolute(xstrdup(filename), pwd);
781 resume |= global_aflag;
782 if (!quiet && resume)
783 mprintf("Resuming upload of %s to %s\n",
784 g.gl_pathv[i], abs_dst);
785 else if (!quiet && !resume)
786 mprintf("Uploading %s to %s\n",
787 g.gl_pathv[i], abs_dst);
788 /* XXX follow_link_flag */
789 if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
790 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
791 pflag || global_pflag, 1, resume,
792 fflag || global_fflag, 0, 0) == -1)
795 if (do_upload(conn, g.gl_pathv[i], abs_dst,
796 pflag || global_pflag, resume,
797 fflag || global_fflag, 0) == -1)
810 sdirent_comp(const void *aa, const void *bb)
812 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
813 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
814 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
816 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
817 if (sort_flag & LS_NAME_SORT)
818 return (rmul * strcmp(a->filename, b->filename));
819 else if (sort_flag & LS_TIME_SORT)
820 return (rmul * NCMP(a->a.mtime, b->a.mtime));
821 else if (sort_flag & LS_SIZE_SORT)
822 return (rmul * NCMP(a->a.size, b->a.size));
824 fatal("Unknown ls sort type");
827 /* sftp ls.1 replacement for directories */
829 do_ls_dir(struct sftp_conn *conn, const char *path,
830 const char *strip_path, int lflag)
833 u_int c = 1, colspace = 0, columns = 1;
836 if ((n = do_readdir(conn, path, &d)) != 0)
839 if (!(lflag & LS_SHORT_VIEW)) {
840 u_int m = 0, width = 80;
844 /* Count entries for sort and find longest filename */
845 for (n = 0; d[n] != NULL; n++) {
846 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
847 m = MAXIMUM(m, strlen(d[n]->filename));
850 /* Add any subpath that also needs to be counted */
851 tmp = path_strip(path, strip_path);
855 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
858 columns = width / (m + 2);
859 columns = MAXIMUM(columns, 1);
860 colspace = width / columns;
861 colspace = MINIMUM(colspace, width);
864 if (lflag & SORT_FLAGS) {
865 for (n = 0; d[n] != NULL; n++)
866 ; /* count entries */
867 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
868 qsort(d, n, sizeof(*d), sdirent_comp);
871 get_remote_user_groups_from_dirents(conn, d);
872 for (n = 0; d[n] != NULL && !interrupted; n++) {
875 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
878 tmp = path_append(path, d[n]->filename);
879 fname = path_strip(tmp, strip_path);
882 if (lflag & LS_LONG_VIEW) {
883 if ((lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) != 0 ||
884 can_get_users_groups_by_id(conn)) {
888 memset(&sb, 0, sizeof(sb));
889 attrib_to_stat(&d[n]->a, &sb);
890 lname = ls_file(fname, &sb, 1,
891 (lflag & LS_SI_UNITS),
892 ruser_name(sb.st_uid),
893 rgroup_name(sb.st_gid));
894 mprintf("%s\n", lname);
897 mprintf("%s\n", d[n]->longname);
899 mprintf("%-*s", colspace, fname);
910 if (!(lflag & LS_LONG_VIEW) && (c != 1))
913 free_sftp_dirents(d);
918 sglob_comp(const void *aa, const void *bb)
920 u_int a = *(const u_int *)aa;
921 u_int b = *(const u_int *)bb;
922 const char *ap = sort_glob->gl_pathv[a];
923 const char *bp = sort_glob->gl_pathv[b];
924 const struct stat *as = sort_glob->gl_statv[a];
925 const struct stat *bs = sort_glob->gl_statv[b];
926 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
928 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
929 if (sort_flag & LS_NAME_SORT)
930 return (rmul * strcmp(ap, bp));
931 else if (sort_flag & LS_TIME_SORT) {
932 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
933 if (timespeccmp(&as->st_mtim, &bs->st_mtim, ==))
935 return timespeccmp(&as->st_mtim, &bs->st_mtim, <) ?
937 #elif defined(HAVE_STRUCT_STAT_ST_MTIME)
938 return (rmul * NCMP(as->st_mtime, bs->st_mtime));
942 } else if (sort_flag & LS_SIZE_SORT)
943 return (rmul * NCMP(as->st_size, bs->st_size));
945 fatal("Unknown ls sort type");
948 /* sftp ls.1 replacement which handles path globs */
950 do_globbed_ls(struct sftp_conn *conn, const char *path,
951 const char *strip_path, int lflag)
957 u_int i, j, nentries, *indices = NULL, c = 1;
958 u_int colspace = 0, columns = 1, m = 0, width = 80;
960 memset(&g, 0, sizeof(g));
962 if ((r = remote_glob(conn, path,
963 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
965 (g.gl_pathc && !g.gl_matchc)) {
968 if (r == GLOB_NOSPACE) {
969 error("Can't ls: Too many matches for \"%s\"", path);
971 error("Can't ls: \"%s\" not found", path);
980 * If the glob returns a single match and it is a directory,
981 * then just list its contents.
983 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
984 S_ISDIR(g.gl_statv[0]->st_mode)) {
985 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
990 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
993 if (!(lflag & LS_SHORT_VIEW)) {
994 /* Count entries for sort and find longest filename */
995 for (i = 0; g.gl_pathv[i]; i++)
996 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
998 columns = width / (m + 2);
999 columns = MAXIMUM(columns, 1);
1000 colspace = width / columns;
1004 * Sorting: rather than mess with the contents of glob_t, prepare
1005 * an array of indices into it and sort that. For the usual
1006 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
1008 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
1009 ; /* count entries */
1010 indices = xcalloc(nentries, sizeof(*indices));
1011 for (i = 0; i < nentries; i++)
1014 if (lflag & SORT_FLAGS) {
1016 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
1017 qsort(indices, nentries, sizeof(*indices), sglob_comp);
1021 get_remote_user_groups_from_glob(conn, &g);
1022 for (j = 0; j < nentries && !interrupted; j++) {
1024 fname = path_strip(g.gl_pathv[i], strip_path);
1025 if (lflag & LS_LONG_VIEW) {
1026 if (g.gl_statv[i] == NULL) {
1027 error("no stat information for %s", fname);
1031 lname = ls_file(fname, g.gl_statv[i], 1,
1032 (lflag & LS_SI_UNITS),
1033 ruser_name(g.gl_statv[i]->st_uid),
1034 rgroup_name(g.gl_statv[i]->st_gid));
1035 mprintf("%s\n", lname);
1038 mprintf("%-*s", colspace, fname);
1048 if (!(lflag & LS_LONG_VIEW) && (c != 1))
1060 do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
1062 struct sftp_statvfs st;
1063 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
1064 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
1065 char s_icapacity[16], s_dcapacity[16];
1067 if (do_statvfs(conn, path, &st, 1) == -1)
1069 if (st.f_files == 0)
1070 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1072 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1073 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1076 if (st.f_blocks == 0)
1077 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1079 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1080 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1084 printf(" Inodes Used Avail "
1085 "(root) %%Capacity\n");
1086 printf("%11llu %11llu %11llu %11llu %s\n",
1087 (unsigned long long)st.f_files,
1088 (unsigned long long)(st.f_files - st.f_ffree),
1089 (unsigned long long)st.f_favail,
1090 (unsigned long long)st.f_ffree, s_icapacity);
1092 strlcpy(s_used, "error", sizeof(s_used));
1093 strlcpy(s_avail, "error", sizeof(s_avail));
1094 strlcpy(s_root, "error", sizeof(s_root));
1095 strlcpy(s_total, "error", sizeof(s_total));
1096 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1097 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1098 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1099 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1100 printf(" Size Used Avail (root) %%Capacity\n");
1101 printf("%7sB %7sB %7sB %7sB %s\n",
1102 s_total, s_used, s_avail, s_root, s_dcapacity);
1104 printf(" Size Used Avail "
1105 "(root) %%Capacity\n");
1106 printf("%12llu %12llu %12llu %12llu %s\n",
1107 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1108 (unsigned long long)(st.f_frsize *
1109 (st.f_blocks - st.f_bfree) / 1024),
1110 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1111 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1118 * Undo escaping of glob sequences in place. Used to undo extra escaping
1119 * applied in makeargv() when the string is destined for a function that
1123 undo_glob_escape(char *s)
1158 * Split a string into an argument vector using sh(1)-style quoting,
1159 * comment and escaping rules, but with some tweaks to handle glob(3)
1161 * The "sloppy" flag allows for recovery from missing terminating quote, for
1162 * use in parsing incomplete commandlines during tab autocompletion.
1164 * Returns NULL on error or a NULL-terminated array of arguments.
1166 * If "lastquote" is not NULL, the quoting character used for the last
1167 * argument is placed in *lastquote ("\0", "'" or "\"").
1169 * If "terminated" is not NULL, *terminated will be set to 1 when the
1170 * last argument's quote has been properly terminated or 0 otherwise.
1171 * This parameter is only of use if "sloppy" is set.
1174 #define MAXARGLEN 8192
1176 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1181 static char argvs[MAXARGLEN];
1182 static char *argv[MAXARGS + 1];
1183 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1186 if (strlen(arg) > sizeof(argvs) - 1) {
1188 error("string too long");
1191 if (terminated != NULL)
1193 if (lastquote != NULL)
1198 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1199 error("Too many arguments.");
1202 if (isspace((unsigned char)arg[i])) {
1203 if (state == MA_UNQUOTED) {
1204 /* Terminate current argument */
1208 } else if (state != MA_START)
1209 argvs[j++] = arg[i];
1210 } else if (arg[i] == '"' || arg[i] == '\'') {
1211 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1212 if (state == MA_START) {
1213 argv[argc] = argvs + j;
1215 if (lastquote != NULL)
1216 *lastquote = arg[i];
1217 } else if (state == MA_UNQUOTED)
1219 else if (state == q)
1220 state = MA_UNQUOTED;
1222 argvs[j++] = arg[i];
1223 } else if (arg[i] == '\\') {
1224 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1225 quot = state == MA_SQUOTE ? '\'' : '"';
1226 /* Unescape quote we are in */
1227 /* XXX support \n and friends? */
1228 if (arg[i + 1] == quot) {
1230 argvs[j++] = arg[i];
1231 } else if (arg[i + 1] == '?' ||
1232 arg[i + 1] == '[' || arg[i + 1] == '*') {
1234 * Special case for sftp: append
1235 * double-escaped glob sequence -
1236 * glob will undo one level of
1237 * escaping. NB. string can grow here.
1239 if (j >= sizeof(argvs) - 5)
1240 goto args_too_longs;
1242 argvs[j++] = arg[i++];
1244 argvs[j++] = arg[i];
1246 argvs[j++] = arg[i++];
1247 argvs[j++] = arg[i];
1250 if (state == MA_START) {
1251 argv[argc] = argvs + j;
1252 state = MA_UNQUOTED;
1253 if (lastquote != NULL)
1256 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1257 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1259 * Special case for sftp: append
1260 * escaped glob sequence -
1261 * glob will undo one level of
1264 argvs[j++] = arg[i++];
1265 argvs[j++] = arg[i];
1267 /* Unescape everything */
1268 /* XXX support \n and friends? */
1270 argvs[j++] = arg[i];
1273 } else if (arg[i] == '#') {
1274 if (state == MA_SQUOTE || state == MA_DQUOTE)
1275 argvs[j++] = arg[i];
1278 } else if (arg[i] == '\0') {
1279 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1281 state = MA_UNQUOTED;
1282 if (terminated != NULL)
1286 error("Unterminated quoted argument");
1290 if (state == MA_UNQUOTED) {
1296 if (state == MA_START) {
1297 argv[argc] = argvs + j;
1298 state = MA_UNQUOTED;
1299 if (lastquote != NULL)
1302 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1303 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1305 * Special case for sftp: escape quoted
1306 * glob(3) wildcards. NB. string can grow
1309 if (j >= sizeof(argvs) - 3)
1310 goto args_too_longs;
1312 argvs[j++] = arg[i];
1314 argvs[j++] = arg[i];
1323 parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
1324 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1325 int *rflag, int *sflag,
1326 unsigned long *n_arg, char **path1, char **path2)
1328 const char *cmd, *cp = *cpp;
1332 int path1_mandatory = 0, i, cmdnum, optidx, argc;
1334 /* Skip leading whitespace */
1335 cp = cp + strspn(cp, WHITESPACE);
1338 * Check for leading '-' (disable error processing) and '@' (suppress
1343 for (;*cp != '\0'; cp++) {
1346 } else if (*cp == '@') {
1349 /* all other characters terminate prefix processing */
1353 cp = cp + strspn(cp, WHITESPACE);
1355 /* Ignore blank lines and lines which begin with comment '#' char */
1356 if (*cp == '\0' || *cp == '#')
1359 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1362 /* Figure out which command we have */
1363 for (i = 0; cmds[i].c != NULL; i++) {
1364 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1374 } else if (cmdnum == -1) {
1375 error("Invalid command.");
1379 /* Get arguments and parse flags */
1380 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1381 *rflag = *sflag = 0;
1382 *path1 = *path2 = NULL;
1389 if ((optidx = parse_getput_flags(cmd, argv, argc,
1390 aflag, fflag, pflag, rflag)) == -1)
1392 /* Get first pathname (mandatory) */
1393 if (argc - optidx < 1) {
1394 error("You must specify at least one path after a "
1395 "%s command.", cmd);
1398 *path1 = xstrdup(argv[optidx]);
1399 /* Get second pathname (optional) */
1400 if (argc - optidx > 1) {
1401 *path2 = xstrdup(argv[optidx + 1]);
1402 /* Destination is not globbed */
1403 undo_glob_escape(*path2);
1407 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1409 goto parse_two_paths;
1411 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1413 goto parse_two_paths;
1415 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1417 goto parse_two_paths;
1419 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1422 if (argc - optidx < 2) {
1423 error("You must specify two paths after a %s "
1427 *path1 = xstrdup(argv[optidx]);
1428 *path2 = xstrdup(argv[optidx + 1]);
1429 /* Paths are not globbed */
1430 undo_glob_escape(*path1);
1431 undo_glob_escape(*path2);
1437 path1_mandatory = 1;
1441 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1443 /* Get pathname (mandatory) */
1444 if (argc - optidx < 1) {
1445 if (!path1_mandatory)
1446 break; /* return a NULL path1 */
1447 error("You must specify a path after a %s command.",
1451 *path1 = xstrdup(argv[optidx]);
1452 /* Only "rm" globs */
1454 undo_glob_escape(*path1);
1457 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1460 /* Default to current directory if no path specified */
1461 if (argc - optidx < 1)
1464 *path1 = xstrdup(argv[optidx]);
1465 undo_glob_escape(*path1);
1469 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1471 /* Path is optional */
1472 if (argc - optidx > 0)
1473 *path1 = xstrdup(argv[optidx]);
1476 /* Skip ls command and following whitespace */
1477 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1479 /* Uses the rest of the line */
1487 if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
1489 /* Get numeric arg (mandatory) */
1490 if (argc - optidx < 1)
1493 ll = strtoll(argv[optidx], &cp2, base);
1494 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1495 ((ll == LLONG_MIN || ll == LLONG_MAX) && errno == ERANGE) ||
1496 ll < 0 || ll > UINT32_MAX) {
1498 error("You must supply a numeric argument "
1499 "to the %s command.", cmd);
1503 if (cmdnum == I_LUMASK)
1505 /* Get pathname (mandatory) */
1506 if (argc - optidx < 2) {
1507 error("You must specify a path after a %s command.",
1511 *path1 = xstrdup(argv[optidx + 1]);
1519 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1523 fatal("Command not implemented");
1531 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1532 const char *startdir, int err_abort, int echo_command)
1534 const char *ocmd = cmd;
1535 char *path1, *path2, *tmp;
1536 int ignore_errors = 0, disable_echo = 1;
1537 int aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1538 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1540 unsigned long n_arg = 0;
1542 char path_buf[PATH_MAX];
1546 path1 = path2 = NULL;
1547 cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag,
1548 &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg,
1550 if (ignore_errors != 0)
1553 if (echo_command && !disable_echo)
1554 mprintf("sftp> %s\n", ocmd);
1556 memset(&g, 0, sizeof(g));
1558 /* Perform command */
1564 /* Unrecognized command */
1571 err = process_get(conn, path1, path2, *pwd, pflag,
1572 rflag, aflag, fflag);
1578 err = process_put(conn, path1, path2, *pwd, pflag,
1579 rflag, aflag, fflag);
1582 path1 = make_absolute(path1, *pwd);
1583 path2 = make_absolute(path2, *pwd);
1584 err = do_copy(conn, path1, path2);
1587 path1 = make_absolute(path1, *pwd);
1588 path2 = make_absolute(path2, *pwd);
1589 err = do_rename(conn, path1, path2, lflag);
1596 path1 = make_absolute(path1, *pwd);
1597 path2 = make_absolute(path2, *pwd);
1598 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1601 path1 = make_absolute_pwd_glob(path1, *pwd);
1602 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1603 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1605 mprintf("Removing %s\n", g.gl_pathv[i]);
1606 err = do_rm(conn, g.gl_pathv[i]);
1607 if (err != 0 && err_abort)
1612 path1 = make_absolute(path1, *pwd);
1614 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1616 err = do_mkdir(conn, path1, &a, 1);
1619 path1 = make_absolute(path1, *pwd);
1620 err = do_rmdir(conn, path1);
1623 if (path1 == NULL || *path1 == '\0')
1624 path1 = xstrdup(startdir);
1625 path1 = make_absolute(path1, *pwd);
1626 if ((tmp = do_realpath(conn, path1)) == NULL) {
1630 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1635 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1636 error("Can't change directory: Can't check target");
1641 if (!S_ISDIR(aa->perm)) {
1642 error("Can't change directory: \"%s\" is not "
1643 "a directory", tmp);
1653 do_ls_dir(conn, *pwd, *pwd, lflag);
1657 /* Strip pwd off beginning of non-absolute paths */
1659 if (!path_absolute(path1))
1662 path1 = make_absolute_pwd_glob(path1, *pwd);
1663 err = do_globbed_ls(conn, path1, tmp, lflag);
1666 /* Default to current directory if no path specified */
1668 path1 = xstrdup(*pwd);
1669 path1 = make_absolute(path1, *pwd);
1670 err = do_df(conn, path1, hflag, iflag);
1673 if (path1 == NULL || *path1 == '\0')
1674 path1 = xstrdup("~");
1675 tmp = tilde_expand_filename(path1, getuid());
1678 if (chdir(path1) == -1) {
1679 error("Couldn't change local directory to "
1680 "\"%s\": %s", path1, strerror(errno));
1685 if (mkdir(path1, 0777) == -1) {
1686 error("Couldn't create local directory "
1687 "\"%s\": %s", path1, strerror(errno));
1695 local_do_shell(cmd);
1699 printf("Local umask: %03lo\n", n_arg);
1702 path1 = make_absolute_pwd_glob(path1, *pwd);
1704 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1706 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1707 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1709 mprintf("Changing mode on %s\n",
1711 err = (hflag ? do_lsetstat : do_setstat)(conn,
1713 if (err != 0 && err_abort)
1719 path1 = make_absolute_pwd_glob(path1, *pwd);
1720 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1721 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1722 if (!(aa = (hflag ? do_lstat : do_stat)(conn,
1723 g.gl_pathv[i], 0))) {
1730 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1731 error("Can't get current ownership of "
1732 "remote file \"%s\"", g.gl_pathv[i]);
1739 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1740 if (cmdnum == I_CHOWN) {
1742 mprintf("Changing owner on %s\n",
1747 mprintf("Changing group on %s\n",
1751 err = (hflag ? do_lsetstat : do_setstat)(conn,
1753 if (err != 0 && err_abort)
1758 mprintf("Remote working directory: %s\n", *pwd);
1761 if (!getcwd(path_buf, sizeof(path_buf))) {
1762 error("Couldn't get local cwd: %s", strerror(errno));
1766 mprintf("Local working directory: %s\n", path_buf);
1769 /* Processed below */
1775 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1778 showprogress = !showprogress;
1780 printf("Progress meter enabled\n");
1782 printf("Progress meter disabled\n");
1785 fatal("%d is not implemented", cmdnum);
1793 /* If an unignored error occurs in batch mode we should abort. */
1794 if (err_abort && err != 0)
1796 else if (cmdnum == I_QUIT)
1804 prompt(EditLine *el)
1809 /* Display entries in 'list' after skipping the first 'len' chars */
1811 complete_display(char **list, u_int len)
1813 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1817 /* Count entries for sort and find longest */
1818 for (y = 0; list[y]; y++)
1819 m = MAXIMUM(m, strlen(list[y]));
1821 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1824 m = m > len ? m - len : 0;
1825 columns = width / (m + 2);
1826 columns = MAXIMUM(columns, 1);
1827 colspace = width / columns;
1828 colspace = MINIMUM(colspace, width);
1832 for (y = 0; list[y]; y++) {
1833 llen = strlen(list[y]);
1834 tmp = llen > len ? list[y] + len : "";
1835 mprintf("%-*s", colspace, tmp);
1846 * Given a "list" of words that begin with a common prefix of "word",
1847 * attempt to find an autocompletion to extends "word" by the next
1848 * characters common to all entries in "list".
1851 complete_ambiguous(const char *word, char **list, size_t count)
1857 u_int y, matchlen = strlen(list[0]);
1859 /* Find length of common stem */
1860 for (y = 1; list[y]; y++) {
1863 for (x = 0; x < matchlen; x++)
1864 if (list[0][x] != list[y][x])
1870 if (matchlen > strlen(word)) {
1871 char *tmp = xstrdup(list[0]);
1873 tmp[matchlen] = '\0';
1878 return xstrdup(word);
1881 /* Autocomplete a sftp command */
1883 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1886 u_int y, count = 0, cmdlen, tmplen;
1887 char *tmp, **list, argterm[3];
1890 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1892 /* No command specified: display all available commands */
1894 for (y = 0; cmds[y].c; y++)
1895 list[count++] = xstrdup(cmds[y].c);
1898 complete_display(list, 0);
1900 for (y = 0; list[y] != NULL; y++)
1906 /* Prepare subset of commands that start with "cmd" */
1907 cmdlen = strlen(cmd);
1908 for (y = 0; cmds[y].c; y++) {
1909 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1910 list[count++] = xstrdup(cmds[y].c);
1919 /* Complete ambiguous command */
1920 tmp = complete_ambiguous(cmd, list, count);
1922 complete_display(list, 0);
1924 for (y = 0; list[y]; y++)
1929 tmplen = strlen(tmp);
1930 cmdlen = strlen(cmd);
1931 /* If cmd may be extended then do so */
1932 if (tmplen > cmdlen)
1933 if (el_insertstr(el, tmp + cmdlen) == -1)
1934 fatal("el_insertstr failed.");
1936 /* Terminate argument cleanly */
1940 argterm[y++] = quote;
1941 if (lastarg || *(lf->cursor) != ' ')
1944 if (y > 0 && el_insertstr(el, argterm) == -1)
1945 fatal("el_insertstr failed.");
1954 * Determine whether a particular sftp command's arguments (if any) represent
1955 * local or remote files. The "cmdarg" argument specifies the actual argument
1956 * and accepts values 1 or 2.
1959 complete_is_remote(char *cmd, int cmdarg) {
1965 for (i = 0; cmds[i].c; i++) {
1966 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) {
1969 else if (cmdarg == 2)
1978 /* Autocomplete a filename "file" */
1980 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1981 char *file, int remote, int lastarg, char quote, int terminated)
1984 char *tmp, *tmp2, ins[8];
1985 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1989 /* Glob from "file" location */
1993 xasprintf(&tmp, "%s*", file);
1995 /* Check if the path is absolute. */
1996 isabs = path_absolute(tmp);
1998 memset(&g, 0, sizeof(g));
1999 if (remote != LOCAL) {
2000 tmp2 = make_absolute_pwd_glob(tmp, remote_path);
2003 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
2005 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
2007 /* Determine length of pwd so we can trim completion display */
2008 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
2009 /* Terminate counting on first unescaped glob metacharacter */
2010 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
2011 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
2015 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
2017 if (tmp[tmplen] == '/')
2018 pwdlen = tmplen + 1; /* track last seen '/' */
2023 if (g.gl_matchc == 0)
2026 if (g.gl_matchc > 1)
2027 complete_display(g.gl_pathv, pwdlen);
2029 /* Don't try to extend globs */
2030 if (file == NULL || hadglob)
2033 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
2034 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
2040 tmplen = strlen(tmp);
2041 filelen = strlen(file);
2043 /* Count the number of escaped characters in the input string. */
2045 for (i = 0; i < filelen; i++) {
2046 if (!isesc && file[i] == '\\' && i + 1 < filelen){
2053 if (tmplen > (filelen - cesc)) {
2054 tmp2 = tmp + filelen - cesc;
2056 /* quote argument on way out */
2057 for (i = 0; i < len; i += clen) {
2058 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
2059 (size_t)clen > sizeof(ins) - 2)
2060 fatal("invalid multibyte character");
2062 memcpy(ins + 1, tmp2 + i, clen);
2063 ins[clen + 1] = '\0';
2073 if (quote == '\0' || tmp2[i] == quote) {
2074 if (el_insertstr(el, ins) == -1)
2075 fatal("el_insertstr "
2081 if (el_insertstr(el, ins + 1) == -1)
2082 fatal("el_insertstr failed.");
2089 if (g.gl_matchc == 1) {
2091 if (!terminated && quote != '\0')
2093 if (*(lf->cursor - 1) != '/' &&
2094 (lastarg || *(lf->cursor) != ' '))
2097 if (i > 0 && el_insertstr(el, ins) == -1)
2098 fatal("el_insertstr failed.");
2107 /* tab-completion hook function, called via libedit */
2108 static unsigned char
2109 complete(EditLine *el, int ch)
2111 char **argv, *line, quote;
2113 u_int cursor, len, terminated, ret = CC_ERROR;
2115 struct complete_ctx *complete_ctx;
2118 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2119 fatal_f("el_get failed");
2121 /* Figure out which argument the cursor points to */
2122 cursor = lf->cursor - lf->buffer;
2123 line = xmalloc(cursor + 1);
2124 memcpy(line, lf->buffer, cursor);
2125 line[cursor] = '\0';
2126 argv = makeargv(line, &carg, 1, "e, &terminated);
2129 /* Get all the arguments on the line */
2130 len = lf->lastchar - lf->buffer;
2131 line = xmalloc(len + 1);
2132 memcpy(line, lf->buffer, len);
2134 argv = makeargv(line, &argc, 1, NULL, NULL);
2136 /* Ensure cursor is at EOL or a argument boundary */
2137 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2138 line[cursor] != '\n') {
2144 /* Show all available commands */
2145 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2147 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2148 /* Handle the command parsing */
2149 if (complete_cmd_parse(el, argv[0], argc == carg,
2150 quote, terminated) != 0)
2152 } else if (carg >= 1) {
2153 /* Handle file parsing */
2155 int i = 0, cmdarg = 0;
2156 char *filematch = NULL;
2158 if (carg > 1 && line[cursor-1] != ' ')
2159 filematch = argv[carg - 1];
2161 for (i = 1; i < carg; i++) {
2163 if (argv[i][0] != '-')
2168 * If previous argument is complete, then offer completion
2171 if (line[cursor - 1] == ' ')
2174 remote = complete_is_remote(argv[0], cmdarg);
2176 if ((remote == REMOTE || remote == LOCAL) &&
2177 complete_match(el, complete_ctx->conn,
2178 *complete_ctx->remote_pathp, filematch,
2179 remote, carg == argc, quote, terminated) != 0)
2186 #endif /* USE_LIBEDIT */
2189 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2192 char *dir = NULL, *startdir = NULL;
2194 int err, interactive;
2195 EditLine *el = NULL;
2199 extern char *__progname;
2200 struct complete_ctx complete_ctx;
2202 if (!batchmode && isatty(STDIN_FILENO)) {
2203 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2204 fatal("Couldn't initialise editline");
2205 if ((hl = history_init()) == NULL)
2206 fatal("Couldn't initialise editline history");
2207 history(hl, &hev, H_SETSIZE, 100);
2208 el_set(el, EL_HIST, history, hl);
2210 el_set(el, EL_PROMPT, prompt);
2211 el_set(el, EL_EDITOR, "emacs");
2212 el_set(el, EL_TERMINAL, NULL);
2213 el_set(el, EL_SIGNAL, 1);
2214 el_source(el, NULL);
2216 /* Tab Completion */
2217 el_set(el, EL_ADDFN, "ftp-complete",
2218 "Context sensitive argument completion", complete);
2219 complete_ctx.conn = conn;
2220 complete_ctx.remote_pathp = &remote_path;
2221 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2222 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2223 /* enable ctrl-left-arrow and ctrl-right-arrow */
2224 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2225 el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL);
2226 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2227 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2228 /* make ^w match ksh behaviour */
2229 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2231 #endif /* USE_LIBEDIT */
2233 remote_path = do_realpath(conn, ".");
2234 if (remote_path == NULL)
2236 startdir = xstrdup(remote_path);
2238 if (file1 != NULL) {
2239 dir = xstrdup(file1);
2240 dir = make_absolute(dir, remote_path);
2242 if (remote_is_dir(conn, dir) && file2 == NULL) {
2244 mprintf("Changing to: %s\n", dir);
2245 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2246 if (parse_dispatch_command(conn, cmd,
2247 &remote_path, startdir, 1, 0) != 0) {
2255 /* XXX this is wrong wrt quoting */
2256 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2257 global_aflag ? " -a" : "", dir,
2258 file2 == NULL ? "" : " ",
2259 file2 == NULL ? "" : file2);
2260 err = parse_dispatch_command(conn, cmd,
2261 &remote_path, startdir, 1, 0);
2271 setvbuf(stdout, NULL, _IOLBF, 0);
2272 setvbuf(infile, NULL, _IOLBF, 0);
2274 interactive = !batchmode && isatty(STDIN_FILENO);
2277 struct sigaction sa;
2280 memset(&sa, 0, sizeof(sa));
2281 sa.sa_handler = interactive ? read_interrupt : killchild;
2282 if (sigaction(SIGINT, &sa, NULL) == -1) {
2283 debug3("sigaction(%s): %s", strsignal(SIGINT),
2290 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2302 if ((line = el_gets(el, &count)) == NULL ||
2309 history(hl, &hev, H_ENTER, line);
2310 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2311 fprintf(stderr, "Error: input line too long\n");
2314 #endif /* USE_LIBEDIT */
2317 cmd[strcspn(cmd, "\n")] = '\0';
2319 /* Handle user interrupts gracefully during commands */
2321 ssh_signal(SIGINT, cmd_interrupt);
2323 err = parse_dispatch_command(conn, cmd, &remote_path,
2324 startdir, batchmode, !interactive && el == NULL);
2328 ssh_signal(SIGCHLD, SIG_DFL);
2336 #endif /* USE_LIBEDIT */
2338 /* err == 1 signifies normal "quit" exit */
2339 return (err >= 0 ? 0 : -1);
2343 connect_to_server(char *path, char **args, int *in, int *out)
2347 int pin[2], pout[2];
2349 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2350 fatal("pipe: %s", strerror(errno));
2355 #else /* USE_PIPES */
2358 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2359 fatal("socketpair: %s", strerror(errno));
2360 *in = *out = inout[0];
2361 c_in = c_out = inout[1];
2362 #endif /* USE_PIPES */
2364 if ((sshpid = fork()) == -1)
2365 fatal("fork: %s", strerror(errno));
2366 else if (sshpid == 0) {
2367 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2368 (dup2(c_out, STDOUT_FILENO) == -1)) {
2369 fprintf(stderr, "dup2: %s\n", strerror(errno));
2378 * The underlying ssh is in the same process group, so we must
2379 * ignore SIGINT if we want to gracefully abort commands,
2380 * otherwise the signal will make it to the ssh process and
2381 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2382 * underlying ssh, it must *not* ignore that signal.
2384 ssh_signal(SIGINT, SIG_IGN);
2385 ssh_signal(SIGTERM, SIG_DFL);
2387 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2391 ssh_signal(SIGTERM, killchild);
2392 ssh_signal(SIGINT, killchild);
2393 ssh_signal(SIGHUP, killchild);
2394 ssh_signal(SIGTSTP, suspchild);
2395 ssh_signal(SIGTTIN, suspchild);
2396 ssh_signal(SIGTTOU, suspchild);
2397 ssh_signal(SIGCHLD, sigchld_handler);
2405 extern char *__progname;
2408 "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2409 " [-D sftp_server_command] [-F ssh_config] [-i identity_file]\n"
2410 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
2411 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
2412 " [-X sftp_option] destination\n",
2418 main(int argc, char **argv)
2420 int r, in, out, ch, err, tmp, port = -1, noisy = 0;
2421 char *host = NULL, *user, *cp, **cpp, *file2 = NULL;
2422 int debug_level = 0;
2423 char *file1 = NULL, *sftp_server = NULL;
2424 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2426 LogLevel ll = SYSLOG_LEVEL_INFO;
2429 extern char *optarg;
2430 struct sftp_conn *conn;
2431 size_t copy_buffer_len = 0;
2432 size_t num_requests = 0;
2433 long long llv, limit_kbps = 0;
2435 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2439 __progname = ssh_get_progname(argv[0]);
2440 memset(&args, '\0', sizeof(args));
2442 addargs(&args, "%s", ssh_program);
2443 addargs(&args, "-oForwardX11 no");
2444 addargs(&args, "-oPermitLocalCommand no");
2445 addargs(&args, "-oClearAllForwardings yes");
2447 ll = SYSLOG_LEVEL_INFO;
2450 while ((ch = getopt(argc, argv,
2451 "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:X:")) != -1) {
2453 /* Passed through to ssh(1) */
2458 addargs(&args, "-%c", ch);
2460 /* Passed through to ssh(1) with argument */
2466 addargs(&args, "-%c", ch);
2467 addargs(&args, "%s", optarg);
2470 ll = SYSLOG_LEVEL_ERROR;
2473 addargs(&args, "-%c", ch);
2476 port = a2port(optarg);
2478 fatal("Bad port \"%s\"\n", optarg);
2481 if (debug_level < 3) {
2482 addargs(&args, "-v");
2483 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2488 fatal("SSH protocol v.1 is no longer supported");
2491 /* accept silently */
2497 copy_buffer_len = strtol(optarg, &cp, 10);
2498 if (copy_buffer_len == 0 || *cp != '\0')
2499 fatal("Invalid buffer size \"%s\"", optarg);
2503 fatal("Batch file already specified.");
2505 /* Allow "-" as stdin */
2506 if (strcmp(optarg, "-") != 0 &&
2507 (infile = fopen(optarg, "r")) == NULL)
2508 fatal("%s (%s).", strerror(errno), optarg);
2510 quiet = batchmode = 1;
2511 addargs(&args, "-obatchmode yes");
2517 noisy = 1; /* Used to clear quiet mode after getopt */
2523 sftp_direct = optarg;
2526 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2530 limit_kbps *= 1024; /* kbps */
2536 num_requests = strtol(optarg, &cp, 10);
2537 if (num_requests == 0 || *cp != '\0')
2538 fatal("Invalid number of requests \"%s\"",
2542 sftp_server = optarg;
2545 ssh_program = optarg;
2546 replacearg(&args, 0, "%s", ssh_program);
2549 /* Please keep in sync with ssh.c -X */
2550 if (strncmp(optarg, "buffer=", 7) == 0) {
2551 r = scan_scaled(optarg + 7, &llv);
2552 if (r == 0 && (llv <= 0 || llv > 256 * 1024)) {
2557 fatal("Invalid buffer size \"%s\": %s",
2558 optarg + 7, strerror(errno));
2560 copy_buffer_len = (size_t)llv;
2561 } else if (strncmp(optarg, "nrequests=", 10) == 0) {
2562 llv = strtonum(optarg + 10, 1, 256 * 1024,
2564 if (errstr != NULL) {
2565 fatal("Invalid number of requests "
2566 "\"%s\": %s", optarg + 10, errstr);
2568 num_requests = (size_t)llv;
2570 fatal("Invalid -X option");
2579 /* Do this last because we want the user to be able to override it */
2580 addargs(&args, "-oForwardAgent no");
2582 if (!isatty(STDERR_FILENO))
2588 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2590 if (sftp_direct == NULL) {
2591 if (optind == argc || argc > (optind + 2))
2595 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2604 /* Try with user, host and path. */
2605 if (parse_user_host_path(*argv, &user, &host,
2608 /* Try with user and host. */
2609 if (parse_user_host_port(*argv, &user, &host, NULL)
2612 /* Treat as a plain hostname. */
2613 host = xstrdup(*argv);
2614 host = cleanhostname(host);
2617 file2 = *(argv + 1);
2620 fprintf(stderr, "Missing hostname\n");
2625 addargs(&args, "-oPort %d", port);
2627 addargs(&args, "-l");
2628 addargs(&args, "%s", user);
2631 /* no subsystem if the server-spec contains a '/' */
2632 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2633 addargs(&args, "-s");
2635 addargs(&args, "--");
2636 addargs(&args, "%s", host);
2637 addargs(&args, "%s", (sftp_server != NULL ?
2638 sftp_server : "sftp"));
2640 connect_to_server(ssh_program, args.list, &in, &out);
2642 if ((r = argv_split(sftp_direct, &tmp, &cpp, 1)) != 0)
2643 fatal_r(r, "Parse -D arguments");
2645 fatal("No sftp server specified via -D");
2646 connect_to_server(cpp[0], cpp, &in, &out);
2647 argv_free(cpp, tmp);
2651 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2653 fatal("Couldn't initialise connection to server");
2656 if (sftp_direct == NULL)
2657 fprintf(stderr, "Connected to %s.\n", host);
2659 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2662 err = interactive_loop(conn, file1, file2);
2664 #if !defined(USE_PIPES)
2665 shutdown(in, SHUT_RDWR);
2666 shutdown(out, SHUT_RDWR);
2674 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
2676 fatal("Couldn't wait for ssh process: %s",
2679 exit(err == 0 ? 0 : 1);