1 /* $OpenBSD: sftp.c,v 1.222 2022/09/19 10:46:00 djm Exp $ */
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
25 #include <sys/socket.h>
27 #ifdef HAVE_SYS_STATVFS_H
28 #include <sys/statvfs.h>
46 typedef void EditLine
;
62 #include "pathnames.h"
69 #include "sftp-common.h"
70 #include "sftp-client.h"
71 #include "sftp-usergroup.h"
73 /* File to read commands from */
76 /* Are we in batchfile mode? */
79 /* PID of ssh transport process */
80 static volatile pid_t sshpid
= -1;
82 /* Suppress diagnostic messages */
85 /* This is set to 0 if the progressmeter is not desired. */
88 /* When this option is set, we always recursively download/upload directories */
91 /* When this option is set, we resume download or upload if possible */
94 /* When this option is set, the file transfers will always preserve times */
97 /* When this option is set, transfers will have fsync() called on each file */
100 /* SIGINT received during command processing */
101 volatile sig_atomic_t interrupted
= 0;
103 /* I wish qsort() took a separate ctx for the comparison function...*/
107 /* Context used for commandline completion */
108 struct complete_ctx
{
109 struct sftp_conn
*conn
;
113 int remote_glob(struct sftp_conn
*, const char *, int,
114 int (*)(const char *, int), glob_t
*); /* proto for sftp-glob.c */
116 extern char *__progname
;
118 /* Separators for interactive commands */
119 #define WHITESPACE " \t\r\n"
122 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
123 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
124 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
125 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
126 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
127 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
128 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
129 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
130 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
132 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
133 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
135 /* Commands for interactive mode */
170 const int t
; /* Completion type for the first argument */
171 const int t2
; /* completion type for the optional second argument */
174 /* Type of completion */
179 static const struct CMD cmds
[] = {
180 { "bye", I_QUIT
, NOARGS
, NOARGS
},
181 { "cd", I_CHDIR
, REMOTE
, NOARGS
},
182 { "chdir", I_CHDIR
, REMOTE
, NOARGS
},
183 { "chgrp", I_CHGRP
, REMOTE
, NOARGS
},
184 { "chmod", I_CHMOD
, REMOTE
, NOARGS
},
185 { "chown", I_CHOWN
, REMOTE
, NOARGS
},
186 { "copy", I_COPY
, REMOTE
, LOCAL
},
187 { "cp", I_COPY
, REMOTE
, LOCAL
},
188 { "df", I_DF
, REMOTE
, NOARGS
},
189 { "dir", I_LS
, REMOTE
, NOARGS
},
190 { "exit", I_QUIT
, NOARGS
, NOARGS
},
191 { "get", I_GET
, REMOTE
, LOCAL
},
192 { "help", I_HELP
, NOARGS
, NOARGS
},
193 { "lcd", I_LCHDIR
, LOCAL
, NOARGS
},
194 { "lchdir", I_LCHDIR
, LOCAL
, NOARGS
},
195 { "lls", I_LLS
, LOCAL
, NOARGS
},
196 { "lmkdir", I_LMKDIR
, LOCAL
, NOARGS
},
197 { "ln", I_LINK
, REMOTE
, REMOTE
},
198 { "lpwd", I_LPWD
, LOCAL
, NOARGS
},
199 { "ls", I_LS
, REMOTE
, NOARGS
},
200 { "lumask", I_LUMASK
, NOARGS
, NOARGS
},
201 { "mkdir", I_MKDIR
, REMOTE
, NOARGS
},
202 { "mget", I_GET
, REMOTE
, LOCAL
},
203 { "mput", I_PUT
, LOCAL
, REMOTE
},
204 { "progress", I_PROGRESS
, NOARGS
, NOARGS
},
205 { "put", I_PUT
, LOCAL
, REMOTE
},
206 { "pwd", I_PWD
, REMOTE
, NOARGS
},
207 { "quit", I_QUIT
, NOARGS
, NOARGS
},
208 { "reget", I_REGET
, REMOTE
, LOCAL
},
209 { "rename", I_RENAME
, REMOTE
, REMOTE
},
210 { "reput", I_REPUT
, LOCAL
, REMOTE
},
211 { "rm", I_RM
, REMOTE
, NOARGS
},
212 { "rmdir", I_RMDIR
, REMOTE
, NOARGS
},
213 { "symlink", I_SYMLINK
, REMOTE
, REMOTE
},
214 { "version", I_VERSION
, NOARGS
, NOARGS
},
215 { "!", I_SHELL
, NOARGS
, NOARGS
},
216 { "?", I_HELP
, NOARGS
, NOARGS
},
229 waitpid(pid
, NULL
, 0);
241 while (waitpid(sshpid
, NULL
, WUNTRACED
) == -1 && errno
== EINTR
)
244 kill(getpid(), SIGSTOP
);
249 cmd_interrupt(int signo
)
251 const char msg
[] = "\rInterrupt \n";
252 int olderrno
= errno
;
254 (void)write(STDERR_FILENO
, msg
, sizeof(msg
) - 1);
261 read_interrupt(int signo
)
268 sigchld_handler(int sig
)
270 int save_errno
= errno
;
272 const char msg
[] = "\rConnection closed. \n";
274 /* Report if ssh transport process dies. */
275 while ((pid
= waitpid(sshpid
, NULL
, WNOHANG
)) == -1 && errno
== EINTR
)
278 (void)write(STDERR_FILENO
, msg
, sizeof(msg
) - 1);
288 printf("Available commands:\n"
290 "cd path Change remote directory to 'path'\n"
291 "chgrp [-h] grp path Change group of file 'path' to 'grp'\n"
292 "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n"
293 "chown [-h] own path Change owner of file 'path' to 'own'\n"
294 "copy oldpath newpath Copy remote file\n"
295 "cp oldpath newpath Copy remote file\n"
296 "df [-hi] [path] Display statistics for current directory or\n"
297 " filesystem containing 'path'\n"
299 "get [-afpR] remote [local] Download file\n"
300 "help Display this help text\n"
301 "lcd path Change local directory to 'path'\n"
302 "lls [ls-options [path]] Display local directory listing\n"
303 "lmkdir path Create local directory\n"
304 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
305 "lpwd Print local working directory\n"
306 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
307 "lumask umask Set local umask to 'umask'\n"
308 "mkdir path Create remote directory\n"
309 "progress Toggle display of progress meter\n"
310 "put [-afpR] local [remote] Upload file\n"
311 "pwd Display remote working directory\n"
313 "reget [-fpR] remote [local] Resume download file\n"
314 "rename oldpath newpath Rename remote file\n"
315 "reput [-fpR] local [remote] Resume upload file\n"
316 "rm path Delete remote file\n"
317 "rmdir path Remove remote directory\n"
318 "symlink oldpath newpath Symlink remote file\n"
319 "version Show SFTP version\n"
320 "!command Execute 'command' in local shell\n"
321 "! Escape to local shell\n"
322 "? Synonym for help\n");
326 local_do_shell(const char *args
)
335 if ((shell
= getenv("SHELL")) == NULL
|| *shell
== '\0')
336 shell
= _PATH_BSHELL
;
338 if ((pid
= fork()) == -1)
339 fatal("Couldn't fork: %s", strerror(errno
));
342 /* XXX: child has pipe fds to ssh subproc open - issue? */
344 debug3("Executing %s -c \"%s\"", shell
, args
);
345 execl(shell
, shell
, "-c", args
, (char *)NULL
);
347 debug3("Executing %s", shell
);
348 execl(shell
, shell
, (char *)NULL
);
350 fprintf(stderr
, "Couldn't execute \"%s\": %s\n", shell
,
354 while (waitpid(pid
, &status
, 0) == -1)
356 fatal("Couldn't wait for child: %s", strerror(errno
));
357 if (!WIFEXITED(status
))
358 error("Shell exited abnormally");
359 else if (WEXITSTATUS(status
))
360 error("Shell exited with status %d", WEXITSTATUS(status
));
364 local_do_ls(const char *args
)
367 local_do_shell(_PATH_LS
);
369 int len
= strlen(_PATH_LS
" ") + strlen(args
) + 1;
370 char *buf
= xmalloc(len
);
372 /* XXX: quoting - rip quoting code from ftp? */
373 snprintf(buf
, len
, _PATH_LS
" %s", args
);
379 /* Strip one path (usually the pwd) from the start of another */
381 path_strip(const char *path
, const char *strip
)
386 return (xstrdup(path
));
389 if (strncmp(path
, strip
, len
) == 0) {
390 if (strip
[len
- 1] != '/' && path
[len
] == '/')
392 return (xstrdup(path
+ len
));
395 return (xstrdup(path
));
399 parse_getput_flags(const char *cmd
, char **argv
, int argc
,
400 int *aflag
, int *fflag
, int *pflag
, int *rflag
)
402 extern int opterr
, optind
, optopt
, optreset
;
405 optind
= optreset
= 1;
408 *aflag
= *fflag
= *rflag
= *pflag
= 0;
409 while ((ch
= getopt(argc
, argv
, "afPpRr")) != -1) {
426 error("%s: Invalid flag -%c", cmd
, optopt
);
435 parse_link_flags(const char *cmd
, char **argv
, int argc
, int *sflag
)
437 extern int opterr
, optind
, optopt
, optreset
;
440 optind
= optreset
= 1;
444 while ((ch
= getopt(argc
, argv
, "s")) != -1) {
450 error("%s: Invalid flag -%c", cmd
, optopt
);
459 parse_rename_flags(const char *cmd
, char **argv
, int argc
, int *lflag
)
461 extern int opterr
, optind
, optopt
, optreset
;
464 optind
= optreset
= 1;
468 while ((ch
= getopt(argc
, argv
, "l")) != -1) {
474 error("%s: Invalid flag -%c", cmd
, optopt
);
483 parse_ls_flags(char **argv
, int argc
, int *lflag
)
485 extern int opterr
, optind
, optopt
, optreset
;
488 optind
= optreset
= 1;
491 *lflag
= LS_NAME_SORT
;
492 while ((ch
= getopt(argc
, argv
, "1Safhlnrt")) != -1) {
495 *lflag
&= ~VIEW_FLAGS
;
496 *lflag
|= LS_SHORT_VIEW
;
499 *lflag
&= ~SORT_FLAGS
;
500 *lflag
|= LS_SIZE_SORT
;
503 *lflag
|= LS_SHOW_ALL
;
506 *lflag
&= ~SORT_FLAGS
;
509 *lflag
|= LS_SI_UNITS
;
512 *lflag
&= ~LS_SHORT_VIEW
;
513 *lflag
|= LS_LONG_VIEW
;
516 *lflag
&= ~LS_SHORT_VIEW
;
517 *lflag
|= LS_NUMERIC_VIEW
|LS_LONG_VIEW
;
520 *lflag
|= LS_REVERSE_SORT
;
523 *lflag
&= ~SORT_FLAGS
;
524 *lflag
|= LS_TIME_SORT
;
527 error("ls: Invalid flag -%c", optopt
);
536 parse_df_flags(const char *cmd
, char **argv
, int argc
, int *hflag
, int *iflag
)
538 extern int opterr
, optind
, optopt
, optreset
;
541 optind
= optreset
= 1;
545 while ((ch
= getopt(argc
, argv
, "hi")) != -1) {
554 error("%s: Invalid flag -%c", cmd
, optopt
);
563 parse_ch_flags(const char *cmd
, char **argv
, int argc
, int *hflag
)
565 extern int opterr
, optind
, optopt
, optreset
;
568 optind
= optreset
= 1;
572 while ((ch
= getopt(argc
, argv
, "h")) != -1) {
578 error("%s: Invalid flag -%c", cmd
, optopt
);
587 parse_no_flags(const char *cmd
, char **argv
, int argc
)
589 extern int opterr
, optind
, optopt
, optreset
;
592 optind
= optreset
= 1;
595 while ((ch
= getopt(argc
, argv
, "")) != -1) {
598 error("%s: Invalid flag -%c", cmd
, optopt
);
607 escape_glob(const char *s
)
613 ret
= xcalloc(2, len
+ 1);
614 for (i
= o
= 0; i
< len
; i
++) {
615 if (strchr("[]?*\\", s
[i
]) != NULL
)
624 make_absolute_pwd_glob(const char *p
, const char *pwd
)
628 escpwd
= escape_glob(pwd
);
631 ret
= make_absolute(xstrdup(p
), escpwd
);
637 process_get(struct sftp_conn
*conn
, const char *src
, const char *dst
,
638 const char *pwd
, int pflag
, int rflag
, int resume
, int fflag
)
640 char *filename
, *abs_src
= NULL
, *abs_dst
= NULL
, *tmp
= NULL
;
644 abs_src
= make_absolute_pwd_glob(src
, pwd
);
645 memset(&g
, 0, sizeof(g
));
647 debug3("Looking up %s", abs_src
);
648 if ((r
= remote_glob(conn
, abs_src
, GLOB_MARK
, NULL
, &g
)) != 0) {
649 if (r
== GLOB_NOSPACE
) {
650 error("Too many matches for \"%s\".", abs_src
);
652 error("File \"%s\" not found.", abs_src
);
659 * If multiple matches then dst must be a directory or
662 if (g
.gl_matchc
> 1 && dst
!= NULL
&& !local_is_dir(dst
)) {
663 error("Multiple source paths, but destination "
664 "\"%s\" is not a directory", dst
);
669 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
670 tmp
= xstrdup(g
.gl_pathv
[i
]);
671 if ((filename
= basename(tmp
)) == NULL
) {
672 error("basename %s: %s", tmp
, strerror(errno
));
678 if (g
.gl_matchc
== 1 && dst
) {
679 if (local_is_dir(dst
)) {
680 abs_dst
= path_append(dst
, filename
);
682 abs_dst
= xstrdup(dst
);
685 abs_dst
= path_append(dst
, filename
);
687 abs_dst
= xstrdup(filename
);
691 resume
|= global_aflag
;
692 if (!quiet
&& resume
)
693 mprintf("Resuming %s to %s\n",
694 g
.gl_pathv
[i
], abs_dst
);
695 else if (!quiet
&& !resume
)
696 mprintf("Fetching %s to %s\n",
697 g
.gl_pathv
[i
], abs_dst
);
698 /* XXX follow link flag */
699 if (globpath_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
700 if (download_dir(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
701 pflag
|| global_pflag
, 1, resume
,
702 fflag
|| global_fflag
, 0, 0) == -1)
705 if (do_download(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
706 pflag
|| global_pflag
, resume
,
707 fflag
|| global_fflag
, 0) == -1)
721 process_put(struct sftp_conn
*conn
, const char *src
, const char *dst
,
722 const char *pwd
, int pflag
, int rflag
, int resume
, int fflag
)
724 char *tmp_dst
= NULL
;
725 char *abs_dst
= NULL
;
726 char *tmp
= NULL
, *filename
= NULL
;
729 int i
, dst_is_dir
= 1;
733 tmp_dst
= xstrdup(dst
);
734 tmp_dst
= make_absolute(tmp_dst
, pwd
);
737 memset(&g
, 0, sizeof(g
));
738 debug3("Looking up %s", src
);
739 if (glob(src
, GLOB_NOCHECK
| GLOB_MARK
, NULL
, &g
)) {
740 error("File \"%s\" not found.", src
);
745 /* If we aren't fetching to pwd then stash this status for later */
747 dst_is_dir
= remote_is_dir(conn
, tmp_dst
);
749 /* If multiple matches, dst may be directory or unspecified */
750 if (g
.gl_matchc
> 1 && tmp_dst
&& !dst_is_dir
) {
751 error("Multiple paths match, but destination "
752 "\"%s\" is not a directory", tmp_dst
);
757 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
758 if (stat(g
.gl_pathv
[i
], &sb
) == -1) {
760 error("stat %s: %s", g
.gl_pathv
[i
], strerror(errno
));
764 tmp
= xstrdup(g
.gl_pathv
[i
]);
765 if ((filename
= basename(tmp
)) == NULL
) {
766 error("basename %s: %s", tmp
, strerror(errno
));
772 if (g
.gl_matchc
== 1 && tmp_dst
) {
773 /* If directory specified, append filename */
775 abs_dst
= path_append(tmp_dst
, filename
);
777 abs_dst
= xstrdup(tmp_dst
);
778 } else if (tmp_dst
) {
779 abs_dst
= path_append(tmp_dst
, filename
);
781 abs_dst
= make_absolute(xstrdup(filename
), pwd
);
785 resume
|= global_aflag
;
786 if (!quiet
&& resume
)
787 mprintf("Resuming upload of %s to %s\n",
788 g
.gl_pathv
[i
], abs_dst
);
789 else if (!quiet
&& !resume
)
790 mprintf("Uploading %s to %s\n",
791 g
.gl_pathv
[i
], abs_dst
);
792 /* XXX follow_link_flag */
793 if (globpath_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
794 if (upload_dir(conn
, g
.gl_pathv
[i
], abs_dst
,
795 pflag
|| global_pflag
, 1, resume
,
796 fflag
|| global_fflag
, 0, 0) == -1)
799 if (do_upload(conn
, g
.gl_pathv
[i
], abs_dst
,
800 pflag
|| global_pflag
, resume
,
801 fflag
|| global_fflag
, 0) == -1)
814 sdirent_comp(const void *aa
, const void *bb
)
816 SFTP_DIRENT
*a
= *(SFTP_DIRENT
**)aa
;
817 SFTP_DIRENT
*b
= *(SFTP_DIRENT
**)bb
;
818 int rmul
= sort_flag
& LS_REVERSE_SORT
? -1 : 1;
820 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
821 if (sort_flag
& LS_NAME_SORT
)
822 return (rmul
* strcmp(a
->filename
, b
->filename
));
823 else if (sort_flag
& LS_TIME_SORT
)
824 return (rmul
* NCMP(a
->a
.mtime
, b
->a
.mtime
));
825 else if (sort_flag
& LS_SIZE_SORT
)
826 return (rmul
* NCMP(a
->a
.size
, b
->a
.size
));
828 fatal("Unknown ls sort type");
831 /* sftp ls.1 replacement for directories */
833 do_ls_dir(struct sftp_conn
*conn
, const char *path
,
834 const char *strip_path
, int lflag
)
837 u_int c
= 1, colspace
= 0, columns
= 1;
840 if ((n
= do_readdir(conn
, path
, &d
)) != 0)
843 if (!(lflag
& LS_SHORT_VIEW
)) {
844 u_int m
= 0, width
= 80;
848 /* Count entries for sort and find longest filename */
849 for (n
= 0; d
[n
] != NULL
; n
++) {
850 if (d
[n
]->filename
[0] != '.' || (lflag
& LS_SHOW_ALL
))
851 m
= MAXIMUM(m
, strlen(d
[n
]->filename
));
854 /* Add any subpath that also needs to be counted */
855 tmp
= path_strip(path
, strip_path
);
859 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
862 columns
= width
/ (m
+ 2);
863 columns
= MAXIMUM(columns
, 1);
864 colspace
= width
/ columns
;
865 colspace
= MINIMUM(colspace
, width
);
868 if (lflag
& SORT_FLAGS
) {
869 for (n
= 0; d
[n
] != NULL
; n
++)
870 ; /* count entries */
871 sort_flag
= lflag
& (SORT_FLAGS
|LS_REVERSE_SORT
);
872 qsort(d
, n
, sizeof(*d
), sdirent_comp
);
875 get_remote_user_groups_from_dirents(conn
, d
);
876 for (n
= 0; d
[n
] != NULL
&& !interrupted
; n
++) {
879 if (d
[n
]->filename
[0] == '.' && !(lflag
& LS_SHOW_ALL
))
882 tmp
= path_append(path
, d
[n
]->filename
);
883 fname
= path_strip(tmp
, strip_path
);
886 if (lflag
& LS_LONG_VIEW
) {
887 if ((lflag
& (LS_NUMERIC_VIEW
|LS_SI_UNITS
)) != 0 ||
888 can_get_users_groups_by_id(conn
)) {
892 memset(&sb
, 0, sizeof(sb
));
893 attrib_to_stat(&d
[n
]->a
, &sb
);
894 lname
= ls_file(fname
, &sb
, 1,
895 (lflag
& LS_SI_UNITS
),
896 ruser_name(sb
.st_uid
),
897 rgroup_name(sb
.st_gid
));
898 mprintf("%s\n", lname
);
901 mprintf("%s\n", d
[n
]->longname
);
903 mprintf("%-*s", colspace
, fname
);
914 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
917 free_sftp_dirents(d
);
922 sglob_comp(const void *aa
, const void *bb
)
924 u_int a
= *(const u_int
*)aa
;
925 u_int b
= *(const u_int
*)bb
;
926 const char *ap
= sort_glob
->gl_pathv
[a
];
927 const char *bp
= sort_glob
->gl_pathv
[b
];
928 const struct stat
*as
= sort_glob
->gl_statv
[a
];
929 const struct stat
*bs
= sort_glob
->gl_statv
[b
];
930 int rmul
= sort_flag
& LS_REVERSE_SORT
? -1 : 1;
932 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
933 if (sort_flag
& LS_NAME_SORT
)
934 return (rmul
* strcmp(ap
, bp
));
935 else if (sort_flag
& LS_TIME_SORT
) {
936 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
937 if (timespeccmp(&as
->st_mtim
, &bs
->st_mtim
, ==))
939 return timespeccmp(&as
->st_mtim
, &bs
->st_mtim
, <) ?
941 #elif defined(HAVE_STRUCT_STAT_ST_MTIME)
942 return (rmul
* NCMP(as
->st_mtime
, bs
->st_mtime
));
946 } else if (sort_flag
& LS_SIZE_SORT
)
947 return (rmul
* NCMP(as
->st_size
, bs
->st_size
));
949 fatal("Unknown ls sort type");
952 /* sftp ls.1 replacement which handles path globs */
954 do_globbed_ls(struct sftp_conn
*conn
, const char *path
,
955 const char *strip_path
, int lflag
)
961 u_int i
, j
, nentries
, *indices
= NULL
, c
= 1;
962 u_int colspace
= 0, columns
= 1, m
= 0, width
= 80;
964 memset(&g
, 0, sizeof(g
));
966 if ((r
= remote_glob(conn
, path
,
967 GLOB_MARK
|GLOB_NOCHECK
|GLOB_BRACE
|GLOB_KEEPSTAT
|GLOB_NOSORT
,
969 (g
.gl_pathc
&& !g
.gl_matchc
)) {
972 if (r
== GLOB_NOSPACE
) {
973 error("Can't ls: Too many matches for \"%s\"", path
);
975 error("Can't ls: \"%s\" not found", path
);
984 * If the glob returns a single match and it is a directory,
985 * then just list its contents.
987 if (g
.gl_matchc
== 1 && g
.gl_statv
[0] != NULL
&&
988 S_ISDIR(g
.gl_statv
[0]->st_mode
)) {
989 err
= do_ls_dir(conn
, g
.gl_pathv
[0], strip_path
, lflag
);
994 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
997 if (!(lflag
& LS_SHORT_VIEW
)) {
998 /* Count entries for sort and find longest filename */
999 for (i
= 0; g
.gl_pathv
[i
]; i
++)
1000 m
= MAXIMUM(m
, strlen(g
.gl_pathv
[i
]));
1002 columns
= width
/ (m
+ 2);
1003 columns
= MAXIMUM(columns
, 1);
1004 colspace
= width
/ columns
;
1008 * Sorting: rather than mess with the contents of glob_t, prepare
1009 * an array of indices into it and sort that. For the usual
1010 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
1012 for (nentries
= 0; g
.gl_pathv
[nentries
] != NULL
; nentries
++)
1013 ; /* count entries */
1014 indices
= calloc(nentries
, sizeof(*indices
));
1015 for (i
= 0; i
< nentries
; i
++)
1018 if (lflag
& SORT_FLAGS
) {
1020 sort_flag
= lflag
& (SORT_FLAGS
|LS_REVERSE_SORT
);
1021 qsort(indices
, nentries
, sizeof(*indices
), sglob_comp
);
1025 get_remote_user_groups_from_glob(conn
, &g
);
1026 for (j
= 0; j
< nentries
&& !interrupted
; j
++) {
1028 fname
= path_strip(g
.gl_pathv
[i
], strip_path
);
1029 if (lflag
& LS_LONG_VIEW
) {
1030 if (g
.gl_statv
[i
] == NULL
) {
1031 error("no stat information for %s", fname
);
1034 lname
= ls_file(fname
, g
.gl_statv
[i
], 1,
1035 (lflag
& LS_SI_UNITS
),
1036 ruser_name(g
.gl_statv
[i
]->st_uid
),
1037 rgroup_name(g
.gl_statv
[i
]->st_gid
));
1038 mprintf("%s\n", lname
);
1041 mprintf("%-*s", colspace
, fname
);
1051 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
1063 do_df(struct sftp_conn
*conn
, const char *path
, int hflag
, int iflag
)
1065 struct sftp_statvfs st
;
1066 char s_used
[FMT_SCALED_STRSIZE
], s_avail
[FMT_SCALED_STRSIZE
];
1067 char s_root
[FMT_SCALED_STRSIZE
], s_total
[FMT_SCALED_STRSIZE
];
1068 char s_icapacity
[16], s_dcapacity
[16];
1070 if (do_statvfs(conn
, path
, &st
, 1) == -1)
1072 if (st
.f_files
== 0)
1073 strlcpy(s_icapacity
, "ERR", sizeof(s_icapacity
));
1075 snprintf(s_icapacity
, sizeof(s_icapacity
), "%3llu%%",
1076 (unsigned long long)(100 * (st
.f_files
- st
.f_ffree
) /
1079 if (st
.f_blocks
== 0)
1080 strlcpy(s_dcapacity
, "ERR", sizeof(s_dcapacity
));
1082 snprintf(s_dcapacity
, sizeof(s_dcapacity
), "%3llu%%",
1083 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
1087 printf(" Inodes Used Avail "
1088 "(root) %%Capacity\n");
1089 printf("%11llu %11llu %11llu %11llu %s\n",
1090 (unsigned long long)st
.f_files
,
1091 (unsigned long long)(st
.f_files
- st
.f_ffree
),
1092 (unsigned long long)st
.f_favail
,
1093 (unsigned long long)st
.f_ffree
, s_icapacity
);
1095 strlcpy(s_used
, "error", sizeof(s_used
));
1096 strlcpy(s_avail
, "error", sizeof(s_avail
));
1097 strlcpy(s_root
, "error", sizeof(s_root
));
1098 strlcpy(s_total
, "error", sizeof(s_total
));
1099 fmt_scaled((st
.f_blocks
- st
.f_bfree
) * st
.f_frsize
, s_used
);
1100 fmt_scaled(st
.f_bavail
* st
.f_frsize
, s_avail
);
1101 fmt_scaled(st
.f_bfree
* st
.f_frsize
, s_root
);
1102 fmt_scaled(st
.f_blocks
* st
.f_frsize
, s_total
);
1103 printf(" Size Used Avail (root) %%Capacity\n");
1104 printf("%7sB %7sB %7sB %7sB %s\n",
1105 s_total
, s_used
, s_avail
, s_root
, s_dcapacity
);
1107 printf(" Size Used Avail "
1108 "(root) %%Capacity\n");
1109 printf("%12llu %12llu %12llu %12llu %s\n",
1110 (unsigned long long)(st
.f_frsize
* st
.f_blocks
/ 1024),
1111 (unsigned long long)(st
.f_frsize
*
1112 (st
.f_blocks
- st
.f_bfree
) / 1024),
1113 (unsigned long long)(st
.f_frsize
* st
.f_bavail
/ 1024),
1114 (unsigned long long)(st
.f_frsize
* st
.f_bfree
/ 1024),
1121 * Undo escaping of glob sequences in place. Used to undo extra escaping
1122 * applied in makeargv() when the string is destined for a function that
1126 undo_glob_escape(char *s
)
1161 * Split a string into an argument vector using sh(1)-style quoting,
1162 * comment and escaping rules, but with some tweaks to handle glob(3)
1164 * The "sloppy" flag allows for recovery from missing terminating quote, for
1165 * use in parsing incomplete commandlines during tab autocompletion.
1167 * Returns NULL on error or a NULL-terminated array of arguments.
1169 * If "lastquote" is not NULL, the quoting character used for the last
1170 * argument is placed in *lastquote ("\0", "'" or "\"").
1172 * If "terminated" is not NULL, *terminated will be set to 1 when the
1173 * last argument's quote has been properly terminated or 0 otherwise.
1174 * This parameter is only of use if "sloppy" is set.
1177 #define MAXARGLEN 8192
1179 makeargv(const char *arg
, int *argcp
, int sloppy
, char *lastquote
,
1184 static char argvs
[MAXARGLEN
];
1185 static char *argv
[MAXARGS
+ 1];
1186 enum { MA_START
, MA_SQUOTE
, MA_DQUOTE
, MA_UNQUOTED
} state
, q
;
1189 if (strlen(arg
) > sizeof(argvs
) - 1) {
1191 error("string too long");
1194 if (terminated
!= NULL
)
1196 if (lastquote
!= NULL
)
1201 if ((size_t)argc
>= sizeof(argv
) / sizeof(*argv
)){
1202 error("Too many arguments.");
1205 if (isspace((unsigned char)arg
[i
])) {
1206 if (state
== MA_UNQUOTED
) {
1207 /* Terminate current argument */
1211 } else if (state
!= MA_START
)
1212 argvs
[j
++] = arg
[i
];
1213 } else if (arg
[i
] == '"' || arg
[i
] == '\'') {
1214 q
= arg
[i
] == '"' ? MA_DQUOTE
: MA_SQUOTE
;
1215 if (state
== MA_START
) {
1216 argv
[argc
] = argvs
+ j
;
1218 if (lastquote
!= NULL
)
1219 *lastquote
= arg
[i
];
1220 } else if (state
== MA_UNQUOTED
)
1222 else if (state
== q
)
1223 state
= MA_UNQUOTED
;
1225 argvs
[j
++] = arg
[i
];
1226 } else if (arg
[i
] == '\\') {
1227 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1228 quot
= state
== MA_SQUOTE
? '\'' : '"';
1229 /* Unescape quote we are in */
1230 /* XXX support \n and friends? */
1231 if (arg
[i
+ 1] == quot
) {
1233 argvs
[j
++] = arg
[i
];
1234 } else if (arg
[i
+ 1] == '?' ||
1235 arg
[i
+ 1] == '[' || arg
[i
+ 1] == '*') {
1237 * Special case for sftp: append
1238 * double-escaped glob sequence -
1239 * glob will undo one level of
1240 * escaping. NB. string can grow here.
1242 if (j
>= sizeof(argvs
) - 5)
1243 goto args_too_longs
;
1245 argvs
[j
++] = arg
[i
++];
1247 argvs
[j
++] = arg
[i
];
1249 argvs
[j
++] = arg
[i
++];
1250 argvs
[j
++] = arg
[i
];
1253 if (state
== MA_START
) {
1254 argv
[argc
] = argvs
+ j
;
1255 state
= MA_UNQUOTED
;
1256 if (lastquote
!= NULL
)
1259 if (arg
[i
+ 1] == '?' || arg
[i
+ 1] == '[' ||
1260 arg
[i
+ 1] == '*' || arg
[i
+ 1] == '\\') {
1262 * Special case for sftp: append
1263 * escaped glob sequence -
1264 * glob will undo one level of
1267 argvs
[j
++] = arg
[i
++];
1268 argvs
[j
++] = arg
[i
];
1270 /* Unescape everything */
1271 /* XXX support \n and friends? */
1273 argvs
[j
++] = arg
[i
];
1276 } else if (arg
[i
] == '#') {
1277 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
)
1278 argvs
[j
++] = arg
[i
];
1281 } else if (arg
[i
] == '\0') {
1282 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1284 state
= MA_UNQUOTED
;
1285 if (terminated
!= NULL
)
1289 error("Unterminated quoted argument");
1293 if (state
== MA_UNQUOTED
) {
1299 if (state
== MA_START
) {
1300 argv
[argc
] = argvs
+ j
;
1301 state
= MA_UNQUOTED
;
1302 if (lastquote
!= NULL
)
1305 if ((state
== MA_SQUOTE
|| state
== MA_DQUOTE
) &&
1306 (arg
[i
] == '?' || arg
[i
] == '[' || arg
[i
] == '*')) {
1308 * Special case for sftp: escape quoted
1309 * glob(3) wildcards. NB. string can grow
1312 if (j
>= sizeof(argvs
) - 3)
1313 goto args_too_longs
;
1315 argvs
[j
++] = arg
[i
];
1317 argvs
[j
++] = arg
[i
];
1326 parse_args(const char **cpp
, int *ignore_errors
, int *disable_echo
, int *aflag
,
1327 int *fflag
, int *hflag
, int *iflag
, int *lflag
, int *pflag
,
1328 int *rflag
, int *sflag
,
1329 unsigned long *n_arg
, char **path1
, char **path2
)
1331 const char *cmd
, *cp
= *cpp
;
1335 int path1_mandatory
= 0, i
, cmdnum
, optidx
, argc
;
1337 /* Skip leading whitespace */
1338 cp
= cp
+ strspn(cp
, WHITESPACE
);
1341 * Check for leading '-' (disable error processing) and '@' (suppress
1346 for (;*cp
!= '\0'; cp
++) {
1349 } else if (*cp
== '@') {
1352 /* all other characters terminate prefix processing */
1356 cp
= cp
+ strspn(cp
, WHITESPACE
);
1358 /* Ignore blank lines and lines which begin with comment '#' char */
1359 if (*cp
== '\0' || *cp
== '#')
1362 if ((argv
= makeargv(cp
, &argc
, 0, NULL
, NULL
)) == NULL
)
1365 /* Figure out which command we have */
1366 for (i
= 0; cmds
[i
].c
!= NULL
; i
++) {
1367 if (argv
[0] != NULL
&& strcasecmp(cmds
[i
].c
, argv
[0]) == 0)
1377 } else if (cmdnum
== -1) {
1378 error("Invalid command.");
1382 /* Get arguments and parse flags */
1383 *aflag
= *fflag
= *hflag
= *iflag
= *lflag
= *pflag
= 0;
1384 *rflag
= *sflag
= 0;
1385 *path1
= *path2
= NULL
;
1392 if ((optidx
= parse_getput_flags(cmd
, argv
, argc
,
1393 aflag
, fflag
, pflag
, rflag
)) == -1)
1395 /* Get first pathname (mandatory) */
1396 if (argc
- optidx
< 1) {
1397 error("You must specify at least one path after a "
1398 "%s command.", cmd
);
1401 *path1
= xstrdup(argv
[optidx
]);
1402 /* Get second pathname (optional) */
1403 if (argc
- optidx
> 1) {
1404 *path2
= xstrdup(argv
[optidx
+ 1]);
1405 /* Destination is not globbed */
1406 undo_glob_escape(*path2
);
1410 if ((optidx
= parse_link_flags(cmd
, argv
, argc
, sflag
)) == -1)
1412 goto parse_two_paths
;
1414 if ((optidx
= parse_no_flags(cmd
, argv
, argc
)) == -1)
1416 goto parse_two_paths
;
1418 if ((optidx
= parse_rename_flags(cmd
, argv
, argc
, lflag
)) == -1)
1420 goto parse_two_paths
;
1422 if ((optidx
= parse_no_flags(cmd
, argv
, argc
)) == -1)
1425 if (argc
- optidx
< 2) {
1426 error("You must specify two paths after a %s "
1430 *path1
= xstrdup(argv
[optidx
]);
1431 *path2
= xstrdup(argv
[optidx
+ 1]);
1432 /* Paths are not globbed */
1433 undo_glob_escape(*path1
);
1434 undo_glob_escape(*path2
);
1440 path1_mandatory
= 1;
1444 if ((optidx
= parse_no_flags(cmd
, argv
, argc
)) == -1)
1446 /* Get pathname (mandatory) */
1447 if (argc
- optidx
< 1) {
1448 if (!path1_mandatory
)
1449 break; /* return a NULL path1 */
1450 error("You must specify a path after a %s command.",
1454 *path1
= xstrdup(argv
[optidx
]);
1455 /* Only "rm" globs */
1457 undo_glob_escape(*path1
);
1460 if ((optidx
= parse_df_flags(cmd
, argv
, argc
, hflag
,
1463 /* Default to current directory if no path specified */
1464 if (argc
- optidx
< 1)
1467 *path1
= xstrdup(argv
[optidx
]);
1468 undo_glob_escape(*path1
);
1472 if ((optidx
= parse_ls_flags(argv
, argc
, lflag
)) == -1)
1474 /* Path is optional */
1475 if (argc
- optidx
> 0)
1476 *path1
= xstrdup(argv
[optidx
]);
1479 /* Skip ls command and following whitespace */
1480 cp
= cp
+ strlen(cmd
) + strspn(cp
, WHITESPACE
);
1482 /* Uses the rest of the line */
1490 if ((optidx
= parse_ch_flags(cmd
, argv
, argc
, hflag
)) == -1)
1492 /* Get numeric arg (mandatory) */
1493 if (argc
- optidx
< 1)
1496 ll
= strtoll(argv
[optidx
], &cp2
, base
);
1497 if (cp2
== argv
[optidx
] || *cp2
!= '\0' ||
1498 ((ll
== LLONG_MIN
|| ll
== LLONG_MAX
) && errno
== ERANGE
) ||
1499 ll
< 0 || ll
> UINT32_MAX
) {
1501 error("You must supply a numeric argument "
1502 "to the %s command.", cmd
);
1506 if (cmdnum
== I_LUMASK
)
1508 /* Get pathname (mandatory) */
1509 if (argc
- optidx
< 2) {
1510 error("You must specify a path after a %s command.",
1514 *path1
= xstrdup(argv
[optidx
+ 1]);
1522 if ((optidx
= parse_no_flags(cmd
, argv
, argc
)) == -1)
1526 fatal("Command not implemented");
1534 parse_dispatch_command(struct sftp_conn
*conn
, const char *cmd
, char **pwd
,
1535 const char *startdir
, int err_abort
, int echo_command
)
1537 const char *ocmd
= cmd
;
1538 char *path1
, *path2
, *tmp
;
1539 int ignore_errors
= 0, disable_echo
= 1;
1540 int aflag
= 0, fflag
= 0, hflag
= 0, iflag
= 0;
1541 int lflag
= 0, pflag
= 0, rflag
= 0, sflag
= 0;
1543 unsigned long n_arg
= 0;
1545 char path_buf
[PATH_MAX
];
1549 path1
= path2
= NULL
;
1550 cmdnum
= parse_args(&cmd
, &ignore_errors
, &disable_echo
, &aflag
, &fflag
,
1551 &hflag
, &iflag
, &lflag
, &pflag
, &rflag
, &sflag
, &n_arg
,
1553 if (ignore_errors
!= 0)
1556 if (echo_command
&& !disable_echo
)
1557 mprintf("sftp> %s\n", ocmd
);
1559 memset(&g
, 0, sizeof(g
));
1561 /* Perform command */
1567 /* Unrecognized command */
1574 err
= process_get(conn
, path1
, path2
, *pwd
, pflag
,
1575 rflag
, aflag
, fflag
);
1581 err
= process_put(conn
, path1
, path2
, *pwd
, pflag
,
1582 rflag
, aflag
, fflag
);
1585 path1
= make_absolute(path1
, *pwd
);
1586 path2
= make_absolute(path2
, *pwd
);
1587 err
= do_copy(conn
, path1
, path2
);
1590 path1
= make_absolute(path1
, *pwd
);
1591 path2
= make_absolute(path2
, *pwd
);
1592 err
= do_rename(conn
, path1
, path2
, lflag
);
1599 path1
= make_absolute(path1
, *pwd
);
1600 path2
= make_absolute(path2
, *pwd
);
1601 err
= (sflag
? do_symlink
: do_hardlink
)(conn
, path1
, path2
);
1604 path1
= make_absolute_pwd_glob(path1
, *pwd
);
1605 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1606 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1608 mprintf("Removing %s\n", g
.gl_pathv
[i
]);
1609 err
= do_rm(conn
, g
.gl_pathv
[i
]);
1610 if (err
!= 0 && err_abort
)
1615 path1
= make_absolute(path1
, *pwd
);
1617 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1619 err
= do_mkdir(conn
, path1
, &a
, 1);
1622 path1
= make_absolute(path1
, *pwd
);
1623 err
= do_rmdir(conn
, path1
);
1626 if (path1
== NULL
|| *path1
== '\0')
1627 path1
= xstrdup(startdir
);
1628 path1
= make_absolute(path1
, *pwd
);
1629 if ((tmp
= do_realpath(conn
, path1
)) == NULL
) {
1633 if ((aa
= do_stat(conn
, tmp
, 0)) == NULL
) {
1638 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
)) {
1639 error("Can't change directory: Can't check target");
1644 if (!S_ISDIR(aa
->perm
)) {
1645 error("Can't change directory: \"%s\" is not "
1646 "a directory", tmp
);
1656 do_ls_dir(conn
, *pwd
, *pwd
, lflag
);
1660 /* Strip pwd off beginning of non-absolute paths */
1662 if (!path_absolute(path1
))
1665 path1
= make_absolute_pwd_glob(path1
, *pwd
);
1666 err
= do_globbed_ls(conn
, path1
, tmp
, lflag
);
1669 /* Default to current directory if no path specified */
1671 path1
= xstrdup(*pwd
);
1672 path1
= make_absolute(path1
, *pwd
);
1673 err
= do_df(conn
, path1
, hflag
, iflag
);
1676 if (path1
== NULL
|| *path1
== '\0')
1677 path1
= xstrdup("~");
1678 tmp
= tilde_expand_filename(path1
, getuid());
1681 if (chdir(path1
) == -1) {
1682 error("Couldn't change local directory to "
1683 "\"%s\": %s", path1
, strerror(errno
));
1688 if (mkdir(path1
, 0777) == -1) {
1689 error("Couldn't create local directory "
1690 "\"%s\": %s", path1
, strerror(errno
));
1698 local_do_shell(cmd
);
1702 printf("Local umask: %03lo\n", n_arg
);
1705 path1
= make_absolute_pwd_glob(path1
, *pwd
);
1707 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1709 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1710 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1712 mprintf("Changing mode on %s\n",
1714 err
= (hflag
? do_lsetstat
: do_setstat
)(conn
,
1716 if (err
!= 0 && err_abort
)
1722 path1
= make_absolute_pwd_glob(path1
, *pwd
);
1723 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1724 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1725 if (!(aa
= (hflag
? do_lstat
: do_stat
)(conn
,
1726 g
.gl_pathv
[i
], 0))) {
1733 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_UIDGID
)) {
1734 error("Can't get current ownership of "
1735 "remote file \"%s\"", g
.gl_pathv
[i
]);
1742 aa
->flags
&= SSH2_FILEXFER_ATTR_UIDGID
;
1743 if (cmdnum
== I_CHOWN
) {
1745 mprintf("Changing owner on %s\n",
1750 mprintf("Changing group on %s\n",
1754 err
= (hflag
? do_lsetstat
: do_setstat
)(conn
,
1756 if (err
!= 0 && err_abort
)
1761 mprintf("Remote working directory: %s\n", *pwd
);
1764 if (!getcwd(path_buf
, sizeof(path_buf
))) {
1765 error("Couldn't get local cwd: %s", strerror(errno
));
1769 mprintf("Local working directory: %s\n", path_buf
);
1772 /* Processed below */
1778 printf("SFTP protocol version %u\n", sftp_proto_version(conn
));
1781 showprogress
= !showprogress
;
1783 printf("Progress meter enabled\n");
1785 printf("Progress meter disabled\n");
1788 fatal("%d is not implemented", cmdnum
);
1796 /* If an unignored error occurs in batch mode we should abort. */
1797 if (err_abort
&& err
!= 0)
1799 else if (cmdnum
== I_QUIT
)
1807 prompt(EditLine
*el
)
1812 /* Display entries in 'list' after skipping the first 'len' chars */
1814 complete_display(char **list
, u_int len
)
1816 u_int y
, m
= 0, width
= 80, columns
= 1, colspace
= 0, llen
;
1820 /* Count entries for sort and find longest */
1821 for (y
= 0; list
[y
]; y
++)
1822 m
= MAXIMUM(m
, strlen(list
[y
]));
1824 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
1827 m
= m
> len
? m
- len
: 0;
1828 columns
= width
/ (m
+ 2);
1829 columns
= MAXIMUM(columns
, 1);
1830 colspace
= width
/ columns
;
1831 colspace
= MINIMUM(colspace
, width
);
1835 for (y
= 0; list
[y
]; y
++) {
1836 llen
= strlen(list
[y
]);
1837 tmp
= llen
> len
? list
[y
] + len
: "";
1838 mprintf("%-*s", colspace
, tmp
);
1849 * Given a "list" of words that begin with a common prefix of "word",
1850 * attempt to find an autocompletion to extends "word" by the next
1851 * characters common to all entries in "list".
1854 complete_ambiguous(const char *word
, char **list
, size_t count
)
1860 u_int y
, matchlen
= strlen(list
[0]);
1862 /* Find length of common stem */
1863 for (y
= 1; list
[y
]; y
++) {
1866 for (x
= 0; x
< matchlen
; x
++)
1867 if (list
[0][x
] != list
[y
][x
])
1873 if (matchlen
> strlen(word
)) {
1874 char *tmp
= xstrdup(list
[0]);
1876 tmp
[matchlen
] = '\0';
1881 return xstrdup(word
);
1884 /* Autocomplete a sftp command */
1886 complete_cmd_parse(EditLine
*el
, char *cmd
, int lastarg
, char quote
,
1889 u_int y
, count
= 0, cmdlen
, tmplen
;
1890 char *tmp
, **list
, argterm
[3];
1893 list
= xcalloc((sizeof(cmds
) / sizeof(*cmds
)) + 1, sizeof(char *));
1895 /* No command specified: display all available commands */
1897 for (y
= 0; cmds
[y
].c
; y
++)
1898 list
[count
++] = xstrdup(cmds
[y
].c
);
1901 complete_display(list
, 0);
1903 for (y
= 0; list
[y
] != NULL
; y
++)
1909 /* Prepare subset of commands that start with "cmd" */
1910 cmdlen
= strlen(cmd
);
1911 for (y
= 0; cmds
[y
].c
; y
++) {
1912 if (!strncasecmp(cmd
, cmds
[y
].c
, cmdlen
))
1913 list
[count
++] = xstrdup(cmds
[y
].c
);
1922 /* Complete ambiguous command */
1923 tmp
= complete_ambiguous(cmd
, list
, count
);
1925 complete_display(list
, 0);
1927 for (y
= 0; list
[y
]; y
++)
1932 tmplen
= strlen(tmp
);
1933 cmdlen
= strlen(cmd
);
1934 /* If cmd may be extended then do so */
1935 if (tmplen
> cmdlen
)
1936 if (el_insertstr(el
, tmp
+ cmdlen
) == -1)
1937 fatal("el_insertstr failed.");
1939 /* Terminate argument cleanly */
1943 argterm
[y
++] = quote
;
1944 if (lastarg
|| *(lf
->cursor
) != ' ')
1947 if (y
> 0 && el_insertstr(el
, argterm
) == -1)
1948 fatal("el_insertstr failed.");
1957 * Determine whether a particular sftp command's arguments (if any) represent
1958 * local or remote files. The "cmdarg" argument specifies the actual argument
1959 * and accepts values 1 or 2.
1962 complete_is_remote(char *cmd
, int cmdarg
) {
1968 for (i
= 0; cmds
[i
].c
; i
++) {
1969 if (!strncasecmp(cmd
, cmds
[i
].c
, strlen(cmds
[i
].c
))) {
1972 else if (cmdarg
== 2)
1981 /* Autocomplete a filename "file" */
1983 complete_match(EditLine
*el
, struct sftp_conn
*conn
, char *remote_path
,
1984 char *file
, int remote
, int lastarg
, char quote
, int terminated
)
1987 char *tmp
, *tmp2
, ins
[8];
1988 u_int i
, hadglob
, pwdlen
, len
, tmplen
, filelen
, cesc
, isesc
, isabs
;
1992 /* Glob from "file" location */
1996 xasprintf(&tmp
, "%s*", file
);
1998 /* Check if the path is absolute. */
1999 isabs
= path_absolute(tmp
);
2001 memset(&g
, 0, sizeof(g
));
2002 if (remote
!= LOCAL
) {
2003 tmp
= make_absolute_pwd_glob(tmp
, remote_path
);
2004 remote_glob(conn
, tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
2006 glob(tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
2008 /* Determine length of pwd so we can trim completion display */
2009 for (hadglob
= tmplen
= pwdlen
= 0; tmp
[tmplen
] != 0; tmplen
++) {
2010 /* Terminate counting on first unescaped glob metacharacter */
2011 if (tmp
[tmplen
] == '*' || tmp
[tmplen
] == '?') {
2012 if (tmp
[tmplen
] != '*' || tmp
[tmplen
+ 1] != '\0')
2016 if (tmp
[tmplen
] == '\\' && tmp
[tmplen
+ 1] != '\0')
2018 if (tmp
[tmplen
] == '/')
2019 pwdlen
= tmplen
+ 1; /* track last seen '/' */
2024 if (g
.gl_matchc
== 0)
2027 if (g
.gl_matchc
> 1)
2028 complete_display(g
.gl_pathv
, pwdlen
);
2030 /* Don't try to extend globs */
2031 if (file
== NULL
|| hadglob
)
2034 tmp2
= complete_ambiguous(file
, g
.gl_pathv
, g
.gl_matchc
);
2035 tmp
= path_strip(tmp2
, isabs
? NULL
: remote_path
);
2041 tmplen
= strlen(tmp
);
2042 filelen
= strlen(file
);
2044 /* Count the number of escaped characters in the input string. */
2046 for (i
= 0; i
< filelen
; i
++) {
2047 if (!isesc
&& file
[i
] == '\\' && i
+ 1 < filelen
){
2054 if (tmplen
> (filelen
- cesc
)) {
2055 tmp2
= tmp
+ filelen
- cesc
;
2057 /* quote argument on way out */
2058 for (i
= 0; i
< len
; i
+= clen
) {
2059 if ((clen
= mblen(tmp2
+ i
, len
- i
)) < 0 ||
2060 (size_t)clen
> sizeof(ins
) - 2)
2061 fatal("invalid multibyte character");
2063 memcpy(ins
+ 1, tmp2
+ i
, clen
);
2064 ins
[clen
+ 1] = '\0';
2074 if (quote
== '\0' || tmp2
[i
] == quote
) {
2075 if (el_insertstr(el
, ins
) == -1)
2076 fatal("el_insertstr "
2082 if (el_insertstr(el
, ins
+ 1) == -1)
2083 fatal("el_insertstr failed.");
2090 if (g
.gl_matchc
== 1) {
2092 if (!terminated
&& quote
!= '\0')
2094 if (*(lf
->cursor
- 1) != '/' &&
2095 (lastarg
|| *(lf
->cursor
) != ' '))
2098 if (i
> 0 && el_insertstr(el
, ins
) == -1)
2099 fatal("el_insertstr failed.");
2108 /* tab-completion hook function, called via libedit */
2109 static unsigned char
2110 complete(EditLine
*el
, int ch
)
2112 char **argv
, *line
, quote
;
2114 u_int cursor
, len
, terminated
, ret
= CC_ERROR
;
2116 struct complete_ctx
*complete_ctx
;
2119 if (el_get(el
, EL_CLIENTDATA
, (void**)&complete_ctx
) != 0)
2120 fatal_f("el_get failed");
2122 /* Figure out which argument the cursor points to */
2123 cursor
= lf
->cursor
- lf
->buffer
;
2124 line
= xmalloc(cursor
+ 1);
2125 memcpy(line
, lf
->buffer
, cursor
);
2126 line
[cursor
] = '\0';
2127 argv
= makeargv(line
, &carg
, 1, "e
, &terminated
);
2130 /* Get all the arguments on the line */
2131 len
= lf
->lastchar
- lf
->buffer
;
2132 line
= xmalloc(len
+ 1);
2133 memcpy(line
, lf
->buffer
, len
);
2135 argv
= makeargv(line
, &argc
, 1, NULL
, NULL
);
2137 /* Ensure cursor is at EOL or a argument boundary */
2138 if (line
[cursor
] != ' ' && line
[cursor
] != '\0' &&
2139 line
[cursor
] != '\n') {
2145 /* Show all available commands */
2146 complete_cmd_parse(el
, NULL
, argc
== carg
, '\0', 1);
2148 } else if (carg
== 1 && cursor
> 0 && line
[cursor
- 1] != ' ') {
2149 /* Handle the command parsing */
2150 if (complete_cmd_parse(el
, argv
[0], argc
== carg
,
2151 quote
, terminated
) != 0)
2153 } else if (carg
>= 1) {
2154 /* Handle file parsing */
2156 int i
= 0, cmdarg
= 0;
2157 char *filematch
= NULL
;
2159 if (carg
> 1 && line
[cursor
-1] != ' ')
2160 filematch
= argv
[carg
- 1];
2162 for (i
= 1; i
< carg
; i
++) {
2164 if (argv
[i
][0] != '-')
2169 * If previous argument is complete, then offer completion
2172 if (line
[cursor
- 1] == ' ')
2175 remote
= complete_is_remote(argv
[0], cmdarg
);
2177 if ((remote
== REMOTE
|| remote
== LOCAL
) &&
2178 complete_match(el
, complete_ctx
->conn
,
2179 *complete_ctx
->remote_pathp
, filematch
,
2180 remote
, carg
== argc
, quote
, terminated
) != 0)
2187 #endif /* USE_LIBEDIT */
2190 interactive_loop(struct sftp_conn
*conn
, char *file1
, char *file2
)
2193 char *dir
= NULL
, *startdir
= NULL
;
2195 int err
, interactive
;
2196 EditLine
*el
= NULL
;
2200 extern char *__progname
;
2201 struct complete_ctx complete_ctx
;
2203 if (!batchmode
&& isatty(STDIN_FILENO
)) {
2204 if ((el
= el_init(__progname
, stdin
, stdout
, stderr
)) == NULL
)
2205 fatal("Couldn't initialise editline");
2206 if ((hl
= history_init()) == NULL
)
2207 fatal("Couldn't initialise editline history");
2208 history(hl
, &hev
, H_SETSIZE
, 100);
2209 el_set(el
, EL_HIST
, history
, hl
);
2211 el_set(el
, EL_PROMPT
, prompt
);
2212 el_set(el
, EL_EDITOR
, "emacs");
2213 el_set(el
, EL_TERMINAL
, NULL
);
2214 el_set(el
, EL_SIGNAL
, 1);
2215 el_source(el
, NULL
);
2217 /* Tab Completion */
2218 el_set(el
, EL_ADDFN
, "ftp-complete",
2219 "Context sensitive argument completion", complete
);
2220 complete_ctx
.conn
= conn
;
2221 complete_ctx
.remote_pathp
= &remote_path
;
2222 el_set(el
, EL_CLIENTDATA
, (void*)&complete_ctx
);
2223 el_set(el
, EL_BIND
, "^I", "ftp-complete", NULL
);
2224 /* enable ctrl-left-arrow and ctrl-right-arrow */
2225 el_set(el
, EL_BIND
, "\\e[1;5C", "em-next-word", NULL
);
2226 el_set(el
, EL_BIND
, "\\e\\e[C", "em-next-word", NULL
);
2227 el_set(el
, EL_BIND
, "\\e[1;5D", "ed-prev-word", NULL
);
2228 el_set(el
, EL_BIND
, "\\e\\e[D", "ed-prev-word", NULL
);
2229 /* make ^w match ksh behaviour */
2230 el_set(el
, EL_BIND
, "^w", "ed-delete-prev-word", NULL
);
2232 #endif /* USE_LIBEDIT */
2234 remote_path
= do_realpath(conn
, ".");
2235 if (remote_path
== NULL
)
2237 startdir
= xstrdup(remote_path
);
2239 if (file1
!= NULL
) {
2240 dir
= xstrdup(file1
);
2241 dir
= make_absolute(dir
, remote_path
);
2243 if (remote_is_dir(conn
, dir
) && file2
== NULL
) {
2245 mprintf("Changing to: %s\n", dir
);
2246 snprintf(cmd
, sizeof cmd
, "cd \"%s\"", dir
);
2247 if (parse_dispatch_command(conn
, cmd
,
2248 &remote_path
, startdir
, 1, 0) != 0) {
2256 /* XXX this is wrong wrt quoting */
2257 snprintf(cmd
, sizeof cmd
, "get%s %s%s%s",
2258 global_aflag
? " -a" : "", dir
,
2259 file2
== NULL
? "" : " ",
2260 file2
== NULL
? "" : file2
);
2261 err
= parse_dispatch_command(conn
, cmd
,
2262 &remote_path
, startdir
, 1, 0);
2272 setvbuf(stdout
, NULL
, _IOLBF
, 0);
2273 setvbuf(infile
, NULL
, _IOLBF
, 0);
2275 interactive
= !batchmode
&& isatty(STDIN_FILENO
);
2278 struct sigaction sa
;
2281 memset(&sa
, 0, sizeof(sa
));
2282 sa
.sa_handler
= interactive
? read_interrupt
: killchild
;
2283 if (sigaction(SIGINT
, &sa
, NULL
) == -1) {
2284 debug3("sigaction(%s): %s", strsignal(SIGINT
),
2291 if (fgets(cmd
, sizeof(cmd
), infile
) == NULL
) {
2303 if ((line
= el_gets(el
, &count
)) == NULL
||
2310 history(hl
, &hev
, H_ENTER
, line
);
2311 if (strlcpy(cmd
, line
, sizeof(cmd
)) >= sizeof(cmd
)) {
2312 fprintf(stderr
, "Error: input line too long\n");
2315 #endif /* USE_LIBEDIT */
2318 cmd
[strcspn(cmd
, "\n")] = '\0';
2320 /* Handle user interrupts gracefully during commands */
2322 ssh_signal(SIGINT
, cmd_interrupt
);
2324 err
= parse_dispatch_command(conn
, cmd
, &remote_path
,
2325 startdir
, batchmode
, !interactive
&& el
== NULL
);
2329 ssh_signal(SIGCHLD
, SIG_DFL
);
2337 #endif /* USE_LIBEDIT */
2339 /* err == 1 signifies normal "quit" exit */
2340 return (err
>= 0 ? 0 : -1);
2344 connect_to_server(char *path
, char **args
, int *in
, int *out
)
2348 int pin
[2], pout
[2];
2350 if ((pipe(pin
) == -1) || (pipe(pout
) == -1))
2351 fatal("pipe: %s", strerror(errno
));
2356 #else /* USE_PIPES */
2359 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) == -1)
2360 fatal("socketpair: %s", strerror(errno
));
2361 *in
= *out
= inout
[0];
2362 c_in
= c_out
= inout
[1];
2363 #endif /* USE_PIPES */
2365 if ((sshpid
= fork()) == -1)
2366 fatal("fork: %s", strerror(errno
));
2367 else if (sshpid
== 0) {
2368 if ((dup2(c_in
, STDIN_FILENO
) == -1) ||
2369 (dup2(c_out
, STDOUT_FILENO
) == -1)) {
2370 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
2379 * The underlying ssh is in the same process group, so we must
2380 * ignore SIGINT if we want to gracefully abort commands,
2381 * otherwise the signal will make it to the ssh process and
2382 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2383 * underlying ssh, it must *not* ignore that signal.
2385 ssh_signal(SIGINT
, SIG_IGN
);
2386 ssh_signal(SIGTERM
, SIG_DFL
);
2388 fprintf(stderr
, "exec: %s: %s\n", path
, strerror(errno
));
2392 ssh_signal(SIGTERM
, killchild
);
2393 ssh_signal(SIGINT
, killchild
);
2394 ssh_signal(SIGHUP
, killchild
);
2395 ssh_signal(SIGTSTP
, suspchild
);
2396 ssh_signal(SIGTTIN
, suspchild
);
2397 ssh_signal(SIGTTOU
, suspchild
);
2398 ssh_signal(SIGCHLD
, sigchld_handler
);
2406 extern char *__progname
;
2409 "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2410 " [-D sftp_server_command] [-F ssh_config] [-i identity_file]\n"
2411 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
2412 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
2419 main(int argc
, char **argv
)
2421 int r
, in
, out
, ch
, err
, tmp
, port
= -1, noisy
= 0;
2422 char *host
= NULL
, *user
, *cp
, **cpp
, *file2
= NULL
;
2423 int debug_level
= 0;
2424 char *file1
= NULL
, *sftp_server
= NULL
;
2425 char *ssh_program
= _PATH_SSH_PROGRAM
, *sftp_direct
= NULL
;
2427 LogLevel ll
= SYSLOG_LEVEL_INFO
;
2430 extern char *optarg
;
2431 struct sftp_conn
*conn
;
2432 size_t copy_buffer_len
= 0;
2433 size_t num_requests
= 0;
2434 long long limit_kbps
= 0;
2436 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2440 __progname
= ssh_get_progname(argv
[0]);
2441 memset(&args
, '\0', sizeof(args
));
2443 addargs(&args
, "%s", ssh_program
);
2444 addargs(&args
, "-oForwardX11 no");
2445 addargs(&args
, "-oPermitLocalCommand no");
2446 addargs(&args
, "-oClearAllForwardings yes");
2448 ll
= SYSLOG_LEVEL_INFO
;
2451 while ((ch
= getopt(argc
, argv
,
2452 "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:")) != -1) {
2454 /* Passed through to ssh(1) */
2459 addargs(&args
, "-%c", ch
);
2461 /* Passed through to ssh(1) with argument */
2467 addargs(&args
, "-%c", ch
);
2468 addargs(&args
, "%s", optarg
);
2471 ll
= SYSLOG_LEVEL_ERROR
;
2474 addargs(&args
, "-%c", ch
);
2477 port
= a2port(optarg
);
2479 fatal("Bad port \"%s\"\n", optarg
);
2482 if (debug_level
< 3) {
2483 addargs(&args
, "-v");
2484 ll
= SYSLOG_LEVEL_DEBUG1
+ debug_level
;
2489 fatal("SSH protocol v.1 is no longer supported");
2492 /* accept silently */
2498 copy_buffer_len
= strtol(optarg
, &cp
, 10);
2499 if (copy_buffer_len
== 0 || *cp
!= '\0')
2500 fatal("Invalid buffer size \"%s\"", optarg
);
2504 fatal("Batch file already specified.");
2506 /* Allow "-" as stdin */
2507 if (strcmp(optarg
, "-") != 0 &&
2508 (infile
= fopen(optarg
, "r")) == NULL
)
2509 fatal("%s (%s).", strerror(errno
), optarg
);
2511 quiet
= batchmode
= 1;
2512 addargs(&args
, "-obatchmode yes");
2518 noisy
= 1; /* Used to clear quiet mode after getopt */
2524 sftp_direct
= optarg
;
2527 limit_kbps
= strtonum(optarg
, 1, 100 * 1024 * 1024,
2531 limit_kbps
*= 1024; /* kbps */
2537 num_requests
= strtol(optarg
, &cp
, 10);
2538 if (num_requests
== 0 || *cp
!= '\0')
2539 fatal("Invalid number of requests \"%s\"",
2543 sftp_server
= optarg
;
2546 ssh_program
= optarg
;
2547 replacearg(&args
, 0, "%s", ssh_program
);
2555 /* Do this last because we want the user to be able to override it */
2556 addargs(&args
, "-oForwardAgent no");
2558 if (!isatty(STDERR_FILENO
))
2564 log_init(argv
[0], ll
, SYSLOG_FACILITY_USER
, 1);
2566 if (sftp_direct
== NULL
) {
2567 if (optind
== argc
|| argc
> (optind
+ 2))
2571 switch (parse_uri("sftp", *argv
, &user
, &host
, &tmp
, &file1
)) {
2580 /* Try with user, host and path. */
2581 if (parse_user_host_path(*argv
, &user
, &host
,
2584 /* Try with user and host. */
2585 if (parse_user_host_port(*argv
, &user
, &host
, NULL
)
2588 /* Treat as a plain hostname. */
2589 host
= xstrdup(*argv
);
2590 host
= cleanhostname(host
);
2593 file2
= *(argv
+ 1);
2596 fprintf(stderr
, "Missing hostname\n");
2601 addargs(&args
, "-oPort %d", port
);
2603 addargs(&args
, "-l");
2604 addargs(&args
, "%s", user
);
2607 /* no subsystem if the server-spec contains a '/' */
2608 if (sftp_server
== NULL
|| strchr(sftp_server
, '/') == NULL
)
2609 addargs(&args
, "-s");
2611 addargs(&args
, "--");
2612 addargs(&args
, "%s", host
);
2613 addargs(&args
, "%s", (sftp_server
!= NULL
?
2614 sftp_server
: "sftp"));
2616 connect_to_server(ssh_program
, args
.list
, &in
, &out
);
2618 if ((r
= argv_split(sftp_direct
, &tmp
, &cpp
, 1)) != 0)
2619 fatal_r(r
, "Parse -D arguments");
2621 fatal("No sftp server specified via -D");
2622 connect_to_server(cpp
[0], cpp
, &in
, &out
);
2623 argv_free(cpp
, tmp
);
2627 conn
= do_init(in
, out
, copy_buffer_len
, num_requests
, limit_kbps
);
2629 fatal("Couldn't initialise connection to server");
2632 if (sftp_direct
== NULL
)
2633 fprintf(stderr
, "Connected to %s.\n", host
);
2635 fprintf(stderr
, "Attached to %s.\n", sftp_direct
);
2638 err
= interactive_loop(conn
, file1
, file2
);
2640 #if !defined(USE_PIPES)
2641 shutdown(in
, SHUT_RDWR
);
2642 shutdown(out
, SHUT_RDWR
);
2650 while (waitpid(sshpid
, NULL
, 0) == -1 && sshpid
> 1)
2652 fatal("Couldn't wait for ssh process: %s",
2655 exit(err
== 0 ? 0 : 1);