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(char *p, const char *pwd)
624 escpwd = escape_glob(pwd);
627 ret = make_absolute(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(xstrdup(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 tmp = make_absolute_pwd_glob(tmp, remote_path);
2001 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
2003 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
2005 /* Determine length of pwd so we can trim completion display */
2006 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
2007 /* Terminate counting on first unescaped glob metacharacter */
2008 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
2009 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
2013 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
2015 if (tmp[tmplen] == '/')
2016 pwdlen = tmplen + 1; /* track last seen '/' */
2021 if (g.gl_matchc == 0)
2024 if (g.gl_matchc > 1)
2025 complete_display(g.gl_pathv, pwdlen);
2027 /* Don't try to extend globs */
2028 if (file == NULL || hadglob)
2031 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
2032 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
2038 tmplen = strlen(tmp);
2039 filelen = strlen(file);
2041 /* Count the number of escaped characters in the input string. */
2043 for (i = 0; i < filelen; i++) {
2044 if (!isesc && file[i] == '\\' && i + 1 < filelen){
2051 if (tmplen > (filelen - cesc)) {
2052 tmp2 = tmp + filelen - cesc;
2054 /* quote argument on way out */
2055 for (i = 0; i < len; i += clen) {
2056 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
2057 (size_t)clen > sizeof(ins) - 2)
2058 fatal("invalid multibyte character");
2060 memcpy(ins + 1, tmp2 + i, clen);
2061 ins[clen + 1] = '\0';
2071 if (quote == '\0' || tmp2[i] == quote) {
2072 if (el_insertstr(el, ins) == -1)
2073 fatal("el_insertstr "
2079 if (el_insertstr(el, ins + 1) == -1)
2080 fatal("el_insertstr failed.");
2087 if (g.gl_matchc == 1) {
2089 if (!terminated && quote != '\0')
2091 if (*(lf->cursor - 1) != '/' &&
2092 (lastarg || *(lf->cursor) != ' '))
2095 if (i > 0 && el_insertstr(el, ins) == -1)
2096 fatal("el_insertstr failed.");
2105 /* tab-completion hook function, called via libedit */
2106 static unsigned char
2107 complete(EditLine *el, int ch)
2109 char **argv, *line, quote;
2111 u_int cursor, len, terminated, ret = CC_ERROR;
2113 struct complete_ctx *complete_ctx;
2116 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2117 fatal_f("el_get failed");
2119 /* Figure out which argument the cursor points to */
2120 cursor = lf->cursor - lf->buffer;
2121 line = xmalloc(cursor + 1);
2122 memcpy(line, lf->buffer, cursor);
2123 line[cursor] = '\0';
2124 argv = makeargv(line, &carg, 1, "e, &terminated);
2127 /* Get all the arguments on the line */
2128 len = lf->lastchar - lf->buffer;
2129 line = xmalloc(len + 1);
2130 memcpy(line, lf->buffer, len);
2132 argv = makeargv(line, &argc, 1, NULL, NULL);
2134 /* Ensure cursor is at EOL or a argument boundary */
2135 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2136 line[cursor] != '\n') {
2142 /* Show all available commands */
2143 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2145 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2146 /* Handle the command parsing */
2147 if (complete_cmd_parse(el, argv[0], argc == carg,
2148 quote, terminated) != 0)
2150 } else if (carg >= 1) {
2151 /* Handle file parsing */
2153 int i = 0, cmdarg = 0;
2154 char *filematch = NULL;
2156 if (carg > 1 && line[cursor-1] != ' ')
2157 filematch = argv[carg - 1];
2159 for (i = 1; i < carg; i++) {
2161 if (argv[i][0] != '-')
2166 * If previous argument is complete, then offer completion
2169 if (line[cursor - 1] == ' ')
2172 remote = complete_is_remote(argv[0], cmdarg);
2174 if ((remote == REMOTE || remote == LOCAL) &&
2175 complete_match(el, complete_ctx->conn,
2176 *complete_ctx->remote_pathp, filematch,
2177 remote, carg == argc, quote, terminated) != 0)
2184 #endif /* USE_LIBEDIT */
2187 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2190 char *dir = NULL, *startdir = NULL;
2192 int err, interactive;
2193 EditLine *el = NULL;
2197 extern char *__progname;
2198 struct complete_ctx complete_ctx;
2200 if (!batchmode && isatty(STDIN_FILENO)) {
2201 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2202 fatal("Couldn't initialise editline");
2203 if ((hl = history_init()) == NULL)
2204 fatal("Couldn't initialise editline history");
2205 history(hl, &hev, H_SETSIZE, 100);
2206 el_set(el, EL_HIST, history, hl);
2208 el_set(el, EL_PROMPT, prompt);
2209 el_set(el, EL_EDITOR, "emacs");
2210 el_set(el, EL_TERMINAL, NULL);
2211 el_set(el, EL_SIGNAL, 1);
2212 el_source(el, NULL);
2214 /* Tab Completion */
2215 el_set(el, EL_ADDFN, "ftp-complete",
2216 "Context sensitive argument completion", complete);
2217 complete_ctx.conn = conn;
2218 complete_ctx.remote_pathp = &remote_path;
2219 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2220 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2221 /* enable ctrl-left-arrow and ctrl-right-arrow */
2222 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2223 el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL);
2224 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2225 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2226 /* make ^w match ksh behaviour */
2227 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2229 #endif /* USE_LIBEDIT */
2231 remote_path = do_realpath(conn, ".");
2232 if (remote_path == NULL)
2234 startdir = xstrdup(remote_path);
2236 if (file1 != NULL) {
2237 dir = xstrdup(file1);
2238 dir = make_absolute(dir, remote_path);
2240 if (remote_is_dir(conn, dir) && file2 == NULL) {
2242 mprintf("Changing to: %s\n", dir);
2243 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2244 if (parse_dispatch_command(conn, cmd,
2245 &remote_path, startdir, 1, 0) != 0) {
2253 /* XXX this is wrong wrt quoting */
2254 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2255 global_aflag ? " -a" : "", dir,
2256 file2 == NULL ? "" : " ",
2257 file2 == NULL ? "" : file2);
2258 err = parse_dispatch_command(conn, cmd,
2259 &remote_path, startdir, 1, 0);
2269 setvbuf(stdout, NULL, _IOLBF, 0);
2270 setvbuf(infile, NULL, _IOLBF, 0);
2272 interactive = !batchmode && isatty(STDIN_FILENO);
2275 struct sigaction sa;
2278 memset(&sa, 0, sizeof(sa));
2279 sa.sa_handler = interactive ? read_interrupt : killchild;
2280 if (sigaction(SIGINT, &sa, NULL) == -1) {
2281 debug3("sigaction(%s): %s", strsignal(SIGINT),
2288 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2300 if ((line = el_gets(el, &count)) == NULL ||
2307 history(hl, &hev, H_ENTER, line);
2308 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2309 fprintf(stderr, "Error: input line too long\n");
2312 #endif /* USE_LIBEDIT */
2315 cmd[strcspn(cmd, "\n")] = '\0';
2317 /* Handle user interrupts gracefully during commands */
2319 ssh_signal(SIGINT, cmd_interrupt);
2321 err = parse_dispatch_command(conn, cmd, &remote_path,
2322 startdir, batchmode, !interactive && el == NULL);
2326 ssh_signal(SIGCHLD, SIG_DFL);
2334 #endif /* USE_LIBEDIT */
2336 /* err == 1 signifies normal "quit" exit */
2337 return (err >= 0 ? 0 : -1);
2341 connect_to_server(char *path, char **args, int *in, int *out)
2345 int pin[2], pout[2];
2347 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2348 fatal("pipe: %s", strerror(errno));
2353 #else /* USE_PIPES */
2356 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2357 fatal("socketpair: %s", strerror(errno));
2358 *in = *out = inout[0];
2359 c_in = c_out = inout[1];
2360 #endif /* USE_PIPES */
2362 if ((sshpid = fork()) == -1)
2363 fatal("fork: %s", strerror(errno));
2364 else if (sshpid == 0) {
2365 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2366 (dup2(c_out, STDOUT_FILENO) == -1)) {
2367 fprintf(stderr, "dup2: %s\n", strerror(errno));
2376 * The underlying ssh is in the same process group, so we must
2377 * ignore SIGINT if we want to gracefully abort commands,
2378 * otherwise the signal will make it to the ssh process and
2379 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2380 * underlying ssh, it must *not* ignore that signal.
2382 ssh_signal(SIGINT, SIG_IGN);
2383 ssh_signal(SIGTERM, SIG_DFL);
2385 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2389 ssh_signal(SIGTERM, killchild);
2390 ssh_signal(SIGINT, killchild);
2391 ssh_signal(SIGHUP, killchild);
2392 ssh_signal(SIGTSTP, suspchild);
2393 ssh_signal(SIGTTIN, suspchild);
2394 ssh_signal(SIGTTOU, suspchild);
2395 ssh_signal(SIGCHLD, sigchld_handler);
2403 extern char *__progname;
2406 "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2407 " [-D sftp_server_command] [-F ssh_config] [-i identity_file]\n"
2408 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
2409 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
2410 " [-X sftp_option] destination\n",
2416 main(int argc, char **argv)
2418 int r, in, out, ch, err, tmp, port = -1, noisy = 0;
2419 char *host = NULL, *user, *cp, **cpp, *file2 = NULL;
2420 int debug_level = 0;
2421 char *file1 = NULL, *sftp_server = NULL;
2422 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2424 LogLevel ll = SYSLOG_LEVEL_INFO;
2427 extern char *optarg;
2428 struct sftp_conn *conn;
2429 size_t copy_buffer_len = 0;
2430 size_t num_requests = 0;
2431 long long llv, limit_kbps = 0;
2433 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2437 __progname = ssh_get_progname(argv[0]);
2438 memset(&args, '\0', sizeof(args));
2440 addargs(&args, "%s", ssh_program);
2441 addargs(&args, "-oForwardX11 no");
2442 addargs(&args, "-oPermitLocalCommand no");
2443 addargs(&args, "-oClearAllForwardings yes");
2445 ll = SYSLOG_LEVEL_INFO;
2448 while ((ch = getopt(argc, argv,
2449 "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:X:")) != -1) {
2451 /* Passed through to ssh(1) */
2456 addargs(&args, "-%c", ch);
2458 /* Passed through to ssh(1) with argument */
2464 addargs(&args, "-%c", ch);
2465 addargs(&args, "%s", optarg);
2468 ll = SYSLOG_LEVEL_ERROR;
2471 addargs(&args, "-%c", ch);
2474 port = a2port(optarg);
2476 fatal("Bad port \"%s\"\n", optarg);
2479 if (debug_level < 3) {
2480 addargs(&args, "-v");
2481 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2486 fatal("SSH protocol v.1 is no longer supported");
2489 /* accept silently */
2495 copy_buffer_len = strtol(optarg, &cp, 10);
2496 if (copy_buffer_len == 0 || *cp != '\0')
2497 fatal("Invalid buffer size \"%s\"", optarg);
2501 fatal("Batch file already specified.");
2503 /* Allow "-" as stdin */
2504 if (strcmp(optarg, "-") != 0 &&
2505 (infile = fopen(optarg, "r")) == NULL)
2506 fatal("%s (%s).", strerror(errno), optarg);
2508 quiet = batchmode = 1;
2509 addargs(&args, "-obatchmode yes");
2515 noisy = 1; /* Used to clear quiet mode after getopt */
2521 sftp_direct = optarg;
2524 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2528 limit_kbps *= 1024; /* kbps */
2534 num_requests = strtol(optarg, &cp, 10);
2535 if (num_requests == 0 || *cp != '\0')
2536 fatal("Invalid number of requests \"%s\"",
2540 sftp_server = optarg;
2543 ssh_program = optarg;
2544 replacearg(&args, 0, "%s", ssh_program);
2547 /* Please keep in sync with ssh.c -X */
2548 if (strncmp(optarg, "buffer=", 7) == 0) {
2549 r = scan_scaled(optarg + 7, &llv);
2550 if (r == 0 && (llv <= 0 || llv > 256 * 1024)) {
2555 fatal("Invalid buffer size \"%s\": %s",
2556 optarg + 7, strerror(errno));
2558 copy_buffer_len = (size_t)llv;
2559 } else if (strncmp(optarg, "nrequests=", 10) == 0) {
2560 llv = strtonum(optarg + 10, 1, 256 * 1024,
2562 if (errstr != NULL) {
2563 fatal("Invalid number of requests "
2564 "\"%s\": %s", optarg + 10, errstr);
2566 num_requests = (size_t)llv;
2568 fatal("Invalid -X option");
2577 /* Do this last because we want the user to be able to override it */
2578 addargs(&args, "-oForwardAgent no");
2580 if (!isatty(STDERR_FILENO))
2586 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2588 if (sftp_direct == NULL) {
2589 if (optind == argc || argc > (optind + 2))
2593 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2602 /* Try with user, host and path. */
2603 if (parse_user_host_path(*argv, &user, &host,
2606 /* Try with user and host. */
2607 if (parse_user_host_port(*argv, &user, &host, NULL)
2610 /* Treat as a plain hostname. */
2611 host = xstrdup(*argv);
2612 host = cleanhostname(host);
2615 file2 = *(argv + 1);
2618 fprintf(stderr, "Missing hostname\n");
2623 addargs(&args, "-oPort %d", port);
2625 addargs(&args, "-l");
2626 addargs(&args, "%s", user);
2629 /* no subsystem if the server-spec contains a '/' */
2630 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2631 addargs(&args, "-s");
2633 addargs(&args, "--");
2634 addargs(&args, "%s", host);
2635 addargs(&args, "%s", (sftp_server != NULL ?
2636 sftp_server : "sftp"));
2638 connect_to_server(ssh_program, args.list, &in, &out);
2640 if ((r = argv_split(sftp_direct, &tmp, &cpp, 1)) != 0)
2641 fatal_r(r, "Parse -D arguments");
2643 fatal("No sftp server specified via -D");
2644 connect_to_server(cpp[0], cpp, &in, &out);
2645 argv_free(cpp, tmp);
2649 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2651 fatal("Couldn't initialise connection to server");
2654 if (sftp_direct == NULL)
2655 fprintf(stderr, "Connected to %s.\n", host);
2657 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2660 err = interactive_loop(conn, file1, file2);
2662 #if !defined(USE_PIPES)
2663 shutdown(in, SHUT_RDWR);
2664 shutdown(out, SHUT_RDWR);
2672 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
2674 fatal("Couldn't wait for ssh process: %s",
2677 exit(err == 0 ? 0 : 1);