1 /* $OpenBSD: sftp.c,v 1.121 2010/01/13 12:48:34 jmc 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 { "progress", I_PROGRESS
, NOARGS
},
185 { "put", I_PUT
, LOCAL
},
186 { "pwd", I_PWD
, REMOTE
},
187 { "quit", I_QUIT
, NOARGS
},
188 { "rename", I_RENAME
, REMOTE
},
189 { "rm", I_RM
, REMOTE
},
190 { "rmdir", I_RMDIR
, REMOTE
},
191 { "symlink", I_SYMLINK
, REMOTE
},
192 { "version", I_VERSION
, NOARGS
},
193 { "!", I_SHELL
, NOARGS
},
194 { "?", I_HELP
, NOARGS
},
198 int interactive_loop(struct sftp_conn
*, char *file1
, char *file2
);
205 kill(sshpid
, SIGTERM
);
206 waitpid(sshpid
, NULL
, 0);
214 cmd_interrupt(int signo
)
216 const char msg
[] = "\rInterrupt \n";
217 int olderrno
= errno
;
219 write(STDERR_FILENO
, msg
, sizeof(msg
) - 1);
227 printf("Available commands:\n"
229 "cd path Change remote directory to 'path'\n"
230 "chgrp grp path Change group of file 'path' to 'grp'\n"
231 "chmod mode path Change permissions of file 'path' to 'mode'\n"
232 "chown own path Change owner of file 'path' to 'own'\n"
233 "df [-hi] [path] Display statistics for current directory or\n"
234 " filesystem containing 'path'\n"
236 "get [-Ppr] remote [local] Download file\n"
237 "help Display this help text\n"
238 "lcd path Change local directory to 'path'\n"
239 "lls [ls-options [path]] Display local directory listing\n"
240 "lmkdir path Create local directory\n"
241 "ln oldpath newpath Symlink remote file\n"
242 "lpwd Print local working directory\n"
243 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
244 "lumask umask Set local umask to 'umask'\n"
245 "mkdir path Create remote directory\n"
246 "progress Toggle display of progress meter\n"
247 "put [-Ppr] local [remote] Upload file\n"
248 "pwd Display remote working directory\n"
250 "rename oldpath newpath Rename remote file\n"
251 "rm path Delete remote file\n"
252 "rmdir path Remove remote directory\n"
253 "symlink oldpath newpath Symlink remote file\n"
254 "version Show SFTP version\n"
255 "!command Execute 'command' in local shell\n"
256 "! Escape to local shell\n"
257 "? Synonym for help\n");
261 local_do_shell(const char *args
)
270 if ((shell
= getenv("SHELL")) == NULL
)
271 shell
= _PATH_BSHELL
;
273 if ((pid
= fork()) == -1)
274 fatal("Couldn't fork: %s", strerror(errno
));
277 /* XXX: child has pipe fds to ssh subproc open - issue? */
279 debug3("Executing %s -c \"%s\"", shell
, args
);
280 execl(shell
, shell
, "-c", args
, (char *)NULL
);
282 debug3("Executing %s", shell
);
283 execl(shell
, shell
, (char *)NULL
);
285 fprintf(stderr
, "Couldn't execute \"%s\": %s\n", shell
,
289 while (waitpid(pid
, &status
, 0) == -1)
291 fatal("Couldn't wait for child: %s", strerror(errno
));
292 if (!WIFEXITED(status
))
293 error("Shell exited abnormally");
294 else if (WEXITSTATUS(status
))
295 error("Shell exited with status %d", WEXITSTATUS(status
));
299 local_do_ls(const char *args
)
302 local_do_shell(_PATH_LS
);
304 int len
= strlen(_PATH_LS
" ") + strlen(args
) + 1;
305 char *buf
= xmalloc(len
);
307 /* XXX: quoting - rip quoting code from ftp? */
308 snprintf(buf
, len
, _PATH_LS
" %s", args
);
314 /* Strip one path (usually the pwd) from the start of another */
316 path_strip(char *path
, char *strip
)
321 return (xstrdup(path
));
324 if (strncmp(path
, strip
, len
) == 0) {
325 if (strip
[len
- 1] != '/' && path
[len
] == '/')
327 return (xstrdup(path
+ len
));
330 return (xstrdup(path
));
334 make_absolute(char *p
, char *pwd
)
339 if (p
&& p
[0] != '/') {
340 abs_str
= path_append(pwd
, p
);
348 parse_getput_flags(const char *cmd
, char **argv
, int argc
, int *pflag
,
351 extern int opterr
, optind
, optopt
, optreset
;
354 optind
= optreset
= 1;
358 while ((ch
= getopt(argc
, argv
, "PpRr")) != -1) {
369 error("%s: Invalid flag -%c", cmd
, optopt
);
378 parse_ls_flags(char **argv
, int argc
, int *lflag
)
380 extern int opterr
, optind
, optopt
, optreset
;
383 optind
= optreset
= 1;
386 *lflag
= LS_NAME_SORT
;
387 while ((ch
= getopt(argc
, argv
, "1Safhlnrt")) != -1) {
390 *lflag
&= ~VIEW_FLAGS
;
391 *lflag
|= LS_SHORT_VIEW
;
394 *lflag
&= ~SORT_FLAGS
;
395 *lflag
|= LS_SIZE_SORT
;
398 *lflag
|= LS_SHOW_ALL
;
401 *lflag
&= ~SORT_FLAGS
;
404 *lflag
|= LS_SI_UNITS
;
407 *lflag
&= ~LS_SHORT_VIEW
;
408 *lflag
|= LS_LONG_VIEW
;
411 *lflag
&= ~LS_SHORT_VIEW
;
412 *lflag
|= LS_NUMERIC_VIEW
|LS_LONG_VIEW
;
415 *lflag
|= LS_REVERSE_SORT
;
418 *lflag
&= ~SORT_FLAGS
;
419 *lflag
|= LS_TIME_SORT
;
422 error("ls: Invalid flag -%c", optopt
);
431 parse_df_flags(const char *cmd
, char **argv
, int argc
, int *hflag
, int *iflag
)
433 extern int opterr
, optind
, optopt
, optreset
;
436 optind
= optreset
= 1;
440 while ((ch
= getopt(argc
, argv
, "hi")) != -1) {
449 error("%s: Invalid flag -%c", cmd
, optopt
);
462 /* XXX: report errors? */
463 if (stat(path
, &sb
) == -1)
466 return(S_ISDIR(sb
.st_mode
));
470 remote_is_dir(struct sftp_conn
*conn
, char *path
)
474 /* XXX: report errors? */
475 if ((a
= do_stat(conn
, path
, 1)) == NULL
)
477 if (!(a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
))
479 return(S_ISDIR(a
->perm
));
482 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
484 pathname_is_dir(char *pathname
)
486 size_t l
= strlen(pathname
);
488 return l
> 0 && pathname
[l
- 1] == '/';
492 process_get(struct sftp_conn
*conn
, char *src
, char *dst
, char *pwd
,
493 int pflag
, int rflag
)
495 char *abs_src
= NULL
;
496 char *abs_dst
= NULL
;
498 char *filename
, *tmp
=NULL
;
501 abs_src
= xstrdup(src
);
502 abs_src
= make_absolute(abs_src
, pwd
);
503 memset(&g
, 0, sizeof(g
));
505 debug3("Looking up %s", abs_src
);
506 if (remote_glob(conn
, abs_src
, GLOB_MARK
, NULL
, &g
)) {
507 error("File \"%s\" not found.", abs_src
);
513 * If multiple matches then dst must be a directory or
516 if (g
.gl_matchc
> 1 && dst
!= NULL
&& !is_dir(dst
)) {
517 error("Multiple source paths, but destination "
518 "\"%s\" is not a directory", dst
);
523 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
524 tmp
= xstrdup(g
.gl_pathv
[i
]);
525 if ((filename
= basename(tmp
)) == NULL
) {
526 error("basename %s: %s", tmp
, strerror(errno
));
532 if (g
.gl_matchc
== 1 && dst
) {
534 abs_dst
= path_append(dst
, filename
);
536 abs_dst
= xstrdup(dst
);
539 abs_dst
= path_append(dst
, filename
);
541 abs_dst
= xstrdup(filename
);
545 printf("Fetching %s to %s\n", g
.gl_pathv
[i
], abs_dst
);
546 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
547 if (download_dir(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
548 pflag
|| global_pflag
, 1) == -1)
551 if (do_download(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
552 pflag
|| global_pflag
) == -1)
566 process_put(struct sftp_conn
*conn
, char *src
, char *dst
, char *pwd
,
567 int pflag
, int rflag
)
569 char *tmp_dst
= NULL
;
570 char *abs_dst
= NULL
;
571 char *tmp
= NULL
, *filename
= NULL
;
574 int i
, dst_is_dir
= 1;
578 tmp_dst
= xstrdup(dst
);
579 tmp_dst
= make_absolute(tmp_dst
, pwd
);
582 memset(&g
, 0, sizeof(g
));
583 debug3("Looking up %s", src
);
584 if (glob(src
, GLOB_NOCHECK
| GLOB_MARK
, NULL
, &g
)) {
585 error("File \"%s\" not found.", src
);
590 /* If we aren't fetching to pwd then stash this status for later */
592 dst_is_dir
= remote_is_dir(conn
, tmp_dst
);
594 /* If multiple matches, dst may be directory or unspecified */
595 if (g
.gl_matchc
> 1 && tmp_dst
&& !dst_is_dir
) {
596 error("Multiple paths match, but destination "
597 "\"%s\" is not a directory", tmp_dst
);
602 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
603 if (stat(g
.gl_pathv
[i
], &sb
) == -1) {
605 error("stat %s: %s", g
.gl_pathv
[i
], strerror(errno
));
609 tmp
= xstrdup(g
.gl_pathv
[i
]);
610 if ((filename
= basename(tmp
)) == NULL
) {
611 error("basename %s: %s", tmp
, strerror(errno
));
617 if (g
.gl_matchc
== 1 && tmp_dst
) {
618 /* If directory specified, append filename */
620 abs_dst
= path_append(tmp_dst
, filename
);
622 abs_dst
= xstrdup(tmp_dst
);
623 } else if (tmp_dst
) {
624 abs_dst
= path_append(tmp_dst
, filename
);
626 abs_dst
= make_absolute(xstrdup(filename
), pwd
);
630 printf("Uploading %s to %s\n", g
.gl_pathv
[i
], abs_dst
);
631 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
632 if (upload_dir(conn
, g
.gl_pathv
[i
], abs_dst
,
633 pflag
|| global_pflag
, 1) == -1)
636 if (do_upload(conn
, g
.gl_pathv
[i
], abs_dst
,
637 pflag
|| global_pflag
) == -1)
652 sdirent_comp(const void *aa
, const void *bb
)
654 SFTP_DIRENT
*a
= *(SFTP_DIRENT
**)aa
;
655 SFTP_DIRENT
*b
= *(SFTP_DIRENT
**)bb
;
656 int rmul
= sort_flag
& LS_REVERSE_SORT
? -1 : 1;
658 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
659 if (sort_flag
& LS_NAME_SORT
)
660 return (rmul
* strcmp(a
->filename
, b
->filename
));
661 else if (sort_flag
& LS_TIME_SORT
)
662 return (rmul
* NCMP(a
->a
.mtime
, b
->a
.mtime
));
663 else if (sort_flag
& LS_SIZE_SORT
)
664 return (rmul
* NCMP(a
->a
.size
, b
->a
.size
));
666 fatal("Unknown ls sort type");
669 /* sftp ls.1 replacement for directories */
671 do_ls_dir(struct sftp_conn
*conn
, char *path
, char *strip_path
, int lflag
)
674 u_int c
= 1, colspace
= 0, columns
= 1;
677 if ((n
= do_readdir(conn
, path
, &d
)) != 0)
680 if (!(lflag
& LS_SHORT_VIEW
)) {
681 u_int m
= 0, width
= 80;
685 /* Count entries for sort and find longest filename */
686 for (n
= 0; d
[n
] != NULL
; n
++) {
687 if (d
[n
]->filename
[0] != '.' || (lflag
& LS_SHOW_ALL
))
688 m
= MAX(m
, strlen(d
[n
]->filename
));
691 /* Add any subpath that also needs to be counted */
692 tmp
= path_strip(path
, strip_path
);
696 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
699 columns
= width
/ (m
+ 2);
700 columns
= MAX(columns
, 1);
701 colspace
= width
/ columns
;
702 colspace
= MIN(colspace
, width
);
705 if (lflag
& SORT_FLAGS
) {
706 for (n
= 0; d
[n
] != NULL
; n
++)
707 ; /* count entries */
708 sort_flag
= lflag
& (SORT_FLAGS
|LS_REVERSE_SORT
);
709 qsort(d
, n
, sizeof(*d
), sdirent_comp
);
712 for (n
= 0; d
[n
] != NULL
&& !interrupted
; n
++) {
715 if (d
[n
]->filename
[0] == '.' && !(lflag
& LS_SHOW_ALL
))
718 tmp
= path_append(path
, d
[n
]->filename
);
719 fname
= path_strip(tmp
, strip_path
);
722 if (lflag
& LS_LONG_VIEW
) {
723 if (lflag
& (LS_NUMERIC_VIEW
|LS_SI_UNITS
)) {
727 memset(&sb
, 0, sizeof(sb
));
728 attrib_to_stat(&d
[n
]->a
, &sb
);
729 lname
= ls_file(fname
, &sb
, 1,
730 (lflag
& LS_SI_UNITS
));
731 printf("%s\n", lname
);
734 printf("%s\n", d
[n
]->longname
);
736 printf("%-*s", colspace
, fname
);
747 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
750 free_sftp_dirents(d
);
754 /* sftp ls.1 replacement which handles path globs */
756 do_globbed_ls(struct sftp_conn
*conn
, char *path
, char *strip_path
,
760 u_int i
, c
= 1, colspace
= 0, columns
= 1;
763 memset(&g
, 0, sizeof(g
));
765 if (remote_glob(conn
, path
, GLOB_MARK
|GLOB_NOCHECK
|GLOB_BRACE
,
766 NULL
, &g
) || (g
.gl_pathc
&& !g
.gl_matchc
)) {
769 error("Can't ls: \"%s\" not found", path
);
777 * If the glob returns a single match and it is a directory,
778 * then just list its contents.
780 if (g
.gl_matchc
== 1) {
781 if ((a
= do_lstat(conn
, g
.gl_pathv
[0], 1)) == NULL
) {
785 if ((a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
) &&
789 err
= do_ls_dir(conn
, g
.gl_pathv
[0], strip_path
, lflag
);
795 if (!(lflag
& LS_SHORT_VIEW
)) {
796 u_int m
= 0, width
= 80;
799 /* Count entries for sort and find longest filename */
800 for (i
= 0; g
.gl_pathv
[i
]; i
++)
801 m
= MAX(m
, strlen(g
.gl_pathv
[i
]));
803 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
806 columns
= width
/ (m
+ 2);
807 columns
= MAX(columns
, 1);
808 colspace
= width
/ columns
;
811 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++, a
= NULL
) {
814 fname
= path_strip(g
.gl_pathv
[i
], strip_path
);
816 if (lflag
& LS_LONG_VIEW
) {
821 * XXX: this is slow - 1 roundtrip per path
822 * A solution to this is to fork glob() and
823 * build a sftp specific version which keeps the
824 * attribs (which currently get thrown away)
825 * that the server returns as well as the filenames.
827 memset(&sb
, 0, sizeof(sb
));
829 a
= do_lstat(conn
, g
.gl_pathv
[i
], 1);
831 attrib_to_stat(a
, &sb
);
832 lname
= ls_file(fname
, &sb
, 1, (lflag
& LS_SI_UNITS
));
833 printf("%s\n", lname
);
836 printf("%-*s", colspace
, fname
);
846 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
857 do_df(struct sftp_conn
*conn
, char *path
, int hflag
, int iflag
)
859 struct sftp_statvfs st
;
860 char s_used
[FMT_SCALED_STRSIZE
];
861 char s_avail
[FMT_SCALED_STRSIZE
];
862 char s_root
[FMT_SCALED_STRSIZE
];
863 char s_total
[FMT_SCALED_STRSIZE
];
864 unsigned long long ffree
;
866 if (do_statvfs(conn
, path
, &st
, 1) == -1)
869 ffree
= st
.f_files
? (100 * (st
.f_files
- st
.f_ffree
) / st
.f_files
) : 0;
870 printf(" Inodes Used Avail "
871 "(root) %%Capacity\n");
872 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
873 (unsigned long long)st
.f_files
,
874 (unsigned long long)(st
.f_files
- st
.f_ffree
),
875 (unsigned long long)st
.f_favail
,
876 (unsigned long long)st
.f_ffree
, ffree
);
878 strlcpy(s_used
, "error", sizeof(s_used
));
879 strlcpy(s_avail
, "error", sizeof(s_avail
));
880 strlcpy(s_root
, "error", sizeof(s_root
));
881 strlcpy(s_total
, "error", sizeof(s_total
));
882 fmt_scaled((st
.f_blocks
- st
.f_bfree
) * st
.f_frsize
, s_used
);
883 fmt_scaled(st
.f_bavail
* st
.f_frsize
, s_avail
);
884 fmt_scaled(st
.f_bfree
* st
.f_frsize
, s_root
);
885 fmt_scaled(st
.f_blocks
* st
.f_frsize
, s_total
);
886 printf(" Size Used Avail (root) %%Capacity\n");
887 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
888 s_total
, s_used
, s_avail
, s_root
,
889 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
892 printf(" Size Used Avail "
893 "(root) %%Capacity\n");
894 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
895 (unsigned long long)(st
.f_frsize
* st
.f_blocks
/ 1024),
896 (unsigned long long)(st
.f_frsize
*
897 (st
.f_blocks
- st
.f_bfree
) / 1024),
898 (unsigned long long)(st
.f_frsize
* st
.f_bavail
/ 1024),
899 (unsigned long long)(st
.f_frsize
* st
.f_bfree
/ 1024),
900 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
907 * Undo escaping of glob sequences in place. Used to undo extra escaping
908 * applied in makeargv() when the string is destined for a function that
912 undo_glob_escape(char *s
)
947 * Split a string into an argument vector using sh(1)-style quoting,
948 * comment and escaping rules, but with some tweaks to handle glob(3)
950 * The "sloppy" flag allows for recovery from missing terminating quote, for
951 * use in parsing incomplete commandlines during tab autocompletion.
953 * Returns NULL on error or a NULL-terminated array of arguments.
955 * If "lastquote" is not NULL, the quoting character used for the last
956 * argument is placed in *lastquote ("\0", "'" or "\"").
958 * If "terminated" is not NULL, *terminated will be set to 1 when the
959 * last argument's quote has been properly terminated or 0 otherwise.
960 * This parameter is only of use if "sloppy" is set.
963 #define MAXARGLEN 8192
965 makeargv(const char *arg
, int *argcp
, int sloppy
, char *lastquote
,
970 static char argvs
[MAXARGLEN
];
971 static char *argv
[MAXARGS
+ 1];
972 enum { MA_START
, MA_SQUOTE
, MA_DQUOTE
, MA_UNQUOTED
} state
, q
;
975 if (strlen(arg
) > sizeof(argvs
) - 1) {
977 error("string too long");
980 if (terminated
!= NULL
)
982 if (lastquote
!= NULL
)
987 if (isspace(arg
[i
])) {
988 if (state
== MA_UNQUOTED
) {
989 /* Terminate current argument */
993 } else if (state
!= MA_START
)
995 } else if (arg
[i
] == '"' || arg
[i
] == '\'') {
996 q
= arg
[i
] == '"' ? MA_DQUOTE
: MA_SQUOTE
;
997 if (state
== MA_START
) {
998 argv
[argc
] = argvs
+ j
;
1000 if (lastquote
!= NULL
)
1001 *lastquote
= arg
[i
];
1002 } else if (state
== MA_UNQUOTED
)
1004 else if (state
== q
)
1005 state
= MA_UNQUOTED
;
1007 argvs
[j
++] = arg
[i
];
1008 } else if (arg
[i
] == '\\') {
1009 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1010 quot
= state
== MA_SQUOTE
? '\'' : '"';
1011 /* Unescape quote we are in */
1012 /* XXX support \n and friends? */
1013 if (arg
[i
+ 1] == quot
) {
1015 argvs
[j
++] = arg
[i
];
1016 } else if (arg
[i
+ 1] == '?' ||
1017 arg
[i
+ 1] == '[' || arg
[i
+ 1] == '*') {
1019 * Special case for sftp: append
1020 * double-escaped glob sequence -
1021 * glob will undo one level of
1022 * escaping. NB. string can grow here.
1024 if (j
>= sizeof(argvs
) - 5)
1025 goto args_too_longs
;
1027 argvs
[j
++] = arg
[i
++];
1029 argvs
[j
++] = arg
[i
];
1031 argvs
[j
++] = arg
[i
++];
1032 argvs
[j
++] = arg
[i
];
1035 if (state
== MA_START
) {
1036 argv
[argc
] = argvs
+ j
;
1037 state
= MA_UNQUOTED
;
1038 if (lastquote
!= NULL
)
1041 if (arg
[i
+ 1] == '?' || arg
[i
+ 1] == '[' ||
1042 arg
[i
+ 1] == '*' || arg
[i
+ 1] == '\\') {
1044 * Special case for sftp: append
1045 * escaped glob sequence -
1046 * glob will undo one level of
1049 argvs
[j
++] = arg
[i
++];
1050 argvs
[j
++] = arg
[i
];
1052 /* Unescape everything */
1053 /* XXX support \n and friends? */
1055 argvs
[j
++] = arg
[i
];
1058 } else if (arg
[i
] == '#') {
1059 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
)
1060 argvs
[j
++] = arg
[i
];
1063 } else if (arg
[i
] == '\0') {
1064 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1066 state
= MA_UNQUOTED
;
1067 if (terminated
!= NULL
)
1071 error("Unterminated quoted argument");
1075 if (state
== MA_UNQUOTED
) {
1081 if (state
== MA_START
) {
1082 argv
[argc
] = argvs
+ j
;
1083 state
= MA_UNQUOTED
;
1084 if (lastquote
!= NULL
)
1087 if ((state
== MA_SQUOTE
|| state
== MA_DQUOTE
) &&
1088 (arg
[i
] == '?' || arg
[i
] == '[' || arg
[i
] == '*')) {
1090 * Special case for sftp: escape quoted
1091 * glob(3) wildcards. NB. string can grow
1094 if (j
>= sizeof(argvs
) - 3)
1095 goto args_too_longs
;
1097 argvs
[j
++] = arg
[i
];
1099 argvs
[j
++] = arg
[i
];
1108 parse_args(const char **cpp
, int *pflag
, int *rflag
, int *lflag
, int *iflag
,
1109 int *hflag
, unsigned long *n_arg
, char **path1
, char **path2
)
1111 const char *cmd
, *cp
= *cpp
;
1115 int i
, cmdnum
, optidx
, argc
;
1117 /* Skip leading whitespace */
1118 cp
= cp
+ strspn(cp
, WHITESPACE
);
1120 /* Check for leading '-' (disable error processing) */
1125 cp
= cp
+ strspn(cp
, WHITESPACE
);
1128 /* Ignore blank lines and lines which begin with comment '#' char */
1129 if (*cp
== '\0' || *cp
== '#')
1132 if ((argv
= makeargv(cp
, &argc
, 0, NULL
, NULL
)) == NULL
)
1135 /* Figure out which command we have */
1136 for (i
= 0; cmds
[i
].c
!= NULL
; i
++) {
1137 if (strcasecmp(cmds
[i
].c
, argv
[0]) == 0)
1147 } else if (cmdnum
== -1) {
1148 error("Invalid command.");
1152 /* Get arguments and parse flags */
1153 *lflag
= *pflag
= *rflag
= *hflag
= *n_arg
= 0;
1154 *path1
= *path2
= NULL
;
1159 if ((optidx
= parse_getput_flags(cmd
, argv
, argc
, pflag
, rflag
)) == -1)
1161 /* Get first pathname (mandatory) */
1162 if (argc
- optidx
< 1) {
1163 error("You must specify at least one path after a "
1164 "%s command.", cmd
);
1167 *path1
= xstrdup(argv
[optidx
]);
1168 /* Get second pathname (optional) */
1169 if (argc
- optidx
> 1) {
1170 *path2
= xstrdup(argv
[optidx
+ 1]);
1171 /* Destination is not globbed */
1172 undo_glob_escape(*path2
);
1177 if (argc
- optidx
< 2) {
1178 error("You must specify two paths after a %s "
1182 *path1
= xstrdup(argv
[optidx
]);
1183 *path2
= xstrdup(argv
[optidx
+ 1]);
1184 /* Paths are not globbed */
1185 undo_glob_escape(*path1
);
1186 undo_glob_escape(*path2
);
1194 /* Get pathname (mandatory) */
1195 if (argc
- optidx
< 1) {
1196 error("You must specify a path after a %s command.",
1200 *path1
= xstrdup(argv
[optidx
]);
1201 /* Only "rm" globs */
1203 undo_glob_escape(*path1
);
1206 if ((optidx
= parse_df_flags(cmd
, argv
, argc
, hflag
,
1209 /* Default to current directory if no path specified */
1210 if (argc
- optidx
< 1)
1213 *path1
= xstrdup(argv
[optidx
]);
1214 undo_glob_escape(*path1
);
1218 if ((optidx
= parse_ls_flags(argv
, argc
, lflag
)) == -1)
1220 /* Path is optional */
1221 if (argc
- optidx
> 0)
1222 *path1
= xstrdup(argv
[optidx
]);
1225 /* Skip ls command and following whitespace */
1226 cp
= cp
+ strlen(cmd
) + strspn(cp
, WHITESPACE
);
1228 /* Uses the rest of the line */
1235 /* Get numeric arg (mandatory) */
1236 if (argc
- optidx
< 1)
1239 l
= strtol(argv
[optidx
], &cp2
, base
);
1240 if (cp2
== argv
[optidx
] || *cp2
!= '\0' ||
1241 ((l
== LONG_MIN
|| l
== LONG_MAX
) && errno
== ERANGE
) ||
1244 error("You must supply a numeric argument "
1245 "to the %s command.", cmd
);
1249 if (cmdnum
== I_LUMASK
)
1251 /* Get pathname (mandatory) */
1252 if (argc
- optidx
< 2) {
1253 error("You must specify a path after a %s command.",
1257 *path1
= xstrdup(argv
[optidx
+ 1]);
1267 fatal("Command not implemented");
1275 parse_dispatch_command(struct sftp_conn
*conn
, const char *cmd
, char **pwd
,
1278 char *path1
, *path2
, *tmp
;
1279 int pflag
= 0, rflag
= 0, lflag
= 0, iflag
= 0, hflag
= 0, cmdnum
, i
;
1280 unsigned long n_arg
= 0;
1282 char path_buf
[MAXPATHLEN
];
1286 path1
= path2
= NULL
;
1287 cmdnum
= parse_args(&cmd
, &pflag
, &rflag
, &lflag
, &iflag
, &hflag
, &n_arg
,
1293 memset(&g
, 0, sizeof(g
));
1295 /* Perform command */
1301 /* Unrecognized command */
1305 err
= process_get(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1308 err
= process_put(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1311 path1
= make_absolute(path1
, *pwd
);
1312 path2
= make_absolute(path2
, *pwd
);
1313 err
= do_rename(conn
, path1
, path2
);
1316 path2
= make_absolute(path2
, *pwd
);
1317 err
= do_symlink(conn
, path1
, path2
);
1320 path1
= make_absolute(path1
, *pwd
);
1321 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1322 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1323 printf("Removing %s\n", g
.gl_pathv
[i
]);
1324 err
= do_rm(conn
, g
.gl_pathv
[i
]);
1325 if (err
!= 0 && err_abort
)
1330 path1
= make_absolute(path1
, *pwd
);
1332 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1334 err
= do_mkdir(conn
, path1
, &a
, 1);
1337 path1
= make_absolute(path1
, *pwd
);
1338 err
= do_rmdir(conn
, path1
);
1341 path1
= make_absolute(path1
, *pwd
);
1342 if ((tmp
= do_realpath(conn
, path1
)) == NULL
) {
1346 if ((aa
= do_stat(conn
, tmp
, 0)) == NULL
) {
1351 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
)) {
1352 error("Can't change directory: Can't check target");
1357 if (!S_ISDIR(aa
->perm
)) {
1358 error("Can't change directory: \"%s\" is not "
1359 "a directory", tmp
);
1369 do_globbed_ls(conn
, *pwd
, *pwd
, lflag
);
1373 /* Strip pwd off beginning of non-absolute paths */
1378 path1
= make_absolute(path1
, *pwd
);
1379 err
= do_globbed_ls(conn
, path1
, tmp
, lflag
);
1382 /* Default to current directory if no path specified */
1384 path1
= xstrdup(*pwd
);
1385 path1
= make_absolute(path1
, *pwd
);
1386 err
= do_df(conn
, path1
, hflag
, iflag
);
1389 if (chdir(path1
) == -1) {
1390 error("Couldn't change local directory to "
1391 "\"%s\": %s", path1
, strerror(errno
));
1396 if (mkdir(path1
, 0777) == -1) {
1397 error("Couldn't create local directory "
1398 "\"%s\": %s", path1
, strerror(errno
));
1406 local_do_shell(cmd
);
1410 printf("Local umask: %03lo\n", n_arg
);
1413 path1
= make_absolute(path1
, *pwd
);
1415 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1417 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1418 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1419 printf("Changing mode on %s\n", g
.gl_pathv
[i
]);
1420 err
= do_setstat(conn
, g
.gl_pathv
[i
], &a
);
1421 if (err
!= 0 && err_abort
)
1427 path1
= make_absolute(path1
, *pwd
);
1428 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1429 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1430 if (!(aa
= do_stat(conn
, g
.gl_pathv
[i
], 0))) {
1437 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_UIDGID
)) {
1438 error("Can't get current ownership of "
1439 "remote file \"%s\"", g
.gl_pathv
[i
]);
1446 aa
->flags
&= SSH2_FILEXFER_ATTR_UIDGID
;
1447 if (cmdnum
== I_CHOWN
) {
1448 printf("Changing owner on %s\n", g
.gl_pathv
[i
]);
1451 printf("Changing group on %s\n", g
.gl_pathv
[i
]);
1454 err
= do_setstat(conn
, g
.gl_pathv
[i
], aa
);
1455 if (err
!= 0 && err_abort
)
1460 printf("Remote working directory: %s\n", *pwd
);
1463 if (!getcwd(path_buf
, sizeof(path_buf
))) {
1464 error("Couldn't get local cwd: %s", strerror(errno
));
1468 printf("Local working directory: %s\n", path_buf
);
1471 /* Processed below */
1477 printf("SFTP protocol version %u\n", sftp_proto_version(conn
));
1480 showprogress
= !showprogress
;
1482 printf("Progress meter enabled\n");
1484 printf("Progress meter disabled\n");
1487 fatal("%d is not implemented", cmdnum
);
1497 /* If an unignored error occurs in batch mode we should abort. */
1498 if (err_abort
&& err
!= 0)
1500 else if (cmdnum
== I_QUIT
)
1508 prompt(EditLine
*el
)
1513 /* Display entries in 'list' after skipping the first 'len' chars */
1515 complete_display(char **list
, u_int len
)
1517 u_int y
, m
= 0, width
= 80, columns
= 1, colspace
= 0, llen
;
1521 /* Count entries for sort and find longest */
1522 for (y
= 0; list
[y
]; y
++)
1523 m
= MAX(m
, strlen(list
[y
]));
1525 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
1528 m
= m
> len
? m
- len
: 0;
1529 columns
= width
/ (m
+ 2);
1530 columns
= MAX(columns
, 1);
1531 colspace
= width
/ columns
;
1532 colspace
= MIN(colspace
, width
);
1536 for (y
= 0; list
[y
]; y
++) {
1537 llen
= strlen(list
[y
]);
1538 tmp
= llen
> len
? list
[y
] + len
: "";
1539 printf("%-*s", colspace
, tmp
);
1550 * Given a "list" of words that begin with a common prefix of "word",
1551 * attempt to find an autocompletion to extends "word" by the next
1552 * characters common to all entries in "list".
1555 complete_ambiguous(const char *word
, char **list
, size_t count
)
1561 u_int y
, matchlen
= strlen(list
[0]);
1563 /* Find length of common stem */
1564 for (y
= 1; list
[y
]; y
++) {
1567 for (x
= 0; x
< matchlen
; x
++)
1568 if (list
[0][x
] != list
[y
][x
])
1574 if (matchlen
> strlen(word
)) {
1575 char *tmp
= xstrdup(list
[0]);
1577 tmp
[matchlen
] = '\0';
1582 return xstrdup(word
);
1585 /* Autocomplete a sftp command */
1587 complete_cmd_parse(EditLine
*el
, char *cmd
, int lastarg
, char quote
,
1590 u_int y
, count
= 0, cmdlen
, tmplen
;
1591 char *tmp
, **list
, argterm
[3];
1594 list
= xcalloc((sizeof(cmds
) / sizeof(*cmds
)) + 1, sizeof(char *));
1596 /* No command specified: display all available commands */
1598 for (y
= 0; cmds
[y
].c
; y
++)
1599 list
[count
++] = xstrdup(cmds
[y
].c
);
1602 complete_display(list
, 0);
1604 for (y
= 0; list
[y
] != NULL
; y
++)
1610 /* Prepare subset of commands that start with "cmd" */
1611 cmdlen
= strlen(cmd
);
1612 for (y
= 0; cmds
[y
].c
; y
++) {
1613 if (!strncasecmp(cmd
, cmds
[y
].c
, cmdlen
))
1614 list
[count
++] = xstrdup(cmds
[y
].c
);
1621 /* Complete ambigious command */
1622 tmp
= complete_ambiguous(cmd
, list
, count
);
1624 complete_display(list
, 0);
1626 for (y
= 0; list
[y
]; y
++)
1631 tmplen
= strlen(tmp
);
1632 cmdlen
= strlen(cmd
);
1633 /* If cmd may be extended then do so */
1634 if (tmplen
> cmdlen
)
1635 if (el_insertstr(el
, tmp
+ cmdlen
) == -1)
1636 fatal("el_insertstr failed.");
1638 /* Terminate argument cleanly */
1642 argterm
[y
++] = quote
;
1643 if (lastarg
|| *(lf
->cursor
) != ' ')
1646 if (y
> 0 && el_insertstr(el
, argterm
) == -1)
1647 fatal("el_insertstr failed.");
1656 * Determine whether a particular sftp command's arguments (if any)
1657 * represent local or remote files.
1660 complete_is_remote(char *cmd
) {
1666 for (i
= 0; cmds
[i
].c
; i
++) {
1667 if (!strncasecmp(cmd
, cmds
[i
].c
, strlen(cmds
[i
].c
)))
1674 /* Autocomplete a filename "file" */
1676 complete_match(EditLine
*el
, struct sftp_conn
*conn
, char *remote_path
,
1677 char *file
, int remote
, int lastarg
, char quote
, int terminated
)
1680 char *tmp
, *tmp2
, ins
[3];
1681 u_int i
, hadglob
, pwdlen
, len
, tmplen
, filelen
;
1684 /* Glob from "file" location */
1688 xasprintf(&tmp
, "%s*", file
);
1690 memset(&g
, 0, sizeof(g
));
1691 if (remote
!= LOCAL
) {
1692 tmp
= make_absolute(tmp
, remote_path
);
1693 remote_glob(conn
, tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1695 glob(tmp
, GLOB_DOOFFS
|GLOB_MARK
, NULL
, &g
);
1697 /* Determine length of pwd so we can trim completion display */
1698 for (hadglob
= tmplen
= pwdlen
= 0; tmp
[tmplen
] != 0; tmplen
++) {
1699 /* Terminate counting on first unescaped glob metacharacter */
1700 if (tmp
[tmplen
] == '*' || tmp
[tmplen
] == '?') {
1701 if (tmp
[tmplen
] != '*' || tmp
[tmplen
+ 1] != '\0')
1705 if (tmp
[tmplen
] == '\\' && tmp
[tmplen
+ 1] != '\0')
1707 if (tmp
[tmplen
] == '/')
1708 pwdlen
= tmplen
+ 1; /* track last seen '/' */
1712 if (g
.gl_matchc
== 0)
1715 if (g
.gl_matchc
> 1)
1716 complete_display(g
.gl_pathv
, pwdlen
);
1719 /* Don't try to extend globs */
1720 if (file
== NULL
|| hadglob
)
1723 tmp2
= complete_ambiguous(file
, g
.gl_pathv
, g
.gl_matchc
);
1724 tmp
= path_strip(tmp2
, remote_path
);
1730 tmplen
= strlen(tmp
);
1731 filelen
= strlen(file
);
1733 if (tmplen
> filelen
) {
1734 tmp2
= tmp
+ filelen
;
1736 /* quote argument on way out */
1737 for (i
= 0; i
< len
; i
++) {
1747 if (quote
== '\0' || tmp2
[i
] == quote
) {
1748 if (el_insertstr(el
, ins
) == -1)
1749 fatal("el_insertstr "
1755 if (el_insertstr(el
, ins
+ 1) == -1)
1756 fatal("el_insertstr failed.");
1763 if (g
.gl_matchc
== 1) {
1767 if (*(lf
->cursor
- 1) != '/' &&
1768 (lastarg
|| *(lf
->cursor
) != ' '))
1771 if (i
> 0 && el_insertstr(el
, ins
) == -1)
1772 fatal("el_insertstr failed.");
1781 /* tab-completion hook function, called via libedit */
1782 static unsigned char
1783 complete(EditLine
*el
, int ch
)
1785 char **argv
, *line
, quote
;
1786 u_int argc
, carg
, cursor
, len
, terminated
, ret
= CC_ERROR
;
1788 struct complete_ctx
*complete_ctx
;
1791 if (el_get(el
, EL_CLIENTDATA
, (void**)&complete_ctx
) != 0)
1792 fatal("%s: el_get failed", __func__
);
1794 /* Figure out which argument the cursor points to */
1795 cursor
= lf
->cursor
- lf
->buffer
;
1796 line
= (char *)xmalloc(cursor
+ 1);
1797 memcpy(line
, lf
->buffer
, cursor
);
1798 line
[cursor
] = '\0';
1799 argv
= makeargv(line
, &carg
, 1, "e
, &terminated
);
1802 /* Get all the arguments on the line */
1803 len
= lf
->lastchar
- lf
->buffer
;
1804 line
= (char *)xmalloc(len
+ 1);
1805 memcpy(line
, lf
->buffer
, len
);
1807 argv
= makeargv(line
, &argc
, 1, NULL
, NULL
);
1809 /* Ensure cursor is at EOL or a argument boundary */
1810 if (line
[cursor
] != ' ' && line
[cursor
] != '\0' &&
1811 line
[cursor
] != '\n') {
1817 /* Show all available commands */
1818 complete_cmd_parse(el
, NULL
, argc
== carg
, '\0', 1);
1820 } else if (carg
== 1 && cursor
> 0 && line
[cursor
- 1] != ' ') {
1821 /* Handle the command parsing */
1822 if (complete_cmd_parse(el
, argv
[0], argc
== carg
,
1823 quote
, terminated
) != 0)
1825 } else if (carg
>= 1) {
1826 /* Handle file parsing */
1827 int remote
= complete_is_remote(argv
[0]);
1828 char *filematch
= NULL
;
1830 if (carg
> 1 && line
[cursor
-1] != ' ')
1831 filematch
= argv
[carg
- 1];
1834 complete_match(el
, complete_ctx
->conn
,
1835 *complete_ctx
->remote_pathp
, filematch
,
1836 remote
, carg
== argc
, quote
, terminated
) != 0)
1843 #endif /* USE_LIBEDIT */
1846 interactive_loop(struct sftp_conn
*conn
, char *file1
, char *file2
)
1851 int err
, interactive
;
1852 EditLine
*el
= NULL
;
1856 extern char *__progname
;
1857 struct complete_ctx complete_ctx
;
1859 if (!batchmode
&& isatty(STDIN_FILENO
)) {
1860 if ((el
= el_init(__progname
, stdin
, stdout
, stderr
)) == NULL
)
1861 fatal("Couldn't initialise editline");
1862 if ((hl
= history_init()) == NULL
)
1863 fatal("Couldn't initialise editline history");
1864 history(hl
, &hev
, H_SETSIZE
, 100);
1865 el_set(el
, EL_HIST
, history
, hl
);
1867 el_set(el
, EL_PROMPT
, prompt
);
1868 el_set(el
, EL_EDITOR
, "emacs");
1869 el_set(el
, EL_TERMINAL
, NULL
);
1870 el_set(el
, EL_SIGNAL
, 1);
1871 el_source(el
, NULL
);
1873 /* Tab Completion */
1874 el_set(el
, EL_ADDFN
, "ftp-complete",
1875 "Context senstive argument completion", complete
);
1876 complete_ctx
.conn
= conn
;
1877 complete_ctx
.remote_pathp
= &remote_path
;
1878 el_set(el
, EL_CLIENTDATA
, (void*)&complete_ctx
);
1879 el_set(el
, EL_BIND
, "^I", "ftp-complete", NULL
);
1881 #endif /* USE_LIBEDIT */
1883 remote_path
= do_realpath(conn
, ".");
1884 if (remote_path
== NULL
)
1887 if (file1
!= NULL
) {
1888 dir
= xstrdup(file1
);
1889 dir
= make_absolute(dir
, remote_path
);
1891 if (remote_is_dir(conn
, dir
) && file2
== NULL
) {
1892 printf("Changing to: %s\n", dir
);
1893 snprintf(cmd
, sizeof cmd
, "cd \"%s\"", dir
);
1894 if (parse_dispatch_command(conn
, cmd
,
1895 &remote_path
, 1) != 0) {
1903 snprintf(cmd
, sizeof cmd
, "get %s", dir
);
1905 snprintf(cmd
, sizeof cmd
, "get %s %s", dir
,
1908 err
= parse_dispatch_command(conn
, cmd
,
1918 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1919 setvbuf(stdout
, NULL
, _IOLBF
, 0);
1920 setvbuf(infile
, NULL
, _IOLBF
, 0);
1926 interactive
= !batchmode
&& isatty(STDIN_FILENO
);
1931 signal(SIGINT
, SIG_IGN
);
1936 if (fgets(cmd
, sizeof(cmd
), infile
) == NULL
) {
1941 if (!interactive
) { /* Echo command */
1942 printf("sftp> %s", cmd
);
1943 if (strlen(cmd
) > 0 &&
1944 cmd
[strlen(cmd
) - 1] != '\n')
1952 if ((line
= el_gets(el
, &count
)) == NULL
||
1957 history(hl
, &hev
, H_ENTER
, line
);
1958 if (strlcpy(cmd
, line
, sizeof(cmd
)) >= sizeof(cmd
)) {
1959 fprintf(stderr
, "Error: input line too long\n");
1962 #endif /* USE_LIBEDIT */
1965 cp
= strrchr(cmd
, '\n');
1969 /* Handle user interrupts gracefully during commands */
1971 signal(SIGINT
, cmd_interrupt
);
1973 err
= parse_dispatch_command(conn
, cmd
, &remote_path
,
1984 #endif /* USE_LIBEDIT */
1986 /* err == 1 signifies normal "quit" exit */
1987 return (err
>= 0 ? 0 : -1);
1991 connect_to_server(char *path
, char **args
, int *in
, int *out
)
1996 int pin
[2], pout
[2];
1998 if ((pipe(pin
) == -1) || (pipe(pout
) == -1))
1999 fatal("pipe: %s", strerror(errno
));
2004 #else /* USE_PIPES */
2007 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) == -1)
2008 fatal("socketpair: %s", strerror(errno
));
2009 *in
= *out
= inout
[0];
2010 c_in
= c_out
= inout
[1];
2011 #endif /* USE_PIPES */
2013 if ((sshpid
= fork()) == -1)
2014 fatal("fork: %s", strerror(errno
));
2015 else if (sshpid
== 0) {
2016 if ((dup2(c_in
, STDIN_FILENO
) == -1) ||
2017 (dup2(c_out
, STDOUT_FILENO
) == -1)) {
2018 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
2027 * The underlying ssh is in the same process group, so we must
2028 * ignore SIGINT if we want to gracefully abort commands,
2029 * otherwise the signal will make it to the ssh process and
2032 signal(SIGINT
, SIG_IGN
);
2034 fprintf(stderr
, "exec: %s: %s\n", path
, strerror(errno
));
2038 signal(SIGTERM
, killchild
);
2039 signal(SIGINT
, killchild
);
2040 signal(SIGHUP
, killchild
);
2048 extern char *__progname
;
2051 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2052 " [-D sftp_server_path] [-F ssh_config] "
2053 "[-i identity_file]\n"
2054 " [-o ssh_option] [-P port] [-R num_requests] "
2056 " [-s subsystem | sftp_server] host\n"
2057 " %s [user@]host[:file ...]\n"
2058 " %s [user@]host[:dir[/]]\n"
2059 " %s -b batchfile [user@]host\n",
2060 __progname
, __progname
, __progname
, __progname
);
2065 main(int argc
, char **argv
)
2067 int in
, out
, ch
, err
;
2068 char *host
= NULL
, *userhost
, *cp
, *file2
= NULL
;
2069 int debug_level
= 0, sshver
= 2;
2070 char *file1
= NULL
, *sftp_server
= NULL
;
2071 char *ssh_program
= _PATH_SSH_PROGRAM
, *sftp_direct
= NULL
;
2072 LogLevel ll
= SYSLOG_LEVEL_INFO
;
2075 extern char *optarg
;
2076 struct sftp_conn
*conn
;
2077 size_t copy_buffer_len
= DEFAULT_COPY_BUFLEN
;
2078 size_t num_requests
= DEFAULT_NUM_REQUESTS
;
2080 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2083 __progname
= ssh_get_progname(argv
[0]);
2084 memset(&args
, '\0', sizeof(args
));
2086 addargs(&args
, "%s", ssh_program
);
2087 addargs(&args
, "-oForwardX11 no");
2088 addargs(&args
, "-oForwardAgent no");
2089 addargs(&args
, "-oPermitLocalCommand no");
2090 addargs(&args
, "-oClearAllForwardings yes");
2092 ll
= SYSLOG_LEVEL_INFO
;
2095 while ((ch
= getopt(argc
, argv
,
2096 "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
2098 /* Passed through to ssh(1) */
2102 addargs(&args
, "-%c", ch
);
2104 /* Passed through to ssh(1) with argument */
2109 addargs(&args
, "-%c", ch
);
2110 addargs(&args
, "%s", optarg
);
2114 addargs(&args
, "-%c", ch
);
2117 addargs(&args
, "-oPort %s", optarg
);
2120 if (debug_level
< 3) {
2121 addargs(&args
, "-v");
2122 ll
= SYSLOG_LEVEL_DEBUG1
+ debug_level
;
2128 if (sftp_server
== NULL
)
2129 sftp_server
= _PATH_SFTP_SERVER
;
2135 copy_buffer_len
= strtol(optarg
, &cp
, 10);
2136 if (copy_buffer_len
== 0 || *cp
!= '\0')
2137 fatal("Invalid buffer size \"%s\"", optarg
);
2141 fatal("Batch file already specified.");
2143 /* Allow "-" as stdin */
2144 if (strcmp(optarg
, "-") != 0 &&
2145 (infile
= fopen(optarg
, "r")) == NULL
)
2146 fatal("%s (%s).", strerror(errno
), optarg
);
2149 addargs(&args
, "-obatchmode yes");
2155 sftp_direct
= optarg
;
2161 num_requests
= strtol(optarg
, &cp
, 10);
2162 if (num_requests
== 0 || *cp
!= '\0')
2163 fatal("Invalid number of requests \"%s\"",
2167 sftp_server
= optarg
;
2170 ssh_program
= optarg
;
2171 replacearg(&args
, 0, "%s", ssh_program
);
2179 if (!isatty(STDERR_FILENO
))
2182 log_init(argv
[0], ll
, SYSLOG_FACILITY_USER
, 1);
2184 if (sftp_direct
== NULL
) {
2185 if (optind
== argc
|| argc
> (optind
+ 2))
2188 userhost
= xstrdup(argv
[optind
]);
2189 file2
= argv
[optind
+1];
2191 if ((host
= strrchr(userhost
, '@')) == NULL
)
2196 fprintf(stderr
, "Missing username\n");
2199 addargs(&args
, "-l");
2200 addargs(&args
, "%s", userhost
);
2203 if ((cp
= colon(host
)) != NULL
) {
2208 host
= cleanhostname(host
);
2210 fprintf(stderr
, "Missing hostname\n");
2214 addargs(&args
, "-oProtocol %d", sshver
);
2216 /* no subsystem if the server-spec contains a '/' */
2217 if (sftp_server
== NULL
|| strchr(sftp_server
, '/') == NULL
)
2218 addargs(&args
, "-s");
2220 addargs(&args
, "--");
2221 addargs(&args
, "%s", host
);
2222 addargs(&args
, "%s", (sftp_server
!= NULL
?
2223 sftp_server
: "sftp"));
2225 connect_to_server(ssh_program
, args
.list
, &in
, &out
);
2228 addargs(&args
, "sftp-server");
2230 connect_to_server(sftp_direct
, args
.list
, &in
, &out
);
2234 conn
= do_init(in
, out
, copy_buffer_len
, num_requests
);
2236 fatal("Couldn't initialise connection to server");
2239 if (sftp_direct
== NULL
)
2240 fprintf(stderr
, "Connected to %s.\n", host
);
2242 fprintf(stderr
, "Attached to %s.\n", sftp_direct
);
2245 err
= interactive_loop(conn
, file1
, file2
);
2247 #if !defined(USE_PIPES)
2248 shutdown(in
, SHUT_RDWR
);
2249 shutdown(out
, SHUT_RDWR
);
2257 while (waitpid(sshpid
, NULL
, 0) == -1)
2259 fatal("Couldn't wait for ssh process: %s",
2262 exit(err
== 0 ? 0 : 1);