1 /* $OpenBSD: sftp.c,v 1.117 2010/01/08 21:50:49 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 /* Ignore blank lines and lines which begin with comment '#' char */
1116 if (*cp
== '\0' || *cp
== '#')
1119 /* Check for leading '-' (disable error processing) */
1126 if ((argv
= makeargv(cp
, &argc
, 0, NULL
, NULL
)) == NULL
)
1129 /* Figure out which command we have */
1130 for (i
= 0; cmds
[i
].c
!= NULL
; i
++) {
1131 if (strcasecmp(cmds
[i
].c
, argv
[0]) == 0)
1141 } else if (cmdnum
== -1) {
1142 error("Invalid command.");
1146 /* Get arguments and parse flags */
1147 *lflag
= *pflag
= *rflag
= *hflag
= *n_arg
= 0;
1148 *path1
= *path2
= NULL
;
1153 if ((optidx
= parse_getput_flags(cmd
, argv
, argc
, pflag
, rflag
)) == -1)
1155 /* Get first pathname (mandatory) */
1156 if (argc
- optidx
< 1) {
1157 error("You must specify at least one path after a "
1158 "%s command.", cmd
);
1161 *path1
= xstrdup(argv
[optidx
]);
1162 /* Get second pathname (optional) */
1163 if (argc
- optidx
> 1) {
1164 *path2
= xstrdup(argv
[optidx
+ 1]);
1165 /* Destination is not globbed */
1166 undo_glob_escape(*path2
);
1171 if (argc
- optidx
< 2) {
1172 error("You must specify two paths after a %s "
1176 *path1
= xstrdup(argv
[optidx
]);
1177 *path2
= xstrdup(argv
[optidx
+ 1]);
1178 /* Paths are not globbed */
1179 undo_glob_escape(*path1
);
1180 undo_glob_escape(*path2
);
1188 /* Get pathname (mandatory) */
1189 if (argc
- optidx
< 1) {
1190 error("You must specify a path after a %s command.",
1194 *path1
= xstrdup(argv
[optidx
]);
1195 /* Only "rm" globs */
1197 undo_glob_escape(*path1
);
1200 if ((optidx
= parse_df_flags(cmd
, argv
, argc
, hflag
,
1203 /* Default to current directory if no path specified */
1204 if (argc
- optidx
< 1)
1207 *path1
= xstrdup(argv
[optidx
]);
1208 undo_glob_escape(*path1
);
1212 if ((optidx
= parse_ls_flags(argv
, argc
, lflag
)) == -1)
1214 /* Path is optional */
1215 if (argc
- optidx
> 0)
1216 *path1
= xstrdup(argv
[optidx
]);
1219 /* Skip ls command and following whitespace */
1220 cp
= cp
+ strlen(cmd
) + strspn(cp
, WHITESPACE
);
1222 /* Uses the rest of the line */
1229 /* Get numeric arg (mandatory) */
1230 if (argc
- optidx
< 1)
1233 l
= strtol(argv
[optidx
], &cp2
, base
);
1234 if (cp2
== argv
[optidx
] || *cp2
!= '\0' ||
1235 ((l
== LONG_MIN
|| l
== LONG_MAX
) && errno
== ERANGE
) ||
1238 error("You must supply a numeric argument "
1239 "to the %s command.", cmd
);
1243 if (cmdnum
== I_LUMASK
)
1245 /* Get pathname (mandatory) */
1246 if (argc
- optidx
< 2) {
1247 error("You must specify a path after a %s command.",
1251 *path1
= xstrdup(argv
[optidx
+ 1]);
1261 fatal("Command not implemented");
1269 parse_dispatch_command(struct sftp_conn
*conn
, const char *cmd
, char **pwd
,
1272 char *path1
, *path2
, *tmp
;
1273 int pflag
= 0, rflag
= 0, lflag
= 0, iflag
= 0, hflag
= 0, cmdnum
, i
;
1274 unsigned long n_arg
= 0;
1276 char path_buf
[MAXPATHLEN
];
1280 path1
= path2
= NULL
;
1281 cmdnum
= parse_args(&cmd
, &pflag
, &rflag
, &lflag
, &iflag
, &hflag
, &n_arg
,
1287 memset(&g
, 0, sizeof(g
));
1289 /* Perform command */
1295 /* Unrecognized command */
1299 err
= process_get(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1302 err
= process_put(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1305 path1
= make_absolute(path1
, *pwd
);
1306 path2
= make_absolute(path2
, *pwd
);
1307 err
= do_rename(conn
, path1
, path2
);
1310 path2
= make_absolute(path2
, *pwd
);
1311 err
= do_symlink(conn
, path1
, path2
);
1314 path1
= make_absolute(path1
, *pwd
);
1315 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1316 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1317 printf("Removing %s\n", g
.gl_pathv
[i
]);
1318 err
= do_rm(conn
, g
.gl_pathv
[i
]);
1319 if (err
!= 0 && err_abort
)
1324 path1
= make_absolute(path1
, *pwd
);
1326 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1328 err
= do_mkdir(conn
, path1
, &a
, 1);
1331 path1
= make_absolute(path1
, *pwd
);
1332 err
= do_rmdir(conn
, path1
);
1335 path1
= make_absolute(path1
, *pwd
);
1336 if ((tmp
= do_realpath(conn
, path1
)) == NULL
) {
1340 if ((aa
= do_stat(conn
, tmp
, 0)) == NULL
) {
1345 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
)) {
1346 error("Can't change directory: Can't check target");
1351 if (!S_ISDIR(aa
->perm
)) {
1352 error("Can't change directory: \"%s\" is not "
1353 "a directory", tmp
);
1363 do_globbed_ls(conn
, *pwd
, *pwd
, lflag
);
1367 /* Strip pwd off beginning of non-absolute paths */
1372 path1
= make_absolute(path1
, *pwd
);
1373 err
= do_globbed_ls(conn
, path1
, tmp
, lflag
);
1376 /* Default to current directory if no path specified */
1378 path1
= xstrdup(*pwd
);
1379 path1
= make_absolute(path1
, *pwd
);
1380 err
= do_df(conn
, path1
, hflag
, iflag
);
1383 if (chdir(path1
) == -1) {
1384 error("Couldn't change local directory to "
1385 "\"%s\": %s", path1
, strerror(errno
));
1390 if (mkdir(path1
, 0777) == -1) {
1391 error("Couldn't create local directory "
1392 "\"%s\": %s", path1
, strerror(errno
));
1400 local_do_shell(cmd
);
1404 printf("Local umask: %03lo\n", n_arg
);
1407 path1
= make_absolute(path1
, *pwd
);
1409 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1411 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1412 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1413 printf("Changing mode on %s\n", g
.gl_pathv
[i
]);
1414 err
= do_setstat(conn
, g
.gl_pathv
[i
], &a
);
1415 if (err
!= 0 && err_abort
)
1421 path1
= make_absolute(path1
, *pwd
);
1422 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1423 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1424 if (!(aa
= do_stat(conn
, g
.gl_pathv
[i
], 0))) {
1431 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_UIDGID
)) {
1432 error("Can't get current ownership of "
1433 "remote file \"%s\"", g
.gl_pathv
[i
]);
1440 aa
->flags
&= SSH2_FILEXFER_ATTR_UIDGID
;
1441 if (cmdnum
== I_CHOWN
) {
1442 printf("Changing owner on %s\n", g
.gl_pathv
[i
]);
1445 printf("Changing group on %s\n", g
.gl_pathv
[i
]);
1448 err
= do_setstat(conn
, g
.gl_pathv
[i
], aa
);
1449 if (err
!= 0 && err_abort
)
1454 printf("Remote working directory: %s\n", *pwd
);
1457 if (!getcwd(path_buf
, sizeof(path_buf
))) {
1458 error("Couldn't get local cwd: %s", strerror(errno
));
1462 printf("Local working directory: %s\n", path_buf
);
1465 /* Processed below */
1471 printf("SFTP protocol version %u\n", sftp_proto_version(conn
));
1474 showprogress
= !showprogress
;
1476 printf("Progress meter enabled\n");
1478 printf("Progress meter disabled\n");
1481 fatal("%d is not implemented", cmdnum
);
1491 /* If an unignored error occurs in batch mode we should abort. */
1492 if (err_abort
&& err
!= 0)
1494 else if (cmdnum
== I_QUIT
)
1502 prompt(EditLine
*el
)
1507 /* Display entries in 'list' after skipping the first 'len' chars */
1509 complete_display(char **list
, u_int len
)
1511 u_int y
, m
= 0, width
= 80, columns
= 1, colspace
= 0, llen
;
1515 /* Count entries for sort and find longest */
1516 for (y
= 0; list
[y
]; y
++)
1517 m
= MAX(m
, strlen(list
[y
]));
1519 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
1522 m
= m
> len
? m
- len
: 0;
1523 columns
= width
/ (m
+ 2);
1524 columns
= MAX(columns
, 1);
1525 colspace
= width
/ columns
;
1526 colspace
= MIN(colspace
, width
);
1530 for (y
= 0; list
[y
]; y
++) {
1531 llen
= strlen(list
[y
]);
1532 tmp
= llen
> len
? list
[y
] + len
: "";
1533 printf("%-*s", colspace
, tmp
);
1544 * Given a "list" of words that begin with a common prefix of "word",
1545 * attempt to find an autocompletion to extends "word" by the next
1546 * characters common to all entries in "list".
1549 complete_ambiguous(const char *word
, char **list
, size_t count
)
1555 u_int y
, matchlen
= strlen(list
[0]);
1557 /* Find length of common stem */
1558 for (y
= 1; list
[y
]; y
++) {
1561 for (x
= 0; x
< matchlen
; x
++)
1562 if (list
[0][x
] != list
[y
][x
])
1568 if (matchlen
> strlen(word
)) {
1569 char *tmp
= xstrdup(list
[0]);
1571 tmp
[matchlen
] = '\0';
1576 return xstrdup(word
);
1579 /* Autocomplete a sftp command */
1581 complete_cmd_parse(EditLine
*el
, char *cmd
, int lastarg
, char quote
,
1584 u_int y
, count
= 0, cmdlen
, tmplen
;
1585 char *tmp
, **list
, argterm
[3];
1588 list
= xcalloc((sizeof(cmds
) / sizeof(*cmds
)) + 1, sizeof(char *));
1590 /* No command specified: display all available commands */
1592 for (y
= 0; cmds
[y
].c
; y
++)
1593 list
[count
++] = xstrdup(cmds
[y
].c
);
1596 complete_display(list
, 0);
1598 for (y
= 0; list
[y
] != NULL
; y
++)
1604 /* Prepare subset of commands that start with "cmd" */
1605 cmdlen
= strlen(cmd
);
1606 for (y
= 0; cmds
[y
].c
; y
++) {
1607 if (!strncasecmp(cmd
, cmds
[y
].c
, cmdlen
))
1608 list
[count
++] = xstrdup(cmds
[y
].c
);
1615 /* Complete ambigious command */
1616 tmp
= complete_ambiguous(cmd
, list
, count
);
1618 complete_display(list
, 0);
1620 for (y
= 0; list
[y
]; y
++)
1625 tmplen
= strlen(tmp
);
1626 cmdlen
= strlen(cmd
);
1627 /* If cmd may be extended then do so */
1628 if (tmplen
> cmdlen
)
1629 if (el_insertstr(el
, tmp
+ cmdlen
) == -1)
1630 fatal("el_insertstr failed.");
1632 /* Terminate argument cleanly */
1636 argterm
[y
++] = quote
;
1637 if (lastarg
|| *(lf
->cursor
) != ' ')
1640 if (y
> 0 && el_insertstr(el
, argterm
) == -1)
1641 fatal("el_insertstr failed.");
1650 * Determine whether a particular sftp command's arguments (if any)
1651 * represent local or remote files.
1654 complete_is_remote(char *cmd
) {
1660 for (i
= 0; cmds
[i
].c
; i
++) {
1661 if (!strncasecmp(cmd
, cmds
[i
].c
, strlen(cmds
[i
].c
)))
1668 /* Autocomplete a filename "file" */
1670 complete_match(EditLine
*el
, struct sftp_conn
*conn
, char *remote_path
,
1671 char *file
, int remote
, int lastarg
, char quote
, int terminated
)
1674 char *tmp
, *tmp2
, ins
[3];
1675 u_int i
, hadglob
, pwdlen
, len
, tmplen
, filelen
;
1678 /* Glob from "file" location */
1682 xasprintf(&tmp
, "%s*", file
);
1684 memset(&g
, 0, sizeof(g
));
1685 if (remote
!= LOCAL
) {
1686 tmp
= make_absolute(tmp
, remote_path
);
1687 remote_glob(conn
, tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1689 glob(tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1691 /* Determine length of pwd so we can trim completion display */
1692 for (hadglob
= tmplen
= pwdlen
= 0; tmp
[tmplen
] != 0; tmplen
++) {
1693 /* Terminate counting on first unescaped glob metacharacter */
1694 if (tmp
[tmplen
] == '*' || tmp
[tmplen
] == '?') {
1695 if (tmp
[tmplen
] != '*' || tmp
[tmplen
+ 1] != '\0')
1699 if (tmp
[tmplen
] == '\\' && tmp
[tmplen
+ 1] != '\0')
1701 if (tmp
[tmplen
] == '/')
1702 pwdlen
= tmplen
+ 1; /* track last seen '/' */
1706 if (g
.gl_matchc
== 0)
1709 if (g
.gl_matchc
> 1)
1710 complete_display(g
.gl_pathv
, pwdlen
);
1713 /* Don't try to extend globs */
1714 if (file
== NULL
|| hadglob
)
1717 tmp2
= complete_ambiguous(file
, g
.gl_pathv
, g
.gl_matchc
);
1718 tmp
= path_strip(tmp2
, remote_path
);
1724 tmplen
= strlen(tmp
);
1725 filelen
= strlen(file
);
1727 if (tmplen
> filelen
) {
1728 tmp2
= tmp
+ filelen
;
1730 /* quote argument on way out */
1731 for (i
= 0; i
< len
; i
++) {
1741 if (quote
== '\0' || tmp2
[i
] == quote
) {
1742 if (el_insertstr(el
, ins
) == -1)
1743 fatal("el_insertstr "
1749 if (el_insertstr(el
, ins
+ 1) == -1)
1750 fatal("el_insertstr failed.");
1758 * XXX should we really extend here? the user may not be done if
1759 * the filename is a directory.
1761 if (g
.gl_matchc
== 1) {
1765 if (lastarg
|| *(lf
->cursor
) != ' ')
1768 if (i
> 0 && el_insertstr(el
, ins
) == -1)
1769 fatal("el_insertstr failed.");
1778 /* tab-completion hook function, called via libedit */
1779 static unsigned char
1780 complete(EditLine
*el
, int ch
)
1782 char **argv
, *line
, quote
;
1783 u_int argc
, carg
, cursor
, len
, terminated
, ret
= CC_ERROR
;
1785 struct complete_ctx
*complete_ctx
;
1788 if (el_get(el
, EL_CLIENTDATA
, (void**)&complete_ctx
) != 0)
1789 fatal("%s: el_get failed", __func__
);
1791 /* Figure out which argument the cursor points to */
1792 cursor
= lf
->cursor
- lf
->buffer
;
1793 line
= (char *)xmalloc(cursor
+ 1);
1794 memcpy(line
, lf
->buffer
, cursor
);
1795 line
[cursor
] = '\0';
1796 argv
= makeargv(line
, &carg
, 1, "e
, &terminated
);
1799 /* Get all the arguments on the line */
1800 len
= lf
->lastchar
- lf
->buffer
;
1801 line
= (char *)xmalloc(len
+ 1);
1802 memcpy(line
, lf
->buffer
, len
);
1804 argv
= makeargv(line
, &argc
, 1, NULL
, NULL
);
1806 /* Ensure cursor is at EOL or a argument boundary */
1807 if (line
[cursor
] != ' ' && line
[cursor
] != '\0' &&
1808 line
[cursor
] != '\n') {
1814 /* Show all available commands */
1815 complete_cmd_parse(el
, NULL
, argc
== carg
, '\0', 1);
1817 } else if (carg
== 1 && cursor
> 0 && line
[cursor
- 1] != ' ') {
1818 /* Handle the command parsing */
1819 if (complete_cmd_parse(el
, argv
[0], argc
== carg
,
1820 quote
, terminated
) != 0)
1822 } else if (carg
>= 1) {
1823 /* Handle file parsing */
1824 int remote
= complete_is_remote(argv
[0]);
1825 char *filematch
= NULL
;
1827 if (carg
> 1 && line
[cursor
-1] != ' ')
1828 filematch
= argv
[carg
- 1];
1831 complete_match(el
, complete_ctx
->conn
,
1832 *complete_ctx
->remote_pathp
, filematch
,
1833 remote
, carg
== argc
, quote
, terminated
) != 0)
1840 #endif /* USE_LIBEDIT */
1843 interactive_loop(struct sftp_conn
*conn
, char *file1
, char *file2
)
1848 int err
, interactive
;
1849 EditLine
*el
= NULL
;
1853 extern char *__progname
;
1854 struct complete_ctx complete_ctx
;
1856 if (!batchmode
&& isatty(STDIN_FILENO
)) {
1857 if ((el
= el_init(__progname
, stdin
, stdout
, stderr
)) == NULL
)
1858 fatal("Couldn't initialise editline");
1859 if ((hl
= history_init()) == NULL
)
1860 fatal("Couldn't initialise editline history");
1861 history(hl
, &hev
, H_SETSIZE
, 100);
1862 el_set(el
, EL_HIST
, history
, hl
);
1864 el_set(el
, EL_PROMPT
, prompt
);
1865 el_set(el
, EL_EDITOR
, "emacs");
1866 el_set(el
, EL_TERMINAL
, NULL
);
1867 el_set(el
, EL_SIGNAL
, 1);
1868 el_source(el
, NULL
);
1870 /* Tab Completion */
1871 el_set(el
, EL_ADDFN
, "ftp-complete",
1872 "Context senstive argument completion", complete
);
1873 complete_ctx
.conn
= conn
;
1874 complete_ctx
.remote_pathp
= &remote_path
;
1875 el_set(el
, EL_CLIENTDATA
, (void*)&complete_ctx
);
1876 el_set(el
, EL_BIND
, "^I", "ftp-complete", NULL
);
1878 #endif /* USE_LIBEDIT */
1880 remote_path
= do_realpath(conn
, ".");
1881 if (remote_path
== NULL
)
1884 if (file1
!= NULL
) {
1885 dir
= xstrdup(file1
);
1886 dir
= make_absolute(dir
, remote_path
);
1888 if (remote_is_dir(conn
, dir
) && file2
== NULL
) {
1889 printf("Changing to: %s\n", dir
);
1890 snprintf(cmd
, sizeof cmd
, "cd \"%s\"", dir
);
1891 if (parse_dispatch_command(conn
, cmd
,
1892 &remote_path
, 1) != 0) {
1900 snprintf(cmd
, sizeof cmd
, "get %s", dir
);
1902 snprintf(cmd
, sizeof cmd
, "get %s %s", dir
,
1905 err
= parse_dispatch_command(conn
, cmd
,
1915 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1916 setvbuf(stdout
, NULL
, _IOLBF
, 0);
1917 setvbuf(infile
, NULL
, _IOLBF
, 0);
1923 interactive
= !batchmode
&& isatty(STDIN_FILENO
);
1928 signal(SIGINT
, SIG_IGN
);
1933 if (fgets(cmd
, sizeof(cmd
), infile
) == NULL
) {
1938 if (!interactive
) { /* Echo command */
1939 printf("sftp> %s", cmd
);
1940 if (strlen(cmd
) > 0 &&
1941 cmd
[strlen(cmd
) - 1] != '\n')
1949 if ((line
= el_gets(el
, &count
)) == NULL
||
1954 history(hl
, &hev
, H_ENTER
, line
);
1955 if (strlcpy(cmd
, line
, sizeof(cmd
)) >= sizeof(cmd
)) {
1956 fprintf(stderr
, "Error: input line too long\n");
1959 #endif /* USE_LIBEDIT */
1962 cp
= strrchr(cmd
, '\n');
1966 /* Handle user interrupts gracefully during commands */
1968 signal(SIGINT
, cmd_interrupt
);
1970 err
= parse_dispatch_command(conn
, cmd
, &remote_path
,
1981 #endif /* USE_LIBEDIT */
1983 /* err == 1 signifies normal "quit" exit */
1984 return (err
>= 0 ? 0 : -1);
1988 connect_to_server(char *path
, char **args
, int *in
, int *out
)
1993 int pin
[2], pout
[2];
1995 if ((pipe(pin
) == -1) || (pipe(pout
) == -1))
1996 fatal("pipe: %s", strerror(errno
));
2001 #else /* USE_PIPES */
2004 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) == -1)
2005 fatal("socketpair: %s", strerror(errno
));
2006 *in
= *out
= inout
[0];
2007 c_in
= c_out
= inout
[1];
2008 #endif /* USE_PIPES */
2010 if ((sshpid
= fork()) == -1)
2011 fatal("fork: %s", strerror(errno
));
2012 else if (sshpid
== 0) {
2013 if ((dup2(c_in
, STDIN_FILENO
) == -1) ||
2014 (dup2(c_out
, STDOUT_FILENO
) == -1)) {
2015 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
2024 * The underlying ssh is in the same process group, so we must
2025 * ignore SIGINT if we want to gracefully abort commands,
2026 * otherwise the signal will make it to the ssh process and
2029 signal(SIGINT
, SIG_IGN
);
2031 fprintf(stderr
, "exec: %s: %s\n", path
, strerror(errno
));
2035 signal(SIGTERM
, killchild
);
2036 signal(SIGINT
, killchild
);
2037 signal(SIGHUP
, killchild
);
2045 extern char *__progname
;
2048 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2049 " [-D sftp_server_path] [-F ssh_config] "
2050 "[-i identity_file]\n"
2051 " [-o ssh_option] [-P port] [-R num_requests] "
2053 " [-s subsystem | sftp_server] host\n"
2054 " %s [user@]host[:file ...]\n"
2055 " %s [user@]host[:dir[/]]\n"
2056 " %s -b batchfile [user@]host\n",
2057 __progname
, __progname
, __progname
, __progname
);
2062 main(int argc
, char **argv
)
2064 int in
, out
, ch
, err
;
2065 char *host
= NULL
, *userhost
, *cp
, *file2
= NULL
;
2066 int debug_level
= 0, sshver
= 2;
2067 char *file1
= NULL
, *sftp_server
= NULL
;
2068 char *ssh_program
= _PATH_SSH_PROGRAM
, *sftp_direct
= NULL
;
2069 LogLevel ll
= SYSLOG_LEVEL_INFO
;
2072 extern char *optarg
;
2073 struct sftp_conn
*conn
;
2074 size_t copy_buffer_len
= DEFAULT_COPY_BUFLEN
;
2075 size_t num_requests
= DEFAULT_NUM_REQUESTS
;
2077 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2080 __progname
= ssh_get_progname(argv
[0]);
2081 memset(&args
, '\0', sizeof(args
));
2083 addargs(&args
, "%s", ssh_program
);
2084 addargs(&args
, "-oForwardX11 no");
2085 addargs(&args
, "-oForwardAgent no");
2086 addargs(&args
, "-oPermitLocalCommand no");
2087 addargs(&args
, "-oClearAllForwardings yes");
2089 ll
= SYSLOG_LEVEL_INFO
;
2092 while ((ch
= getopt(argc
, argv
,
2093 "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
2095 /* Passed through to ssh(1) */
2099 addargs(&args
, "-%c", ch
);
2101 /* Passed through to ssh(1) with argument */
2106 addargs(&args
, "-%c", ch
);
2107 addargs(&args
, "%s", optarg
);
2111 addargs(&args
, "-%c", ch
);
2114 addargs(&args
, "-oPort %s", optarg
);
2117 if (debug_level
< 3) {
2118 addargs(&args
, "-v");
2119 ll
= SYSLOG_LEVEL_DEBUG1
+ debug_level
;
2125 if (sftp_server
== NULL
)
2126 sftp_server
= _PATH_SFTP_SERVER
;
2132 copy_buffer_len
= strtol(optarg
, &cp
, 10);
2133 if (copy_buffer_len
== 0 || *cp
!= '\0')
2134 fatal("Invalid buffer size \"%s\"", optarg
);
2138 fatal("Batch file already specified.");
2140 /* Allow "-" as stdin */
2141 if (strcmp(optarg
, "-") != 0 &&
2142 (infile
= fopen(optarg
, "r")) == NULL
)
2143 fatal("%s (%s).", strerror(errno
), optarg
);
2146 addargs(&args
, "-obatchmode yes");
2152 sftp_direct
= optarg
;
2158 num_requests
= strtol(optarg
, &cp
, 10);
2159 if (num_requests
== 0 || *cp
!= '\0')
2160 fatal("Invalid number of requests \"%s\"",
2164 sftp_server
= optarg
;
2167 ssh_program
= optarg
;
2168 replacearg(&args
, 0, "%s", ssh_program
);
2176 if (!isatty(STDERR_FILENO
))
2179 log_init(argv
[0], ll
, SYSLOG_FACILITY_USER
, 1);
2181 if (sftp_direct
== NULL
) {
2182 if (optind
== argc
|| argc
> (optind
+ 2))
2185 userhost
= xstrdup(argv
[optind
]);
2186 file2
= argv
[optind
+1];
2188 if ((host
= strrchr(userhost
, '@')) == NULL
)
2193 fprintf(stderr
, "Missing username\n");
2196 addargs(&args
, "-l");
2197 addargs(&args
, "%s", userhost
);
2200 if ((cp
= colon(host
)) != NULL
) {
2205 host
= cleanhostname(host
);
2207 fprintf(stderr
, "Missing hostname\n");
2211 addargs(&args
, "-oProtocol %d", sshver
);
2213 /* no subsystem if the server-spec contains a '/' */
2214 if (sftp_server
== NULL
|| strchr(sftp_server
, '/') == NULL
)
2215 addargs(&args
, "-s");
2217 addargs(&args
, "--");
2218 addargs(&args
, "%s", host
);
2219 addargs(&args
, "%s", (sftp_server
!= NULL
?
2220 sftp_server
: "sftp"));
2222 connect_to_server(ssh_program
, args
.list
, &in
, &out
);
2225 addargs(&args
, "sftp-server");
2227 connect_to_server(sftp_direct
, args
.list
, &in
, &out
);
2231 conn
= do_init(in
, out
, copy_buffer_len
, num_requests
);
2233 fatal("Couldn't initialise connection to server");
2236 if (sftp_direct
== NULL
)
2237 fprintf(stderr
, "Connected to %s.\n", host
);
2239 fprintf(stderr
, "Attached to %s.\n", sftp_direct
);
2242 err
= interactive_loop(conn
, file1
, file2
);
2244 #if !defined(USE_PIPES)
2245 shutdown(in
, SHUT_RDWR
);
2246 shutdown(out
, SHUT_RDWR
);
2254 while (waitpid(sshpid
, NULL
, 0) == -1)
2256 fatal("Couldn't wait for ssh process: %s",
2259 exit(err
== 0 ? 0 : 1);