1 /* $OpenBSD: sftp.c,v 1.125 2010/06/18 00:58:39 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 */
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
)
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
,
762 u_int i
, c
= 1, colspace
= 0, columns
= 1;
765 memset(&g
, 0, sizeof(g
));
767 if (remote_glob(conn
, path
, GLOB_MARK
|GLOB_NOCHECK
|GLOB_BRACE
,
768 NULL
, &g
) || (g
.gl_pathc
&& !g
.gl_matchc
)) {
771 error("Can't ls: \"%s\" not found", path
);
779 * If the glob returns a single match and it is a directory,
780 * then just list its contents.
782 if (g
.gl_matchc
== 1) {
783 if ((a
= do_lstat(conn
, g
.gl_pathv
[0], 1)) == NULL
) {
787 if ((a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
) &&
791 err
= do_ls_dir(conn
, g
.gl_pathv
[0], strip_path
, lflag
);
797 if (!(lflag
& LS_SHORT_VIEW
)) {
798 u_int m
= 0, width
= 80;
801 /* Count entries for sort and find longest filename */
802 for (i
= 0; g
.gl_pathv
[i
]; i
++)
803 m
= MAX(m
, strlen(g
.gl_pathv
[i
]));
805 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
808 columns
= width
/ (m
+ 2);
809 columns
= MAX(columns
, 1);
810 colspace
= width
/ columns
;
813 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++, a
= NULL
) {
816 fname
= path_strip(g
.gl_pathv
[i
], strip_path
);
818 if (lflag
& LS_LONG_VIEW
) {
823 * XXX: this is slow - 1 roundtrip per path
824 * A solution to this is to fork glob() and
825 * build a sftp specific version which keeps the
826 * attribs (which currently get thrown away)
827 * that the server returns as well as the filenames.
829 memset(&sb
, 0, sizeof(sb
));
831 a
= do_lstat(conn
, g
.gl_pathv
[i
], 1);
833 attrib_to_stat(a
, &sb
);
834 lname
= ls_file(fname
, &sb
, 1, (lflag
& LS_SI_UNITS
));
835 printf("%s\n", lname
);
838 printf("%-*s", colspace
, fname
);
848 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
859 do_df(struct sftp_conn
*conn
, char *path
, int hflag
, int iflag
)
861 struct sftp_statvfs st
;
862 char s_used
[FMT_SCALED_STRSIZE
];
863 char s_avail
[FMT_SCALED_STRSIZE
];
864 char s_root
[FMT_SCALED_STRSIZE
];
865 char s_total
[FMT_SCALED_STRSIZE
];
866 unsigned long long ffree
;
868 if (do_statvfs(conn
, path
, &st
, 1) == -1)
871 ffree
= st
.f_files
? (100 * (st
.f_files
- st
.f_ffree
) / st
.f_files
) : 0;
872 printf(" Inodes Used Avail "
873 "(root) %%Capacity\n");
874 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
875 (unsigned long long)st
.f_files
,
876 (unsigned long long)(st
.f_files
- st
.f_ffree
),
877 (unsigned long long)st
.f_favail
,
878 (unsigned long long)st
.f_ffree
, ffree
);
880 strlcpy(s_used
, "error", sizeof(s_used
));
881 strlcpy(s_avail
, "error", sizeof(s_avail
));
882 strlcpy(s_root
, "error", sizeof(s_root
));
883 strlcpy(s_total
, "error", sizeof(s_total
));
884 fmt_scaled((st
.f_blocks
- st
.f_bfree
) * st
.f_frsize
, s_used
);
885 fmt_scaled(st
.f_bavail
* st
.f_frsize
, s_avail
);
886 fmt_scaled(st
.f_bfree
* st
.f_frsize
, s_root
);
887 fmt_scaled(st
.f_blocks
* st
.f_frsize
, s_total
);
888 printf(" Size Used Avail (root) %%Capacity\n");
889 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
890 s_total
, s_used
, s_avail
, s_root
,
891 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
894 printf(" Size Used Avail "
895 "(root) %%Capacity\n");
896 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
897 (unsigned long long)(st
.f_frsize
* st
.f_blocks
/ 1024),
898 (unsigned long long)(st
.f_frsize
*
899 (st
.f_blocks
- st
.f_bfree
) / 1024),
900 (unsigned long long)(st
.f_frsize
* st
.f_bavail
/ 1024),
901 (unsigned long long)(st
.f_frsize
* st
.f_bfree
/ 1024),
902 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
909 * Undo escaping of glob sequences in place. Used to undo extra escaping
910 * applied in makeargv() when the string is destined for a function that
914 undo_glob_escape(char *s
)
949 * Split a string into an argument vector using sh(1)-style quoting,
950 * comment and escaping rules, but with some tweaks to handle glob(3)
952 * The "sloppy" flag allows for recovery from missing terminating quote, for
953 * use in parsing incomplete commandlines during tab autocompletion.
955 * Returns NULL on error or a NULL-terminated array of arguments.
957 * If "lastquote" is not NULL, the quoting character used for the last
958 * argument is placed in *lastquote ("\0", "'" or "\"").
960 * If "terminated" is not NULL, *terminated will be set to 1 when the
961 * last argument's quote has been properly terminated or 0 otherwise.
962 * This parameter is only of use if "sloppy" is set.
965 #define MAXARGLEN 8192
967 makeargv(const char *arg
, int *argcp
, int sloppy
, char *lastquote
,
972 static char argvs
[MAXARGLEN
];
973 static char *argv
[MAXARGS
+ 1];
974 enum { MA_START
, MA_SQUOTE
, MA_DQUOTE
, MA_UNQUOTED
} state
, q
;
977 if (strlen(arg
) > sizeof(argvs
) - 1) {
979 error("string too long");
982 if (terminated
!= NULL
)
984 if (lastquote
!= NULL
)
989 if (isspace(arg
[i
])) {
990 if (state
== MA_UNQUOTED
) {
991 /* Terminate current argument */
995 } else if (state
!= MA_START
)
997 } else if (arg
[i
] == '"' || arg
[i
] == '\'') {
998 q
= arg
[i
] == '"' ? MA_DQUOTE
: MA_SQUOTE
;
999 if (state
== MA_START
) {
1000 argv
[argc
] = argvs
+ j
;
1002 if (lastquote
!= NULL
)
1003 *lastquote
= arg
[i
];
1004 } else if (state
== MA_UNQUOTED
)
1006 else if (state
== q
)
1007 state
= MA_UNQUOTED
;
1009 argvs
[j
++] = arg
[i
];
1010 } else if (arg
[i
] == '\\') {
1011 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1012 quot
= state
== MA_SQUOTE
? '\'' : '"';
1013 /* Unescape quote we are in */
1014 /* XXX support \n and friends? */
1015 if (arg
[i
+ 1] == quot
) {
1017 argvs
[j
++] = arg
[i
];
1018 } else if (arg
[i
+ 1] == '?' ||
1019 arg
[i
+ 1] == '[' || arg
[i
+ 1] == '*') {
1021 * Special case for sftp: append
1022 * double-escaped glob sequence -
1023 * glob will undo one level of
1024 * escaping. NB. string can grow here.
1026 if (j
>= sizeof(argvs
) - 5)
1027 goto args_too_longs
;
1029 argvs
[j
++] = arg
[i
++];
1031 argvs
[j
++] = arg
[i
];
1033 argvs
[j
++] = arg
[i
++];
1034 argvs
[j
++] = arg
[i
];
1037 if (state
== MA_START
) {
1038 argv
[argc
] = argvs
+ j
;
1039 state
= MA_UNQUOTED
;
1040 if (lastquote
!= NULL
)
1043 if (arg
[i
+ 1] == '?' || arg
[i
+ 1] == '[' ||
1044 arg
[i
+ 1] == '*' || arg
[i
+ 1] == '\\') {
1046 * Special case for sftp: append
1047 * escaped glob sequence -
1048 * glob will undo one level of
1051 argvs
[j
++] = arg
[i
++];
1052 argvs
[j
++] = arg
[i
];
1054 /* Unescape everything */
1055 /* XXX support \n and friends? */
1057 argvs
[j
++] = arg
[i
];
1060 } else if (arg
[i
] == '#') {
1061 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
)
1062 argvs
[j
++] = arg
[i
];
1065 } else if (arg
[i
] == '\0') {
1066 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1068 state
= MA_UNQUOTED
;
1069 if (terminated
!= NULL
)
1073 error("Unterminated quoted argument");
1077 if (state
== MA_UNQUOTED
) {
1083 if (state
== MA_START
) {
1084 argv
[argc
] = argvs
+ j
;
1085 state
= MA_UNQUOTED
;
1086 if (lastquote
!= NULL
)
1089 if ((state
== MA_SQUOTE
|| state
== MA_DQUOTE
) &&
1090 (arg
[i
] == '?' || arg
[i
] == '[' || arg
[i
] == '*')) {
1092 * Special case for sftp: escape quoted
1093 * glob(3) wildcards. NB. string can grow
1096 if (j
>= sizeof(argvs
) - 3)
1097 goto args_too_longs
;
1099 argvs
[j
++] = arg
[i
];
1101 argvs
[j
++] = arg
[i
];
1110 parse_args(const char **cpp
, int *pflag
, int *rflag
, int *lflag
, int *iflag
,
1111 int *hflag
, unsigned long *n_arg
, char **path1
, char **path2
)
1113 const char *cmd
, *cp
= *cpp
;
1117 int i
, cmdnum
, optidx
, argc
;
1119 /* Skip leading whitespace */
1120 cp
= cp
+ strspn(cp
, WHITESPACE
);
1122 /* Check for leading '-' (disable error processing) */
1127 cp
= cp
+ strspn(cp
, WHITESPACE
);
1130 /* Ignore blank lines and lines which begin with comment '#' char */
1131 if (*cp
== '\0' || *cp
== '#')
1134 if ((argv
= makeargv(cp
, &argc
, 0, NULL
, NULL
)) == NULL
)
1137 /* Figure out which command we have */
1138 for (i
= 0; cmds
[i
].c
!= NULL
; i
++) {
1139 if (strcasecmp(cmds
[i
].c
, argv
[0]) == 0)
1149 } else if (cmdnum
== -1) {
1150 error("Invalid command.");
1154 /* Get arguments and parse flags */
1155 *lflag
= *pflag
= *rflag
= *hflag
= *n_arg
= 0;
1156 *path1
= *path2
= NULL
;
1161 if ((optidx
= parse_getput_flags(cmd
, argv
, argc
, pflag
, rflag
)) == -1)
1163 /* Get first pathname (mandatory) */
1164 if (argc
- optidx
< 1) {
1165 error("You must specify at least one path after a "
1166 "%s command.", cmd
);
1169 *path1
= xstrdup(argv
[optidx
]);
1170 /* Get second pathname (optional) */
1171 if (argc
- optidx
> 1) {
1172 *path2
= xstrdup(argv
[optidx
+ 1]);
1173 /* Destination is not globbed */
1174 undo_glob_escape(*path2
);
1179 if (argc
- optidx
< 2) {
1180 error("You must specify two paths after a %s "
1184 *path1
= xstrdup(argv
[optidx
]);
1185 *path2
= xstrdup(argv
[optidx
+ 1]);
1186 /* Paths are not globbed */
1187 undo_glob_escape(*path1
);
1188 undo_glob_escape(*path2
);
1196 /* Get pathname (mandatory) */
1197 if (argc
- optidx
< 1) {
1198 error("You must specify a path after a %s command.",
1202 *path1
= xstrdup(argv
[optidx
]);
1203 /* Only "rm" globs */
1205 undo_glob_escape(*path1
);
1208 if ((optidx
= parse_df_flags(cmd
, argv
, argc
, hflag
,
1211 /* Default to current directory if no path specified */
1212 if (argc
- optidx
< 1)
1215 *path1
= xstrdup(argv
[optidx
]);
1216 undo_glob_escape(*path1
);
1220 if ((optidx
= parse_ls_flags(argv
, argc
, lflag
)) == -1)
1222 /* Path is optional */
1223 if (argc
- optidx
> 0)
1224 *path1
= xstrdup(argv
[optidx
]);
1227 /* Skip ls command and following whitespace */
1228 cp
= cp
+ strlen(cmd
) + strspn(cp
, WHITESPACE
);
1230 /* Uses the rest of the line */
1237 /* Get numeric arg (mandatory) */
1238 if (argc
- optidx
< 1)
1241 l
= strtol(argv
[optidx
], &cp2
, base
);
1242 if (cp2
== argv
[optidx
] || *cp2
!= '\0' ||
1243 ((l
== LONG_MIN
|| l
== LONG_MAX
) && errno
== ERANGE
) ||
1246 error("You must supply a numeric argument "
1247 "to the %s command.", cmd
);
1251 if (cmdnum
== I_LUMASK
)
1253 /* Get pathname (mandatory) */
1254 if (argc
- optidx
< 2) {
1255 error("You must specify a path after a %s command.",
1259 *path1
= xstrdup(argv
[optidx
+ 1]);
1269 fatal("Command not implemented");
1277 parse_dispatch_command(struct sftp_conn
*conn
, const char *cmd
, char **pwd
,
1280 char *path1
, *path2
, *tmp
;
1281 int pflag
= 0, rflag
= 0, lflag
= 0, iflag
= 0, hflag
= 0, cmdnum
, i
;
1282 unsigned long n_arg
= 0;
1284 char path_buf
[MAXPATHLEN
];
1288 path1
= path2
= NULL
;
1289 cmdnum
= parse_args(&cmd
, &pflag
, &rflag
, &lflag
, &iflag
, &hflag
, &n_arg
,
1295 memset(&g
, 0, sizeof(g
));
1297 /* Perform command */
1303 /* Unrecognized command */
1307 err
= process_get(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1310 err
= process_put(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1313 path1
= make_absolute(path1
, *pwd
);
1314 path2
= make_absolute(path2
, *pwd
);
1315 err
= do_rename(conn
, path1
, path2
);
1318 path2
= make_absolute(path2
, *pwd
);
1319 err
= do_symlink(conn
, path1
, path2
);
1322 path1
= make_absolute(path1
, *pwd
);
1323 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1324 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1325 printf("Removing %s\n", g
.gl_pathv
[i
]);
1326 err
= do_rm(conn
, g
.gl_pathv
[i
]);
1327 if (err
!= 0 && err_abort
)
1332 path1
= make_absolute(path1
, *pwd
);
1334 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1336 err
= do_mkdir(conn
, path1
, &a
, 1);
1339 path1
= make_absolute(path1
, *pwd
);
1340 err
= do_rmdir(conn
, path1
);
1343 path1
= make_absolute(path1
, *pwd
);
1344 if ((tmp
= do_realpath(conn
, path1
)) == NULL
) {
1348 if ((aa
= do_stat(conn
, tmp
, 0)) == NULL
) {
1353 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
)) {
1354 error("Can't change directory: Can't check target");
1359 if (!S_ISDIR(aa
->perm
)) {
1360 error("Can't change directory: \"%s\" is not "
1361 "a directory", tmp
);
1371 do_ls_dir(conn
, *pwd
, *pwd
, lflag
);
1375 /* Strip pwd off beginning of non-absolute paths */
1380 path1
= make_absolute(path1
, *pwd
);
1381 err
= do_globbed_ls(conn
, path1
, tmp
, lflag
);
1384 /* Default to current directory if no path specified */
1386 path1
= xstrdup(*pwd
);
1387 path1
= make_absolute(path1
, *pwd
);
1388 err
= do_df(conn
, path1
, hflag
, iflag
);
1391 if (chdir(path1
) == -1) {
1392 error("Couldn't change local directory to "
1393 "\"%s\": %s", path1
, strerror(errno
));
1398 if (mkdir(path1
, 0777) == -1) {
1399 error("Couldn't create local directory "
1400 "\"%s\": %s", path1
, strerror(errno
));
1408 local_do_shell(cmd
);
1412 printf("Local umask: %03lo\n", n_arg
);
1415 path1
= make_absolute(path1
, *pwd
);
1417 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1419 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1420 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1421 printf("Changing mode on %s\n", g
.gl_pathv
[i
]);
1422 err
= do_setstat(conn
, g
.gl_pathv
[i
], &a
);
1423 if (err
!= 0 && err_abort
)
1429 path1
= make_absolute(path1
, *pwd
);
1430 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1431 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1432 if (!(aa
= do_stat(conn
, g
.gl_pathv
[i
], 0))) {
1439 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_UIDGID
)) {
1440 error("Can't get current ownership of "
1441 "remote file \"%s\"", g
.gl_pathv
[i
]);
1448 aa
->flags
&= SSH2_FILEXFER_ATTR_UIDGID
;
1449 if (cmdnum
== I_CHOWN
) {
1450 printf("Changing owner on %s\n", g
.gl_pathv
[i
]);
1453 printf("Changing group on %s\n", g
.gl_pathv
[i
]);
1456 err
= do_setstat(conn
, g
.gl_pathv
[i
], aa
);
1457 if (err
!= 0 && err_abort
)
1462 printf("Remote working directory: %s\n", *pwd
);
1465 if (!getcwd(path_buf
, sizeof(path_buf
))) {
1466 error("Couldn't get local cwd: %s", strerror(errno
));
1470 printf("Local working directory: %s\n", path_buf
);
1473 /* Processed below */
1479 printf("SFTP protocol version %u\n", sftp_proto_version(conn
));
1482 showprogress
= !showprogress
;
1484 printf("Progress meter enabled\n");
1486 printf("Progress meter disabled\n");
1489 fatal("%d is not implemented", cmdnum
);
1499 /* If an unignored error occurs in batch mode we should abort. */
1500 if (err_abort
&& err
!= 0)
1502 else if (cmdnum
== I_QUIT
)
1510 prompt(EditLine
*el
)
1515 /* Display entries in 'list' after skipping the first 'len' chars */
1517 complete_display(char **list
, u_int len
)
1519 u_int y
, m
= 0, width
= 80, columns
= 1, colspace
= 0, llen
;
1523 /* Count entries for sort and find longest */
1524 for (y
= 0; list
[y
]; y
++)
1525 m
= MAX(m
, strlen(list
[y
]));
1527 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
1530 m
= m
> len
? m
- len
: 0;
1531 columns
= width
/ (m
+ 2);
1532 columns
= MAX(columns
, 1);
1533 colspace
= width
/ columns
;
1534 colspace
= MIN(colspace
, width
);
1538 for (y
= 0; list
[y
]; y
++) {
1539 llen
= strlen(list
[y
]);
1540 tmp
= llen
> len
? list
[y
] + len
: "";
1541 printf("%-*s", colspace
, tmp
);
1552 * Given a "list" of words that begin with a common prefix of "word",
1553 * attempt to find an autocompletion to extends "word" by the next
1554 * characters common to all entries in "list".
1557 complete_ambiguous(const char *word
, char **list
, size_t count
)
1563 u_int y
, matchlen
= strlen(list
[0]);
1565 /* Find length of common stem */
1566 for (y
= 1; list
[y
]; y
++) {
1569 for (x
= 0; x
< matchlen
; x
++)
1570 if (list
[0][x
] != list
[y
][x
])
1576 if (matchlen
> strlen(word
)) {
1577 char *tmp
= xstrdup(list
[0]);
1579 tmp
[matchlen
] = '\0';
1584 return xstrdup(word
);
1587 /* Autocomplete a sftp command */
1589 complete_cmd_parse(EditLine
*el
, char *cmd
, int lastarg
, char quote
,
1592 u_int y
, count
= 0, cmdlen
, tmplen
;
1593 char *tmp
, **list
, argterm
[3];
1596 list
= xcalloc((sizeof(cmds
) / sizeof(*cmds
)) + 1, sizeof(char *));
1598 /* No command specified: display all available commands */
1600 for (y
= 0; cmds
[y
].c
; y
++)
1601 list
[count
++] = xstrdup(cmds
[y
].c
);
1604 complete_display(list
, 0);
1606 for (y
= 0; list
[y
] != NULL
; y
++)
1612 /* Prepare subset of commands that start with "cmd" */
1613 cmdlen
= strlen(cmd
);
1614 for (y
= 0; cmds
[y
].c
; y
++) {
1615 if (!strncasecmp(cmd
, cmds
[y
].c
, cmdlen
))
1616 list
[count
++] = xstrdup(cmds
[y
].c
);
1623 /* Complete ambigious command */
1624 tmp
= complete_ambiguous(cmd
, list
, count
);
1626 complete_display(list
, 0);
1628 for (y
= 0; list
[y
]; y
++)
1633 tmplen
= strlen(tmp
);
1634 cmdlen
= strlen(cmd
);
1635 /* If cmd may be extended then do so */
1636 if (tmplen
> cmdlen
)
1637 if (el_insertstr(el
, tmp
+ cmdlen
) == -1)
1638 fatal("el_insertstr failed.");
1640 /* Terminate argument cleanly */
1644 argterm
[y
++] = quote
;
1645 if (lastarg
|| *(lf
->cursor
) != ' ')
1648 if (y
> 0 && el_insertstr(el
, argterm
) == -1)
1649 fatal("el_insertstr failed.");
1658 * Determine whether a particular sftp command's arguments (if any)
1659 * represent local or remote files.
1662 complete_is_remote(char *cmd
) {
1668 for (i
= 0; cmds
[i
].c
; i
++) {
1669 if (!strncasecmp(cmd
, cmds
[i
].c
, strlen(cmds
[i
].c
)))
1676 /* Autocomplete a filename "file" */
1678 complete_match(EditLine
*el
, struct sftp_conn
*conn
, char *remote_path
,
1679 char *file
, int remote
, int lastarg
, char quote
, int terminated
)
1682 char *tmp
, *tmp2
, ins
[3];
1683 u_int i
, hadglob
, pwdlen
, len
, tmplen
, filelen
;
1686 /* Glob from "file" location */
1690 xasprintf(&tmp
, "%s*", file
);
1692 memset(&g
, 0, sizeof(g
));
1693 if (remote
!= LOCAL
) {
1694 tmp
= make_absolute(tmp
, remote_path
);
1695 remote_glob(conn
, tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1697 glob(tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1699 /* Determine length of pwd so we can trim completion display */
1700 for (hadglob
= tmplen
= pwdlen
= 0; tmp
[tmplen
] != 0; tmplen
++) {
1701 /* Terminate counting on first unescaped glob metacharacter */
1702 if (tmp
[tmplen
] == '*' || tmp
[tmplen
] == '?') {
1703 if (tmp
[tmplen
] != '*' || tmp
[tmplen
+ 1] != '\0')
1707 if (tmp
[tmplen
] == '\\' && tmp
[tmplen
+ 1] != '\0')
1709 if (tmp
[tmplen
] == '/')
1710 pwdlen
= tmplen
+ 1; /* track last seen '/' */
1714 if (g
.gl_matchc
== 0)
1717 if (g
.gl_matchc
> 1)
1718 complete_display(g
.gl_pathv
, pwdlen
);
1721 /* Don't try to extend globs */
1722 if (file
== NULL
|| hadglob
)
1725 tmp2
= complete_ambiguous(file
, g
.gl_pathv
, g
.gl_matchc
);
1726 tmp
= path_strip(tmp2
, remote_path
);
1732 tmplen
= strlen(tmp
);
1733 filelen
= strlen(file
);
1735 if (tmplen
> filelen
) {
1736 tmp2
= tmp
+ filelen
;
1738 /* quote argument on way out */
1739 for (i
= 0; i
< len
; i
++) {
1749 if (quote
== '\0' || tmp2
[i
] == quote
) {
1750 if (el_insertstr(el
, ins
) == -1)
1751 fatal("el_insertstr "
1757 if (el_insertstr(el
, ins
+ 1) == -1)
1758 fatal("el_insertstr failed.");
1765 if (g
.gl_matchc
== 1) {
1769 if (*(lf
->cursor
- 1) != '/' &&
1770 (lastarg
|| *(lf
->cursor
) != ' '))
1773 if (i
> 0 && el_insertstr(el
, ins
) == -1)
1774 fatal("el_insertstr failed.");
1783 /* tab-completion hook function, called via libedit */
1784 static unsigned char
1785 complete(EditLine
*el
, int ch
)
1787 char **argv
, *line
, quote
;
1788 u_int argc
, carg
, cursor
, len
, terminated
, ret
= CC_ERROR
;
1790 struct complete_ctx
*complete_ctx
;
1793 if (el_get(el
, EL_CLIENTDATA
, (void**)&complete_ctx
) != 0)
1794 fatal("%s: el_get failed", __func__
);
1796 /* Figure out which argument the cursor points to */
1797 cursor
= lf
->cursor
- lf
->buffer
;
1798 line
= (char *)xmalloc(cursor
+ 1);
1799 memcpy(line
, lf
->buffer
, cursor
);
1800 line
[cursor
] = '\0';
1801 argv
= makeargv(line
, &carg
, 1, "e
, &terminated
);
1804 /* Get all the arguments on the line */
1805 len
= lf
->lastchar
- lf
->buffer
;
1806 line
= (char *)xmalloc(len
+ 1);
1807 memcpy(line
, lf
->buffer
, len
);
1809 argv
= makeargv(line
, &argc
, 1, NULL
, NULL
);
1811 /* Ensure cursor is at EOL or a argument boundary */
1812 if (line
[cursor
] != ' ' && line
[cursor
] != '\0' &&
1813 line
[cursor
] != '\n') {
1819 /* Show all available commands */
1820 complete_cmd_parse(el
, NULL
, argc
== carg
, '\0', 1);
1822 } else if (carg
== 1 && cursor
> 0 && line
[cursor
- 1] != ' ') {
1823 /* Handle the command parsing */
1824 if (complete_cmd_parse(el
, argv
[0], argc
== carg
,
1825 quote
, terminated
) != 0)
1827 } else if (carg
>= 1) {
1828 /* Handle file parsing */
1829 int remote
= complete_is_remote(argv
[0]);
1830 char *filematch
= NULL
;
1832 if (carg
> 1 && line
[cursor
-1] != ' ')
1833 filematch
= argv
[carg
- 1];
1836 complete_match(el
, complete_ctx
->conn
,
1837 *complete_ctx
->remote_pathp
, filematch
,
1838 remote
, carg
== argc
, quote
, terminated
) != 0)
1845 #endif /* USE_LIBEDIT */
1848 interactive_loop(struct sftp_conn
*conn
, char *file1
, char *file2
)
1853 int err
, interactive
;
1854 EditLine
*el
= NULL
;
1858 extern char *__progname
;
1859 struct complete_ctx complete_ctx
;
1861 if (!batchmode
&& isatty(STDIN_FILENO
)) {
1862 if ((el
= el_init(__progname
, stdin
, stdout
, stderr
)) == NULL
)
1863 fatal("Couldn't initialise editline");
1864 if ((hl
= history_init()) == NULL
)
1865 fatal("Couldn't initialise editline history");
1866 history(hl
, &hev
, H_SETSIZE
, 100);
1867 el_set(el
, EL_HIST
, history
, hl
);
1869 el_set(el
, EL_PROMPT
, prompt
);
1870 el_set(el
, EL_EDITOR
, "emacs");
1871 el_set(el
, EL_TERMINAL
, NULL
);
1872 el_set(el
, EL_SIGNAL
, 1);
1873 el_source(el
, NULL
);
1875 /* Tab Completion */
1876 el_set(el
, EL_ADDFN
, "ftp-complete",
1877 "Context senstive argument completion", complete
);
1878 complete_ctx
.conn
= conn
;
1879 complete_ctx
.remote_pathp
= &remote_path
;
1880 el_set(el
, EL_CLIENTDATA
, (void*)&complete_ctx
);
1881 el_set(el
, EL_BIND
, "^I", "ftp-complete", NULL
);
1883 #endif /* USE_LIBEDIT */
1885 remote_path
= do_realpath(conn
, ".");
1886 if (remote_path
== NULL
)
1889 if (file1
!= NULL
) {
1890 dir
= xstrdup(file1
);
1891 dir
= make_absolute(dir
, remote_path
);
1893 if (remote_is_dir(conn
, dir
) && file2
== NULL
) {
1894 printf("Changing to: %s\n", dir
);
1895 snprintf(cmd
, sizeof cmd
, "cd \"%s\"", dir
);
1896 if (parse_dispatch_command(conn
, cmd
,
1897 &remote_path
, 1) != 0) {
1905 snprintf(cmd
, sizeof cmd
, "get %s", dir
);
1907 snprintf(cmd
, sizeof cmd
, "get %s %s", dir
,
1910 err
= parse_dispatch_command(conn
, cmd
,
1920 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1921 setvbuf(stdout
, NULL
, _IOLBF
, 0);
1922 setvbuf(infile
, NULL
, _IOLBF
, 0);
1928 interactive
= !batchmode
&& isatty(STDIN_FILENO
);
1933 signal(SIGINT
, SIG_IGN
);
1938 if (fgets(cmd
, sizeof(cmd
), infile
) == NULL
) {
1943 if (!interactive
) { /* Echo command */
1944 printf("sftp> %s", cmd
);
1945 if (strlen(cmd
) > 0 &&
1946 cmd
[strlen(cmd
) - 1] != '\n')
1954 if ((line
= el_gets(el
, &count
)) == NULL
||
1959 history(hl
, &hev
, H_ENTER
, line
);
1960 if (strlcpy(cmd
, line
, sizeof(cmd
)) >= sizeof(cmd
)) {
1961 fprintf(stderr
, "Error: input line too long\n");
1964 #endif /* USE_LIBEDIT */
1967 cp
= strrchr(cmd
, '\n');
1971 /* Handle user interrupts gracefully during commands */
1973 signal(SIGINT
, cmd_interrupt
);
1975 err
= parse_dispatch_command(conn
, cmd
, &remote_path
,
1986 #endif /* USE_LIBEDIT */
1988 /* err == 1 signifies normal "quit" exit */
1989 return (err
>= 0 ? 0 : -1);
1993 connect_to_server(char *path
, char **args
, int *in
, int *out
)
1998 int pin
[2], pout
[2];
2000 if ((pipe(pin
) == -1) || (pipe(pout
) == -1))
2001 fatal("pipe: %s", strerror(errno
));
2006 #else /* USE_PIPES */
2009 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) == -1)
2010 fatal("socketpair: %s", strerror(errno
));
2011 *in
= *out
= inout
[0];
2012 c_in
= c_out
= inout
[1];
2013 #endif /* USE_PIPES */
2015 if ((sshpid
= fork()) == -1)
2016 fatal("fork: %s", strerror(errno
));
2017 else if (sshpid
== 0) {
2018 if ((dup2(c_in
, STDIN_FILENO
) == -1) ||
2019 (dup2(c_out
, STDOUT_FILENO
) == -1)) {
2020 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
2029 * The underlying ssh is in the same process group, so we must
2030 * ignore SIGINT if we want to gracefully abort commands,
2031 * otherwise the signal will make it to the ssh process and
2032 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2033 * underlying ssh, it must *not* ignore that signal.
2035 signal(SIGINT
, SIG_IGN
);
2036 signal(SIGTERM
, SIG_DFL
);
2038 fprintf(stderr
, "exec: %s: %s\n", path
, strerror(errno
));
2042 signal(SIGTERM
, killchild
);
2043 signal(SIGINT
, killchild
);
2044 signal(SIGHUP
, killchild
);
2052 extern char *__progname
;
2055 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2056 " [-D sftp_server_path] [-F ssh_config] "
2057 "[-i identity_file]\n"
2058 " [-o ssh_option] [-P port] [-R num_requests] "
2060 " [-s subsystem | sftp_server] host\n"
2061 " %s [user@]host[:file ...]\n"
2062 " %s [user@]host[:dir[/]]\n"
2063 " %s -b batchfile [user@]host\n",
2064 __progname
, __progname
, __progname
, __progname
);
2069 main(int argc
, char **argv
)
2071 int in
, out
, ch
, err
;
2072 char *host
= NULL
, *userhost
, *cp
, *file2
= NULL
;
2073 int debug_level
= 0, sshver
= 2;
2074 char *file1
= NULL
, *sftp_server
= NULL
;
2075 char *ssh_program
= _PATH_SSH_PROGRAM
, *sftp_direct
= NULL
;
2076 LogLevel ll
= SYSLOG_LEVEL_INFO
;
2079 extern char *optarg
;
2080 struct sftp_conn
*conn
;
2081 size_t copy_buffer_len
= DEFAULT_COPY_BUFLEN
;
2082 size_t num_requests
= DEFAULT_NUM_REQUESTS
;
2084 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2087 __progname
= ssh_get_progname(argv
[0]);
2088 memset(&args
, '\0', sizeof(args
));
2090 addargs(&args
, "%s", ssh_program
);
2091 addargs(&args
, "-oForwardX11 no");
2092 addargs(&args
, "-oForwardAgent no");
2093 addargs(&args
, "-oPermitLocalCommand no");
2094 addargs(&args
, "-oClearAllForwardings yes");
2096 ll
= SYSLOG_LEVEL_INFO
;
2099 while ((ch
= getopt(argc
, argv
,
2100 "1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
2102 /* Passed through to ssh(1) */
2106 addargs(&args
, "-%c", ch
);
2108 /* Passed through to ssh(1) with argument */
2113 addargs(&args
, "-%c", ch
);
2114 addargs(&args
, "%s", optarg
);
2118 addargs(&args
, "-%c", ch
);
2121 addargs(&args
, "-oPort %s", optarg
);
2124 if (debug_level
< 3) {
2125 addargs(&args
, "-v");
2126 ll
= SYSLOG_LEVEL_DEBUG1
+ debug_level
;
2132 if (sftp_server
== NULL
)
2133 sftp_server
= _PATH_SFTP_SERVER
;
2139 copy_buffer_len
= strtol(optarg
, &cp
, 10);
2140 if (copy_buffer_len
== 0 || *cp
!= '\0')
2141 fatal("Invalid buffer size \"%s\"", optarg
);
2145 fatal("Batch file already specified.");
2147 /* Allow "-" as stdin */
2148 if (strcmp(optarg
, "-") != 0 &&
2149 (infile
= fopen(optarg
, "r")) == NULL
)
2150 fatal("%s (%s).", strerror(errno
), optarg
);
2153 addargs(&args
, "-obatchmode yes");
2159 sftp_direct
= optarg
;
2165 num_requests
= strtol(optarg
, &cp
, 10);
2166 if (num_requests
== 0 || *cp
!= '\0')
2167 fatal("Invalid number of requests \"%s\"",
2171 sftp_server
= optarg
;
2174 ssh_program
= optarg
;
2175 replacearg(&args
, 0, "%s", ssh_program
);
2183 if (!isatty(STDERR_FILENO
))
2186 log_init(argv
[0], ll
, SYSLOG_FACILITY_USER
, 1);
2188 if (sftp_direct
== NULL
) {
2189 if (optind
== argc
|| argc
> (optind
+ 2))
2192 userhost
= xstrdup(argv
[optind
]);
2193 file2
= argv
[optind
+1];
2195 if ((host
= strrchr(userhost
, '@')) == NULL
)
2200 fprintf(stderr
, "Missing username\n");
2203 addargs(&args
, "-l");
2204 addargs(&args
, "%s", userhost
);
2207 if ((cp
= colon(host
)) != NULL
) {
2212 host
= cleanhostname(host
);
2214 fprintf(stderr
, "Missing hostname\n");
2218 addargs(&args
, "-oProtocol %d", sshver
);
2220 /* no subsystem if the server-spec contains a '/' */
2221 if (sftp_server
== NULL
|| strchr(sftp_server
, '/') == NULL
)
2222 addargs(&args
, "-s");
2224 addargs(&args
, "--");
2225 addargs(&args
, "%s", host
);
2226 addargs(&args
, "%s", (sftp_server
!= NULL
?
2227 sftp_server
: "sftp"));
2229 connect_to_server(ssh_program
, args
.list
, &in
, &out
);
2232 addargs(&args
, "sftp-server");
2234 connect_to_server(sftp_direct
, args
.list
, &in
, &out
);
2238 conn
= do_init(in
, out
, copy_buffer_len
, num_requests
);
2240 fatal("Couldn't initialise connection to server");
2243 if (sftp_direct
== NULL
)
2244 fprintf(stderr
, "Connected to %s.\n", host
);
2246 fprintf(stderr
, "Attached to %s.\n", sftp_direct
);
2249 err
= interactive_loop(conn
, file1
, file2
);
2251 #if !defined(USE_PIPES)
2252 shutdown(in
, SHUT_RDWR
);
2253 shutdown(out
, SHUT_RDWR
);
2261 while (waitpid(sshpid
, NULL
, 0) == -1)
2263 fatal("Couldn't wait for ssh process: %s",
2266 exit(err
== 0 ? 0 : 1);