1 /* $OpenBSD: sftp.c,v 1.175 2016/07/22 03:47:36 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/param.h> /* MIN MAX */
21 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #ifdef HAVE_SYS_STAT_H
24 # include <sys/stat.h>
26 #include <sys/param.h>
27 #include <sys/socket.h>
29 #ifdef HAVE_SYS_STATVFS_H
30 #include <sys/statvfs.h>
48 typedef void EditLine
;
65 #include "pathnames.h"
72 #include "sftp-common.h"
73 #include "sftp-client.h"
75 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
76 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
78 /* File to read commands from */
81 /* Are we in batchfile mode? */
84 /* PID of ssh transport process */
85 static pid_t sshpid
= -1;
87 /* Suppress diagnositic messages */
90 /* This is set to 0 if the progressmeter is not desired. */
93 /* When this option is set, we always recursively download/upload directories */
96 /* When this option is set, we resume download or upload if possible */
99 /* When this option is set, the file transfers will always preserve times */
100 int global_pflag
= 0;
102 /* When this option is set, transfers will have fsync() called on each file */
103 int global_fflag
= 0;
105 /* SIGINT received during command processing */
106 volatile sig_atomic_t interrupted
= 0;
108 /* 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);
236 cmd_interrupt(int signo
)
238 const char msg
[] = "\rInterrupt \n";
239 int olderrno
= errno
;
241 (void)write(STDERR_FILENO
, msg
, sizeof(msg
) - 1);
249 printf("Available commands:\n"
251 "cd path Change remote directory to 'path'\n"
252 "chgrp grp path Change group of file 'path' to 'grp'\n"
253 "chmod mode path Change permissions of file 'path' to 'mode'\n"
254 "chown own path Change owner of file 'path' to 'own'\n"
255 "df [-hi] [path] Display statistics for current directory or\n"
256 " filesystem containing 'path'\n"
258 "get [-afPpRr] remote [local] Download file\n"
259 "reget [-fPpRr] remote [local] Resume download file\n"
260 "reput [-fPpRr] [local] remote Resume upload file\n"
261 "help Display this help text\n"
262 "lcd path Change local directory to 'path'\n"
263 "lls [ls-options [path]] Display local directory listing\n"
264 "lmkdir path Create local directory\n"
265 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
266 "lpwd Print local working directory\n"
267 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
268 "lumask umask Set local umask to 'umask'\n"
269 "mkdir path Create remote directory\n"
270 "progress Toggle display of progress meter\n"
271 "put [-afPpRr] local [remote] Upload file\n"
272 "pwd Display remote working directory\n"
274 "rename oldpath newpath Rename remote file\n"
275 "rm path Delete remote file\n"
276 "rmdir path Remove remote directory\n"
277 "symlink oldpath newpath Symlink remote file\n"
278 "version Show SFTP version\n"
279 "!command Execute 'command' in local shell\n"
280 "! Escape to local shell\n"
281 "? Synonym for help\n");
285 local_do_shell(const char *args
)
294 if ((shell
= getenv("SHELL")) == NULL
|| *shell
== '\0')
295 shell
= _PATH_BSHELL
;
297 if ((pid
= fork()) == -1)
298 fatal("Couldn't fork: %s", strerror(errno
));
301 /* XXX: child has pipe fds to ssh subproc open - issue? */
303 debug3("Executing %s -c \"%s\"", shell
, args
);
304 execl(shell
, shell
, "-c", args
, (char *)NULL
);
306 debug3("Executing %s", shell
);
307 execl(shell
, shell
, (char *)NULL
);
309 fprintf(stderr
, "Couldn't execute \"%s\": %s\n", shell
,
313 while (waitpid(pid
, &status
, 0) == -1)
315 fatal("Couldn't wait for child: %s", strerror(errno
));
316 if (!WIFEXITED(status
))
317 error("Shell exited abnormally");
318 else if (WEXITSTATUS(status
))
319 error("Shell exited with status %d", WEXITSTATUS(status
));
323 local_do_ls(const char *args
)
326 local_do_shell(_PATH_LS
);
328 int len
= strlen(_PATH_LS
" ") + strlen(args
) + 1;
329 char *buf
= xmalloc(len
);
331 /* XXX: quoting - rip quoting code from ftp? */
332 snprintf(buf
, len
, _PATH_LS
" %s", args
);
338 /* Strip one path (usually the pwd) from the start of another */
340 path_strip(const char *path
, const char *strip
)
345 return (xstrdup(path
));
348 if (strncmp(path
, strip
, len
) == 0) {
349 if (strip
[len
- 1] != '/' && path
[len
] == '/')
351 return (xstrdup(path
+ len
));
354 return (xstrdup(path
));
358 make_absolute(char *p
, const char *pwd
)
363 if (p
&& p
[0] != '/') {
364 abs_str
= path_append(pwd
, p
);
372 parse_getput_flags(const char *cmd
, char **argv
, int argc
,
373 int *aflag
, int *fflag
, int *pflag
, int *rflag
)
375 extern int opterr
, optind
, optopt
, optreset
;
378 optind
= optreset
= 1;
381 *aflag
= *fflag
= *rflag
= *pflag
= 0;
382 while ((ch
= getopt(argc
, argv
, "afPpRr")) != -1) {
399 error("%s: Invalid flag -%c", cmd
, optopt
);
408 parse_link_flags(const char *cmd
, char **argv
, int argc
, int *sflag
)
410 extern int opterr
, optind
, optopt
, optreset
;
413 optind
= optreset
= 1;
417 while ((ch
= getopt(argc
, argv
, "s")) != -1) {
423 error("%s: Invalid flag -%c", cmd
, optopt
);
432 parse_rename_flags(const char *cmd
, char **argv
, int argc
, int *lflag
)
434 extern int opterr
, optind
, optopt
, optreset
;
437 optind
= optreset
= 1;
441 while ((ch
= getopt(argc
, argv
, "l")) != -1) {
447 error("%s: Invalid flag -%c", cmd
, optopt
);
456 parse_ls_flags(char **argv
, int argc
, int *lflag
)
458 extern int opterr
, optind
, optopt
, optreset
;
461 optind
= optreset
= 1;
464 *lflag
= LS_NAME_SORT
;
465 while ((ch
= getopt(argc
, argv
, "1Safhlnrt")) != -1) {
468 *lflag
&= ~VIEW_FLAGS
;
469 *lflag
|= LS_SHORT_VIEW
;
472 *lflag
&= ~SORT_FLAGS
;
473 *lflag
|= LS_SIZE_SORT
;
476 *lflag
|= LS_SHOW_ALL
;
479 *lflag
&= ~SORT_FLAGS
;
482 *lflag
|= LS_SI_UNITS
;
485 *lflag
&= ~LS_SHORT_VIEW
;
486 *lflag
|= LS_LONG_VIEW
;
489 *lflag
&= ~LS_SHORT_VIEW
;
490 *lflag
|= LS_NUMERIC_VIEW
|LS_LONG_VIEW
;
493 *lflag
|= LS_REVERSE_SORT
;
496 *lflag
&= ~SORT_FLAGS
;
497 *lflag
|= LS_TIME_SORT
;
500 error("ls: Invalid flag -%c", optopt
);
509 parse_df_flags(const char *cmd
, char **argv
, int argc
, int *hflag
, int *iflag
)
511 extern int opterr
, optind
, optopt
, optreset
;
514 optind
= optreset
= 1;
518 while ((ch
= getopt(argc
, argv
, "hi")) != -1) {
527 error("%s: Invalid flag -%c", cmd
, optopt
);
536 parse_no_flags(const char *cmd
, char **argv
, int argc
)
538 extern int opterr
, optind
, optopt
, optreset
;
541 optind
= optreset
= 1;
544 while ((ch
= getopt(argc
, argv
, "")) != -1) {
547 error("%s: Invalid flag -%c", cmd
, optopt
);
556 is_dir(const char *path
)
560 /* XXX: report errors? */
561 if (stat(path
, &sb
) == -1)
564 return(S_ISDIR(sb
.st_mode
));
568 remote_is_dir(struct sftp_conn
*conn
, const char *path
)
572 /* XXX: report errors? */
573 if ((a
= do_stat(conn
, path
, 1)) == NULL
)
575 if (!(a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
))
577 return(S_ISDIR(a
->perm
));
580 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
582 pathname_is_dir(const char *pathname
)
584 size_t l
= strlen(pathname
);
586 return l
> 0 && pathname
[l
- 1] == '/';
590 process_get(struct sftp_conn
*conn
, const char *src
, const char *dst
,
591 const char *pwd
, int pflag
, int rflag
, int resume
, int fflag
)
593 char *abs_src
= NULL
;
594 char *abs_dst
= NULL
;
596 char *filename
, *tmp
=NULL
;
599 abs_src
= xstrdup(src
);
600 abs_src
= make_absolute(abs_src
, pwd
);
601 memset(&g
, 0, sizeof(g
));
603 debug3("Looking up %s", abs_src
);
604 if ((r
= remote_glob(conn
, abs_src
, GLOB_MARK
, NULL
, &g
)) != 0) {
605 if (r
== GLOB_NOSPACE
) {
606 error("Too many matches for \"%s\".", abs_src
);
608 error("File \"%s\" not found.", abs_src
);
615 * If multiple matches then dst must be a directory or
618 if (g
.gl_matchc
> 1 && dst
!= NULL
&& !is_dir(dst
)) {
619 error("Multiple source paths, but destination "
620 "\"%s\" is not a directory", dst
);
625 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
626 tmp
= xstrdup(g
.gl_pathv
[i
]);
627 if ((filename
= basename(tmp
)) == NULL
) {
628 error("basename %s: %s", tmp
, strerror(errno
));
634 if (g
.gl_matchc
== 1 && dst
) {
636 abs_dst
= path_append(dst
, filename
);
638 abs_dst
= xstrdup(dst
);
641 abs_dst
= path_append(dst
, filename
);
643 abs_dst
= xstrdup(filename
);
647 resume
|= global_aflag
;
648 if (!quiet
&& resume
)
649 mprintf("Resuming %s to %s\n",
650 g
.gl_pathv
[i
], abs_dst
);
651 else if (!quiet
&& !resume
)
652 mprintf("Fetching %s to %s\n",
653 g
.gl_pathv
[i
], abs_dst
);
654 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
655 if (download_dir(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
656 pflag
|| global_pflag
, 1, resume
,
657 fflag
|| global_fflag
) == -1)
660 if (do_download(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
661 pflag
|| global_pflag
, resume
,
662 fflag
|| global_fflag
) == -1)
676 process_put(struct sftp_conn
*conn
, const char *src
, const char *dst
,
677 const char *pwd
, int pflag
, int rflag
, int resume
, int fflag
)
679 char *tmp_dst
= NULL
;
680 char *abs_dst
= NULL
;
681 char *tmp
= NULL
, *filename
= NULL
;
684 int i
, dst_is_dir
= 1;
688 tmp_dst
= xstrdup(dst
);
689 tmp_dst
= make_absolute(tmp_dst
, pwd
);
692 memset(&g
, 0, sizeof(g
));
693 debug3("Looking up %s", src
);
694 if (glob(src
, GLOB_NOCHECK
| GLOB_MARK
, NULL
, &g
)) {
695 error("File \"%s\" not found.", src
);
700 /* If we aren't fetching to pwd then stash this status for later */
702 dst_is_dir
= remote_is_dir(conn
, tmp_dst
);
704 /* If multiple matches, dst may be directory or unspecified */
705 if (g
.gl_matchc
> 1 && tmp_dst
&& !dst_is_dir
) {
706 error("Multiple paths match, but destination "
707 "\"%s\" is not a directory", tmp_dst
);
712 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
713 if (stat(g
.gl_pathv
[i
], &sb
) == -1) {
715 error("stat %s: %s", g
.gl_pathv
[i
], strerror(errno
));
719 tmp
= xstrdup(g
.gl_pathv
[i
]);
720 if ((filename
= basename(tmp
)) == NULL
) {
721 error("basename %s: %s", tmp
, strerror(errno
));
727 if (g
.gl_matchc
== 1 && tmp_dst
) {
728 /* If directory specified, append filename */
730 abs_dst
= path_append(tmp_dst
, filename
);
732 abs_dst
= xstrdup(tmp_dst
);
733 } else if (tmp_dst
) {
734 abs_dst
= path_append(tmp_dst
, filename
);
736 abs_dst
= make_absolute(xstrdup(filename
), pwd
);
740 resume
|= global_aflag
;
741 if (!quiet
&& resume
)
742 mprintf("Resuming upload of %s to %s\n",
743 g
.gl_pathv
[i
], abs_dst
);
744 else if (!quiet
&& !resume
)
745 mprintf("Uploading %s to %s\n",
746 g
.gl_pathv
[i
], abs_dst
);
747 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
748 if (upload_dir(conn
, g
.gl_pathv
[i
], abs_dst
,
749 pflag
|| global_pflag
, 1, resume
,
750 fflag
|| global_fflag
) == -1)
753 if (do_upload(conn
, g
.gl_pathv
[i
], abs_dst
,
754 pflag
|| global_pflag
, resume
,
755 fflag
|| global_fflag
) == -1)
768 sdirent_comp(const void *aa
, const void *bb
)
770 SFTP_DIRENT
*a
= *(SFTP_DIRENT
**)aa
;
771 SFTP_DIRENT
*b
= *(SFTP_DIRENT
**)bb
;
772 int rmul
= sort_flag
& LS_REVERSE_SORT
? -1 : 1;
774 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
775 if (sort_flag
& LS_NAME_SORT
)
776 return (rmul
* strcmp(a
->filename
, b
->filename
));
777 else if (sort_flag
& LS_TIME_SORT
)
778 return (rmul
* NCMP(a
->a
.mtime
, b
->a
.mtime
));
779 else if (sort_flag
& LS_SIZE_SORT
)
780 return (rmul
* NCMP(a
->a
.size
, b
->a
.size
));
782 fatal("Unknown ls sort type");
785 /* sftp ls.1 replacement for directories */
787 do_ls_dir(struct sftp_conn
*conn
, const char *path
,
788 const char *strip_path
, int lflag
)
791 u_int c
= 1, colspace
= 0, columns
= 1;
794 if ((n
= do_readdir(conn
, path
, &d
)) != 0)
797 if (!(lflag
& LS_SHORT_VIEW
)) {
798 u_int m
= 0, width
= 80;
802 /* Count entries for sort and find longest filename */
803 for (n
= 0; d
[n
] != NULL
; n
++) {
804 if (d
[n
]->filename
[0] != '.' || (lflag
& LS_SHOW_ALL
))
805 m
= MAX(m
, strlen(d
[n
]->filename
));
808 /* Add any subpath that also needs to be counted */
809 tmp
= path_strip(path
, strip_path
);
813 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
816 columns
= width
/ (m
+ 2);
817 columns
= MAX(columns
, 1);
818 colspace
= width
/ columns
;
819 colspace
= MIN(colspace
, width
);
822 if (lflag
& SORT_FLAGS
) {
823 for (n
= 0; d
[n
] != NULL
; n
++)
824 ; /* count entries */
825 sort_flag
= lflag
& (SORT_FLAGS
|LS_REVERSE_SORT
);
826 qsort(d
, n
, sizeof(*d
), sdirent_comp
);
829 for (n
= 0; d
[n
] != NULL
&& !interrupted
; n
++) {
832 if (d
[n
]->filename
[0] == '.' && !(lflag
& LS_SHOW_ALL
))
835 tmp
= path_append(path
, d
[n
]->filename
);
836 fname
= path_strip(tmp
, strip_path
);
839 if (lflag
& LS_LONG_VIEW
) {
840 if (lflag
& (LS_NUMERIC_VIEW
|LS_SI_UNITS
)) {
844 memset(&sb
, 0, sizeof(sb
));
845 attrib_to_stat(&d
[n
]->a
, &sb
);
846 lname
= ls_file(fname
, &sb
, 1,
847 (lflag
& LS_SI_UNITS
));
848 mprintf("%s\n", lname
);
851 mprintf("%s\n", d
[n
]->longname
);
853 mprintf("%-*s", colspace
, fname
);
864 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
867 free_sftp_dirents(d
);
871 /* sftp ls.1 replacement which handles path globs */
873 do_globbed_ls(struct sftp_conn
*conn
, const char *path
,
874 const char *strip_path
, int lflag
)
880 u_int i
, c
= 1, colspace
= 0, columns
= 1, m
= 0, width
= 80;
882 memset(&g
, 0, sizeof(g
));
884 if ((r
= remote_glob(conn
, path
,
885 GLOB_MARK
|GLOB_NOCHECK
|GLOB_BRACE
|GLOB_KEEPSTAT
|GLOB_NOSORT
,
887 (g
.gl_pathc
&& !g
.gl_matchc
)) {
890 if (r
== GLOB_NOSPACE
) {
891 error("Can't ls: Too many matches for \"%s\"", path
);
893 error("Can't ls: \"%s\" not found", path
);
902 * If the glob returns a single match and it is a directory,
903 * then just list its contents.
905 if (g
.gl_matchc
== 1 && g
.gl_statv
[0] != NULL
&&
906 S_ISDIR(g
.gl_statv
[0]->st_mode
)) {
907 err
= do_ls_dir(conn
, g
.gl_pathv
[0], strip_path
, lflag
);
912 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
915 if (!(lflag
& LS_SHORT_VIEW
)) {
916 /* Count entries for sort and find longest filename */
917 for (i
= 0; g
.gl_pathv
[i
]; i
++)
918 m
= MAX(m
, strlen(g
.gl_pathv
[i
]));
920 columns
= width
/ (m
+ 2);
921 columns
= MAX(columns
, 1);
922 colspace
= width
/ columns
;
925 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
926 fname
= path_strip(g
.gl_pathv
[i
], strip_path
);
927 if (lflag
& LS_LONG_VIEW
) {
928 if (g
.gl_statv
[i
] == NULL
) {
929 error("no stat information for %s", fname
);
932 lname
= ls_file(fname
, g
.gl_statv
[i
], 1,
933 (lflag
& LS_SI_UNITS
));
934 mprintf("%s\n", lname
);
937 mprintf("%-*s", colspace
, fname
);
947 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
958 do_df(struct sftp_conn
*conn
, const char *path
, int hflag
, int iflag
)
960 struct sftp_statvfs st
;
961 char s_used
[FMT_SCALED_STRSIZE
];
962 char s_avail
[FMT_SCALED_STRSIZE
];
963 char s_root
[FMT_SCALED_STRSIZE
];
964 char s_total
[FMT_SCALED_STRSIZE
];
965 unsigned long long ffree
;
967 if (do_statvfs(conn
, path
, &st
, 1) == -1)
970 ffree
= st
.f_files
? (100 * (st
.f_files
- st
.f_ffree
) / st
.f_files
) : 0;
971 printf(" Inodes Used Avail "
972 "(root) %%Capacity\n");
973 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
974 (unsigned long long)st
.f_files
,
975 (unsigned long long)(st
.f_files
- st
.f_ffree
),
976 (unsigned long long)st
.f_favail
,
977 (unsigned long long)st
.f_ffree
, ffree
);
979 strlcpy(s_used
, "error", sizeof(s_used
));
980 strlcpy(s_avail
, "error", sizeof(s_avail
));
981 strlcpy(s_root
, "error", sizeof(s_root
));
982 strlcpy(s_total
, "error", sizeof(s_total
));
983 fmt_scaled((st
.f_blocks
- st
.f_bfree
) * st
.f_frsize
, s_used
);
984 fmt_scaled(st
.f_bavail
* st
.f_frsize
, s_avail
);
985 fmt_scaled(st
.f_bfree
* st
.f_frsize
, s_root
);
986 fmt_scaled(st
.f_blocks
* st
.f_frsize
, s_total
);
987 printf(" Size Used Avail (root) %%Capacity\n");
988 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
989 s_total
, s_used
, s_avail
, s_root
,
990 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
993 printf(" Size Used Avail "
994 "(root) %%Capacity\n");
995 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
996 (unsigned long long)(st
.f_frsize
* st
.f_blocks
/ 1024),
997 (unsigned long long)(st
.f_frsize
*
998 (st
.f_blocks
- st
.f_bfree
) / 1024),
999 (unsigned long long)(st
.f_frsize
* st
.f_bavail
/ 1024),
1000 (unsigned long long)(st
.f_frsize
* st
.f_bfree
/ 1024),
1001 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
1008 * Undo escaping of glob sequences in place. Used to undo extra escaping
1009 * applied in makeargv() when the string is destined for a function that
1013 undo_glob_escape(char *s
)
1048 * Split a string into an argument vector using sh(1)-style quoting,
1049 * comment and escaping rules, but with some tweaks to handle glob(3)
1051 * The "sloppy" flag allows for recovery from missing terminating quote, for
1052 * use in parsing incomplete commandlines during tab autocompletion.
1054 * Returns NULL on error or a NULL-terminated array of arguments.
1056 * If "lastquote" is not NULL, the quoting character used for the last
1057 * argument is placed in *lastquote ("\0", "'" or "\"").
1059 * If "terminated" is not NULL, *terminated will be set to 1 when the
1060 * last argument's quote has been properly terminated or 0 otherwise.
1061 * This parameter is only of use if "sloppy" is set.
1064 #define MAXARGLEN 8192
1066 makeargv(const char *arg
, int *argcp
, int sloppy
, char *lastquote
,
1071 static char argvs
[MAXARGLEN
];
1072 static char *argv
[MAXARGS
+ 1];
1073 enum { MA_START
, MA_SQUOTE
, MA_DQUOTE
, MA_UNQUOTED
} state
, q
;
1076 if (strlen(arg
) > sizeof(argvs
) - 1) {
1078 error("string too long");
1081 if (terminated
!= NULL
)
1083 if (lastquote
!= NULL
)
1088 if ((size_t)argc
>= sizeof(argv
) / sizeof(*argv
)){
1089 error("Too many arguments.");
1092 if (isspace((unsigned char)arg
[i
])) {
1093 if (state
== MA_UNQUOTED
) {
1094 /* Terminate current argument */
1098 } else if (state
!= MA_START
)
1099 argvs
[j
++] = arg
[i
];
1100 } else if (arg
[i
] == '"' || arg
[i
] == '\'') {
1101 q
= arg
[i
] == '"' ? MA_DQUOTE
: MA_SQUOTE
;
1102 if (state
== MA_START
) {
1103 argv
[argc
] = argvs
+ j
;
1105 if (lastquote
!= NULL
)
1106 *lastquote
= arg
[i
];
1107 } else if (state
== MA_UNQUOTED
)
1109 else if (state
== q
)
1110 state
= MA_UNQUOTED
;
1112 argvs
[j
++] = arg
[i
];
1113 } else if (arg
[i
] == '\\') {
1114 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1115 quot
= state
== MA_SQUOTE
? '\'' : '"';
1116 /* Unescape quote we are in */
1117 /* XXX support \n and friends? */
1118 if (arg
[i
+ 1] == quot
) {
1120 argvs
[j
++] = arg
[i
];
1121 } else if (arg
[i
+ 1] == '?' ||
1122 arg
[i
+ 1] == '[' || arg
[i
+ 1] == '*') {
1124 * Special case for sftp: append
1125 * double-escaped glob sequence -
1126 * glob will undo one level of
1127 * escaping. NB. string can grow here.
1129 if (j
>= sizeof(argvs
) - 5)
1130 goto args_too_longs
;
1132 argvs
[j
++] = arg
[i
++];
1134 argvs
[j
++] = arg
[i
];
1136 argvs
[j
++] = arg
[i
++];
1137 argvs
[j
++] = arg
[i
];
1140 if (state
== MA_START
) {
1141 argv
[argc
] = argvs
+ j
;
1142 state
= MA_UNQUOTED
;
1143 if (lastquote
!= NULL
)
1146 if (arg
[i
+ 1] == '?' || arg
[i
+ 1] == '[' ||
1147 arg
[i
+ 1] == '*' || arg
[i
+ 1] == '\\') {
1149 * Special case for sftp: append
1150 * escaped glob sequence -
1151 * glob will undo one level of
1154 argvs
[j
++] = arg
[i
++];
1155 argvs
[j
++] = arg
[i
];
1157 /* Unescape everything */
1158 /* XXX support \n and friends? */
1160 argvs
[j
++] = arg
[i
];
1163 } else if (arg
[i
] == '#') {
1164 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
)
1165 argvs
[j
++] = arg
[i
];
1168 } else if (arg
[i
] == '\0') {
1169 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1171 state
= MA_UNQUOTED
;
1172 if (terminated
!= NULL
)
1176 error("Unterminated quoted argument");
1180 if (state
== MA_UNQUOTED
) {
1186 if (state
== MA_START
) {
1187 argv
[argc
] = argvs
+ j
;
1188 state
= MA_UNQUOTED
;
1189 if (lastquote
!= NULL
)
1192 if ((state
== MA_SQUOTE
|| state
== MA_DQUOTE
) &&
1193 (arg
[i
] == '?' || arg
[i
] == '[' || arg
[i
] == '*')) {
1195 * Special case for sftp: escape quoted
1196 * glob(3) wildcards. NB. string can grow
1199 if (j
>= sizeof(argvs
) - 3)
1200 goto args_too_longs
;
1202 argvs
[j
++] = arg
[i
];
1204 argvs
[j
++] = arg
[i
];
1213 parse_args(const char **cpp
, int *ignore_errors
, int *aflag
,
1214 int *fflag
, int *hflag
, int *iflag
, int *lflag
, int *pflag
,
1215 int *rflag
, int *sflag
,
1216 unsigned long *n_arg
, char **path1
, char **path2
)
1218 const char *cmd
, *cp
= *cpp
;
1222 int i
, cmdnum
, optidx
, argc
;
1224 /* Skip leading whitespace */
1225 cp
= cp
+ strspn(cp
, WHITESPACE
);
1227 /* Check for leading '-' (disable error processing) */
1232 cp
= cp
+ strspn(cp
, WHITESPACE
);
1235 /* Ignore blank lines and lines which begin with comment '#' char */
1236 if (*cp
== '\0' || *cp
== '#')
1239 if ((argv
= makeargv(cp
, &argc
, 0, NULL
, NULL
)) == NULL
)
1242 /* Figure out which command we have */
1243 for (i
= 0; cmds
[i
].c
!= NULL
; i
++) {
1244 if (argv
[0] != NULL
&& strcasecmp(cmds
[i
].c
, argv
[0]) == 0)
1254 } else if (cmdnum
== -1) {
1255 error("Invalid command.");
1259 /* Get arguments and parse flags */
1260 *aflag
= *fflag
= *hflag
= *iflag
= *lflag
= *pflag
= 0;
1261 *rflag
= *sflag
= 0;
1262 *path1
= *path2
= NULL
;
1269 if ((optidx
= parse_getput_flags(cmd
, argv
, argc
,
1270 aflag
, fflag
, pflag
, rflag
)) == -1)
1272 /* Get first pathname (mandatory) */
1273 if (argc
- optidx
< 1) {
1274 error("You must specify at least one path after a "
1275 "%s command.", cmd
);
1278 *path1
= xstrdup(argv
[optidx
]);
1279 /* Get second pathname (optional) */
1280 if (argc
- optidx
> 1) {
1281 *path2
= xstrdup(argv
[optidx
+ 1]);
1282 /* Destination is not globbed */
1283 undo_glob_escape(*path2
);
1287 if ((optidx
= parse_link_flags(cmd
, argv
, argc
, sflag
)) == -1)
1289 goto parse_two_paths
;
1291 if ((optidx
= parse_rename_flags(cmd
, argv
, argc
, lflag
)) == -1)
1293 goto parse_two_paths
;
1295 if ((optidx
= parse_no_flags(cmd
, argv
, argc
)) == -1)
1298 if (argc
- optidx
< 2) {
1299 error("You must specify two paths after a %s "
1303 *path1
= xstrdup(argv
[optidx
]);
1304 *path2
= xstrdup(argv
[optidx
+ 1]);
1305 /* Paths are not globbed */
1306 undo_glob_escape(*path1
);
1307 undo_glob_escape(*path2
);
1315 if ((optidx
= parse_no_flags(cmd
, argv
, argc
)) == -1)
1317 /* Get pathname (mandatory) */
1318 if (argc
- optidx
< 1) {
1319 error("You must specify a path after a %s command.",
1323 *path1
= xstrdup(argv
[optidx
]);
1324 /* Only "rm" globs */
1326 undo_glob_escape(*path1
);
1329 if ((optidx
= parse_df_flags(cmd
, argv
, argc
, hflag
,
1332 /* Default to current directory if no path specified */
1333 if (argc
- optidx
< 1)
1336 *path1
= xstrdup(argv
[optidx
]);
1337 undo_glob_escape(*path1
);
1341 if ((optidx
= parse_ls_flags(argv
, argc
, lflag
)) == -1)
1343 /* Path is optional */
1344 if (argc
- optidx
> 0)
1345 *path1
= xstrdup(argv
[optidx
]);
1348 /* Skip ls command and following whitespace */
1349 cp
= cp
+ strlen(cmd
) + strspn(cp
, WHITESPACE
);
1351 /* Uses the rest of the line */
1358 if ((optidx
= parse_no_flags(cmd
, argv
, argc
)) == -1)
1360 /* Get numeric arg (mandatory) */
1361 if (argc
- optidx
< 1)
1364 l
= strtol(argv
[optidx
], &cp2
, base
);
1365 if (cp2
== argv
[optidx
] || *cp2
!= '\0' ||
1366 ((l
== LONG_MIN
|| l
== LONG_MAX
) && errno
== ERANGE
) ||
1369 error("You must supply a numeric argument "
1370 "to the %s command.", cmd
);
1374 if (cmdnum
== I_LUMASK
)
1376 /* Get pathname (mandatory) */
1377 if (argc
- optidx
< 2) {
1378 error("You must specify a path after a %s command.",
1382 *path1
= xstrdup(argv
[optidx
+ 1]);
1390 if ((optidx
= parse_no_flags(cmd
, argv
, argc
)) == -1)
1394 fatal("Command not implemented");
1402 parse_dispatch_command(struct sftp_conn
*conn
, const char *cmd
, char **pwd
,
1405 char *path1
, *path2
, *tmp
;
1406 int ignore_errors
= 0, aflag
= 0, fflag
= 0, hflag
= 0,
1408 int lflag
= 0, pflag
= 0, rflag
= 0, sflag
= 0;
1410 unsigned long n_arg
= 0;
1412 char path_buf
[PATH_MAX
];
1416 path1
= path2
= NULL
;
1417 cmdnum
= parse_args(&cmd
, &ignore_errors
, &aflag
, &fflag
, &hflag
,
1418 &iflag
, &lflag
, &pflag
, &rflag
, &sflag
, &n_arg
, &path1
, &path2
);
1419 if (ignore_errors
!= 0)
1422 memset(&g
, 0, sizeof(g
));
1424 /* Perform command */
1430 /* Unrecognized command */
1437 err
= process_get(conn
, path1
, path2
, *pwd
, pflag
,
1438 rflag
, aflag
, fflag
);
1444 err
= process_put(conn
, path1
, path2
, *pwd
, pflag
,
1445 rflag
, aflag
, fflag
);
1448 path1
= make_absolute(path1
, *pwd
);
1449 path2
= make_absolute(path2
, *pwd
);
1450 err
= do_rename(conn
, path1
, path2
, lflag
);
1456 path1
= make_absolute(path1
, *pwd
);
1457 path2
= make_absolute(path2
, *pwd
);
1458 err
= (sflag
? do_symlink
: do_hardlink
)(conn
, path1
, path2
);
1461 path1
= make_absolute(path1
, *pwd
);
1462 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1463 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1465 mprintf("Removing %s\n", g
.gl_pathv
[i
]);
1466 err
= do_rm(conn
, g
.gl_pathv
[i
]);
1467 if (err
!= 0 && err_abort
)
1472 path1
= make_absolute(path1
, *pwd
);
1474 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1476 err
= do_mkdir(conn
, path1
, &a
, 1);
1479 path1
= make_absolute(path1
, *pwd
);
1480 err
= do_rmdir(conn
, path1
);
1483 path1
= make_absolute(path1
, *pwd
);
1484 if ((tmp
= do_realpath(conn
, path1
)) == NULL
) {
1488 if ((aa
= do_stat(conn
, tmp
, 0)) == NULL
) {
1493 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
)) {
1494 error("Can't change directory: Can't check target");
1499 if (!S_ISDIR(aa
->perm
)) {
1500 error("Can't change directory: \"%s\" is not "
1501 "a directory", tmp
);
1511 do_ls_dir(conn
, *pwd
, *pwd
, lflag
);
1515 /* Strip pwd off beginning of non-absolute paths */
1520 path1
= make_absolute(path1
, *pwd
);
1521 err
= do_globbed_ls(conn
, path1
, tmp
, lflag
);
1524 /* Default to current directory if no path specified */
1526 path1
= xstrdup(*pwd
);
1527 path1
= make_absolute(path1
, *pwd
);
1528 err
= do_df(conn
, path1
, hflag
, iflag
);
1531 tmp
= tilde_expand_filename(path1
, getuid());
1534 if (chdir(path1
) == -1) {
1535 error("Couldn't change local directory to "
1536 "\"%s\": %s", path1
, strerror(errno
));
1541 if (mkdir(path1
, 0777) == -1) {
1542 error("Couldn't create local directory "
1543 "\"%s\": %s", path1
, strerror(errno
));
1551 local_do_shell(cmd
);
1555 printf("Local umask: %03lo\n", n_arg
);
1558 path1
= make_absolute(path1
, *pwd
);
1560 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1562 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1563 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1565 mprintf("Changing mode on %s\n",
1567 err
= do_setstat(conn
, g
.gl_pathv
[i
], &a
);
1568 if (err
!= 0 && err_abort
)
1574 path1
= make_absolute(path1
, *pwd
);
1575 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1576 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1577 if (!(aa
= do_stat(conn
, g
.gl_pathv
[i
], 0))) {
1584 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_UIDGID
)) {
1585 error("Can't get current ownership of "
1586 "remote file \"%s\"", g
.gl_pathv
[i
]);
1593 aa
->flags
&= SSH2_FILEXFER_ATTR_UIDGID
;
1594 if (cmdnum
== I_CHOWN
) {
1596 mprintf("Changing owner on %s\n",
1601 mprintf("Changing group on %s\n",
1605 err
= do_setstat(conn
, g
.gl_pathv
[i
], aa
);
1606 if (err
!= 0 && err_abort
)
1611 mprintf("Remote working directory: %s\n", *pwd
);
1614 if (!getcwd(path_buf
, sizeof(path_buf
))) {
1615 error("Couldn't get local cwd: %s", strerror(errno
));
1619 mprintf("Local working directory: %s\n", path_buf
);
1622 /* Processed below */
1628 printf("SFTP protocol version %u\n", sftp_proto_version(conn
));
1631 showprogress
= !showprogress
;
1633 printf("Progress meter enabled\n");
1635 printf("Progress meter disabled\n");
1638 fatal("%d is not implemented", cmdnum
);
1646 /* If an unignored error occurs in batch mode we should abort. */
1647 if (err_abort
&& err
!= 0)
1649 else if (cmdnum
== I_QUIT
)
1657 prompt(EditLine
*el
)
1662 /* Display entries in 'list' after skipping the first 'len' chars */
1664 complete_display(char **list
, u_int len
)
1666 u_int y
, m
= 0, width
= 80, columns
= 1, colspace
= 0, llen
;
1670 /* Count entries for sort and find longest */
1671 for (y
= 0; list
[y
]; y
++)
1672 m
= MAX(m
, strlen(list
[y
]));
1674 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
1677 m
= m
> len
? m
- len
: 0;
1678 columns
= width
/ (m
+ 2);
1679 columns
= MAX(columns
, 1);
1680 colspace
= width
/ columns
;
1681 colspace
= MIN(colspace
, width
);
1685 for (y
= 0; list
[y
]; y
++) {
1686 llen
= strlen(list
[y
]);
1687 tmp
= llen
> len
? list
[y
] + len
: "";
1688 mprintf("%-*s", colspace
, tmp
);
1699 * Given a "list" of words that begin with a common prefix of "word",
1700 * attempt to find an autocompletion to extends "word" by the next
1701 * characters common to all entries in "list".
1704 complete_ambiguous(const char *word
, char **list
, size_t count
)
1710 u_int y
, matchlen
= strlen(list
[0]);
1712 /* Find length of common stem */
1713 for (y
= 1; list
[y
]; y
++) {
1716 for (x
= 0; x
< matchlen
; x
++)
1717 if (list
[0][x
] != list
[y
][x
])
1723 if (matchlen
> strlen(word
)) {
1724 char *tmp
= xstrdup(list
[0]);
1726 tmp
[matchlen
] = '\0';
1731 return xstrdup(word
);
1734 /* Autocomplete a sftp command */
1736 complete_cmd_parse(EditLine
*el
, char *cmd
, int lastarg
, char quote
,
1739 u_int y
, count
= 0, cmdlen
, tmplen
;
1740 char *tmp
, **list
, argterm
[3];
1743 list
= xcalloc((sizeof(cmds
) / sizeof(*cmds
)) + 1, sizeof(char *));
1745 /* No command specified: display all available commands */
1747 for (y
= 0; cmds
[y
].c
; y
++)
1748 list
[count
++] = xstrdup(cmds
[y
].c
);
1751 complete_display(list
, 0);
1753 for (y
= 0; list
[y
] != NULL
; y
++)
1759 /* Prepare subset of commands that start with "cmd" */
1760 cmdlen
= strlen(cmd
);
1761 for (y
= 0; cmds
[y
].c
; y
++) {
1762 if (!strncasecmp(cmd
, cmds
[y
].c
, cmdlen
))
1763 list
[count
++] = xstrdup(cmds
[y
].c
);
1772 /* Complete ambigious command */
1773 tmp
= complete_ambiguous(cmd
, list
, count
);
1775 complete_display(list
, 0);
1777 for (y
= 0; list
[y
]; y
++)
1782 tmplen
= strlen(tmp
);
1783 cmdlen
= strlen(cmd
);
1784 /* If cmd may be extended then do so */
1785 if (tmplen
> cmdlen
)
1786 if (el_insertstr(el
, tmp
+ cmdlen
) == -1)
1787 fatal("el_insertstr failed.");
1789 /* Terminate argument cleanly */
1793 argterm
[y
++] = quote
;
1794 if (lastarg
|| *(lf
->cursor
) != ' ')
1797 if (y
> 0 && el_insertstr(el
, argterm
) == -1)
1798 fatal("el_insertstr failed.");
1807 * Determine whether a particular sftp command's arguments (if any)
1808 * represent local or remote files.
1811 complete_is_remote(char *cmd
) {
1817 for (i
= 0; cmds
[i
].c
; i
++) {
1818 if (!strncasecmp(cmd
, cmds
[i
].c
, strlen(cmds
[i
].c
)))
1825 /* Autocomplete a filename "file" */
1827 complete_match(EditLine
*el
, struct sftp_conn
*conn
, char *remote_path
,
1828 char *file
, int remote
, int lastarg
, char quote
, int terminated
)
1831 char *tmp
, *tmp2
, ins
[8];
1832 u_int i
, hadglob
, pwdlen
, len
, tmplen
, filelen
, cesc
, isesc
, isabs
;
1836 /* Glob from "file" location */
1840 xasprintf(&tmp
, "%s*", file
);
1842 /* Check if the path is absolute. */
1843 isabs
= tmp
[0] == '/';
1845 memset(&g
, 0, sizeof(g
));
1846 if (remote
!= LOCAL
) {
1847 tmp
= make_absolute(tmp
, remote_path
);
1848 remote_glob(conn
, tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1850 glob(tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1852 /* Determine length of pwd so we can trim completion display */
1853 for (hadglob
= tmplen
= pwdlen
= 0; tmp
[tmplen
] != 0; tmplen
++) {
1854 /* Terminate counting on first unescaped glob metacharacter */
1855 if (tmp
[tmplen
] == '*' || tmp
[tmplen
] == '?') {
1856 if (tmp
[tmplen
] != '*' || tmp
[tmplen
+ 1] != '\0')
1860 if (tmp
[tmplen
] == '\\' && tmp
[tmplen
+ 1] != '\0')
1862 if (tmp
[tmplen
] == '/')
1863 pwdlen
= tmplen
+ 1; /* track last seen '/' */
1868 if (g
.gl_matchc
== 0)
1871 if (g
.gl_matchc
> 1)
1872 complete_display(g
.gl_pathv
, pwdlen
);
1874 /* Don't try to extend globs */
1875 if (file
== NULL
|| hadglob
)
1878 tmp2
= complete_ambiguous(file
, g
.gl_pathv
, g
.gl_matchc
);
1879 tmp
= path_strip(tmp2
, isabs
? NULL
: remote_path
);
1885 tmplen
= strlen(tmp
);
1886 filelen
= strlen(file
);
1888 /* Count the number of escaped characters in the input string. */
1890 for (i
= 0; i
< filelen
; i
++) {
1891 if (!isesc
&& file
[i
] == '\\' && i
+ 1 < filelen
){
1898 if (tmplen
> (filelen
- cesc
)) {
1899 tmp2
= tmp
+ filelen
- cesc
;
1901 /* quote argument on way out */
1902 for (i
= 0; i
< len
; i
+= clen
) {
1903 if ((clen
= mblen(tmp2
+ i
, len
- i
)) < 0 ||
1904 (size_t)clen
> sizeof(ins
) - 2)
1905 fatal("invalid multibyte character");
1907 memcpy(ins
+ 1, tmp2
+ i
, clen
);
1908 ins
[clen
+ 1] = '\0';
1918 if (quote
== '\0' || tmp2
[i
] == quote
) {
1919 if (el_insertstr(el
, ins
) == -1)
1920 fatal("el_insertstr "
1926 if (el_insertstr(el
, ins
+ 1) == -1)
1927 fatal("el_insertstr failed.");
1934 if (g
.gl_matchc
== 1) {
1936 if (!terminated
&& quote
!= '\0')
1938 if (*(lf
->cursor
- 1) != '/' &&
1939 (lastarg
|| *(lf
->cursor
) != ' '))
1942 if (i
> 0 && el_insertstr(el
, ins
) == -1)
1943 fatal("el_insertstr failed.");
1952 /* tab-completion hook function, called via libedit */
1953 static unsigned char
1954 complete(EditLine
*el
, int ch
)
1956 char **argv
, *line
, quote
;
1958 u_int cursor
, len
, terminated
, ret
= CC_ERROR
;
1960 struct complete_ctx
*complete_ctx
;
1963 if (el_get(el
, EL_CLIENTDATA
, (void**)&complete_ctx
) != 0)
1964 fatal("%s: el_get failed", __func__
);
1966 /* Figure out which argument the cursor points to */
1967 cursor
= lf
->cursor
- lf
->buffer
;
1968 line
= xmalloc(cursor
+ 1);
1969 memcpy(line
, lf
->buffer
, cursor
);
1970 line
[cursor
] = '\0';
1971 argv
= makeargv(line
, &carg
, 1, "e
, &terminated
);
1974 /* Get all the arguments on the line */
1975 len
= lf
->lastchar
- lf
->buffer
;
1976 line
= xmalloc(len
+ 1);
1977 memcpy(line
, lf
->buffer
, len
);
1979 argv
= makeargv(line
, &argc
, 1, NULL
, NULL
);
1981 /* Ensure cursor is at EOL or a argument boundary */
1982 if (line
[cursor
] != ' ' && line
[cursor
] != '\0' &&
1983 line
[cursor
] != '\n') {
1989 /* Show all available commands */
1990 complete_cmd_parse(el
, NULL
, argc
== carg
, '\0', 1);
1992 } else if (carg
== 1 && cursor
> 0 && line
[cursor
- 1] != ' ') {
1993 /* Handle the command parsing */
1994 if (complete_cmd_parse(el
, argv
[0], argc
== carg
,
1995 quote
, terminated
) != 0)
1997 } else if (carg
>= 1) {
1998 /* Handle file parsing */
1999 int remote
= complete_is_remote(argv
[0]);
2000 char *filematch
= NULL
;
2002 if (carg
> 1 && line
[cursor
-1] != ' ')
2003 filematch
= argv
[carg
- 1];
2006 complete_match(el
, complete_ctx
->conn
,
2007 *complete_ctx
->remote_pathp
, filematch
,
2008 remote
, carg
== argc
, quote
, terminated
) != 0)
2015 #endif /* USE_LIBEDIT */
2018 interactive_loop(struct sftp_conn
*conn
, char *file1
, char *file2
)
2023 int err
, interactive
;
2024 EditLine
*el
= NULL
;
2028 extern char *__progname
;
2029 struct complete_ctx complete_ctx
;
2031 if (!batchmode
&& isatty(STDIN_FILENO
)) {
2032 if ((el
= el_init(__progname
, stdin
, stdout
, stderr
)) == NULL
)
2033 fatal("Couldn't initialise editline");
2034 if ((hl
= history_init()) == NULL
)
2035 fatal("Couldn't initialise editline history");
2036 history(hl
, &hev
, H_SETSIZE
, 100);
2037 el_set(el
, EL_HIST
, history
, hl
);
2039 el_set(el
, EL_PROMPT
, prompt
);
2040 el_set(el
, EL_EDITOR
, "emacs");
2041 el_set(el
, EL_TERMINAL
, NULL
);
2042 el_set(el
, EL_SIGNAL
, 1);
2043 el_source(el
, NULL
);
2045 /* Tab Completion */
2046 el_set(el
, EL_ADDFN
, "ftp-complete",
2047 "Context sensitive argument completion", complete
);
2048 complete_ctx
.conn
= conn
;
2049 complete_ctx
.remote_pathp
= &remote_path
;
2050 el_set(el
, EL_CLIENTDATA
, (void*)&complete_ctx
);
2051 el_set(el
, EL_BIND
, "^I", "ftp-complete", NULL
);
2052 /* enable ctrl-left-arrow and ctrl-right-arrow */
2053 el_set(el
, EL_BIND
, "\\e[1;5C", "em-next-word", NULL
);
2054 el_set(el
, EL_BIND
, "\\e[5C", "em-next-word", NULL
);
2055 el_set(el
, EL_BIND
, "\\e[1;5D", "ed-prev-word", NULL
);
2056 el_set(el
, EL_BIND
, "\\e\\e[D", "ed-prev-word", NULL
);
2057 /* make ^w match ksh behaviour */
2058 el_set(el
, EL_BIND
, "^w", "ed-delete-prev-word", NULL
);
2060 #endif /* USE_LIBEDIT */
2062 remote_path
= do_realpath(conn
, ".");
2063 if (remote_path
== NULL
)
2066 if (file1
!= NULL
) {
2067 dir
= xstrdup(file1
);
2068 dir
= make_absolute(dir
, remote_path
);
2070 if (remote_is_dir(conn
, dir
) && file2
== NULL
) {
2072 mprintf("Changing to: %s\n", dir
);
2073 snprintf(cmd
, sizeof cmd
, "cd \"%s\"", dir
);
2074 if (parse_dispatch_command(conn
, cmd
,
2075 &remote_path
, 1) != 0) {
2082 /* XXX this is wrong wrt quoting */
2083 snprintf(cmd
, sizeof cmd
, "get%s %s%s%s",
2084 global_aflag
? " -a" : "", dir
,
2085 file2
== NULL
? "" : " ",
2086 file2
== NULL
? "" : file2
);
2087 err
= parse_dispatch_command(conn
, cmd
,
2097 setvbuf(stdout
, NULL
, _IOLBF
, 0);
2098 setvbuf(infile
, NULL
, _IOLBF
, 0);
2100 interactive
= !batchmode
&& isatty(STDIN_FILENO
);
2105 signal(SIGINT
, SIG_IGN
);
2110 if (fgets(cmd
, sizeof(cmd
), infile
) == NULL
) {
2115 if (!interactive
) { /* Echo command */
2116 mprintf("sftp> %s", cmd
);
2117 if (strlen(cmd
) > 0 &&
2118 cmd
[strlen(cmd
) - 1] != '\n')
2126 if ((line
= el_gets(el
, &count
)) == NULL
||
2131 history(hl
, &hev
, H_ENTER
, line
);
2132 if (strlcpy(cmd
, line
, sizeof(cmd
)) >= sizeof(cmd
)) {
2133 fprintf(stderr
, "Error: input line too long\n");
2136 #endif /* USE_LIBEDIT */
2139 cp
= strrchr(cmd
, '\n');
2143 /* Handle user interrupts gracefully during commands */
2145 signal(SIGINT
, cmd_interrupt
);
2147 err
= parse_dispatch_command(conn
, cmd
, &remote_path
,
2158 #endif /* USE_LIBEDIT */
2160 /* err == 1 signifies normal "quit" exit */
2161 return (err
>= 0 ? 0 : -1);
2165 connect_to_server(char *path
, char **args
, int *in
, int *out
)
2170 int pin
[2], pout
[2];
2172 if ((pipe(pin
) == -1) || (pipe(pout
) == -1))
2173 fatal("pipe: %s", strerror(errno
));
2178 #else /* USE_PIPES */
2181 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) == -1)
2182 fatal("socketpair: %s", strerror(errno
));
2183 *in
= *out
= inout
[0];
2184 c_in
= c_out
= inout
[1];
2185 #endif /* USE_PIPES */
2187 if ((sshpid
= fork()) == -1)
2188 fatal("fork: %s", strerror(errno
));
2189 else if (sshpid
== 0) {
2190 if ((dup2(c_in
, STDIN_FILENO
) == -1) ||
2191 (dup2(c_out
, STDOUT_FILENO
) == -1)) {
2192 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
2201 * The underlying ssh is in the same process group, so we must
2202 * ignore SIGINT if we want to gracefully abort commands,
2203 * otherwise the signal will make it to the ssh process and
2204 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2205 * underlying ssh, it must *not* ignore that signal.
2207 signal(SIGINT
, SIG_IGN
);
2208 signal(SIGTERM
, SIG_DFL
);
2210 fprintf(stderr
, "exec: %s: %s\n", path
, strerror(errno
));
2214 signal(SIGTERM
, killchild
);
2215 signal(SIGINT
, killchild
);
2216 signal(SIGHUP
, killchild
);
2224 extern char *__progname
;
2227 "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2228 " [-D sftp_server_path] [-F ssh_config] "
2229 "[-i identity_file] [-l limit]\n"
2230 " [-o ssh_option] [-P port] [-R num_requests] "
2232 " [-s subsystem | sftp_server] host\n"
2233 " %s [user@]host[:file ...]\n"
2234 " %s [user@]host[:dir[/]]\n"
2235 " %s -b batchfile [user@]host\n",
2236 __progname
, __progname
, __progname
, __progname
);
2241 main(int argc
, char **argv
)
2243 int in
, out
, ch
, err
;
2244 char *host
= NULL
, *userhost
, *cp
, *file2
= NULL
;
2245 int debug_level
= 0, sshver
= 2;
2246 char *file1
= NULL
, *sftp_server
= NULL
;
2247 char *ssh_program
= _PATH_SSH_PROGRAM
, *sftp_direct
= NULL
;
2249 LogLevel ll
= SYSLOG_LEVEL_INFO
;
2252 extern char *optarg
;
2253 struct sftp_conn
*conn
;
2254 size_t copy_buffer_len
= DEFAULT_COPY_BUFLEN
;
2255 size_t num_requests
= DEFAULT_NUM_REQUESTS
;
2256 long long limit_kbps
= 0;
2258 ssh_malloc_init(); /* must be called before any mallocs */
2259 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2261 setlocale(LC_CTYPE
, "");
2263 __progname
= ssh_get_progname(argv
[0]);
2264 memset(&args
, '\0', sizeof(args
));
2266 addargs(&args
, "%s", ssh_program
);
2267 addargs(&args
, "-oForwardX11 no");
2268 addargs(&args
, "-oForwardAgent no");
2269 addargs(&args
, "-oPermitLocalCommand no");
2270 addargs(&args
, "-oClearAllForwardings yes");
2272 ll
= SYSLOG_LEVEL_INFO
;
2275 while ((ch
= getopt(argc
, argv
,
2276 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2278 /* Passed through to ssh(1) */
2282 addargs(&args
, "-%c", ch
);
2284 /* Passed through to ssh(1) with argument */
2289 addargs(&args
, "-%c", ch
);
2290 addargs(&args
, "%s", optarg
);
2293 ll
= SYSLOG_LEVEL_ERROR
;
2296 addargs(&args
, "-%c", ch
);
2299 addargs(&args
, "-oPort %s", optarg
);
2302 if (debug_level
< 3) {
2303 addargs(&args
, "-v");
2304 ll
= SYSLOG_LEVEL_DEBUG1
+ debug_level
;
2310 if (sftp_server
== NULL
)
2311 sftp_server
= _PATH_SFTP_SERVER
;
2320 copy_buffer_len
= strtol(optarg
, &cp
, 10);
2321 if (copy_buffer_len
== 0 || *cp
!= '\0')
2322 fatal("Invalid buffer size \"%s\"", optarg
);
2326 fatal("Batch file already specified.");
2328 /* Allow "-" as stdin */
2329 if (strcmp(optarg
, "-") != 0 &&
2330 (infile
= fopen(optarg
, "r")) == NULL
)
2331 fatal("%s (%s).", strerror(errno
), optarg
);
2333 quiet
= batchmode
= 1;
2334 addargs(&args
, "-obatchmode yes");
2343 sftp_direct
= optarg
;
2346 limit_kbps
= strtonum(optarg
, 1, 100 * 1024 * 1024,
2350 limit_kbps
*= 1024; /* kbps */
2356 num_requests
= strtol(optarg
, &cp
, 10);
2357 if (num_requests
== 0 || *cp
!= '\0')
2358 fatal("Invalid number of requests \"%s\"",
2362 sftp_server
= optarg
;
2365 ssh_program
= optarg
;
2366 replacearg(&args
, 0, "%s", ssh_program
);
2374 if (!isatty(STDERR_FILENO
))
2377 log_init(argv
[0], ll
, SYSLOG_FACILITY_USER
, 1);
2379 if (sftp_direct
== NULL
) {
2380 if (optind
== argc
|| argc
> (optind
+ 2))
2383 userhost
= xstrdup(argv
[optind
]);
2384 file2
= argv
[optind
+1];
2386 if ((host
= strrchr(userhost
, '@')) == NULL
)
2391 fprintf(stderr
, "Missing username\n");
2394 addargs(&args
, "-l");
2395 addargs(&args
, "%s", userhost
);
2398 if ((cp
= colon(host
)) != NULL
) {
2403 host
= cleanhostname(host
);
2405 fprintf(stderr
, "Missing hostname\n");
2409 addargs(&args
, "-oProtocol %d", sshver
);
2411 /* no subsystem if the server-spec contains a '/' */
2412 if (sftp_server
== NULL
|| strchr(sftp_server
, '/') == NULL
)
2413 addargs(&args
, "-s");
2415 addargs(&args
, "--");
2416 addargs(&args
, "%s", host
);
2417 addargs(&args
, "%s", (sftp_server
!= NULL
?
2418 sftp_server
: "sftp"));
2420 connect_to_server(ssh_program
, args
.list
, &in
, &out
);
2423 addargs(&args
, "sftp-server");
2425 connect_to_server(sftp_direct
, args
.list
, &in
, &out
);
2429 conn
= do_init(in
, out
, copy_buffer_len
, num_requests
, limit_kbps
);
2431 fatal("Couldn't initialise connection to server");
2434 if (sftp_direct
== NULL
)
2435 fprintf(stderr
, "Connected to %s.\n", host
);
2437 fprintf(stderr
, "Attached to %s.\n", sftp_direct
);
2440 err
= interactive_loop(conn
, file1
, file2
);
2442 #if !defined(USE_PIPES)
2443 shutdown(in
, SHUT_RDWR
);
2444 shutdown(out
, SHUT_RDWR
);
2452 while (waitpid(sshpid
, NULL
, 0) == -1)
2454 fatal("Couldn't wait for ssh process: %s",
2457 exit(err
== 0 ? 0 : 1);