1 /* $OpenBSD: sftp.c,v 1.132 2010/12/04 00:18:01 djm Exp $ */
4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #ifdef HAVE_SYS_STAT_H
24 # include <sys/stat.h>
26 #include <sys/param.h>
27 #include <sys/socket.h>
29 #ifdef HAVE_SYS_STATVFS_H
30 #include <sys/statvfs.h>
45 typedef void EditLine;
64 #include "pathnames.h"
69 #include "sftp-common.h"
70 #include "sftp-client.h"
72 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
73 #define DEFAULT_NUM_REQUESTS 256 /* # concurrent outstanding requests */
75 /* File to read commands from */
78 /* Are we in batchfile mode? */
81 /* PID of ssh transport process */
82 static pid_t sshpid = -1;
84 /* This is set to 0 if the progressmeter is not desired. */
87 /* When this option is set, we always recursively download/upload directories */
90 /* When this option is set, the file transfers will always preserve times */
93 /* SIGINT received during command processing */
94 volatile sig_atomic_t interrupted = 0;
96 /* I wish qsort() took a separate ctx for the comparison function...*/
99 /* Context used for commandline completion */
100 struct complete_ctx {
101 struct sftp_conn *conn;
105 int remote_glob(struct sftp_conn *, const char *, int,
106 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
108 extern char *__progname;
110 /* Separators for interactive commands */
111 #define WHITESPACE " \t\r\n"
114 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
115 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
116 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
117 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
118 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
119 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
120 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
121 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
122 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
124 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
125 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
127 /* Commands for interactive mode */
152 #define I_PROGRESS 23
160 /* Type of completion */
165 static const struct CMD cmds[] = {
166 { "bye", I_QUIT, NOARGS },
167 { "cd", I_CHDIR, REMOTE },
168 { "chdir", I_CHDIR, REMOTE },
169 { "chgrp", I_CHGRP, REMOTE },
170 { "chmod", I_CHMOD, REMOTE },
171 { "chown", I_CHOWN, REMOTE },
172 { "df", I_DF, REMOTE },
173 { "dir", I_LS, REMOTE },
174 { "exit", I_QUIT, NOARGS },
175 { "get", I_GET, REMOTE },
176 { "help", I_HELP, NOARGS },
177 { "lcd", I_LCHDIR, LOCAL },
178 { "lchdir", I_LCHDIR, LOCAL },
179 { "lls", I_LLS, LOCAL },
180 { "lmkdir", I_LMKDIR, LOCAL },
181 { "ln", I_LINK, REMOTE },
182 { "lpwd", I_LPWD, LOCAL },
183 { "ls", I_LS, REMOTE },
184 { "lumask", I_LUMASK, NOARGS },
185 { "mkdir", I_MKDIR, REMOTE },
186 { "mget", I_GET, REMOTE },
187 { "mput", I_PUT, LOCAL },
188 { "progress", I_PROGRESS, NOARGS },
189 { "put", I_PUT, LOCAL },
190 { "pwd", I_PWD, REMOTE },
191 { "quit", I_QUIT, NOARGS },
192 { "rename", I_RENAME, REMOTE },
193 { "rm", I_RM, REMOTE },
194 { "rmdir", I_RMDIR, REMOTE },
195 { "symlink", I_SYMLINK, REMOTE },
196 { "version", I_VERSION, NOARGS },
197 { "!", I_SHELL, NOARGS },
198 { "?", I_HELP, NOARGS },
202 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
209 kill(sshpid, SIGTERM);
210 waitpid(sshpid, NULL, 0);
218 cmd_interrupt(int signo)
220 const char msg[] = "\rInterrupt \n";
221 int olderrno = errno;
223 write(STDERR_FILENO, msg, sizeof(msg) - 1);
231 printf("Available commands:\n"
233 "cd path Change remote directory to 'path'\n"
234 "chgrp grp path Change group of file 'path' to 'grp'\n"
235 "chmod mode path Change permissions of file 'path' to 'mode'\n"
236 "chown own path Change owner of file 'path' to 'own'\n"
237 "df [-hi] [path] Display statistics for current directory or\n"
238 " filesystem containing 'path'\n"
240 "get [-Ppr] remote [local] Download file\n"
241 "help Display this help text\n"
242 "lcd path Change local directory to 'path'\n"
243 "lls [ls-options [path]] Display local directory listing\n"
244 "lmkdir path Create local directory\n"
245 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
246 "lpwd Print local working directory\n"
247 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
248 "lumask umask Set local umask to 'umask'\n"
249 "mkdir path Create remote directory\n"
250 "progress Toggle display of progress meter\n"
251 "put [-Ppr] local [remote] Upload file\n"
252 "pwd Display remote working directory\n"
254 "rename oldpath newpath Rename remote file\n"
255 "rm path Delete remote file\n"
256 "rmdir path Remove remote directory\n"
257 "symlink oldpath newpath Symlink remote file\n"
258 "version Show SFTP version\n"
259 "!command Execute 'command' in local shell\n"
260 "! Escape to local shell\n"
261 "? Synonym for help\n");
265 local_do_shell(const char *args)
274 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
275 shell = _PATH_BSHELL;
277 if ((pid = fork()) == -1)
278 fatal("Couldn't fork: %s", strerror(errno));
281 /* XXX: child has pipe fds to ssh subproc open - issue? */
283 debug3("Executing %s -c \"%s\"", shell, args);
284 execl(shell, shell, "-c", args, (char *)NULL);
286 debug3("Executing %s", shell);
287 execl(shell, shell, (char *)NULL);
289 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
293 while (waitpid(pid, &status, 0) == -1)
295 fatal("Couldn't wait for child: %s", strerror(errno));
296 if (!WIFEXITED(status))
297 error("Shell exited abnormally");
298 else if (WEXITSTATUS(status))
299 error("Shell exited with status %d", WEXITSTATUS(status));
303 local_do_ls(const char *args)
306 local_do_shell(_PATH_LS);
308 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
309 char *buf = xmalloc(len);
311 /* XXX: quoting - rip quoting code from ftp? */
312 snprintf(buf, len, _PATH_LS " %s", args);
318 /* Strip one path (usually the pwd) from the start of another */
320 path_strip(char *path, char *strip)
325 return (xstrdup(path));
328 if (strncmp(path, strip, len) == 0) {
329 if (strip[len - 1] != '/' && path[len] == '/')
331 return (xstrdup(path + len));
334 return (xstrdup(path));
338 make_absolute(char *p, char *pwd)
343 if (p && p[0] != '/') {
344 abs_str = path_append(pwd, p);
352 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
355 extern int opterr, optind, optopt, optreset;
358 optind = optreset = 1;
362 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
373 error("%s: Invalid flag -%c", cmd, optopt);
382 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
384 extern int opterr, optind, optopt, optreset;
387 optind = optreset = 1;
391 while ((ch = getopt(argc, argv, "s")) != -1) {
397 error("%s: Invalid flag -%c", cmd, optopt);
406 parse_ls_flags(char **argv, int argc, int *lflag)
408 extern int opterr, optind, optopt, optreset;
411 optind = optreset = 1;
414 *lflag = LS_NAME_SORT;
415 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
418 *lflag &= ~VIEW_FLAGS;
419 *lflag |= LS_SHORT_VIEW;
422 *lflag &= ~SORT_FLAGS;
423 *lflag |= LS_SIZE_SORT;
426 *lflag |= LS_SHOW_ALL;
429 *lflag &= ~SORT_FLAGS;
432 *lflag |= LS_SI_UNITS;
435 *lflag &= ~LS_SHORT_VIEW;
436 *lflag |= LS_LONG_VIEW;
439 *lflag &= ~LS_SHORT_VIEW;
440 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
443 *lflag |= LS_REVERSE_SORT;
446 *lflag &= ~SORT_FLAGS;
447 *lflag |= LS_TIME_SORT;
450 error("ls: Invalid flag -%c", optopt);
459 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
461 extern int opterr, optind, optopt, optreset;
464 optind = optreset = 1;
468 while ((ch = getopt(argc, argv, "hi")) != -1) {
477 error("%s: Invalid flag -%c", cmd, optopt);
490 /* XXX: report errors? */
491 if (stat(path, &sb) == -1)
494 return(S_ISDIR(sb.st_mode));
498 remote_is_dir(struct sftp_conn *conn, char *path)
502 /* XXX: report errors? */
503 if ((a = do_stat(conn, path, 1)) == NULL)
505 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
507 return(S_ISDIR(a->perm));
510 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
512 pathname_is_dir(char *pathname)
514 size_t l = strlen(pathname);
516 return l > 0 && pathname[l - 1] == '/';
520 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
521 int pflag, int rflag)
523 char *abs_src = NULL;
524 char *abs_dst = NULL;
526 char *filename, *tmp=NULL;
529 abs_src = xstrdup(src);
530 abs_src = make_absolute(abs_src, pwd);
531 memset(&g, 0, sizeof(g));
533 debug3("Looking up %s", abs_src);
534 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
535 error("File \"%s\" not found.", abs_src);
541 * If multiple matches then dst must be a directory or
544 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
545 error("Multiple source paths, but destination "
546 "\"%s\" is not a directory", dst);
551 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
552 tmp = xstrdup(g.gl_pathv[i]);
553 if ((filename = basename(tmp)) == NULL) {
554 error("basename %s: %s", tmp, strerror(errno));
560 if (g.gl_matchc == 1 && dst) {
562 abs_dst = path_append(dst, filename);
564 abs_dst = xstrdup(dst);
567 abs_dst = path_append(dst, filename);
569 abs_dst = xstrdup(filename);
573 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
574 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
575 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
576 pflag || global_pflag, 1) == -1)
579 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
580 pflag || global_pflag) == -1)
594 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
595 int pflag, int rflag)
597 char *tmp_dst = NULL;
598 char *abs_dst = NULL;
599 char *tmp = NULL, *filename = NULL;
602 int i, dst_is_dir = 1;
606 tmp_dst = xstrdup(dst);
607 tmp_dst = make_absolute(tmp_dst, pwd);
610 memset(&g, 0, sizeof(g));
611 debug3("Looking up %s", src);
612 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
613 error("File \"%s\" not found.", src);
618 /* If we aren't fetching to pwd then stash this status for later */
620 dst_is_dir = remote_is_dir(conn, tmp_dst);
622 /* If multiple matches, dst may be directory or unspecified */
623 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
624 error("Multiple paths match, but destination "
625 "\"%s\" is not a directory", tmp_dst);
630 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
631 if (stat(g.gl_pathv[i], &sb) == -1) {
633 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
637 tmp = xstrdup(g.gl_pathv[i]);
638 if ((filename = basename(tmp)) == NULL) {
639 error("basename %s: %s", tmp, strerror(errno));
645 if (g.gl_matchc == 1 && tmp_dst) {
646 /* If directory specified, append filename */
648 abs_dst = path_append(tmp_dst, filename);
650 abs_dst = xstrdup(tmp_dst);
651 } else if (tmp_dst) {
652 abs_dst = path_append(tmp_dst, filename);
654 abs_dst = make_absolute(xstrdup(filename), pwd);
658 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
659 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
660 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
661 pflag || global_pflag, 1) == -1)
664 if (do_upload(conn, g.gl_pathv[i], abs_dst,
665 pflag || global_pflag) == -1)
680 sdirent_comp(const void *aa, const void *bb)
682 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
683 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
684 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
686 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
687 if (sort_flag & LS_NAME_SORT)
688 return (rmul * strcmp(a->filename, b->filename));
689 else if (sort_flag & LS_TIME_SORT)
690 return (rmul * NCMP(a->a.mtime, b->a.mtime));
691 else if (sort_flag & LS_SIZE_SORT)
692 return (rmul * NCMP(a->a.size, b->a.size));
694 fatal("Unknown ls sort type");
697 /* sftp ls.1 replacement for directories */
699 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
702 u_int c = 1, colspace = 0, columns = 1;
705 if ((n = do_readdir(conn, path, &d)) != 0)
708 if (!(lflag & LS_SHORT_VIEW)) {
709 u_int m = 0, width = 80;
713 /* Count entries for sort and find longest filename */
714 for (n = 0; d[n] != NULL; n++) {
715 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
716 m = MAX(m, strlen(d[n]->filename));
719 /* Add any subpath that also needs to be counted */
720 tmp = path_strip(path, strip_path);
724 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
727 columns = width / (m + 2);
728 columns = MAX(columns, 1);
729 colspace = width / columns;
730 colspace = MIN(colspace, width);
733 if (lflag & SORT_FLAGS) {
734 for (n = 0; d[n] != NULL; n++)
735 ; /* count entries */
736 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
737 qsort(d, n, sizeof(*d), sdirent_comp);
740 for (n = 0; d[n] != NULL && !interrupted; n++) {
743 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
746 tmp = path_append(path, d[n]->filename);
747 fname = path_strip(tmp, strip_path);
750 if (lflag & LS_LONG_VIEW) {
751 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
755 memset(&sb, 0, sizeof(sb));
756 attrib_to_stat(&d[n]->a, &sb);
757 lname = ls_file(fname, &sb, 1,
758 (lflag & LS_SI_UNITS));
759 printf("%s\n", lname);
762 printf("%s\n", d[n]->longname);
764 printf("%-*s", colspace, fname);
775 if (!(lflag & LS_LONG_VIEW) && (c != 1))
778 free_sftp_dirents(d);
782 /* sftp ls.1 replacement which handles path globs */
784 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
792 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
794 memset(&g, 0, sizeof(g));
796 if (remote_glob(conn, path,
797 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT, NULL, &g) ||
798 (g.gl_pathc && !g.gl_matchc)) {
801 error("Can't ls: \"%s\" not found", path);
809 * If the glob returns a single match and it is a directory,
810 * then just list its contents.
812 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
813 S_ISDIR(g.gl_statv[0]->st_mode)) {
814 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
819 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
822 if (!(lflag & LS_SHORT_VIEW)) {
823 /* Count entries for sort and find longest filename */
824 for (i = 0; g.gl_pathv[i]; i++)
825 m = MAX(m, strlen(g.gl_pathv[i]));
827 columns = width / (m + 2);
828 columns = MAX(columns, 1);
829 colspace = width / columns;
832 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
833 fname = path_strip(g.gl_pathv[i], strip_path);
834 if (lflag & LS_LONG_VIEW) {
835 if (g.gl_statv[i] == NULL) {
836 error("no stat information for %s", fname);
839 lname = ls_file(fname, g.gl_statv[i], 1,
840 (lflag & LS_SI_UNITS));
841 printf("%s\n", lname);
844 printf("%-*s", colspace, fname);
854 if (!(lflag & LS_LONG_VIEW) && (c != 1))
865 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
867 struct sftp_statvfs st;
868 char s_used[FMT_SCALED_STRSIZE];
869 char s_avail[FMT_SCALED_STRSIZE];
870 char s_root[FMT_SCALED_STRSIZE];
871 char s_total[FMT_SCALED_STRSIZE];
872 unsigned long long ffree;
874 if (do_statvfs(conn, path, &st, 1) == -1)
877 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
878 printf(" Inodes Used Avail "
879 "(root) %%Capacity\n");
880 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
881 (unsigned long long)st.f_files,
882 (unsigned long long)(st.f_files - st.f_ffree),
883 (unsigned long long)st.f_favail,
884 (unsigned long long)st.f_ffree, ffree);
886 strlcpy(s_used, "error", sizeof(s_used));
887 strlcpy(s_avail, "error", sizeof(s_avail));
888 strlcpy(s_root, "error", sizeof(s_root));
889 strlcpy(s_total, "error", sizeof(s_total));
890 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
891 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
892 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
893 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
894 printf(" Size Used Avail (root) %%Capacity\n");
895 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
896 s_total, s_used, s_avail, s_root,
897 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
900 printf(" Size Used Avail "
901 "(root) %%Capacity\n");
902 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
903 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
904 (unsigned long long)(st.f_frsize *
905 (st.f_blocks - st.f_bfree) / 1024),
906 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
907 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
908 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
915 * Undo escaping of glob sequences in place. Used to undo extra escaping
916 * applied in makeargv() when the string is destined for a function that
920 undo_glob_escape(char *s)
955 * Split a string into an argument vector using sh(1)-style quoting,
956 * comment and escaping rules, but with some tweaks to handle glob(3)
958 * The "sloppy" flag allows for recovery from missing terminating quote, for
959 * use in parsing incomplete commandlines during tab autocompletion.
961 * Returns NULL on error or a NULL-terminated array of arguments.
963 * If "lastquote" is not NULL, the quoting character used for the last
964 * argument is placed in *lastquote ("\0", "'" or "\"").
966 * If "terminated" is not NULL, *terminated will be set to 1 when the
967 * last argument's quote has been properly terminated or 0 otherwise.
968 * This parameter is only of use if "sloppy" is set.
971 #define MAXARGLEN 8192
973 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
978 static char argvs[MAXARGLEN];
979 static char *argv[MAXARGS + 1];
980 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
983 if (strlen(arg) > sizeof(argvs) - 1) {
985 error("string too long");
988 if (terminated != NULL)
990 if (lastquote != NULL)
995 if (isspace(arg[i])) {
996 if (state == MA_UNQUOTED) {
997 /* Terminate current argument */
1001 } else if (state != MA_START)
1002 argvs[j++] = arg[i];
1003 } else if (arg[i] == '"' || arg[i] == '\'') {
1004 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1005 if (state == MA_START) {
1006 argv[argc] = argvs + j;
1008 if (lastquote != NULL)
1009 *lastquote = arg[i];
1010 } else if (state == MA_UNQUOTED)
1012 else if (state == q)
1013 state = MA_UNQUOTED;
1015 argvs[j++] = arg[i];
1016 } else if (arg[i] == '\\') {
1017 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1018 quot = state == MA_SQUOTE ? '\'' : '"';
1019 /* Unescape quote we are in */
1020 /* XXX support \n and friends? */
1021 if (arg[i + 1] == quot) {
1023 argvs[j++] = arg[i];
1024 } else if (arg[i + 1] == '?' ||
1025 arg[i + 1] == '[' || arg[i + 1] == '*') {
1027 * Special case for sftp: append
1028 * double-escaped glob sequence -
1029 * glob will undo one level of
1030 * escaping. NB. string can grow here.
1032 if (j >= sizeof(argvs) - 5)
1033 goto args_too_longs;
1035 argvs[j++] = arg[i++];
1037 argvs[j++] = arg[i];
1039 argvs[j++] = arg[i++];
1040 argvs[j++] = arg[i];
1043 if (state == MA_START) {
1044 argv[argc] = argvs + j;
1045 state = MA_UNQUOTED;
1046 if (lastquote != NULL)
1049 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1050 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1052 * Special case for sftp: append
1053 * escaped glob sequence -
1054 * glob will undo one level of
1057 argvs[j++] = arg[i++];
1058 argvs[j++] = arg[i];
1060 /* Unescape everything */
1061 /* XXX support \n and friends? */
1063 argvs[j++] = arg[i];
1066 } else if (arg[i] == '#') {
1067 if (state == MA_SQUOTE || state == MA_DQUOTE)
1068 argvs[j++] = arg[i];
1071 } else if (arg[i] == '\0') {
1072 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1074 state = MA_UNQUOTED;
1075 if (terminated != NULL)
1079 error("Unterminated quoted argument");
1083 if (state == MA_UNQUOTED) {
1089 if (state == MA_START) {
1090 argv[argc] = argvs + j;
1091 state = MA_UNQUOTED;
1092 if (lastquote != NULL)
1095 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1096 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1098 * Special case for sftp: escape quoted
1099 * glob(3) wildcards. NB. string can grow
1102 if (j >= sizeof(argvs) - 3)
1103 goto args_too_longs;
1105 argvs[j++] = arg[i];
1107 argvs[j++] = arg[i];
1116 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1117 int *hflag, int *sflag, unsigned long *n_arg, char **path1, char **path2)
1119 const char *cmd, *cp = *cpp;
1123 int i, cmdnum, optidx, argc;
1125 /* Skip leading whitespace */
1126 cp = cp + strspn(cp, WHITESPACE);
1128 /* Check for leading '-' (disable error processing) */
1133 cp = cp + strspn(cp, WHITESPACE);
1136 /* Ignore blank lines and lines which begin with comment '#' char */
1137 if (*cp == '\0' || *cp == '#')
1140 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1143 /* Figure out which command we have */
1144 for (i = 0; cmds[i].c != NULL; i++) {
1145 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1155 } else if (cmdnum == -1) {
1156 error("Invalid command.");
1160 /* Get arguments and parse flags */
1161 *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1162 *path1 = *path2 = NULL;
1167 if ((optidx = parse_getput_flags(cmd, argv, argc,
1168 pflag, rflag)) == -1)
1170 /* Get first pathname (mandatory) */
1171 if (argc - optidx < 1) {
1172 error("You must specify at least one path after a "
1173 "%s command.", cmd);
1176 *path1 = xstrdup(argv[optidx]);
1177 /* Get second pathname (optional) */
1178 if (argc - optidx > 1) {
1179 *path2 = xstrdup(argv[optidx + 1]);
1180 /* Destination is not globbed */
1181 undo_glob_escape(*path2);
1185 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1189 if (argc - optidx < 2) {
1190 error("You must specify two paths after a %s "
1194 *path1 = xstrdup(argv[optidx]);
1195 *path2 = xstrdup(argv[optidx + 1]);
1196 /* Paths are not globbed */
1197 undo_glob_escape(*path1);
1198 undo_glob_escape(*path2);
1206 /* Get pathname (mandatory) */
1207 if (argc - optidx < 1) {
1208 error("You must specify a path after a %s command.",
1212 *path1 = xstrdup(argv[optidx]);
1213 /* Only "rm" globs */
1215 undo_glob_escape(*path1);
1218 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1221 /* Default to current directory if no path specified */
1222 if (argc - optidx < 1)
1225 *path1 = xstrdup(argv[optidx]);
1226 undo_glob_escape(*path1);
1230 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1232 /* Path is optional */
1233 if (argc - optidx > 0)
1234 *path1 = xstrdup(argv[optidx]);
1237 /* Skip ls command and following whitespace */
1238 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1240 /* Uses the rest of the line */
1247 /* Get numeric arg (mandatory) */
1248 if (argc - optidx < 1)
1251 l = strtol(argv[optidx], &cp2, base);
1252 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1253 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1256 error("You must supply a numeric argument "
1257 "to the %s command.", cmd);
1261 if (cmdnum == I_LUMASK)
1263 /* Get pathname (mandatory) */
1264 if (argc - optidx < 2) {
1265 error("You must specify a path after a %s command.",
1269 *path1 = xstrdup(argv[optidx + 1]);
1279 fatal("Command not implemented");
1287 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1290 char *path1, *path2, *tmp;
1291 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0;
1293 unsigned long n_arg = 0;
1295 char path_buf[MAXPATHLEN];
1299 path1 = path2 = NULL;
1300 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag,
1301 &sflag, &n_arg, &path1, &path2);
1306 memset(&g, 0, sizeof(g));
1308 /* Perform command */
1314 /* Unrecognized command */
1318 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1321 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1324 path1 = make_absolute(path1, *pwd);
1325 path2 = make_absolute(path2, *pwd);
1326 err = do_rename(conn, path1, path2);
1331 path1 = make_absolute(path1, *pwd);
1332 path2 = make_absolute(path2, *pwd);
1333 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1336 path1 = make_absolute(path1, *pwd);
1337 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1338 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1339 printf("Removing %s\n", g.gl_pathv[i]);
1340 err = do_rm(conn, g.gl_pathv[i]);
1341 if (err != 0 && err_abort)
1346 path1 = make_absolute(path1, *pwd);
1348 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1350 err = do_mkdir(conn, path1, &a, 1);
1353 path1 = make_absolute(path1, *pwd);
1354 err = do_rmdir(conn, path1);
1357 path1 = make_absolute(path1, *pwd);
1358 if ((tmp = do_realpath(conn, path1)) == NULL) {
1362 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1367 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1368 error("Can't change directory: Can't check target");
1373 if (!S_ISDIR(aa->perm)) {
1374 error("Can't change directory: \"%s\" is not "
1375 "a directory", tmp);
1385 do_ls_dir(conn, *pwd, *pwd, lflag);
1389 /* Strip pwd off beginning of non-absolute paths */
1394 path1 = make_absolute(path1, *pwd);
1395 err = do_globbed_ls(conn, path1, tmp, lflag);
1398 /* Default to current directory if no path specified */
1400 path1 = xstrdup(*pwd);
1401 path1 = make_absolute(path1, *pwd);
1402 err = do_df(conn, path1, hflag, iflag);
1405 if (chdir(path1) == -1) {
1406 error("Couldn't change local directory to "
1407 "\"%s\": %s", path1, strerror(errno));
1412 if (mkdir(path1, 0777) == -1) {
1413 error("Couldn't create local directory "
1414 "\"%s\": %s", path1, strerror(errno));
1422 local_do_shell(cmd);
1426 printf("Local umask: %03lo\n", n_arg);
1429 path1 = make_absolute(path1, *pwd);
1431 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1433 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1434 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1435 printf("Changing mode on %s\n", g.gl_pathv[i]);
1436 err = do_setstat(conn, g.gl_pathv[i], &a);
1437 if (err != 0 && err_abort)
1443 path1 = make_absolute(path1, *pwd);
1444 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1445 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1446 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1453 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1454 error("Can't get current ownership of "
1455 "remote file \"%s\"", g.gl_pathv[i]);
1462 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1463 if (cmdnum == I_CHOWN) {
1464 printf("Changing owner on %s\n", g.gl_pathv[i]);
1467 printf("Changing group on %s\n", g.gl_pathv[i]);
1470 err = do_setstat(conn, g.gl_pathv[i], aa);
1471 if (err != 0 && err_abort)
1476 printf("Remote working directory: %s\n", *pwd);
1479 if (!getcwd(path_buf, sizeof(path_buf))) {
1480 error("Couldn't get local cwd: %s", strerror(errno));
1484 printf("Local working directory: %s\n", path_buf);
1487 /* Processed below */
1493 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1496 showprogress = !showprogress;
1498 printf("Progress meter enabled\n");
1500 printf("Progress meter disabled\n");
1503 fatal("%d is not implemented", cmdnum);
1513 /* If an unignored error occurs in batch mode we should abort. */
1514 if (err_abort && err != 0)
1516 else if (cmdnum == I_QUIT)
1524 prompt(EditLine *el)
1529 /* Display entries in 'list' after skipping the first 'len' chars */
1531 complete_display(char **list, u_int len)
1533 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1537 /* Count entries for sort and find longest */
1538 for (y = 0; list[y]; y++)
1539 m = MAX(m, strlen(list[y]));
1541 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1544 m = m > len ? m - len : 0;
1545 columns = width / (m + 2);
1546 columns = MAX(columns, 1);
1547 colspace = width / columns;
1548 colspace = MIN(colspace, width);
1552 for (y = 0; list[y]; y++) {
1553 llen = strlen(list[y]);
1554 tmp = llen > len ? list[y] + len : "";
1555 printf("%-*s", colspace, tmp);
1566 * Given a "list" of words that begin with a common prefix of "word",
1567 * attempt to find an autocompletion to extends "word" by the next
1568 * characters common to all entries in "list".
1571 complete_ambiguous(const char *word, char **list, size_t count)
1577 u_int y, matchlen = strlen(list[0]);
1579 /* Find length of common stem */
1580 for (y = 1; list[y]; y++) {
1583 for (x = 0; x < matchlen; x++)
1584 if (list[0][x] != list[y][x])
1590 if (matchlen > strlen(word)) {
1591 char *tmp = xstrdup(list[0]);
1593 tmp[matchlen] = '\0';
1598 return xstrdup(word);
1601 /* Autocomplete a sftp command */
1603 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1606 u_int y, count = 0, cmdlen, tmplen;
1607 char *tmp, **list, argterm[3];
1610 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1612 /* No command specified: display all available commands */
1614 for (y = 0; cmds[y].c; y++)
1615 list[count++] = xstrdup(cmds[y].c);
1618 complete_display(list, 0);
1620 for (y = 0; list[y] != NULL; y++)
1626 /* Prepare subset of commands that start with "cmd" */
1627 cmdlen = strlen(cmd);
1628 for (y = 0; cmds[y].c; y++) {
1629 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1630 list[count++] = xstrdup(cmds[y].c);
1637 /* Complete ambigious command */
1638 tmp = complete_ambiguous(cmd, list, count);
1640 complete_display(list, 0);
1642 for (y = 0; list[y]; y++)
1647 tmplen = strlen(tmp);
1648 cmdlen = strlen(cmd);
1649 /* If cmd may be extended then do so */
1650 if (tmplen > cmdlen)
1651 if (el_insertstr(el, tmp + cmdlen) == -1)
1652 fatal("el_insertstr failed.");
1654 /* Terminate argument cleanly */
1658 argterm[y++] = quote;
1659 if (lastarg || *(lf->cursor) != ' ')
1662 if (y > 0 && el_insertstr(el, argterm) == -1)
1663 fatal("el_insertstr failed.");
1672 * Determine whether a particular sftp command's arguments (if any)
1673 * represent local or remote files.
1676 complete_is_remote(char *cmd) {
1682 for (i = 0; cmds[i].c; i++) {
1683 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1690 /* Autocomplete a filename "file" */
1692 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1693 char *file, int remote, int lastarg, char quote, int terminated)
1696 char *tmp, *tmp2, ins[3];
1697 u_int i, hadglob, pwdlen, len, tmplen, filelen;
1700 /* Glob from "file" location */
1704 xasprintf(&tmp, "%s*", file);
1706 memset(&g, 0, sizeof(g));
1707 if (remote != LOCAL) {
1708 tmp = make_absolute(tmp, remote_path);
1709 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1711 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1713 /* Determine length of pwd so we can trim completion display */
1714 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1715 /* Terminate counting on first unescaped glob metacharacter */
1716 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1717 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1721 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1723 if (tmp[tmplen] == '/')
1724 pwdlen = tmplen + 1; /* track last seen '/' */
1728 if (g.gl_matchc == 0)
1731 if (g.gl_matchc > 1)
1732 complete_display(g.gl_pathv, pwdlen);
1735 /* Don't try to extend globs */
1736 if (file == NULL || hadglob)
1739 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1740 tmp = path_strip(tmp2, remote_path);
1746 tmplen = strlen(tmp);
1747 filelen = strlen(file);
1749 if (tmplen > filelen) {
1750 tmp2 = tmp + filelen;
1752 /* quote argument on way out */
1753 for (i = 0; i < len; i++) {
1764 if (quote == '\0' || tmp2[i] == quote) {
1765 if (el_insertstr(el, ins) == -1)
1766 fatal("el_insertstr "
1772 if (el_insertstr(el, ins + 1) == -1)
1773 fatal("el_insertstr failed.");
1780 if (g.gl_matchc == 1) {
1784 if (*(lf->cursor - 1) != '/' &&
1785 (lastarg || *(lf->cursor) != ' '))
1788 if (i > 0 && el_insertstr(el, ins) == -1)
1789 fatal("el_insertstr failed.");
1798 /* tab-completion hook function, called via libedit */
1799 static unsigned char
1800 complete(EditLine *el, int ch)
1802 char **argv, *line, quote;
1803 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1805 struct complete_ctx *complete_ctx;
1808 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1809 fatal("%s: el_get failed", __func__);
1811 /* Figure out which argument the cursor points to */
1812 cursor = lf->cursor - lf->buffer;
1813 line = (char *)xmalloc(cursor + 1);
1814 memcpy(line, lf->buffer, cursor);
1815 line[cursor] = '\0';
1816 argv = makeargv(line, &carg, 1, "e, &terminated);
1819 /* Get all the arguments on the line */
1820 len = lf->lastchar - lf->buffer;
1821 line = (char *)xmalloc(len + 1);
1822 memcpy(line, lf->buffer, len);
1824 argv = makeargv(line, &argc, 1, NULL, NULL);
1826 /* Ensure cursor is at EOL or a argument boundary */
1827 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1828 line[cursor] != '\n') {
1834 /* Show all available commands */
1835 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1837 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1838 /* Handle the command parsing */
1839 if (complete_cmd_parse(el, argv[0], argc == carg,
1840 quote, terminated) != 0)
1842 } else if (carg >= 1) {
1843 /* Handle file parsing */
1844 int remote = complete_is_remote(argv[0]);
1845 char *filematch = NULL;
1847 if (carg > 1 && line[cursor-1] != ' ')
1848 filematch = argv[carg - 1];
1851 complete_match(el, complete_ctx->conn,
1852 *complete_ctx->remote_pathp, filematch,
1853 remote, carg == argc, quote, terminated) != 0)
1860 #endif /* USE_LIBEDIT */
1863 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1868 int err, interactive;
1869 EditLine *el = NULL;
1873 extern char *__progname;
1874 struct complete_ctx complete_ctx;
1876 if (!batchmode && isatty(STDIN_FILENO)) {
1877 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1878 fatal("Couldn't initialise editline");
1879 if ((hl = history_init()) == NULL)
1880 fatal("Couldn't initialise editline history");
1881 history(hl, &hev, H_SETSIZE, 100);
1882 el_set(el, EL_HIST, history, hl);
1884 el_set(el, EL_PROMPT, prompt);
1885 el_set(el, EL_EDITOR, "emacs");
1886 el_set(el, EL_TERMINAL, NULL);
1887 el_set(el, EL_SIGNAL, 1);
1888 el_source(el, NULL);
1890 /* Tab Completion */
1891 el_set(el, EL_ADDFN, "ftp-complete",
1892 "Context sensitive argument completion", complete);
1893 complete_ctx.conn = conn;
1894 complete_ctx.remote_pathp = &remote_path;
1895 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1896 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1898 #endif /* USE_LIBEDIT */
1900 remote_path = do_realpath(conn, ".");
1901 if (remote_path == NULL)
1904 if (file1 != NULL) {
1905 dir = xstrdup(file1);
1906 dir = make_absolute(dir, remote_path);
1908 if (remote_is_dir(conn, dir) && file2 == NULL) {
1909 printf("Changing to: %s\n", dir);
1910 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1911 if (parse_dispatch_command(conn, cmd,
1912 &remote_path, 1) != 0) {
1920 snprintf(cmd, sizeof cmd, "get %s", dir);
1922 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1925 err = parse_dispatch_command(conn, cmd,
1935 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1936 setvbuf(stdout, NULL, _IOLBF, 0);
1937 setvbuf(infile, NULL, _IOLBF, 0);
1943 interactive = !batchmode && isatty(STDIN_FILENO);
1948 signal(SIGINT, SIG_IGN);
1953 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1958 if (!interactive) { /* Echo command */
1959 printf("sftp> %s", cmd);
1960 if (strlen(cmd) > 0 &&
1961 cmd[strlen(cmd) - 1] != '\n')
1969 if ((line = el_gets(el, &count)) == NULL ||
1974 history(hl, &hev, H_ENTER, line);
1975 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1976 fprintf(stderr, "Error: input line too long\n");
1979 #endif /* USE_LIBEDIT */
1982 cp = strrchr(cmd, '\n');
1986 /* Handle user interrupts gracefully during commands */
1988 signal(SIGINT, cmd_interrupt);
1990 err = parse_dispatch_command(conn, cmd, &remote_path,
2001 #endif /* USE_LIBEDIT */
2003 /* err == 1 signifies normal "quit" exit */
2004 return (err >= 0 ? 0 : -1);
2008 connect_to_server(char *path, char **args, int *in, int *out)
2013 int pin[2], pout[2];
2015 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2016 fatal("pipe: %s", strerror(errno));
2021 #else /* USE_PIPES */
2024 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2025 fatal("socketpair: %s", strerror(errno));
2026 *in = *out = inout[0];
2027 c_in = c_out = inout[1];
2028 #endif /* USE_PIPES */
2030 if ((sshpid = fork()) == -1)
2031 fatal("fork: %s", strerror(errno));
2032 else if (sshpid == 0) {
2033 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2034 (dup2(c_out, STDOUT_FILENO) == -1)) {
2035 fprintf(stderr, "dup2: %s\n", strerror(errno));
2044 * The underlying ssh is in the same process group, so we must
2045 * ignore SIGINT if we want to gracefully abort commands,
2046 * otherwise the signal will make it to the ssh process and
2047 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2048 * underlying ssh, it must *not* ignore that signal.
2050 signal(SIGINT, SIG_IGN);
2051 signal(SIGTERM, SIG_DFL);
2053 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2057 signal(SIGTERM, killchild);
2058 signal(SIGINT, killchild);
2059 signal(SIGHUP, killchild);
2067 extern char *__progname;
2070 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2071 " [-D sftp_server_path] [-F ssh_config] "
2072 "[-i identity_file] [-l limit]\n"
2073 " [-o ssh_option] [-P port] [-R num_requests] "
2075 " [-s subsystem | sftp_server] host\n"
2076 " %s [user@]host[:file ...]\n"
2077 " %s [user@]host[:dir[/]]\n"
2078 " %s -b batchfile [user@]host\n",
2079 __progname, __progname, __progname, __progname);
2084 main(int argc, char **argv)
2086 int in, out, ch, err;
2087 char *host = NULL, *userhost, *cp, *file2 = NULL;
2088 int debug_level = 0, sshver = 2;
2089 char *file1 = NULL, *sftp_server = NULL;
2090 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2092 LogLevel ll = SYSLOG_LEVEL_INFO;
2095 extern char *optarg;
2096 struct sftp_conn *conn;
2097 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2098 size_t num_requests = DEFAULT_NUM_REQUESTS;
2099 long long limit_kbps = 0;
2101 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2104 __progname = ssh_get_progname(argv[0]);
2105 memset(&args, '\0', sizeof(args));
2107 addargs(&args, "%s", ssh_program);
2108 addargs(&args, "-oForwardX11 no");
2109 addargs(&args, "-oForwardAgent no");
2110 addargs(&args, "-oPermitLocalCommand no");
2111 addargs(&args, "-oClearAllForwardings yes");
2113 ll = SYSLOG_LEVEL_INFO;
2116 while ((ch = getopt(argc, argv,
2117 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2119 /* Passed through to ssh(1) */
2123 addargs(&args, "-%c", ch);
2125 /* Passed through to ssh(1) with argument */
2130 addargs(&args, "-%c", ch);
2131 addargs(&args, "%s", optarg);
2135 addargs(&args, "-%c", ch);
2138 addargs(&args, "-oPort %s", optarg);
2141 if (debug_level < 3) {
2142 addargs(&args, "-v");
2143 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2149 if (sftp_server == NULL)
2150 sftp_server = _PATH_SFTP_SERVER;
2156 copy_buffer_len = strtol(optarg, &cp, 10);
2157 if (copy_buffer_len == 0 || *cp != '\0')
2158 fatal("Invalid buffer size \"%s\"", optarg);
2162 fatal("Batch file already specified.");
2164 /* Allow "-" as stdin */
2165 if (strcmp(optarg, "-") != 0 &&
2166 (infile = fopen(optarg, "r")) == NULL)
2167 fatal("%s (%s).", strerror(errno), optarg);
2170 addargs(&args, "-obatchmode yes");
2176 sftp_direct = optarg;
2179 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2183 limit_kbps *= 1024; /* kbps */
2189 num_requests = strtol(optarg, &cp, 10);
2190 if (num_requests == 0 || *cp != '\0')
2191 fatal("Invalid number of requests \"%s\"",
2195 sftp_server = optarg;
2198 ssh_program = optarg;
2199 replacearg(&args, 0, "%s", ssh_program);
2207 if (!isatty(STDERR_FILENO))
2210 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2212 if (sftp_direct == NULL) {
2213 if (optind == argc || argc > (optind + 2))
2216 userhost = xstrdup(argv[optind]);
2217 file2 = argv[optind+1];
2219 if ((host = strrchr(userhost, '@')) == NULL)
2224 fprintf(stderr, "Missing username\n");
2227 addargs(&args, "-l");
2228 addargs(&args, "%s", userhost);
2231 if ((cp = colon(host)) != NULL) {
2236 host = cleanhostname(host);
2238 fprintf(stderr, "Missing hostname\n");
2242 addargs(&args, "-oProtocol %d", sshver);
2244 /* no subsystem if the server-spec contains a '/' */
2245 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2246 addargs(&args, "-s");
2248 addargs(&args, "--");
2249 addargs(&args, "%s", host);
2250 addargs(&args, "%s", (sftp_server != NULL ?
2251 sftp_server : "sftp"));
2253 connect_to_server(ssh_program, args.list, &in, &out);
2256 addargs(&args, "sftp-server");
2258 connect_to_server(sftp_direct, args.list, &in, &out);
2262 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2264 fatal("Couldn't initialise connection to server");
2267 if (sftp_direct == NULL)
2268 fprintf(stderr, "Connected to %s.\n", host);
2270 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2273 err = interactive_loop(conn, file1, file2);
2275 #if !defined(USE_PIPES)
2276 shutdown(in, SHUT_RDWR);
2277 shutdown(out, SHUT_RDWR);
2285 while (waitpid(sshpid, NULL, 0) == -1)
2287 fatal("Couldn't wait for ssh process: %s",
2290 exit(err == 0 ? 0 : 1);