1 /* $OpenBSD: sftp.c,v 1.107 2009/02/02 11:15:14 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>
41 typedef void EditLine
;
60 #include "pathnames.h"
65 #include "sftp-common.h"
66 #include "sftp-client.h"
68 /* File to read commands from */
71 /* Are we in batchfile mode? */
74 /* Size of buffer used when copying files */
75 size_t copy_buffer_len
= 32768;
77 /* Number of concurrent outstanding requests */
78 size_t num_requests
= 64;
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 /* SIGINT received during command processing */
87 volatile sig_atomic_t interrupted
= 0;
89 /* I wish qsort() took a separate ctx for the comparison function...*/
92 int remote_glob(struct sftp_conn
*, const char *, int,
93 int (*)(const char *, int), glob_t
*); /* proto for sftp-glob.c */
95 extern char *__progname
;
97 /* Separators for interactive commands */
98 #define WHITESPACE " \t\r\n"
101 #define LS_LONG_VIEW 0x01 /* Full view ala ls -l */
102 #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */
103 #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */
104 #define LS_NAME_SORT 0x08 /* Sort by name (default) */
105 #define LS_TIME_SORT 0x10 /* Sort by mtime */
106 #define LS_SIZE_SORT 0x20 /* Sort by file size */
107 #define LS_REVERSE_SORT 0x40 /* Reverse sort order */
108 #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */
110 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
111 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
113 /* Commands for interactive mode */
137 #define I_PROGRESS 23
144 static const struct CMD cmds
[] = {
147 { "chdir", I_CHDIR
},
148 { "chgrp", I_CHGRP
},
149 { "chmod", I_CHMOD
},
150 { "chown", I_CHOWN
},
158 { "lchdir", I_LCHDIR
},
160 { "lmkdir", I_LMKDIR
},
164 { "lumask", I_LUMASK
},
165 { "mkdir", I_MKDIR
},
166 { "progress", I_PROGRESS
},
171 { "rename", I_RENAME
},
173 { "rmdir", I_RMDIR
},
174 { "symlink", I_SYMLINK
},
175 { "version", I_VERSION
},
181 int interactive_loop(int fd_in
, int fd_out
, char *file1
, char *file2
);
188 kill(sshpid
, SIGTERM
);
189 waitpid(sshpid
, NULL
, 0);
197 cmd_interrupt(int signo
)
199 const char msg
[] = "\rInterrupt \n";
200 int olderrno
= errno
;
202 write(STDERR_FILENO
, msg
, sizeof(msg
) - 1);
210 printf("Available commands:\n"
212 "cd path Change remote directory to 'path'\n"
213 "chgrp grp path Change group of file 'path' to 'grp'\n"
214 "chmod mode path Change permissions of file 'path' to 'mode'\n"
215 "chown own path Change owner of file 'path' to 'own'\n"
216 "df [-hi] [path] Display statistics for current directory or\n"
217 " filesystem containing 'path'\n"
219 "get [-P] remote-path [local-path] Download file\n"
220 "help Display this help text\n"
221 "lcd path Change local directory to 'path'\n"
222 "lls [ls-options [path]] Display local directory listing\n"
223 "lmkdir path Create local directory\n"
224 "ln oldpath newpath Symlink remote file\n"
225 "lpwd Print local working directory\n"
226 "ls [-1aflnrSt] [path] Display remote directory listing\n"
227 "lumask umask Set local umask to 'umask'\n"
228 "mkdir path Create remote directory\n"
229 "progress Toggle display of progress meter\n"
230 "put [-P] local-path [remote-path] Upload file\n"
231 "pwd Display remote working directory\n"
233 "rename oldpath newpath Rename remote file\n"
234 "rm path Delete remote file\n"
235 "rmdir path Remove remote directory\n"
236 "symlink oldpath newpath Symlink remote file\n"
237 "version Show SFTP version\n"
238 "!command Execute 'command' in local shell\n"
239 "! Escape to local shell\n"
240 "? Synonym for help\n");
244 local_do_shell(const char *args
)
253 if ((shell
= getenv("SHELL")) == NULL
)
254 shell
= _PATH_BSHELL
;
256 if ((pid
= fork()) == -1)
257 fatal("Couldn't fork: %s", strerror(errno
));
260 /* XXX: child has pipe fds to ssh subproc open - issue? */
262 debug3("Executing %s -c \"%s\"", shell
, args
);
263 execl(shell
, shell
, "-c", args
, (char *)NULL
);
265 debug3("Executing %s", shell
);
266 execl(shell
, shell
, (char *)NULL
);
268 fprintf(stderr
, "Couldn't execute \"%s\": %s\n", shell
,
272 while (waitpid(pid
, &status
, 0) == -1)
274 fatal("Couldn't wait for child: %s", strerror(errno
));
275 if (!WIFEXITED(status
))
276 error("Shell exited abnormally");
277 else if (WEXITSTATUS(status
))
278 error("Shell exited with status %d", WEXITSTATUS(status
));
282 local_do_ls(const char *args
)
285 local_do_shell(_PATH_LS
);
287 int len
= strlen(_PATH_LS
" ") + strlen(args
) + 1;
288 char *buf
= xmalloc(len
);
290 /* XXX: quoting - rip quoting code from ftp? */
291 snprintf(buf
, len
, _PATH_LS
" %s", args
);
297 /* Strip one path (usually the pwd) from the start of another */
299 path_strip(char *path
, char *strip
)
304 return (xstrdup(path
));
307 if (strncmp(path
, strip
, len
) == 0) {
308 if (strip
[len
- 1] != '/' && path
[len
] == '/')
310 return (xstrdup(path
+ len
));
313 return (xstrdup(path
));
317 path_append(char *p1
, char *p2
)
320 size_t len
= strlen(p1
) + strlen(p2
) + 2;
323 strlcpy(ret
, p1
, len
);
324 if (p1
[0] != '\0' && p1
[strlen(p1
) - 1] != '/')
325 strlcat(ret
, "/", len
);
326 strlcat(ret
, p2
, len
);
332 make_absolute(char *p
, char *pwd
)
337 if (p
&& p
[0] != '/') {
338 abs_str
= path_append(pwd
, p
);
346 infer_path(const char *p
, char **ifp
)
350 cp
= strrchr(p
, '/');
357 error("Invalid path");
361 *ifp
= xstrdup(cp
+ 1);
366 parse_getput_flags(const char *cmd
, char **argv
, int argc
, int *pflag
)
368 extern int opterr
, optind
, optopt
, optreset
;
371 optind
= optreset
= 1;
375 while ((ch
= getopt(argc
, argv
, "Pp")) != -1) {
382 error("%s: Invalid flag -%c", cmd
, optopt
);
391 parse_ls_flags(char **argv
, int argc
, int *lflag
)
393 extern int opterr
, optind
, optopt
, optreset
;
396 optind
= optreset
= 1;
399 *lflag
= LS_NAME_SORT
;
400 while ((ch
= getopt(argc
, argv
, "1Saflnrt")) != -1) {
403 *lflag
&= ~VIEW_FLAGS
;
404 *lflag
|= LS_SHORT_VIEW
;
407 *lflag
&= ~SORT_FLAGS
;
408 *lflag
|= LS_SIZE_SORT
;
411 *lflag
|= LS_SHOW_ALL
;
414 *lflag
&= ~SORT_FLAGS
;
417 *lflag
&= ~VIEW_FLAGS
;
418 *lflag
|= LS_LONG_VIEW
;
421 *lflag
&= ~VIEW_FLAGS
;
422 *lflag
|= LS_NUMERIC_VIEW
|LS_LONG_VIEW
;
425 *lflag
|= LS_REVERSE_SORT
;
428 *lflag
&= ~SORT_FLAGS
;
429 *lflag
|= LS_TIME_SORT
;
432 error("ls: Invalid flag -%c", optopt
);
441 parse_df_flags(const char *cmd
, char **argv
, int argc
, int *hflag
, int *iflag
)
443 extern int opterr
, optind
, optopt
, optreset
;
446 optind
= optreset
= 1;
450 while ((ch
= getopt(argc
, argv
, "hi")) != -1) {
459 error("%s: Invalid flag -%c", cmd
, optopt
);
472 /* XXX: report errors? */
473 if (stat(path
, &sb
) == -1)
476 return(S_ISDIR(sb
.st_mode
));
480 remote_is_dir(struct sftp_conn
*conn
, char *path
)
484 /* XXX: report errors? */
485 if ((a
= do_stat(conn
, path
, 1)) == NULL
)
487 if (!(a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
))
489 return(S_ISDIR(a
->perm
));
493 process_get(struct sftp_conn
*conn
, char *src
, char *dst
, char *pwd
, int pflag
)
495 char *abs_src
= NULL
;
496 char *abs_dst
= NULL
;
502 abs_src
= xstrdup(src
);
503 abs_src
= make_absolute(abs_src
, pwd
);
505 memset(&g
, 0, sizeof(g
));
506 debug3("Looking up %s", abs_src
);
507 if (remote_glob(conn
, abs_src
, 0, NULL
, &g
)) {
508 error("File \"%s\" not found.", abs_src
);
513 /* If multiple matches, dst must be a directory or unspecified */
514 if (g
.gl_matchc
> 1 && dst
&& !is_dir(dst
)) {
515 error("Multiple files match, but \"%s\" is not a directory",
521 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
522 if (infer_path(g
.gl_pathv
[i
], &tmp
)) {
527 if (g
.gl_matchc
== 1 && dst
) {
528 /* If directory specified, append filename */
531 if (infer_path(g
.gl_pathv
[0], &tmp
)) {
535 abs_dst
= path_append(dst
, tmp
);
538 abs_dst
= xstrdup(dst
);
540 abs_dst
= path_append(dst
, tmp
);
545 printf("Fetching %s to %s\n", g
.gl_pathv
[i
], abs_dst
);
546 if (do_download(conn
, g
.gl_pathv
[i
], abs_dst
, pflag
) == -1)
559 process_put(struct sftp_conn
*conn
, char *src
, char *dst
, char *pwd
, int pflag
)
561 char *tmp_dst
= NULL
;
562 char *abs_dst
= NULL
;
570 tmp_dst
= xstrdup(dst
);
571 tmp_dst
= make_absolute(tmp_dst
, pwd
);
574 memset(&g
, 0, sizeof(g
));
575 debug3("Looking up %s", src
);
576 if (glob(src
, GLOB_NOCHECK
, NULL
, &g
)) {
577 error("File \"%s\" not found.", src
);
582 /* If multiple matches, dst may be directory or unspecified */
583 if (g
.gl_matchc
> 1 && tmp_dst
&& !remote_is_dir(conn
, tmp_dst
)) {
584 error("Multiple files match, but \"%s\" is not a directory",
590 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
591 if (stat(g
.gl_pathv
[i
], &sb
) == -1) {
593 error("stat %s: %s", g
.gl_pathv
[i
], strerror(errno
));
597 if (!S_ISREG(sb
.st_mode
)) {
598 error("skipping non-regular file %s",
602 if (infer_path(g
.gl_pathv
[i
], &tmp
)) {
607 if (g
.gl_matchc
== 1 && tmp_dst
) {
608 /* If directory specified, append filename */
609 if (remote_is_dir(conn
, tmp_dst
)) {
610 if (infer_path(g
.gl_pathv
[0], &tmp
)) {
614 abs_dst
= path_append(tmp_dst
, tmp
);
617 abs_dst
= xstrdup(tmp_dst
);
619 } else if (tmp_dst
) {
620 abs_dst
= path_append(tmp_dst
, tmp
);
623 abs_dst
= make_absolute(tmp
, pwd
);
625 printf("Uploading %s to %s\n", g
.gl_pathv
[i
], abs_dst
);
626 if (do_upload(conn
, g
.gl_pathv
[i
], abs_dst
, pflag
) == -1)
640 sdirent_comp(const void *aa
, const void *bb
)
642 SFTP_DIRENT
*a
= *(SFTP_DIRENT
**)aa
;
643 SFTP_DIRENT
*b
= *(SFTP_DIRENT
**)bb
;
644 int rmul
= sort_flag
& LS_REVERSE_SORT
? -1 : 1;
646 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
647 if (sort_flag
& LS_NAME_SORT
)
648 return (rmul
* strcmp(a
->filename
, b
->filename
));
649 else if (sort_flag
& LS_TIME_SORT
)
650 return (rmul
* NCMP(a
->a
.mtime
, b
->a
.mtime
));
651 else if (sort_flag
& LS_SIZE_SORT
)
652 return (rmul
* NCMP(a
->a
.size
, b
->a
.size
));
654 fatal("Unknown ls sort type");
657 /* sftp ls.1 replacement for directories */
659 do_ls_dir(struct sftp_conn
*conn
, char *path
, char *strip_path
, int lflag
)
662 u_int c
= 1, colspace
= 0, columns
= 1;
665 if ((n
= do_readdir(conn
, path
, &d
)) != 0)
668 if (!(lflag
& LS_SHORT_VIEW
)) {
669 u_int m
= 0, width
= 80;
673 /* Count entries for sort and find longest filename */
674 for (n
= 0; d
[n
] != NULL
; n
++) {
675 if (d
[n
]->filename
[0] != '.' || (lflag
& LS_SHOW_ALL
))
676 m
= MAX(m
, strlen(d
[n
]->filename
));
679 /* Add any subpath that also needs to be counted */
680 tmp
= path_strip(path
, strip_path
);
684 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
687 columns
= width
/ (m
+ 2);
688 columns
= MAX(columns
, 1);
689 colspace
= width
/ columns
;
690 colspace
= MIN(colspace
, width
);
693 if (lflag
& SORT_FLAGS
) {
694 for (n
= 0; d
[n
] != NULL
; n
++)
695 ; /* count entries */
696 sort_flag
= lflag
& (SORT_FLAGS
|LS_REVERSE_SORT
);
697 qsort(d
, n
, sizeof(*d
), sdirent_comp
);
700 for (n
= 0; d
[n
] != NULL
&& !interrupted
; n
++) {
703 if (d
[n
]->filename
[0] == '.' && !(lflag
& LS_SHOW_ALL
))
706 tmp
= path_append(path
, d
[n
]->filename
);
707 fname
= path_strip(tmp
, strip_path
);
710 if (lflag
& LS_LONG_VIEW
) {
711 if (lflag
& LS_NUMERIC_VIEW
) {
715 memset(&sb
, 0, sizeof(sb
));
716 attrib_to_stat(&d
[n
]->a
, &sb
);
717 lname
= ls_file(fname
, &sb
, 1);
718 printf("%s\n", lname
);
721 printf("%s\n", d
[n
]->longname
);
723 printf("%-*s", colspace
, fname
);
734 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
737 free_sftp_dirents(d
);
741 /* sftp ls.1 replacement which handles path globs */
743 do_globbed_ls(struct sftp_conn
*conn
, char *path
, char *strip_path
,
747 u_int i
, c
= 1, colspace
= 0, columns
= 1;
750 memset(&g
, 0, sizeof(g
));
752 if (remote_glob(conn
, path
, GLOB_MARK
|GLOB_NOCHECK
|GLOB_BRACE
,
753 NULL
, &g
) || (g
.gl_pathc
&& !g
.gl_matchc
)) {
756 error("Can't ls: \"%s\" not found", path
);
764 * If the glob returns a single match and it is a directory,
765 * then just list its contents.
767 if (g
.gl_matchc
== 1) {
768 if ((a
= do_lstat(conn
, g
.gl_pathv
[0], 1)) == NULL
) {
772 if ((a
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
) &&
776 err
= do_ls_dir(conn
, g
.gl_pathv
[0], strip_path
, lflag
);
782 if (!(lflag
& LS_SHORT_VIEW
)) {
783 u_int m
= 0, width
= 80;
786 /* Count entries for sort and find longest filename */
787 for (i
= 0; g
.gl_pathv
[i
]; i
++)
788 m
= MAX(m
, strlen(g
.gl_pathv
[i
]));
790 if (ioctl(fileno(stdin
), TIOCGWINSZ
, &ws
) != -1)
793 columns
= width
/ (m
+ 2);
794 columns
= MAX(columns
, 1);
795 colspace
= width
/ columns
;
798 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++, a
= NULL
) {
801 fname
= path_strip(g
.gl_pathv
[i
], strip_path
);
803 if (lflag
& LS_LONG_VIEW
) {
808 * XXX: this is slow - 1 roundtrip per path
809 * A solution to this is to fork glob() and
810 * build a sftp specific version which keeps the
811 * attribs (which currently get thrown away)
812 * that the server returns as well as the filenames.
814 memset(&sb
, 0, sizeof(sb
));
816 a
= do_lstat(conn
, g
.gl_pathv
[i
], 1);
818 attrib_to_stat(a
, &sb
);
819 lname
= ls_file(fname
, &sb
, 1);
820 printf("%s\n", lname
);
823 printf("%-*s", colspace
, fname
);
833 if (!(lflag
& LS_LONG_VIEW
) && (c
!= 1))
844 do_df(struct sftp_conn
*conn
, char *path
, int hflag
, int iflag
)
846 struct sftp_statvfs st
;
847 char s_used
[FMT_SCALED_STRSIZE
];
848 char s_avail
[FMT_SCALED_STRSIZE
];
849 char s_root
[FMT_SCALED_STRSIZE
];
850 char s_total
[FMT_SCALED_STRSIZE
];
852 if (do_statvfs(conn
, path
, &st
, 1) == -1)
855 printf(" Inodes Used Avail "
856 "(root) %%Capacity\n");
857 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
858 (unsigned long long)st
.f_files
,
859 (unsigned long long)(st
.f_files
- st
.f_ffree
),
860 (unsigned long long)st
.f_favail
,
861 (unsigned long long)st
.f_ffree
,
862 (unsigned long long)(100 * (st
.f_files
- st
.f_ffree
) /
865 strlcpy(s_used
, "error", sizeof(s_used
));
866 strlcpy(s_avail
, "error", sizeof(s_avail
));
867 strlcpy(s_root
, "error", sizeof(s_root
));
868 strlcpy(s_total
, "error", sizeof(s_total
));
869 fmt_scaled((st
.f_blocks
- st
.f_bfree
) * st
.f_frsize
, s_used
);
870 fmt_scaled(st
.f_bavail
* st
.f_frsize
, s_avail
);
871 fmt_scaled(st
.f_bfree
* st
.f_frsize
, s_root
);
872 fmt_scaled(st
.f_blocks
* st
.f_frsize
, s_total
);
873 printf(" Size Used Avail (root) %%Capacity\n");
874 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
875 s_total
, s_used
, s_avail
, s_root
,
876 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
879 printf(" Size Used Avail "
880 "(root) %%Capacity\n");
881 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
882 (unsigned long long)(st
.f_frsize
* st
.f_blocks
/ 1024),
883 (unsigned long long)(st
.f_frsize
*
884 (st
.f_blocks
- st
.f_bfree
) / 1024),
885 (unsigned long long)(st
.f_frsize
* st
.f_bavail
/ 1024),
886 (unsigned long long)(st
.f_frsize
* st
.f_bfree
/ 1024),
887 (unsigned long long)(100 * (st
.f_blocks
- st
.f_bfree
) /
894 * Undo escaping of glob sequences in place. Used to undo extra escaping
895 * applied in makeargv() when the string is destined for a function that
899 undo_glob_escape(char *s
)
934 * Split a string into an argument vector using sh(1)-style quoting,
935 * comment and escaping rules, but with some tweaks to handle glob(3)
937 * Returns NULL on error or a NULL-terminated array of arguments.
940 #define MAXARGLEN 8192
942 makeargv(const char *arg
, int *argcp
)
946 static char argvs
[MAXARGLEN
];
947 static char *argv
[MAXARGS
+ 1];
948 enum { MA_START
, MA_SQUOTE
, MA_DQUOTE
, MA_UNQUOTED
} state
, q
;
951 if (strlen(arg
) > sizeof(argvs
) - 1) {
953 error("string too long");
959 if (isspace(arg
[i
])) {
960 if (state
== MA_UNQUOTED
) {
961 /* Terminate current argument */
965 } else if (state
!= MA_START
)
967 } else if (arg
[i
] == '"' || arg
[i
] == '\'') {
968 q
= arg
[i
] == '"' ? MA_DQUOTE
: MA_SQUOTE
;
969 if (state
== MA_START
) {
970 argv
[argc
] = argvs
+ j
;
972 } else if (state
== MA_UNQUOTED
)
978 } else if (arg
[i
] == '\\') {
979 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
980 quot
= state
== MA_SQUOTE
? '\'' : '"';
981 /* Unescape quote we are in */
982 /* XXX support \n and friends? */
983 if (arg
[i
+ 1] == quot
) {
986 } else if (arg
[i
+ 1] == '?' ||
987 arg
[i
+ 1] == '[' || arg
[i
+ 1] == '*') {
989 * Special case for sftp: append
990 * double-escaped glob sequence -
991 * glob will undo one level of
992 * escaping. NB. string can grow here.
994 if (j
>= sizeof(argvs
) - 5)
997 argvs
[j
++] = arg
[i
++];
1001 argvs
[j
++] = arg
[i
++];
1002 argvs
[j
++] = arg
[i
];
1005 if (state
== MA_START
) {
1006 argv
[argc
] = argvs
+ j
;
1007 state
= MA_UNQUOTED
;
1009 if (arg
[i
+ 1] == '?' || arg
[i
+ 1] == '[' ||
1010 arg
[i
+ 1] == '*' || arg
[i
+ 1] == '\\') {
1012 * Special case for sftp: append
1013 * escaped glob sequence -
1014 * glob will undo one level of
1017 argvs
[j
++] = arg
[i
++];
1018 argvs
[j
++] = arg
[i
];
1020 /* Unescape everything */
1021 /* XXX support \n and friends? */
1023 argvs
[j
++] = arg
[i
];
1026 } else if (arg
[i
] == '#') {
1027 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
)
1028 argvs
[j
++] = arg
[i
];
1031 } else if (arg
[i
] == '\0') {
1032 if (state
== MA_SQUOTE
|| state
== MA_DQUOTE
) {
1033 error("Unterminated quoted argument");
1037 if (state
== MA_UNQUOTED
) {
1043 if (state
== MA_START
) {
1044 argv
[argc
] = argvs
+ j
;
1045 state
= MA_UNQUOTED
;
1047 if ((state
== MA_SQUOTE
|| state
== MA_DQUOTE
) &&
1048 (arg
[i
] == '?' || arg
[i
] == '[' || arg
[i
] == '*')) {
1050 * Special case for sftp: escape quoted
1051 * glob(3) wildcards. NB. string can grow
1054 if (j
>= sizeof(argvs
) - 3)
1055 goto args_too_longs
;
1057 argvs
[j
++] = arg
[i
];
1059 argvs
[j
++] = arg
[i
];
1068 parse_args(const char **cpp
, int *pflag
, int *lflag
, int *iflag
, int *hflag
,
1069 unsigned long *n_arg
, char **path1
, char **path2
)
1071 const char *cmd
, *cp
= *cpp
;
1075 int i
, cmdnum
, optidx
, argc
;
1077 /* Skip leading whitespace */
1078 cp
= cp
+ strspn(cp
, WHITESPACE
);
1080 /* Ignore blank lines and lines which begin with comment '#' char */
1081 if (*cp
== '\0' || *cp
== '#')
1084 /* Check for leading '-' (disable error processing) */
1091 if ((argv
= makeargv(cp
, &argc
)) == NULL
)
1094 /* Figure out which command we have */
1095 for (i
= 0; cmds
[i
].c
!= NULL
; i
++) {
1096 if (strcasecmp(cmds
[i
].c
, argv
[0]) == 0)
1106 } else if (cmdnum
== -1) {
1107 error("Invalid command.");
1111 /* Get arguments and parse flags */
1112 *lflag
= *pflag
= *hflag
= *n_arg
= 0;
1113 *path1
= *path2
= NULL
;
1118 if ((optidx
= parse_getput_flags(cmd
, argv
, argc
, pflag
)) == -1)
1120 /* Get first pathname (mandatory) */
1121 if (argc
- optidx
< 1) {
1122 error("You must specify at least one path after a "
1123 "%s command.", cmd
);
1126 *path1
= xstrdup(argv
[optidx
]);
1127 /* Get second pathname (optional) */
1128 if (argc
- optidx
> 1) {
1129 *path2
= xstrdup(argv
[optidx
+ 1]);
1130 /* Destination is not globbed */
1131 undo_glob_escape(*path2
);
1136 if (argc
- optidx
< 2) {
1137 error("You must specify two paths after a %s "
1141 *path1
= xstrdup(argv
[optidx
]);
1142 *path2
= xstrdup(argv
[optidx
+ 1]);
1143 /* Paths are not globbed */
1144 undo_glob_escape(*path1
);
1145 undo_glob_escape(*path2
);
1153 /* Get pathname (mandatory) */
1154 if (argc
- optidx
< 1) {
1155 error("You must specify a path after a %s command.",
1159 *path1
= xstrdup(argv
[optidx
]);
1160 /* Only "rm" globs */
1162 undo_glob_escape(*path1
);
1165 if ((optidx
= parse_df_flags(cmd
, argv
, argc
, hflag
,
1168 /* Default to current directory if no path specified */
1169 if (argc
- optidx
< 1)
1172 *path1
= xstrdup(argv
[optidx
]);
1173 undo_glob_escape(*path1
);
1177 if ((optidx
= parse_ls_flags(argv
, argc
, lflag
)) == -1)
1179 /* Path is optional */
1180 if (argc
- optidx
> 0)
1181 *path1
= xstrdup(argv
[optidx
]);
1184 /* Skip ls command and following whitespace */
1185 cp
= cp
+ strlen(cmd
) + strspn(cp
, WHITESPACE
);
1187 /* Uses the rest of the line */
1194 /* Get numeric arg (mandatory) */
1195 if (argc
- optidx
< 1)
1198 l
= strtol(argv
[optidx
], &cp2
, base
);
1199 if (cp2
== argv
[optidx
] || *cp2
!= '\0' ||
1200 ((l
== LONG_MIN
|| l
== LONG_MAX
) && errno
== ERANGE
) ||
1203 error("You must supply a numeric argument "
1204 "to the %s command.", cmd
);
1208 if (cmdnum
== I_LUMASK
)
1210 /* Get pathname (mandatory) */
1211 if (argc
- optidx
< 2) {
1212 error("You must specify a path after a %s command.",
1216 *path1
= xstrdup(argv
[optidx
+ 1]);
1226 fatal("Command not implemented");
1234 parse_dispatch_command(struct sftp_conn
*conn
, const char *cmd
, char **pwd
,
1237 char *path1
, *path2
, *tmp
;
1238 int pflag
= 0, lflag
= 0, iflag
= 0, hflag
= 0, cmdnum
, i
;
1239 unsigned long n_arg
= 0;
1241 char path_buf
[MAXPATHLEN
];
1245 path1
= path2
= NULL
;
1246 cmdnum
= parse_args(&cmd
, &pflag
, &lflag
, &iflag
, &hflag
, &n_arg
,
1252 memset(&g
, 0, sizeof(g
));
1254 /* Perform command */
1260 /* Unrecognized command */
1264 err
= process_get(conn
, path1
, path2
, *pwd
, pflag
);
1267 err
= process_put(conn
, path1
, path2
, *pwd
, pflag
);
1270 path1
= make_absolute(path1
, *pwd
);
1271 path2
= make_absolute(path2
, *pwd
);
1272 err
= do_rename(conn
, path1
, path2
);
1275 path2
= make_absolute(path2
, *pwd
);
1276 err
= do_symlink(conn
, path1
, path2
);
1279 path1
= make_absolute(path1
, *pwd
);
1280 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1281 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1282 printf("Removing %s\n", g
.gl_pathv
[i
]);
1283 err
= do_rm(conn
, g
.gl_pathv
[i
]);
1284 if (err
!= 0 && err_abort
)
1289 path1
= make_absolute(path1
, *pwd
);
1291 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1293 err
= do_mkdir(conn
, path1
, &a
);
1296 path1
= make_absolute(path1
, *pwd
);
1297 err
= do_rmdir(conn
, path1
);
1300 path1
= make_absolute(path1
, *pwd
);
1301 if ((tmp
= do_realpath(conn
, path1
)) == NULL
) {
1305 if ((aa
= do_stat(conn
, tmp
, 0)) == NULL
) {
1310 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_PERMISSIONS
)) {
1311 error("Can't change directory: Can't check target");
1316 if (!S_ISDIR(aa
->perm
)) {
1317 error("Can't change directory: \"%s\" is not "
1318 "a directory", tmp
);
1328 do_globbed_ls(conn
, *pwd
, *pwd
, lflag
);
1332 /* Strip pwd off beginning of non-absolute paths */
1337 path1
= make_absolute(path1
, *pwd
);
1338 err
= do_globbed_ls(conn
, path1
, tmp
, lflag
);
1341 /* Default to current directory if no path specified */
1343 path1
= xstrdup(*pwd
);
1344 path1
= make_absolute(path1
, *pwd
);
1345 err
= do_df(conn
, path1
, hflag
, iflag
);
1348 if (chdir(path1
) == -1) {
1349 error("Couldn't change local directory to "
1350 "\"%s\": %s", path1
, strerror(errno
));
1355 if (mkdir(path1
, 0777) == -1) {
1356 error("Couldn't create local directory "
1357 "\"%s\": %s", path1
, strerror(errno
));
1365 local_do_shell(cmd
);
1369 printf("Local umask: %03lo\n", n_arg
);
1372 path1
= make_absolute(path1
, *pwd
);
1374 a
.flags
|= SSH2_FILEXFER_ATTR_PERMISSIONS
;
1376 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1377 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1378 printf("Changing mode on %s\n", g
.gl_pathv
[i
]);
1379 err
= do_setstat(conn
, g
.gl_pathv
[i
], &a
);
1380 if (err
!= 0 && err_abort
)
1386 path1
= make_absolute(path1
, *pwd
);
1387 remote_glob(conn
, path1
, GLOB_NOCHECK
, NULL
, &g
);
1388 for (i
= 0; g
.gl_pathv
[i
] && !interrupted
; i
++) {
1389 if (!(aa
= do_stat(conn
, g
.gl_pathv
[i
], 0))) {
1396 if (!(aa
->flags
& SSH2_FILEXFER_ATTR_UIDGID
)) {
1397 error("Can't get current ownership of "
1398 "remote file \"%s\"", g
.gl_pathv
[i
]);
1405 aa
->flags
&= SSH2_FILEXFER_ATTR_UIDGID
;
1406 if (cmdnum
== I_CHOWN
) {
1407 printf("Changing owner on %s\n", g
.gl_pathv
[i
]);
1410 printf("Changing group on %s\n", g
.gl_pathv
[i
]);
1413 err
= do_setstat(conn
, g
.gl_pathv
[i
], aa
);
1414 if (err
!= 0 && err_abort
)
1419 printf("Remote working directory: %s\n", *pwd
);
1422 if (!getcwd(path_buf
, sizeof(path_buf
))) {
1423 error("Couldn't get local cwd: %s", strerror(errno
));
1427 printf("Local working directory: %s\n", path_buf
);
1430 /* Processed below */
1436 printf("SFTP protocol version %u\n", sftp_proto_version(conn
));
1439 showprogress
= !showprogress
;
1441 printf("Progress meter enabled\n");
1443 printf("Progress meter disabled\n");
1446 fatal("%d is not implemented", cmdnum
);
1456 /* If an unignored error occurs in batch mode we should abort. */
1457 if (err_abort
&& err
!= 0)
1459 else if (cmdnum
== I_QUIT
)
1467 prompt(EditLine
*el
)
1474 interactive_loop(int fd_in
, int fd_out
, char *file1
, char *file2
)
1479 struct sftp_conn
*conn
;
1480 int err
, interactive
;
1481 EditLine
*el
= NULL
;
1485 extern char *__progname
;
1487 if (!batchmode
&& isatty(STDIN_FILENO
)) {
1488 if ((el
= el_init(__progname
, stdin
, stdout
, stderr
)) == NULL
)
1489 fatal("Couldn't initialise editline");
1490 if ((hl
= history_init()) == NULL
)
1491 fatal("Couldn't initialise editline history");
1492 history(hl
, &hev
, H_SETSIZE
, 100);
1493 el_set(el
, EL_HIST
, history
, hl
);
1495 el_set(el
, EL_PROMPT
, prompt
);
1496 el_set(el
, EL_EDITOR
, "emacs");
1497 el_set(el
, EL_TERMINAL
, NULL
);
1498 el_set(el
, EL_SIGNAL
, 1);
1499 el_source(el
, NULL
);
1501 #endif /* USE_LIBEDIT */
1503 conn
= do_init(fd_in
, fd_out
, copy_buffer_len
, num_requests
);
1505 fatal("Couldn't initialise connection to server");
1507 pwd
= do_realpath(conn
, ".");
1511 if (file1
!= NULL
) {
1512 dir
= xstrdup(file1
);
1513 dir
= make_absolute(dir
, pwd
);
1515 if (remote_is_dir(conn
, dir
) && file2
== NULL
) {
1516 printf("Changing to: %s\n", dir
);
1517 snprintf(cmd
, sizeof cmd
, "cd \"%s\"", dir
);
1518 if (parse_dispatch_command(conn
, cmd
, &pwd
, 1) != 0) {
1526 snprintf(cmd
, sizeof cmd
, "get %s", dir
);
1528 snprintf(cmd
, sizeof cmd
, "get %s %s", dir
,
1531 err
= parse_dispatch_command(conn
, cmd
, &pwd
, 1);
1540 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1541 setvbuf(stdout
, NULL
, _IOLBF
, 0);
1542 setvbuf(infile
, NULL
, _IOLBF
, 0);
1548 interactive
= !batchmode
&& isatty(STDIN_FILENO
);
1553 signal(SIGINT
, SIG_IGN
);
1558 if (fgets(cmd
, sizeof(cmd
), infile
) == NULL
) {
1563 if (!interactive
) { /* Echo command */
1564 printf("sftp> %s", cmd
);
1565 if (strlen(cmd
) > 0 &&
1566 cmd
[strlen(cmd
) - 1] != '\n')
1574 if ((line
= el_gets(el
, &count
)) == NULL
|| count
<= 0) {
1578 history(hl
, &hev
, H_ENTER
, line
);
1579 if (strlcpy(cmd
, line
, sizeof(cmd
)) >= sizeof(cmd
)) {
1580 fprintf(stderr
, "Error: input line too long\n");
1583 #endif /* USE_LIBEDIT */
1586 cp
= strrchr(cmd
, '\n');
1590 /* Handle user interrupts gracefully during commands */
1592 signal(SIGINT
, cmd_interrupt
);
1594 err
= parse_dispatch_command(conn
, cmd
, &pwd
, batchmode
);
1604 #endif /* USE_LIBEDIT */
1606 /* err == 1 signifies normal "quit" exit */
1607 return (err
>= 0 ? 0 : -1);
1611 connect_to_server(char *path
, char **args
, int *in
, int *out
)
1616 int pin
[2], pout
[2];
1618 if ((pipe(pin
) == -1) || (pipe(pout
) == -1))
1619 fatal("pipe: %s", strerror(errno
));
1624 #else /* USE_PIPES */
1627 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, inout
) == -1)
1628 fatal("socketpair: %s", strerror(errno
));
1629 *in
= *out
= inout
[0];
1630 c_in
= c_out
= inout
[1];
1631 #endif /* USE_PIPES */
1633 if ((sshpid
= fork()) == -1)
1634 fatal("fork: %s", strerror(errno
));
1635 else if (sshpid
== 0) {
1636 if ((dup2(c_in
, STDIN_FILENO
) == -1) ||
1637 (dup2(c_out
, STDOUT_FILENO
) == -1)) {
1638 fprintf(stderr
, "dup2: %s\n", strerror(errno
));
1647 * The underlying ssh is in the same process group, so we must
1648 * ignore SIGINT if we want to gracefully abort commands,
1649 * otherwise the signal will make it to the ssh process and
1652 signal(SIGINT
, SIG_IGN
);
1654 fprintf(stderr
, "exec: %s: %s\n", path
, strerror(errno
));
1658 signal(SIGTERM
, killchild
);
1659 signal(SIGINT
, killchild
);
1660 signal(SIGHUP
, killchild
);
1668 extern char *__progname
;
1671 "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n"
1672 " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n"
1673 " [-S program] [-s subsystem | sftp_server] host\n"
1674 " %s [user@]host[:file ...]\n"
1675 " %s [user@]host[:dir[/]]\n"
1676 " %s -b batchfile [user@]host\n", __progname
, __progname
, __progname
, __progname
);
1681 main(int argc
, char **argv
)
1683 int in
, out
, ch
, err
;
1684 char *host
, *userhost
, *cp
, *file2
= NULL
;
1685 int debug_level
= 0, sshver
= 2;
1686 char *file1
= NULL
, *sftp_server
= NULL
;
1687 char *ssh_program
= _PATH_SSH_PROGRAM
, *sftp_direct
= NULL
;
1688 LogLevel ll
= SYSLOG_LEVEL_INFO
;
1691 extern char *optarg
;
1693 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1696 __progname
= ssh_get_progname(argv
[0]);
1697 memset(&args
, '\0', sizeof(args
));
1699 addargs(&args
, "%s", ssh_program
);
1700 addargs(&args
, "-oForwardX11 no");
1701 addargs(&args
, "-oForwardAgent no");
1702 addargs(&args
, "-oPermitLocalCommand no");
1703 addargs(&args
, "-oClearAllForwardings yes");
1705 ll
= SYSLOG_LEVEL_INFO
;
1708 while ((ch
= getopt(argc
, argv
, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
1711 addargs(&args
, "-C");
1714 if (debug_level
< 3) {
1715 addargs(&args
, "-v");
1716 ll
= SYSLOG_LEVEL_DEBUG1
+ debug_level
;
1722 addargs(&args
, "-%c%s", ch
, optarg
);
1726 if (sftp_server
== NULL
)
1727 sftp_server
= _PATH_SFTP_SERVER
;
1730 sftp_server
= optarg
;
1733 ssh_program
= optarg
;
1734 replacearg(&args
, 0, "%s", ssh_program
);
1738 fatal("Batch file already specified.");
1740 /* Allow "-" as stdin */
1741 if (strcmp(optarg
, "-") != 0 &&
1742 (infile
= fopen(optarg
, "r")) == NULL
)
1743 fatal("%s (%s).", strerror(errno
), optarg
);
1746 addargs(&args
, "-obatchmode yes");
1749 sftp_direct
= optarg
;
1752 copy_buffer_len
= strtol(optarg
, &cp
, 10);
1753 if (copy_buffer_len
== 0 || *cp
!= '\0')
1754 fatal("Invalid buffer size \"%s\"", optarg
);
1757 num_requests
= strtol(optarg
, &cp
, 10);
1758 if (num_requests
== 0 || *cp
!= '\0')
1759 fatal("Invalid number of requests \"%s\"",
1768 if (!isatty(STDERR_FILENO
))
1771 log_init(argv
[0], ll
, SYSLOG_FACILITY_USER
, 1);
1773 if (sftp_direct
== NULL
) {
1774 if (optind
== argc
|| argc
> (optind
+ 2))
1777 userhost
= xstrdup(argv
[optind
]);
1778 file2
= argv
[optind
+1];
1780 if ((host
= strrchr(userhost
, '@')) == NULL
)
1785 fprintf(stderr
, "Missing username\n");
1788 addargs(&args
, "-l%s", userhost
);
1791 if ((cp
= colon(host
)) != NULL
) {
1796 host
= cleanhostname(host
);
1798 fprintf(stderr
, "Missing hostname\n");
1802 addargs(&args
, "-oProtocol %d", sshver
);
1804 /* no subsystem if the server-spec contains a '/' */
1805 if (sftp_server
== NULL
|| strchr(sftp_server
, '/') == NULL
)
1806 addargs(&args
, "-s");
1808 addargs(&args
, "%s", host
);
1809 addargs(&args
, "%s", (sftp_server
!= NULL
?
1810 sftp_server
: "sftp"));
1813 fprintf(stderr
, "Connecting to %s...\n", host
);
1814 connect_to_server(ssh_program
, args
.list
, &in
, &out
);
1817 addargs(&args
, "sftp-server");
1820 fprintf(stderr
, "Attaching to %s...\n", sftp_direct
);
1821 connect_to_server(sftp_direct
, args
.list
, &in
, &out
);
1825 err
= interactive_loop(in
, out
, file1
, file2
);
1827 #if !defined(USE_PIPES)
1828 shutdown(in
, SHUT_RDWR
);
1829 shutdown(out
, SHUT_RDWR
);
1837 while (waitpid(sshpid
, NULL
, 0) == -1)
1839 fatal("Couldn't wait for ssh process: %s",
1842 exit(err
== 0 ? 0 : 1);