1 /* $OpenBSD: sftp.c,v 1.132 2010/12/04 00:18:01 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>
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 */
151 #define I_PROGRESS 23
159 /* Type of completion */
164 static const struct CMD cmds
[] = {
165 { "bye", I_QUIT
, NOARGS
},
166 { "cd", I_CHDIR
, REMOTE
},
167 { "chdir", I_CHDIR
, REMOTE
},
168 { "chgrp", I_CHGRP
, REMOTE
},
169 { "chmod", I_CHMOD
, REMOTE
},
170 { "chown", I_CHOWN
, REMOTE
},
171 { "df", I_DF
, REMOTE
},
172 { "dir", I_LS
, REMOTE
},
173 { "exit", I_QUIT
, NOARGS
},
174 { "get", I_GET
, REMOTE
},
175 { "help", I_HELP
, NOARGS
},
176 { "lcd", I_LCHDIR
, LOCAL
},
177 { "lchdir", I_LCHDIR
, LOCAL
},
178 { "lls", I_LLS
, LOCAL
},
179 { "lmkdir", I_LMKDIR
, LOCAL
},
180 { "ln", I_LINK
, REMOTE
},
181 { "lpwd", I_LPWD
, LOCAL
},
182 { "ls", I_LS
, REMOTE
},
183 { "lumask", I_LUMASK
, NOARGS
},
184 { "mkdir", I_MKDIR
, REMOTE
},
185 { "mget", I_GET
, REMOTE
},
186 { "mput", I_PUT
, LOCAL
},
187 { "progress", I_PROGRESS
, NOARGS
},
188 { "put", I_PUT
, LOCAL
},
189 { "pwd", I_PWD
, REMOTE
},
190 { "quit", I_QUIT
, NOARGS
},
191 { "rename", I_RENAME
, REMOTE
},
192 { "rm", I_RM
, REMOTE
},
193 { "rmdir", I_RMDIR
, REMOTE
},
194 { "symlink", I_SYMLINK
, REMOTE
},
195 { "version", I_VERSION
, NOARGS
},
196 { "!", I_SHELL
, NOARGS
},
197 { "?", I_HELP
, NOARGS
},
201 int interactive_loop(struct sftp_conn
*, char *file1
, char *file2
);
208 kill(sshpid
, SIGTERM
);
209 waitpid(sshpid
, NULL
, 0);
217 cmd_interrupt(int signo
)
219 const char msg
[] = "\rInterrupt \n";
220 int olderrno
= errno
;
222 write(STDERR_FILENO
, msg
, sizeof(msg
) - 1);
230 printf("Available commands:\n"
232 "cd path Change remote directory to 'path'\n"
233 "chgrp grp path Change group of file 'path' to 'grp'\n"
234 "chmod mode path Change permissions of file 'path' to 'mode'\n"
235 "chown own path Change owner of file 'path' to 'own'\n"
236 "df [-hi] [path] Display statistics for current directory or\n"
237 " filesystem containing 'path'\n"
239 "get [-Ppr] remote [local] Download file\n"
240 "help Display this help text\n"
241 "lcd path Change local directory to 'path'\n"
242 "lls [ls-options [path]] Display local directory listing\n"
243 "lmkdir path Create local directory\n"
244 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
245 "lpwd Print local working directory\n"
246 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
247 "lumask umask Set local umask to 'umask'\n"
248 "mkdir path Create remote directory\n"
249 "progress Toggle display of progress meter\n"
250 "put [-Ppr] local [remote] Upload file\n"
251 "pwd Display remote working directory\n"
253 "rename oldpath newpath Rename remote file\n"
254 "rm path Delete remote file\n"
255 "rmdir path Remove remote directory\n"
256 "symlink oldpath newpath Symlink remote file\n"
257 "version Show SFTP version\n"
258 "!command Execute 'command' in local shell\n"
259 "! Escape to local shell\n"
260 "? Synonym for help\n");
264 local_do_shell(const char *args
)
273 if ((shell
= getenv("SHELL")) == NULL
|| *shell
== '\0')
274 shell
= _PATH_BSHELL
;
276 if ((pid
= fork()) == -1)
277 fatal("Couldn't fork: %s", strerror(errno
));
280 /* XXX: child has pipe fds to ssh subproc open - issue? */
282 debug3("Executing %s -c \"%s\"", shell
, args
);
283 execl(shell
, shell
, "-c", args
, (char *)NULL
);
285 debug3("Executing %s", shell
);
286 execl(shell
, shell
, (char *)NULL
);
288 fprintf(stderr
, "Couldn't execute \"%s\": %s\n", shell
,
292 while (waitpid(pid
, &status
, 0) == -1)
294 fatal("Couldn't wait for child: %s", strerror(errno
));
295 if (!WIFEXITED(status
))
296 error("Shell exited abnormally");
297 else if (WEXITSTATUS(status
))
298 error("Shell exited with status %d", WEXITSTATUS(status
));
302 local_do_ls(const char *args
)
305 local_do_shell(_PATH_LS
);
307 int len
= strlen(_PATH_LS
" ") + strlen(args
) + 1;
308 char *buf
= xmalloc(len
);
310 /* XXX: quoting - rip quoting code from ftp? */
311 snprintf(buf
, len
, _PATH_LS
" %s", args
);
317 /* Strip one path (usually the pwd) from the start of another */
319 path_strip(char *path
, char *strip
)
324 return (xstrdup(path
));
327 if (strncmp(path
, strip
, len
) == 0) {
328 if (strip
[len
- 1] != '/' && path
[len
] == '/')
330 return (xstrdup(path
+ len
));
333 return (xstrdup(path
));
337 make_absolute(char *p
, char *pwd
)
342 if (p
&& p
[0] != '/') {
343 abs_str
= path_append(pwd
, p
);
351 parse_getput_flags(const char *cmd
, char **argv
, int argc
, int *pflag
,
354 extern int opterr
, optind
, optopt
, optreset
;
357 optind
= optreset
= 1;
361 while ((ch
= getopt(argc
, argv
, "PpRr")) != -1) {
372 error("%s: Invalid flag -%c", cmd
, optopt
);
381 parse_link_flags(const char *cmd
, char **argv
, int argc
, int *sflag
)
383 extern int opterr
, optind
, optopt
, optreset
;
386 optind
= optreset
= 1;
390 while ((ch
= getopt(argc
, argv
, "s")) != -1) {
396 error("%s: Invalid flag -%c", cmd
, optopt
);
405 parse_ls_flags(char **argv
, int argc
, int *lflag
)
407 extern int opterr
, optind
, optopt
, optreset
;
410 optind
= optreset
= 1;
413 *lflag
= LS_NAME_SORT
;
414 while ((ch
= getopt(argc
, argv
, "1Safhlnrt")) != -1) {
417 *lflag
&= ~VIEW_FLAGS
;
418 *lflag
|= LS_SHORT_VIEW
;
421 *lflag
&= ~SORT_FLAGS
;
422 *lflag
|= LS_SIZE_SORT
;
425 *lflag
|= LS_SHOW_ALL
;
428 *lflag
&= ~SORT_FLAGS
;
431 *lflag
|= LS_SI_UNITS
;
434 *lflag
&= ~LS_SHORT_VIEW
;
435 *lflag
|= LS_LONG_VIEW
;
438 *lflag
&= ~LS_SHORT_VIEW
;
439 *lflag
|= LS_NUMERIC_VIEW
|LS_LONG_VIEW
;
442 *lflag
|= LS_REVERSE_SORT
;
445 *lflag
&= ~SORT_FLAGS
;
446 *lflag
|= LS_TIME_SORT
;
449 error("ls: Invalid flag -%c", optopt
);
458 parse_df_flags(const char *cmd
, char **argv
, int argc
, int *hflag
, int *iflag
)
460 extern int opterr
, optind
, optopt
, optreset
;
463 optind
= optreset
= 1;
467 while ((ch
= getopt(argc
, argv
, "hi")) != -1) {
476 error("%s: Invalid flag -%c", cmd
, optopt
);
489 /* XXX: report errors? */
490 if (stat(path
, &sb
) == -1)
493 return(S_ISDIR(sb
.st_mode
));
497 remote_is_dir(struct sftp_conn
*conn
, char *path
)
501 /* XXX: report errors? */
502 if ((a
= do_stat(conn
, path
, 1)) == NULL
)
504 if (!(a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
))
506 return(S_ISDIR(a
->perm
));
509 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
511 pathname_is_dir(char *pathname
)
513 size_t l
= strlen(pathname
);
515 return l
> 0 && pathname
[l
- 1] == '/';
519 process_get(struct sftp_conn
*conn
, char *src
, char *dst
, char *pwd
,
520 int pflag
, int rflag
)
522 char *abs_src
= NULL
;
523 char *abs_dst
= NULL
;
525 char *filename
, *tmp
=NULL
;
528 abs_src
= xstrdup(src
);
529 abs_src
= make_absolute(abs_src
, pwd
);
530 memset(&g
, 0, sizeof(g
));
532 debug3("Looking up %s", abs_src
);
533 if (remote_glob(conn
, abs_src
, GLOB_MARK
, NULL
, &g
)) {
534 error("File \"%s\" not found.", abs_src
);
540 * If multiple matches then dst must be a directory or
543 if (g
.gl_matchc
> 1 && dst
!= NULL
&& !is_dir(dst
)) {
544 error("Multiple source paths, but destination "
545 "\"%s\" is not a directory", dst
);
550 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
551 tmp
= xstrdup(g
.gl_pathv
[i
]);
552 if ((filename
= basename(tmp
)) == NULL
) {
553 error("basename %s: %s", tmp
, strerror(errno
));
559 if (g
.gl_matchc
== 1 && dst
) {
561 abs_dst
= path_append(dst
, filename
);
563 abs_dst
= xstrdup(dst
);
566 abs_dst
= path_append(dst
, filename
);
568 abs_dst
= xstrdup(filename
);
572 printf("Fetching %s to %s\n", g
.gl_pathv
[i
], abs_dst
);
573 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
574 if (download_dir(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
575 pflag
|| global_pflag
, 1) == -1)
578 if (do_download(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
579 pflag
|| global_pflag
) == -1)
593 process_put(struct sftp_conn
*conn
, char *src
, char *dst
, char *pwd
,
594 int pflag
, int rflag
)
596 char *tmp_dst
= NULL
;
597 char *abs_dst
= NULL
;
598 char *tmp
= NULL
, *filename
= NULL
;
601 int i
, dst_is_dir
= 1;
605 tmp_dst
= xstrdup(dst
);
606 tmp_dst
= make_absolute(tmp_dst
, pwd
);
609 memset(&g
, 0, sizeof(g
));
610 debug3("Looking up %s", src
);
611 if (glob(src
, GLOB_NOCHECK
| GLOB_MARK
, NULL
, &g
)) {
612 error("File \"%s\" not found.", src
);
617 /* If we aren't fetching to pwd then stash this status for later */
619 dst_is_dir
= remote_is_dir(conn
, tmp_dst
);
621 /* If multiple matches, dst may be directory or unspecified */
622 if (g
.gl_matchc
> 1 && tmp_dst
&& !dst_is_dir
) {
623 error("Multiple paths match, but destination "
624 "\"%s\" is not a directory", tmp_dst
);
629 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
630 if (stat(g
.gl_pathv
[i
], &sb
) == -1) {
632 error("stat %s: %s", g
.gl_pathv
[i
], strerror(errno
));
636 tmp
= xstrdup(g
.gl_pathv
[i
]);
637 if ((filename
= basename(tmp
)) == NULL
) {
638 error("basename %s: %s", tmp
, strerror(errno
));
644 if (g
.gl_matchc
== 1 && tmp_dst
) {
645 /* If directory specified, append filename */
647 abs_dst
= path_append(tmp_dst
, filename
);
649 abs_dst
= xstrdup(tmp_dst
);
650 } else if (tmp_dst
) {
651 abs_dst
= path_append(tmp_dst
, filename
);
653 abs_dst
= make_absolute(xstrdup(filename
), pwd
);
657 printf("Uploading %s to %s\n", g
.gl_pathv
[i
], abs_dst
);
658 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
659 if (upload_dir(conn
, g
.gl_pathv
[i
], abs_dst
,
660 pflag
|| global_pflag
, 1) == -1)
663 if (do_upload(conn
, g
.gl_pathv
[i
], abs_dst
,
664 pflag
|| global_pflag
) == -1)
679 sdirent_comp(const void *aa
, const void *bb
)
681 SFTP_DIRENT
*a
= *(SFTP_DIRENT
**)aa
;
682 SFTP_DIRENT
*b
= *(SFTP_DIRENT
**)bb
;
683 int rmul
= sort_flag
& LS_REVERSE_SORT
? -1 : 1;
685 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
686 if (sort_flag
& LS_NAME_SORT
)
687 return (rmul
* strcmp(a
->filename
, b
->filename
));
688 else if (sort_flag
& LS_TIME_SORT
)
689 return (rmul
* NCMP(a
->a
.mtime
, b
->a
.mtime
));
690 else if (sort_flag
& LS_SIZE_SORT
)
691 return (rmul
* NCMP(a
->a
.size
, b
->a
.size
));
693 fatal("Unknown ls sort type");
696 /* sftp ls.1 replacement for directories */
698 do_ls_dir(struct sftp_conn
*conn
, char *path
, char *strip_path
, int lflag
)
701 u_int c
= 1, colspace
= 0, columns
= 1;
704 if ((n
= do_readdir(conn
, path
, &d
)) != 0)
707 if (!(lflag
& LS_SHORT_VIEW
)) {
708 u_int m
= 0, width
= 80;
712 /* Count entries for sort and find longest filename */
713 for (n
= 0; d
[n
] != NULL
; n
++) {
714 if (d
[n
]->filename
[0] != '.' || (lflag
& LS_SHOW_ALL
))
715 m
= MAX(m
, strlen(d
[n
]->filename
));
718 /* Add any subpath that also needs to be counted */
719 tmp
= path_strip(path
, strip_path
);
723 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
726 columns
= width
/ (m
+ 2);
727 columns
= MAX(columns
, 1);
728 colspace
= width
/ columns
;
729 colspace
= MIN(colspace
, width
);
732 if (lflag
& SORT_FLAGS
) {
733 for (n
= 0; d
[n
] != NULL
; n
++)
734 ; /* count entries */
735 sort_flag
= lflag
& (SORT_FLAGS
|LS_REVERSE_SORT
);
736 qsort(d
, n
, sizeof(*d
), sdirent_comp
);
739 for (n
= 0; d
[n
] != NULL
&& !interrupted
; n
++) {
742 if (d
[n
]->filename
[0] == '.' && !(lflag
& LS_SHOW_ALL
))
745 tmp
= path_append(path
, d
[n
]->filename
);
746 fname
= path_strip(tmp
, strip_path
);
749 if (lflag
& LS_LONG_VIEW
) {
750 if (lflag
& (LS_NUMERIC_VIEW
|LS_SI_UNITS
)) {
754 memset(&sb
, 0, sizeof(sb
));
755 attrib_to_stat(&d
[n
]->a
, &sb
);
756 lname
= ls_file(fname
, &sb
, 1,
757 (lflag
& LS_SI_UNITS
));
758 printf("%s\n", lname
);
761 printf("%s\n", d
[n
]->longname
);
763 printf("%-*s", colspace
, fname
);
774 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
777 free_sftp_dirents(d
);
781 /* sftp ls.1 replacement which handles path globs */
783 do_globbed_ls(struct sftp_conn
*conn
, char *path
, char *strip_path
,
791 u_int i
, c
= 1, colspace
= 0, columns
= 1, m
= 0, width
= 80;
793 memset(&g
, 0, sizeof(g
));
795 if (remote_glob(conn
, path
,
796 GLOB_MARK
|GLOB_NOCHECK
|GLOB_BRACE
|GLOB_KEEPSTAT
, NULL
, &g
) ||
797 (g
.gl_pathc
&& !g
.gl_matchc
)) {
800 error("Can't ls: \"%s\" not found", path
);
808 * If the glob returns a single match and it is a directory,
809 * then just list its contents.
811 if (g
.gl_matchc
== 1 && g
.gl_statv
[0] != NULL
&&
812 S_ISDIR(g
.gl_statv
[0]->st_mode
)) {
813 err
= do_ls_dir(conn
, g
.gl_pathv
[0], strip_path
, lflag
);
818 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
821 if (!(lflag
& LS_SHORT_VIEW
)) {
822 /* Count entries for sort and find longest filename */
823 for (i
= 0; g
.gl_pathv
[i
]; i
++)
824 m
= MAX(m
, strlen(g
.gl_pathv
[i
]));
826 columns
= width
/ (m
+ 2);
827 columns
= MAX(columns
, 1);
828 colspace
= width
/ columns
;
831 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++, a
= NULL
) {
832 fname
= path_strip(g
.gl_pathv
[i
], strip_path
);
833 if (lflag
& LS_LONG_VIEW
) {
834 if (g
.gl_statv
[i
] == NULL
) {
835 error("no stat information for %s", fname
);
838 lname
= ls_file(fname
, g
.gl_statv
[i
], 1,
839 (lflag
& LS_SI_UNITS
));
840 printf("%s\n", lname
);
843 printf("%-*s", colspace
, fname
);
853 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
864 do_df(struct sftp_conn
*conn
, char *path
, int hflag
, int iflag
)
866 struct sftp_statvfs st
;
867 char s_used
[FMT_SCALED_STRSIZE
];
868 char s_avail
[FMT_SCALED_STRSIZE
];
869 char s_root
[FMT_SCALED_STRSIZE
];
870 char s_total
[FMT_SCALED_STRSIZE
];
871 unsigned long long ffree
;
873 if (do_statvfs(conn
, path
, &st
, 1) == -1)
876 ffree
= st
.f_files
? (100 * (st
.f_files
- st
.f_ffree
) / st
.f_files
) : 0;
877 printf(" Inodes Used Avail "
878 "(root) %%Capacity\n");
879 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
880 (unsigned long long)st
.f_files
,
881 (unsigned long long)(st
.f_files
- st
.f_ffree
),
882 (unsigned long long)st
.f_favail
,
883 (unsigned long long)st
.f_ffree
, ffree
);
885 strlcpy(s_used
, "error", sizeof(s_used
));
886 strlcpy(s_avail
, "error", sizeof(s_avail
));
887 strlcpy(s_root
, "error", sizeof(s_root
));
888 strlcpy(s_total
, "error", sizeof(s_total
));
889 fmt_scaled((st
.f_blocks
- st
.f_bfree
) * st
.f_frsize
, s_used
);
890 fmt_scaled(st
.f_bavail
* st
.f_frsize
, s_avail
);
891 fmt_scaled(st
.f_bfree
* st
.f_frsize
, s_root
);
892 fmt_scaled(st
.f_blocks
* st
.f_frsize
, s_total
);
893 printf(" Size Used Avail (root) %%Capacity\n");
894 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
895 s_total
, s_used
, s_avail
, s_root
,
896 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
899 printf(" Size Used Avail "
900 "(root) %%Capacity\n");
901 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
902 (unsigned long long)(st
.f_frsize
* st
.f_blocks
/ 1024),
903 (unsigned long long)(st
.f_frsize
*
904 (st
.f_blocks
- st
.f_bfree
) / 1024),
905 (unsigned long long)(st
.f_frsize
* st
.f_bavail
/ 1024),
906 (unsigned long long)(st
.f_frsize
* st
.f_bfree
/ 1024),
907 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
914 * Undo escaping of glob sequences in place. Used to undo extra escaping
915 * applied in makeargv() when the string is destined for a function that
919 undo_glob_escape(char *s
)
954 * Split a string into an argument vector using sh(1)-style quoting,
955 * comment and escaping rules, but with some tweaks to handle glob(3)
957 * The "sloppy" flag allows for recovery from missing terminating quote, for
958 * use in parsing incomplete commandlines during tab autocompletion.
960 * Returns NULL on error or a NULL-terminated array of arguments.
962 * If "lastquote" is not NULL, the quoting character used for the last
963 * argument is placed in *lastquote ("\0", "'" or "\"").
965 * If "terminated" is not NULL, *terminated will be set to 1 when the
966 * last argument's quote has been properly terminated or 0 otherwise.
967 * This parameter is only of use if "sloppy" is set.
970 #define MAXARGLEN 8192
972 makeargv(const char *arg
, int *argcp
, int sloppy
, char *lastquote
,
977 static char argvs
[MAXARGLEN
];
978 static char *argv
[MAXARGS
+ 1];
979 enum { MA_START
, MA_SQUOTE
, MA_DQUOTE
, MA_UNQUOTED
} state
, q
;
982 if (strlen(arg
) > sizeof(argvs
) - 1) {
984 error("string too long");
987 if (terminated
!= NULL
)
989 if (lastquote
!= NULL
)
994 if (isspace(arg
[i
])) {
995 if (state
== MA_UNQUOTED
) {
996 /* Terminate current argument */
1000 } else if (state
!= MA_START
)
1001 argvs
[j
++] = arg
[i
];
1002 } else if (arg
[i
] == '"' || arg
[i
] == '\'') {
1003 q
= arg
[i
] == '"' ? MA_DQUOTE
: MA_SQUOTE
;
1004 if (state
== MA_START
) {
1005 argv
[argc
] = argvs
+ j
;
1007 if (lastquote
!= NULL
)
1008 *lastquote
= arg
[i
];
1009 } else if (state
== MA_UNQUOTED
)
1011 else if (state
== q
)
1012 state
= MA_UNQUOTED
;
1014 argvs
[j
++] = arg
[i
];
1015 } else if (arg
[i
] == '\\') {
1016 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1017 quot
= state
== MA_SQUOTE
? '\'' : '"';
1018 /* Unescape quote we are in */
1019 /* XXX support \n and friends? */
1020 if (arg
[i
+ 1] == quot
) {
1022 argvs
[j
++] = arg
[i
];
1023 } else if (arg
[i
+ 1] == '?' ||
1024 arg
[i
+ 1] == '[' || arg
[i
+ 1] == '*') {
1026 * Special case for sftp: append
1027 * double-escaped glob sequence -
1028 * glob will undo one level of
1029 * escaping. NB. string can grow here.
1031 if (j
>= sizeof(argvs
) - 5)
1032 goto args_too_longs
;
1034 argvs
[j
++] = arg
[i
++];
1036 argvs
[j
++] = arg
[i
];
1038 argvs
[j
++] = arg
[i
++];
1039 argvs
[j
++] = arg
[i
];
1042 if (state
== MA_START
) {
1043 argv
[argc
] = argvs
+ j
;
1044 state
= MA_UNQUOTED
;
1045 if (lastquote
!= NULL
)
1048 if (arg
[i
+ 1] == '?' || arg
[i
+ 1] == '[' ||
1049 arg
[i
+ 1] == '*' || arg
[i
+ 1] == '\\') {
1051 * Special case for sftp: append
1052 * escaped glob sequence -
1053 * glob will undo one level of
1056 argvs
[j
++] = arg
[i
++];
1057 argvs
[j
++] = arg
[i
];
1059 /* Unescape everything */
1060 /* XXX support \n and friends? */
1062 argvs
[j
++] = arg
[i
];
1065 } else if (arg
[i
] == '#') {
1066 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
)
1067 argvs
[j
++] = arg
[i
];
1070 } else if (arg
[i
] == '\0') {
1071 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1073 state
= MA_UNQUOTED
;
1074 if (terminated
!= NULL
)
1078 error("Unterminated quoted argument");
1082 if (state
== MA_UNQUOTED
) {
1088 if (state
== MA_START
) {
1089 argv
[argc
] = argvs
+ j
;
1090 state
= MA_UNQUOTED
;
1091 if (lastquote
!= NULL
)
1094 if ((state
== MA_SQUOTE
|| state
== MA_DQUOTE
) &&
1095 (arg
[i
] == '?' || arg
[i
] == '[' || arg
[i
] == '*')) {
1097 * Special case for sftp: escape quoted
1098 * glob(3) wildcards. NB. string can grow
1101 if (j
>= sizeof(argvs
) - 3)
1102 goto args_too_longs
;
1104 argvs
[j
++] = arg
[i
];
1106 argvs
[j
++] = arg
[i
];
1115 parse_args(const char **cpp
, int *pflag
, int *rflag
, int *lflag
, int *iflag
,
1116 int *hflag
, int *sflag
, unsigned long *n_arg
, char **path1
, char **path2
)
1118 const char *cmd
, *cp
= *cpp
;
1122 int i
, cmdnum
, optidx
, argc
;
1124 /* Skip leading whitespace */
1125 cp
= cp
+ strspn(cp
, WHITESPACE
);
1127 /* Check for leading '-' (disable error processing) */
1132 cp
= cp
+ strspn(cp
, WHITESPACE
);
1135 /* Ignore blank lines and lines which begin with comment '#' char */
1136 if (*cp
== '\0' || *cp
== '#')
1139 if ((argv
= makeargv(cp
, &argc
, 0, NULL
, NULL
)) == NULL
)
1142 /* Figure out which command we have */
1143 for (i
= 0; cmds
[i
].c
!= NULL
; i
++) {
1144 if (strcasecmp(cmds
[i
].c
, argv
[0]) == 0)
1154 } else if (cmdnum
== -1) {
1155 error("Invalid command.");
1159 /* Get arguments and parse flags */
1160 *lflag
= *pflag
= *rflag
= *hflag
= *n_arg
= 0;
1161 *path1
= *path2
= NULL
;
1166 if ((optidx
= parse_getput_flags(cmd
, argv
, argc
,
1167 pflag
, rflag
)) == -1)
1169 /* Get first pathname (mandatory) */
1170 if (argc
- optidx
< 1) {
1171 error("You must specify at least one path after a "
1172 "%s command.", cmd
);
1175 *path1
= xstrdup(argv
[optidx
]);
1176 /* Get second pathname (optional) */
1177 if (argc
- optidx
> 1) {
1178 *path2
= xstrdup(argv
[optidx
+ 1]);
1179 /* Destination is not globbed */
1180 undo_glob_escape(*path2
);
1184 if ((optidx
= parse_link_flags(cmd
, argv
, argc
, sflag
)) == -1)
1188 if (argc
- optidx
< 2) {
1189 error("You must specify two paths after a %s "
1193 *path1
= xstrdup(argv
[optidx
]);
1194 *path2
= xstrdup(argv
[optidx
+ 1]);
1195 /* Paths are not globbed */
1196 undo_glob_escape(*path1
);
1197 undo_glob_escape(*path2
);
1205 /* Get pathname (mandatory) */
1206 if (argc
- optidx
< 1) {
1207 error("You must specify a path after a %s command.",
1211 *path1
= xstrdup(argv
[optidx
]);
1212 /* Only "rm" globs */
1214 undo_glob_escape(*path1
);
1217 if ((optidx
= parse_df_flags(cmd
, argv
, argc
, hflag
,
1220 /* Default to current directory if no path specified */
1221 if (argc
- optidx
< 1)
1224 *path1
= xstrdup(argv
[optidx
]);
1225 undo_glob_escape(*path1
);
1229 if ((optidx
= parse_ls_flags(argv
, argc
, lflag
)) == -1)
1231 /* Path is optional */
1232 if (argc
- optidx
> 0)
1233 *path1
= xstrdup(argv
[optidx
]);
1236 /* Skip ls command and following whitespace */
1237 cp
= cp
+ strlen(cmd
) + strspn(cp
, WHITESPACE
);
1239 /* Uses the rest of the line */
1246 /* Get numeric arg (mandatory) */
1247 if (argc
- optidx
< 1)
1250 l
= strtol(argv
[optidx
], &cp2
, base
);
1251 if (cp2
== argv
[optidx
] || *cp2
!= '\0' ||
1252 ((l
== LONG_MIN
|| l
== LONG_MAX
) && errno
== ERANGE
) ||
1255 error("You must supply a numeric argument "
1256 "to the %s command.", cmd
);
1260 if (cmdnum
== I_LUMASK
)
1262 /* Get pathname (mandatory) */
1263 if (argc
- optidx
< 2) {
1264 error("You must specify a path after a %s command.",
1268 *path1
= xstrdup(argv
[optidx
+ 1]);
1278 fatal("Command not implemented");
1286 parse_dispatch_command(struct sftp_conn
*conn
, const char *cmd
, char **pwd
,
1289 char *path1
, *path2
, *tmp
;
1290 int pflag
= 0, rflag
= 0, lflag
= 0, iflag
= 0, hflag
= 0, sflag
= 0;
1292 unsigned long n_arg
= 0;
1294 char path_buf
[MAXPATHLEN
];
1298 path1
= path2
= NULL
;
1299 cmdnum
= parse_args(&cmd
, &pflag
, &rflag
, &lflag
, &iflag
, &hflag
,
1300 &sflag
, &n_arg
, &path1
, &path2
);
1305 memset(&g
, 0, sizeof(g
));
1307 /* Perform command */
1313 /* Unrecognized command */
1317 err
= process_get(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1320 err
= process_put(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1323 path1
= make_absolute(path1
, *pwd
);
1324 path2
= make_absolute(path2
, *pwd
);
1325 err
= do_rename(conn
, path1
, path2
);
1330 path1
= make_absolute(path1
, *pwd
);
1331 path2
= make_absolute(path2
, *pwd
);
1332 err
= (sflag
? do_symlink
: do_hardlink
)(conn
, path1
, path2
);
1335 path1
= make_absolute(path1
, *pwd
);
1336 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1337 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1338 printf("Removing %s\n", g
.gl_pathv
[i
]);
1339 err
= do_rm(conn
, g
.gl_pathv
[i
]);
1340 if (err
!= 0 && err_abort
)
1345 path1
= make_absolute(path1
, *pwd
);
1347 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1349 err
= do_mkdir(conn
, path1
, &a
, 1);
1352 path1
= make_absolute(path1
, *pwd
);
1353 err
= do_rmdir(conn
, path1
);
1356 path1
= make_absolute(path1
, *pwd
);
1357 if ((tmp
= do_realpath(conn
, path1
)) == NULL
) {
1361 if ((aa
= do_stat(conn
, tmp
, 0)) == NULL
) {
1366 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
)) {
1367 error("Can't change directory: Can't check target");
1372 if (!S_ISDIR(aa
->perm
)) {
1373 error("Can't change directory: \"%s\" is not "
1374 "a directory", tmp
);
1384 do_ls_dir(conn
, *pwd
, *pwd
, lflag
);
1388 /* Strip pwd off beginning of non-absolute paths */
1393 path1
= make_absolute(path1
, *pwd
);
1394 err
= do_globbed_ls(conn
, path1
, tmp
, lflag
);
1397 /* Default to current directory if no path specified */
1399 path1
= xstrdup(*pwd
);
1400 path1
= make_absolute(path1
, *pwd
);
1401 err
= do_df(conn
, path1
, hflag
, iflag
);
1404 if (chdir(path1
) == -1) {
1405 error("Couldn't change local directory to "
1406 "\"%s\": %s", path1
, strerror(errno
));
1411 if (mkdir(path1
, 0777) == -1) {
1412 error("Couldn't create local directory "
1413 "\"%s\": %s", path1
, strerror(errno
));
1421 local_do_shell(cmd
);
1425 printf("Local umask: %03lo\n", n_arg
);
1428 path1
= make_absolute(path1
, *pwd
);
1430 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1432 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1433 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1434 printf("Changing mode on %s\n", g
.gl_pathv
[i
]);
1435 err
= do_setstat(conn
, g
.gl_pathv
[i
], &a
);
1436 if (err
!= 0 && err_abort
)
1442 path1
= make_absolute(path1
, *pwd
);
1443 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1444 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1445 if (!(aa
= do_stat(conn
, g
.gl_pathv
[i
], 0))) {
1452 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_UIDGID
)) {
1453 error("Can't get current ownership of "
1454 "remote file \"%s\"", g
.gl_pathv
[i
]);
1461 aa
->flags
&= SSH2_FILEXFER_ATTR_UIDGID
;
1462 if (cmdnum
== I_CHOWN
) {
1463 printf("Changing owner on %s\n", g
.gl_pathv
[i
]);
1466 printf("Changing group on %s\n", g
.gl_pathv
[i
]);
1469 err
= do_setstat(conn
, g
.gl_pathv
[i
], aa
);
1470 if (err
!= 0 && err_abort
)
1475 printf("Remote working directory: %s\n", *pwd
);
1478 if (!getcwd(path_buf
, sizeof(path_buf
))) {
1479 error("Couldn't get local cwd: %s", strerror(errno
));
1483 printf("Local working directory: %s\n", path_buf
);
1486 /* Processed below */
1492 printf("SFTP protocol version %u\n", sftp_proto_version(conn
));
1495 showprogress
= !showprogress
;
1497 printf("Progress meter enabled\n");
1499 printf("Progress meter disabled\n");
1502 fatal("%d is not implemented", cmdnum
);
1512 /* If an unignored error occurs in batch mode we should abort. */
1513 if (err_abort
&& err
!= 0)
1515 else if (cmdnum
== I_QUIT
)
1523 prompt(EditLine
*el
)
1528 /* Display entries in 'list' after skipping the first 'len' chars */
1530 complete_display(char **list
, u_int len
)
1532 u_int y
, m
= 0, width
= 80, columns
= 1, colspace
= 0, llen
;
1536 /* Count entries for sort and find longest */
1537 for (y
= 0; list
[y
]; y
++)
1538 m
= MAX(m
, strlen(list
[y
]));
1540 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
1543 m
= m
> len
? m
- len
: 0;
1544 columns
= width
/ (m
+ 2);
1545 columns
= MAX(columns
, 1);
1546 colspace
= width
/ columns
;
1547 colspace
= MIN(colspace
, width
);
1551 for (y
= 0; list
[y
]; y
++) {
1552 llen
= strlen(list
[y
]);
1553 tmp
= llen
> len
? list
[y
] + len
: "";
1554 printf("%-*s", colspace
, tmp
);
1565 * Given a "list" of words that begin with a common prefix of "word",
1566 * attempt to find an autocompletion to extends "word" by the next
1567 * characters common to all entries in "list".
1570 complete_ambiguous(const char *word
, char **list
, size_t count
)
1576 u_int y
, matchlen
= strlen(list
[0]);
1578 /* Find length of common stem */
1579 for (y
= 1; list
[y
]; y
++) {
1582 for (x
= 0; x
< matchlen
; x
++)
1583 if (list
[0][x
] != list
[y
][x
])
1589 if (matchlen
> strlen(word
)) {
1590 char *tmp
= xstrdup(list
[0]);
1592 tmp
[matchlen
] = '\0';
1597 return xstrdup(word
);
1600 /* Autocomplete a sftp command */
1602 complete_cmd_parse(EditLine
*el
, char *cmd
, int lastarg
, char quote
,
1605 u_int y
, count
= 0, cmdlen
, tmplen
;
1606 char *tmp
, **list
, argterm
[3];
1609 list
= xcalloc((sizeof(cmds
) / sizeof(*cmds
)) + 1, sizeof(char *));
1611 /* No command specified: display all available commands */
1613 for (y
= 0; cmds
[y
].c
; y
++)
1614 list
[count
++] = xstrdup(cmds
[y
].c
);
1617 complete_display(list
, 0);
1619 for (y
= 0; list
[y
] != NULL
; y
++)
1625 /* Prepare subset of commands that start with "cmd" */
1626 cmdlen
= strlen(cmd
);
1627 for (y
= 0; cmds
[y
].c
; y
++) {
1628 if (!strncasecmp(cmd
, cmds
[y
].c
, cmdlen
))
1629 list
[count
++] = xstrdup(cmds
[y
].c
);
1636 /* Complete ambigious command */
1637 tmp
= complete_ambiguous(cmd
, list
, count
);
1639 complete_display(list
, 0);
1641 for (y
= 0; list
[y
]; y
++)
1646 tmplen
= strlen(tmp
);
1647 cmdlen
= strlen(cmd
);
1648 /* If cmd may be extended then do so */
1649 if (tmplen
> cmdlen
)
1650 if (el_insertstr(el
, tmp
+ cmdlen
) == -1)
1651 fatal("el_insertstr failed.");
1653 /* Terminate argument cleanly */
1657 argterm
[y
++] = quote
;
1658 if (lastarg
|| *(lf
->cursor
) != ' ')
1661 if (y
> 0 && el_insertstr(el
, argterm
) == -1)
1662 fatal("el_insertstr failed.");
1671 * Determine whether a particular sftp command's arguments (if any)
1672 * represent local or remote files.
1675 complete_is_remote(char *cmd
) {
1681 for (i
= 0; cmds
[i
].c
; i
++) {
1682 if (!strncasecmp(cmd
, cmds
[i
].c
, strlen(cmds
[i
].c
)))
1689 /* Autocomplete a filename "file" */
1691 complete_match(EditLine
*el
, struct sftp_conn
*conn
, char *remote_path
,
1692 char *file
, int remote
, int lastarg
, char quote
, int terminated
)
1695 char *tmp
, *tmp2
, ins
[3];
1696 u_int i
, hadglob
, pwdlen
, len
, tmplen
, filelen
;
1699 /* Glob from "file" location */
1703 xasprintf(&tmp
, "%s*", file
);
1705 memset(&g
, 0, sizeof(g
));
1706 if (remote
!= LOCAL
) {
1707 tmp
= make_absolute(tmp
, remote_path
);
1708 remote_glob(conn
, tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1710 glob(tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1712 /* Determine length of pwd so we can trim completion display */
1713 for (hadglob
= tmplen
= pwdlen
= 0; tmp
[tmplen
] != 0; tmplen
++) {
1714 /* Terminate counting on first unescaped glob metacharacter */
1715 if (tmp
[tmplen
] == '*' || tmp
[tmplen
] == '?') {
1716 if (tmp
[tmplen
] != '*' || tmp
[tmplen
+ 1] != '\0')
1720 if (tmp
[tmplen
] == '\\' && tmp
[tmplen
+ 1] != '\0')
1722 if (tmp
[tmplen
] == '/')
1723 pwdlen
= tmplen
+ 1; /* track last seen '/' */
1727 if (g
.gl_matchc
== 0)
1730 if (g
.gl_matchc
> 1)
1731 complete_display(g
.gl_pathv
, pwdlen
);
1734 /* Don't try to extend globs */
1735 if (file
== NULL
|| hadglob
)
1738 tmp2
= complete_ambiguous(file
, g
.gl_pathv
, g
.gl_matchc
);
1739 tmp
= path_strip(tmp2
, remote_path
);
1745 tmplen
= strlen(tmp
);
1746 filelen
= strlen(file
);
1748 if (tmplen
> filelen
) {
1749 tmp2
= tmp
+ filelen
;
1751 /* quote argument on way out */
1752 for (i
= 0; i
< len
; i
++) {
1763 if (quote
== '\0' || tmp2
[i
] == quote
) {
1764 if (el_insertstr(el
, ins
) == -1)
1765 fatal("el_insertstr "
1771 if (el_insertstr(el
, ins
+ 1) == -1)
1772 fatal("el_insertstr failed.");
1779 if (g
.gl_matchc
== 1) {
1783 if (*(lf
->cursor
- 1) != '/' &&
1784 (lastarg
|| *(lf
->cursor
) != ' '))
1787 if (i
> 0 && el_insertstr(el
, ins
) == -1)
1788 fatal("el_insertstr failed.");
1797 /* tab-completion hook function, called via libedit */
1798 static unsigned char
1799 complete(EditLine
*el
, int ch
)
1801 char **argv
, *line
, quote
;
1802 u_int argc
, carg
, cursor
, len
, terminated
, ret
= CC_ERROR
;
1804 struct complete_ctx
*complete_ctx
;
1807 if (el_get(el
, EL_CLIENTDATA
, (void**)&complete_ctx
) != 0)
1808 fatal("%s: el_get failed", __func__
);
1810 /* Figure out which argument the cursor points to */
1811 cursor
= lf
->cursor
- lf
->buffer
;
1812 line
= (char *)xmalloc(cursor
+ 1);
1813 memcpy(line
, lf
->buffer
, cursor
);
1814 line
[cursor
] = '\0';
1815 argv
= makeargv(line
, &carg
, 1, "e
, &terminated
);
1818 /* Get all the arguments on the line */
1819 len
= lf
->lastchar
- lf
->buffer
;
1820 line
= (char *)xmalloc(len
+ 1);
1821 memcpy(line
, lf
->buffer
, len
);
1823 argv
= makeargv(line
, &argc
, 1, NULL
, NULL
);
1825 /* Ensure cursor is at EOL or a argument boundary */
1826 if (line
[cursor
] != ' ' && line
[cursor
] != '\0' &&
1827 line
[cursor
] != '\n') {
1833 /* Show all available commands */
1834 complete_cmd_parse(el
, NULL
, argc
== carg
, '\0', 1);
1836 } else if (carg
== 1 && cursor
> 0 && line
[cursor
- 1] != ' ') {
1837 /* Handle the command parsing */
1838 if (complete_cmd_parse(el
, argv
[0], argc
== carg
,
1839 quote
, terminated
) != 0)
1841 } else if (carg
>= 1) {
1842 /* Handle file parsing */
1843 int remote
= complete_is_remote(argv
[0]);
1844 char *filematch
= NULL
;
1846 if (carg
> 1 && line
[cursor
-1] != ' ')
1847 filematch
= argv
[carg
- 1];
1850 complete_match(el
, complete_ctx
->conn
,
1851 *complete_ctx
->remote_pathp
, filematch
,
1852 remote
, carg
== argc
, quote
, terminated
) != 0)
1859 #endif /* USE_LIBEDIT */
1862 interactive_loop(struct sftp_conn
*conn
, char *file1
, char *file2
)
1867 int err
, interactive
;
1868 EditLine
*el
= NULL
;
1872 extern char *__progname
;
1873 struct complete_ctx complete_ctx
;
1875 if (!batchmode
&& isatty(STDIN_FILENO
)) {
1876 if ((el
= el_init(__progname
, stdin
, stdout
, stderr
)) == NULL
)
1877 fatal("Couldn't initialise editline");
1878 if ((hl
= history_init()) == NULL
)
1879 fatal("Couldn't initialise editline history");
1880 history(hl
, &hev
, H_SETSIZE
, 100);
1881 el_set(el
, EL_HIST
, history
, hl
);
1883 el_set(el
, EL_PROMPT
, prompt
);
1884 el_set(el
, EL_EDITOR
, "emacs");
1885 el_set(el
, EL_TERMINAL
, NULL
);
1886 el_set(el
, EL_SIGNAL
, 1);
1887 el_source(el
, NULL
);
1889 /* Tab Completion */
1890 el_set(el
, EL_ADDFN
, "ftp-complete",
1891 "Context sensitive argument completion", complete
);
1892 complete_ctx
.conn
= conn
;
1893 complete_ctx
.remote_pathp
= &remote_path
;
1894 el_set(el
, EL_CLIENTDATA
, (void*)&complete_ctx
);
1895 el_set(el
, EL_BIND
, "^I", "ftp-complete", NULL
);
1897 #endif /* USE_LIBEDIT */
1899 remote_path
= do_realpath(conn
, ".");
1900 if (remote_path
== NULL
)
1903 if (file1
!= NULL
) {
1904 dir
= xstrdup(file1
);
1905 dir
= make_absolute(dir
, remote_path
);
1907 if (remote_is_dir(conn
, dir
) && file2
== NULL
) {
1908 printf("Changing to: %s\n", dir
);
1909 snprintf(cmd
, sizeof cmd
, "cd \"%s\"", dir
);
1910 if (parse_dispatch_command(conn
, cmd
,
1911 &remote_path
, 1) != 0) {
1919 snprintf(cmd
, sizeof cmd
, "get %s", dir
);
1921 snprintf(cmd
, sizeof cmd
, "get %s %s", dir
,
1924 err
= parse_dispatch_command(conn
, cmd
,
1934 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1935 setvbuf(stdout
, NULL
, _IOLBF
, 0);
1936 setvbuf(infile
, NULL
, _IOLBF
, 0);
1942 interactive
= !batchmode
&& isatty(STDIN_FILENO
);
1947 signal(SIGINT
, SIG_IGN
);
1952 if (fgets(cmd
, sizeof(cmd
), infile
) == NULL
) {
1957 if (!interactive
) { /* Echo command */
1958 printf("sftp> %s", cmd
);
1959 if (strlen(cmd
) > 0 &&
1960 cmd
[strlen(cmd
) - 1] != '\n')
1968 if ((line
= el_gets(el
, &count
)) == NULL
||
1973 history(hl
, &hev
, H_ENTER
, line
);
1974 if (strlcpy(cmd
, line
, sizeof(cmd
)) >= sizeof(cmd
)) {
1975 fprintf(stderr
, "Error: input line too long\n");
1978 #endif /* USE_LIBEDIT */
1981 cp
= strrchr(cmd
, '\n');
1985 /* Handle user interrupts gracefully during commands */
1987 signal(SIGINT
, cmd_interrupt
);
1989 err
= parse_dispatch_command(conn
, cmd
, &remote_path
,
2000 #endif /* USE_LIBEDIT */
2002 /* err == 1 signifies normal "quit" exit */
2003 return (err
>= 0 ? 0 : -1);
2007 connect_to_server(char *path
, char **args
, int *in
, int *out
)
2012 int pin
[2], pout
[2];
2014 if ((pipe(pin
) == -1) || (pipe(pout
) == -1))
2015 fatal("pipe: %s", strerror(errno
));
2020 #else /* USE_PIPES */
2023 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) == -1)
2024 fatal("socketpair: %s", strerror(errno
));
2025 *in
= *out
= inout
[0];
2026 c_in
= c_out
= inout
[1];
2027 #endif /* USE_PIPES */
2029 if ((sshpid
= fork()) == -1)
2030 fatal("fork: %s", strerror(errno
));
2031 else if (sshpid
== 0) {
2032 if ((dup2(c_in
, STDIN_FILENO
) == -1) ||
2033 (dup2(c_out
, STDOUT_FILENO
) == -1)) {
2034 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
2043 * The underlying ssh is in the same process group, so we must
2044 * ignore SIGINT if we want to gracefully abort commands,
2045 * otherwise the signal will make it to the ssh process and
2046 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2047 * underlying ssh, it must *not* ignore that signal.
2049 signal(SIGINT
, SIG_IGN
);
2050 signal(SIGTERM
, SIG_DFL
);
2052 fprintf(stderr
, "exec: %s: %s\n", path
, strerror(errno
));
2056 signal(SIGTERM
, killchild
);
2057 signal(SIGINT
, killchild
);
2058 signal(SIGHUP
, killchild
);
2066 extern char *__progname
;
2069 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2070 " [-D sftp_server_path] [-F ssh_config] "
2071 "[-i identity_file] [-l limit]\n"
2072 " [-o ssh_option] [-P port] [-R num_requests] "
2074 " [-s subsystem | sftp_server] host\n"
2075 " %s [user@]host[:file ...]\n"
2076 " %s [user@]host[:dir[/]]\n"
2077 " %s -b batchfile [user@]host\n",
2078 __progname
, __progname
, __progname
, __progname
);
2083 main(int argc
, char **argv
)
2085 int in
, out
, ch
, err
;
2086 char *host
= NULL
, *userhost
, *cp
, *file2
= NULL
;
2087 int debug_level
= 0, sshver
= 2;
2088 char *file1
= NULL
, *sftp_server
= NULL
;
2089 char *ssh_program
= _PATH_SSH_PROGRAM
, *sftp_direct
= NULL
;
2091 LogLevel ll
= SYSLOG_LEVEL_INFO
;
2094 extern char *optarg
;
2095 struct sftp_conn
*conn
;
2096 size_t copy_buffer_len
= DEFAULT_COPY_BUFLEN
;
2097 size_t num_requests
= DEFAULT_NUM_REQUESTS
;
2098 long long limit_kbps
= 0;
2100 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2103 __progname
= ssh_get_progname(argv
[0]);
2104 memset(&args
, '\0', sizeof(args
));
2106 addargs(&args
, "%s", ssh_program
);
2107 addargs(&args
, "-oForwardX11 no");
2108 addargs(&args
, "-oForwardAgent no");
2109 addargs(&args
, "-oPermitLocalCommand no");
2110 addargs(&args
, "-oClearAllForwardings yes");
2112 ll
= SYSLOG_LEVEL_INFO
;
2115 while ((ch
= getopt(argc
, argv
,
2116 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2118 /* Passed through to ssh(1) */
2122 addargs(&args
, "-%c", ch
);
2124 /* Passed through to ssh(1) with argument */
2129 addargs(&args
, "-%c", ch
);
2130 addargs(&args
, "%s", optarg
);
2134 addargs(&args
, "-%c", ch
);
2137 addargs(&args
, "-oPort %s", optarg
);
2140 if (debug_level
< 3) {
2141 addargs(&args
, "-v");
2142 ll
= SYSLOG_LEVEL_DEBUG1
+ debug_level
;
2148 if (sftp_server
== NULL
)
2149 sftp_server
= _PATH_SFTP_SERVER
;
2155 copy_buffer_len
= strtol(optarg
, &cp
, 10);
2156 if (copy_buffer_len
== 0 || *cp
!= '\0')
2157 fatal("Invalid buffer size \"%s\"", optarg
);
2161 fatal("Batch file already specified.");
2163 /* Allow "-" as stdin */
2164 if (strcmp(optarg
, "-") != 0 &&
2165 (infile
= fopen(optarg
, "r")) == NULL
)
2166 fatal("%s (%s).", strerror(errno
), optarg
);
2169 addargs(&args
, "-obatchmode yes");
2175 sftp_direct
= optarg
;
2178 limit_kbps
= strtonum(optarg
, 1, 100 * 1024 * 1024,
2182 limit_kbps
*= 1024; /* kbps */
2188 num_requests
= strtol(optarg
, &cp
, 10);
2189 if (num_requests
== 0 || *cp
!= '\0')
2190 fatal("Invalid number of requests \"%s\"",
2194 sftp_server
= optarg
;
2197 ssh_program
= optarg
;
2198 replacearg(&args
, 0, "%s", ssh_program
);
2206 if (!isatty(STDERR_FILENO
))
2209 log_init(argv
[0], ll
, SYSLOG_FACILITY_USER
, 1);
2211 if (sftp_direct
== NULL
) {
2212 if (optind
== argc
|| argc
> (optind
+ 2))
2215 userhost
= xstrdup(argv
[optind
]);
2216 file2
= argv
[optind
+1];
2218 if ((host
= strrchr(userhost
, '@')) == NULL
)
2223 fprintf(stderr
, "Missing username\n");
2226 addargs(&args
, "-l");
2227 addargs(&args
, "%s", userhost
);
2230 if ((cp
= colon(host
)) != NULL
) {
2235 host
= cleanhostname(host
);
2237 fprintf(stderr
, "Missing hostname\n");
2241 addargs(&args
, "-oProtocol %d", sshver
);
2243 /* no subsystem if the server-spec contains a '/' */
2244 if (sftp_server
== NULL
|| strchr(sftp_server
, '/') == NULL
)
2245 addargs(&args
, "-s");
2247 addargs(&args
, "--");
2248 addargs(&args
, "%s", host
);
2249 addargs(&args
, "%s", (sftp_server
!= NULL
?
2250 sftp_server
: "sftp"));
2252 connect_to_server(ssh_program
, args
.list
, &in
, &out
);
2255 addargs(&args
, "sftp-server");
2257 connect_to_server(sftp_direct
, args
.list
, &in
, &out
);
2261 conn
= do_init(in
, out
, copy_buffer_len
, num_requests
, limit_kbps
);
2263 fatal("Couldn't initialise connection to server");
2266 if (sftp_direct
== NULL
)
2267 fprintf(stderr
, "Connected to %s.\n", host
);
2269 fprintf(stderr
, "Attached to %s.\n", sftp_direct
);
2272 err
= interactive_loop(conn
, file1
, file2
);
2274 #if !defined(USE_PIPES)
2275 shutdown(in
, SHUT_RDWR
);
2276 shutdown(out
, SHUT_RDWR
);
2284 while (waitpid(sshpid
, NULL
, 0) == -1)
2286 fatal("Couldn't wait for ssh process: %s",
2289 exit(err
== 0 ? 0 : 1);