1 /* $OpenBSD: sftp.c,v 1.236 2023/09/10 23:12:32 djm Exp $ */
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
25 #include <sys/socket.h>
27 #ifdef HAVE_SYS_STATVFS_H
28 #include <sys/statvfs.h>
46 typedef void EditLine;
62 #include "pathnames.h"
69 #include "sftp-common.h"
70 #include "sftp-client.h"
71 #include "sftp-usergroup.h"
73 /* File to read commands from */
76 /* Are we in batchfile mode? */
79 /* PID of ssh transport process */
80 static volatile pid_t sshpid = -1;
82 /* Suppress diagnostic messages */
85 /* This is set to 0 if the progressmeter is not desired. */
88 /* When this option is set, we always recursively download/upload directories */
91 /* When this option is set, we resume download or upload if possible */
94 /* When this option is set, the file transfers will always preserve times */
97 /* When this option is set, transfers will have fsync() called on each file */
100 /* SIGINT received during command processing */
101 volatile sig_atomic_t interrupted = 0;
103 /* I wish qsort() took a separate ctx for the comparison function...*/
107 /* Context used for commandline completion */
108 struct complete_ctx {
109 struct sftp_conn *conn;
113 int sftp_glob(struct sftp_conn *, const char *, int,
114 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
116 extern char *__progname;
118 /* Separators for interactive commands */
119 #define WHITESPACE " \t\r\n"
122 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
123 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
124 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
125 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
126 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
127 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
128 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
129 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
130 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
132 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
133 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
135 /* Commands for interactive mode */
170 const int t; /* Completion type for the first argument */
171 const int t2; /* completion type for the optional second argument */
174 /* Type of completion */
179 static const struct CMD cmds[] = {
180 { "bye", I_QUIT, NOARGS, NOARGS },
181 { "cd", I_CHDIR, REMOTE, NOARGS },
182 { "chdir", I_CHDIR, REMOTE, NOARGS },
183 { "chgrp", I_CHGRP, REMOTE, NOARGS },
184 { "chmod", I_CHMOD, REMOTE, NOARGS },
185 { "chown", I_CHOWN, REMOTE, NOARGS },
186 { "copy", I_COPY, REMOTE, LOCAL },
187 { "cp", I_COPY, REMOTE, LOCAL },
188 { "df", I_DF, REMOTE, NOARGS },
189 { "dir", I_LS, REMOTE, NOARGS },
190 { "exit", I_QUIT, NOARGS, NOARGS },
191 { "get", I_GET, REMOTE, LOCAL },
192 { "help", I_HELP, NOARGS, NOARGS },
193 { "lcd", I_LCHDIR, LOCAL, NOARGS },
194 { "lchdir", I_LCHDIR, LOCAL, NOARGS },
195 { "lls", I_LLS, LOCAL, NOARGS },
196 { "lmkdir", I_LMKDIR, LOCAL, NOARGS },
197 { "ln", I_LINK, REMOTE, REMOTE },
198 { "lpwd", I_LPWD, LOCAL, NOARGS },
199 { "ls", I_LS, REMOTE, NOARGS },
200 { "lumask", I_LUMASK, NOARGS, NOARGS },
201 { "mkdir", I_MKDIR, REMOTE, NOARGS },
202 { "mget", I_GET, REMOTE, LOCAL },
203 { "mput", I_PUT, LOCAL, REMOTE },
204 { "progress", I_PROGRESS, NOARGS, NOARGS },
205 { "put", I_PUT, LOCAL, REMOTE },
206 { "pwd", I_PWD, REMOTE, NOARGS },
207 { "quit", I_QUIT, NOARGS, NOARGS },
208 { "reget", I_REGET, REMOTE, LOCAL },
209 { "rename", I_RENAME, REMOTE, REMOTE },
210 { "reput", I_REPUT, LOCAL, REMOTE },
211 { "rm", I_RM, REMOTE, NOARGS },
212 { "rmdir", I_RMDIR, REMOTE, NOARGS },
213 { "symlink", I_SYMLINK, REMOTE, REMOTE },
214 { "version", I_VERSION, NOARGS, NOARGS },
215 { "!", I_SHELL, NOARGS, NOARGS },
216 { "?", I_HELP, NOARGS, NOARGS },
228 (void)waitpid(pid, NULL, 0);
239 while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR)
242 kill(getpid(), SIGSTOP);
246 cmd_interrupt(int signo)
248 const char msg[] = "\rInterrupt \n";
249 int olderrno = errno;
251 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
257 read_interrupt(int signo)
263 sigchld_handler(int sig)
265 int save_errno = errno;
267 const char msg[] = "\rConnection closed. \n";
269 /* Report if ssh transport process dies. */
270 while ((pid = waitpid(sshpid, NULL, WNOHANG)) == -1 && errno == EINTR)
274 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
284 printf("Available commands:\n"
286 "cd path Change remote directory to 'path'\n"
287 "chgrp [-h] grp path Change group of file 'path' to 'grp'\n"
288 "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n"
289 "chown [-h] own path Change owner of file 'path' to 'own'\n"
290 "copy oldpath newpath Copy remote file\n"
291 "cp oldpath newpath Copy remote file\n"
292 "df [-hi] [path] Display statistics for current directory or\n"
293 " filesystem containing 'path'\n"
295 "get [-afpR] remote [local] Download file\n"
296 "help Display this help text\n"
297 "lcd path Change local directory to 'path'\n"
298 "lls [ls-options [path]] Display local directory listing\n"
299 "lmkdir path Create local directory\n"
300 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
301 "lpwd Print local working directory\n"
302 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
303 "lumask umask Set local umask to 'umask'\n"
304 "mkdir path Create remote directory\n"
305 "progress Toggle display of progress meter\n"
306 "put [-afpR] local [remote] Upload file\n"
307 "pwd Display remote working directory\n"
309 "reget [-fpR] remote [local] Resume download file\n"
310 "rename oldpath newpath Rename remote file\n"
311 "reput [-fpR] local [remote] Resume upload file\n"
312 "rm path Delete remote file\n"
313 "rmdir path Remove remote directory\n"
314 "symlink oldpath newpath Symlink remote file\n"
315 "version Show SFTP version\n"
316 "!command Execute 'command' in local shell\n"
317 "! Escape to local shell\n"
318 "? Synonym for help\n");
322 local_do_shell(const char *args)
331 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
332 shell = _PATH_BSHELL;
334 if ((pid = fork()) == -1)
335 fatal("Couldn't fork: %s", strerror(errno));
338 /* XXX: child has pipe fds to ssh subproc open - issue? */
340 debug3("Executing %s -c \"%s\"", shell, args);
341 execl(shell, shell, "-c", args, (char *)NULL);
343 debug3("Executing %s", shell);
344 execl(shell, shell, (char *)NULL);
346 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
350 while (waitpid(pid, &status, 0) == -1)
352 fatal("Couldn't wait for child: %s", strerror(errno));
353 if (!WIFEXITED(status))
354 error("Shell exited abnormally");
355 else if (WEXITSTATUS(status))
356 error("Shell exited with status %d", WEXITSTATUS(status));
360 local_do_ls(const char *args)
363 local_do_shell(_PATH_LS);
365 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
366 char *buf = xmalloc(len);
368 /* XXX: quoting - rip quoting code from ftp? */
369 snprintf(buf, len, _PATH_LS " %s", args);
375 /* Strip one path (usually the pwd) from the start of another */
377 path_strip(const char *path, const char *strip)
382 return (xstrdup(path));
385 if (strncmp(path, strip, len) == 0) {
386 if (strip[len - 1] != '/' && path[len] == '/')
388 return (xstrdup(path + len));
391 return (xstrdup(path));
395 parse_getput_flags(const char *cmd, char **argv, int argc,
396 int *aflag, int *fflag, int *pflag, int *rflag)
398 extern int opterr, optind, optopt, optreset;
401 optind = optreset = 1;
404 *aflag = *fflag = *rflag = *pflag = 0;
405 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
422 error("%s: Invalid flag -%c", cmd, optopt);
431 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
433 extern int opterr, optind, optopt, optreset;
436 optind = optreset = 1;
440 while ((ch = getopt(argc, argv, "s")) != -1) {
446 error("%s: Invalid flag -%c", cmd, optopt);
455 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
457 extern int opterr, optind, optopt, optreset;
460 optind = optreset = 1;
464 while ((ch = getopt(argc, argv, "l")) != -1) {
470 error("%s: Invalid flag -%c", cmd, optopt);
479 parse_ls_flags(char **argv, int argc, int *lflag)
481 extern int opterr, optind, optopt, optreset;
484 optind = optreset = 1;
487 *lflag = LS_NAME_SORT;
488 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
491 *lflag &= ~VIEW_FLAGS;
492 *lflag |= LS_SHORT_VIEW;
495 *lflag &= ~SORT_FLAGS;
496 *lflag |= LS_SIZE_SORT;
499 *lflag |= LS_SHOW_ALL;
502 *lflag &= ~SORT_FLAGS;
505 *lflag |= LS_SI_UNITS;
508 *lflag &= ~LS_SHORT_VIEW;
509 *lflag |= LS_LONG_VIEW;
512 *lflag &= ~LS_SHORT_VIEW;
513 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
516 *lflag |= LS_REVERSE_SORT;
519 *lflag &= ~SORT_FLAGS;
520 *lflag |= LS_TIME_SORT;
523 error("ls: Invalid flag -%c", optopt);
532 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
534 extern int opterr, optind, optopt, optreset;
537 optind = optreset = 1;
541 while ((ch = getopt(argc, argv, "hi")) != -1) {
550 error("%s: Invalid flag -%c", cmd, optopt);
559 parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag)
561 extern int opterr, optind, optopt, optreset;
564 optind = optreset = 1;
568 while ((ch = getopt(argc, argv, "h")) != -1) {
574 error("%s: Invalid flag -%c", cmd, optopt);
583 parse_no_flags(const char *cmd, char **argv, int argc)
585 extern int opterr, optind, optopt, optreset;
588 optind = optreset = 1;
591 while ((ch = getopt(argc, argv, "")) != -1) {
594 error("%s: Invalid flag -%c", cmd, optopt);
603 escape_glob(const char *s)
609 ret = xcalloc(2, len + 1);
610 for (i = o = 0; i < len; i++) {
611 if (strchr("[]?*\\", s[i]) != NULL)
620 * Arg p must be dynamically allocated. make_absolute will either return it
621 * or free it and allocate a new one. Caller must free returned string.
624 make_absolute_pwd_glob(char *p, const char *pwd)
628 escpwd = escape_glob(pwd);
631 ret = sftp_make_absolute(p, escpwd);
637 local_is_dir(const char *path)
641 if (stat(path, &sb) == -1)
643 return S_ISDIR(sb.st_mode);
647 process_get(struct sftp_conn *conn, const char *src, const char *dst,
648 const char *pwd, int pflag, int rflag, int resume, int fflag)
650 char *filename, *abs_src = NULL, *abs_dst = NULL, *tmp = NULL;
654 abs_src = make_absolute_pwd_glob(xstrdup(src), pwd);
655 memset(&g, 0, sizeof(g));
657 debug3("Looking up %s", abs_src);
658 if ((r = sftp_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
659 if (r == GLOB_NOSPACE) {
660 error("Too many matches for \"%s\".", abs_src);
662 error("File \"%s\" not found.", abs_src);
669 * If multiple matches then dst must be a directory or
672 if (g.gl_matchc > 1 && dst != NULL && !local_is_dir(dst)) {
673 error("Multiple source paths, but destination "
674 "\"%s\" is not a directory", dst);
679 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
680 tmp = xstrdup(g.gl_pathv[i]);
681 if ((filename = basename(tmp)) == NULL) {
682 error("basename %s: %s", tmp, strerror(errno));
688 if (g.gl_matchc == 1 && dst) {
689 if (local_is_dir(dst)) {
690 abs_dst = sftp_path_append(dst, filename);
692 abs_dst = xstrdup(dst);
695 abs_dst = sftp_path_append(dst, filename);
697 abs_dst = xstrdup(filename);
701 resume |= global_aflag;
702 if (!quiet && resume)
703 mprintf("Resuming %s to %s\n",
704 g.gl_pathv[i], abs_dst);
705 else if (!quiet && !resume)
706 mprintf("Fetching %s to %s\n",
707 g.gl_pathv[i], abs_dst);
708 /* XXX follow link flag */
709 if (sftp_globpath_is_dir(g.gl_pathv[i]) &&
710 (rflag || global_rflag)) {
711 if (sftp_download_dir(conn, g.gl_pathv[i], abs_dst,
712 NULL, pflag || global_pflag, 1, resume,
713 fflag || global_fflag, 0, 0) == -1)
716 if (sftp_download(conn, g.gl_pathv[i], abs_dst, NULL,
717 pflag || global_pflag, resume,
718 fflag || global_fflag, 0) == -1)
732 process_put(struct sftp_conn *conn, const char *src, const char *dst,
733 const char *pwd, int pflag, int rflag, int resume, int fflag)
735 char *tmp_dst = NULL;
736 char *abs_dst = NULL;
737 char *tmp = NULL, *filename = NULL;
740 int i, dst_is_dir = 1;
744 tmp_dst = xstrdup(dst);
745 tmp_dst = sftp_make_absolute(tmp_dst, pwd);
748 memset(&g, 0, sizeof(g));
749 debug3("Looking up %s", src);
750 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
751 error("File \"%s\" not found.", src);
756 /* If we aren't fetching to pwd then stash this status for later */
758 dst_is_dir = sftp_remote_is_dir(conn, tmp_dst);
760 /* If multiple matches, dst may be directory or unspecified */
761 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
762 error("Multiple paths match, but destination "
763 "\"%s\" is not a directory", tmp_dst);
768 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
769 if (stat(g.gl_pathv[i], &sb) == -1) {
771 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
775 tmp = xstrdup(g.gl_pathv[i]);
776 if ((filename = basename(tmp)) == NULL) {
777 error("basename %s: %s", tmp, strerror(errno));
785 if (g.gl_matchc == 1 && tmp_dst) {
786 /* If directory specified, append filename */
788 abs_dst = sftp_path_append(tmp_dst, filename);
790 abs_dst = xstrdup(tmp_dst);
791 } else if (tmp_dst) {
792 abs_dst = sftp_path_append(tmp_dst, filename);
794 abs_dst = sftp_make_absolute(xstrdup(filename), pwd);
798 resume |= global_aflag;
799 if (!quiet && resume)
800 mprintf("Resuming upload of %s to %s\n",
801 g.gl_pathv[i], abs_dst);
802 else if (!quiet && !resume)
803 mprintf("Uploading %s to %s\n",
804 g.gl_pathv[i], abs_dst);
805 /* XXX follow_link_flag */
806 if (sftp_globpath_is_dir(g.gl_pathv[i]) &&
807 (rflag || global_rflag)) {
808 if (sftp_upload_dir(conn, g.gl_pathv[i], abs_dst,
809 pflag || global_pflag, 1, resume,
810 fflag || global_fflag, 0, 0) == -1)
813 if (sftp_upload(conn, g.gl_pathv[i], abs_dst,
814 pflag || global_pflag, resume,
815 fflag || global_fflag, 0) == -1)
828 sdirent_comp(const void *aa, const void *bb)
830 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
831 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
832 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
834 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
835 if (sort_flag & LS_NAME_SORT)
836 return (rmul * strcmp(a->filename, b->filename));
837 else if (sort_flag & LS_TIME_SORT)
838 return (rmul * NCMP(a->a.mtime, b->a.mtime));
839 else if (sort_flag & LS_SIZE_SORT)
840 return (rmul * NCMP(a->a.size, b->a.size));
842 fatal("Unknown ls sort type");
845 /* sftp ls.1 replacement for directories */
847 do_ls_dir(struct sftp_conn *conn, const char *path,
848 const char *strip_path, int lflag)
851 u_int c = 1, colspace = 0, columns = 1;
854 if ((n = sftp_readdir(conn, path, &d)) != 0)
857 if (!(lflag & LS_SHORT_VIEW)) {
858 u_int m = 0, width = 80;
862 /* Count entries for sort and find longest filename */
863 for (n = 0; d[n] != NULL; n++) {
864 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
865 m = MAXIMUM(m, strlen(d[n]->filename));
868 /* Add any subpath that also needs to be counted */
869 tmp = path_strip(path, strip_path);
873 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
876 columns = width / (m + 2);
877 columns = MAXIMUM(columns, 1);
878 colspace = width / columns;
879 colspace = MINIMUM(colspace, width);
882 if (lflag & SORT_FLAGS) {
883 for (n = 0; d[n] != NULL; n++)
884 ; /* count entries */
885 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
886 qsort(d, n, sizeof(*d), sdirent_comp);
889 get_remote_user_groups_from_dirents(conn, d);
890 for (n = 0; d[n] != NULL && !interrupted; n++) {
893 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
896 tmp = sftp_path_append(path, d[n]->filename);
897 fname = path_strip(tmp, strip_path);
900 if (lflag & LS_LONG_VIEW) {
901 if ((lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) != 0 ||
902 sftp_can_get_users_groups_by_id(conn)) {
906 memset(&sb, 0, sizeof(sb));
907 attrib_to_stat(&d[n]->a, &sb);
908 lname = ls_file(fname, &sb, 1,
909 (lflag & LS_SI_UNITS),
910 ruser_name(sb.st_uid),
911 rgroup_name(sb.st_gid));
912 mprintf("%s\n", lname);
915 mprintf("%s\n", d[n]->longname);
917 mprintf("%-*s", colspace, fname);
928 if (!(lflag & LS_LONG_VIEW) && (c != 1))
931 sftp_free_dirents(d);
936 sglob_comp(const void *aa, const void *bb)
938 u_int a = *(const u_int *)aa;
939 u_int b = *(const u_int *)bb;
940 const char *ap = sort_glob->gl_pathv[a];
941 const char *bp = sort_glob->gl_pathv[b];
942 const struct stat *as = sort_glob->gl_statv[a];
943 const struct stat *bs = sort_glob->gl_statv[b];
944 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
946 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
947 if (sort_flag & LS_NAME_SORT)
948 return (rmul * strcmp(ap, bp));
949 else if (sort_flag & LS_TIME_SORT) {
950 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
951 if (timespeccmp(&as->st_mtim, &bs->st_mtim, ==))
953 return timespeccmp(&as->st_mtim, &bs->st_mtim, <) ?
955 #elif defined(HAVE_STRUCT_STAT_ST_MTIME)
956 return (rmul * NCMP(as->st_mtime, bs->st_mtime));
960 } else if (sort_flag & LS_SIZE_SORT)
961 return (rmul * NCMP(as->st_size, bs->st_size));
963 fatal("Unknown ls sort type");
966 /* sftp ls.1 replacement which handles path globs */
968 do_globbed_ls(struct sftp_conn *conn, const char *path,
969 const char *strip_path, int lflag)
975 u_int i, j, nentries, *indices = NULL, c = 1;
976 u_int colspace = 0, columns = 1, m = 0, width = 80;
978 memset(&g, 0, sizeof(g));
980 if ((r = sftp_glob(conn, path,
981 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
983 (g.gl_pathc && !g.gl_matchc)) {
986 if (r == GLOB_NOSPACE) {
987 error("Can't ls: Too many matches for \"%s\"", path);
989 error("Can't ls: \"%s\" not found", path);
998 * If the glob returns a single match and it is a directory,
999 * then just list its contents.
1001 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
1002 S_ISDIR(g.gl_statv[0]->st_mode)) {
1003 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
1008 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1011 if (!(lflag & LS_SHORT_VIEW)) {
1012 /* Count entries for sort and find longest filename */
1013 for (i = 0; g.gl_pathv[i]; i++)
1014 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
1016 columns = width / (m + 2);
1017 columns = MAXIMUM(columns, 1);
1018 colspace = width / columns;
1022 * Sorting: rather than mess with the contents of glob_t, prepare
1023 * an array of indices into it and sort that. For the usual
1024 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
1026 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
1027 ; /* count entries */
1028 indices = xcalloc(nentries, sizeof(*indices));
1029 for (i = 0; i < nentries; i++)
1032 if (lflag & SORT_FLAGS) {
1034 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
1035 qsort(indices, nentries, sizeof(*indices), sglob_comp);
1039 get_remote_user_groups_from_glob(conn, &g);
1040 for (j = 0; j < nentries && !interrupted; j++) {
1042 fname = path_strip(g.gl_pathv[i], strip_path);
1043 if (lflag & LS_LONG_VIEW) {
1044 if (g.gl_statv[i] == NULL) {
1045 error("no stat information for %s", fname);
1049 lname = ls_file(fname, g.gl_statv[i], 1,
1050 (lflag & LS_SI_UNITS),
1051 ruser_name(g.gl_statv[i]->st_uid),
1052 rgroup_name(g.gl_statv[i]->st_gid));
1053 mprintf("%s\n", lname);
1056 mprintf("%-*s", colspace, fname);
1066 if (!(lflag & LS_LONG_VIEW) && (c != 1))
1078 do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
1080 struct sftp_statvfs st;
1081 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
1082 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
1083 char s_icapacity[16], s_dcapacity[16];
1085 if (sftp_statvfs(conn, path, &st, 1) == -1)
1087 if (st.f_files == 0)
1088 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1090 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1091 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1094 if (st.f_blocks == 0)
1095 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1097 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1098 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1102 printf(" Inodes Used Avail "
1103 "(root) %%Capacity\n");
1104 printf("%11llu %11llu %11llu %11llu %s\n",
1105 (unsigned long long)st.f_files,
1106 (unsigned long long)(st.f_files - st.f_ffree),
1107 (unsigned long long)st.f_favail,
1108 (unsigned long long)st.f_ffree, s_icapacity);
1110 strlcpy(s_used, "error", sizeof(s_used));
1111 strlcpy(s_avail, "error", sizeof(s_avail));
1112 strlcpy(s_root, "error", sizeof(s_root));
1113 strlcpy(s_total, "error", sizeof(s_total));
1114 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1115 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1116 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1117 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1118 printf(" Size Used Avail (root) %%Capacity\n");
1119 printf("%7sB %7sB %7sB %7sB %s\n",
1120 s_total, s_used, s_avail, s_root, s_dcapacity);
1122 printf(" Size Used Avail "
1123 "(root) %%Capacity\n");
1124 printf("%12llu %12llu %12llu %12llu %s\n",
1125 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1126 (unsigned long long)(st.f_frsize *
1127 (st.f_blocks - st.f_bfree) / 1024),
1128 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1129 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1136 * Undo escaping of glob sequences in place. Used to undo extra escaping
1137 * applied in makeargv() when the string is destined for a function that
1141 undo_glob_escape(char *s)
1176 * Split a string into an argument vector using sh(1)-style quoting,
1177 * comment and escaping rules, but with some tweaks to handle glob(3)
1179 * The "sloppy" flag allows for recovery from missing terminating quote, for
1180 * use in parsing incomplete commandlines during tab autocompletion.
1182 * Returns NULL on error or a NULL-terminated array of arguments.
1184 * If "lastquote" is not NULL, the quoting character used for the last
1185 * argument is placed in *lastquote ("\0", "'" or "\"").
1187 * If "terminated" is not NULL, *terminated will be set to 1 when the
1188 * last argument's quote has been properly terminated or 0 otherwise.
1189 * This parameter is only of use if "sloppy" is set.
1192 #define MAXARGLEN 8192
1194 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1199 static char argvs[MAXARGLEN];
1200 static char *argv[MAXARGS + 1];
1201 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1204 if (strlen(arg) > sizeof(argvs) - 1) {
1206 error("string too long");
1209 if (terminated != NULL)
1211 if (lastquote != NULL)
1216 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1217 error("Too many arguments.");
1220 if (isspace((unsigned char)arg[i])) {
1221 if (state == MA_UNQUOTED) {
1222 /* Terminate current argument */
1226 } else if (state != MA_START)
1227 argvs[j++] = arg[i];
1228 } else if (arg[i] == '"' || arg[i] == '\'') {
1229 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1230 if (state == MA_START) {
1231 argv[argc] = argvs + j;
1233 if (lastquote != NULL)
1234 *lastquote = arg[i];
1235 } else if (state == MA_UNQUOTED)
1237 else if (state == q)
1238 state = MA_UNQUOTED;
1240 argvs[j++] = arg[i];
1241 } else if (arg[i] == '\\') {
1242 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1243 quot = state == MA_SQUOTE ? '\'' : '"';
1244 /* Unescape quote we are in */
1245 /* XXX support \n and friends? */
1246 if (arg[i + 1] == quot) {
1248 argvs[j++] = arg[i];
1249 } else if (arg[i + 1] == '?' ||
1250 arg[i + 1] == '[' || arg[i + 1] == '*') {
1252 * Special case for sftp: append
1253 * double-escaped glob sequence -
1254 * glob will undo one level of
1255 * escaping. NB. string can grow here.
1257 if (j >= sizeof(argvs) - 5)
1258 goto args_too_longs;
1260 argvs[j++] = arg[i++];
1262 argvs[j++] = arg[i];
1264 argvs[j++] = arg[i++];
1265 argvs[j++] = arg[i];
1268 if (state == MA_START) {
1269 argv[argc] = argvs + j;
1270 state = MA_UNQUOTED;
1271 if (lastquote != NULL)
1274 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1275 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1277 * Special case for sftp: append
1278 * escaped glob sequence -
1279 * glob will undo one level of
1282 argvs[j++] = arg[i++];
1283 argvs[j++] = arg[i];
1285 /* Unescape everything */
1286 /* XXX support \n and friends? */
1288 argvs[j++] = arg[i];
1291 } else if (arg[i] == '#') {
1292 if (state == MA_SQUOTE || state == MA_DQUOTE)
1293 argvs[j++] = arg[i];
1296 } else if (arg[i] == '\0') {
1297 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1299 state = MA_UNQUOTED;
1300 if (terminated != NULL)
1304 error("Unterminated quoted argument");
1308 if (state == MA_UNQUOTED) {
1314 if (state == MA_START) {
1315 argv[argc] = argvs + j;
1316 state = MA_UNQUOTED;
1317 if (lastquote != NULL)
1320 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1321 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1323 * Special case for sftp: escape quoted
1324 * glob(3) wildcards. NB. string can grow
1327 if (j >= sizeof(argvs) - 3)
1328 goto args_too_longs;
1330 argvs[j++] = arg[i];
1332 argvs[j++] = arg[i];
1341 parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
1342 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1343 int *rflag, int *sflag,
1344 unsigned long *n_arg, char **path1, char **path2)
1346 const char *cmd, *cp = *cpp;
1350 int path1_mandatory = 0, i, cmdnum, optidx, argc;
1352 /* Skip leading whitespace */
1353 cp = cp + strspn(cp, WHITESPACE);
1356 * Check for leading '-' (disable error processing) and '@' (suppress
1361 for (;*cp != '\0'; cp++) {
1364 } else if (*cp == '@') {
1367 /* all other characters terminate prefix processing */
1371 cp = cp + strspn(cp, WHITESPACE);
1373 /* Ignore blank lines and lines which begin with comment '#' char */
1374 if (*cp == '\0' || *cp == '#')
1377 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1380 /* Figure out which command we have */
1381 for (i = 0; cmds[i].c != NULL; i++) {
1382 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1392 } else if (cmdnum == -1) {
1393 error("Invalid command.");
1397 /* Get arguments and parse flags */
1398 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1399 *rflag = *sflag = 0;
1400 *path1 = *path2 = NULL;
1407 if ((optidx = parse_getput_flags(cmd, argv, argc,
1408 aflag, fflag, pflag, rflag)) == -1)
1410 /* Get first pathname (mandatory) */
1411 if (argc - optidx < 1) {
1412 error("You must specify at least one path after a "
1413 "%s command.", cmd);
1416 *path1 = xstrdup(argv[optidx]);
1417 /* Get second pathname (optional) */
1418 if (argc - optidx > 1) {
1419 *path2 = xstrdup(argv[optidx + 1]);
1420 /* Destination is not globbed */
1421 undo_glob_escape(*path2);
1425 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1427 goto parse_two_paths;
1429 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1431 goto parse_two_paths;
1433 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1435 goto parse_two_paths;
1437 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1440 if (argc - optidx < 2) {
1441 error("You must specify two paths after a %s "
1445 *path1 = xstrdup(argv[optidx]);
1446 *path2 = xstrdup(argv[optidx + 1]);
1447 /* Paths are not globbed */
1448 undo_glob_escape(*path1);
1449 undo_glob_escape(*path2);
1455 path1_mandatory = 1;
1459 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1461 /* Get pathname (mandatory) */
1462 if (argc - optidx < 1) {
1463 if (!path1_mandatory)
1464 break; /* return a NULL path1 */
1465 error("You must specify a path after a %s command.",
1469 *path1 = xstrdup(argv[optidx]);
1470 /* Only "rm" globs */
1472 undo_glob_escape(*path1);
1475 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1478 /* Default to current directory if no path specified */
1479 if (argc - optidx < 1)
1482 *path1 = xstrdup(argv[optidx]);
1483 undo_glob_escape(*path1);
1487 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1489 /* Path is optional */
1490 if (argc - optidx > 0)
1491 *path1 = xstrdup(argv[optidx]);
1494 /* Skip ls command and following whitespace */
1495 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1497 /* Uses the rest of the line */
1505 if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
1507 /* Get numeric arg (mandatory) */
1508 if (argc - optidx < 1)
1511 ll = strtoll(argv[optidx], &cp2, base);
1512 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1513 ((ll == LLONG_MIN || ll == LLONG_MAX) && errno == ERANGE) ||
1514 ll < 0 || ll > UINT32_MAX) {
1516 error("You must supply a numeric argument "
1517 "to the %s command.", cmd);
1521 if (cmdnum == I_LUMASK)
1523 /* Get pathname (mandatory) */
1524 if (argc - optidx < 2) {
1525 error("You must specify a path after a %s command.",
1529 *path1 = xstrdup(argv[optidx + 1]);
1537 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1541 fatal("Command not implemented");
1549 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1550 const char *startdir, int err_abort, int echo_command)
1552 const char *ocmd = cmd;
1553 char *path1, *path2, *tmp;
1554 int ignore_errors = 0, disable_echo = 1;
1555 int aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1556 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1558 unsigned long n_arg = 0;
1560 char path_buf[PATH_MAX];
1564 path1 = path2 = NULL;
1565 cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag,
1566 &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg,
1568 if (ignore_errors != 0)
1571 if (echo_command && !disable_echo)
1572 mprintf("sftp> %s\n", ocmd);
1574 memset(&g, 0, sizeof(g));
1576 /* Perform command */
1582 /* Unrecognized command */
1589 err = process_get(conn, path1, path2, *pwd, pflag,
1590 rflag, aflag, fflag);
1596 err = process_put(conn, path1, path2, *pwd, pflag,
1597 rflag, aflag, fflag);
1600 path1 = sftp_make_absolute(path1, *pwd);
1601 path2 = sftp_make_absolute(path2, *pwd);
1602 err = sftp_copy(conn, path1, path2);
1605 path1 = sftp_make_absolute(path1, *pwd);
1606 path2 = sftp_make_absolute(path2, *pwd);
1607 err = sftp_rename(conn, path1, path2, lflag);
1614 path1 = sftp_make_absolute(path1, *pwd);
1615 path2 = sftp_make_absolute(path2, *pwd);
1616 err = (sflag ? sftp_symlink : sftp_hardlink)(conn,
1620 path1 = make_absolute_pwd_glob(path1, *pwd);
1621 sftp_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1622 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1624 mprintf("Removing %s\n", g.gl_pathv[i]);
1625 err = sftp_rm(conn, g.gl_pathv[i]);
1626 if (err != 0 && err_abort)
1631 path1 = sftp_make_absolute(path1, *pwd);
1633 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1635 err = sftp_mkdir(conn, path1, &a, 1);
1638 path1 = sftp_make_absolute(path1, *pwd);
1639 err = sftp_rmdir(conn, path1);
1642 if (path1 == NULL || *path1 == '\0')
1643 path1 = xstrdup(startdir);
1644 path1 = sftp_make_absolute(path1, *pwd);
1645 if ((tmp = sftp_realpath(conn, path1)) == NULL) {
1649 if (sftp_stat(conn, tmp, 0, &aa) != 0) {
1654 if (!(aa.flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1655 error("Can't change directory: Can't check target");
1660 if (!S_ISDIR(aa.perm)) {
1661 error("Can't change directory: \"%s\" is not "
1662 "a directory", tmp);
1672 do_ls_dir(conn, *pwd, *pwd, lflag);
1676 /* Strip pwd off beginning of non-absolute paths */
1678 if (!path_absolute(path1))
1681 path1 = make_absolute_pwd_glob(path1, *pwd);
1682 err = do_globbed_ls(conn, path1, tmp, lflag);
1685 /* Default to current directory if no path specified */
1687 path1 = xstrdup(*pwd);
1688 path1 = sftp_make_absolute(path1, *pwd);
1689 err = do_df(conn, path1, hflag, iflag);
1692 if (path1 == NULL || *path1 == '\0')
1693 path1 = xstrdup("~");
1694 tmp = tilde_expand_filename(path1, getuid());
1697 if (chdir(path1) == -1) {
1698 error("Couldn't change local directory to "
1699 "\"%s\": %s", path1, strerror(errno));
1704 if (mkdir(path1, 0777) == -1) {
1705 error("Couldn't create local directory "
1706 "\"%s\": %s", path1, strerror(errno));
1714 local_do_shell(cmd);
1718 printf("Local umask: %03lo\n", n_arg);
1721 path1 = make_absolute_pwd_glob(path1, *pwd);
1723 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1725 sftp_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1726 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1728 mprintf("Changing mode on %s\n",
1730 err = (hflag ? sftp_lsetstat : sftp_setstat)(conn,
1732 if (err != 0 && err_abort)
1738 path1 = make_absolute_pwd_glob(path1, *pwd);
1739 sftp_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1740 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1741 if ((hflag ? sftp_lstat : sftp_stat)(conn,
1742 g.gl_pathv[i], 0, &aa) != 0) {
1749 if (!(aa.flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1750 error("Can't get current ownership of "
1751 "remote file \"%s\"", g.gl_pathv[i]);
1758 aa.flags &= SSH2_FILEXFER_ATTR_UIDGID;
1759 if (cmdnum == I_CHOWN) {
1761 mprintf("Changing owner on %s\n",
1766 mprintf("Changing group on %s\n",
1770 err = (hflag ? sftp_lsetstat : sftp_setstat)(conn,
1771 g.gl_pathv[i], &aa);
1772 if (err != 0 && err_abort)
1777 mprintf("Remote working directory: %s\n", *pwd);
1780 if (!getcwd(path_buf, sizeof(path_buf))) {
1781 error("Couldn't get local cwd: %s", strerror(errno));
1785 mprintf("Local working directory: %s\n", path_buf);
1788 /* Processed below */
1794 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1797 showprogress = !showprogress;
1799 printf("Progress meter enabled\n");
1801 printf("Progress meter disabled\n");
1804 fatal("%d is not implemented", cmdnum);
1812 /* If an unignored error occurs in batch mode we should abort. */
1813 if (err_abort && err != 0)
1815 else if (cmdnum == I_QUIT)
1823 prompt(EditLine *el)
1828 /* Display entries in 'list' after skipping the first 'len' chars */
1830 complete_display(char **list, u_int len)
1832 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1836 /* Count entries for sort and find longest */
1837 for (y = 0; list[y]; y++)
1838 m = MAXIMUM(m, strlen(list[y]));
1840 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1843 m = m > len ? m - len : 0;
1844 columns = width / (m + 2);
1845 columns = MAXIMUM(columns, 1);
1846 colspace = width / columns;
1847 colspace = MINIMUM(colspace, width);
1851 for (y = 0; list[y]; y++) {
1852 llen = strlen(list[y]);
1853 tmp = llen > len ? list[y] + len : "";
1854 mprintf("%-*s", colspace, tmp);
1865 * Given a "list" of words that begin with a common prefix of "word",
1866 * attempt to find an autocompletion to extends "word" by the next
1867 * characters common to all entries in "list".
1870 complete_ambiguous(const char *word, char **list, size_t count)
1876 u_int y, matchlen = strlen(list[0]);
1878 /* Find length of common stem */
1879 for (y = 1; list[y]; y++) {
1882 for (x = 0; x < matchlen; x++)
1883 if (list[0][x] != list[y][x])
1889 if (matchlen > strlen(word)) {
1890 char *tmp = xstrdup(list[0]);
1892 tmp[matchlen] = '\0';
1897 return xstrdup(word);
1900 /* Autocomplete a sftp command */
1902 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1905 u_int y, count = 0, cmdlen, tmplen;
1906 char *tmp, **list, argterm[3];
1909 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1911 /* No command specified: display all available commands */
1913 for (y = 0; cmds[y].c; y++)
1914 list[count++] = xstrdup(cmds[y].c);
1917 complete_display(list, 0);
1919 for (y = 0; list[y] != NULL; y++)
1925 /* Prepare subset of commands that start with "cmd" */
1926 cmdlen = strlen(cmd);
1927 for (y = 0; cmds[y].c; y++) {
1928 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1929 list[count++] = xstrdup(cmds[y].c);
1938 /* Complete ambiguous command */
1939 tmp = complete_ambiguous(cmd, list, count);
1941 complete_display(list, 0);
1943 for (y = 0; list[y]; y++)
1948 tmplen = strlen(tmp);
1949 cmdlen = strlen(cmd);
1950 /* If cmd may be extended then do so */
1951 if (tmplen > cmdlen)
1952 if (el_insertstr(el, tmp + cmdlen) == -1)
1953 fatal("el_insertstr failed.");
1955 /* Terminate argument cleanly */
1959 argterm[y++] = quote;
1960 if (lastarg || *(lf->cursor) != ' ')
1963 if (y > 0 && el_insertstr(el, argterm) == -1)
1964 fatal("el_insertstr failed.");
1973 * Determine whether a particular sftp command's arguments (if any) represent
1974 * local or remote files. The "cmdarg" argument specifies the actual argument
1975 * and accepts values 1 or 2.
1978 complete_is_remote(char *cmd, int cmdarg) {
1984 for (i = 0; cmds[i].c; i++) {
1985 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) {
1988 else if (cmdarg == 2)
1997 /* Autocomplete a filename "file" */
1999 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
2000 char *file, int remote, int lastarg, char quote, int terminated)
2003 char *tmp, *tmp2, ins[8];
2004 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
2008 /* Glob from "file" location */
2012 xasprintf(&tmp, "%s*", file);
2014 /* Check if the path is absolute. */
2015 isabs = path_absolute(tmp);
2017 memset(&g, 0, sizeof(g));
2018 if (remote != LOCAL) {
2019 tmp = make_absolute_pwd_glob(tmp, remote_path);
2020 sftp_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
2022 (void)glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
2024 /* Determine length of pwd so we can trim completion display */
2025 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
2026 /* Terminate counting on first unescaped glob metacharacter */
2027 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
2028 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
2032 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
2034 if (tmp[tmplen] == '/')
2035 pwdlen = tmplen + 1; /* track last seen '/' */
2040 if (g.gl_matchc == 0)
2043 if (g.gl_matchc > 1)
2044 complete_display(g.gl_pathv, pwdlen);
2046 /* Don't try to extend globs */
2047 if (file == NULL || hadglob)
2050 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
2051 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
2057 tmplen = strlen(tmp);
2058 filelen = strlen(file);
2060 /* Count the number of escaped characters in the input string. */
2062 for (i = 0; i < filelen; i++) {
2063 if (!isesc && file[i] == '\\' && i + 1 < filelen){
2070 if (tmplen > (filelen - cesc)) {
2071 tmp2 = tmp + filelen - cesc;
2073 /* quote argument on way out */
2074 for (i = 0; i < len; i += clen) {
2075 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
2076 (size_t)clen > sizeof(ins) - 2)
2077 fatal("invalid multibyte character");
2079 memcpy(ins + 1, tmp2 + i, clen);
2080 ins[clen + 1] = '\0';
2090 if (quote == '\0' || tmp2[i] == quote) {
2091 if (el_insertstr(el, ins) == -1)
2092 fatal("el_insertstr "
2098 if (el_insertstr(el, ins + 1) == -1)
2099 fatal("el_insertstr failed.");
2106 if (g.gl_matchc == 1) {
2108 if (!terminated && quote != '\0')
2110 if (*(lf->cursor - 1) != '/' &&
2111 (lastarg || *(lf->cursor) != ' '))
2114 if (i > 0 && el_insertstr(el, ins) == -1)
2115 fatal("el_insertstr failed.");
2124 /* tab-completion hook function, called via libedit */
2125 static unsigned char
2126 complete(EditLine *el, int ch)
2128 char **argv, *line, quote;
2130 u_int cursor, len, terminated, ret = CC_ERROR;
2132 struct complete_ctx *complete_ctx;
2135 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2136 fatal_f("el_get failed");
2138 /* Figure out which argument the cursor points to */
2139 cursor = lf->cursor - lf->buffer;
2140 line = xmalloc(cursor + 1);
2141 memcpy(line, lf->buffer, cursor);
2142 line[cursor] = '\0';
2143 argv = makeargv(line, &carg, 1, "e, &terminated);
2146 /* Get all the arguments on the line */
2147 len = lf->lastchar - lf->buffer;
2148 line = xmalloc(len + 1);
2149 memcpy(line, lf->buffer, len);
2151 argv = makeargv(line, &argc, 1, NULL, NULL);
2153 /* Ensure cursor is at EOL or a argument boundary */
2154 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2155 line[cursor] != '\n') {
2161 /* Show all available commands */
2162 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2164 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2165 /* Handle the command parsing */
2166 if (complete_cmd_parse(el, argv[0], argc == carg,
2167 quote, terminated) != 0)
2169 } else if (carg >= 1) {
2170 /* Handle file parsing */
2172 int i = 0, cmdarg = 0;
2173 char *filematch = NULL;
2175 if (carg > 1 && line[cursor-1] != ' ')
2176 filematch = argv[carg - 1];
2178 for (i = 1; i < carg; i++) {
2180 if (argv[i][0] != '-')
2185 * If previous argument is complete, then offer completion
2188 if (line[cursor - 1] == ' ')
2191 remote = complete_is_remote(argv[0], cmdarg);
2193 if ((remote == REMOTE || remote == LOCAL) &&
2194 complete_match(el, complete_ctx->conn,
2195 *complete_ctx->remote_pathp, filematch,
2196 remote, carg == argc, quote, terminated) != 0)
2203 #endif /* USE_LIBEDIT */
2206 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2209 char *dir = NULL, *startdir = NULL;
2211 int err, interactive;
2212 EditLine *el = NULL;
2216 extern char *__progname;
2217 struct complete_ctx complete_ctx;
2219 if (!batchmode && isatty(STDIN_FILENO)) {
2220 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2221 fatal("Couldn't initialise editline");
2222 if ((hl = history_init()) == NULL)
2223 fatal("Couldn't initialise editline history");
2224 history(hl, &hev, H_SETSIZE, 100);
2225 el_set(el, EL_HIST, history, hl);
2227 el_set(el, EL_PROMPT, prompt);
2228 el_set(el, EL_EDITOR, "emacs");
2229 el_set(el, EL_TERMINAL, NULL);
2230 el_set(el, EL_SIGNAL, 1);
2231 el_source(el, NULL);
2233 /* Tab Completion */
2234 el_set(el, EL_ADDFN, "ftp-complete",
2235 "Context sensitive argument completion", complete);
2236 complete_ctx.conn = conn;
2237 complete_ctx.remote_pathp = &remote_path;
2238 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2239 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2240 /* enable ctrl-left-arrow and ctrl-right-arrow */
2241 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2242 el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL);
2243 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2244 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2245 /* make ^w match ksh behaviour */
2246 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2248 #endif /* USE_LIBEDIT */
2250 if ((remote_path = sftp_realpath(conn, ".")) == NULL)
2252 startdir = xstrdup(remote_path);
2254 if (file1 != NULL) {
2255 dir = xstrdup(file1);
2256 dir = sftp_make_absolute(dir, remote_path);
2258 if (sftp_remote_is_dir(conn, dir) && file2 == NULL) {
2260 mprintf("Changing to: %s\n", dir);
2261 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2262 if (parse_dispatch_command(conn, cmd,
2263 &remote_path, startdir, 1, 0) != 0) {
2271 /* XXX this is wrong wrt quoting */
2272 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2273 global_aflag ? " -a" : "", dir,
2274 file2 == NULL ? "" : " ",
2275 file2 == NULL ? "" : file2);
2276 err = parse_dispatch_command(conn, cmd,
2277 &remote_path, startdir, 1, 0);
2287 setvbuf(stdout, NULL, _IOLBF, 0);
2288 setvbuf(infile, NULL, _IOLBF, 0);
2290 interactive = !batchmode && isatty(STDIN_FILENO);
2293 struct sigaction sa;
2296 memset(&sa, 0, sizeof(sa));
2297 sa.sa_handler = interactive ? read_interrupt : killchild;
2298 if (sigaction(SIGINT, &sa, NULL) == -1) {
2299 debug3("sigaction(%s): %s", strsignal(SIGINT),
2306 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2318 if ((line = el_gets(el, &count)) == NULL ||
2325 history(hl, &hev, H_ENTER, line);
2326 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2327 fprintf(stderr, "Error: input line too long\n");
2330 #endif /* USE_LIBEDIT */
2333 cmd[strcspn(cmd, "\n")] = '\0';
2335 /* Handle user interrupts gracefully during commands */
2337 ssh_signal(SIGINT, cmd_interrupt);
2339 err = parse_dispatch_command(conn, cmd, &remote_path,
2340 startdir, batchmode, !interactive && el == NULL);
2344 ssh_signal(SIGCHLD, SIG_DFL);
2352 #endif /* USE_LIBEDIT */
2354 /* err == 1 signifies normal "quit" exit */
2355 return (err >= 0 ? 0 : -1);
2359 connect_to_server(char *path, char **args, int *in, int *out)
2363 int pin[2], pout[2];
2365 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2366 fatal("pipe: %s", strerror(errno));
2371 #else /* USE_PIPES */
2374 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2375 fatal("socketpair: %s", strerror(errno));
2376 *in = *out = inout[0];
2377 c_in = c_out = inout[1];
2378 #endif /* USE_PIPES */
2380 if ((sshpid = fork()) == -1)
2381 fatal("fork: %s", strerror(errno));
2382 else if (sshpid == 0) {
2383 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2384 (dup2(c_out, STDOUT_FILENO) == -1)) {
2385 fprintf(stderr, "dup2: %s\n", strerror(errno));
2394 * The underlying ssh is in the same process group, so we must
2395 * ignore SIGINT if we want to gracefully abort commands,
2396 * otherwise the signal will make it to the ssh process and
2397 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2398 * underlying ssh, it must *not* ignore that signal.
2400 ssh_signal(SIGINT, SIG_IGN);
2401 ssh_signal(SIGTERM, SIG_DFL);
2403 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2407 ssh_signal(SIGTERM, killchild);
2408 ssh_signal(SIGINT, killchild);
2409 ssh_signal(SIGHUP, killchild);
2410 ssh_signal(SIGTSTP, suspchild);
2411 ssh_signal(SIGTTIN, suspchild);
2412 ssh_signal(SIGTTOU, suspchild);
2413 ssh_signal(SIGCHLD, sigchld_handler);
2421 extern char *__progname;
2424 "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2425 " [-D sftp_server_command] [-F ssh_config] [-i identity_file]\n"
2426 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
2427 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
2428 " [-X sftp_option] destination\n",
2434 main(int argc, char **argv)
2436 int r, in, out, ch, err, tmp, port = -1, noisy = 0;
2437 char *host = NULL, *user, *cp, **cpp, *file2 = NULL;
2438 int debug_level = 0;
2439 char *file1 = NULL, *sftp_server = NULL;
2440 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2442 LogLevel ll = SYSLOG_LEVEL_INFO;
2445 extern char *optarg;
2446 struct sftp_conn *conn;
2447 size_t copy_buffer_len = 0;
2448 size_t num_requests = 0;
2449 long long llv, limit_kbps = 0;
2451 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2455 __progname = ssh_get_progname(argv[0]);
2456 memset(&args, '\0', sizeof(args));
2458 addargs(&args, "%s", ssh_program);
2459 addargs(&args, "-oForwardX11 no");
2460 addargs(&args, "-oPermitLocalCommand no");
2461 addargs(&args, "-oClearAllForwardings yes");
2463 ll = SYSLOG_LEVEL_INFO;
2466 while ((ch = getopt(argc, argv,
2467 "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:X:")) != -1) {
2469 /* Passed through to ssh(1) */
2474 addargs(&args, "-%c", ch);
2476 /* Passed through to ssh(1) with argument */
2482 addargs(&args, "-%c", ch);
2483 addargs(&args, "%s", optarg);
2486 ll = SYSLOG_LEVEL_ERROR;
2489 addargs(&args, "-%c", ch);
2492 port = a2port(optarg);
2494 fatal("Bad port \"%s\"\n", optarg);
2497 if (debug_level < 3) {
2498 addargs(&args, "-v");
2499 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2504 fatal("SSH protocol v.1 is no longer supported");
2507 /* accept silently */
2513 copy_buffer_len = strtol(optarg, &cp, 10);
2514 if (copy_buffer_len == 0 || *cp != '\0')
2515 fatal("Invalid buffer size \"%s\"", optarg);
2519 fatal("Batch file already specified.");
2521 /* Allow "-" as stdin */
2522 if (strcmp(optarg, "-") != 0 &&
2523 (infile = fopen(optarg, "r")) == NULL)
2524 fatal("%s (%s).", strerror(errno), optarg);
2526 quiet = batchmode = 1;
2527 addargs(&args, "-obatchmode yes");
2533 noisy = 1; /* Used to clear quiet mode after getopt */
2539 sftp_direct = optarg;
2542 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2546 limit_kbps *= 1024; /* kbps */
2552 num_requests = strtol(optarg, &cp, 10);
2553 if (num_requests == 0 || *cp != '\0')
2554 fatal("Invalid number of requests \"%s\"",
2558 sftp_server = optarg;
2561 ssh_program = optarg;
2562 replacearg(&args, 0, "%s", ssh_program);
2565 /* Please keep in sync with ssh.c -X */
2566 if (strncmp(optarg, "buffer=", 7) == 0) {
2567 r = scan_scaled(optarg + 7, &llv);
2568 if (r == 0 && (llv <= 0 || llv > 256 * 1024)) {
2573 fatal("Invalid buffer size \"%s\": %s",
2574 optarg + 7, strerror(errno));
2576 copy_buffer_len = (size_t)llv;
2577 } else if (strncmp(optarg, "nrequests=", 10) == 0) {
2578 llv = strtonum(optarg + 10, 1, 256 * 1024,
2580 if (errstr != NULL) {
2581 fatal("Invalid number of requests "
2582 "\"%s\": %s", optarg + 10, errstr);
2584 num_requests = (size_t)llv;
2586 fatal("Invalid -X option");
2595 /* Do this last because we want the user to be able to override it */
2596 addargs(&args, "-oForwardAgent no");
2598 if (!isatty(STDERR_FILENO))
2604 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2606 if (sftp_direct == NULL) {
2607 if (optind == argc || argc > (optind + 2))
2611 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2620 /* Try with user, host and path. */
2621 if (parse_user_host_path(*argv, &user, &host,
2624 /* Try with user and host. */
2625 if (parse_user_host_port(*argv, &user, &host, NULL)
2628 /* Treat as a plain hostname. */
2629 host = xstrdup(*argv);
2630 host = cleanhostname(host);
2633 file2 = *(argv + 1);
2636 fprintf(stderr, "Missing hostname\n");
2641 addargs(&args, "-oPort %d", port);
2643 addargs(&args, "-l");
2644 addargs(&args, "%s", user);
2647 /* no subsystem if the server-spec contains a '/' */
2648 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2649 addargs(&args, "-s");
2651 addargs(&args, "--");
2652 addargs(&args, "%s", host);
2653 addargs(&args, "%s", (sftp_server != NULL ?
2654 sftp_server : "sftp"));
2656 connect_to_server(ssh_program, args.list, &in, &out);
2658 if ((r = argv_split(sftp_direct, &tmp, &cpp, 1)) != 0)
2659 fatal_r(r, "Parse -D arguments");
2661 fatal("No sftp server specified via -D");
2662 connect_to_server(cpp[0], cpp, &in, &out);
2663 argv_free(cpp, tmp);
2667 conn = sftp_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2669 fatal("Couldn't initialise connection to server");
2672 if (sftp_direct == NULL)
2673 fprintf(stderr, "Connected to %s.\n", host);
2675 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2678 err = interactive_loop(conn, file1, file2);
2680 #if !defined(USE_PIPES)
2681 shutdown(in, SHUT_RDWR);
2682 shutdown(out, SHUT_RDWR);
2690 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1)
2692 fatal("Couldn't wait for ssh process: %s",
2695 exit(err == 0 ? 0 : 1);