1 /* $OpenBSD: sftp.c,v 1.131 2010/10/23 22:06:12 sthen 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>
44 typedef void EditLine
;
63 #include "pathnames.h"
68 #include "sftp-common.h"
69 #include "sftp-client.h"
71 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
72 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
74 /* File to read commands from */
77 /* Are we in batchfile mode? */
80 /* PID of ssh transport process */
81 static pid_t sshpid
= -1;
83 /* This is set to 0 if the progressmeter is not desired. */
86 /* When this option is set, we always recursively download/upload directories */
89 /* When this option is set, the file transfers will always preserve times */
92 /* SIGINT received during command processing */
93 volatile sig_atomic_t interrupted
= 0;
95 /* I wish qsort() took a separate ctx for the comparison function...*/
98 /* Context used for commandline completion */
100 struct sftp_conn
*conn
;
104 int remote_glob(struct sftp_conn
*, const char *, int,
105 int (*)(const char *, int), glob_t
*); /* proto for sftp-glob.c */
107 extern char *__progname
;
109 /* Separators for interactive commands */
110 #define WHITESPACE " \t\r\n"
113 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
114 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
115 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
116 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
117 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
118 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
119 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
120 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
121 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
123 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
124 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
126 /* Commands for interactive mode */
150 #define I_PROGRESS 23
158 /* Type of completion */
163 static const struct CMD cmds
[] = {
164 { "bye", I_QUIT
, NOARGS
},
165 { "cd", I_CHDIR
, REMOTE
},
166 { "chdir", I_CHDIR
, REMOTE
},
167 { "chgrp", I_CHGRP
, REMOTE
},
168 { "chmod", I_CHMOD
, REMOTE
},
169 { "chown", I_CHOWN
, REMOTE
},
170 { "df", I_DF
, REMOTE
},
171 { "dir", I_LS
, REMOTE
},
172 { "exit", I_QUIT
, NOARGS
},
173 { "get", I_GET
, REMOTE
},
174 { "help", I_HELP
, NOARGS
},
175 { "lcd", I_LCHDIR
, LOCAL
},
176 { "lchdir", I_LCHDIR
, LOCAL
},
177 { "lls", I_LLS
, LOCAL
},
178 { "lmkdir", I_LMKDIR
, LOCAL
},
179 { "ln", I_SYMLINK
, REMOTE
},
180 { "lpwd", I_LPWD
, LOCAL
},
181 { "ls", I_LS
, REMOTE
},
182 { "lumask", I_LUMASK
, NOARGS
},
183 { "mkdir", I_MKDIR
, REMOTE
},
184 { "mget", I_GET
, REMOTE
},
185 { "mput", I_PUT
, LOCAL
},
186 { "progress", I_PROGRESS
, NOARGS
},
187 { "put", I_PUT
, LOCAL
},
188 { "pwd", I_PWD
, REMOTE
},
189 { "quit", I_QUIT
, NOARGS
},
190 { "rename", I_RENAME
, REMOTE
},
191 { "rm", I_RM
, REMOTE
},
192 { "rmdir", I_RMDIR
, REMOTE
},
193 { "symlink", I_SYMLINK
, REMOTE
},
194 { "version", I_VERSION
, NOARGS
},
195 { "!", I_SHELL
, NOARGS
},
196 { "?", I_HELP
, NOARGS
},
200 int interactive_loop(struct sftp_conn
*, char *file1
, char *file2
);
207 kill(sshpid
, SIGTERM
);
208 waitpid(sshpid
, NULL
, 0);
216 cmd_interrupt(int signo
)
218 const char msg
[] = "\rInterrupt \n";
219 int olderrno
= errno
;
221 write(STDERR_FILENO
, msg
, sizeof(msg
) - 1);
229 printf("Available commands:\n"
231 "cd path Change remote directory to 'path'\n"
232 "chgrp grp path Change group of file 'path' to 'grp'\n"
233 "chmod mode path Change permissions of file 'path' to 'mode'\n"
234 "chown own path Change owner of file 'path' to 'own'\n"
235 "df [-hi] [path] Display statistics for current directory or\n"
236 " filesystem containing 'path'\n"
238 "get [-Ppr] remote [local] Download file\n"
239 "help Display this help text\n"
240 "lcd path Change local directory to 'path'\n"
241 "lls [ls-options [path]] Display local directory listing\n"
242 "lmkdir path Create local directory\n"
243 "ln oldpath newpath Symlink remote file\n"
244 "lpwd Print local working directory\n"
245 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
246 "lumask umask Set local umask to 'umask'\n"
247 "mkdir path Create remote directory\n"
248 "progress Toggle display of progress meter\n"
249 "put [-Ppr] local [remote] Upload file\n"
250 "pwd Display remote working directory\n"
252 "rename oldpath newpath Rename remote file\n"
253 "rm path Delete remote file\n"
254 "rmdir path Remove remote directory\n"
255 "symlink oldpath newpath Symlink remote file\n"
256 "version Show SFTP version\n"
257 "!command Execute 'command' in local shell\n"
258 "! Escape to local shell\n"
259 "? Synonym for help\n");
263 local_do_shell(const char *args
)
272 if ((shell
= getenv("SHELL")) == NULL
|| *shell
== '\0')
273 shell
= _PATH_BSHELL
;
275 if ((pid
= fork()) == -1)
276 fatal("Couldn't fork: %s", strerror(errno
));
279 /* XXX: child has pipe fds to ssh subproc open - issue? */
281 debug3("Executing %s -c \"%s\"", shell
, args
);
282 execl(shell
, shell
, "-c", args
, (char *)NULL
);
284 debug3("Executing %s", shell
);
285 execl(shell
, shell
, (char *)NULL
);
287 fprintf(stderr
, "Couldn't execute \"%s\": %s\n", shell
,
291 while (waitpid(pid
, &status
, 0) == -1)
293 fatal("Couldn't wait for child: %s", strerror(errno
));
294 if (!WIFEXITED(status
))
295 error("Shell exited abnormally");
296 else if (WEXITSTATUS(status
))
297 error("Shell exited with status %d", WEXITSTATUS(status
));
301 local_do_ls(const char *args
)
304 local_do_shell(_PATH_LS
);
306 int len
= strlen(_PATH_LS
" ") + strlen(args
) + 1;
307 char *buf
= xmalloc(len
);
309 /* XXX: quoting - rip quoting code from ftp? */
310 snprintf(buf
, len
, _PATH_LS
" %s", args
);
316 /* Strip one path (usually the pwd) from the start of another */
318 path_strip(char *path
, char *strip
)
323 return (xstrdup(path
));
326 if (strncmp(path
, strip
, len
) == 0) {
327 if (strip
[len
- 1] != '/' && path
[len
] == '/')
329 return (xstrdup(path
+ len
));
332 return (xstrdup(path
));
336 make_absolute(char *p
, char *pwd
)
341 if (p
&& p
[0] != '/') {
342 abs_str
= path_append(pwd
, p
);
350 parse_getput_flags(const char *cmd
, char **argv
, int argc
, int *pflag
,
353 extern int opterr
, optind
, optopt
, optreset
;
356 optind
= optreset
= 1;
360 while ((ch
= getopt(argc
, argv
, "PpRr")) != -1) {
371 error("%s: Invalid flag -%c", cmd
, optopt
);
380 parse_ls_flags(char **argv
, int argc
, int *lflag
)
382 extern int opterr
, optind
, optopt
, optreset
;
385 optind
= optreset
= 1;
388 *lflag
= LS_NAME_SORT
;
389 while ((ch
= getopt(argc
, argv
, "1Safhlnrt")) != -1) {
392 *lflag
&= ~VIEW_FLAGS
;
393 *lflag
|= LS_SHORT_VIEW
;
396 *lflag
&= ~SORT_FLAGS
;
397 *lflag
|= LS_SIZE_SORT
;
400 *lflag
|= LS_SHOW_ALL
;
403 *lflag
&= ~SORT_FLAGS
;
406 *lflag
|= LS_SI_UNITS
;
409 *lflag
&= ~LS_SHORT_VIEW
;
410 *lflag
|= LS_LONG_VIEW
;
413 *lflag
&= ~LS_SHORT_VIEW
;
414 *lflag
|= LS_NUMERIC_VIEW
|LS_LONG_VIEW
;
417 *lflag
|= LS_REVERSE_SORT
;
420 *lflag
&= ~SORT_FLAGS
;
421 *lflag
|= LS_TIME_SORT
;
424 error("ls: Invalid flag -%c", optopt
);
433 parse_df_flags(const char *cmd
, char **argv
, int argc
, int *hflag
, int *iflag
)
435 extern int opterr
, optind
, optopt
, optreset
;
438 optind
= optreset
= 1;
442 while ((ch
= getopt(argc
, argv
, "hi")) != -1) {
451 error("%s: Invalid flag -%c", cmd
, optopt
);
464 /* XXX: report errors? */
465 if (stat(path
, &sb
) == -1)
468 return(S_ISDIR(sb
.st_mode
));
472 remote_is_dir(struct sftp_conn
*conn
, char *path
)
476 /* XXX: report errors? */
477 if ((a
= do_stat(conn
, path
, 1)) == NULL
)
479 if (!(a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
))
481 return(S_ISDIR(a
->perm
));
484 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
486 pathname_is_dir(char *pathname
)
488 size_t l
= strlen(pathname
);
490 return l
> 0 && pathname
[l
- 1] == '/';
494 process_get(struct sftp_conn
*conn
, char *src
, char *dst
, char *pwd
,
495 int pflag
, int rflag
)
497 char *abs_src
= NULL
;
498 char *abs_dst
= NULL
;
500 char *filename
, *tmp
=NULL
;
503 abs_src
= xstrdup(src
);
504 abs_src
= make_absolute(abs_src
, pwd
);
505 memset(&g
, 0, sizeof(g
));
507 debug3("Looking up %s", abs_src
);
508 if (remote_glob(conn
, abs_src
, GLOB_MARK
, NULL
, &g
)) {
509 error("File \"%s\" not found.", abs_src
);
515 * If multiple matches then dst must be a directory or
518 if (g
.gl_matchc
> 1 && dst
!= NULL
&& !is_dir(dst
)) {
519 error("Multiple source paths, but destination "
520 "\"%s\" is not a directory", dst
);
525 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
526 tmp
= xstrdup(g
.gl_pathv
[i
]);
527 if ((filename
= basename(tmp
)) == NULL
) {
528 error("basename %s: %s", tmp
, strerror(errno
));
534 if (g
.gl_matchc
== 1 && dst
) {
536 abs_dst
= path_append(dst
, filename
);
538 abs_dst
= xstrdup(dst
);
541 abs_dst
= path_append(dst
, filename
);
543 abs_dst
= xstrdup(filename
);
547 printf("Fetching %s to %s\n", g
.gl_pathv
[i
], abs_dst
);
548 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
549 if (download_dir(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
550 pflag
|| global_pflag
, 1) == -1)
553 if (do_download(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
554 pflag
|| global_pflag
) == -1)
568 process_put(struct sftp_conn
*conn
, char *src
, char *dst
, char *pwd
,
569 int pflag
, int rflag
)
571 char *tmp_dst
= NULL
;
572 char *abs_dst
= NULL
;
573 char *tmp
= NULL
, *filename
= NULL
;
576 int i
, dst_is_dir
= 1;
580 tmp_dst
= xstrdup(dst
);
581 tmp_dst
= make_absolute(tmp_dst
, pwd
);
584 memset(&g
, 0, sizeof(g
));
585 debug3("Looking up %s", src
);
586 if (glob(src
, GLOB_NOCHECK
| GLOB_MARK
, NULL
, &g
)) {
587 error("File \"%s\" not found.", src
);
592 /* If we aren't fetching to pwd then stash this status for later */
594 dst_is_dir
= remote_is_dir(conn
, tmp_dst
);
596 /* If multiple matches, dst may be directory or unspecified */
597 if (g
.gl_matchc
> 1 && tmp_dst
&& !dst_is_dir
) {
598 error("Multiple paths match, but destination "
599 "\"%s\" is not a directory", tmp_dst
);
604 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
605 if (stat(g
.gl_pathv
[i
], &sb
) == -1) {
607 error("stat %s: %s", g
.gl_pathv
[i
], strerror(errno
));
611 tmp
= xstrdup(g
.gl_pathv
[i
]);
612 if ((filename
= basename(tmp
)) == NULL
) {
613 error("basename %s: %s", tmp
, strerror(errno
));
619 if (g
.gl_matchc
== 1 && tmp_dst
) {
620 /* If directory specified, append filename */
622 abs_dst
= path_append(tmp_dst
, filename
);
624 abs_dst
= xstrdup(tmp_dst
);
625 } else if (tmp_dst
) {
626 abs_dst
= path_append(tmp_dst
, filename
);
628 abs_dst
= make_absolute(xstrdup(filename
), pwd
);
632 printf("Uploading %s to %s\n", g
.gl_pathv
[i
], abs_dst
);
633 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
634 if (upload_dir(conn
, g
.gl_pathv
[i
], abs_dst
,
635 pflag
|| global_pflag
, 1) == -1)
638 if (do_upload(conn
, g
.gl_pathv
[i
], abs_dst
,
639 pflag
|| global_pflag
) == -1)
654 sdirent_comp(const void *aa
, const void *bb
)
656 SFTP_DIRENT
*a
= *(SFTP_DIRENT
**)aa
;
657 SFTP_DIRENT
*b
= *(SFTP_DIRENT
**)bb
;
658 int rmul
= sort_flag
& LS_REVERSE_SORT
? -1 : 1;
660 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
661 if (sort_flag
& LS_NAME_SORT
)
662 return (rmul
* strcmp(a
->filename
, b
->filename
));
663 else if (sort_flag
& LS_TIME_SORT
)
664 return (rmul
* NCMP(a
->a
.mtime
, b
->a
.mtime
));
665 else if (sort_flag
& LS_SIZE_SORT
)
666 return (rmul
* NCMP(a
->a
.size
, b
->a
.size
));
668 fatal("Unknown ls sort type");
671 /* sftp ls.1 replacement for directories */
673 do_ls_dir(struct sftp_conn
*conn
, char *path
, char *strip_path
, int lflag
)
676 u_int c
= 1, colspace
= 0, columns
= 1;
679 if ((n
= do_readdir(conn
, path
, &d
)) != 0)
682 if (!(lflag
& LS_SHORT_VIEW
)) {
683 u_int m
= 0, width
= 80;
687 /* Count entries for sort and find longest filename */
688 for (n
= 0; d
[n
] != NULL
; n
++) {
689 if (d
[n
]->filename
[0] != '.' || (lflag
& LS_SHOW_ALL
))
690 m
= MAX(m
, strlen(d
[n
]->filename
));
693 /* Add any subpath that also needs to be counted */
694 tmp
= path_strip(path
, strip_path
);
698 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
701 columns
= width
/ (m
+ 2);
702 columns
= MAX(columns
, 1);
703 colspace
= width
/ columns
;
704 colspace
= MIN(colspace
, width
);
707 if (lflag
& SORT_FLAGS
) {
708 for (n
= 0; d
[n
] != NULL
; n
++)
709 ; /* count entries */
710 sort_flag
= lflag
& (SORT_FLAGS
|LS_REVERSE_SORT
);
711 qsort(d
, n
, sizeof(*d
), sdirent_comp
);
714 for (n
= 0; d
[n
] != NULL
&& !interrupted
; n
++) {
717 if (d
[n
]->filename
[0] == '.' && !(lflag
& LS_SHOW_ALL
))
720 tmp
= path_append(path
, d
[n
]->filename
);
721 fname
= path_strip(tmp
, strip_path
);
724 if (lflag
& LS_LONG_VIEW
) {
725 if (lflag
& (LS_NUMERIC_VIEW
|LS_SI_UNITS
)) {
729 memset(&sb
, 0, sizeof(sb
));
730 attrib_to_stat(&d
[n
]->a
, &sb
);
731 lname
= ls_file(fname
, &sb
, 1,
732 (lflag
& LS_SI_UNITS
));
733 printf("%s\n", lname
);
736 printf("%s\n", d
[n
]->longname
);
738 printf("%-*s", colspace
, fname
);
749 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
752 free_sftp_dirents(d
);
756 /* sftp ls.1 replacement which handles path globs */
758 do_globbed_ls(struct sftp_conn
*conn
, char *path
, char *strip_path
,
766 u_int i
, c
= 1, colspace
= 0, columns
= 1, m
= 0, width
= 80;
768 memset(&g
, 0, sizeof(g
));
770 if (remote_glob(conn
, path
,
771 GLOB_MARK
|GLOB_NOCHECK
|GLOB_BRACE
|GLOB_KEEPSTAT
, NULL
, &g
) ||
772 (g
.gl_pathc
&& !g
.gl_matchc
)) {
775 error("Can't ls: \"%s\" not found", path
);
783 * If the glob returns a single match and it is a directory,
784 * then just list its contents.
786 if (g
.gl_matchc
== 1 && g
.gl_statv
[0] != NULL
&&
787 S_ISDIR(g
.gl_statv
[0]->st_mode
)) {
788 err
= do_ls_dir(conn
, g
.gl_pathv
[0], strip_path
, lflag
);
793 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
796 if (!(lflag
& LS_SHORT_VIEW
)) {
797 /* Count entries for sort and find longest filename */
798 for (i
= 0; g
.gl_pathv
[i
]; i
++)
799 m
= MAX(m
, strlen(g
.gl_pathv
[i
]));
801 columns
= width
/ (m
+ 2);
802 columns
= MAX(columns
, 1);
803 colspace
= width
/ columns
;
806 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++, a
= NULL
) {
807 fname
= path_strip(g
.gl_pathv
[i
], strip_path
);
808 if (lflag
& LS_LONG_VIEW
) {
809 if (g
.gl_statv
[i
] == NULL
) {
810 error("no stat information for %s", fname
);
813 lname
= ls_file(fname
, g
.gl_statv
[i
], 1,
814 (lflag
& LS_SI_UNITS
));
815 printf("%s\n", lname
);
818 printf("%-*s", colspace
, fname
);
828 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
839 do_df(struct sftp_conn
*conn
, char *path
, int hflag
, int iflag
)
841 struct sftp_statvfs st
;
842 char s_used
[FMT_SCALED_STRSIZE
];
843 char s_avail
[FMT_SCALED_STRSIZE
];
844 char s_root
[FMT_SCALED_STRSIZE
];
845 char s_total
[FMT_SCALED_STRSIZE
];
846 unsigned long long ffree
;
848 if (do_statvfs(conn
, path
, &st
, 1) == -1)
851 ffree
= st
.f_files
? (100 * (st
.f_files
- st
.f_ffree
) / st
.f_files
) : 0;
852 printf(" Inodes Used Avail "
853 "(root) %%Capacity\n");
854 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
855 (unsigned long long)st
.f_files
,
856 (unsigned long long)(st
.f_files
- st
.f_ffree
),
857 (unsigned long long)st
.f_favail
,
858 (unsigned long long)st
.f_ffree
, ffree
);
860 strlcpy(s_used
, "error", sizeof(s_used
));
861 strlcpy(s_avail
, "error", sizeof(s_avail
));
862 strlcpy(s_root
, "error", sizeof(s_root
));
863 strlcpy(s_total
, "error", sizeof(s_total
));
864 fmt_scaled((st
.f_blocks
- st
.f_bfree
) * st
.f_frsize
, s_used
);
865 fmt_scaled(st
.f_bavail
* st
.f_frsize
, s_avail
);
866 fmt_scaled(st
.f_bfree
* st
.f_frsize
, s_root
);
867 fmt_scaled(st
.f_blocks
* st
.f_frsize
, s_total
);
868 printf(" Size Used Avail (root) %%Capacity\n");
869 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
870 s_total
, s_used
, s_avail
, s_root
,
871 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
874 printf(" Size Used Avail "
875 "(root) %%Capacity\n");
876 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
877 (unsigned long long)(st
.f_frsize
* st
.f_blocks
/ 1024),
878 (unsigned long long)(st
.f_frsize
*
879 (st
.f_blocks
- st
.f_bfree
) / 1024),
880 (unsigned long long)(st
.f_frsize
* st
.f_bavail
/ 1024),
881 (unsigned long long)(st
.f_frsize
* st
.f_bfree
/ 1024),
882 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
889 * Undo escaping of glob sequences in place. Used to undo extra escaping
890 * applied in makeargv() when the string is destined for a function that
894 undo_glob_escape(char *s
)
929 * Split a string into an argument vector using sh(1)-style quoting,
930 * comment and escaping rules, but with some tweaks to handle glob(3)
932 * The "sloppy" flag allows for recovery from missing terminating quote, for
933 * use in parsing incomplete commandlines during tab autocompletion.
935 * Returns NULL on error or a NULL-terminated array of arguments.
937 * If "lastquote" is not NULL, the quoting character used for the last
938 * argument is placed in *lastquote ("\0", "'" or "\"").
940 * If "terminated" is not NULL, *terminated will be set to 1 when the
941 * last argument's quote has been properly terminated or 0 otherwise.
942 * This parameter is only of use if "sloppy" is set.
945 #define MAXARGLEN 8192
947 makeargv(const char *arg
, int *argcp
, int sloppy
, char *lastquote
,
952 static char argvs
[MAXARGLEN
];
953 static char *argv
[MAXARGS
+ 1];
954 enum { MA_START
, MA_SQUOTE
, MA_DQUOTE
, MA_UNQUOTED
} state
, q
;
957 if (strlen(arg
) > sizeof(argvs
) - 1) {
959 error("string too long");
962 if (terminated
!= NULL
)
964 if (lastquote
!= NULL
)
969 if (isspace(arg
[i
])) {
970 if (state
== MA_UNQUOTED
) {
971 /* Terminate current argument */
975 } else if (state
!= MA_START
)
977 } else if (arg
[i
] == '"' || arg
[i
] == '\'') {
978 q
= arg
[i
] == '"' ? MA_DQUOTE
: MA_SQUOTE
;
979 if (state
== MA_START
) {
980 argv
[argc
] = argvs
+ j
;
982 if (lastquote
!= NULL
)
984 } else if (state
== MA_UNQUOTED
)
990 } else if (arg
[i
] == '\\') {
991 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
992 quot
= state
== MA_SQUOTE
? '\'' : '"';
993 /* Unescape quote we are in */
994 /* XXX support \n and friends? */
995 if (arg
[i
+ 1] == quot
) {
998 } else if (arg
[i
+ 1] == '?' ||
999 arg
[i
+ 1] == '[' || arg
[i
+ 1] == '*') {
1001 * Special case for sftp: append
1002 * double-escaped glob sequence -
1003 * glob will undo one level of
1004 * escaping. NB. string can grow here.
1006 if (j
>= sizeof(argvs
) - 5)
1007 goto args_too_longs
;
1009 argvs
[j
++] = arg
[i
++];
1011 argvs
[j
++] = arg
[i
];
1013 argvs
[j
++] = arg
[i
++];
1014 argvs
[j
++] = arg
[i
];
1017 if (state
== MA_START
) {
1018 argv
[argc
] = argvs
+ j
;
1019 state
= MA_UNQUOTED
;
1020 if (lastquote
!= NULL
)
1023 if (arg
[i
+ 1] == '?' || arg
[i
+ 1] == '[' ||
1024 arg
[i
+ 1] == '*' || arg
[i
+ 1] == '\\') {
1026 * Special case for sftp: append
1027 * escaped glob sequence -
1028 * glob will undo one level of
1031 argvs
[j
++] = arg
[i
++];
1032 argvs
[j
++] = arg
[i
];
1034 /* Unescape everything */
1035 /* XXX support \n and friends? */
1037 argvs
[j
++] = arg
[i
];
1040 } else if (arg
[i
] == '#') {
1041 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
)
1042 argvs
[j
++] = arg
[i
];
1045 } else if (arg
[i
] == '\0') {
1046 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1048 state
= MA_UNQUOTED
;
1049 if (terminated
!= NULL
)
1053 error("Unterminated quoted argument");
1057 if (state
== MA_UNQUOTED
) {
1063 if (state
== MA_START
) {
1064 argv
[argc
] = argvs
+ j
;
1065 state
= MA_UNQUOTED
;
1066 if (lastquote
!= NULL
)
1069 if ((state
== MA_SQUOTE
|| state
== MA_DQUOTE
) &&
1070 (arg
[i
] == '?' || arg
[i
] == '[' || arg
[i
] == '*')) {
1072 * Special case for sftp: escape quoted
1073 * glob(3) wildcards. NB. string can grow
1076 if (j
>= sizeof(argvs
) - 3)
1077 goto args_too_longs
;
1079 argvs
[j
++] = arg
[i
];
1081 argvs
[j
++] = arg
[i
];
1090 parse_args(const char **cpp
, int *pflag
, int *rflag
, int *lflag
, int *iflag
,
1091 int *hflag
, unsigned long *n_arg
, char **path1
, char **path2
)
1093 const char *cmd
, *cp
= *cpp
;
1097 int i
, cmdnum
, optidx
, argc
;
1099 /* Skip leading whitespace */
1100 cp
= cp
+ strspn(cp
, WHITESPACE
);
1102 /* Check for leading '-' (disable error processing) */
1107 cp
= cp
+ strspn(cp
, WHITESPACE
);
1110 /* Ignore blank lines and lines which begin with comment '#' char */
1111 if (*cp
== '\0' || *cp
== '#')
1114 if ((argv
= makeargv(cp
, &argc
, 0, NULL
, NULL
)) == NULL
)
1117 /* Figure out which command we have */
1118 for (i
= 0; cmds
[i
].c
!= NULL
; i
++) {
1119 if (strcasecmp(cmds
[i
].c
, argv
[0]) == 0)
1129 } else if (cmdnum
== -1) {
1130 error("Invalid command.");
1134 /* Get arguments and parse flags */
1135 *lflag
= *pflag
= *rflag
= *hflag
= *n_arg
= 0;
1136 *path1
= *path2
= NULL
;
1141 if ((optidx
= parse_getput_flags(cmd
, argv
, argc
, pflag
, rflag
)) == -1)
1143 /* Get first pathname (mandatory) */
1144 if (argc
- optidx
< 1) {
1145 error("You must specify at least one path after a "
1146 "%s command.", cmd
);
1149 *path1
= xstrdup(argv
[optidx
]);
1150 /* Get second pathname (optional) */
1151 if (argc
- optidx
> 1) {
1152 *path2
= xstrdup(argv
[optidx
+ 1]);
1153 /* Destination is not globbed */
1154 undo_glob_escape(*path2
);
1159 if (argc
- optidx
< 2) {
1160 error("You must specify two paths after a %s "
1164 *path1
= xstrdup(argv
[optidx
]);
1165 *path2
= xstrdup(argv
[optidx
+ 1]);
1166 /* Paths are not globbed */
1167 undo_glob_escape(*path1
);
1168 undo_glob_escape(*path2
);
1176 /* Get pathname (mandatory) */
1177 if (argc
- optidx
< 1) {
1178 error("You must specify a path after a %s command.",
1182 *path1
= xstrdup(argv
[optidx
]);
1183 /* Only "rm" globs */
1185 undo_glob_escape(*path1
);
1188 if ((optidx
= parse_df_flags(cmd
, argv
, argc
, hflag
,
1191 /* Default to current directory if no path specified */
1192 if (argc
- optidx
< 1)
1195 *path1
= xstrdup(argv
[optidx
]);
1196 undo_glob_escape(*path1
);
1200 if ((optidx
= parse_ls_flags(argv
, argc
, lflag
)) == -1)
1202 /* Path is optional */
1203 if (argc
- optidx
> 0)
1204 *path1
= xstrdup(argv
[optidx
]);
1207 /* Skip ls command and following whitespace */
1208 cp
= cp
+ strlen(cmd
) + strspn(cp
, WHITESPACE
);
1210 /* Uses the rest of the line */
1217 /* Get numeric arg (mandatory) */
1218 if (argc
- optidx
< 1)
1221 l
= strtol(argv
[optidx
], &cp2
, base
);
1222 if (cp2
== argv
[optidx
] || *cp2
!= '\0' ||
1223 ((l
== LONG_MIN
|| l
== LONG_MAX
) && errno
== ERANGE
) ||
1226 error("You must supply a numeric argument "
1227 "to the %s command.", cmd
);
1231 if (cmdnum
== I_LUMASK
)
1233 /* Get pathname (mandatory) */
1234 if (argc
- optidx
< 2) {
1235 error("You must specify a path after a %s command.",
1239 *path1
= xstrdup(argv
[optidx
+ 1]);
1249 fatal("Command not implemented");
1257 parse_dispatch_command(struct sftp_conn
*conn
, const char *cmd
, char **pwd
,
1260 char *path1
, *path2
, *tmp
;
1261 int pflag
= 0, rflag
= 0, lflag
= 0, iflag
= 0, hflag
= 0, cmdnum
, i
;
1262 unsigned long n_arg
= 0;
1264 char path_buf
[MAXPATHLEN
];
1268 path1
= path2
= NULL
;
1269 cmdnum
= parse_args(&cmd
, &pflag
, &rflag
, &lflag
, &iflag
, &hflag
, &n_arg
,
1275 memset(&g
, 0, sizeof(g
));
1277 /* Perform command */
1283 /* Unrecognized command */
1287 err
= process_get(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1290 err
= process_put(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1293 path1
= make_absolute(path1
, *pwd
);
1294 path2
= make_absolute(path2
, *pwd
);
1295 err
= do_rename(conn
, path1
, path2
);
1298 path2
= make_absolute(path2
, *pwd
);
1299 err
= do_symlink(conn
, path1
, path2
);
1302 path1
= make_absolute(path1
, *pwd
);
1303 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1304 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1305 printf("Removing %s\n", g
.gl_pathv
[i
]);
1306 err
= do_rm(conn
, g
.gl_pathv
[i
]);
1307 if (err
!= 0 && err_abort
)
1312 path1
= make_absolute(path1
, *pwd
);
1314 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1316 err
= do_mkdir(conn
, path1
, &a
, 1);
1319 path1
= make_absolute(path1
, *pwd
);
1320 err
= do_rmdir(conn
, path1
);
1323 path1
= make_absolute(path1
, *pwd
);
1324 if ((tmp
= do_realpath(conn
, path1
)) == NULL
) {
1328 if ((aa
= do_stat(conn
, tmp
, 0)) == NULL
) {
1333 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
)) {
1334 error("Can't change directory: Can't check target");
1339 if (!S_ISDIR(aa
->perm
)) {
1340 error("Can't change directory: \"%s\" is not "
1341 "a directory", tmp
);
1351 do_ls_dir(conn
, *pwd
, *pwd
, lflag
);
1355 /* Strip pwd off beginning of non-absolute paths */
1360 path1
= make_absolute(path1
, *pwd
);
1361 err
= do_globbed_ls(conn
, path1
, tmp
, lflag
);
1364 /* Default to current directory if no path specified */
1366 path1
= xstrdup(*pwd
);
1367 path1
= make_absolute(path1
, *pwd
);
1368 err
= do_df(conn
, path1
, hflag
, iflag
);
1371 if (chdir(path1
) == -1) {
1372 error("Couldn't change local directory to "
1373 "\"%s\": %s", path1
, strerror(errno
));
1378 if (mkdir(path1
, 0777) == -1) {
1379 error("Couldn't create local directory "
1380 "\"%s\": %s", path1
, strerror(errno
));
1388 local_do_shell(cmd
);
1392 printf("Local umask: %03lo\n", n_arg
);
1395 path1
= make_absolute(path1
, *pwd
);
1397 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1399 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1400 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1401 printf("Changing mode on %s\n", g
.gl_pathv
[i
]);
1402 err
= do_setstat(conn
, g
.gl_pathv
[i
], &a
);
1403 if (err
!= 0 && err_abort
)
1409 path1
= make_absolute(path1
, *pwd
);
1410 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1411 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1412 if (!(aa
= do_stat(conn
, g
.gl_pathv
[i
], 0))) {
1419 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_UIDGID
)) {
1420 error("Can't get current ownership of "
1421 "remote file \"%s\"", g
.gl_pathv
[i
]);
1428 aa
->flags
&= SSH2_FILEXFER_ATTR_UIDGID
;
1429 if (cmdnum
== I_CHOWN
) {
1430 printf("Changing owner on %s\n", g
.gl_pathv
[i
]);
1433 printf("Changing group on %s\n", g
.gl_pathv
[i
]);
1436 err
= do_setstat(conn
, g
.gl_pathv
[i
], aa
);
1437 if (err
!= 0 && err_abort
)
1442 printf("Remote working directory: %s\n", *pwd
);
1445 if (!getcwd(path_buf
, sizeof(path_buf
))) {
1446 error("Couldn't get local cwd: %s", strerror(errno
));
1450 printf("Local working directory: %s\n", path_buf
);
1453 /* Processed below */
1459 printf("SFTP protocol version %u\n", sftp_proto_version(conn
));
1462 showprogress
= !showprogress
;
1464 printf("Progress meter enabled\n");
1466 printf("Progress meter disabled\n");
1469 fatal("%d is not implemented", cmdnum
);
1479 /* If an unignored error occurs in batch mode we should abort. */
1480 if (err_abort
&& err
!= 0)
1482 else if (cmdnum
== I_QUIT
)
1490 prompt(EditLine
*el
)
1495 /* Display entries in 'list' after skipping the first 'len' chars */
1497 complete_display(char **list
, u_int len
)
1499 u_int y
, m
= 0, width
= 80, columns
= 1, colspace
= 0, llen
;
1503 /* Count entries for sort and find longest */
1504 for (y
= 0; list
[y
]; y
++)
1505 m
= MAX(m
, strlen(list
[y
]));
1507 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
1510 m
= m
> len
? m
- len
: 0;
1511 columns
= width
/ (m
+ 2);
1512 columns
= MAX(columns
, 1);
1513 colspace
= width
/ columns
;
1514 colspace
= MIN(colspace
, width
);
1518 for (y
= 0; list
[y
]; y
++) {
1519 llen
= strlen(list
[y
]);
1520 tmp
= llen
> len
? list
[y
] + len
: "";
1521 printf("%-*s", colspace
, tmp
);
1532 * Given a "list" of words that begin with a common prefix of "word",
1533 * attempt to find an autocompletion to extends "word" by the next
1534 * characters common to all entries in "list".
1537 complete_ambiguous(const char *word
, char **list
, size_t count
)
1543 u_int y
, matchlen
= strlen(list
[0]);
1545 /* Find length of common stem */
1546 for (y
= 1; list
[y
]; y
++) {
1549 for (x
= 0; x
< matchlen
; x
++)
1550 if (list
[0][x
] != list
[y
][x
])
1556 if (matchlen
> strlen(word
)) {
1557 char *tmp
= xstrdup(list
[0]);
1559 tmp
[matchlen
] = '\0';
1564 return xstrdup(word
);
1567 /* Autocomplete a sftp command */
1569 complete_cmd_parse(EditLine
*el
, char *cmd
, int lastarg
, char quote
,
1572 u_int y
, count
= 0, cmdlen
, tmplen
;
1573 char *tmp
, **list
, argterm
[3];
1576 list
= xcalloc((sizeof(cmds
) / sizeof(*cmds
)) + 1, sizeof(char *));
1578 /* No command specified: display all available commands */
1580 for (y
= 0; cmds
[y
].c
; y
++)
1581 list
[count
++] = xstrdup(cmds
[y
].c
);
1584 complete_display(list
, 0);
1586 for (y
= 0; list
[y
] != NULL
; y
++)
1592 /* Prepare subset of commands that start with "cmd" */
1593 cmdlen
= strlen(cmd
);
1594 for (y
= 0; cmds
[y
].c
; y
++) {
1595 if (!strncasecmp(cmd
, cmds
[y
].c
, cmdlen
))
1596 list
[count
++] = xstrdup(cmds
[y
].c
);
1603 /* Complete ambigious command */
1604 tmp
= complete_ambiguous(cmd
, list
, count
);
1606 complete_display(list
, 0);
1608 for (y
= 0; list
[y
]; y
++)
1613 tmplen
= strlen(tmp
);
1614 cmdlen
= strlen(cmd
);
1615 /* If cmd may be extended then do so */
1616 if (tmplen
> cmdlen
)
1617 if (el_insertstr(el
, tmp
+ cmdlen
) == -1)
1618 fatal("el_insertstr failed.");
1620 /* Terminate argument cleanly */
1624 argterm
[y
++] = quote
;
1625 if (lastarg
|| *(lf
->cursor
) != ' ')
1628 if (y
> 0 && el_insertstr(el
, argterm
) == -1)
1629 fatal("el_insertstr failed.");
1638 * Determine whether a particular sftp command's arguments (if any)
1639 * represent local or remote files.
1642 complete_is_remote(char *cmd
) {
1648 for (i
= 0; cmds
[i
].c
; i
++) {
1649 if (!strncasecmp(cmd
, cmds
[i
].c
, strlen(cmds
[i
].c
)))
1656 /* Autocomplete a filename "file" */
1658 complete_match(EditLine
*el
, struct sftp_conn
*conn
, char *remote_path
,
1659 char *file
, int remote
, int lastarg
, char quote
, int terminated
)
1662 char *tmp
, *tmp2
, ins
[3];
1663 u_int i
, hadglob
, pwdlen
, len
, tmplen
, filelen
;
1666 /* Glob from "file" location */
1670 xasprintf(&tmp
, "%s*", file
);
1672 memset(&g
, 0, sizeof(g
));
1673 if (remote
!= LOCAL
) {
1674 tmp
= make_absolute(tmp
, remote_path
);
1675 remote_glob(conn
, tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1677 glob(tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1679 /* Determine length of pwd so we can trim completion display */
1680 for (hadglob
= tmplen
= pwdlen
= 0; tmp
[tmplen
] != 0; tmplen
++) {
1681 /* Terminate counting on first unescaped glob metacharacter */
1682 if (tmp
[tmplen
] == '*' || tmp
[tmplen
] == '?') {
1683 if (tmp
[tmplen
] != '*' || tmp
[tmplen
+ 1] != '\0')
1687 if (tmp
[tmplen
] == '\\' && tmp
[tmplen
+ 1] != '\0')
1689 if (tmp
[tmplen
] == '/')
1690 pwdlen
= tmplen
+ 1; /* track last seen '/' */
1694 if (g
.gl_matchc
== 0)
1697 if (g
.gl_matchc
> 1)
1698 complete_display(g
.gl_pathv
, pwdlen
);
1701 /* Don't try to extend globs */
1702 if (file
== NULL
|| hadglob
)
1705 tmp2
= complete_ambiguous(file
, g
.gl_pathv
, g
.gl_matchc
);
1706 tmp
= path_strip(tmp2
, remote_path
);
1712 tmplen
= strlen(tmp
);
1713 filelen
= strlen(file
);
1715 if (tmplen
> filelen
) {
1716 tmp2
= tmp
+ filelen
;
1718 /* quote argument on way out */
1719 for (i
= 0; i
< len
; i
++) {
1730 if (quote
== '\0' || tmp2
[i
] == quote
) {
1731 if (el_insertstr(el
, ins
) == -1)
1732 fatal("el_insertstr "
1738 if (el_insertstr(el
, ins
+ 1) == -1)
1739 fatal("el_insertstr failed.");
1746 if (g
.gl_matchc
== 1) {
1750 if (*(lf
->cursor
- 1) != '/' &&
1751 (lastarg
|| *(lf
->cursor
) != ' '))
1754 if (i
> 0 && el_insertstr(el
, ins
) == -1)
1755 fatal("el_insertstr failed.");
1764 /* tab-completion hook function, called via libedit */
1765 static unsigned char
1766 complete(EditLine
*el
, int ch
)
1768 char **argv
, *line
, quote
;
1769 u_int argc
, carg
, cursor
, len
, terminated
, ret
= CC_ERROR
;
1771 struct complete_ctx
*complete_ctx
;
1774 if (el_get(el
, EL_CLIENTDATA
, (void**)&complete_ctx
) != 0)
1775 fatal("%s: el_get failed", __func__
);
1777 /* Figure out which argument the cursor points to */
1778 cursor
= lf
->cursor
- lf
->buffer
;
1779 line
= (char *)xmalloc(cursor
+ 1);
1780 memcpy(line
, lf
->buffer
, cursor
);
1781 line
[cursor
] = '\0';
1782 argv
= makeargv(line
, &carg
, 1, "e
, &terminated
);
1785 /* Get all the arguments on the line */
1786 len
= lf
->lastchar
- lf
->buffer
;
1787 line
= (char *)xmalloc(len
+ 1);
1788 memcpy(line
, lf
->buffer
, len
);
1790 argv
= makeargv(line
, &argc
, 1, NULL
, NULL
);
1792 /* Ensure cursor is at EOL or a argument boundary */
1793 if (line
[cursor
] != ' ' && line
[cursor
] != '\0' &&
1794 line
[cursor
] != '\n') {
1800 /* Show all available commands */
1801 complete_cmd_parse(el
, NULL
, argc
== carg
, '\0', 1);
1803 } else if (carg
== 1 && cursor
> 0 && line
[cursor
- 1] != ' ') {
1804 /* Handle the command parsing */
1805 if (complete_cmd_parse(el
, argv
[0], argc
== carg
,
1806 quote
, terminated
) != 0)
1808 } else if (carg
>= 1) {
1809 /* Handle file parsing */
1810 int remote
= complete_is_remote(argv
[0]);
1811 char *filematch
= NULL
;
1813 if (carg
> 1 && line
[cursor
-1] != ' ')
1814 filematch
= argv
[carg
- 1];
1817 complete_match(el
, complete_ctx
->conn
,
1818 *complete_ctx
->remote_pathp
, filematch
,
1819 remote
, carg
== argc
, quote
, terminated
) != 0)
1826 #endif /* USE_LIBEDIT */
1829 interactive_loop(struct sftp_conn
*conn
, char *file1
, char *file2
)
1834 int err
, interactive
;
1835 EditLine
*el
= NULL
;
1839 extern char *__progname
;
1840 struct complete_ctx complete_ctx
;
1842 if (!batchmode
&& isatty(STDIN_FILENO
)) {
1843 if ((el
= el_init(__progname
, stdin
, stdout
, stderr
)) == NULL
)
1844 fatal("Couldn't initialise editline");
1845 if ((hl
= history_init()) == NULL
)
1846 fatal("Couldn't initialise editline history");
1847 history(hl
, &hev
, H_SETSIZE
, 100);
1848 el_set(el
, EL_HIST
, history
, hl
);
1850 el_set(el
, EL_PROMPT
, prompt
);
1851 el_set(el
, EL_EDITOR
, "emacs");
1852 el_set(el
, EL_TERMINAL
, NULL
);
1853 el_set(el
, EL_SIGNAL
, 1);
1854 el_source(el
, NULL
);
1856 /* Tab Completion */
1857 el_set(el
, EL_ADDFN
, "ftp-complete",
1858 "Context sensitive argument completion", complete
);
1859 complete_ctx
.conn
= conn
;
1860 complete_ctx
.remote_pathp
= &remote_path
;
1861 el_set(el
, EL_CLIENTDATA
, (void*)&complete_ctx
);
1862 el_set(el
, EL_BIND
, "^I", "ftp-complete", NULL
);
1864 #endif /* USE_LIBEDIT */
1866 remote_path
= do_realpath(conn
, ".");
1867 if (remote_path
== NULL
)
1870 if (file1
!= NULL
) {
1871 dir
= xstrdup(file1
);
1872 dir
= make_absolute(dir
, remote_path
);
1874 if (remote_is_dir(conn
, dir
) && file2
== NULL
) {
1875 printf("Changing to: %s\n", dir
);
1876 snprintf(cmd
, sizeof cmd
, "cd \"%s\"", dir
);
1877 if (parse_dispatch_command(conn
, cmd
,
1878 &remote_path
, 1) != 0) {
1886 snprintf(cmd
, sizeof cmd
, "get %s", dir
);
1888 snprintf(cmd
, sizeof cmd
, "get %s %s", dir
,
1891 err
= parse_dispatch_command(conn
, cmd
,
1901 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1902 setvbuf(stdout
, NULL
, _IOLBF
, 0);
1903 setvbuf(infile
, NULL
, _IOLBF
, 0);
1909 interactive
= !batchmode
&& isatty(STDIN_FILENO
);
1914 signal(SIGINT
, SIG_IGN
);
1919 if (fgets(cmd
, sizeof(cmd
), infile
) == NULL
) {
1924 if (!interactive
) { /* Echo command */
1925 printf("sftp> %s", cmd
);
1926 if (strlen(cmd
) > 0 &&
1927 cmd
[strlen(cmd
) - 1] != '\n')
1935 if ((line
= el_gets(el
, &count
)) == NULL
||
1940 history(hl
, &hev
, H_ENTER
, line
);
1941 if (strlcpy(cmd
, line
, sizeof(cmd
)) >= sizeof(cmd
)) {
1942 fprintf(stderr
, "Error: input line too long\n");
1945 #endif /* USE_LIBEDIT */
1948 cp
= strrchr(cmd
, '\n');
1952 /* Handle user interrupts gracefully during commands */
1954 signal(SIGINT
, cmd_interrupt
);
1956 err
= parse_dispatch_command(conn
, cmd
, &remote_path
,
1967 #endif /* USE_LIBEDIT */
1969 /* err == 1 signifies normal "quit" exit */
1970 return (err
>= 0 ? 0 : -1);
1974 connect_to_server(char *path
, char **args
, int *in
, int *out
)
1979 int pin
[2], pout
[2];
1981 if ((pipe(pin
) == -1) || (pipe(pout
) == -1))
1982 fatal("pipe: %s", strerror(errno
));
1987 #else /* USE_PIPES */
1990 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) == -1)
1991 fatal("socketpair: %s", strerror(errno
));
1992 *in
= *out
= inout
[0];
1993 c_in
= c_out
= inout
[1];
1994 #endif /* USE_PIPES */
1996 if ((sshpid
= fork()) == -1)
1997 fatal("fork: %s", strerror(errno
));
1998 else if (sshpid
== 0) {
1999 if ((dup2(c_in
, STDIN_FILENO
) == -1) ||
2000 (dup2(c_out
, STDOUT_FILENO
) == -1)) {
2001 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
2010 * The underlying ssh is in the same process group, so we must
2011 * ignore SIGINT if we want to gracefully abort commands,
2012 * otherwise the signal will make it to the ssh process and
2013 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2014 * underlying ssh, it must *not* ignore that signal.
2016 signal(SIGINT
, SIG_IGN
);
2017 signal(SIGTERM
, SIG_DFL
);
2019 fprintf(stderr
, "exec: %s: %s\n", path
, strerror(errno
));
2023 signal(SIGTERM
, killchild
);
2024 signal(SIGINT
, killchild
);
2025 signal(SIGHUP
, killchild
);
2033 extern char *__progname
;
2036 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2037 " [-D sftp_server_path] [-F ssh_config] "
2038 "[-i identity_file] [-l limit]\n"
2039 " [-o ssh_option] [-P port] [-R num_requests] "
2041 " [-s subsystem | sftp_server] host\n"
2042 " %s [user@]host[:file ...]\n"
2043 " %s [user@]host[:dir[/]]\n"
2044 " %s -b batchfile [user@]host\n",
2045 __progname
, __progname
, __progname
, __progname
);
2050 main(int argc
, char **argv
)
2052 int in
, out
, ch
, err
;
2053 char *host
= NULL
, *userhost
, *cp
, *file2
= NULL
;
2054 int debug_level
= 0, sshver
= 2;
2055 char *file1
= NULL
, *sftp_server
= NULL
;
2056 char *ssh_program
= _PATH_SSH_PROGRAM
, *sftp_direct
= NULL
;
2058 LogLevel ll
= SYSLOG_LEVEL_INFO
;
2061 extern char *optarg
;
2062 struct sftp_conn
*conn
;
2063 size_t copy_buffer_len
= DEFAULT_COPY_BUFLEN
;
2064 size_t num_requests
= DEFAULT_NUM_REQUESTS
;
2065 long long limit_kbps
= 0;
2067 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2070 __progname
= ssh_get_progname(argv
[0]);
2071 memset(&args
, '\0', sizeof(args
));
2073 addargs(&args
, "%s", ssh_program
);
2074 addargs(&args
, "-oForwardX11 no");
2075 addargs(&args
, "-oForwardAgent no");
2076 addargs(&args
, "-oPermitLocalCommand no");
2077 addargs(&args
, "-oClearAllForwardings yes");
2079 ll
= SYSLOG_LEVEL_INFO
;
2082 while ((ch
= getopt(argc
, argv
,
2083 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2085 /* Passed through to ssh(1) */
2089 addargs(&args
, "-%c", ch
);
2091 /* Passed through to ssh(1) with argument */
2096 addargs(&args
, "-%c", ch
);
2097 addargs(&args
, "%s", optarg
);
2101 addargs(&args
, "-%c", ch
);
2104 addargs(&args
, "-oPort %s", optarg
);
2107 if (debug_level
< 3) {
2108 addargs(&args
, "-v");
2109 ll
= SYSLOG_LEVEL_DEBUG1
+ debug_level
;
2115 if (sftp_server
== NULL
)
2116 sftp_server
= _PATH_SFTP_SERVER
;
2122 copy_buffer_len
= strtol(optarg
, &cp
, 10);
2123 if (copy_buffer_len
== 0 || *cp
!= '\0')
2124 fatal("Invalid buffer size \"%s\"", optarg
);
2128 fatal("Batch file already specified.");
2130 /* Allow "-" as stdin */
2131 if (strcmp(optarg
, "-") != 0 &&
2132 (infile
= fopen(optarg
, "r")) == NULL
)
2133 fatal("%s (%s).", strerror(errno
), optarg
);
2136 addargs(&args
, "-obatchmode yes");
2142 sftp_direct
= optarg
;
2145 limit_kbps
= strtonum(optarg
, 1, 100 * 1024 * 1024,
2149 limit_kbps
*= 1024; /* kbps */
2155 num_requests
= strtol(optarg
, &cp
, 10);
2156 if (num_requests
== 0 || *cp
!= '\0')
2157 fatal("Invalid number of requests \"%s\"",
2161 sftp_server
= optarg
;
2164 ssh_program
= optarg
;
2165 replacearg(&args
, 0, "%s", ssh_program
);
2173 if (!isatty(STDERR_FILENO
))
2176 log_init(argv
[0], ll
, SYSLOG_FACILITY_USER
, 1);
2178 if (sftp_direct
== NULL
) {
2179 if (optind
== argc
|| argc
> (optind
+ 2))
2182 userhost
= xstrdup(argv
[optind
]);
2183 file2
= argv
[optind
+1];
2185 if ((host
= strrchr(userhost
, '@')) == NULL
)
2190 fprintf(stderr
, "Missing username\n");
2193 addargs(&args
, "-l");
2194 addargs(&args
, "%s", userhost
);
2197 if ((cp
= colon(host
)) != NULL
) {
2202 host
= cleanhostname(host
);
2204 fprintf(stderr
, "Missing hostname\n");
2208 addargs(&args
, "-oProtocol %d", sshver
);
2210 /* no subsystem if the server-spec contains a '/' */
2211 if (sftp_server
== NULL
|| strchr(sftp_server
, '/') == NULL
)
2212 addargs(&args
, "-s");
2214 addargs(&args
, "--");
2215 addargs(&args
, "%s", host
);
2216 addargs(&args
, "%s", (sftp_server
!= NULL
?
2217 sftp_server
: "sftp"));
2219 connect_to_server(ssh_program
, args
.list
, &in
, &out
);
2222 addargs(&args
, "sftp-server");
2224 connect_to_server(sftp_direct
, args
.list
, &in
, &out
);
2228 conn
= do_init(in
, out
, copy_buffer_len
, num_requests
, limit_kbps
);
2230 fatal("Couldn't initialise connection to server");
2233 if (sftp_direct
== NULL
)
2234 fprintf(stderr
, "Connected to %s.\n", host
);
2236 fprintf(stderr
, "Attached to %s.\n", sftp_direct
);
2239 err
= interactive_loop(conn
, file1
, file2
);
2241 #if !defined(USE_PIPES)
2242 shutdown(in
, SHUT_RDWR
);
2243 shutdown(out
, SHUT_RDWR
);
2251 while (waitpid(sshpid
, NULL
, 0) == -1)
2253 fatal("Couldn't wait for ssh process: %s",
2256 exit(err
== 0 ? 0 : 1);