1 /* $OpenBSD: sftp.c,v 1.111 2009/08/18 18:36:21 djm Exp $ */
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
25 #include <sys/param.h>
26 #include <sys/socket.h>
28 #ifdef HAVE_SYS_STATVFS_H
29 #include <sys/statvfs.h>
44 typedef void EditLine
;
63 #include "pathnames.h"
68 #include "sftp-common.h"
69 #include "sftp-client.h"
71 /* File to read commands from */
74 /* Are we in batchfile mode? */
77 /* Size of buffer used when copying files */
78 size_t copy_buffer_len
= 32768;
80 /* Number of concurrent outstanding requests */
81 size_t num_requests
= 64;
83 /* PID of ssh transport process */
84 static pid_t sshpid
= -1;
86 /* This is set to 0 if the progressmeter is not desired. */
89 /* When this option is set, we always recursively download/upload directories */
92 /* When this option is set, the file transfers will always preserve times */
95 /* SIGINT received during command processing */
96 volatile sig_atomic_t interrupted
= 0;
98 /* I wish qsort() took a separate ctx for the comparison function...*/
101 int remote_glob(struct sftp_conn
*, const char *, int,
102 int (*)(const char *, int), glob_t
*); /* proto for sftp-glob.c */
104 extern char *__progname
;
106 /* Separators for interactive commands */
107 #define WHITESPACE " \t\r\n"
110 #define LS_LONG_VIEW 0x01 /* Full view ala ls -l */
111 #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */
112 #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */
113 #define LS_NAME_SORT 0x08 /* Sort by name (default) */
114 #define LS_TIME_SORT 0x10 /* Sort by mtime */
115 #define LS_SIZE_SORT 0x20 /* Sort by file size */
116 #define LS_REVERSE_SORT 0x40 /* Reverse sort order */
117 #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */
119 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
120 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
122 /* Commands for interactive mode */
146 #define I_PROGRESS 23
153 static const struct CMD cmds
[] = {
156 { "chdir", I_CHDIR
},
157 { "chgrp", I_CHGRP
},
158 { "chmod", I_CHMOD
},
159 { "chown", I_CHOWN
},
167 { "lchdir", I_LCHDIR
},
169 { "lmkdir", I_LMKDIR
},
173 { "lumask", I_LUMASK
},
174 { "mkdir", I_MKDIR
},
175 { "progress", I_PROGRESS
},
180 { "rename", I_RENAME
},
182 { "rmdir", I_RMDIR
},
183 { "symlink", I_SYMLINK
},
184 { "version", I_VERSION
},
190 int interactive_loop(int fd_in
, int fd_out
, char *file1
, char *file2
);
197 kill(sshpid
, SIGTERM
);
198 waitpid(sshpid
, NULL
, 0);
206 cmd_interrupt(int signo
)
208 const char msg
[] = "\rInterrupt \n";
209 int olderrno
= errno
;
211 write(STDERR_FILENO
, msg
, sizeof(msg
) - 1);
219 printf("Available commands:\n"
221 "cd path Change remote directory to 'path'\n"
222 "chgrp grp path Change group of file 'path' to 'grp'\n"
223 "chmod mode path Change permissions of file 'path' to 'mode'\n"
224 "chown own path Change owner of file 'path' to 'own'\n"
225 "df [-hi] [path] Display statistics for current directory or\n"
226 " filesystem containing 'path'\n"
228 "get [-Pr] remote-path [local-path] Download file\n"
229 "help Display this help text\n"
230 "lcd path Change local directory to 'path'\n"
231 "lls [ls-options [path]] Display local directory listing\n"
232 "lmkdir path Create local directory\n"
233 "ln oldpath newpath Symlink remote file\n"
234 "lpwd Print local working directory\n"
235 "ls [-1aflnrSt] [path] Display remote directory listing\n"
236 "lumask umask Set local umask to 'umask'\n"
237 "mkdir path Create remote directory\n"
238 "progress Toggle display of progress meter\n"
239 "put [-Pr] local-path [remote-path] Upload file\n"
240 "pwd Display remote working directory\n"
242 "rename oldpath newpath Rename remote file\n"
243 "rm path Delete remote file\n"
244 "rmdir path Remove remote directory\n"
245 "symlink oldpath newpath Symlink remote file\n"
246 "version Show SFTP version\n"
247 "!command Execute 'command' in local shell\n"
248 "! Escape to local shell\n"
249 "? Synonym for help\n");
253 local_do_shell(const char *args
)
262 if ((shell
= getenv("SHELL")) == NULL
)
263 shell
= _PATH_BSHELL
;
265 if ((pid
= fork()) == -1)
266 fatal("Couldn't fork: %s", strerror(errno
));
269 /* XXX: child has pipe fds to ssh subproc open - issue? */
271 debug3("Executing %s -c \"%s\"", shell
, args
);
272 execl(shell
, shell
, "-c", args
, (char *)NULL
);
274 debug3("Executing %s", shell
);
275 execl(shell
, shell
, (char *)NULL
);
277 fprintf(stderr
, "Couldn't execute \"%s\": %s\n", shell
,
281 while (waitpid(pid
, &status
, 0) == -1)
283 fatal("Couldn't wait for child: %s", strerror(errno
));
284 if (!WIFEXITED(status
))
285 error("Shell exited abnormally");
286 else if (WEXITSTATUS(status
))
287 error("Shell exited with status %d", WEXITSTATUS(status
));
291 local_do_ls(const char *args
)
294 local_do_shell(_PATH_LS
);
296 int len
= strlen(_PATH_LS
" ") + strlen(args
) + 1;
297 char *buf
= xmalloc(len
);
299 /* XXX: quoting - rip quoting code from ftp? */
300 snprintf(buf
, len
, _PATH_LS
" %s", args
);
306 /* Strip one path (usually the pwd) from the start of another */
308 path_strip(char *path
, char *strip
)
313 return (xstrdup(path
));
316 if (strncmp(path
, strip
, len
) == 0) {
317 if (strip
[len
- 1] != '/' && path
[len
] == '/')
319 return (xstrdup(path
+ len
));
322 return (xstrdup(path
));
326 make_absolute(char *p
, char *pwd
)
331 if (p
&& p
[0] != '/') {
332 abs_str
= path_append(pwd
, p
);
340 parse_getput_flags(const char *cmd
, char **argv
, int argc
, int *pflag
,
343 extern int opterr
, optind
, optopt
, optreset
;
346 optind
= optreset
= 1;
350 while ((ch
= getopt(argc
, argv
, "PpRr")) != -1) {
361 error("%s: Invalid flag -%c", cmd
, optopt
);
370 parse_ls_flags(char **argv
, int argc
, int *lflag
)
372 extern int opterr
, optind
, optopt
, optreset
;
375 optind
= optreset
= 1;
378 *lflag
= LS_NAME_SORT
;
379 while ((ch
= getopt(argc
, argv
, "1Saflnrt")) != -1) {
382 *lflag
&= ~VIEW_FLAGS
;
383 *lflag
|= LS_SHORT_VIEW
;
386 *lflag
&= ~SORT_FLAGS
;
387 *lflag
|= LS_SIZE_SORT
;
390 *lflag
|= LS_SHOW_ALL
;
393 *lflag
&= ~SORT_FLAGS
;
396 *lflag
&= ~VIEW_FLAGS
;
397 *lflag
|= LS_LONG_VIEW
;
400 *lflag
&= ~VIEW_FLAGS
;
401 *lflag
|= LS_NUMERIC_VIEW
|LS_LONG_VIEW
;
404 *lflag
|= LS_REVERSE_SORT
;
407 *lflag
&= ~SORT_FLAGS
;
408 *lflag
|= LS_TIME_SORT
;
411 error("ls: Invalid flag -%c", optopt
);
420 parse_df_flags(const char *cmd
, char **argv
, int argc
, int *hflag
, int *iflag
)
422 extern int opterr
, optind
, optopt
, optreset
;
425 optind
= optreset
= 1;
429 while ((ch
= getopt(argc
, argv
, "hi")) != -1) {
438 error("%s: Invalid flag -%c", cmd
, optopt
);
451 /* XXX: report errors? */
452 if (stat(path
, &sb
) == -1)
455 return(S_ISDIR(sb
.st_mode
));
459 remote_is_dir(struct sftp_conn
*conn
, char *path
)
463 /* XXX: report errors? */
464 if ((a
= do_stat(conn
, path
, 1)) == NULL
)
466 if (!(a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
))
468 return(S_ISDIR(a
->perm
));
471 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
473 pathname_is_dir(char *pathname
)
475 size_t l
= strlen(pathname
);
477 return l
> 0 && pathname
[l
- 1] == '/';
481 process_get(struct sftp_conn
*conn
, char *src
, char *dst
, char *pwd
,
482 int pflag
, int rflag
)
484 char *abs_src
= NULL
;
485 char *abs_dst
= NULL
;
487 char *filename
, *tmp
=NULL
;
490 abs_src
= xstrdup(src
);
491 abs_src
= make_absolute(abs_src
, pwd
);
492 memset(&g
, 0, sizeof(g
));
494 debug3("Looking up %s", abs_src
);
495 if (remote_glob(conn
, abs_src
, GLOB_MARK
, NULL
, &g
)) {
496 error("File \"%s\" not found.", abs_src
);
502 * If multiple matches then dst must be a directory or
505 if (g
.gl_matchc
> 1 && dst
!= NULL
&& !is_dir(dst
)) {
506 error("Multiple source paths, but destination "
507 "\"%s\" is not a directory", dst
);
512 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
513 tmp
= xstrdup(g
.gl_pathv
[i
]);
514 if ((filename
= basename(tmp
)) == NULL
) {
515 error("basename %s: %s", tmp
, strerror(errno
));
521 if (g
.gl_matchc
== 1 && dst
) {
523 abs_dst
= path_append(dst
, filename
);
525 abs_dst
= xstrdup(dst
);
528 abs_dst
= path_append(dst
, filename
);
530 abs_dst
= xstrdup(filename
);
534 printf("Fetching %s to %s\n", g
.gl_pathv
[i
], abs_dst
);
535 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
536 if (download_dir(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
537 pflag
|| global_pflag
, 1) == -1)
540 if (do_download(conn
, g
.gl_pathv
[i
], abs_dst
, NULL
,
541 pflag
|| global_pflag
) == -1)
555 process_put(struct sftp_conn
*conn
, char *src
, char *dst
, char *pwd
,
556 int pflag
, int rflag
)
558 char *tmp_dst
= NULL
;
559 char *abs_dst
= NULL
;
560 char *tmp
= NULL
, *filename
= NULL
;
563 int i
, dst_is_dir
= 1;
567 tmp_dst
= xstrdup(dst
);
568 tmp_dst
= make_absolute(tmp_dst
, pwd
);
571 memset(&g
, 0, sizeof(g
));
572 debug3("Looking up %s", src
);
573 if (glob(src
, GLOB_NOCHECK
| GLOB_MARK
, NULL
, &g
)) {
574 error("File \"%s\" not found.", src
);
579 /* If we aren't fetching to pwd then stash this status for later */
581 dst_is_dir
= remote_is_dir(conn
, tmp_dst
);
583 /* If multiple matches, dst may be directory or unspecified */
584 if (g
.gl_matchc
> 1 && tmp_dst
&& !dst_is_dir
) {
585 error("Multiple paths match, but destination "
586 "\"%s\" is not a directory", tmp_dst
);
591 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
592 if (stat(g
.gl_pathv
[i
], &sb
) == -1) {
594 error("stat %s: %s", g
.gl_pathv
[i
], strerror(errno
));
598 tmp
= xstrdup(g
.gl_pathv
[i
]);
599 if ((filename
= basename(tmp
)) == NULL
) {
600 error("basename %s: %s", tmp
, strerror(errno
));
606 if (g
.gl_matchc
== 1 && tmp_dst
) {
607 /* If directory specified, append filename */
609 abs_dst
= path_append(tmp_dst
, filename
);
611 abs_dst
= xstrdup(tmp_dst
);
612 } else if (tmp_dst
) {
613 abs_dst
= path_append(tmp_dst
, filename
);
615 abs_dst
= make_absolute(xstrdup(filename
), pwd
);
619 printf("Uploading %s to %s\n", g
.gl_pathv
[i
], abs_dst
);
620 if (pathname_is_dir(g
.gl_pathv
[i
]) && (rflag
|| global_rflag
)) {
621 if (upload_dir(conn
, g
.gl_pathv
[i
], abs_dst
,
622 pflag
|| global_pflag
, 1) == -1)
625 if (do_upload(conn
, g
.gl_pathv
[i
], abs_dst
,
626 pflag
|| global_pflag
) == -1)
641 sdirent_comp(const void *aa
, const void *bb
)
643 SFTP_DIRENT
*a
= *(SFTP_DIRENT
**)aa
;
644 SFTP_DIRENT
*b
= *(SFTP_DIRENT
**)bb
;
645 int rmul
= sort_flag
& LS_REVERSE_SORT
? -1 : 1;
647 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
648 if (sort_flag
& LS_NAME_SORT
)
649 return (rmul
* strcmp(a
->filename
, b
->filename
));
650 else if (sort_flag
& LS_TIME_SORT
)
651 return (rmul
* NCMP(a
->a
.mtime
, b
->a
.mtime
));
652 else if (sort_flag
& LS_SIZE_SORT
)
653 return (rmul
* NCMP(a
->a
.size
, b
->a
.size
));
655 fatal("Unknown ls sort type");
658 /* sftp ls.1 replacement for directories */
660 do_ls_dir(struct sftp_conn
*conn
, char *path
, char *strip_path
, int lflag
)
663 u_int c
= 1, colspace
= 0, columns
= 1;
666 if ((n
= do_readdir(conn
, path
, &d
)) != 0)
669 if (!(lflag
& LS_SHORT_VIEW
)) {
670 u_int m
= 0, width
= 80;
674 /* Count entries for sort and find longest filename */
675 for (n
= 0; d
[n
] != NULL
; n
++) {
676 if (d
[n
]->filename
[0] != '.' || (lflag
& LS_SHOW_ALL
))
677 m
= MAX(m
, strlen(d
[n
]->filename
));
680 /* Add any subpath that also needs to be counted */
681 tmp
= path_strip(path
, strip_path
);
685 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
688 columns
= width
/ (m
+ 2);
689 columns
= MAX(columns
, 1);
690 colspace
= width
/ columns
;
691 colspace
= MIN(colspace
, width
);
694 if (lflag
& SORT_FLAGS
) {
695 for (n
= 0; d
[n
] != NULL
; n
++)
696 ; /* count entries */
697 sort_flag
= lflag
& (SORT_FLAGS
|LS_REVERSE_SORT
);
698 qsort(d
, n
, sizeof(*d
), sdirent_comp
);
701 for (n
= 0; d
[n
] != NULL
&& !interrupted
; n
++) {
704 if (d
[n
]->filename
[0] == '.' && !(lflag
& LS_SHOW_ALL
))
707 tmp
= path_append(path
, d
[n
]->filename
);
708 fname
= path_strip(tmp
, strip_path
);
711 if (lflag
& LS_LONG_VIEW
) {
712 if (lflag
& LS_NUMERIC_VIEW
) {
716 memset(&sb
, 0, sizeof(sb
));
717 attrib_to_stat(&d
[n
]->a
, &sb
);
718 lname
= ls_file(fname
, &sb
, 1);
719 printf("%s\n", lname
);
722 printf("%s\n", d
[n
]->longname
);
724 printf("%-*s", colspace
, fname
);
735 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
738 free_sftp_dirents(d
);
742 /* sftp ls.1 replacement which handles path globs */
744 do_globbed_ls(struct sftp_conn
*conn
, char *path
, char *strip_path
,
748 u_int i
, c
= 1, colspace
= 0, columns
= 1;
751 memset(&g
, 0, sizeof(g
));
753 if (remote_glob(conn
, path
, GLOB_MARK
|GLOB_NOCHECK
|GLOB_BRACE
,
754 NULL
, &g
) || (g
.gl_pathc
&& !g
.gl_matchc
)) {
757 error("Can't ls: \"%s\" not found", path
);
765 * If the glob returns a single match and it is a directory,
766 * then just list its contents.
768 if (g
.gl_matchc
== 1) {
769 if ((a
= do_lstat(conn
, g
.gl_pathv
[0], 1)) == NULL
) {
773 if ((a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
) &&
777 err
= do_ls_dir(conn
, g
.gl_pathv
[0], strip_path
, lflag
);
783 if (!(lflag
& LS_SHORT_VIEW
)) {
784 u_int m
= 0, width
= 80;
787 /* Count entries for sort and find longest filename */
788 for (i
= 0; g
.gl_pathv
[i
]; i
++)
789 m
= MAX(m
, strlen(g
.gl_pathv
[i
]));
791 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
794 columns
= width
/ (m
+ 2);
795 columns
= MAX(columns
, 1);
796 colspace
= width
/ columns
;
799 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++, a
= NULL
) {
802 fname
= path_strip(g
.gl_pathv
[i
], strip_path
);
804 if (lflag
& LS_LONG_VIEW
) {
809 * XXX: this is slow - 1 roundtrip per path
810 * A solution to this is to fork glob() and
811 * build a sftp specific version which keeps the
812 * attribs (which currently get thrown away)
813 * that the server returns as well as the filenames.
815 memset(&sb
, 0, sizeof(sb
));
817 a
= do_lstat(conn
, g
.gl_pathv
[i
], 1);
819 attrib_to_stat(a
, &sb
);
820 lname
= ls_file(fname
, &sb
, 1);
821 printf("%s\n", lname
);
824 printf("%-*s", colspace
, fname
);
834 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
845 do_df(struct sftp_conn
*conn
, char *path
, int hflag
, int iflag
)
847 struct sftp_statvfs st
;
848 char s_used
[FMT_SCALED_STRSIZE
];
849 char s_avail
[FMT_SCALED_STRSIZE
];
850 char s_root
[FMT_SCALED_STRSIZE
];
851 char s_total
[FMT_SCALED_STRSIZE
];
853 if (do_statvfs(conn
, path
, &st
, 1) == -1)
856 printf(" Inodes Used Avail "
857 "(root) %%Capacity\n");
858 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
859 (unsigned long long)st
.f_files
,
860 (unsigned long long)(st
.f_files
- st
.f_ffree
),
861 (unsigned long long)st
.f_favail
,
862 (unsigned long long)st
.f_ffree
,
863 (unsigned long long)(100 * (st
.f_files
- st
.f_ffree
) /
866 strlcpy(s_used
, "error", sizeof(s_used
));
867 strlcpy(s_avail
, "error", sizeof(s_avail
));
868 strlcpy(s_root
, "error", sizeof(s_root
));
869 strlcpy(s_total
, "error", sizeof(s_total
));
870 fmt_scaled((st
.f_blocks
- st
.f_bfree
) * st
.f_frsize
, s_used
);
871 fmt_scaled(st
.f_bavail
* st
.f_frsize
, s_avail
);
872 fmt_scaled(st
.f_bfree
* st
.f_frsize
, s_root
);
873 fmt_scaled(st
.f_blocks
* st
.f_frsize
, s_total
);
874 printf(" Size Used Avail (root) %%Capacity\n");
875 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
876 s_total
, s_used
, s_avail
, s_root
,
877 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
880 printf(" Size Used Avail "
881 "(root) %%Capacity\n");
882 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
883 (unsigned long long)(st
.f_frsize
* st
.f_blocks
/ 1024),
884 (unsigned long long)(st
.f_frsize
*
885 (st
.f_blocks
- st
.f_bfree
) / 1024),
886 (unsigned long long)(st
.f_frsize
* st
.f_bavail
/ 1024),
887 (unsigned long long)(st
.f_frsize
* st
.f_bfree
/ 1024),
888 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
895 * Undo escaping of glob sequences in place. Used to undo extra escaping
896 * applied in makeargv() when the string is destined for a function that
900 undo_glob_escape(char *s
)
935 * Split a string into an argument vector using sh(1)-style quoting,
936 * comment and escaping rules, but with some tweaks to handle glob(3)
938 * Returns NULL on error or a NULL-terminated array of arguments.
941 #define MAXARGLEN 8192
943 makeargv(const char *arg
, int *argcp
)
947 static char argvs
[MAXARGLEN
];
948 static char *argv
[MAXARGS
+ 1];
949 enum { MA_START
, MA_SQUOTE
, MA_DQUOTE
, MA_UNQUOTED
} state
, q
;
952 if (strlen(arg
) > sizeof(argvs
) - 1) {
954 error("string too long");
960 if (isspace(arg
[i
])) {
961 if (state
== MA_UNQUOTED
) {
962 /* Terminate current argument */
966 } else if (state
!= MA_START
)
968 } else if (arg
[i
] == '"' || arg
[i
] == '\'') {
969 q
= arg
[i
] == '"' ? MA_DQUOTE
: MA_SQUOTE
;
970 if (state
== MA_START
) {
971 argv
[argc
] = argvs
+ j
;
973 } else if (state
== MA_UNQUOTED
)
979 } else if (arg
[i
] == '\\') {
980 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
981 quot
= state
== MA_SQUOTE
? '\'' : '"';
982 /* Unescape quote we are in */
983 /* XXX support \n and friends? */
984 if (arg
[i
+ 1] == quot
) {
987 } else if (arg
[i
+ 1] == '?' ||
988 arg
[i
+ 1] == '[' || arg
[i
+ 1] == '*') {
990 * Special case for sftp: append
991 * double-escaped glob sequence -
992 * glob will undo one level of
993 * escaping. NB. string can grow here.
995 if (j
>= sizeof(argvs
) - 5)
998 argvs
[j
++] = arg
[i
++];
1000 argvs
[j
++] = arg
[i
];
1002 argvs
[j
++] = arg
[i
++];
1003 argvs
[j
++] = arg
[i
];
1006 if (state
== MA_START
) {
1007 argv
[argc
] = argvs
+ j
;
1008 state
= MA_UNQUOTED
;
1010 if (arg
[i
+ 1] == '?' || arg
[i
+ 1] == '[' ||
1011 arg
[i
+ 1] == '*' || arg
[i
+ 1] == '\\') {
1013 * Special case for sftp: append
1014 * escaped glob sequence -
1015 * glob will undo one level of
1018 argvs
[j
++] = arg
[i
++];
1019 argvs
[j
++] = arg
[i
];
1021 /* Unescape everything */
1022 /* XXX support \n and friends? */
1024 argvs
[j
++] = arg
[i
];
1027 } else if (arg
[i
] == '#') {
1028 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
)
1029 argvs
[j
++] = arg
[i
];
1032 } else if (arg
[i
] == '\0') {
1033 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1034 error("Unterminated quoted argument");
1038 if (state
== MA_UNQUOTED
) {
1044 if (state
== MA_START
) {
1045 argv
[argc
] = argvs
+ j
;
1046 state
= MA_UNQUOTED
;
1048 if ((state
== MA_SQUOTE
|| state
== MA_DQUOTE
) &&
1049 (arg
[i
] == '?' || arg
[i
] == '[' || arg
[i
] == '*')) {
1051 * Special case for sftp: escape quoted
1052 * glob(3) wildcards. NB. string can grow
1055 if (j
>= sizeof(argvs
) - 3)
1056 goto args_too_longs
;
1058 argvs
[j
++] = arg
[i
];
1060 argvs
[j
++] = arg
[i
];
1069 parse_args(const char **cpp
, int *pflag
, int *rflag
, int *lflag
, int *iflag
, int *hflag
,
1070 unsigned long *n_arg
, char **path1
, char **path2
)
1072 const char *cmd
, *cp
= *cpp
;
1076 int i
, cmdnum
, optidx
, argc
;
1078 /* Skip leading whitespace */
1079 cp
= cp
+ strspn(cp
, WHITESPACE
);
1081 /* Ignore blank lines and lines which begin with comment '#' char */
1082 if (*cp
== '\0' || *cp
== '#')
1085 /* Check for leading '-' (disable error processing) */
1092 if ((argv
= makeargv(cp
, &argc
)) == NULL
)
1095 /* Figure out which command we have */
1096 for (i
= 0; cmds
[i
].c
!= NULL
; i
++) {
1097 if (strcasecmp(cmds
[i
].c
, argv
[0]) == 0)
1107 } else if (cmdnum
== -1) {
1108 error("Invalid command.");
1112 /* Get arguments and parse flags */
1113 *lflag
= *pflag
= *rflag
= *hflag
= *n_arg
= 0;
1114 *path1
= *path2
= NULL
;
1119 if ((optidx
= parse_getput_flags(cmd
, argv
, argc
, pflag
, rflag
)) == -1)
1121 /* Get first pathname (mandatory) */
1122 if (argc
- optidx
< 1) {
1123 error("You must specify at least one path after a "
1124 "%s command.", cmd
);
1127 *path1
= xstrdup(argv
[optidx
]);
1128 /* Get second pathname (optional) */
1129 if (argc
- optidx
> 1) {
1130 *path2
= xstrdup(argv
[optidx
+ 1]);
1131 /* Destination is not globbed */
1132 undo_glob_escape(*path2
);
1137 if (argc
- optidx
< 2) {
1138 error("You must specify two paths after a %s "
1142 *path1
= xstrdup(argv
[optidx
]);
1143 *path2
= xstrdup(argv
[optidx
+ 1]);
1144 /* Paths are not globbed */
1145 undo_glob_escape(*path1
);
1146 undo_glob_escape(*path2
);
1154 /* Get pathname (mandatory) */
1155 if (argc
- optidx
< 1) {
1156 error("You must specify a path after a %s command.",
1160 *path1
= xstrdup(argv
[optidx
]);
1161 /* Only "rm" globs */
1163 undo_glob_escape(*path1
);
1166 if ((optidx
= parse_df_flags(cmd
, argv
, argc
, hflag
,
1169 /* Default to current directory if no path specified */
1170 if (argc
- optidx
< 1)
1173 *path1
= xstrdup(argv
[optidx
]);
1174 undo_glob_escape(*path1
);
1178 if ((optidx
= parse_ls_flags(argv
, argc
, lflag
)) == -1)
1180 /* Path is optional */
1181 if (argc
- optidx
> 0)
1182 *path1
= xstrdup(argv
[optidx
]);
1185 /* Skip ls command and following whitespace */
1186 cp
= cp
+ strlen(cmd
) + strspn(cp
, WHITESPACE
);
1188 /* Uses the rest of the line */
1195 /* Get numeric arg (mandatory) */
1196 if (argc
- optidx
< 1)
1199 l
= strtol(argv
[optidx
], &cp2
, base
);
1200 if (cp2
== argv
[optidx
] || *cp2
!= '\0' ||
1201 ((l
== LONG_MIN
|| l
== LONG_MAX
) && errno
== ERANGE
) ||
1204 error("You must supply a numeric argument "
1205 "to the %s command.", cmd
);
1209 if (cmdnum
== I_LUMASK
)
1211 /* Get pathname (mandatory) */
1212 if (argc
- optidx
< 2) {
1213 error("You must specify a path after a %s command.",
1217 *path1
= xstrdup(argv
[optidx
+ 1]);
1227 fatal("Command not implemented");
1235 parse_dispatch_command(struct sftp_conn
*conn
, const char *cmd
, char **pwd
,
1238 char *path1
, *path2
, *tmp
;
1239 int pflag
= 0, rflag
= 0, lflag
= 0, iflag
= 0, hflag
= 0, cmdnum
, i
;
1240 unsigned long n_arg
= 0;
1242 char path_buf
[MAXPATHLEN
];
1246 path1
= path2
= NULL
;
1247 cmdnum
= parse_args(&cmd
, &pflag
, &rflag
, &lflag
, &iflag
, &hflag
, &n_arg
,
1253 memset(&g
, 0, sizeof(g
));
1255 /* Perform command */
1261 /* Unrecognized command */
1265 err
= process_get(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1268 err
= process_put(conn
, path1
, path2
, *pwd
, pflag
, rflag
);
1271 path1
= make_absolute(path1
, *pwd
);
1272 path2
= make_absolute(path2
, *pwd
);
1273 err
= do_rename(conn
, path1
, path2
);
1276 path2
= make_absolute(path2
, *pwd
);
1277 err
= do_symlink(conn
, path1
, path2
);
1280 path1
= make_absolute(path1
, *pwd
);
1281 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1282 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1283 printf("Removing %s\n", g
.gl_pathv
[i
]);
1284 err
= do_rm(conn
, g
.gl_pathv
[i
]);
1285 if (err
!= 0 && err_abort
)
1290 path1
= make_absolute(path1
, *pwd
);
1292 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1294 err
= do_mkdir(conn
, path1
, &a
, 1);
1297 path1
= make_absolute(path1
, *pwd
);
1298 err
= do_rmdir(conn
, path1
);
1301 path1
= make_absolute(path1
, *pwd
);
1302 if ((tmp
= do_realpath(conn
, path1
)) == NULL
) {
1306 if ((aa
= do_stat(conn
, tmp
, 0)) == NULL
) {
1311 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
)) {
1312 error("Can't change directory: Can't check target");
1317 if (!S_ISDIR(aa
->perm
)) {
1318 error("Can't change directory: \"%s\" is not "
1319 "a directory", tmp
);
1329 do_globbed_ls(conn
, *pwd
, *pwd
, lflag
);
1333 /* Strip pwd off beginning of non-absolute paths */
1338 path1
= make_absolute(path1
, *pwd
);
1339 err
= do_globbed_ls(conn
, path1
, tmp
, lflag
);
1342 /* Default to current directory if no path specified */
1344 path1
= xstrdup(*pwd
);
1345 path1
= make_absolute(path1
, *pwd
);
1346 err
= do_df(conn
, path1
, hflag
, iflag
);
1349 if (chdir(path1
) == -1) {
1350 error("Couldn't change local directory to "
1351 "\"%s\": %s", path1
, strerror(errno
));
1356 if (mkdir(path1
, 0777) == -1) {
1357 error("Couldn't create local directory "
1358 "\"%s\": %s", path1
, strerror(errno
));
1366 local_do_shell(cmd
);
1370 printf("Local umask: %03lo\n", n_arg
);
1373 path1
= make_absolute(path1
, *pwd
);
1375 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1377 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1378 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1379 printf("Changing mode on %s\n", g
.gl_pathv
[i
]);
1380 err
= do_setstat(conn
, g
.gl_pathv
[i
], &a
);
1381 if (err
!= 0 && err_abort
)
1387 path1
= make_absolute(path1
, *pwd
);
1388 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1389 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1390 if (!(aa
= do_stat(conn
, g
.gl_pathv
[i
], 0))) {
1397 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_UIDGID
)) {
1398 error("Can't get current ownership of "
1399 "remote file \"%s\"", g
.gl_pathv
[i
]);
1406 aa
->flags
&= SSH2_FILEXFER_ATTR_UIDGID
;
1407 if (cmdnum
== I_CHOWN
) {
1408 printf("Changing owner on %s\n", g
.gl_pathv
[i
]);
1411 printf("Changing group on %s\n", g
.gl_pathv
[i
]);
1414 err
= do_setstat(conn
, g
.gl_pathv
[i
], aa
);
1415 if (err
!= 0 && err_abort
)
1420 printf("Remote working directory: %s\n", *pwd
);
1423 if (!getcwd(path_buf
, sizeof(path_buf
))) {
1424 error("Couldn't get local cwd: %s", strerror(errno
));
1428 printf("Local working directory: %s\n", path_buf
);
1431 /* Processed below */
1437 printf("SFTP protocol version %u\n", sftp_proto_version(conn
));
1440 showprogress
= !showprogress
;
1442 printf("Progress meter enabled\n");
1444 printf("Progress meter disabled\n");
1447 fatal("%d is not implemented", cmdnum
);
1457 /* If an unignored error occurs in batch mode we should abort. */
1458 if (err_abort
&& err
!= 0)
1460 else if (cmdnum
== I_QUIT
)
1468 prompt(EditLine
*el
)
1475 interactive_loop(int fd_in
, int fd_out
, char *file1
, char *file2
)
1480 struct sftp_conn
*conn
;
1481 int err
, interactive
;
1482 EditLine
*el
= NULL
;
1486 extern char *__progname
;
1488 if (!batchmode
&& isatty(STDIN_FILENO
)) {
1489 if ((el
= el_init(__progname
, stdin
, stdout
, stderr
)) == NULL
)
1490 fatal("Couldn't initialise editline");
1491 if ((hl
= history_init()) == NULL
)
1492 fatal("Couldn't initialise editline history");
1493 history(hl
, &hev
, H_SETSIZE
, 100);
1494 el_set(el
, EL_HIST
, history
, hl
);
1496 el_set(el
, EL_PROMPT
, prompt
);
1497 el_set(el
, EL_EDITOR
, "emacs");
1498 el_set(el
, EL_TERMINAL
, NULL
);
1499 el_set(el
, EL_SIGNAL
, 1);
1500 el_source(el
, NULL
);
1502 #endif /* USE_LIBEDIT */
1504 conn
= do_init(fd_in
, fd_out
, copy_buffer_len
, num_requests
);
1506 fatal("Couldn't initialise connection to server");
1508 pwd
= do_realpath(conn
, ".");
1512 if (file1
!= NULL
) {
1513 dir
= xstrdup(file1
);
1514 dir
= make_absolute(dir
, pwd
);
1516 if (remote_is_dir(conn
, dir
) && file2
== NULL
) {
1517 printf("Changing to: %s\n", dir
);
1518 snprintf(cmd
, sizeof cmd
, "cd \"%s\"", dir
);
1519 if (parse_dispatch_command(conn
, cmd
, &pwd
, 1) != 0) {
1527 snprintf(cmd
, sizeof cmd
, "get %s", dir
);
1529 snprintf(cmd
, sizeof cmd
, "get %s %s", dir
,
1532 err
= parse_dispatch_command(conn
, cmd
, &pwd
, 1);
1541 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1542 setvbuf(stdout
, NULL
, _IOLBF
, 0);
1543 setvbuf(infile
, NULL
, _IOLBF
, 0);
1549 interactive
= !batchmode
&& isatty(STDIN_FILENO
);
1554 signal(SIGINT
, SIG_IGN
);
1559 if (fgets(cmd
, sizeof(cmd
), infile
) == NULL
) {
1564 if (!interactive
) { /* Echo command */
1565 printf("sftp> %s", cmd
);
1566 if (strlen(cmd
) > 0 &&
1567 cmd
[strlen(cmd
) - 1] != '\n')
1575 if ((line
= el_gets(el
, &count
)) == NULL
|| count
<= 0) {
1579 history(hl
, &hev
, H_ENTER
, line
);
1580 if (strlcpy(cmd
, line
, sizeof(cmd
)) >= sizeof(cmd
)) {
1581 fprintf(stderr
, "Error: input line too long\n");
1584 #endif /* USE_LIBEDIT */
1587 cp
= strrchr(cmd
, '\n');
1591 /* Handle user interrupts gracefully during commands */
1593 signal(SIGINT
, cmd_interrupt
);
1595 err
= parse_dispatch_command(conn
, cmd
, &pwd
, batchmode
);
1605 #endif /* USE_LIBEDIT */
1607 /* err == 1 signifies normal "quit" exit */
1608 return (err
>= 0 ? 0 : -1);
1612 connect_to_server(char *path
, char **args
, int *in
, int *out
)
1617 int pin
[2], pout
[2];
1619 if ((pipe(pin
) == -1) || (pipe(pout
) == -1))
1620 fatal("pipe: %s", strerror(errno
));
1625 #else /* USE_PIPES */
1628 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) == -1)
1629 fatal("socketpair: %s", strerror(errno
));
1630 *in
= *out
= inout
[0];
1631 c_in
= c_out
= inout
[1];
1632 #endif /* USE_PIPES */
1634 if ((sshpid
= fork()) == -1)
1635 fatal("fork: %s", strerror(errno
));
1636 else if (sshpid
== 0) {
1637 if ((dup2(c_in
, STDIN_FILENO
) == -1) ||
1638 (dup2(c_out
, STDOUT_FILENO
) == -1)) {
1639 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
1648 * The underlying ssh is in the same process group, so we must
1649 * ignore SIGINT if we want to gracefully abort commands,
1650 * otherwise the signal will make it to the ssh process and
1653 signal(SIGINT
, SIG_IGN
);
1655 fprintf(stderr
, "exec: %s: %s\n", path
, strerror(errno
));
1659 signal(SIGTERM
, killchild
);
1660 signal(SIGINT
, killchild
);
1661 signal(SIGHUP
, killchild
);
1669 extern char *__progname
;
1672 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
1673 " [-D sftp_server_path] [-F ssh_config] "
1674 "[-i identity_file]\n"
1675 " [-o ssh_option] [-P port] [-R num_requests] "
1677 " [-s subsystem | sftp_server] host\n"
1678 " %s [user@]host[:file ...]\n"
1679 " %s [user@]host[:dir[/]]\n"
1680 " %s -b batchfile [user@]host\n",
1681 __progname
, __progname
, __progname
, __progname
);
1686 main(int argc
, char **argv
)
1688 int in
, out
, ch
, err
;
1689 char *host
, *userhost
, *cp
, *file2
= NULL
;
1690 int debug_level
= 0, sshver
= 2;
1691 char *file1
= NULL
, *sftp_server
= NULL
;
1692 char *ssh_program
= _PATH_SSH_PROGRAM
, *sftp_direct
= NULL
;
1693 LogLevel ll
= SYSLOG_LEVEL_INFO
;
1696 extern char *optarg
;
1698 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1701 __progname
= ssh_get_progname(argv
[0]);
1702 memset(&args
, '\0', sizeof(args
));
1704 addargs(&args
, "%s", ssh_program
);
1705 addargs(&args
, "-oForwardX11 no");
1706 addargs(&args
, "-oForwardAgent no");
1707 addargs(&args
, "-oPermitLocalCommand no");
1708 addargs(&args
, "-oClearAllForwardings yes");
1710 ll
= SYSLOG_LEVEL_INFO
;
1713 while ((ch
= getopt(argc
, argv
,
1714 "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
1716 /* Passed through to ssh(1) */
1720 addargs(&args
, "-%c", ch
);
1722 /* Passed through to ssh(1) with argument */
1727 addargs(&args
, "-%c%s", ch
, optarg
);
1731 addargs(&args
, "-%c", ch
);
1734 addargs(&args
, "-oPort %s", optarg
);
1737 if (debug_level
< 3) {
1738 addargs(&args
, "-v");
1739 ll
= SYSLOG_LEVEL_DEBUG1
+ debug_level
;
1745 if (sftp_server
== NULL
)
1746 sftp_server
= _PATH_SFTP_SERVER
;
1752 copy_buffer_len
= strtol(optarg
, &cp
, 10);
1753 if (copy_buffer_len
== 0 || *cp
!= '\0')
1754 fatal("Invalid buffer size \"%s\"", optarg
);
1758 fatal("Batch file already specified.");
1760 /* Allow "-" as stdin */
1761 if (strcmp(optarg
, "-") != 0 &&
1762 (infile
= fopen(optarg
, "r")) == NULL
)
1763 fatal("%s (%s).", strerror(errno
), optarg
);
1766 addargs(&args
, "-obatchmode yes");
1772 sftp_direct
= optarg
;
1778 num_requests
= strtol(optarg
, &cp
, 10);
1779 if (num_requests
== 0 || *cp
!= '\0')
1780 fatal("Invalid number of requests \"%s\"",
1784 sftp_server
= optarg
;
1787 ssh_program
= optarg
;
1788 replacearg(&args
, 0, "%s", ssh_program
);
1796 if (!isatty(STDERR_FILENO
))
1799 log_init(argv
[0], ll
, SYSLOG_FACILITY_USER
, 1);
1801 if (sftp_direct
== NULL
) {
1802 if (optind
== argc
|| argc
> (optind
+ 2))
1805 userhost
= xstrdup(argv
[optind
]);
1806 file2
= argv
[optind
+1];
1808 if ((host
= strrchr(userhost
, '@')) == NULL
)
1813 fprintf(stderr
, "Missing username\n");
1816 addargs(&args
, "-l%s", userhost
);
1819 if ((cp
= colon(host
)) != NULL
) {
1824 host
= cleanhostname(host
);
1826 fprintf(stderr
, "Missing hostname\n");
1830 addargs(&args
, "-oProtocol %d", sshver
);
1832 /* no subsystem if the server-spec contains a '/' */
1833 if (sftp_server
== NULL
|| strchr(sftp_server
, '/') == NULL
)
1834 addargs(&args
, "-s");
1836 addargs(&args
, "%s", host
);
1837 addargs(&args
, "%s", (sftp_server
!= NULL
?
1838 sftp_server
: "sftp"));
1841 fprintf(stderr
, "Connecting to %s...\n", host
);
1842 connect_to_server(ssh_program
, args
.list
, &in
, &out
);
1845 addargs(&args
, "sftp-server");
1848 fprintf(stderr
, "Attaching to %s...\n", sftp_direct
);
1849 connect_to_server(sftp_direct
, args
.list
, &in
, &out
);
1853 err
= interactive_loop(in
, out
, file1
, file2
);
1855 #if !defined(USE_PIPES)
1856 shutdown(in
, SHUT_RDWR
);
1857 shutdown(out
, SHUT_RDWR
);
1865 while (waitpid(sshpid
, NULL
, 0) == -1)
1867 fatal("Couldn't wait for ssh process: %s",
1870 exit(err
== 0 ? 0 : 1);