1 /* $OpenBSD: sftp.c,v 1.118 2010/01/09 11:13:02 dtucker 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 0x01 /* Full view ala ls -l */
114 #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */
115 #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */
116 #define LS_NAME_SORT 0x08 /* Sort by name (default) */
117 #define LS_TIME_SORT 0x10 /* Sort by mtime */
118 #define LS_SIZE_SORT 0x20 /* Sort by file size */
119 #define LS_REVERSE_SORT 0x40 /* Reverse sort order */
120 #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */
122 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
123 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
125 /* Commands for interactive mode */
149 #define I_PROGRESS 23
157 /* Type of completion */
162 static const struct CMD cmds
[] = {
163 { "bye", I_QUIT
, NOARGS
},
164 { "cd", I_CHDIR
, REMOTE
},
165 { "chdir", I_CHDIR
, REMOTE
},
166 { "chgrp", I_CHGRP
, REMOTE
},
167 { "chmod", I_CHMOD
, REMOTE
},
168 { "chown", I_CHOWN
, REMOTE
},
169 { "df", I_DF
, REMOTE
},
170 { "dir", I_LS
, REMOTE
},
171 { "exit", I_QUIT
, NOARGS
},
172 { "get", I_GET
, REMOTE
},
173 { "help", I_HELP
, NOARGS
},
174 { "lcd", I_LCHDIR
, LOCAL
},
175 { "lchdir", I_LCHDIR
, LOCAL
},
176 { "lls", I_LLS
, LOCAL
},
177 { "lmkdir", I_LMKDIR
, LOCAL
},
178 { "ln", I_SYMLINK
, REMOTE
},
179 { "lpwd", I_LPWD
, LOCAL
},
180 { "ls", I_LS
, REMOTE
},
181 { "lumask", I_LUMASK
, NOARGS
},
182 { "mkdir", I_MKDIR
, REMOTE
},
183 { "progress", I_PROGRESS
, NOARGS
},
184 { "put", I_PUT
, LOCAL
},
185 { "pwd", I_PWD
, REMOTE
},
186 { "quit", I_QUIT
, NOARGS
},
187 { "rename", I_RENAME
, REMOTE
},
188 { "rm", I_RM
, REMOTE
},
189 { "rmdir", I_RMDIR
, REMOTE
},
190 { "symlink", I_SYMLINK
, REMOTE
},
191 { "version", I_VERSION
, NOARGS
},
192 { "!", I_SHELL
, NOARGS
},
193 { "?", I_HELP
, NOARGS
},
197 int interactive_loop(struct sftp_conn
*, char *file1
, char *file2
);
204 kill(sshpid
, SIGTERM
);
205 waitpid(sshpid
, NULL
, 0);
213 cmd_interrupt(int signo
)
215 const char msg
[] = "\rInterrupt \n";
216 int olderrno
= errno
;
218 write(STDERR_FILENO
, msg
, sizeof(msg
) - 1);
226 printf("Available commands:\n"
228 "cd path Change remote directory to 'path'\n"
229 "chgrp grp path Change group of file 'path' to 'grp'\n"
230 "chmod mode path Change permissions of file 'path' to 'mode'\n"
231 "chown own path Change owner of file 'path' to 'own'\n"
232 "df [-hi] [path] Display statistics for current directory or\n"
233 " filesystem containing 'path'\n"
235 "get [-Pr] remote-path [local-path] Download file\n"
236 "help Display this help text\n"
237 "lcd path Change local directory to 'path'\n"
238 "lls [ls-options [path]] Display local directory listing\n"
239 "lmkdir path Create local directory\n"
240 "ln oldpath newpath Symlink remote file\n"
241 "lpwd Print local working directory\n"
242 "ls [-1aflnrSt] [path] Display remote directory listing\n"
243 "lumask umask Set local umask to 'umask'\n"
244 "mkdir path Create remote directory\n"
245 "progress Toggle display of progress meter\n"
246 "put [-Pr] local-path [remote-path] Upload file\n"
247 "pwd Display remote working directory\n"
249 "rename oldpath newpath Rename remote file\n"
250 "rm path Delete remote file\n"
251 "rmdir path Remove remote directory\n"
252 "symlink oldpath newpath Symlink remote file\n"
253 "version Show SFTP version\n"
254 "!command Execute 'command' in local shell\n"
255 "! Escape to local shell\n"
256 "? Synonym for help\n");
260 local_do_shell(const char *args
)
269 if ((shell
= getenv("SHELL")) == NULL
)
270 shell
= _PATH_BSHELL
;
272 if ((pid
= fork()) == -1)
273 fatal("Couldn't fork: %s", strerror(errno
));
276 /* XXX: child has pipe fds to ssh subproc open - issue? */
278 debug3("Executing %s -c \"%s\"", shell
, args
);
279 execl(shell
, shell
, "-c", args
, (char *)NULL
);
281 debug3("Executing %s", shell
);
282 execl(shell
, shell
, (char *)NULL
);
284 fprintf(stderr
, "Couldn't execute \"%s\": %s\n", shell
,
288 while (waitpid(pid
, &status
, 0) == -1)
290 fatal("Couldn't wait for child: %s", strerror(errno
));
291 if (!WIFEXITED(status
))
292 error("Shell exited abnormally");
293 else if (WEXITSTATUS(status
))
294 error("Shell exited with status %d", WEXITSTATUS(status
));
298 local_do_ls(const char *args
)
301 local_do_shell(_PATH_LS
);
303 int len
= strlen(_PATH_LS
" ") + strlen(args
) + 1;
304 char *buf
= xmalloc(len
);
306 /* XXX: quoting - rip quoting code from ftp? */
307 snprintf(buf
, len
, _PATH_LS
" %s", args
);
313 /* Strip one path (usually the pwd) from the start of another */
315 path_strip(char *path
, char *strip
)
320 return (xstrdup(path
));
323 if (strncmp(path
, strip
, len
) == 0) {
324 if (strip
[len
- 1] != '/' && path
[len
] == '/')
326 return (xstrdup(path
+ len
));
329 return (xstrdup(path
));
333 make_absolute(char *p
, char *pwd
)
338 if (p
&& p
[0] != '/') {
339 abs_str
= path_append(pwd
, p
);
347 parse_getput_flags(const char *cmd
, char **argv
, int argc
, int *pflag
,
350 extern int opterr
, optind
, optopt
, optreset
;
353 optind
= optreset
= 1;
357 while ((ch
= getopt(argc
, argv
, "PpRr")) != -1) {
368 error("%s: Invalid flag -%c", cmd
, optopt
);
377 parse_ls_flags(char **argv
, int argc
, int *lflag
)
379 extern int opterr
, optind
, optopt
, optreset
;
382 optind
= optreset
= 1;
385 *lflag
= LS_NAME_SORT
;
386 while ((ch
= getopt(argc
, argv
, "1Saflnrt")) != -1) {
389 *lflag
&= ~VIEW_FLAGS
;
390 *lflag
|= LS_SHORT_VIEW
;
393 *lflag
&= ~SORT_FLAGS
;
394 *lflag
|= LS_SIZE_SORT
;
397 *lflag
|= LS_SHOW_ALL
;
400 *lflag
&= ~SORT_FLAGS
;
403 *lflag
&= ~VIEW_FLAGS
;
404 *lflag
|= LS_LONG_VIEW
;
407 *lflag
&= ~VIEW_FLAGS
;
408 *lflag
|= LS_NUMERIC_VIEW
|LS_LONG_VIEW
;
411 *lflag
|= LS_REVERSE_SORT
;
414 *lflag
&= ~SORT_FLAGS
;
415 *lflag
|= LS_TIME_SORT
;
418 error("ls: Invalid flag -%c", optopt
);
427 parse_df_flags(const char *cmd
, char **argv
, int argc
, int *hflag
, int *iflag
)
429 extern int opterr
, optind
, optopt
, optreset
;
432 optind
= optreset
= 1;
436 while ((ch
= getopt(argc
, argv
, "hi")) != -1) {
445 error("%s: Invalid flag -%c", cmd
, optopt
);
458 /* XXX: report errors? */
459 if (stat(path
, &sb
) == -1)
462 return(S_ISDIR(sb
.st_mode
));
466 remote_is_dir(struct sftp_conn
*conn
, char *path
)
470 /* XXX: report errors? */
471 if ((a
= do_stat(conn
, path
, 1)) == NULL
)
473 if (!(a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
))
475 return(S_ISDIR(a
->perm
));
478 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
480 pathname_is_dir(char *pathname
)
482 size_t l
= strlen(pathname
);
484 return l
> 0 && pathname
[l
- 1] == '/';
488 process_get(struct sftp_conn
*conn
, char *src
, char *dst
, char *pwd
,
489 int pflag
, int rflag
)
491 char *abs_src
= NULL
;
492 char *abs_dst
= NULL
;
494 char *filename
, *tmp
=NULL
;
497 abs_src
= xstrdup(src
);
498 abs_src
= make_absolute(abs_src
, pwd
);
499 memset(&g
, 0, sizeof(g
));
501 debug3("Looking up %s", abs_src
);
502 if (remote_glob(conn
, abs_src
, GLOB_MARK
, NULL
, &g
)) {
503 error("File \"%s\" not found.", abs_src
);
509 * If multiple matches then dst must be a directory or
512 if (g
.gl_matchc
> 1 && dst
!= NULL
&& !is_dir(dst
)) {
513 error("Multiple source paths, but destination "
514 "\"%s\" is not a directory", dst
);
519 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
520 tmp
= xstrdup(g
.gl_pathv
[i
]);
521 if ((filename
= basename(tmp
)) == NULL
) {
522 error("basename %s: %s", tmp
, strerror(errno
));
528 if (g
.gl_matchc
== 1 && dst
) {
530 abs_dst
= path_append(dst
, filename
);
532 abs_dst
= xstrdup(dst
);
535 abs_dst
= path_append(dst
, filename
);
537 abs_dst
= xstrdup(filename
);
541 printf("Fetching %s to %s\n", g
.gl_pathv
[i
], abs_dst
);
542 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
543 if (download_dir(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
544 pflag
|| global_pflag
, 1) == -1)
547 if (do_download(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
548 pflag
|| global_pflag
) == -1)
562 process_put(struct sftp_conn
*conn
, char *src
, char *dst
, char *pwd
,
563 int pflag
, int rflag
)
565 char *tmp_dst
= NULL
;
566 char *abs_dst
= NULL
;
567 char *tmp
= NULL
, *filename
= NULL
;
570 int i
, dst_is_dir
= 1;
574 tmp_dst
= xstrdup(dst
);
575 tmp_dst
= make_absolute(tmp_dst
, pwd
);
578 memset(&g
, 0, sizeof(g
));
579 debug3("Looking up %s", src
);
580 if (glob(src
, GLOB_NOCHECK
| GLOB_MARK
, NULL
, &g
)) {
581 error("File \"%s\" not found.", src
);
586 /* If we aren't fetching to pwd then stash this status for later */
588 dst_is_dir
= remote_is_dir(conn
, tmp_dst
);
590 /* If multiple matches, dst may be directory or unspecified */
591 if (g
.gl_matchc
> 1 && tmp_dst
&& !dst_is_dir
) {
592 error("Multiple paths match, but destination "
593 "\"%s\" is not a directory", tmp_dst
);
598 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
599 if (stat(g
.gl_pathv
[i
], &sb
) == -1) {
601 error("stat %s: %s", g
.gl_pathv
[i
], strerror(errno
));
605 tmp
= xstrdup(g
.gl_pathv
[i
]);
606 if ((filename
= basename(tmp
)) == NULL
) {
607 error("basename %s: %s", tmp
, strerror(errno
));
613 if (g
.gl_matchc
== 1 && tmp_dst
) {
614 /* If directory specified, append filename */
616 abs_dst
= path_append(tmp_dst
, filename
);
618 abs_dst
= xstrdup(tmp_dst
);
619 } else if (tmp_dst
) {
620 abs_dst
= path_append(tmp_dst
, filename
);
622 abs_dst
= make_absolute(xstrdup(filename
), pwd
);
626 printf("Uploading %s to %s\n", g
.gl_pathv
[i
], abs_dst
);
627 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
628 if (upload_dir(conn
, g
.gl_pathv
[i
], abs_dst
,
629 pflag
|| global_pflag
, 1) == -1)
632 if (do_upload(conn
, g
.gl_pathv
[i
], abs_dst
,
633 pflag
|| global_pflag
) == -1)
648 sdirent_comp(const void *aa
, const void *bb
)
650 SFTP_DIRENT
*a
= *(SFTP_DIRENT
**)aa
;
651 SFTP_DIRENT
*b
= *(SFTP_DIRENT
**)bb
;
652 int rmul
= sort_flag
& LS_REVERSE_SORT
? -1 : 1;
654 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
655 if (sort_flag
& LS_NAME_SORT
)
656 return (rmul
* strcmp(a
->filename
, b
->filename
));
657 else if (sort_flag
& LS_TIME_SORT
)
658 return (rmul
* NCMP(a
->a
.mtime
, b
->a
.mtime
));
659 else if (sort_flag
& LS_SIZE_SORT
)
660 return (rmul
* NCMP(a
->a
.size
, b
->a
.size
));
662 fatal("Unknown ls sort type");
665 /* sftp ls.1 replacement for directories */
667 do_ls_dir(struct sftp_conn
*conn
, char *path
, char *strip_path
, int lflag
)
670 u_int c
= 1, colspace
= 0, columns
= 1;
673 if ((n
= do_readdir(conn
, path
, &d
)) != 0)
676 if (!(lflag
& LS_SHORT_VIEW
)) {
677 u_int m
= 0, width
= 80;
681 /* Count entries for sort and find longest filename */
682 for (n
= 0; d
[n
] != NULL
; n
++) {
683 if (d
[n
]->filename
[0] != '.' || (lflag
& LS_SHOW_ALL
))
684 m
= MAX(m
, strlen(d
[n
]->filename
));
687 /* Add any subpath that also needs to be counted */
688 tmp
= path_strip(path
, strip_path
);
692 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
695 columns
= width
/ (m
+ 2);
696 columns
= MAX(columns
, 1);
697 colspace
= width
/ columns
;
698 colspace
= MIN(colspace
, width
);
701 if (lflag
& SORT_FLAGS
) {
702 for (n
= 0; d
[n
] != NULL
; n
++)
703 ; /* count entries */
704 sort_flag
= lflag
& (SORT_FLAGS
|LS_REVERSE_SORT
);
705 qsort(d
, n
, sizeof(*d
), sdirent_comp
);
708 for (n
= 0; d
[n
] != NULL
&& !interrupted
; n
++) {
711 if (d
[n
]->filename
[0] == '.' && !(lflag
& LS_SHOW_ALL
))
714 tmp
= path_append(path
, d
[n
]->filename
);
715 fname
= path_strip(tmp
, strip_path
);
718 if (lflag
& LS_LONG_VIEW
) {
719 if (lflag
& LS_NUMERIC_VIEW
) {
723 memset(&sb
, 0, sizeof(sb
));
724 attrib_to_stat(&d
[n
]->a
, &sb
);
725 lname
= ls_file(fname
, &sb
, 1);
726 printf("%s\n", lname
);
729 printf("%s\n", d
[n
]->longname
);
731 printf("%-*s", colspace
, fname
);
742 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
745 free_sftp_dirents(d
);
749 /* sftp ls.1 replacement which handles path globs */
751 do_globbed_ls(struct sftp_conn
*conn
, char *path
, char *strip_path
,
755 u_int i
, c
= 1, colspace
= 0, columns
= 1;
758 memset(&g
, 0, sizeof(g
));
760 if (remote_glob(conn
, path
, GLOB_MARK
|GLOB_NOCHECK
|GLOB_BRACE
,
761 NULL
, &g
) || (g
.gl_pathc
&& !g
.gl_matchc
)) {
764 error("Can't ls: \"%s\" not found", path
);
772 * If the glob returns a single match and it is a directory,
773 * then just list its contents.
775 if (g
.gl_matchc
== 1) {
776 if ((a
= do_lstat(conn
, g
.gl_pathv
[0], 1)) == NULL
) {
780 if ((a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
) &&
784 err
= do_ls_dir(conn
, g
.gl_pathv
[0], strip_path
, lflag
);
790 if (!(lflag
& LS_SHORT_VIEW
)) {
791 u_int m
= 0, width
= 80;
794 /* Count entries for sort and find longest filename */
795 for (i
= 0; g
.gl_pathv
[i
]; i
++)
796 m
= MAX(m
, strlen(g
.gl_pathv
[i
]));
798 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
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
) {
809 fname
= path_strip(g
.gl_pathv
[i
], strip_path
);
811 if (lflag
& LS_LONG_VIEW
) {
816 * XXX: this is slow - 1 roundtrip per path
817 * A solution to this is to fork glob() and
818 * build a sftp specific version which keeps the
819 * attribs (which currently get thrown away)
820 * that the server returns as well as the filenames.
822 memset(&sb
, 0, sizeof(sb
));
824 a
= do_lstat(conn
, g
.gl_pathv
[i
], 1);
826 attrib_to_stat(a
, &sb
);
827 lname
= ls_file(fname
, &sb
, 1);
828 printf("%s\n", lname
);
831 printf("%-*s", colspace
, fname
);
841 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
852 do_df(struct sftp_conn
*conn
, char *path
, int hflag
, int iflag
)
854 struct sftp_statvfs st
;
855 char s_used
[FMT_SCALED_STRSIZE
];
856 char s_avail
[FMT_SCALED_STRSIZE
];
857 char s_root
[FMT_SCALED_STRSIZE
];
858 char s_total
[FMT_SCALED_STRSIZE
];
859 unsigned long long ffree
;
861 if (do_statvfs(conn
, path
, &st
, 1) == -1)
864 ffree
= st
.f_files
? (100 * (st
.f_files
- st
.f_ffree
) / st
.f_files
) : 0;
865 printf(" Inodes Used Avail "
866 "(root) %%Capacity\n");
867 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
868 (unsigned long long)st
.f_files
,
869 (unsigned long long)(st
.f_files
- st
.f_ffree
),
870 (unsigned long long)st
.f_favail
,
871 (unsigned long long)st
.f_ffree
, ffree
);
873 strlcpy(s_used
, "error", sizeof(s_used
));
874 strlcpy(s_avail
, "error", sizeof(s_avail
));
875 strlcpy(s_root
, "error", sizeof(s_root
));
876 strlcpy(s_total
, "error", sizeof(s_total
));
877 fmt_scaled((st
.f_blocks
- st
.f_bfree
) * st
.f_frsize
, s_used
);
878 fmt_scaled(st
.f_bavail
* st
.f_frsize
, s_avail
);
879 fmt_scaled(st
.f_bfree
* st
.f_frsize
, s_root
);
880 fmt_scaled(st
.f_blocks
* st
.f_frsize
, s_total
);
881 printf(" Size Used Avail (root) %%Capacity\n");
882 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
883 s_total
, s_used
, s_avail
, s_root
,
884 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
887 printf(" Size Used Avail "
888 "(root) %%Capacity\n");
889 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
890 (unsigned long long)(st
.f_frsize
* st
.f_blocks
/ 1024),
891 (unsigned long long)(st
.f_frsize
*
892 (st
.f_blocks
- st
.f_bfree
) / 1024),
893 (unsigned long long)(st
.f_frsize
* st
.f_bavail
/ 1024),
894 (unsigned long long)(st
.f_frsize
* st
.f_bfree
/ 1024),
895 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
902 * Undo escaping of glob sequences in place. Used to undo extra escaping
903 * applied in makeargv() when the string is destined for a function that
907 undo_glob_escape(char *s
)
942 * Split a string into an argument vector using sh(1)-style quoting,
943 * comment and escaping rules, but with some tweaks to handle glob(3)
945 * The "sloppy" flag allows for recovery from missing terminating quote, for
946 * use in parsing incomplete commandlines during tab autocompletion.
948 * Returns NULL on error or a NULL-terminated array of arguments.
950 * If "lastquote" is not NULL, the quoting character used for the last
951 * argument is placed in *lastquote ("\0", "'" or "\"").
953 * If "terminated" is not NULL, *terminated will be set to 1 when the
954 * last argument's quote has been properly terminated or 0 otherwise.
955 * This parameter is only of use if "sloppy" is set.
958 #define MAXARGLEN 8192
960 makeargv(const char *arg
, int *argcp
, int sloppy
, char *lastquote
,
965 static char argvs
[MAXARGLEN
];
966 static char *argv
[MAXARGS
+ 1];
967 enum { MA_START
, MA_SQUOTE
, MA_DQUOTE
, MA_UNQUOTED
} state
, q
;
970 if (strlen(arg
) > sizeof(argvs
) - 1) {
972 error("string too long");
975 if (terminated
!= NULL
)
977 if (lastquote
!= NULL
)
982 if (isspace(arg
[i
])) {
983 if (state
== MA_UNQUOTED
) {
984 /* Terminate current argument */
988 } else if (state
!= MA_START
)
990 } else if (arg
[i
] == '"' || arg
[i
] == '\'') {
991 q
= arg
[i
] == '"' ? MA_DQUOTE
: MA_SQUOTE
;
992 if (state
== MA_START
) {
993 argv
[argc
] = argvs
+ j
;
995 if (lastquote
!= NULL
)
997 } else if (state
== MA_UNQUOTED
)
1000 state
= MA_UNQUOTED
;
1002 argvs
[j
++] = arg
[i
];
1003 } else if (arg
[i
] == '\\') {
1004 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1005 quot
= state
== MA_SQUOTE
? '\'' : '"';
1006 /* Unescape quote we are in */
1007 /* XXX support \n and friends? */
1008 if (arg
[i
+ 1] == quot
) {
1010 argvs
[j
++] = arg
[i
];
1011 } else if (arg
[i
+ 1] == '?' ||
1012 arg
[i
+ 1] == '[' || arg
[i
+ 1] == '*') {
1014 * Special case for sftp: append
1015 * double-escaped glob sequence -
1016 * glob will undo one level of
1017 * escaping. NB. string can grow here.
1019 if (j
>= sizeof(argvs
) - 5)
1020 goto args_too_longs
;
1022 argvs
[j
++] = arg
[i
++];
1024 argvs
[j
++] = arg
[i
];
1026 argvs
[j
++] = arg
[i
++];
1027 argvs
[j
++] = arg
[i
];
1030 if (state
== MA_START
) {
1031 argv
[argc
] = argvs
+ j
;
1032 state
= MA_UNQUOTED
;
1033 if (lastquote
!= NULL
)
1036 if (arg
[i
+ 1] == '?' || arg
[i
+ 1] == '[' ||
1037 arg
[i
+ 1] == '*' || arg
[i
+ 1] == '\\') {
1039 * Special case for sftp: append
1040 * escaped glob sequence -
1041 * glob will undo one level of
1044 argvs
[j
++] = arg
[i
++];
1045 argvs
[j
++] = arg
[i
];
1047 /* Unescape everything */
1048 /* XXX support \n and friends? */
1050 argvs
[j
++] = arg
[i
];
1053 } else if (arg
[i
] == '#') {
1054 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
)
1055 argvs
[j
++] = arg
[i
];
1058 } else if (arg
[i
] == '\0') {
1059 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1061 state
= MA_UNQUOTED
;
1062 if (terminated
!= NULL
)
1066 error("Unterminated quoted argument");
1070 if (state
== MA_UNQUOTED
) {
1076 if (state
== MA_START
) {
1077 argv
[argc
] = argvs
+ j
;
1078 state
= MA_UNQUOTED
;
1079 if (lastquote
!= NULL
)
1082 if ((state
== MA_SQUOTE
|| state
== MA_DQUOTE
) &&
1083 (arg
[i
] == '?' || arg
[i
] == '[' || arg
[i
] == '*')) {
1085 * Special case for sftp: escape quoted
1086 * glob(3) wildcards. NB. string can grow
1089 if (j
>= sizeof(argvs
) - 3)
1090 goto args_too_longs
;
1092 argvs
[j
++] = arg
[i
];
1094 argvs
[j
++] = arg
[i
];
1103 parse_args(const char **cpp
, int *pflag
, int *rflag
, int *lflag
, int *iflag
,
1104 int *hflag
, unsigned long *n_arg
, char **path1
, char **path2
)
1106 const char *cmd
, *cp
= *cpp
;
1110 int i
, cmdnum
, optidx
, argc
;
1112 /* Skip leading whitespace */
1113 cp
= cp
+ strspn(cp
, WHITESPACE
);
1115 /* Check for leading '-' (disable error processing) */
1120 cp
= cp
+ strspn(cp
, WHITESPACE
);
1123 /* Ignore blank lines and lines which begin with comment '#' char */
1124 if (*cp
== '\0' || *cp
== '#')
1127 if ((argv
= makeargv(cp
, &argc
, 0, NULL
, NULL
)) == NULL
)
1130 /* Figure out which command we have */
1131 for (i
= 0; cmds
[i
].c
!= NULL
; i
++) {
1132 if (strcasecmp(cmds
[i
].c
, argv
[0]) == 0)
1142 } else if (cmdnum
== -1) {
1143 error("Invalid command.");
1147 /* Get arguments and parse flags */
1148 *lflag
= *pflag
= *rflag
= *hflag
= *n_arg
= 0;
1149 *path1
= *path2
= NULL
;
1154 if ((optidx
= parse_getput_flags(cmd
, argv
, argc
, pflag
, rflag
)) == -1)
1156 /* Get first pathname (mandatory) */
1157 if (argc
- optidx
< 1) {
1158 error("You must specify at least one path after a "
1159 "%s command.", cmd
);
1162 *path1
= xstrdup(argv
[optidx
]);
1163 /* Get second pathname (optional) */
1164 if (argc
- optidx
> 1) {
1165 *path2
= xstrdup(argv
[optidx
+ 1]);
1166 /* Destination is not globbed */
1167 undo_glob_escape(*path2
);
1172 if (argc
- optidx
< 2) {
1173 error("You must specify two paths after a %s "
1177 *path1
= xstrdup(argv
[optidx
]);
1178 *path2
= xstrdup(argv
[optidx
+ 1]);
1179 /* Paths are not globbed */
1180 undo_glob_escape(*path1
);
1181 undo_glob_escape(*path2
);
1189 /* Get pathname (mandatory) */
1190 if (argc
- optidx
< 1) {
1191 error("You must specify a path after a %s command.",
1195 *path1
= xstrdup(argv
[optidx
]);
1196 /* Only "rm" globs */
1198 undo_glob_escape(*path1
);
1201 if ((optidx
= parse_df_flags(cmd
, argv
, argc
, hflag
,
1204 /* Default to current directory if no path specified */
1205 if (argc
- optidx
< 1)
1208 *path1
= xstrdup(argv
[optidx
]);
1209 undo_glob_escape(*path1
);
1213 if ((optidx
= parse_ls_flags(argv
, argc
, lflag
)) == -1)
1215 /* Path is optional */
1216 if (argc
- optidx
> 0)
1217 *path1
= xstrdup(argv
[optidx
]);
1220 /* Skip ls command and following whitespace */
1221 cp
= cp
+ strlen(cmd
) + strspn(cp
, WHITESPACE
);
1223 /* Uses the rest of the line */
1230 /* Get numeric arg (mandatory) */
1231 if (argc
- optidx
< 1)
1234 l
= strtol(argv
[optidx
], &cp2
, base
);
1235 if (cp2
== argv
[optidx
] || *cp2
!= '\0' ||
1236 ((l
== LONG_MIN
|| l
== LONG_MAX
) && errno
== ERANGE
) ||
1239 error("You must supply a numeric argument "
1240 "to the %s command.", cmd
);
1244 if (cmdnum
== I_LUMASK
)
1246 /* Get pathname (mandatory) */
1247 if (argc
- optidx
< 2) {
1248 error("You must specify a path after a %s command.",
1252 *path1
= xstrdup(argv
[optidx
+ 1]);
1262 fatal("Command not implemented");
1270 parse_dispatch_command(struct sftp_conn
*conn
, const char *cmd
, char **pwd
,
1273 char *path1
, *path2
, *tmp
;
1274 int pflag
= 0, rflag
= 0, lflag
= 0, iflag
= 0, hflag
= 0, cmdnum
, i
;
1275 unsigned long n_arg
= 0;
1277 char path_buf
[MAXPATHLEN
];
1281 path1
= path2
= NULL
;
1282 cmdnum
= parse_args(&cmd
, &pflag
, &rflag
, &lflag
, &iflag
, &hflag
, &n_arg
,
1288 memset(&g
, 0, sizeof(g
));
1290 /* Perform command */
1296 /* Unrecognized command */
1300 err
= process_get(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1303 err
= process_put(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1306 path1
= make_absolute(path1
, *pwd
);
1307 path2
= make_absolute(path2
, *pwd
);
1308 err
= do_rename(conn
, path1
, path2
);
1311 path2
= make_absolute(path2
, *pwd
);
1312 err
= do_symlink(conn
, path1
, path2
);
1315 path1
= make_absolute(path1
, *pwd
);
1316 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1317 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1318 printf("Removing %s\n", g
.gl_pathv
[i
]);
1319 err
= do_rm(conn
, g
.gl_pathv
[i
]);
1320 if (err
!= 0 && err_abort
)
1325 path1
= make_absolute(path1
, *pwd
);
1327 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1329 err
= do_mkdir(conn
, path1
, &a
, 1);
1332 path1
= make_absolute(path1
, *pwd
);
1333 err
= do_rmdir(conn
, path1
);
1336 path1
= make_absolute(path1
, *pwd
);
1337 if ((tmp
= do_realpath(conn
, path1
)) == NULL
) {
1341 if ((aa
= do_stat(conn
, tmp
, 0)) == NULL
) {
1346 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
)) {
1347 error("Can't change directory: Can't check target");
1352 if (!S_ISDIR(aa
->perm
)) {
1353 error("Can't change directory: \"%s\" is not "
1354 "a directory", tmp
);
1364 do_globbed_ls(conn
, *pwd
, *pwd
, lflag
);
1368 /* Strip pwd off beginning of non-absolute paths */
1373 path1
= make_absolute(path1
, *pwd
);
1374 err
= do_globbed_ls(conn
, path1
, tmp
, lflag
);
1377 /* Default to current directory if no path specified */
1379 path1
= xstrdup(*pwd
);
1380 path1
= make_absolute(path1
, *pwd
);
1381 err
= do_df(conn
, path1
, hflag
, iflag
);
1384 if (chdir(path1
) == -1) {
1385 error("Couldn't change local directory to "
1386 "\"%s\": %s", path1
, strerror(errno
));
1391 if (mkdir(path1
, 0777) == -1) {
1392 error("Couldn't create local directory "
1393 "\"%s\": %s", path1
, strerror(errno
));
1401 local_do_shell(cmd
);
1405 printf("Local umask: %03lo\n", n_arg
);
1408 path1
= make_absolute(path1
, *pwd
);
1410 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1412 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1413 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1414 printf("Changing mode on %s\n", g
.gl_pathv
[i
]);
1415 err
= do_setstat(conn
, g
.gl_pathv
[i
], &a
);
1416 if (err
!= 0 && err_abort
)
1422 path1
= make_absolute(path1
, *pwd
);
1423 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1424 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1425 if (!(aa
= do_stat(conn
, g
.gl_pathv
[i
], 0))) {
1432 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_UIDGID
)) {
1433 error("Can't get current ownership of "
1434 "remote file \"%s\"", g
.gl_pathv
[i
]);
1441 aa
->flags
&= SSH2_FILEXFER_ATTR_UIDGID
;
1442 if (cmdnum
== I_CHOWN
) {
1443 printf("Changing owner on %s\n", g
.gl_pathv
[i
]);
1446 printf("Changing group on %s\n", g
.gl_pathv
[i
]);
1449 err
= do_setstat(conn
, g
.gl_pathv
[i
], aa
);
1450 if (err
!= 0 && err_abort
)
1455 printf("Remote working directory: %s\n", *pwd
);
1458 if (!getcwd(path_buf
, sizeof(path_buf
))) {
1459 error("Couldn't get local cwd: %s", strerror(errno
));
1463 printf("Local working directory: %s\n", path_buf
);
1466 /* Processed below */
1472 printf("SFTP protocol version %u\n", sftp_proto_version(conn
));
1475 showprogress
= !showprogress
;
1477 printf("Progress meter enabled\n");
1479 printf("Progress meter disabled\n");
1482 fatal("%d is not implemented", cmdnum
);
1492 /* If an unignored error occurs in batch mode we should abort. */
1493 if (err_abort
&& err
!= 0)
1495 else if (cmdnum
== I_QUIT
)
1503 prompt(EditLine
*el
)
1508 /* Display entries in 'list' after skipping the first 'len' chars */
1510 complete_display(char **list
, u_int len
)
1512 u_int y
, m
= 0, width
= 80, columns
= 1, colspace
= 0, llen
;
1516 /* Count entries for sort and find longest */
1517 for (y
= 0; list
[y
]; y
++)
1518 m
= MAX(m
, strlen(list
[y
]));
1520 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
1523 m
= m
> len
? m
- len
: 0;
1524 columns
= width
/ (m
+ 2);
1525 columns
= MAX(columns
, 1);
1526 colspace
= width
/ columns
;
1527 colspace
= MIN(colspace
, width
);
1531 for (y
= 0; list
[y
]; y
++) {
1532 llen
= strlen(list
[y
]);
1533 tmp
= llen
> len
? list
[y
] + len
: "";
1534 printf("%-*s", colspace
, tmp
);
1545 * Given a "list" of words that begin with a common prefix of "word",
1546 * attempt to find an autocompletion to extends "word" by the next
1547 * characters common to all entries in "list".
1550 complete_ambiguous(const char *word
, char **list
, size_t count
)
1556 u_int y
, matchlen
= strlen(list
[0]);
1558 /* Find length of common stem */
1559 for (y
= 1; list
[y
]; y
++) {
1562 for (x
= 0; x
< matchlen
; x
++)
1563 if (list
[0][x
] != list
[y
][x
])
1569 if (matchlen
> strlen(word
)) {
1570 char *tmp
= xstrdup(list
[0]);
1572 tmp
[matchlen
] = '\0';
1577 return xstrdup(word
);
1580 /* Autocomplete a sftp command */
1582 complete_cmd_parse(EditLine
*el
, char *cmd
, int lastarg
, char quote
,
1585 u_int y
, count
= 0, cmdlen
, tmplen
;
1586 char *tmp
, **list
, argterm
[3];
1589 list
= xcalloc((sizeof(cmds
) / sizeof(*cmds
)) + 1, sizeof(char *));
1591 /* No command specified: display all available commands */
1593 for (y
= 0; cmds
[y
].c
; y
++)
1594 list
[count
++] = xstrdup(cmds
[y
].c
);
1597 complete_display(list
, 0);
1599 for (y
= 0; list
[y
] != NULL
; y
++)
1605 /* Prepare subset of commands that start with "cmd" */
1606 cmdlen
= strlen(cmd
);
1607 for (y
= 0; cmds
[y
].c
; y
++) {
1608 if (!strncasecmp(cmd
, cmds
[y
].c
, cmdlen
))
1609 list
[count
++] = xstrdup(cmds
[y
].c
);
1616 /* Complete ambigious command */
1617 tmp
= complete_ambiguous(cmd
, list
, count
);
1619 complete_display(list
, 0);
1621 for (y
= 0; list
[y
]; y
++)
1626 tmplen
= strlen(tmp
);
1627 cmdlen
= strlen(cmd
);
1628 /* If cmd may be extended then do so */
1629 if (tmplen
> cmdlen
)
1630 if (el_insertstr(el
, tmp
+ cmdlen
) == -1)
1631 fatal("el_insertstr failed.");
1633 /* Terminate argument cleanly */
1637 argterm
[y
++] = quote
;
1638 if (lastarg
|| *(lf
->cursor
) != ' ')
1641 if (y
> 0 && el_insertstr(el
, argterm
) == -1)
1642 fatal("el_insertstr failed.");
1651 * Determine whether a particular sftp command's arguments (if any)
1652 * represent local or remote files.
1655 complete_is_remote(char *cmd
) {
1661 for (i
= 0; cmds
[i
].c
; i
++) {
1662 if (!strncasecmp(cmd
, cmds
[i
].c
, strlen(cmds
[i
].c
)))
1669 /* Autocomplete a filename "file" */
1671 complete_match(EditLine
*el
, struct sftp_conn
*conn
, char *remote_path
,
1672 char *file
, int remote
, int lastarg
, char quote
, int terminated
)
1675 char *tmp
, *tmp2
, ins
[3];
1676 u_int i
, hadglob
, pwdlen
, len
, tmplen
, filelen
;
1679 /* Glob from "file" location */
1683 xasprintf(&tmp
, "%s*", file
);
1685 memset(&g
, 0, sizeof(g
));
1686 if (remote
!= LOCAL
) {
1687 tmp
= make_absolute(tmp
, remote_path
);
1688 remote_glob(conn
, tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1690 glob(tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1692 /* Determine length of pwd so we can trim completion display */
1693 for (hadglob
= tmplen
= pwdlen
= 0; tmp
[tmplen
] != 0; tmplen
++) {
1694 /* Terminate counting on first unescaped glob metacharacter */
1695 if (tmp
[tmplen
] == '*' || tmp
[tmplen
] == '?') {
1696 if (tmp
[tmplen
] != '*' || tmp
[tmplen
+ 1] != '\0')
1700 if (tmp
[tmplen
] == '\\' && tmp
[tmplen
+ 1] != '\0')
1702 if (tmp
[tmplen
] == '/')
1703 pwdlen
= tmplen
+ 1; /* track last seen '/' */
1707 if (g
.gl_matchc
== 0)
1710 if (g
.gl_matchc
> 1)
1711 complete_display(g
.gl_pathv
, pwdlen
);
1714 /* Don't try to extend globs */
1715 if (file
== NULL
|| hadglob
)
1718 tmp2
= complete_ambiguous(file
, g
.gl_pathv
, g
.gl_matchc
);
1719 tmp
= path_strip(tmp2
, remote_path
);
1725 tmplen
= strlen(tmp
);
1726 filelen
= strlen(file
);
1728 if (tmplen
> filelen
) {
1729 tmp2
= tmp
+ filelen
;
1731 /* quote argument on way out */
1732 for (i
= 0; i
< len
; i
++) {
1742 if (quote
== '\0' || tmp2
[i
] == quote
) {
1743 if (el_insertstr(el
, ins
) == -1)
1744 fatal("el_insertstr "
1750 if (el_insertstr(el
, ins
+ 1) == -1)
1751 fatal("el_insertstr failed.");
1759 * XXX should we really extend here? the user may not be done if
1760 * the filename is a directory.
1762 if (g
.gl_matchc
== 1) {
1766 if (lastarg
|| *(lf
->cursor
) != ' ')
1769 if (i
> 0 && el_insertstr(el
, ins
) == -1)
1770 fatal("el_insertstr failed.");
1779 /* tab-completion hook function, called via libedit */
1780 static unsigned char
1781 complete(EditLine
*el
, int ch
)
1783 char **argv
, *line
, quote
;
1784 u_int argc
, carg
, cursor
, len
, terminated
, ret
= CC_ERROR
;
1786 struct complete_ctx
*complete_ctx
;
1789 if (el_get(el
, EL_CLIENTDATA
, (void**)&complete_ctx
) != 0)
1790 fatal("%s: el_get failed", __func__
);
1792 /* Figure out which argument the cursor points to */
1793 cursor
= lf
->cursor
- lf
->buffer
;
1794 line
= (char *)xmalloc(cursor
+ 1);
1795 memcpy(line
, lf
->buffer
, cursor
);
1796 line
[cursor
] = '\0';
1797 argv
= makeargv(line
, &carg
, 1, "e
, &terminated
);
1800 /* Get all the arguments on the line */
1801 len
= lf
->lastchar
- lf
->buffer
;
1802 line
= (char *)xmalloc(len
+ 1);
1803 memcpy(line
, lf
->buffer
, len
);
1805 argv
= makeargv(line
, &argc
, 1, NULL
, NULL
);
1807 /* Ensure cursor is at EOL or a argument boundary */
1808 if (line
[cursor
] != ' ' && line
[cursor
] != '\0' &&
1809 line
[cursor
] != '\n') {
1815 /* Show all available commands */
1816 complete_cmd_parse(el
, NULL
, argc
== carg
, '\0', 1);
1818 } else if (carg
== 1 && cursor
> 0 && line
[cursor
- 1] != ' ') {
1819 /* Handle the command parsing */
1820 if (complete_cmd_parse(el
, argv
[0], argc
== carg
,
1821 quote
, terminated
) != 0)
1823 } else if (carg
>= 1) {
1824 /* Handle file parsing */
1825 int remote
= complete_is_remote(argv
[0]);
1826 char *filematch
= NULL
;
1828 if (carg
> 1 && line
[cursor
-1] != ' ')
1829 filematch
= argv
[carg
- 1];
1832 complete_match(el
, complete_ctx
->conn
,
1833 *complete_ctx
->remote_pathp
, filematch
,
1834 remote
, carg
== argc
, quote
, terminated
) != 0)
1841 #endif /* USE_LIBEDIT */
1844 interactive_loop(struct sftp_conn
*conn
, char *file1
, char *file2
)
1849 int err
, interactive
;
1850 EditLine
*el
= NULL
;
1854 extern char *__progname
;
1855 struct complete_ctx complete_ctx
;
1857 if (!batchmode
&& isatty(STDIN_FILENO
)) {
1858 if ((el
= el_init(__progname
, stdin
, stdout
, stderr
)) == NULL
)
1859 fatal("Couldn't initialise editline");
1860 if ((hl
= history_init()) == NULL
)
1861 fatal("Couldn't initialise editline history");
1862 history(hl
, &hev
, H_SETSIZE
, 100);
1863 el_set(el
, EL_HIST
, history
, hl
);
1865 el_set(el
, EL_PROMPT
, prompt
);
1866 el_set(el
, EL_EDITOR
, "emacs");
1867 el_set(el
, EL_TERMINAL
, NULL
);
1868 el_set(el
, EL_SIGNAL
, 1);
1869 el_source(el
, NULL
);
1871 /* Tab Completion */
1872 el_set(el
, EL_ADDFN
, "ftp-complete",
1873 "Context senstive argument completion", complete
);
1874 complete_ctx
.conn
= conn
;
1875 complete_ctx
.remote_pathp
= &remote_path
;
1876 el_set(el
, EL_CLIENTDATA
, (void*)&complete_ctx
);
1877 el_set(el
, EL_BIND
, "^I", "ftp-complete", NULL
);
1879 #endif /* USE_LIBEDIT */
1881 remote_path
= do_realpath(conn
, ".");
1882 if (remote_path
== NULL
)
1885 if (file1
!= NULL
) {
1886 dir
= xstrdup(file1
);
1887 dir
= make_absolute(dir
, remote_path
);
1889 if (remote_is_dir(conn
, dir
) && file2
== NULL
) {
1890 printf("Changing to: %s\n", dir
);
1891 snprintf(cmd
, sizeof cmd
, "cd \"%s\"", dir
);
1892 if (parse_dispatch_command(conn
, cmd
,
1893 &remote_path
, 1) != 0) {
1901 snprintf(cmd
, sizeof cmd
, "get %s", dir
);
1903 snprintf(cmd
, sizeof cmd
, "get %s %s", dir
,
1906 err
= parse_dispatch_command(conn
, cmd
,
1916 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1917 setvbuf(stdout
, NULL
, _IOLBF
, 0);
1918 setvbuf(infile
, NULL
, _IOLBF
, 0);
1924 interactive
= !batchmode
&& isatty(STDIN_FILENO
);
1929 signal(SIGINT
, SIG_IGN
);
1934 if (fgets(cmd
, sizeof(cmd
), infile
) == NULL
) {
1939 if (!interactive
) { /* Echo command */
1940 printf("sftp> %s", cmd
);
1941 if (strlen(cmd
) > 0 &&
1942 cmd
[strlen(cmd
) - 1] != '\n')
1950 if ((line
= el_gets(el
, &count
)) == NULL
||
1955 history(hl
, &hev
, H_ENTER
, line
);
1956 if (strlcpy(cmd
, line
, sizeof(cmd
)) >= sizeof(cmd
)) {
1957 fprintf(stderr
, "Error: input line too long\n");
1960 #endif /* USE_LIBEDIT */
1963 cp
= strrchr(cmd
, '\n');
1967 /* Handle user interrupts gracefully during commands */
1969 signal(SIGINT
, cmd_interrupt
);
1971 err
= parse_dispatch_command(conn
, cmd
, &remote_path
,
1982 #endif /* USE_LIBEDIT */
1984 /* err == 1 signifies normal "quit" exit */
1985 return (err
>= 0 ? 0 : -1);
1989 connect_to_server(char *path
, char **args
, int *in
, int *out
)
1994 int pin
[2], pout
[2];
1996 if ((pipe(pin
) == -1) || (pipe(pout
) == -1))
1997 fatal("pipe: %s", strerror(errno
));
2002 #else /* USE_PIPES */
2005 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) == -1)
2006 fatal("socketpair: %s", strerror(errno
));
2007 *in
= *out
= inout
[0];
2008 c_in
= c_out
= inout
[1];
2009 #endif /* USE_PIPES */
2011 if ((sshpid
= fork()) == -1)
2012 fatal("fork: %s", strerror(errno
));
2013 else if (sshpid
== 0) {
2014 if ((dup2(c_in
, STDIN_FILENO
) == -1) ||
2015 (dup2(c_out
, STDOUT_FILENO
) == -1)) {
2016 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
2025 * The underlying ssh is in the same process group, so we must
2026 * ignore SIGINT if we want to gracefully abort commands,
2027 * otherwise the signal will make it to the ssh process and
2030 signal(SIGINT
, SIG_IGN
);
2032 fprintf(stderr
, "exec: %s: %s\n", path
, strerror(errno
));
2036 signal(SIGTERM
, killchild
);
2037 signal(SIGINT
, killchild
);
2038 signal(SIGHUP
, killchild
);
2046 extern char *__progname
;
2049 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2050 " [-D sftp_server_path] [-F ssh_config] "
2051 "[-i identity_file]\n"
2052 " [-o ssh_option] [-P port] [-R num_requests] "
2054 " [-s subsystem | sftp_server] host\n"
2055 " %s [user@]host[:file ...]\n"
2056 " %s [user@]host[:dir[/]]\n"
2057 " %s -b batchfile [user@]host\n",
2058 __progname
, __progname
, __progname
, __progname
);
2063 main(int argc
, char **argv
)
2065 int in
, out
, ch
, err
;
2066 char *host
= NULL
, *userhost
, *cp
, *file2
= NULL
;
2067 int debug_level
= 0, sshver
= 2;
2068 char *file1
= NULL
, *sftp_server
= NULL
;
2069 char *ssh_program
= _PATH_SSH_PROGRAM
, *sftp_direct
= NULL
;
2070 LogLevel ll
= SYSLOG_LEVEL_INFO
;
2073 extern char *optarg
;
2074 struct sftp_conn
*conn
;
2075 size_t copy_buffer_len
= DEFAULT_COPY_BUFLEN
;
2076 size_t num_requests
= DEFAULT_NUM_REQUESTS
;
2078 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2081 __progname
= ssh_get_progname(argv
[0]);
2082 memset(&args
, '\0', sizeof(args
));
2084 addargs(&args
, "%s", ssh_program
);
2085 addargs(&args
, "-oForwardX11 no");
2086 addargs(&args
, "-oForwardAgent no");
2087 addargs(&args
, "-oPermitLocalCommand no");
2088 addargs(&args
, "-oClearAllForwardings yes");
2090 ll
= SYSLOG_LEVEL_INFO
;
2093 while ((ch
= getopt(argc
, argv
,
2094 "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
2096 /* Passed through to ssh(1) */
2100 addargs(&args
, "-%c", ch
);
2102 /* Passed through to ssh(1) with argument */
2107 addargs(&args
, "-%c", ch
);
2108 addargs(&args
, "%s", optarg
);
2112 addargs(&args
, "-%c", ch
);
2115 addargs(&args
, "-oPort %s", optarg
);
2118 if (debug_level
< 3) {
2119 addargs(&args
, "-v");
2120 ll
= SYSLOG_LEVEL_DEBUG1
+ debug_level
;
2126 if (sftp_server
== NULL
)
2127 sftp_server
= _PATH_SFTP_SERVER
;
2133 copy_buffer_len
= strtol(optarg
, &cp
, 10);
2134 if (copy_buffer_len
== 0 || *cp
!= '\0')
2135 fatal("Invalid buffer size \"%s\"", optarg
);
2139 fatal("Batch file already specified.");
2141 /* Allow "-" as stdin */
2142 if (strcmp(optarg
, "-") != 0 &&
2143 (infile
= fopen(optarg
, "r")) == NULL
)
2144 fatal("%s (%s).", strerror(errno
), optarg
);
2147 addargs(&args
, "-obatchmode yes");
2153 sftp_direct
= optarg
;
2159 num_requests
= strtol(optarg
, &cp
, 10);
2160 if (num_requests
== 0 || *cp
!= '\0')
2161 fatal("Invalid number of requests \"%s\"",
2165 sftp_server
= optarg
;
2168 ssh_program
= optarg
;
2169 replacearg(&args
, 0, "%s", ssh_program
);
2177 if (!isatty(STDERR_FILENO
))
2180 log_init(argv
[0], ll
, SYSLOG_FACILITY_USER
, 1);
2182 if (sftp_direct
== NULL
) {
2183 if (optind
== argc
|| argc
> (optind
+ 2))
2186 userhost
= xstrdup(argv
[optind
]);
2187 file2
= argv
[optind
+1];
2189 if ((host
= strrchr(userhost
, '@')) == NULL
)
2194 fprintf(stderr
, "Missing username\n");
2197 addargs(&args
, "-l");
2198 addargs(&args
, "%s", userhost
);
2201 if ((cp
= colon(host
)) != NULL
) {
2206 host
= cleanhostname(host
);
2208 fprintf(stderr
, "Missing hostname\n");
2212 addargs(&args
, "-oProtocol %d", sshver
);
2214 /* no subsystem if the server-spec contains a '/' */
2215 if (sftp_server
== NULL
|| strchr(sftp_server
, '/') == NULL
)
2216 addargs(&args
, "-s");
2218 addargs(&args
, "--");
2219 addargs(&args
, "%s", host
);
2220 addargs(&args
, "%s", (sftp_server
!= NULL
?
2221 sftp_server
: "sftp"));
2223 connect_to_server(ssh_program
, args
.list
, &in
, &out
);
2226 addargs(&args
, "sftp-server");
2228 connect_to_server(sftp_direct
, args
.list
, &in
, &out
);
2232 conn
= do_init(in
, out
, copy_buffer_len
, num_requests
);
2234 fatal("Couldn't initialise connection to server");
2237 if (sftp_direct
== NULL
)
2238 fprintf(stderr
, "Connected to %s.\n", host
);
2240 fprintf(stderr
, "Attached to %s.\n", sftp_direct
);
2243 err
= interactive_loop(conn
, file1
, file2
);
2245 #if !defined(USE_PIPES)
2246 shutdown(in
, SHUT_RDWR
);
2247 shutdown(out
, SHUT_RDWR
);
2255 while (waitpid(sshpid
, NULL
, 0) == -1)
2257 fatal("Couldn't wait for ssh process: %s",
2260 exit(err
== 0 ? 0 : 1);