1 /* $OpenBSD: sftp.c,v 1.136 2012/06/22 14:36:33 dtucker 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,
791 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
793 memset(&g, 0, sizeof(g));
795 if (remote_glob(conn, path,
796 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
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++) {
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);
1639 /* Complete ambigious command */
1640 tmp = complete_ambiguous(cmd, list, count);
1642 complete_display(list, 0);
1644 for (y = 0; list[y]; y++)
1649 tmplen = strlen(tmp);
1650 cmdlen = strlen(cmd);
1651 /* If cmd may be extended then do so */
1652 if (tmplen > cmdlen)
1653 if (el_insertstr(el, tmp + cmdlen) == -1)
1654 fatal("el_insertstr failed.");
1656 /* Terminate argument cleanly */
1660 argterm[y++] = quote;
1661 if (lastarg || *(lf->cursor) != ' ')
1664 if (y > 0 && el_insertstr(el, argterm) == -1)
1665 fatal("el_insertstr failed.");
1674 * Determine whether a particular sftp command's arguments (if any)
1675 * represent local or remote files.
1678 complete_is_remote(char *cmd) {
1684 for (i = 0; cmds[i].c; i++) {
1685 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1692 /* Autocomplete a filename "file" */
1694 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1695 char *file, int remote, int lastarg, char quote, int terminated)
1698 char *tmp, *tmp2, ins[3];
1699 u_int i, hadglob, pwdlen, len, tmplen, filelen;
1702 /* Glob from "file" location */
1706 xasprintf(&tmp, "%s*", file);
1708 memset(&g, 0, sizeof(g));
1709 if (remote != LOCAL) {
1710 tmp = make_absolute(tmp, remote_path);
1711 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1713 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1715 /* Determine length of pwd so we can trim completion display */
1716 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1717 /* Terminate counting on first unescaped glob metacharacter */
1718 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1719 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1723 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1725 if (tmp[tmplen] == '/')
1726 pwdlen = tmplen + 1; /* track last seen '/' */
1730 if (g.gl_matchc == 0)
1733 if (g.gl_matchc > 1)
1734 complete_display(g.gl_pathv, pwdlen);
1737 /* Don't try to extend globs */
1738 if (file == NULL || hadglob)
1741 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1742 tmp = path_strip(tmp2, remote_path);
1748 tmplen = strlen(tmp);
1749 filelen = strlen(file);
1751 if (tmplen > filelen) {
1752 tmp2 = tmp + filelen;
1754 /* quote argument on way out */
1755 for (i = 0; i < len; i++) {
1766 if (quote == '\0' || tmp2[i] == quote) {
1767 if (el_insertstr(el, ins) == -1)
1768 fatal("el_insertstr "
1774 if (el_insertstr(el, ins + 1) == -1)
1775 fatal("el_insertstr failed.");
1782 if (g.gl_matchc == 1) {
1786 if (*(lf->cursor - 1) != '/' &&
1787 (lastarg || *(lf->cursor) != ' '))
1790 if (i > 0 && el_insertstr(el, ins) == -1)
1791 fatal("el_insertstr failed.");
1800 /* tab-completion hook function, called via libedit */
1801 static unsigned char
1802 complete(EditLine *el, int ch)
1804 char **argv, *line, quote;
1805 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1807 struct complete_ctx *complete_ctx;
1810 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1811 fatal("%s: el_get failed", __func__);
1813 /* Figure out which argument the cursor points to */
1814 cursor = lf->cursor - lf->buffer;
1815 line = (char *)xmalloc(cursor + 1);
1816 memcpy(line, lf->buffer, cursor);
1817 line[cursor] = '\0';
1818 argv = makeargv(line, &carg, 1, "e, &terminated);
1821 /* Get all the arguments on the line */
1822 len = lf->lastchar - lf->buffer;
1823 line = (char *)xmalloc(len + 1);
1824 memcpy(line, lf->buffer, len);
1826 argv = makeargv(line, &argc, 1, NULL, NULL);
1828 /* Ensure cursor is at EOL or a argument boundary */
1829 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1830 line[cursor] != '\n') {
1836 /* Show all available commands */
1837 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1839 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1840 /* Handle the command parsing */
1841 if (complete_cmd_parse(el, argv[0], argc == carg,
1842 quote, terminated) != 0)
1844 } else if (carg >= 1) {
1845 /* Handle file parsing */
1846 int remote = complete_is_remote(argv[0]);
1847 char *filematch = NULL;
1849 if (carg > 1 && line[cursor-1] != ' ')
1850 filematch = argv[carg - 1];
1853 complete_match(el, complete_ctx->conn,
1854 *complete_ctx->remote_pathp, filematch,
1855 remote, carg == argc, quote, terminated) != 0)
1862 #endif /* USE_LIBEDIT */
1865 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1870 int err, interactive;
1871 EditLine *el = NULL;
1875 extern char *__progname;
1876 struct complete_ctx complete_ctx;
1878 if (!batchmode && isatty(STDIN_FILENO)) {
1879 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1880 fatal("Couldn't initialise editline");
1881 if ((hl = history_init()) == NULL)
1882 fatal("Couldn't initialise editline history");
1883 history(hl, &hev, H_SETSIZE, 100);
1884 el_set(el, EL_HIST, history, hl);
1886 el_set(el, EL_PROMPT, prompt);
1887 el_set(el, EL_EDITOR, "emacs");
1888 el_set(el, EL_TERMINAL, NULL);
1889 el_set(el, EL_SIGNAL, 1);
1890 el_source(el, NULL);
1892 /* Tab Completion */
1893 el_set(el, EL_ADDFN, "ftp-complete",
1894 "Context sensitive argument completion", complete);
1895 complete_ctx.conn = conn;
1896 complete_ctx.remote_pathp = &remote_path;
1897 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1898 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1900 #endif /* USE_LIBEDIT */
1902 remote_path = do_realpath(conn, ".");
1903 if (remote_path == NULL)
1906 if (file1 != NULL) {
1907 dir = xstrdup(file1);
1908 dir = make_absolute(dir, remote_path);
1910 if (remote_is_dir(conn, dir) && file2 == NULL) {
1911 printf("Changing to: %s\n", dir);
1912 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1913 if (parse_dispatch_command(conn, cmd,
1914 &remote_path, 1) != 0) {
1922 snprintf(cmd, sizeof cmd, "get %s", dir);
1924 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1927 err = parse_dispatch_command(conn, cmd,
1940 interactive = !batchmode && isatty(STDIN_FILENO);
1945 signal(SIGINT, SIG_IGN);
1950 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1955 if (!interactive) { /* Echo command */
1956 printf("sftp> %s", cmd);
1957 if (strlen(cmd) > 0 &&
1958 cmd[strlen(cmd) - 1] != '\n')
1966 if ((line = el_gets(el, &count)) == NULL ||
1971 history(hl, &hev, H_ENTER, line);
1972 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1973 fprintf(stderr, "Error: input line too long\n");
1976 #endif /* USE_LIBEDIT */
1979 cp = strrchr(cmd, '\n');
1983 /* Handle user interrupts gracefully during commands */
1985 signal(SIGINT, cmd_interrupt);
1987 err = parse_dispatch_command(conn, cmd, &remote_path,
1998 #endif /* USE_LIBEDIT */
2000 /* err == 1 signifies normal "quit" exit */
2001 return (err >= 0 ? 0 : -1);
2005 connect_to_server(char *path, char **args, int *in, int *out)
2010 int pin[2], pout[2];
2012 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2013 fatal("pipe: %s", strerror(errno));
2018 #else /* USE_PIPES */
2021 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2022 fatal("socketpair: %s", strerror(errno));
2023 *in = *out = inout[0];
2024 c_in = c_out = inout[1];
2025 #endif /* USE_PIPES */
2027 if ((sshpid = fork()) == -1)
2028 fatal("fork: %s", strerror(errno));
2029 else if (sshpid == 0) {
2030 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2031 (dup2(c_out, STDOUT_FILENO) == -1)) {
2032 fprintf(stderr, "dup2: %s\n", strerror(errno));
2041 * The underlying ssh is in the same process group, so we must
2042 * ignore SIGINT if we want to gracefully abort commands,
2043 * otherwise the signal will make it to the ssh process and
2044 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2045 * underlying ssh, it must *not* ignore that signal.
2047 signal(SIGINT, SIG_IGN);
2048 signal(SIGTERM, SIG_DFL);
2050 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2054 signal(SIGTERM, killchild);
2055 signal(SIGINT, killchild);
2056 signal(SIGHUP, killchild);
2064 extern char *__progname;
2067 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2068 " [-D sftp_server_path] [-F ssh_config] "
2069 "[-i identity_file] [-l limit]\n"
2070 " [-o ssh_option] [-P port] [-R num_requests] "
2072 " [-s subsystem | sftp_server] host\n"
2073 " %s [user@]host[:file ...]\n"
2074 " %s [user@]host[:dir[/]]\n"
2075 " %s -b batchfile [user@]host\n",
2076 __progname, __progname, __progname, __progname);
2081 main(int argc, char **argv)
2083 int in, out, ch, err;
2084 char *host = NULL, *userhost, *cp, *file2 = NULL;
2085 int debug_level = 0, sshver = 2;
2086 char *file1 = NULL, *sftp_server = NULL;
2087 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2089 LogLevel ll = SYSLOG_LEVEL_INFO;
2092 extern char *optarg;
2093 struct sftp_conn *conn;
2094 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2095 size_t num_requests = DEFAULT_NUM_REQUESTS;
2096 long long limit_kbps = 0;
2098 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2101 __progname = ssh_get_progname(argv[0]);
2102 memset(&args, '\0', sizeof(args));
2104 addargs(&args, "%s", ssh_program);
2105 addargs(&args, "-oForwardX11 no");
2106 addargs(&args, "-oForwardAgent no");
2107 addargs(&args, "-oPermitLocalCommand no");
2108 addargs(&args, "-oClearAllForwardings yes");
2110 ll = SYSLOG_LEVEL_INFO;
2113 while ((ch = getopt(argc, argv,
2114 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2116 /* Passed through to ssh(1) */
2120 addargs(&args, "-%c", ch);
2122 /* Passed through to ssh(1) with argument */
2127 addargs(&args, "-%c", ch);
2128 addargs(&args, "%s", optarg);
2132 addargs(&args, "-%c", ch);
2135 addargs(&args, "-oPort %s", optarg);
2138 if (debug_level < 3) {
2139 addargs(&args, "-v");
2140 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2146 if (sftp_server == NULL)
2147 sftp_server = _PATH_SFTP_SERVER;
2153 copy_buffer_len = strtol(optarg, &cp, 10);
2154 if (copy_buffer_len == 0 || *cp != '\0')
2155 fatal("Invalid buffer size \"%s\"", optarg);
2159 fatal("Batch file already specified.");
2161 /* Allow "-" as stdin */
2162 if (strcmp(optarg, "-") != 0 &&
2163 (infile = fopen(optarg, "r")) == NULL)
2164 fatal("%s (%s).", strerror(errno), optarg);
2167 addargs(&args, "-obatchmode yes");
2173 sftp_direct = optarg;
2176 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2180 limit_kbps *= 1024; /* kbps */
2186 num_requests = strtol(optarg, &cp, 10);
2187 if (num_requests == 0 || *cp != '\0')
2188 fatal("Invalid number of requests \"%s\"",
2192 sftp_server = optarg;
2195 ssh_program = optarg;
2196 replacearg(&args, 0, "%s", ssh_program);
2204 if (!isatty(STDERR_FILENO))
2207 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2209 if (sftp_direct == NULL) {
2210 if (optind == argc || argc > (optind + 2))
2213 userhost = xstrdup(argv[optind]);
2214 file2 = argv[optind+1];
2216 if ((host = strrchr(userhost, '@')) == NULL)
2221 fprintf(stderr, "Missing username\n");
2224 addargs(&args, "-l");
2225 addargs(&args, "%s", userhost);
2228 if ((cp = colon(host)) != NULL) {
2233 host = cleanhostname(host);
2235 fprintf(stderr, "Missing hostname\n");
2239 addargs(&args, "-oProtocol %d", sshver);
2241 /* no subsystem if the server-spec contains a '/' */
2242 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2243 addargs(&args, "-s");
2245 addargs(&args, "--");
2246 addargs(&args, "%s", host);
2247 addargs(&args, "%s", (sftp_server != NULL ?
2248 sftp_server : "sftp"));
2250 connect_to_server(ssh_program, args.list, &in, &out);
2253 addargs(&args, "sftp-server");
2255 connect_to_server(sftp_direct, args.list, &in, &out);
2259 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2261 fatal("Couldn't initialise connection to server");
2264 if (sftp_direct == NULL)
2265 fprintf(stderr, "Connected to %s.\n", host);
2267 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2270 err = interactive_loop(conn, file1, file2);
2272 #if !defined(USE_PIPES)
2273 shutdown(in, SHUT_RDWR);
2274 shutdown(out, SHUT_RDWR);
2282 while (waitpid(sshpid, NULL, 0) == -1)
2284 fatal("Couldn't wait for ssh process: %s",
2287 exit(err == 0 ? 0 : 1);