1 /* $OpenBSD: sftp.c,v 1.180 2017/06/10 06:33:34 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/param.h>
26 #include <sys/socket.h>
28 #ifdef HAVE_SYS_STATVFS_H
29 #include <sys/statvfs.h>
47 typedef void EditLine
;
64 #include "pathnames.h"
71 #include "sftp-common.h"
72 #include "sftp-client.h"
74 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
75 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
77 /* File to read commands from */
80 /* Are we in batchfile mode? */
83 /* PID of ssh transport process */
84 static pid_t sshpid
= -1;
86 /* Suppress diagnositic messages */
89 /* This is set to 0 if the progressmeter is not desired. */
92 /* When this option is set, we always recursively download/upload directories */
95 /* When this option is set, we resume download or upload if possible */
98 /* When this option is set, the file transfers will always preserve times */
101 /* When this option is set, transfers will have fsync() called on each file */
102 int global_fflag
= 0;
104 /* SIGINT received during command processing */
105 volatile sig_atomic_t interrupted
= 0;
107 /* I wish qsort() took a separate ctx for the comparison function...*/
111 /* Context used for commandline completion */
112 struct complete_ctx
{
113 struct sftp_conn
*conn
;
117 int remote_glob(struct sftp_conn
*, const char *, int,
118 int (*)(const char *, int), glob_t
*); /* proto for sftp-glob.c */
120 extern char *__progname
;
122 /* Separators for interactive commands */
123 #define WHITESPACE " \t\r\n"
126 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
127 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
128 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
129 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
130 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
131 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
132 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
133 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
134 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
136 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
137 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
139 /* Commands for interactive mode */
176 /* Type of completion */
181 static const struct CMD cmds
[] = {
182 { "bye", I_QUIT
, NOARGS
},
183 { "cd", I_CHDIR
, REMOTE
},
184 { "chdir", I_CHDIR
, REMOTE
},
185 { "chgrp", I_CHGRP
, REMOTE
},
186 { "chmod", I_CHMOD
, REMOTE
},
187 { "chown", I_CHOWN
, REMOTE
},
188 { "df", I_DF
, REMOTE
},
189 { "dir", I_LS
, REMOTE
},
190 { "exit", I_QUIT
, NOARGS
},
191 { "get", I_GET
, REMOTE
},
192 { "help", I_HELP
, NOARGS
},
193 { "lcd", I_LCHDIR
, LOCAL
},
194 { "lchdir", I_LCHDIR
, LOCAL
},
195 { "lls", I_LLS
, LOCAL
},
196 { "lmkdir", I_LMKDIR
, LOCAL
},
197 { "ln", I_LINK
, REMOTE
},
198 { "lpwd", I_LPWD
, LOCAL
},
199 { "ls", I_LS
, REMOTE
},
200 { "lumask", I_LUMASK
, NOARGS
},
201 { "mkdir", I_MKDIR
, REMOTE
},
202 { "mget", I_GET
, REMOTE
},
203 { "mput", I_PUT
, LOCAL
},
204 { "progress", I_PROGRESS
, NOARGS
},
205 { "put", I_PUT
, LOCAL
},
206 { "pwd", I_PWD
, REMOTE
},
207 { "quit", I_QUIT
, NOARGS
},
208 { "reget", I_REGET
, REMOTE
},
209 { "rename", I_RENAME
, REMOTE
},
210 { "reput", I_REPUT
, LOCAL
},
211 { "rm", I_RM
, REMOTE
},
212 { "rmdir", I_RMDIR
, REMOTE
},
213 { "symlink", I_SYMLINK
, REMOTE
},
214 { "version", I_VERSION
, NOARGS
},
215 { "!", I_SHELL
, NOARGS
},
216 { "?", I_HELP
, NOARGS
},
220 int interactive_loop(struct sftp_conn
*, char *file1
, char *file2
);
227 kill(sshpid
, SIGTERM
);
228 waitpid(sshpid
, NULL
, 0);
240 while (waitpid(sshpid
, NULL
, WUNTRACED
) == -1 && errno
== EINTR
)
243 kill(getpid(), SIGSTOP
);
248 cmd_interrupt(int signo
)
250 const char msg
[] = "\rInterrupt \n";
251 int olderrno
= errno
;
253 (void)write(STDERR_FILENO
, msg
, sizeof(msg
) - 1);
261 printf("Available commands:\n"
263 "cd path Change remote directory to 'path'\n"
264 "chgrp grp path Change group of file 'path' to 'grp'\n"
265 "chmod mode path Change permissions of file 'path' to 'mode'\n"
266 "chown own path Change owner of file 'path' to 'own'\n"
267 "df [-hi] [path] Display statistics for current directory or\n"
268 " filesystem containing 'path'\n"
270 "get [-afPpRr] remote [local] Download file\n"
271 "reget [-fPpRr] remote [local] Resume download file\n"
272 "reput [-fPpRr] [local] remote Resume upload file\n"
273 "help Display this help text\n"
274 "lcd path Change local directory to 'path'\n"
275 "lls [ls-options [path]] Display local directory listing\n"
276 "lmkdir path Create local directory\n"
277 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
278 "lpwd Print local working directory\n"
279 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
280 "lumask umask Set local umask to 'umask'\n"
281 "mkdir path Create remote directory\n"
282 "progress Toggle display of progress meter\n"
283 "put [-afPpRr] local [remote] Upload file\n"
284 "pwd Display remote working directory\n"
286 "rename oldpath newpath Rename remote file\n"
287 "rm path Delete remote file\n"
288 "rmdir path Remove remote directory\n"
289 "symlink oldpath newpath Symlink remote file\n"
290 "version Show SFTP version\n"
291 "!command Execute 'command' in local shell\n"
292 "! Escape to local shell\n"
293 "? Synonym for help\n");
297 local_do_shell(const char *args
)
306 if ((shell
= getenv("SHELL")) == NULL
|| *shell
== '\0')
307 shell
= _PATH_BSHELL
;
309 if ((pid
= fork()) == -1)
310 fatal("Couldn't fork: %s", strerror(errno
));
313 /* XXX: child has pipe fds to ssh subproc open - issue? */
315 debug3("Executing %s -c \"%s\"", shell
, args
);
316 execl(shell
, shell
, "-c", args
, (char *)NULL
);
318 debug3("Executing %s", shell
);
319 execl(shell
, shell
, (char *)NULL
);
321 fprintf(stderr
, "Couldn't execute \"%s\": %s\n", shell
,
325 while (waitpid(pid
, &status
, 0) == -1)
327 fatal("Couldn't wait for child: %s", strerror(errno
));
328 if (!WIFEXITED(status
))
329 error("Shell exited abnormally");
330 else if (WEXITSTATUS(status
))
331 error("Shell exited with status %d", WEXITSTATUS(status
));
335 local_do_ls(const char *args
)
338 local_do_shell(_PATH_LS
);
340 int len
= strlen(_PATH_LS
" ") + strlen(args
) + 1;
341 char *buf
= xmalloc(len
);
343 /* XXX: quoting - rip quoting code from ftp? */
344 snprintf(buf
, len
, _PATH_LS
" %s", args
);
350 /* Strip one path (usually the pwd) from the start of another */
352 path_strip(const char *path
, const char *strip
)
357 return (xstrdup(path
));
360 if (strncmp(path
, strip
, len
) == 0) {
361 if (strip
[len
- 1] != '/' && path
[len
] == '/')
363 return (xstrdup(path
+ len
));
366 return (xstrdup(path
));
370 make_absolute(char *p
, const char *pwd
)
375 if (p
&& p
[0] != '/') {
376 abs_str
= path_append(pwd
, p
);
384 parse_getput_flags(const char *cmd
, char **argv
, int argc
,
385 int *aflag
, int *fflag
, int *pflag
, int *rflag
)
387 extern int opterr
, optind
, optopt
, optreset
;
390 optind
= optreset
= 1;
393 *aflag
= *fflag
= *rflag
= *pflag
= 0;
394 while ((ch
= getopt(argc
, argv
, "afPpRr")) != -1) {
411 error("%s: Invalid flag -%c", cmd
, optopt
);
420 parse_link_flags(const char *cmd
, char **argv
, int argc
, int *sflag
)
422 extern int opterr
, optind
, optopt
, optreset
;
425 optind
= optreset
= 1;
429 while ((ch
= getopt(argc
, argv
, "s")) != -1) {
435 error("%s: Invalid flag -%c", cmd
, optopt
);
444 parse_rename_flags(const char *cmd
, char **argv
, int argc
, int *lflag
)
446 extern int opterr
, optind
, optopt
, optreset
;
449 optind
= optreset
= 1;
453 while ((ch
= getopt(argc
, argv
, "l")) != -1) {
459 error("%s: Invalid flag -%c", cmd
, optopt
);
468 parse_ls_flags(char **argv
, int argc
, int *lflag
)
470 extern int opterr
, optind
, optopt
, optreset
;
473 optind
= optreset
= 1;
476 *lflag
= LS_NAME_SORT
;
477 while ((ch
= getopt(argc
, argv
, "1Safhlnrt")) != -1) {
480 *lflag
&= ~VIEW_FLAGS
;
481 *lflag
|= LS_SHORT_VIEW
;
484 *lflag
&= ~SORT_FLAGS
;
485 *lflag
|= LS_SIZE_SORT
;
488 *lflag
|= LS_SHOW_ALL
;
491 *lflag
&= ~SORT_FLAGS
;
494 *lflag
|= LS_SI_UNITS
;
497 *lflag
&= ~LS_SHORT_VIEW
;
498 *lflag
|= LS_LONG_VIEW
;
501 *lflag
&= ~LS_SHORT_VIEW
;
502 *lflag
|= LS_NUMERIC_VIEW
|LS_LONG_VIEW
;
505 *lflag
|= LS_REVERSE_SORT
;
508 *lflag
&= ~SORT_FLAGS
;
509 *lflag
|= LS_TIME_SORT
;
512 error("ls: Invalid flag -%c", optopt
);
521 parse_df_flags(const char *cmd
, char **argv
, int argc
, int *hflag
, int *iflag
)
523 extern int opterr
, optind
, optopt
, optreset
;
526 optind
= optreset
= 1;
530 while ((ch
= getopt(argc
, argv
, "hi")) != -1) {
539 error("%s: Invalid flag -%c", cmd
, optopt
);
548 parse_no_flags(const char *cmd
, char **argv
, int argc
)
550 extern int opterr
, optind
, optopt
, optreset
;
553 optind
= optreset
= 1;
556 while ((ch
= getopt(argc
, argv
, "")) != -1) {
559 error("%s: Invalid flag -%c", cmd
, optopt
);
568 is_dir(const char *path
)
572 /* XXX: report errors? */
573 if (stat(path
, &sb
) == -1)
576 return(S_ISDIR(sb
.st_mode
));
580 remote_is_dir(struct sftp_conn
*conn
, const char *path
)
584 /* XXX: report errors? */
585 if ((a
= do_stat(conn
, path
, 1)) == NULL
)
587 if (!(a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
))
589 return(S_ISDIR(a
->perm
));
592 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
594 pathname_is_dir(const char *pathname
)
596 size_t l
= strlen(pathname
);
598 return l
> 0 && pathname
[l
- 1] == '/';
602 process_get(struct sftp_conn
*conn
, const char *src
, const char *dst
,
603 const char *pwd
, int pflag
, int rflag
, int resume
, int fflag
)
605 char *abs_src
= NULL
;
606 char *abs_dst
= NULL
;
608 char *filename
, *tmp
=NULL
;
611 abs_src
= xstrdup(src
);
612 abs_src
= make_absolute(abs_src
, pwd
);
613 memset(&g
, 0, sizeof(g
));
615 debug3("Looking up %s", abs_src
);
616 if ((r
= remote_glob(conn
, abs_src
, GLOB_MARK
, NULL
, &g
)) != 0) {
617 if (r
== GLOB_NOSPACE
) {
618 error("Too many matches for \"%s\".", abs_src
);
620 error("File \"%s\" not found.", abs_src
);
627 * If multiple matches then dst must be a directory or
630 if (g
.gl_matchc
> 1 && dst
!= NULL
&& !is_dir(dst
)) {
631 error("Multiple source paths, but destination "
632 "\"%s\" is not a directory", dst
);
637 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
638 tmp
= xstrdup(g
.gl_pathv
[i
]);
639 if ((filename
= basename(tmp
)) == NULL
) {
640 error("basename %s: %s", tmp
, strerror(errno
));
646 if (g
.gl_matchc
== 1 && dst
) {
648 abs_dst
= path_append(dst
, filename
);
650 abs_dst
= xstrdup(dst
);
653 abs_dst
= path_append(dst
, filename
);
655 abs_dst
= xstrdup(filename
);
659 resume
|= global_aflag
;
660 if (!quiet
&& resume
)
661 mprintf("Resuming %s to %s\n",
662 g
.gl_pathv
[i
], abs_dst
);
663 else if (!quiet
&& !resume
)
664 mprintf("Fetching %s to %s\n",
665 g
.gl_pathv
[i
], abs_dst
);
666 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
667 if (download_dir(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
668 pflag
|| global_pflag
, 1, resume
,
669 fflag
|| global_fflag
) == -1)
672 if (do_download(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
673 pflag
|| global_pflag
, resume
,
674 fflag
|| global_fflag
) == -1)
688 process_put(struct sftp_conn
*conn
, const char *src
, const char *dst
,
689 const char *pwd
, int pflag
, int rflag
, int resume
, int fflag
)
691 char *tmp_dst
= NULL
;
692 char *abs_dst
= NULL
;
693 char *tmp
= NULL
, *filename
= NULL
;
696 int i
, dst_is_dir
= 1;
700 tmp_dst
= xstrdup(dst
);
701 tmp_dst
= make_absolute(tmp_dst
, pwd
);
704 memset(&g
, 0, sizeof(g
));
705 debug3("Looking up %s", src
);
706 if (glob(src
, GLOB_NOCHECK
| GLOB_MARK
, NULL
, &g
)) {
707 error("File \"%s\" not found.", src
);
712 /* If we aren't fetching to pwd then stash this status for later */
714 dst_is_dir
= remote_is_dir(conn
, tmp_dst
);
716 /* If multiple matches, dst may be directory or unspecified */
717 if (g
.gl_matchc
> 1 && tmp_dst
&& !dst_is_dir
) {
718 error("Multiple paths match, but destination "
719 "\"%s\" is not a directory", tmp_dst
);
724 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
725 if (stat(g
.gl_pathv
[i
], &sb
) == -1) {
727 error("stat %s: %s", g
.gl_pathv
[i
], strerror(errno
));
731 tmp
= xstrdup(g
.gl_pathv
[i
]);
732 if ((filename
= basename(tmp
)) == NULL
) {
733 error("basename %s: %s", tmp
, strerror(errno
));
739 if (g
.gl_matchc
== 1 && tmp_dst
) {
740 /* If directory specified, append filename */
742 abs_dst
= path_append(tmp_dst
, filename
);
744 abs_dst
= xstrdup(tmp_dst
);
745 } else if (tmp_dst
) {
746 abs_dst
= path_append(tmp_dst
, filename
);
748 abs_dst
= make_absolute(xstrdup(filename
), pwd
);
752 resume
|= global_aflag
;
753 if (!quiet
&& resume
)
754 mprintf("Resuming upload of %s to %s\n",
755 g
.gl_pathv
[i
], abs_dst
);
756 else if (!quiet
&& !resume
)
757 mprintf("Uploading %s to %s\n",
758 g
.gl_pathv
[i
], abs_dst
);
759 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
760 if (upload_dir(conn
, g
.gl_pathv
[i
], abs_dst
,
761 pflag
|| global_pflag
, 1, resume
,
762 fflag
|| global_fflag
) == -1)
765 if (do_upload(conn
, g
.gl_pathv
[i
], abs_dst
,
766 pflag
|| global_pflag
, resume
,
767 fflag
|| global_fflag
) == -1)
780 sdirent_comp(const void *aa
, const void *bb
)
782 SFTP_DIRENT
*a
= *(SFTP_DIRENT
**)aa
;
783 SFTP_DIRENT
*b
= *(SFTP_DIRENT
**)bb
;
784 int rmul
= sort_flag
& LS_REVERSE_SORT
? -1 : 1;
786 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
787 if (sort_flag
& LS_NAME_SORT
)
788 return (rmul
* strcmp(a
->filename
, b
->filename
));
789 else if (sort_flag
& LS_TIME_SORT
)
790 return (rmul
* NCMP(a
->a
.mtime
, b
->a
.mtime
));
791 else if (sort_flag
& LS_SIZE_SORT
)
792 return (rmul
* NCMP(a
->a
.size
, b
->a
.size
));
794 fatal("Unknown ls sort type");
797 /* sftp ls.1 replacement for directories */
799 do_ls_dir(struct sftp_conn
*conn
, const char *path
,
800 const char *strip_path
, int lflag
)
803 u_int c
= 1, colspace
= 0, columns
= 1;
806 if ((n
= do_readdir(conn
, path
, &d
)) != 0)
809 if (!(lflag
& LS_SHORT_VIEW
)) {
810 u_int m
= 0, width
= 80;
814 /* Count entries for sort and find longest filename */
815 for (n
= 0; d
[n
] != NULL
; n
++) {
816 if (d
[n
]->filename
[0] != '.' || (lflag
& LS_SHOW_ALL
))
817 m
= MAXIMUM(m
, strlen(d
[n
]->filename
));
820 /* Add any subpath that also needs to be counted */
821 tmp
= path_strip(path
, strip_path
);
825 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
828 columns
= width
/ (m
+ 2);
829 columns
= MAXIMUM(columns
, 1);
830 colspace
= width
/ columns
;
831 colspace
= MINIMUM(colspace
, width
);
834 if (lflag
& SORT_FLAGS
) {
835 for (n
= 0; d
[n
] != NULL
; n
++)
836 ; /* count entries */
837 sort_flag
= lflag
& (SORT_FLAGS
|LS_REVERSE_SORT
);
838 qsort(d
, n
, sizeof(*d
), sdirent_comp
);
841 for (n
= 0; d
[n
] != NULL
&& !interrupted
; n
++) {
844 if (d
[n
]->filename
[0] == '.' && !(lflag
& LS_SHOW_ALL
))
847 tmp
= path_append(path
, d
[n
]->filename
);
848 fname
= path_strip(tmp
, strip_path
);
851 if (lflag
& LS_LONG_VIEW
) {
852 if (lflag
& (LS_NUMERIC_VIEW
|LS_SI_UNITS
)) {
856 memset(&sb
, 0, sizeof(sb
));
857 attrib_to_stat(&d
[n
]->a
, &sb
);
858 lname
= ls_file(fname
, &sb
, 1,
859 (lflag
& LS_SI_UNITS
));
860 mprintf("%s\n", lname
);
863 mprintf("%s\n", d
[n
]->longname
);
865 mprintf("%-*s", colspace
, fname
);
876 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
879 free_sftp_dirents(d
);
884 sglob_comp(const void *aa
, const void *bb
)
886 u_int a
= *(const u_int
*)aa
;
887 u_int b
= *(const u_int
*)bb
;
888 const char *ap
= sort_glob
->gl_pathv
[a
];
889 const char *bp
= sort_glob
->gl_pathv
[b
];
890 const struct stat
*as
= sort_glob
->gl_statv
[a
];
891 const struct stat
*bs
= sort_glob
->gl_statv
[b
];
892 int rmul
= sort_flag
& LS_REVERSE_SORT
? -1 : 1;
894 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
895 if (sort_flag
& LS_NAME_SORT
)
896 return (rmul
* strcmp(ap
, bp
));
897 else if (sort_flag
& LS_TIME_SORT
) {
898 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
899 return (rmul
* timespeccmp(&as
->st_mtim
, &bs
->st_mtim
, <));
900 #elif defined(HAVE_STRUCT_STAT_ST_MTIME)
901 return (rmul
* NCMP(as
->st_mtime
, bs
->st_mtime
));
905 } else if (sort_flag
& LS_SIZE_SORT
)
906 return (rmul
* NCMP(as
->st_size
, bs
->st_size
));
908 fatal("Unknown ls sort type");
911 /* sftp ls.1 replacement which handles path globs */
913 do_globbed_ls(struct sftp_conn
*conn
, const char *path
,
914 const char *strip_path
, int lflag
)
920 u_int i
, j
, nentries
, *indices
= NULL
, c
= 1;
921 u_int colspace
= 0, columns
= 1, m
= 0, width
= 80;
923 memset(&g
, 0, sizeof(g
));
925 if ((r
= remote_glob(conn
, path
,
926 GLOB_MARK
|GLOB_NOCHECK
|GLOB_BRACE
|GLOB_KEEPSTAT
|GLOB_NOSORT
,
928 (g
.gl_pathc
&& !g
.gl_matchc
)) {
931 if (r
== GLOB_NOSPACE
) {
932 error("Can't ls: Too many matches for \"%s\"", path
);
934 error("Can't ls: \"%s\" not found", path
);
943 * If the glob returns a single match and it is a directory,
944 * then just list its contents.
946 if (g
.gl_matchc
== 1 && g
.gl_statv
[0] != NULL
&&
947 S_ISDIR(g
.gl_statv
[0]->st_mode
)) {
948 err
= do_ls_dir(conn
, g
.gl_pathv
[0], strip_path
, lflag
);
953 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
956 if (!(lflag
& LS_SHORT_VIEW
)) {
957 /* Count entries for sort and find longest filename */
958 for (i
= 0; g
.gl_pathv
[i
]; i
++)
959 m
= MAXIMUM(m
, strlen(g
.gl_pathv
[i
]));
961 columns
= width
/ (m
+ 2);
962 columns
= MAXIMUM(columns
, 1);
963 colspace
= width
/ columns
;
967 * Sorting: rather than mess with the contents of glob_t, prepare
968 * an array of indices into it and sort that. For the usual
969 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
971 for (nentries
= 0; g
.gl_pathv
[nentries
] != NULL
; nentries
++)
972 ; /* count entries */
973 indices
= calloc(nentries
, sizeof(*indices
));
974 for (i
= 0; i
< nentries
; i
++)
977 if (lflag
& SORT_FLAGS
) {
979 sort_flag
= lflag
& (SORT_FLAGS
|LS_REVERSE_SORT
);
980 qsort(indices
, nentries
, sizeof(*indices
), sglob_comp
);
984 for (j
= 0; j
< nentries
&& !interrupted
; j
++) {
986 fname
= path_strip(g
.gl_pathv
[i
], strip_path
);
987 if (lflag
& LS_LONG_VIEW
) {
988 if (g
.gl_statv
[i
] == NULL
) {
989 error("no stat information for %s", fname
);
992 lname
= ls_file(fname
, g
.gl_statv
[i
], 1,
993 (lflag
& LS_SI_UNITS
));
994 mprintf("%s\n", lname
);
997 mprintf("%-*s", colspace
, fname
);
1007 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
1019 do_df(struct sftp_conn
*conn
, const char *path
, int hflag
, int iflag
)
1021 struct sftp_statvfs st
;
1022 char s_used
[FMT_SCALED_STRSIZE
], s_avail
[FMT_SCALED_STRSIZE
];
1023 char s_root
[FMT_SCALED_STRSIZE
], s_total
[FMT_SCALED_STRSIZE
];
1024 char s_icapacity
[16], s_dcapacity
[16];
1026 if (do_statvfs(conn
, path
, &st
, 1) == -1)
1028 if (st
.f_files
== 0)
1029 strlcpy(s_icapacity
, "ERR", sizeof(s_icapacity
));
1031 snprintf(s_icapacity
, sizeof(s_icapacity
), "%3llu%%",
1032 (unsigned long long)(100 * (st
.f_files
- st
.f_ffree
) /
1035 if (st
.f_blocks
== 0)
1036 strlcpy(s_dcapacity
, "ERR", sizeof(s_dcapacity
));
1038 snprintf(s_dcapacity
, sizeof(s_dcapacity
), "%3llu%%",
1039 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
1043 printf(" Inodes Used Avail "
1044 "(root) %%Capacity\n");
1045 printf("%11llu %11llu %11llu %11llu %s\n",
1046 (unsigned long long)st
.f_files
,
1047 (unsigned long long)(st
.f_files
- st
.f_ffree
),
1048 (unsigned long long)st
.f_favail
,
1049 (unsigned long long)st
.f_ffree
, s_icapacity
);
1051 strlcpy(s_used
, "error", sizeof(s_used
));
1052 strlcpy(s_avail
, "error", sizeof(s_avail
));
1053 strlcpy(s_root
, "error", sizeof(s_root
));
1054 strlcpy(s_total
, "error", sizeof(s_total
));
1055 fmt_scaled((st
.f_blocks
- st
.f_bfree
) * st
.f_frsize
, s_used
);
1056 fmt_scaled(st
.f_bavail
* st
.f_frsize
, s_avail
);
1057 fmt_scaled(st
.f_bfree
* st
.f_frsize
, s_root
);
1058 fmt_scaled(st
.f_blocks
* st
.f_frsize
, s_total
);
1059 printf(" Size Used Avail (root) %%Capacity\n");
1060 printf("%7sB %7sB %7sB %7sB %s\n",
1061 s_total
, s_used
, s_avail
, s_root
, s_dcapacity
);
1063 printf(" Size Used Avail "
1064 "(root) %%Capacity\n");
1065 printf("%12llu %12llu %12llu %12llu %s\n",
1066 (unsigned long long)(st
.f_frsize
* st
.f_blocks
/ 1024),
1067 (unsigned long long)(st
.f_frsize
*
1068 (st
.f_blocks
- st
.f_bfree
) / 1024),
1069 (unsigned long long)(st
.f_frsize
* st
.f_bavail
/ 1024),
1070 (unsigned long long)(st
.f_frsize
* st
.f_bfree
/ 1024),
1077 * Undo escaping of glob sequences in place. Used to undo extra escaping
1078 * applied in makeargv() when the string is destined for a function that
1082 undo_glob_escape(char *s
)
1117 * Split a string into an argument vector using sh(1)-style quoting,
1118 * comment and escaping rules, but with some tweaks to handle glob(3)
1120 * The "sloppy" flag allows for recovery from missing terminating quote, for
1121 * use in parsing incomplete commandlines during tab autocompletion.
1123 * Returns NULL on error or a NULL-terminated array of arguments.
1125 * If "lastquote" is not NULL, the quoting character used for the last
1126 * argument is placed in *lastquote ("\0", "'" or "\"").
1128 * If "terminated" is not NULL, *terminated will be set to 1 when the
1129 * last argument's quote has been properly terminated or 0 otherwise.
1130 * This parameter is only of use if "sloppy" is set.
1133 #define MAXARGLEN 8192
1135 makeargv(const char *arg
, int *argcp
, int sloppy
, char *lastquote
,
1140 static char argvs
[MAXARGLEN
];
1141 static char *argv
[MAXARGS
+ 1];
1142 enum { MA_START
, MA_SQUOTE
, MA_DQUOTE
, MA_UNQUOTED
} state
, q
;
1145 if (strlen(arg
) > sizeof(argvs
) - 1) {
1147 error("string too long");
1150 if (terminated
!= NULL
)
1152 if (lastquote
!= NULL
)
1157 if ((size_t)argc
>= sizeof(argv
) / sizeof(*argv
)){
1158 error("Too many arguments.");
1161 if (isspace((unsigned char)arg
[i
])) {
1162 if (state
== MA_UNQUOTED
) {
1163 /* Terminate current argument */
1167 } else if (state
!= MA_START
)
1168 argvs
[j
++] = arg
[i
];
1169 } else if (arg
[i
] == '"' || arg
[i
] == '\'') {
1170 q
= arg
[i
] == '"' ? MA_DQUOTE
: MA_SQUOTE
;
1171 if (state
== MA_START
) {
1172 argv
[argc
] = argvs
+ j
;
1174 if (lastquote
!= NULL
)
1175 *lastquote
= arg
[i
];
1176 } else if (state
== MA_UNQUOTED
)
1178 else if (state
== q
)
1179 state
= MA_UNQUOTED
;
1181 argvs
[j
++] = arg
[i
];
1182 } else if (arg
[i
] == '\\') {
1183 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1184 quot
= state
== MA_SQUOTE
? '\'' : '"';
1185 /* Unescape quote we are in */
1186 /* XXX support \n and friends? */
1187 if (arg
[i
+ 1] == quot
) {
1189 argvs
[j
++] = arg
[i
];
1190 } else if (arg
[i
+ 1] == '?' ||
1191 arg
[i
+ 1] == '[' || arg
[i
+ 1] == '*') {
1193 * Special case for sftp: append
1194 * double-escaped glob sequence -
1195 * glob will undo one level of
1196 * escaping. NB. string can grow here.
1198 if (j
>= sizeof(argvs
) - 5)
1199 goto args_too_longs
;
1201 argvs
[j
++] = arg
[i
++];
1203 argvs
[j
++] = arg
[i
];
1205 argvs
[j
++] = arg
[i
++];
1206 argvs
[j
++] = arg
[i
];
1209 if (state
== MA_START
) {
1210 argv
[argc
] = argvs
+ j
;
1211 state
= MA_UNQUOTED
;
1212 if (lastquote
!= NULL
)
1215 if (arg
[i
+ 1] == '?' || arg
[i
+ 1] == '[' ||
1216 arg
[i
+ 1] == '*' || arg
[i
+ 1] == '\\') {
1218 * Special case for sftp: append
1219 * escaped glob sequence -
1220 * glob will undo one level of
1223 argvs
[j
++] = arg
[i
++];
1224 argvs
[j
++] = arg
[i
];
1226 /* Unescape everything */
1227 /* XXX support \n and friends? */
1229 argvs
[j
++] = arg
[i
];
1232 } else if (arg
[i
] == '#') {
1233 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
)
1234 argvs
[j
++] = arg
[i
];
1237 } else if (arg
[i
] == '\0') {
1238 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1240 state
= MA_UNQUOTED
;
1241 if (terminated
!= NULL
)
1245 error("Unterminated quoted argument");
1249 if (state
== MA_UNQUOTED
) {
1255 if (state
== MA_START
) {
1256 argv
[argc
] = argvs
+ j
;
1257 state
= MA_UNQUOTED
;
1258 if (lastquote
!= NULL
)
1261 if ((state
== MA_SQUOTE
|| state
== MA_DQUOTE
) &&
1262 (arg
[i
] == '?' || arg
[i
] == '[' || arg
[i
] == '*')) {
1264 * Special case for sftp: escape quoted
1265 * glob(3) wildcards. NB. string can grow
1268 if (j
>= sizeof(argvs
) - 3)
1269 goto args_too_longs
;
1271 argvs
[j
++] = arg
[i
];
1273 argvs
[j
++] = arg
[i
];
1282 parse_args(const char **cpp
, int *ignore_errors
, int *aflag
,
1283 int *fflag
, int *hflag
, int *iflag
, int *lflag
, int *pflag
,
1284 int *rflag
, int *sflag
,
1285 unsigned long *n_arg
, char **path1
, char **path2
)
1287 const char *cmd
, *cp
= *cpp
;
1291 int i
, cmdnum
, optidx
, argc
;
1293 /* Skip leading whitespace */
1294 cp
= cp
+ strspn(cp
, WHITESPACE
);
1296 /* Check for leading '-' (disable error processing) */
1301 cp
= cp
+ strspn(cp
, WHITESPACE
);
1304 /* Ignore blank lines and lines which begin with comment '#' char */
1305 if (*cp
== '\0' || *cp
== '#')
1308 if ((argv
= makeargv(cp
, &argc
, 0, NULL
, NULL
)) == NULL
)
1311 /* Figure out which command we have */
1312 for (i
= 0; cmds
[i
].c
!= NULL
; i
++) {
1313 if (argv
[0] != NULL
&& strcasecmp(cmds
[i
].c
, argv
[0]) == 0)
1323 } else if (cmdnum
== -1) {
1324 error("Invalid command.");
1328 /* Get arguments and parse flags */
1329 *aflag
= *fflag
= *hflag
= *iflag
= *lflag
= *pflag
= 0;
1330 *rflag
= *sflag
= 0;
1331 *path1
= *path2
= NULL
;
1338 if ((optidx
= parse_getput_flags(cmd
, argv
, argc
,
1339 aflag
, fflag
, pflag
, rflag
)) == -1)
1341 /* Get first pathname (mandatory) */
1342 if (argc
- optidx
< 1) {
1343 error("You must specify at least one path after a "
1344 "%s command.", cmd
);
1347 *path1
= xstrdup(argv
[optidx
]);
1348 /* Get second pathname (optional) */
1349 if (argc
- optidx
> 1) {
1350 *path2
= xstrdup(argv
[optidx
+ 1]);
1351 /* Destination is not globbed */
1352 undo_glob_escape(*path2
);
1356 if ((optidx
= parse_link_flags(cmd
, argv
, argc
, sflag
)) == -1)
1358 goto parse_two_paths
;
1360 if ((optidx
= parse_rename_flags(cmd
, argv
, argc
, lflag
)) == -1)
1362 goto parse_two_paths
;
1364 if ((optidx
= parse_no_flags(cmd
, argv
, argc
)) == -1)
1367 if (argc
- optidx
< 2) {
1368 error("You must specify two paths after a %s "
1372 *path1
= xstrdup(argv
[optidx
]);
1373 *path2
= xstrdup(argv
[optidx
+ 1]);
1374 /* Paths are not globbed */
1375 undo_glob_escape(*path1
);
1376 undo_glob_escape(*path2
);
1384 if ((optidx
= parse_no_flags(cmd
, argv
, argc
)) == -1)
1386 /* Get pathname (mandatory) */
1387 if (argc
- optidx
< 1) {
1388 error("You must specify a path after a %s command.",
1392 *path1
= xstrdup(argv
[optidx
]);
1393 /* Only "rm" globs */
1395 undo_glob_escape(*path1
);
1398 if ((optidx
= parse_df_flags(cmd
, argv
, argc
, hflag
,
1401 /* Default to current directory if no path specified */
1402 if (argc
- optidx
< 1)
1405 *path1
= xstrdup(argv
[optidx
]);
1406 undo_glob_escape(*path1
);
1410 if ((optidx
= parse_ls_flags(argv
, argc
, lflag
)) == -1)
1412 /* Path is optional */
1413 if (argc
- optidx
> 0)
1414 *path1
= xstrdup(argv
[optidx
]);
1417 /* Skip ls command and following whitespace */
1418 cp
= cp
+ strlen(cmd
) + strspn(cp
, WHITESPACE
);
1420 /* Uses the rest of the line */
1427 if ((optidx
= parse_no_flags(cmd
, argv
, argc
)) == -1)
1429 /* Get numeric arg (mandatory) */
1430 if (argc
- optidx
< 1)
1433 l
= strtol(argv
[optidx
], &cp2
, base
);
1434 if (cp2
== argv
[optidx
] || *cp2
!= '\0' ||
1435 ((l
== LONG_MIN
|| l
== LONG_MAX
) && errno
== ERANGE
) ||
1438 error("You must supply a numeric argument "
1439 "to the %s command.", cmd
);
1443 if (cmdnum
== I_LUMASK
)
1445 /* Get pathname (mandatory) */
1446 if (argc
- optidx
< 2) {
1447 error("You must specify a path after a %s command.",
1451 *path1
= xstrdup(argv
[optidx
+ 1]);
1459 if ((optidx
= parse_no_flags(cmd
, argv
, argc
)) == -1)
1463 fatal("Command not implemented");
1471 parse_dispatch_command(struct sftp_conn
*conn
, const char *cmd
, char **pwd
,
1474 char *path1
, *path2
, *tmp
;
1475 int ignore_errors
= 0, aflag
= 0, fflag
= 0, hflag
= 0,
1477 int lflag
= 0, pflag
= 0, rflag
= 0, sflag
= 0;
1479 unsigned long n_arg
= 0;
1481 char path_buf
[PATH_MAX
];
1485 path1
= path2
= NULL
;
1486 cmdnum
= parse_args(&cmd
, &ignore_errors
, &aflag
, &fflag
, &hflag
,
1487 &iflag
, &lflag
, &pflag
, &rflag
, &sflag
, &n_arg
, &path1
, &path2
);
1488 if (ignore_errors
!= 0)
1491 memset(&g
, 0, sizeof(g
));
1493 /* Perform command */
1499 /* Unrecognized command */
1506 err
= process_get(conn
, path1
, path2
, *pwd
, pflag
,
1507 rflag
, aflag
, fflag
);
1513 err
= process_put(conn
, path1
, path2
, *pwd
, pflag
,
1514 rflag
, aflag
, fflag
);
1517 path1
= make_absolute(path1
, *pwd
);
1518 path2
= make_absolute(path2
, *pwd
);
1519 err
= do_rename(conn
, path1
, path2
, lflag
);
1525 path1
= make_absolute(path1
, *pwd
);
1526 path2
= make_absolute(path2
, *pwd
);
1527 err
= (sflag
? do_symlink
: do_hardlink
)(conn
, path1
, path2
);
1530 path1
= make_absolute(path1
, *pwd
);
1531 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1532 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1534 mprintf("Removing %s\n", g
.gl_pathv
[i
]);
1535 err
= do_rm(conn
, g
.gl_pathv
[i
]);
1536 if (err
!= 0 && err_abort
)
1541 path1
= make_absolute(path1
, *pwd
);
1543 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1545 err
= do_mkdir(conn
, path1
, &a
, 1);
1548 path1
= make_absolute(path1
, *pwd
);
1549 err
= do_rmdir(conn
, path1
);
1552 path1
= make_absolute(path1
, *pwd
);
1553 if ((tmp
= do_realpath(conn
, path1
)) == NULL
) {
1557 if ((aa
= do_stat(conn
, tmp
, 0)) == NULL
) {
1562 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
)) {
1563 error("Can't change directory: Can't check target");
1568 if (!S_ISDIR(aa
->perm
)) {
1569 error("Can't change directory: \"%s\" is not "
1570 "a directory", tmp
);
1580 do_ls_dir(conn
, *pwd
, *pwd
, lflag
);
1584 /* Strip pwd off beginning of non-absolute paths */
1589 path1
= make_absolute(path1
, *pwd
);
1590 err
= do_globbed_ls(conn
, path1
, tmp
, lflag
);
1593 /* Default to current directory if no path specified */
1595 path1
= xstrdup(*pwd
);
1596 path1
= make_absolute(path1
, *pwd
);
1597 err
= do_df(conn
, path1
, hflag
, iflag
);
1600 tmp
= tilde_expand_filename(path1
, getuid());
1603 if (chdir(path1
) == -1) {
1604 error("Couldn't change local directory to "
1605 "\"%s\": %s", path1
, strerror(errno
));
1610 if (mkdir(path1
, 0777) == -1) {
1611 error("Couldn't create local directory "
1612 "\"%s\": %s", path1
, strerror(errno
));
1620 local_do_shell(cmd
);
1624 printf("Local umask: %03lo\n", n_arg
);
1627 path1
= make_absolute(path1
, *pwd
);
1629 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1631 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1632 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1634 mprintf("Changing mode on %s\n",
1636 err
= do_setstat(conn
, g
.gl_pathv
[i
], &a
);
1637 if (err
!= 0 && err_abort
)
1643 path1
= make_absolute(path1
, *pwd
);
1644 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1645 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1646 if (!(aa
= do_stat(conn
, g
.gl_pathv
[i
], 0))) {
1653 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_UIDGID
)) {
1654 error("Can't get current ownership of "
1655 "remote file \"%s\"", g
.gl_pathv
[i
]);
1662 aa
->flags
&= SSH2_FILEXFER_ATTR_UIDGID
;
1663 if (cmdnum
== I_CHOWN
) {
1665 mprintf("Changing owner on %s\n",
1670 mprintf("Changing group on %s\n",
1674 err
= do_setstat(conn
, g
.gl_pathv
[i
], aa
);
1675 if (err
!= 0 && err_abort
)
1680 mprintf("Remote working directory: %s\n", *pwd
);
1683 if (!getcwd(path_buf
, sizeof(path_buf
))) {
1684 error("Couldn't get local cwd: %s", strerror(errno
));
1688 mprintf("Local working directory: %s\n", path_buf
);
1691 /* Processed below */
1697 printf("SFTP protocol version %u\n", sftp_proto_version(conn
));
1700 showprogress
= !showprogress
;
1702 printf("Progress meter enabled\n");
1704 printf("Progress meter disabled\n");
1707 fatal("%d is not implemented", cmdnum
);
1715 /* If an unignored error occurs in batch mode we should abort. */
1716 if (err_abort
&& err
!= 0)
1718 else if (cmdnum
== I_QUIT
)
1726 prompt(EditLine
*el
)
1731 /* Display entries in 'list' after skipping the first 'len' chars */
1733 complete_display(char **list
, u_int len
)
1735 u_int y
, m
= 0, width
= 80, columns
= 1, colspace
= 0, llen
;
1739 /* Count entries for sort and find longest */
1740 for (y
= 0; list
[y
]; y
++)
1741 m
= MAXIMUM(m
, strlen(list
[y
]));
1743 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
1746 m
= m
> len
? m
- len
: 0;
1747 columns
= width
/ (m
+ 2);
1748 columns
= MAXIMUM(columns
, 1);
1749 colspace
= width
/ columns
;
1750 colspace
= MINIMUM(colspace
, width
);
1754 for (y
= 0; list
[y
]; y
++) {
1755 llen
= strlen(list
[y
]);
1756 tmp
= llen
> len
? list
[y
] + len
: "";
1757 mprintf("%-*s", colspace
, tmp
);
1768 * Given a "list" of words that begin with a common prefix of "word",
1769 * attempt to find an autocompletion to extends "word" by the next
1770 * characters common to all entries in "list".
1773 complete_ambiguous(const char *word
, char **list
, size_t count
)
1779 u_int y
, matchlen
= strlen(list
[0]);
1781 /* Find length of common stem */
1782 for (y
= 1; list
[y
]; y
++) {
1785 for (x
= 0; x
< matchlen
; x
++)
1786 if (list
[0][x
] != list
[y
][x
])
1792 if (matchlen
> strlen(word
)) {
1793 char *tmp
= xstrdup(list
[0]);
1795 tmp
[matchlen
] = '\0';
1800 return xstrdup(word
);
1803 /* Autocomplete a sftp command */
1805 complete_cmd_parse(EditLine
*el
, char *cmd
, int lastarg
, char quote
,
1808 u_int y
, count
= 0, cmdlen
, tmplen
;
1809 char *tmp
, **list
, argterm
[3];
1812 list
= xcalloc((sizeof(cmds
) / sizeof(*cmds
)) + 1, sizeof(char *));
1814 /* No command specified: display all available commands */
1816 for (y
= 0; cmds
[y
].c
; y
++)
1817 list
[count
++] = xstrdup(cmds
[y
].c
);
1820 complete_display(list
, 0);
1822 for (y
= 0; list
[y
] != NULL
; y
++)
1828 /* Prepare subset of commands that start with "cmd" */
1829 cmdlen
= strlen(cmd
);
1830 for (y
= 0; cmds
[y
].c
; y
++) {
1831 if (!strncasecmp(cmd
, cmds
[y
].c
, cmdlen
))
1832 list
[count
++] = xstrdup(cmds
[y
].c
);
1841 /* Complete ambigious command */
1842 tmp
= complete_ambiguous(cmd
, list
, count
);
1844 complete_display(list
, 0);
1846 for (y
= 0; list
[y
]; y
++)
1851 tmplen
= strlen(tmp
);
1852 cmdlen
= strlen(cmd
);
1853 /* If cmd may be extended then do so */
1854 if (tmplen
> cmdlen
)
1855 if (el_insertstr(el
, tmp
+ cmdlen
) == -1)
1856 fatal("el_insertstr failed.");
1858 /* Terminate argument cleanly */
1862 argterm
[y
++] = quote
;
1863 if (lastarg
|| *(lf
->cursor
) != ' ')
1866 if (y
> 0 && el_insertstr(el
, argterm
) == -1)
1867 fatal("el_insertstr failed.");
1876 * Determine whether a particular sftp command's arguments (if any)
1877 * represent local or remote files.
1880 complete_is_remote(char *cmd
) {
1886 for (i
= 0; cmds
[i
].c
; i
++) {
1887 if (!strncasecmp(cmd
, cmds
[i
].c
, strlen(cmds
[i
].c
)))
1894 /* Autocomplete a filename "file" */
1896 complete_match(EditLine
*el
, struct sftp_conn
*conn
, char *remote_path
,
1897 char *file
, int remote
, int lastarg
, char quote
, int terminated
)
1900 char *tmp
, *tmp2
, ins
[8];
1901 u_int i
, hadglob
, pwdlen
, len
, tmplen
, filelen
, cesc
, isesc
, isabs
;
1905 /* Glob from "file" location */
1909 xasprintf(&tmp
, "%s*", file
);
1911 /* Check if the path is absolute. */
1912 isabs
= tmp
[0] == '/';
1914 memset(&g
, 0, sizeof(g
));
1915 if (remote
!= LOCAL
) {
1916 tmp
= make_absolute(tmp
, remote_path
);
1917 remote_glob(conn
, tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1919 glob(tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1921 /* Determine length of pwd so we can trim completion display */
1922 for (hadglob
= tmplen
= pwdlen
= 0; tmp
[tmplen
] != 0; tmplen
++) {
1923 /* Terminate counting on first unescaped glob metacharacter */
1924 if (tmp
[tmplen
] == '*' || tmp
[tmplen
] == '?') {
1925 if (tmp
[tmplen
] != '*' || tmp
[tmplen
+ 1] != '\0')
1929 if (tmp
[tmplen
] == '\\' && tmp
[tmplen
+ 1] != '\0')
1931 if (tmp
[tmplen
] == '/')
1932 pwdlen
= tmplen
+ 1; /* track last seen '/' */
1937 if (g
.gl_matchc
== 0)
1940 if (g
.gl_matchc
> 1)
1941 complete_display(g
.gl_pathv
, pwdlen
);
1943 /* Don't try to extend globs */
1944 if (file
== NULL
|| hadglob
)
1947 tmp2
= complete_ambiguous(file
, g
.gl_pathv
, g
.gl_matchc
);
1948 tmp
= path_strip(tmp2
, isabs
? NULL
: remote_path
);
1954 tmplen
= strlen(tmp
);
1955 filelen
= strlen(file
);
1957 /* Count the number of escaped characters in the input string. */
1959 for (i
= 0; i
< filelen
; i
++) {
1960 if (!isesc
&& file
[i
] == '\\' && i
+ 1 < filelen
){
1967 if (tmplen
> (filelen
- cesc
)) {
1968 tmp2
= tmp
+ filelen
- cesc
;
1970 /* quote argument on way out */
1971 for (i
= 0; i
< len
; i
+= clen
) {
1972 if ((clen
= mblen(tmp2
+ i
, len
- i
)) < 0 ||
1973 (size_t)clen
> sizeof(ins
) - 2)
1974 fatal("invalid multibyte character");
1976 memcpy(ins
+ 1, tmp2
+ i
, clen
);
1977 ins
[clen
+ 1] = '\0';
1987 if (quote
== '\0' || tmp2
[i
] == quote
) {
1988 if (el_insertstr(el
, ins
) == -1)
1989 fatal("el_insertstr "
1995 if (el_insertstr(el
, ins
+ 1) == -1)
1996 fatal("el_insertstr failed.");
2003 if (g
.gl_matchc
== 1) {
2005 if (!terminated
&& quote
!= '\0')
2007 if (*(lf
->cursor
- 1) != '/' &&
2008 (lastarg
|| *(lf
->cursor
) != ' '))
2011 if (i
> 0 && el_insertstr(el
, ins
) == -1)
2012 fatal("el_insertstr failed.");
2021 /* tab-completion hook function, called via libedit */
2022 static unsigned char
2023 complete(EditLine
*el
, int ch
)
2025 char **argv
, *line
, quote
;
2027 u_int cursor
, len
, terminated
, ret
= CC_ERROR
;
2029 struct complete_ctx
*complete_ctx
;
2032 if (el_get(el
, EL_CLIENTDATA
, (void**)&complete_ctx
) != 0)
2033 fatal("%s: el_get failed", __func__
);
2035 /* Figure out which argument the cursor points to */
2036 cursor
= lf
->cursor
- lf
->buffer
;
2037 line
= xmalloc(cursor
+ 1);
2038 memcpy(line
, lf
->buffer
, cursor
);
2039 line
[cursor
] = '\0';
2040 argv
= makeargv(line
, &carg
, 1, "e
, &terminated
);
2043 /* Get all the arguments on the line */
2044 len
= lf
->lastchar
- lf
->buffer
;
2045 line
= xmalloc(len
+ 1);
2046 memcpy(line
, lf
->buffer
, len
);
2048 argv
= makeargv(line
, &argc
, 1, NULL
, NULL
);
2050 /* Ensure cursor is at EOL or a argument boundary */
2051 if (line
[cursor
] != ' ' && line
[cursor
] != '\0' &&
2052 line
[cursor
] != '\n') {
2058 /* Show all available commands */
2059 complete_cmd_parse(el
, NULL
, argc
== carg
, '\0', 1);
2061 } else if (carg
== 1 && cursor
> 0 && line
[cursor
- 1] != ' ') {
2062 /* Handle the command parsing */
2063 if (complete_cmd_parse(el
, argv
[0], argc
== carg
,
2064 quote
, terminated
) != 0)
2066 } else if (carg
>= 1) {
2067 /* Handle file parsing */
2068 int remote
= complete_is_remote(argv
[0]);
2069 char *filematch
= NULL
;
2071 if (carg
> 1 && line
[cursor
-1] != ' ')
2072 filematch
= argv
[carg
- 1];
2075 complete_match(el
, complete_ctx
->conn
,
2076 *complete_ctx
->remote_pathp
, filematch
,
2077 remote
, carg
== argc
, quote
, terminated
) != 0)
2084 #endif /* USE_LIBEDIT */
2087 interactive_loop(struct sftp_conn
*conn
, char *file1
, char *file2
)
2092 int err
, interactive
;
2093 EditLine
*el
= NULL
;
2097 extern char *__progname
;
2098 struct complete_ctx complete_ctx
;
2100 if (!batchmode
&& isatty(STDIN_FILENO
)) {
2101 if ((el
= el_init(__progname
, stdin
, stdout
, stderr
)) == NULL
)
2102 fatal("Couldn't initialise editline");
2103 if ((hl
= history_init()) == NULL
)
2104 fatal("Couldn't initialise editline history");
2105 history(hl
, &hev
, H_SETSIZE
, 100);
2106 el_set(el
, EL_HIST
, history
, hl
);
2108 el_set(el
, EL_PROMPT
, prompt
);
2109 el_set(el
, EL_EDITOR
, "emacs");
2110 el_set(el
, EL_TERMINAL
, NULL
);
2111 el_set(el
, EL_SIGNAL
, 1);
2112 el_source(el
, NULL
);
2114 /* Tab Completion */
2115 el_set(el
, EL_ADDFN
, "ftp-complete",
2116 "Context sensitive argument completion", complete
);
2117 complete_ctx
.conn
= conn
;
2118 complete_ctx
.remote_pathp
= &remote_path
;
2119 el_set(el
, EL_CLIENTDATA
, (void*)&complete_ctx
);
2120 el_set(el
, EL_BIND
, "^I", "ftp-complete", NULL
);
2121 /* enable ctrl-left-arrow and ctrl-right-arrow */
2122 el_set(el
, EL_BIND
, "\\e[1;5C", "em-next-word", NULL
);
2123 el_set(el
, EL_BIND
, "\\e[5C", "em-next-word", NULL
);
2124 el_set(el
, EL_BIND
, "\\e[1;5D", "ed-prev-word", NULL
);
2125 el_set(el
, EL_BIND
, "\\e\\e[D", "ed-prev-word", NULL
);
2126 /* make ^w match ksh behaviour */
2127 el_set(el
, EL_BIND
, "^w", "ed-delete-prev-word", NULL
);
2129 #endif /* USE_LIBEDIT */
2131 remote_path
= do_realpath(conn
, ".");
2132 if (remote_path
== NULL
)
2135 if (file1
!= NULL
) {
2136 dir
= xstrdup(file1
);
2137 dir
= make_absolute(dir
, remote_path
);
2139 if (remote_is_dir(conn
, dir
) && file2
== NULL
) {
2141 mprintf("Changing to: %s\n", dir
);
2142 snprintf(cmd
, sizeof cmd
, "cd \"%s\"", dir
);
2143 if (parse_dispatch_command(conn
, cmd
,
2144 &remote_path
, 1) != 0) {
2151 /* XXX this is wrong wrt quoting */
2152 snprintf(cmd
, sizeof cmd
, "get%s %s%s%s",
2153 global_aflag
? " -a" : "", dir
,
2154 file2
== NULL
? "" : " ",
2155 file2
== NULL
? "" : file2
);
2156 err
= parse_dispatch_command(conn
, cmd
,
2166 setvbuf(stdout
, NULL
, _IOLBF
, 0);
2167 setvbuf(infile
, NULL
, _IOLBF
, 0);
2169 interactive
= !batchmode
&& isatty(STDIN_FILENO
);
2174 signal(SIGINT
, SIG_IGN
);
2179 if (fgets(cmd
, sizeof(cmd
), infile
) == NULL
) {
2184 if (!interactive
) { /* Echo command */
2185 mprintf("sftp> %s", cmd
);
2186 if (strlen(cmd
) > 0 &&
2187 cmd
[strlen(cmd
) - 1] != '\n')
2195 if ((line
= el_gets(el
, &count
)) == NULL
||
2200 history(hl
, &hev
, H_ENTER
, line
);
2201 if (strlcpy(cmd
, line
, sizeof(cmd
)) >= sizeof(cmd
)) {
2202 fprintf(stderr
, "Error: input line too long\n");
2205 #endif /* USE_LIBEDIT */
2208 cp
= strrchr(cmd
, '\n');
2212 /* Handle user interrupts gracefully during commands */
2214 signal(SIGINT
, cmd_interrupt
);
2216 err
= parse_dispatch_command(conn
, cmd
, &remote_path
,
2227 #endif /* USE_LIBEDIT */
2229 /* err == 1 signifies normal "quit" exit */
2230 return (err
>= 0 ? 0 : -1);
2234 connect_to_server(char *path
, char **args
, int *in
, int *out
)
2239 int pin
[2], pout
[2];
2241 if ((pipe(pin
) == -1) || (pipe(pout
) == -1))
2242 fatal("pipe: %s", strerror(errno
));
2247 #else /* USE_PIPES */
2250 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) == -1)
2251 fatal("socketpair: %s", strerror(errno
));
2252 *in
= *out
= inout
[0];
2253 c_in
= c_out
= inout
[1];
2254 #endif /* USE_PIPES */
2256 if ((sshpid
= fork()) == -1)
2257 fatal("fork: %s", strerror(errno
));
2258 else if (sshpid
== 0) {
2259 if ((dup2(c_in
, STDIN_FILENO
) == -1) ||
2260 (dup2(c_out
, STDOUT_FILENO
) == -1)) {
2261 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
2270 * The underlying ssh is in the same process group, so we must
2271 * ignore SIGINT if we want to gracefully abort commands,
2272 * otherwise the signal will make it to the ssh process and
2273 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2274 * underlying ssh, it must *not* ignore that signal.
2276 signal(SIGINT
, SIG_IGN
);
2277 signal(SIGTERM
, SIG_DFL
);
2279 fprintf(stderr
, "exec: %s: %s\n", path
, strerror(errno
));
2283 signal(SIGTERM
, killchild
);
2284 signal(SIGINT
, killchild
);
2285 signal(SIGHUP
, killchild
);
2286 signal(SIGTSTP
, suspchild
);
2287 signal(SIGTTIN
, suspchild
);
2288 signal(SIGTTOU
, suspchild
);
2296 extern char *__progname
;
2299 "usage: %s [-46aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2300 " [-D sftp_server_path] [-F ssh_config] "
2301 "[-i identity_file] [-l limit]\n"
2302 " [-o ssh_option] [-P port] [-R num_requests] "
2304 " [-s subsystem | sftp_server] host\n"
2305 " %s [user@]host[:file ...]\n"
2306 " %s [user@]host[:dir[/]]\n"
2307 " %s -b batchfile [user@]host\n",
2308 __progname
, __progname
, __progname
, __progname
);
2313 main(int argc
, char **argv
)
2315 int in
, out
, ch
, err
;
2316 char *host
= NULL
, *userhost
, *cp
, *file2
= NULL
;
2317 int debug_level
= 0, sshver
= 2;
2318 char *file1
= NULL
, *sftp_server
= NULL
;
2319 char *ssh_program
= _PATH_SSH_PROGRAM
, *sftp_direct
= NULL
;
2321 LogLevel ll
= SYSLOG_LEVEL_INFO
;
2324 extern char *optarg
;
2325 struct sftp_conn
*conn
;
2326 size_t copy_buffer_len
= DEFAULT_COPY_BUFLEN
;
2327 size_t num_requests
= DEFAULT_NUM_REQUESTS
;
2328 long long limit_kbps
= 0;
2330 ssh_malloc_init(); /* must be called before any mallocs */
2331 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2335 __progname
= ssh_get_progname(argv
[0]);
2336 memset(&args
, '\0', sizeof(args
));
2338 addargs(&args
, "%s", ssh_program
);
2339 addargs(&args
, "-oForwardX11 no");
2340 addargs(&args
, "-oForwardAgent no");
2341 addargs(&args
, "-oPermitLocalCommand no");
2342 addargs(&args
, "-oClearAllForwardings yes");
2344 ll
= SYSLOG_LEVEL_INFO
;
2347 while ((ch
= getopt(argc
, argv
,
2348 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2350 /* Passed through to ssh(1) */
2354 addargs(&args
, "-%c", ch
);
2356 /* Passed through to ssh(1) with argument */
2361 addargs(&args
, "-%c", ch
);
2362 addargs(&args
, "%s", optarg
);
2365 ll
= SYSLOG_LEVEL_ERROR
;
2368 addargs(&args
, "-%c", ch
);
2371 addargs(&args
, "-oPort %s", optarg
);
2374 if (debug_level
< 3) {
2375 addargs(&args
, "-v");
2376 ll
= SYSLOG_LEVEL_DEBUG1
+ debug_level
;
2382 if (sftp_server
== NULL
)
2383 sftp_server
= _PATH_SFTP_SERVER
;
2392 copy_buffer_len
= strtol(optarg
, &cp
, 10);
2393 if (copy_buffer_len
== 0 || *cp
!= '\0')
2394 fatal("Invalid buffer size \"%s\"", optarg
);
2398 fatal("Batch file already specified.");
2400 /* Allow "-" as stdin */
2401 if (strcmp(optarg
, "-") != 0 &&
2402 (infile
= fopen(optarg
, "r")) == NULL
)
2403 fatal("%s (%s).", strerror(errno
), optarg
);
2405 quiet
= batchmode
= 1;
2406 addargs(&args
, "-obatchmode yes");
2415 sftp_direct
= optarg
;
2418 limit_kbps
= strtonum(optarg
, 1, 100 * 1024 * 1024,
2422 limit_kbps
*= 1024; /* kbps */
2428 num_requests
= strtol(optarg
, &cp
, 10);
2429 if (num_requests
== 0 || *cp
!= '\0')
2430 fatal("Invalid number of requests \"%s\"",
2434 sftp_server
= optarg
;
2437 ssh_program
= optarg
;
2438 replacearg(&args
, 0, "%s", ssh_program
);
2446 if (!isatty(STDERR_FILENO
))
2449 log_init(argv
[0], ll
, SYSLOG_FACILITY_USER
, 1);
2451 if (sftp_direct
== NULL
) {
2452 if (optind
== argc
|| argc
> (optind
+ 2))
2455 userhost
= xstrdup(argv
[optind
]);
2456 file2
= argv
[optind
+1];
2458 if ((host
= strrchr(userhost
, '@')) == NULL
)
2463 fprintf(stderr
, "Missing username\n");
2466 addargs(&args
, "-l");
2467 addargs(&args
, "%s", userhost
);
2470 if ((cp
= colon(host
)) != NULL
) {
2475 host
= cleanhostname(host
);
2477 fprintf(stderr
, "Missing hostname\n");
2481 addargs(&args
, "-oProtocol %d", sshver
);
2483 /* no subsystem if the server-spec contains a '/' */
2484 if (sftp_server
== NULL
|| strchr(sftp_server
, '/') == NULL
)
2485 addargs(&args
, "-s");
2487 addargs(&args
, "--");
2488 addargs(&args
, "%s", host
);
2489 addargs(&args
, "%s", (sftp_server
!= NULL
?
2490 sftp_server
: "sftp"));
2492 connect_to_server(ssh_program
, args
.list
, &in
, &out
);
2495 addargs(&args
, "sftp-server");
2497 connect_to_server(sftp_direct
, args
.list
, &in
, &out
);
2501 conn
= do_init(in
, out
, copy_buffer_len
, num_requests
, limit_kbps
);
2503 fatal("Couldn't initialise connection to server");
2506 if (sftp_direct
== NULL
)
2507 fprintf(stderr
, "Connected to %s.\n", host
);
2509 fprintf(stderr
, "Attached to %s.\n", sftp_direct
);
2512 err
= interactive_loop(conn
, file1
, file2
);
2514 #if !defined(USE_PIPES)
2515 shutdown(in
, SHUT_RDWR
);
2516 shutdown(out
, SHUT_RDWR
);
2524 while (waitpid(sshpid
, NULL
, 0) == -1)
2526 fatal("Couldn't wait for ssh process: %s",
2529 exit(err
== 0 ? 0 : 1);