1 /* $OpenBSD: sftp.c,v 1.116 2010/01/04 02:03:57 djm Exp $ */
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
25 #include <sys/param.h>
26 #include <sys/socket.h>
28 #ifdef HAVE_SYS_STATVFS_H
29 #include <sys/statvfs.h>
44 typedef void EditLine;
63 #include "pathnames.h"
68 #include "sftp-common.h"
69 #include "sftp-client.h"
71 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
72 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
74 /* File to read commands from */
77 /* Are we in batchfile mode? */
80 /* PID of ssh transport process */
81 static pid_t sshpid = -1;
83 /* This is set to 0 if the progressmeter is not desired. */
86 /* When this option is set, we always recursively download/upload directories */
89 /* When this option is set, the file transfers will always preserve times */
92 /* SIGINT received during command processing */
93 volatile sig_atomic_t interrupted = 0;
95 /* I wish qsort() took a separate ctx for the comparison function...*/
98 /* Context used for commandline completion */
100 struct sftp_conn *conn;
104 int remote_glob(struct sftp_conn *, const char *, int,
105 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
107 extern char *__progname;
109 /* Separators for interactive commands */
110 #define WHITESPACE " \t\r\n"
113 #define LS_LONG_VIEW 0x01 /* Full view ala ls -l */
114 #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */
115 #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */
116 #define LS_NAME_SORT 0x08 /* Sort by name (default) */
117 #define LS_TIME_SORT 0x10 /* Sort by mtime */
118 #define LS_SIZE_SORT 0x20 /* Sort by file size */
119 #define LS_REVERSE_SORT 0x40 /* Reverse sort order */
120 #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */
122 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
123 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
125 /* Commands for interactive mode */
149 #define I_PROGRESS 23
157 /* Type of completion */
162 static const struct CMD cmds[] = {
163 { "bye", I_QUIT, NOARGS },
164 { "cd", I_CHDIR, REMOTE },
165 { "chdir", I_CHDIR, REMOTE },
166 { "chgrp", I_CHGRP, REMOTE },
167 { "chmod", I_CHMOD, REMOTE },
168 { "chown", I_CHOWN, REMOTE },
169 { "df", I_DF, REMOTE },
170 { "dir", I_LS, REMOTE },
171 { "exit", I_QUIT, NOARGS },
172 { "get", I_GET, REMOTE },
173 { "help", I_HELP, NOARGS },
174 { "lcd", I_LCHDIR, LOCAL },
175 { "lchdir", I_LCHDIR, LOCAL },
176 { "lls", I_LLS, LOCAL },
177 { "lmkdir", I_LMKDIR, LOCAL },
178 { "ln", I_SYMLINK, REMOTE },
179 { "lpwd", I_LPWD, LOCAL },
180 { "ls", I_LS, REMOTE },
181 { "lumask", I_LUMASK, NOARGS },
182 { "mkdir", I_MKDIR, REMOTE },
183 { "progress", I_PROGRESS, NOARGS },
184 { "put", I_PUT, LOCAL },
185 { "pwd", I_PWD, REMOTE },
186 { "quit", I_QUIT, NOARGS },
187 { "rename", I_RENAME, REMOTE },
188 { "rm", I_RM, REMOTE },
189 { "rmdir", I_RMDIR, REMOTE },
190 { "symlink", I_SYMLINK, REMOTE },
191 { "version", I_VERSION, NOARGS },
192 { "!", I_SHELL, NOARGS },
193 { "?", I_HELP, NOARGS },
197 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
204 kill(sshpid, SIGTERM);
205 waitpid(sshpid, NULL, 0);
213 cmd_interrupt(int signo)
215 const char msg[] = "\rInterrupt \n";
216 int olderrno = errno;
218 write(STDERR_FILENO, msg, sizeof(msg) - 1);
226 printf("Available commands:\n"
228 "cd path Change remote directory to 'path'\n"
229 "chgrp grp path Change group of file 'path' to 'grp'\n"
230 "chmod mode path Change permissions of file 'path' to 'mode'\n"
231 "chown own path Change owner of file 'path' to 'own'\n"
232 "df [-hi] [path] Display statistics for current directory or\n"
233 " filesystem containing 'path'\n"
235 "get [-Pr] remote-path [local-path] Download file\n"
236 "help Display this help text\n"
237 "lcd path Change local directory to 'path'\n"
238 "lls [ls-options [path]] Display local directory listing\n"
239 "lmkdir path Create local directory\n"
240 "ln oldpath newpath Symlink remote file\n"
241 "lpwd Print local working directory\n"
242 "ls [-1aflnrSt] [path] Display remote directory listing\n"
243 "lumask umask Set local umask to 'umask'\n"
244 "mkdir path Create remote directory\n"
245 "progress Toggle display of progress meter\n"
246 "put [-Pr] local-path [remote-path] Upload file\n"
247 "pwd Display remote working directory\n"
249 "rename oldpath newpath Rename remote file\n"
250 "rm path Delete remote file\n"
251 "rmdir path Remove remote directory\n"
252 "symlink oldpath newpath Symlink remote file\n"
253 "version Show SFTP version\n"
254 "!command Execute 'command' in local shell\n"
255 "! Escape to local shell\n"
256 "? Synonym for help\n");
260 local_do_shell(const char *args)
269 if ((shell = getenv("SHELL")) == NULL)
270 shell = _PATH_BSHELL;
272 if ((pid = fork()) == -1)
273 fatal("Couldn't fork: %s", strerror(errno));
276 /* XXX: child has pipe fds to ssh subproc open - issue? */
278 debug3("Executing %s -c \"%s\"", shell, args);
279 execl(shell, shell, "-c", args, (char *)NULL);
281 debug3("Executing %s", shell);
282 execl(shell, shell, (char *)NULL);
284 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
288 while (waitpid(pid, &status, 0) == -1)
290 fatal("Couldn't wait for child: %s", strerror(errno));
291 if (!WIFEXITED(status))
292 error("Shell exited abnormally");
293 else if (WEXITSTATUS(status))
294 error("Shell exited with status %d", WEXITSTATUS(status));
298 local_do_ls(const char *args)
301 local_do_shell(_PATH_LS);
303 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
304 char *buf = xmalloc(len);
306 /* XXX: quoting - rip quoting code from ftp? */
307 snprintf(buf, len, _PATH_LS " %s", args);
313 /* Strip one path (usually the pwd) from the start of another */
315 path_strip(char *path, char *strip)
320 return (xstrdup(path));
323 if (strncmp(path, strip, len) == 0) {
324 if (strip[len - 1] != '/' && path[len] == '/')
326 return (xstrdup(path + len));
329 return (xstrdup(path));
333 make_absolute(char *p, char *pwd)
338 if (p && p[0] != '/') {
339 abs_str = path_append(pwd, p);
347 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
350 extern int opterr, optind, optopt, optreset;
353 optind = optreset = 1;
357 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
368 error("%s: Invalid flag -%c", cmd, optopt);
377 parse_ls_flags(char **argv, int argc, int *lflag)
379 extern int opterr, optind, optopt, optreset;
382 optind = optreset = 1;
385 *lflag = LS_NAME_SORT;
386 while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) {
389 *lflag &= ~VIEW_FLAGS;
390 *lflag |= LS_SHORT_VIEW;
393 *lflag &= ~SORT_FLAGS;
394 *lflag |= LS_SIZE_SORT;
397 *lflag |= LS_SHOW_ALL;
400 *lflag &= ~SORT_FLAGS;
403 *lflag &= ~VIEW_FLAGS;
404 *lflag |= LS_LONG_VIEW;
407 *lflag &= ~VIEW_FLAGS;
408 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
411 *lflag |= LS_REVERSE_SORT;
414 *lflag &= ~SORT_FLAGS;
415 *lflag |= LS_TIME_SORT;
418 error("ls: Invalid flag -%c", optopt);
427 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
429 extern int opterr, optind, optopt, optreset;
432 optind = optreset = 1;
436 while ((ch = getopt(argc, argv, "hi")) != -1) {
445 error("%s: Invalid flag -%c", cmd, optopt);
458 /* XXX: report errors? */
459 if (stat(path, &sb) == -1)
462 return(S_ISDIR(sb.st_mode));
466 remote_is_dir(struct sftp_conn *conn, char *path)
470 /* XXX: report errors? */
471 if ((a = do_stat(conn, path, 1)) == NULL)
473 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
475 return(S_ISDIR(a->perm));
478 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
480 pathname_is_dir(char *pathname)
482 size_t l = strlen(pathname);
484 return l > 0 && pathname[l - 1] == '/';
488 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
489 int pflag, int rflag)
491 char *abs_src = NULL;
492 char *abs_dst = NULL;
494 char *filename, *tmp=NULL;
497 abs_src = xstrdup(src);
498 abs_src = make_absolute(abs_src, pwd);
499 memset(&g, 0, sizeof(g));
501 debug3("Looking up %s", abs_src);
502 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
503 error("File \"%s\" not found.", abs_src);
509 * If multiple matches then dst must be a directory or
512 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
513 error("Multiple source paths, but destination "
514 "\"%s\" is not a directory", dst);
519 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
520 tmp = xstrdup(g.gl_pathv[i]);
521 if ((filename = basename(tmp)) == NULL) {
522 error("basename %s: %s", tmp, strerror(errno));
528 if (g.gl_matchc == 1 && dst) {
530 abs_dst = path_append(dst, filename);
532 abs_dst = xstrdup(dst);
535 abs_dst = path_append(dst, filename);
537 abs_dst = xstrdup(filename);
541 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
542 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
543 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
544 pflag || global_pflag, 1) == -1)
547 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
548 pflag || global_pflag) == -1)
562 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
563 int pflag, int rflag)
565 char *tmp_dst = NULL;
566 char *abs_dst = NULL;
567 char *tmp = NULL, *filename = NULL;
570 int i, dst_is_dir = 1;
574 tmp_dst = xstrdup(dst);
575 tmp_dst = make_absolute(tmp_dst, pwd);
578 memset(&g, 0, sizeof(g));
579 debug3("Looking up %s", src);
580 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
581 error("File \"%s\" not found.", src);
586 /* If we aren't fetching to pwd then stash this status for later */
588 dst_is_dir = remote_is_dir(conn, tmp_dst);
590 /* If multiple matches, dst may be directory or unspecified */
591 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
592 error("Multiple paths match, but destination "
593 "\"%s\" is not a directory", tmp_dst);
598 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
599 if (stat(g.gl_pathv[i], &sb) == -1) {
601 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
605 tmp = xstrdup(g.gl_pathv[i]);
606 if ((filename = basename(tmp)) == NULL) {
607 error("basename %s: %s", tmp, strerror(errno));
613 if (g.gl_matchc == 1 && tmp_dst) {
614 /* If directory specified, append filename */
616 abs_dst = path_append(tmp_dst, filename);
618 abs_dst = xstrdup(tmp_dst);
619 } else if (tmp_dst) {
620 abs_dst = path_append(tmp_dst, filename);
622 abs_dst = make_absolute(xstrdup(filename), pwd);
626 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
627 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
628 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
629 pflag || global_pflag, 1) == -1)
632 if (do_upload(conn, g.gl_pathv[i], abs_dst,
633 pflag || global_pflag) == -1)
648 sdirent_comp(const void *aa, const void *bb)
650 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
651 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
652 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
654 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
655 if (sort_flag & LS_NAME_SORT)
656 return (rmul * strcmp(a->filename, b->filename));
657 else if (sort_flag & LS_TIME_SORT)
658 return (rmul * NCMP(a->a.mtime, b->a.mtime));
659 else if (sort_flag & LS_SIZE_SORT)
660 return (rmul * NCMP(a->a.size, b->a.size));
662 fatal("Unknown ls sort type");
665 /* sftp ls.1 replacement for directories */
667 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
670 u_int c = 1, colspace = 0, columns = 1;
673 if ((n = do_readdir(conn, path, &d)) != 0)
676 if (!(lflag & LS_SHORT_VIEW)) {
677 u_int m = 0, width = 80;
681 /* Count entries for sort and find longest filename */
682 for (n = 0; d[n] != NULL; n++) {
683 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
684 m = MAX(m, strlen(d[n]->filename));
687 /* Add any subpath that also needs to be counted */
688 tmp = path_strip(path, strip_path);
692 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
695 columns = width / (m + 2);
696 columns = MAX(columns, 1);
697 colspace = width / columns;
698 colspace = MIN(colspace, width);
701 if (lflag & SORT_FLAGS) {
702 for (n = 0; d[n] != NULL; n++)
703 ; /* count entries */
704 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
705 qsort(d, n, sizeof(*d), sdirent_comp);
708 for (n = 0; d[n] != NULL && !interrupted; n++) {
711 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
714 tmp = path_append(path, d[n]->filename);
715 fname = path_strip(tmp, strip_path);
718 if (lflag & LS_LONG_VIEW) {
719 if (lflag & LS_NUMERIC_VIEW) {
723 memset(&sb, 0, sizeof(sb));
724 attrib_to_stat(&d[n]->a, &sb);
725 lname = ls_file(fname, &sb, 1);
726 printf("%s\n", lname);
729 printf("%s\n", d[n]->longname);
731 printf("%-*s", colspace, fname);
742 if (!(lflag & LS_LONG_VIEW) && (c != 1))
745 free_sftp_dirents(d);
749 /* sftp ls.1 replacement which handles path globs */
751 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
755 u_int i, c = 1, colspace = 0, columns = 1;
758 memset(&g, 0, sizeof(g));
760 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
761 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
764 error("Can't ls: \"%s\" not found", path);
772 * If the glob returns a single match and it is a directory,
773 * then just list its contents.
775 if (g.gl_matchc == 1) {
776 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
780 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
784 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
790 if (!(lflag & LS_SHORT_VIEW)) {
791 u_int m = 0, width = 80;
794 /* Count entries for sort and find longest filename */
795 for (i = 0; g.gl_pathv[i]; i++)
796 m = MAX(m, strlen(g.gl_pathv[i]));
798 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
801 columns = width / (m + 2);
802 columns = MAX(columns, 1);
803 colspace = width / columns;
806 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
809 fname = path_strip(g.gl_pathv[i], strip_path);
811 if (lflag & LS_LONG_VIEW) {
816 * XXX: this is slow - 1 roundtrip per path
817 * A solution to this is to fork glob() and
818 * build a sftp specific version which keeps the
819 * attribs (which currently get thrown away)
820 * that the server returns as well as the filenames.
822 memset(&sb, 0, sizeof(sb));
824 a = do_lstat(conn, g.gl_pathv[i], 1);
826 attrib_to_stat(a, &sb);
827 lname = ls_file(fname, &sb, 1);
828 printf("%s\n", lname);
831 printf("%-*s", colspace, fname);
841 if (!(lflag & LS_LONG_VIEW) && (c != 1))
852 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
854 struct sftp_statvfs st;
855 char s_used[FMT_SCALED_STRSIZE];
856 char s_avail[FMT_SCALED_STRSIZE];
857 char s_root[FMT_SCALED_STRSIZE];
858 char s_total[FMT_SCALED_STRSIZE];
859 unsigned long long ffree;
861 if (do_statvfs(conn, path, &st, 1) == -1)
864 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
865 printf(" Inodes Used Avail "
866 "(root) %%Capacity\n");
867 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
868 (unsigned long long)st.f_files,
869 (unsigned long long)(st.f_files - st.f_ffree),
870 (unsigned long long)st.f_favail,
871 (unsigned long long)st.f_ffree, ffree);
873 strlcpy(s_used, "error", sizeof(s_used));
874 strlcpy(s_avail, "error", sizeof(s_avail));
875 strlcpy(s_root, "error", sizeof(s_root));
876 strlcpy(s_total, "error", sizeof(s_total));
877 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
878 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
879 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
880 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
881 printf(" Size Used Avail (root) %%Capacity\n");
882 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
883 s_total, s_used, s_avail, s_root,
884 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
887 printf(" Size Used Avail "
888 "(root) %%Capacity\n");
889 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
890 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
891 (unsigned long long)(st.f_frsize *
892 (st.f_blocks - st.f_bfree) / 1024),
893 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
894 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
895 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
902 * Undo escaping of glob sequences in place. Used to undo extra escaping
903 * applied in makeargv() when the string is destined for a function that
907 undo_glob_escape(char *s)
942 * Split a string into an argument vector using sh(1)-style quoting,
943 * comment and escaping rules, but with some tweaks to handle glob(3)
945 * The "sloppy" flag allows for recovery from missing terminating quote, for
946 * use in parsing incomplete commandlines during tab autocompletion.
948 * Returns NULL on error or a NULL-terminated array of arguments.
950 * If "lastquote" is not NULL, the quoting character used for the last
951 * argument is placed in *lastquote ("\0", "'" or "\"").
953 * If "terminated" is not NULL, *terminated will be set to 1 when the
954 * last argument's quote has been properly terminated or 0 otherwise.
955 * This parameter is only of use if "sloppy" is set.
958 #define MAXARGLEN 8192
960 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
965 static char argvs[MAXARGLEN];
966 static char *argv[MAXARGS + 1];
967 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
970 if (strlen(arg) > sizeof(argvs) - 1) {
972 error("string too long");
975 if (terminated != NULL)
977 if (lastquote != NULL)
982 if (isspace(arg[i])) {
983 if (state == MA_UNQUOTED) {
984 /* Terminate current argument */
988 } else if (state != MA_START)
990 } else if (arg[i] == '"' || arg[i] == '\'') {
991 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
992 if (state == MA_START) {
993 argv[argc] = argvs + j;
995 if (lastquote != NULL)
997 } else if (state == MA_UNQUOTED)
1000 state = MA_UNQUOTED;
1002 argvs[j++] = arg[i];
1003 } else if (arg[i] == '\\') {
1004 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1005 quot = state == MA_SQUOTE ? '\'' : '"';
1006 /* Unescape quote we are in */
1007 /* XXX support \n and friends? */
1008 if (arg[i + 1] == quot) {
1010 argvs[j++] = arg[i];
1011 } else if (arg[i + 1] == '?' ||
1012 arg[i + 1] == '[' || arg[i + 1] == '*') {
1014 * Special case for sftp: append
1015 * double-escaped glob sequence -
1016 * glob will undo one level of
1017 * escaping. NB. string can grow here.
1019 if (j >= sizeof(argvs) - 5)
1020 goto args_too_longs;
1022 argvs[j++] = arg[i++];
1024 argvs[j++] = arg[i];
1026 argvs[j++] = arg[i++];
1027 argvs[j++] = arg[i];
1030 if (state == MA_START) {
1031 argv[argc] = argvs + j;
1032 state = MA_UNQUOTED;
1033 if (lastquote != NULL)
1036 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1037 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1039 * Special case for sftp: append
1040 * escaped glob sequence -
1041 * glob will undo one level of
1044 argvs[j++] = arg[i++];
1045 argvs[j++] = arg[i];
1047 /* Unescape everything */
1048 /* XXX support \n and friends? */
1050 argvs[j++] = arg[i];
1053 } else if (arg[i] == '#') {
1054 if (state == MA_SQUOTE || state == MA_DQUOTE)
1055 argvs[j++] = arg[i];
1058 } else if (arg[i] == '\0') {
1059 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1061 state = MA_UNQUOTED;
1062 if (terminated != NULL)
1066 error("Unterminated quoted argument");
1070 if (state == MA_UNQUOTED) {
1076 if (state == MA_START) {
1077 argv[argc] = argvs + j;
1078 state = MA_UNQUOTED;
1079 if (lastquote != NULL)
1082 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1083 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1085 * Special case for sftp: escape quoted
1086 * glob(3) wildcards. NB. string can grow
1089 if (j >= sizeof(argvs) - 3)
1090 goto args_too_longs;
1092 argvs[j++] = arg[i];
1094 argvs[j++] = arg[i];
1103 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1104 int *hflag, unsigned long *n_arg, char **path1, char **path2)
1106 const char *cmd, *cp = *cpp;
1110 int i, cmdnum, optidx, argc;
1112 /* Skip leading whitespace */
1113 cp = cp + strspn(cp, WHITESPACE);
1115 /* Ignore blank lines and lines which begin with comment '#' char */
1116 if (*cp == '\0' || *cp == '#')
1119 /* Check for leading '-' (disable error processing) */
1126 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1129 /* Figure out which command we have */
1130 for (i = 0; cmds[i].c != NULL; i++) {
1131 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1141 } else if (cmdnum == -1) {
1142 error("Invalid command.");
1146 /* Get arguments and parse flags */
1147 *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1148 *path1 = *path2 = NULL;
1153 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
1155 /* Get first pathname (mandatory) */
1156 if (argc - optidx < 1) {
1157 error("You must specify at least one path after a "
1158 "%s command.", cmd);
1161 *path1 = xstrdup(argv[optidx]);
1162 /* Get second pathname (optional) */
1163 if (argc - optidx > 1) {
1164 *path2 = xstrdup(argv[optidx + 1]);
1165 /* Destination is not globbed */
1166 undo_glob_escape(*path2);
1171 if (argc - optidx < 2) {
1172 error("You must specify two paths after a %s "
1176 *path1 = xstrdup(argv[optidx]);
1177 *path2 = xstrdup(argv[optidx + 1]);
1178 /* Paths are not globbed */
1179 undo_glob_escape(*path1);
1180 undo_glob_escape(*path2);
1188 /* Get pathname (mandatory) */
1189 if (argc - optidx < 1) {
1190 error("You must specify a path after a %s command.",
1194 *path1 = xstrdup(argv[optidx]);
1195 /* Only "rm" globs */
1197 undo_glob_escape(*path1);
1200 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1203 /* Default to current directory if no path specified */
1204 if (argc - optidx < 1)
1207 *path1 = xstrdup(argv[optidx]);
1208 undo_glob_escape(*path1);
1212 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1214 /* Path is optional */
1215 if (argc - optidx > 0)
1216 *path1 = xstrdup(argv[optidx]);
1219 /* Skip ls command and following whitespace */
1220 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1222 /* Uses the rest of the line */
1229 /* Get numeric arg (mandatory) */
1230 if (argc - optidx < 1)
1233 l = strtol(argv[optidx], &cp2, base);
1234 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1235 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1238 error("You must supply a numeric argument "
1239 "to the %s command.", cmd);
1243 if (cmdnum == I_LUMASK)
1245 /* Get pathname (mandatory) */
1246 if (argc - optidx < 2) {
1247 error("You must specify a path after a %s command.",
1251 *path1 = xstrdup(argv[optidx + 1]);
1261 fatal("Command not implemented");
1269 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1272 char *path1, *path2, *tmp;
1273 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1274 unsigned long n_arg = 0;
1276 char path_buf[MAXPATHLEN];
1280 path1 = path2 = NULL;
1281 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1287 memset(&g, 0, sizeof(g));
1289 /* Perform command */
1295 /* Unrecognized command */
1299 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1302 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1305 path1 = make_absolute(path1, *pwd);
1306 path2 = make_absolute(path2, *pwd);
1307 err = do_rename(conn, path1, path2);
1310 path2 = make_absolute(path2, *pwd);
1311 err = do_symlink(conn, path1, path2);
1314 path1 = make_absolute(path1, *pwd);
1315 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1316 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1317 printf("Removing %s\n", g.gl_pathv[i]);
1318 err = do_rm(conn, g.gl_pathv[i]);
1319 if (err != 0 && err_abort)
1324 path1 = make_absolute(path1, *pwd);
1326 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1328 err = do_mkdir(conn, path1, &a, 1);
1331 path1 = make_absolute(path1, *pwd);
1332 err = do_rmdir(conn, path1);
1335 path1 = make_absolute(path1, *pwd);
1336 if ((tmp = do_realpath(conn, path1)) == NULL) {
1340 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1345 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1346 error("Can't change directory: Can't check target");
1351 if (!S_ISDIR(aa->perm)) {
1352 error("Can't change directory: \"%s\" is not "
1353 "a directory", tmp);
1363 do_globbed_ls(conn, *pwd, *pwd, lflag);
1367 /* Strip pwd off beginning of non-absolute paths */
1372 path1 = make_absolute(path1, *pwd);
1373 err = do_globbed_ls(conn, path1, tmp, lflag);
1376 /* Default to current directory if no path specified */
1378 path1 = xstrdup(*pwd);
1379 path1 = make_absolute(path1, *pwd);
1380 err = do_df(conn, path1, hflag, iflag);
1383 if (chdir(path1) == -1) {
1384 error("Couldn't change local directory to "
1385 "\"%s\": %s", path1, strerror(errno));
1390 if (mkdir(path1, 0777) == -1) {
1391 error("Couldn't create local directory "
1392 "\"%s\": %s", path1, strerror(errno));
1400 local_do_shell(cmd);
1404 printf("Local umask: %03lo\n", n_arg);
1407 path1 = make_absolute(path1, *pwd);
1409 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1411 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1412 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1413 printf("Changing mode on %s\n", g.gl_pathv[i]);
1414 err = do_setstat(conn, g.gl_pathv[i], &a);
1415 if (err != 0 && err_abort)
1421 path1 = make_absolute(path1, *pwd);
1422 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1423 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1424 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1431 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1432 error("Can't get current ownership of "
1433 "remote file \"%s\"", g.gl_pathv[i]);
1440 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1441 if (cmdnum == I_CHOWN) {
1442 printf("Changing owner on %s\n", g.gl_pathv[i]);
1445 printf("Changing group on %s\n", g.gl_pathv[i]);
1448 err = do_setstat(conn, g.gl_pathv[i], aa);
1449 if (err != 0 && err_abort)
1454 printf("Remote working directory: %s\n", *pwd);
1457 if (!getcwd(path_buf, sizeof(path_buf))) {
1458 error("Couldn't get local cwd: %s", strerror(errno));
1462 printf("Local working directory: %s\n", path_buf);
1465 /* Processed below */
1471 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1474 showprogress = !showprogress;
1476 printf("Progress meter enabled\n");
1478 printf("Progress meter disabled\n");
1481 fatal("%d is not implemented", cmdnum);
1491 /* If an unignored error occurs in batch mode we should abort. */
1492 if (err_abort && err != 0)
1494 else if (cmdnum == I_QUIT)
1502 prompt(EditLine *el)
1507 /* Display entries in 'list' after skipping the first 'len' chars */
1509 complete_display(char **list, u_int len)
1511 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1515 /* Count entries for sort and find longest */
1516 for (y = 0; list[y]; y++)
1517 m = MAX(m, strlen(list[y]));
1519 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1522 m = m > len ? m - len : 0;
1523 columns = width / (m + 2);
1524 columns = MAX(columns, 1);
1525 colspace = width / columns;
1526 colspace = MIN(colspace, width);
1530 for (y = 0; list[y]; y++) {
1531 llen = strlen(list[y]);
1532 tmp = llen > len ? list[y] + len : "";
1533 printf("%-*s", colspace, tmp);
1544 * Given a "list" of words that begin with a common prefix of "word",
1545 * attempt to find an autocompletion to extends "word" by the next
1546 * characters common to all entries in "list".
1549 complete_ambiguous(const char *word, char **list, size_t count)
1555 u_int y, matchlen = strlen(list[0]);
1557 /* Find length of common stem */
1558 for (y = 1; list[y]; y++) {
1561 for (x = 0; x < matchlen; x++)
1562 if (list[0][x] != list[y][x])
1568 if (matchlen > strlen(word)) {
1569 char *tmp = xstrdup(list[0]);
1571 tmp[matchlen] = NULL;
1576 return xstrdup(word);
1579 /* Autocomplete a sftp command */
1581 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1584 u_int y, count = 0, cmdlen, tmplen;
1585 char *tmp, **list, argterm[3];
1588 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1590 /* No command specified: display all available commands */
1592 for (y = 0; cmds[y].c; y++)
1593 list[count++] = xstrdup(cmds[y].c);
1596 complete_display(list, 0);
1598 for (y = 0; list[y] != NULL; y++)
1604 /* Prepare subset of commands that start with "cmd" */
1605 cmdlen = strlen(cmd);
1606 for (y = 0; cmds[y].c; y++) {
1607 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1608 list[count++] = xstrdup(cmds[y].c);
1615 /* Complete ambigious command */
1616 tmp = complete_ambiguous(cmd, list, count);
1618 complete_display(list, 0);
1620 for (y = 0; list[y]; y++)
1625 tmplen = strlen(tmp);
1626 cmdlen = strlen(cmd);
1627 /* If cmd may be extended then do so */
1628 if (tmplen > cmdlen)
1629 if (el_insertstr(el, tmp + cmdlen) == -1)
1630 fatal("el_insertstr failed.");
1632 /* Terminate argument cleanly */
1636 argterm[y++] = quote;
1637 if (lastarg || *(lf->cursor) != ' ')
1640 if (y > 0 && el_insertstr(el, argterm) == -1)
1641 fatal("el_insertstr failed.");
1651 * Determine whether a particular sftp command's arguments (if any)
1652 * represent local or remote files.
1655 complete_is_remote(char *cmd) {
1661 for (i = 0; cmds[i].c; i++) {
1662 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1670 /* Autocomplete a filename "file" */
1672 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1673 char *file, int remote, int lastarg, char quote, int terminated)
1676 char *tmp, *tmp2, ins[3];
1677 u_int i, hadglob, pwdlen, len, tmplen, filelen;
1680 /* Glob from "file" location */
1684 xasprintf(&tmp, "%s*", file);
1686 memset(&g, 0, sizeof(g));
1687 if (remote != LOCAL) {
1688 tmp = make_absolute(tmp, remote_path);
1689 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1691 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1693 /* Determine length of pwd so we can trim completion display */
1694 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1695 /* Terminate counting on first unescaped glob metacharacter */
1696 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1697 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1701 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1703 if (tmp[tmplen] == '/')
1704 pwdlen = tmplen + 1; /* track last seen '/' */
1708 if (g.gl_matchc == 0)
1711 if (g.gl_matchc > 1)
1712 complete_display(g.gl_pathv, pwdlen);
1715 /* Don't try to extend globs */
1716 if (file == NULL || hadglob)
1719 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1720 tmp = path_strip(tmp2, remote_path);
1726 tmplen = strlen(tmp);
1727 filelen = strlen(file);
1729 if (tmplen > filelen) {
1730 tmp2 = tmp + filelen;
1732 /* quote argument on way out */
1733 for (i = 0; i < len; i++) {
1743 if (quote == '\0' || tmp2[i] == quote) {
1744 if (el_insertstr(el, ins) == -1)
1745 fatal("el_insertstr "
1751 if (el_insertstr(el, ins + 1) == -1)
1752 fatal("el_insertstr failed.");
1760 * XXX should we really extend here? the user may not be done if
1761 * the filename is a directory.
1763 if (g.gl_matchc == 1) {
1767 if (lastarg || *(lf->cursor) != ' ')
1770 if (i > 0 && el_insertstr(el, ins) == -1)
1771 fatal("el_insertstr failed.");
1780 /* tab-completion hook function, called via libedit */
1781 static unsigned char
1782 complete(EditLine *el, int ch)
1784 char **argv, *line, quote;
1785 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1787 struct complete_ctx *complete_ctx;
1790 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1791 fatal("%s: el_get failed", __func__);
1793 /* Figure out which argument the cursor points to */
1794 cursor = lf->cursor - lf->buffer;
1795 line = (char *)xmalloc(cursor + 1);
1796 memcpy(line, lf->buffer, cursor);
1797 line[cursor] = '\0';
1798 argv = makeargv(line, &carg, 1, "e, &terminated);
1801 /* Get all the arguments on the line */
1802 len = lf->lastchar - lf->buffer;
1803 line = (char *)xmalloc(len + 1);
1804 memcpy(line, lf->buffer, len);
1806 argv = makeargv(line, &argc, 1, NULL, NULL);
1808 /* Ensure cursor is at EOL or a argument boundary */
1809 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1810 line[cursor] != '\n') {
1816 /* Show all available commands */
1817 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1819 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1820 /* Handle the command parsing */
1821 if (complete_cmd_parse(el, argv[0], argc == carg,
1822 quote, terminated) != 0)
1824 } else if (carg >= 1) {
1825 /* Handle file parsing */
1826 int remote = complete_is_remote(argv[0]);
1827 char *filematch = NULL;
1829 if (carg > 1 && line[cursor-1] != ' ')
1830 filematch = argv[carg - 1];
1833 complete_match(el, complete_ctx->conn,
1834 *complete_ctx->remote_pathp, filematch,
1835 remote, carg == argc, quote, terminated) != 0)
1842 #endif /* USE_LIBEDIT */
1845 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1850 int err, interactive;
1851 EditLine *el = NULL;
1855 extern char *__progname;
1856 struct complete_ctx complete_ctx;
1858 if (!batchmode && isatty(STDIN_FILENO)) {
1859 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1860 fatal("Couldn't initialise editline");
1861 if ((hl = history_init()) == NULL)
1862 fatal("Couldn't initialise editline history");
1863 history(hl, &hev, H_SETSIZE, 100);
1864 el_set(el, EL_HIST, history, hl);
1866 el_set(el, EL_PROMPT, prompt);
1867 el_set(el, EL_EDITOR, "emacs");
1868 el_set(el, EL_TERMINAL, NULL);
1869 el_set(el, EL_SIGNAL, 1);
1870 el_source(el, NULL);
1872 /* Tab Completion */
1873 el_set(el, EL_ADDFN, "ftp-complete",
1874 "Context senstive argument completion", complete);
1875 complete_ctx.conn = conn;
1876 complete_ctx.remote_pathp = &remote_path;
1877 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1878 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1880 #endif /* USE_LIBEDIT */
1882 remote_path = do_realpath(conn, ".");
1883 if (remote_path == NULL)
1886 if (file1 != NULL) {
1887 dir = xstrdup(file1);
1888 dir = make_absolute(dir, remote_path);
1890 if (remote_is_dir(conn, dir) && file2 == NULL) {
1891 printf("Changing to: %s\n", dir);
1892 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1893 if (parse_dispatch_command(conn, cmd,
1894 &remote_path, 1) != 0) {
1902 snprintf(cmd, sizeof cmd, "get %s", dir);
1904 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1907 err = parse_dispatch_command(conn, cmd,
1917 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1918 setvbuf(stdout, NULL, _IOLBF, 0);
1919 setvbuf(infile, NULL, _IOLBF, 0);
1925 interactive = !batchmode && isatty(STDIN_FILENO);
1930 signal(SIGINT, SIG_IGN);
1935 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1940 if (!interactive) { /* Echo command */
1941 printf("sftp> %s", cmd);
1942 if (strlen(cmd) > 0 &&
1943 cmd[strlen(cmd) - 1] != '\n')
1951 if ((line = el_gets(el, &count)) == NULL ||
1956 history(hl, &hev, H_ENTER, line);
1957 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1958 fprintf(stderr, "Error: input line too long\n");
1961 #endif /* USE_LIBEDIT */
1964 cp = strrchr(cmd, '\n');
1968 /* Handle user interrupts gracefully during commands */
1970 signal(SIGINT, cmd_interrupt);
1972 err = parse_dispatch_command(conn, cmd, &remote_path,
1983 #endif /* USE_LIBEDIT */
1985 /* err == 1 signifies normal "quit" exit */
1986 return (err >= 0 ? 0 : -1);
1990 connect_to_server(char *path, char **args, int *in, int *out)
1995 int pin[2], pout[2];
1997 if ((pipe(pin) == -1) || (pipe(pout) == -1))
1998 fatal("pipe: %s", strerror(errno));
2003 #else /* USE_PIPES */
2006 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2007 fatal("socketpair: %s", strerror(errno));
2008 *in = *out = inout[0];
2009 c_in = c_out = inout[1];
2010 #endif /* USE_PIPES */
2012 if ((sshpid = fork()) == -1)
2013 fatal("fork: %s", strerror(errno));
2014 else if (sshpid == 0) {
2015 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2016 (dup2(c_out, STDOUT_FILENO) == -1)) {
2017 fprintf(stderr, "dup2: %s\n", strerror(errno));
2026 * The underlying ssh is in the same process group, so we must
2027 * ignore SIGINT if we want to gracefully abort commands,
2028 * otherwise the signal will make it to the ssh process and
2031 signal(SIGINT, SIG_IGN);
2033 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2037 signal(SIGTERM, killchild);
2038 signal(SIGINT, killchild);
2039 signal(SIGHUP, killchild);
2047 extern char *__progname;
2050 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2051 " [-D sftp_server_path] [-F ssh_config] "
2052 "[-i identity_file]\n"
2053 " [-o ssh_option] [-P port] [-R num_requests] "
2055 " [-s subsystem | sftp_server] host\n"
2056 " %s [user@]host[:file ...]\n"
2057 " %s [user@]host[:dir[/]]\n"
2058 " %s -b batchfile [user@]host\n",
2059 __progname, __progname, __progname, __progname);
2064 main(int argc, char **argv)
2066 int in, out, ch, err;
2067 char *host, *userhost, *cp, *file2 = NULL;
2068 int debug_level = 0, sshver = 2;
2069 char *file1 = NULL, *sftp_server = NULL;
2070 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2071 LogLevel ll = SYSLOG_LEVEL_INFO;
2074 extern char *optarg;
2075 struct sftp_conn *conn;
2076 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2077 size_t num_requests = DEFAULT_NUM_REQUESTS;
2079 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2082 __progname = ssh_get_progname(argv[0]);
2083 memset(&args, '\0', sizeof(args));
2085 addargs(&args, "%s", ssh_program);
2086 addargs(&args, "-oForwardX11 no");
2087 addargs(&args, "-oForwardAgent no");
2088 addargs(&args, "-oPermitLocalCommand no");
2089 addargs(&args, "-oClearAllForwardings yes");
2091 ll = SYSLOG_LEVEL_INFO;
2094 while ((ch = getopt(argc, argv,
2095 "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
2097 /* Passed through to ssh(1) */
2101 addargs(&args, "-%c", ch);
2103 /* Passed through to ssh(1) with argument */
2108 addargs(&args, "-%c", ch);
2109 addargs(&args, "%s", optarg);
2113 addargs(&args, "-%c", ch);
2116 addargs(&args, "-oPort %s", optarg);
2119 if (debug_level < 3) {
2120 addargs(&args, "-v");
2121 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2127 if (sftp_server == NULL)
2128 sftp_server = _PATH_SFTP_SERVER;
2134 copy_buffer_len = strtol(optarg, &cp, 10);
2135 if (copy_buffer_len == 0 || *cp != '\0')
2136 fatal("Invalid buffer size \"%s\"", optarg);
2140 fatal("Batch file already specified.");
2142 /* Allow "-" as stdin */
2143 if (strcmp(optarg, "-") != 0 &&
2144 (infile = fopen(optarg, "r")) == NULL)
2145 fatal("%s (%s).", strerror(errno), optarg);
2148 addargs(&args, "-obatchmode yes");
2154 sftp_direct = optarg;
2160 num_requests = strtol(optarg, &cp, 10);
2161 if (num_requests == 0 || *cp != '\0')
2162 fatal("Invalid number of requests \"%s\"",
2166 sftp_server = optarg;
2169 ssh_program = optarg;
2170 replacearg(&args, 0, "%s", ssh_program);
2178 if (!isatty(STDERR_FILENO))
2181 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2183 if (sftp_direct == NULL) {
2184 if (optind == argc || argc > (optind + 2))
2187 userhost = xstrdup(argv[optind]);
2188 file2 = argv[optind+1];
2190 if ((host = strrchr(userhost, '@')) == NULL)
2195 fprintf(stderr, "Missing username\n");
2198 addargs(&args, "-l");
2199 addargs(&args, "%s", userhost);
2202 if ((cp = colon(host)) != NULL) {
2207 host = cleanhostname(host);
2209 fprintf(stderr, "Missing hostname\n");
2213 addargs(&args, "-oProtocol %d", sshver);
2215 /* no subsystem if the server-spec contains a '/' */
2216 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2217 addargs(&args, "-s");
2219 addargs(&args, "--");
2220 addargs(&args, "%s", host);
2221 addargs(&args, "%s", (sftp_server != NULL ?
2222 sftp_server : "sftp"));
2224 connect_to_server(ssh_program, args.list, &in, &out);
2227 addargs(&args, "sftp-server");
2229 connect_to_server(sftp_direct, args.list, &in, &out);
2233 conn = do_init(in, out, copy_buffer_len, num_requests);
2235 fatal("Couldn't initialise connection to server");
2238 if (sftp_direct == NULL)
2239 fprintf(stderr, "Connected to %s.\n", host);
2241 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2244 err = interactive_loop(conn, file1, file2);
2246 #if !defined(USE_PIPES)
2247 shutdown(in, SHUT_RDWR);
2248 shutdown(out, SHUT_RDWR);
2256 while (waitpid(sshpid, NULL, 0) == -1)
2258 fatal("Couldn't wait for ssh process: %s",
2261 exit(err == 0 ? 0 : 1);