1 /* $OpenBSD: sftp.c,v 1.123 2010/01/27 19:21:39 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 */
151 #define I_PROGRESS 23
159 /* Type of completion */
164 static const struct CMD cmds[] = {
165 { "bye", I_QUIT, NOARGS },
166 { "cd", I_CHDIR, REMOTE },
167 { "chdir", I_CHDIR, REMOTE },
168 { "chgrp", I_CHGRP, REMOTE },
169 { "chmod", I_CHMOD, REMOTE },
170 { "chown", I_CHOWN, REMOTE },
171 { "df", I_DF, REMOTE },
172 { "dir", I_LS, REMOTE },
173 { "exit", I_QUIT, NOARGS },
174 { "get", I_GET, REMOTE },
175 { "help", I_HELP, NOARGS },
176 { "lcd", I_LCHDIR, LOCAL },
177 { "lchdir", I_LCHDIR, LOCAL },
178 { "lls", I_LLS, LOCAL },
179 { "lmkdir", I_LMKDIR, LOCAL },
180 { "ln", I_SYMLINK, REMOTE },
181 { "lpwd", I_LPWD, LOCAL },
182 { "ls", I_LS, REMOTE },
183 { "lumask", I_LUMASK, NOARGS },
184 { "mkdir", I_MKDIR, REMOTE },
185 { "progress", I_PROGRESS, NOARGS },
186 { "put", I_PUT, LOCAL },
187 { "pwd", I_PWD, REMOTE },
188 { "quit", I_QUIT, NOARGS },
189 { "rename", I_RENAME, REMOTE },
190 { "rm", I_RM, REMOTE },
191 { "rmdir", I_RMDIR, REMOTE },
192 { "symlink", I_SYMLINK, REMOTE },
193 { "version", I_VERSION, NOARGS },
194 { "!", I_SHELL, NOARGS },
195 { "?", I_HELP, NOARGS },
199 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
206 kill(sshpid, SIGTERM);
207 waitpid(sshpid, NULL, 0);
215 cmd_interrupt(int signo)
217 const char msg[] = "\rInterrupt \n";
218 int olderrno = errno;
220 write(STDERR_FILENO, msg, sizeof(msg) - 1);
228 printf("Available commands:\n"
230 "cd path Change remote directory to 'path'\n"
231 "chgrp grp path Change group of file 'path' to 'grp'\n"
232 "chmod mode path Change permissions of file 'path' to 'mode'\n"
233 "chown own path Change owner of file 'path' to 'own'\n"
234 "df [-hi] [path] Display statistics for current directory or\n"
235 " filesystem containing 'path'\n"
237 "get [-Ppr] remote [local] Download file\n"
238 "help Display this help text\n"
239 "lcd path Change local directory to 'path'\n"
240 "lls [ls-options [path]] Display local directory listing\n"
241 "lmkdir path Create local directory\n"
242 "ln oldpath newpath Symlink remote file\n"
243 "lpwd Print local working directory\n"
244 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
245 "lumask umask Set local umask to 'umask'\n"
246 "mkdir path Create remote directory\n"
247 "progress Toggle display of progress meter\n"
248 "put [-Ppr] local [remote] Upload file\n"
249 "pwd Display remote working directory\n"
251 "rename oldpath newpath Rename remote file\n"
252 "rm path Delete remote file\n"
253 "rmdir path Remove remote directory\n"
254 "symlink oldpath newpath Symlink remote file\n"
255 "version Show SFTP version\n"
256 "!command Execute 'command' in local shell\n"
257 "! Escape to local shell\n"
258 "? Synonym for help\n");
262 local_do_shell(const char *args)
271 if ((shell = getenv("SHELL")) == NULL)
272 shell = _PATH_BSHELL;
274 if ((pid = fork()) == -1)
275 fatal("Couldn't fork: %s", strerror(errno));
278 /* XXX: child has pipe fds to ssh subproc open - issue? */
280 debug3("Executing %s -c \"%s\"", shell, args);
281 execl(shell, shell, "-c", args, (char *)NULL);
283 debug3("Executing %s", shell);
284 execl(shell, shell, (char *)NULL);
286 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
290 while (waitpid(pid, &status, 0) == -1)
292 fatal("Couldn't wait for child: %s", strerror(errno));
293 if (!WIFEXITED(status))
294 error("Shell exited abnormally");
295 else if (WEXITSTATUS(status))
296 error("Shell exited with status %d", WEXITSTATUS(status));
300 local_do_ls(const char *args)
303 local_do_shell(_PATH_LS);
305 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
306 char *buf = xmalloc(len);
308 /* XXX: quoting - rip quoting code from ftp? */
309 snprintf(buf, len, _PATH_LS " %s", args);
315 /* Strip one path (usually the pwd) from the start of another */
317 path_strip(char *path, char *strip)
322 return (xstrdup(path));
325 if (strncmp(path, strip, len) == 0) {
326 if (strip[len - 1] != '/' && path[len] == '/')
328 return (xstrdup(path + len));
331 return (xstrdup(path));
335 make_absolute(char *p, char *pwd)
340 if (p && p[0] != '/') {
341 abs_str = path_append(pwd, p);
349 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
352 extern int opterr, optind, optopt, optreset;
355 optind = optreset = 1;
359 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
370 error("%s: Invalid flag -%c", cmd, optopt);
379 parse_ls_flags(char **argv, int argc, int *lflag)
381 extern int opterr, optind, optopt, optreset;
384 optind = optreset = 1;
387 *lflag = LS_NAME_SORT;
388 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
391 *lflag &= ~VIEW_FLAGS;
392 *lflag |= LS_SHORT_VIEW;
395 *lflag &= ~SORT_FLAGS;
396 *lflag |= LS_SIZE_SORT;
399 *lflag |= LS_SHOW_ALL;
402 *lflag &= ~SORT_FLAGS;
405 *lflag |= LS_SI_UNITS;
408 *lflag &= ~LS_SHORT_VIEW;
409 *lflag |= LS_LONG_VIEW;
412 *lflag &= ~LS_SHORT_VIEW;
413 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
416 *lflag |= LS_REVERSE_SORT;
419 *lflag &= ~SORT_FLAGS;
420 *lflag |= LS_TIME_SORT;
423 error("ls: Invalid flag -%c", optopt);
432 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
434 extern int opterr, optind, optopt, optreset;
437 optind = optreset = 1;
441 while ((ch = getopt(argc, argv, "hi")) != -1) {
450 error("%s: Invalid flag -%c", cmd, optopt);
463 /* XXX: report errors? */
464 if (stat(path, &sb) == -1)
467 return(S_ISDIR(sb.st_mode));
471 remote_is_dir(struct sftp_conn *conn, char *path)
475 /* XXX: report errors? */
476 if ((a = do_stat(conn, path, 1)) == NULL)
478 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
480 return(S_ISDIR(a->perm));
483 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
485 pathname_is_dir(char *pathname)
487 size_t l = strlen(pathname);
489 return l > 0 && pathname[l - 1] == '/';
493 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
494 int pflag, int rflag)
496 char *abs_src = NULL;
497 char *abs_dst = NULL;
499 char *filename, *tmp=NULL;
502 abs_src = xstrdup(src);
503 abs_src = make_absolute(abs_src, pwd);
504 memset(&g, 0, sizeof(g));
506 debug3("Looking up %s", abs_src);
507 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
508 error("File \"%s\" not found.", abs_src);
514 * If multiple matches then dst must be a directory or
517 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
518 error("Multiple source paths, but destination "
519 "\"%s\" is not a directory", dst);
524 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
525 tmp = xstrdup(g.gl_pathv[i]);
526 if ((filename = basename(tmp)) == NULL) {
527 error("basename %s: %s", tmp, strerror(errno));
533 if (g.gl_matchc == 1 && dst) {
535 abs_dst = path_append(dst, filename);
537 abs_dst = xstrdup(dst);
540 abs_dst = path_append(dst, filename);
542 abs_dst = xstrdup(filename);
546 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
547 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
548 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
549 pflag || global_pflag, 1) == -1)
552 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
553 pflag || global_pflag) == -1)
567 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
568 int pflag, int rflag)
570 char *tmp_dst = NULL;
571 char *abs_dst = NULL;
572 char *tmp = NULL, *filename = NULL;
575 int i, dst_is_dir = 1;
579 tmp_dst = xstrdup(dst);
580 tmp_dst = make_absolute(tmp_dst, pwd);
583 memset(&g, 0, sizeof(g));
584 debug3("Looking up %s", src);
585 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
586 error("File \"%s\" not found.", src);
591 /* If we aren't fetching to pwd then stash this status for later */
593 dst_is_dir = remote_is_dir(conn, tmp_dst);
595 /* If multiple matches, dst may be directory or unspecified */
596 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
597 error("Multiple paths match, but destination "
598 "\"%s\" is not a directory", tmp_dst);
603 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
604 if (stat(g.gl_pathv[i], &sb) == -1) {
606 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
610 tmp = xstrdup(g.gl_pathv[i]);
611 if ((filename = basename(tmp)) == NULL) {
612 error("basename %s: %s", tmp, strerror(errno));
618 if (g.gl_matchc == 1 && tmp_dst) {
619 /* If directory specified, append filename */
621 abs_dst = path_append(tmp_dst, filename);
623 abs_dst = xstrdup(tmp_dst);
624 } else if (tmp_dst) {
625 abs_dst = path_append(tmp_dst, filename);
627 abs_dst = make_absolute(xstrdup(filename), pwd);
631 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
632 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
633 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
634 pflag || global_pflag, 1) == -1)
637 if (do_upload(conn, g.gl_pathv[i], abs_dst,
638 pflag || global_pflag) == -1)
653 sdirent_comp(const void *aa, const void *bb)
655 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
656 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
657 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
659 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
660 if (sort_flag & LS_NAME_SORT)
661 return (rmul * strcmp(a->filename, b->filename));
662 else if (sort_flag & LS_TIME_SORT)
663 return (rmul * NCMP(a->a.mtime, b->a.mtime));
664 else if (sort_flag & LS_SIZE_SORT)
665 return (rmul * NCMP(a->a.size, b->a.size));
667 fatal("Unknown ls sort type");
670 /* sftp ls.1 replacement for directories */
672 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
675 u_int c = 1, colspace = 0, columns = 1;
678 if ((n = do_readdir(conn, path, &d)) != 0)
681 if (!(lflag & LS_SHORT_VIEW)) {
682 u_int m = 0, width = 80;
686 /* Count entries for sort and find longest filename */
687 for (n = 0; d[n] != NULL; n++) {
688 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
689 m = MAX(m, strlen(d[n]->filename));
692 /* Add any subpath that also needs to be counted */
693 tmp = path_strip(path, strip_path);
697 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
700 columns = width / (m + 2);
701 columns = MAX(columns, 1);
702 colspace = width / columns;
703 colspace = MIN(colspace, width);
706 if (lflag & SORT_FLAGS) {
707 for (n = 0; d[n] != NULL; n++)
708 ; /* count entries */
709 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
710 qsort(d, n, sizeof(*d), sdirent_comp);
713 for (n = 0; d[n] != NULL && !interrupted; n++) {
716 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
719 tmp = path_append(path, d[n]->filename);
720 fname = path_strip(tmp, strip_path);
723 if (lflag & LS_LONG_VIEW) {
724 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
728 memset(&sb, 0, sizeof(sb));
729 attrib_to_stat(&d[n]->a, &sb);
730 lname = ls_file(fname, &sb, 1,
731 (lflag & LS_SI_UNITS));
732 printf("%s\n", lname);
735 printf("%s\n", d[n]->longname);
737 printf("%-*s", colspace, fname);
748 if (!(lflag & LS_LONG_VIEW) && (c != 1))
751 free_sftp_dirents(d);
755 /* sftp ls.1 replacement which handles path globs */
757 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
761 u_int i, c = 1, colspace = 0, columns = 1;
764 memset(&g, 0, sizeof(g));
766 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
767 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
770 error("Can't ls: \"%s\" not found", path);
778 * If the glob returns a single match and it is a directory,
779 * then just list its contents.
781 if (g.gl_matchc == 1) {
782 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
786 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
790 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
796 if (!(lflag & LS_SHORT_VIEW)) {
797 u_int m = 0, width = 80;
800 /* Count entries for sort and find longest filename */
801 for (i = 0; g.gl_pathv[i]; i++)
802 m = MAX(m, strlen(g.gl_pathv[i]));
804 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
807 columns = width / (m + 2);
808 columns = MAX(columns, 1);
809 colspace = width / columns;
812 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
815 fname = path_strip(g.gl_pathv[i], strip_path);
817 if (lflag & LS_LONG_VIEW) {
822 * XXX: this is slow - 1 roundtrip per path
823 * A solution to this is to fork glob() and
824 * build a sftp specific version which keeps the
825 * attribs (which currently get thrown away)
826 * that the server returns as well as the filenames.
828 memset(&sb, 0, sizeof(sb));
830 a = do_lstat(conn, g.gl_pathv[i], 1);
832 attrib_to_stat(a, &sb);
833 lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS));
834 printf("%s\n", lname);
837 printf("%-*s", colspace, fname);
847 if (!(lflag & LS_LONG_VIEW) && (c != 1))
858 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
860 struct sftp_statvfs st;
861 char s_used[FMT_SCALED_STRSIZE];
862 char s_avail[FMT_SCALED_STRSIZE];
863 char s_root[FMT_SCALED_STRSIZE];
864 char s_total[FMT_SCALED_STRSIZE];
865 unsigned long long ffree;
867 if (do_statvfs(conn, path, &st, 1) == -1)
870 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
871 printf(" Inodes Used Avail "
872 "(root) %%Capacity\n");
873 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
874 (unsigned long long)st.f_files,
875 (unsigned long long)(st.f_files - st.f_ffree),
876 (unsigned long long)st.f_favail,
877 (unsigned long long)st.f_ffree, ffree);
879 strlcpy(s_used, "error", sizeof(s_used));
880 strlcpy(s_avail, "error", sizeof(s_avail));
881 strlcpy(s_root, "error", sizeof(s_root));
882 strlcpy(s_total, "error", sizeof(s_total));
883 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
884 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
885 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
886 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
887 printf(" Size Used Avail (root) %%Capacity\n");
888 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
889 s_total, s_used, s_avail, s_root,
890 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
893 printf(" Size Used Avail "
894 "(root) %%Capacity\n");
895 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
896 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
897 (unsigned long long)(st.f_frsize *
898 (st.f_blocks - st.f_bfree) / 1024),
899 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
900 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
901 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
908 * Undo escaping of glob sequences in place. Used to undo extra escaping
909 * applied in makeargv() when the string is destined for a function that
913 undo_glob_escape(char *s)
948 * Split a string into an argument vector using sh(1)-style quoting,
949 * comment and escaping rules, but with some tweaks to handle glob(3)
951 * The "sloppy" flag allows for recovery from missing terminating quote, for
952 * use in parsing incomplete commandlines during tab autocompletion.
954 * Returns NULL on error or a NULL-terminated array of arguments.
956 * If "lastquote" is not NULL, the quoting character used for the last
957 * argument is placed in *lastquote ("\0", "'" or "\"").
959 * If "terminated" is not NULL, *terminated will be set to 1 when the
960 * last argument's quote has been properly terminated or 0 otherwise.
961 * This parameter is only of use if "sloppy" is set.
964 #define MAXARGLEN 8192
966 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
971 static char argvs[MAXARGLEN];
972 static char *argv[MAXARGS + 1];
973 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
976 if (strlen(arg) > sizeof(argvs) - 1) {
978 error("string too long");
981 if (terminated != NULL)
983 if (lastquote != NULL)
988 if (isspace(arg[i])) {
989 if (state == MA_UNQUOTED) {
990 /* Terminate current argument */
994 } else if (state != MA_START)
996 } else if (arg[i] == '"' || arg[i] == '\'') {
997 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
998 if (state == MA_START) {
999 argv[argc] = argvs + j;
1001 if (lastquote != NULL)
1002 *lastquote = arg[i];
1003 } else if (state == MA_UNQUOTED)
1005 else if (state == q)
1006 state = MA_UNQUOTED;
1008 argvs[j++] = arg[i];
1009 } else if (arg[i] == '\\') {
1010 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1011 quot = state == MA_SQUOTE ? '\'' : '"';
1012 /* Unescape quote we are in */
1013 /* XXX support \n and friends? */
1014 if (arg[i + 1] == quot) {
1016 argvs[j++] = arg[i];
1017 } else if (arg[i + 1] == '?' ||
1018 arg[i + 1] == '[' || arg[i + 1] == '*') {
1020 * Special case for sftp: append
1021 * double-escaped glob sequence -
1022 * glob will undo one level of
1023 * escaping. NB. string can grow here.
1025 if (j >= sizeof(argvs) - 5)
1026 goto args_too_longs;
1028 argvs[j++] = arg[i++];
1030 argvs[j++] = arg[i];
1032 argvs[j++] = arg[i++];
1033 argvs[j++] = arg[i];
1036 if (state == MA_START) {
1037 argv[argc] = argvs + j;
1038 state = MA_UNQUOTED;
1039 if (lastquote != NULL)
1042 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1043 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1045 * Special case for sftp: append
1046 * escaped glob sequence -
1047 * glob will undo one level of
1050 argvs[j++] = arg[i++];
1051 argvs[j++] = arg[i];
1053 /* Unescape everything */
1054 /* XXX support \n and friends? */
1056 argvs[j++] = arg[i];
1059 } else if (arg[i] == '#') {
1060 if (state == MA_SQUOTE || state == MA_DQUOTE)
1061 argvs[j++] = arg[i];
1064 } else if (arg[i] == '\0') {
1065 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1067 state = MA_UNQUOTED;
1068 if (terminated != NULL)
1072 error("Unterminated quoted argument");
1076 if (state == MA_UNQUOTED) {
1082 if (state == MA_START) {
1083 argv[argc] = argvs + j;
1084 state = MA_UNQUOTED;
1085 if (lastquote != NULL)
1088 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1089 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1091 * Special case for sftp: escape quoted
1092 * glob(3) wildcards. NB. string can grow
1095 if (j >= sizeof(argvs) - 3)
1096 goto args_too_longs;
1098 argvs[j++] = arg[i];
1100 argvs[j++] = arg[i];
1109 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1110 int *hflag, unsigned long *n_arg, char **path1, char **path2)
1112 const char *cmd, *cp = *cpp;
1116 int i, cmdnum, optidx, argc;
1118 /* Skip leading whitespace */
1119 cp = cp + strspn(cp, WHITESPACE);
1121 /* Check for leading '-' (disable error processing) */
1126 cp = cp + strspn(cp, WHITESPACE);
1129 /* Ignore blank lines and lines which begin with comment '#' char */
1130 if (*cp == '\0' || *cp == '#')
1133 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1136 /* Figure out which command we have */
1137 for (i = 0; cmds[i].c != NULL; i++) {
1138 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1148 } else if (cmdnum == -1) {
1149 error("Invalid command.");
1153 /* Get arguments and parse flags */
1154 *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1155 *path1 = *path2 = NULL;
1160 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
1162 /* Get first pathname (mandatory) */
1163 if (argc - optidx < 1) {
1164 error("You must specify at least one path after a "
1165 "%s command.", cmd);
1168 *path1 = xstrdup(argv[optidx]);
1169 /* Get second pathname (optional) */
1170 if (argc - optidx > 1) {
1171 *path2 = xstrdup(argv[optidx + 1]);
1172 /* Destination is not globbed */
1173 undo_glob_escape(*path2);
1178 if (argc - optidx < 2) {
1179 error("You must specify two paths after a %s "
1183 *path1 = xstrdup(argv[optidx]);
1184 *path2 = xstrdup(argv[optidx + 1]);
1185 /* Paths are not globbed */
1186 undo_glob_escape(*path1);
1187 undo_glob_escape(*path2);
1195 /* Get pathname (mandatory) */
1196 if (argc - optidx < 1) {
1197 error("You must specify a path after a %s command.",
1201 *path1 = xstrdup(argv[optidx]);
1202 /* Only "rm" globs */
1204 undo_glob_escape(*path1);
1207 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1210 /* Default to current directory if no path specified */
1211 if (argc - optidx < 1)
1214 *path1 = xstrdup(argv[optidx]);
1215 undo_glob_escape(*path1);
1219 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1221 /* Path is optional */
1222 if (argc - optidx > 0)
1223 *path1 = xstrdup(argv[optidx]);
1226 /* Skip ls command and following whitespace */
1227 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1229 /* Uses the rest of the line */
1236 /* Get numeric arg (mandatory) */
1237 if (argc - optidx < 1)
1240 l = strtol(argv[optidx], &cp2, base);
1241 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1242 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1245 error("You must supply a numeric argument "
1246 "to the %s command.", cmd);
1250 if (cmdnum == I_LUMASK)
1252 /* Get pathname (mandatory) */
1253 if (argc - optidx < 2) {
1254 error("You must specify a path after a %s command.",
1258 *path1 = xstrdup(argv[optidx + 1]);
1268 fatal("Command not implemented");
1276 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1279 char *path1, *path2, *tmp;
1280 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1281 unsigned long n_arg = 0;
1283 char path_buf[MAXPATHLEN];
1287 path1 = path2 = NULL;
1288 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1294 memset(&g, 0, sizeof(g));
1296 /* Perform command */
1302 /* Unrecognized command */
1306 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1309 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1312 path1 = make_absolute(path1, *pwd);
1313 path2 = make_absolute(path2, *pwd);
1314 err = do_rename(conn, path1, path2);
1317 path2 = make_absolute(path2, *pwd);
1318 err = do_symlink(conn, path1, path2);
1321 path1 = make_absolute(path1, *pwd);
1322 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1323 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1324 printf("Removing %s\n", g.gl_pathv[i]);
1325 err = do_rm(conn, g.gl_pathv[i]);
1326 if (err != 0 && err_abort)
1331 path1 = make_absolute(path1, *pwd);
1333 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1335 err = do_mkdir(conn, path1, &a, 1);
1338 path1 = make_absolute(path1, *pwd);
1339 err = do_rmdir(conn, path1);
1342 path1 = make_absolute(path1, *pwd);
1343 if ((tmp = do_realpath(conn, path1)) == NULL) {
1347 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1352 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1353 error("Can't change directory: Can't check target");
1358 if (!S_ISDIR(aa->perm)) {
1359 error("Can't change directory: \"%s\" is not "
1360 "a directory", tmp);
1370 do_globbed_ls(conn, *pwd, *pwd, lflag);
1374 /* Strip pwd off beginning of non-absolute paths */
1379 path1 = make_absolute(path1, *pwd);
1380 err = do_globbed_ls(conn, path1, tmp, lflag);
1383 /* Default to current directory if no path specified */
1385 path1 = xstrdup(*pwd);
1386 path1 = make_absolute(path1, *pwd);
1387 err = do_df(conn, path1, hflag, iflag);
1390 if (chdir(path1) == -1) {
1391 error("Couldn't change local directory to "
1392 "\"%s\": %s", path1, strerror(errno));
1397 if (mkdir(path1, 0777) == -1) {
1398 error("Couldn't create local directory "
1399 "\"%s\": %s", path1, strerror(errno));
1407 local_do_shell(cmd);
1411 printf("Local umask: %03lo\n", n_arg);
1414 path1 = make_absolute(path1, *pwd);
1416 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1418 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1419 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1420 printf("Changing mode on %s\n", g.gl_pathv[i]);
1421 err = do_setstat(conn, g.gl_pathv[i], &a);
1422 if (err != 0 && err_abort)
1428 path1 = make_absolute(path1, *pwd);
1429 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1430 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1431 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1438 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1439 error("Can't get current ownership of "
1440 "remote file \"%s\"", g.gl_pathv[i]);
1447 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1448 if (cmdnum == I_CHOWN) {
1449 printf("Changing owner on %s\n", g.gl_pathv[i]);
1452 printf("Changing group on %s\n", g.gl_pathv[i]);
1455 err = do_setstat(conn, g.gl_pathv[i], aa);
1456 if (err != 0 && err_abort)
1461 printf("Remote working directory: %s\n", *pwd);
1464 if (!getcwd(path_buf, sizeof(path_buf))) {
1465 error("Couldn't get local cwd: %s", strerror(errno));
1469 printf("Local working directory: %s\n", path_buf);
1472 /* Processed below */
1478 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1481 showprogress = !showprogress;
1483 printf("Progress meter enabled\n");
1485 printf("Progress meter disabled\n");
1488 fatal("%d is not implemented", cmdnum);
1498 /* If an unignored error occurs in batch mode we should abort. */
1499 if (err_abort && err != 0)
1501 else if (cmdnum == I_QUIT)
1509 prompt(EditLine *el)
1514 /* Display entries in 'list' after skipping the first 'len' chars */
1516 complete_display(char **list, u_int len)
1518 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1522 /* Count entries for sort and find longest */
1523 for (y = 0; list[y]; y++)
1524 m = MAX(m, strlen(list[y]));
1526 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1529 m = m > len ? m - len : 0;
1530 columns = width / (m + 2);
1531 columns = MAX(columns, 1);
1532 colspace = width / columns;
1533 colspace = MIN(colspace, width);
1537 for (y = 0; list[y]; y++) {
1538 llen = strlen(list[y]);
1539 tmp = llen > len ? list[y] + len : "";
1540 printf("%-*s", colspace, tmp);
1551 * Given a "list" of words that begin with a common prefix of "word",
1552 * attempt to find an autocompletion to extends "word" by the next
1553 * characters common to all entries in "list".
1556 complete_ambiguous(const char *word, char **list, size_t count)
1562 u_int y, matchlen = strlen(list[0]);
1564 /* Find length of common stem */
1565 for (y = 1; list[y]; y++) {
1568 for (x = 0; x < matchlen; x++)
1569 if (list[0][x] != list[y][x])
1575 if (matchlen > strlen(word)) {
1576 char *tmp = xstrdup(list[0]);
1578 tmp[matchlen] = '\0';
1583 return xstrdup(word);
1586 /* Autocomplete a sftp command */
1588 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1591 u_int y, count = 0, cmdlen, tmplen;
1592 char *tmp, **list, argterm[3];
1595 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1597 /* No command specified: display all available commands */
1599 for (y = 0; cmds[y].c; y++)
1600 list[count++] = xstrdup(cmds[y].c);
1603 complete_display(list, 0);
1605 for (y = 0; list[y] != NULL; y++)
1611 /* Prepare subset of commands that start with "cmd" */
1612 cmdlen = strlen(cmd);
1613 for (y = 0; cmds[y].c; y++) {
1614 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1615 list[count++] = xstrdup(cmds[y].c);
1622 /* Complete ambigious command */
1623 tmp = complete_ambiguous(cmd, list, count);
1625 complete_display(list, 0);
1627 for (y = 0; list[y]; y++)
1632 tmplen = strlen(tmp);
1633 cmdlen = strlen(cmd);
1634 /* If cmd may be extended then do so */
1635 if (tmplen > cmdlen)
1636 if (el_insertstr(el, tmp + cmdlen) == -1)
1637 fatal("el_insertstr failed.");
1639 /* Terminate argument cleanly */
1643 argterm[y++] = quote;
1644 if (lastarg || *(lf->cursor) != ' ')
1647 if (y > 0 && el_insertstr(el, argterm) == -1)
1648 fatal("el_insertstr failed.");
1657 * Determine whether a particular sftp command's arguments (if any)
1658 * represent local or remote files.
1661 complete_is_remote(char *cmd) {
1667 for (i = 0; cmds[i].c; i++) {
1668 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1675 /* Autocomplete a filename "file" */
1677 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1678 char *file, int remote, int lastarg, char quote, int terminated)
1681 char *tmp, *tmp2, ins[3];
1682 u_int i, hadglob, pwdlen, len, tmplen, filelen;
1685 /* Glob from "file" location */
1689 xasprintf(&tmp, "%s*", file);
1691 memset(&g, 0, sizeof(g));
1692 if (remote != LOCAL) {
1693 tmp = make_absolute(tmp, remote_path);
1694 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1696 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1698 /* Determine length of pwd so we can trim completion display */
1699 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1700 /* Terminate counting on first unescaped glob metacharacter */
1701 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1702 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1706 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1708 if (tmp[tmplen] == '/')
1709 pwdlen = tmplen + 1; /* track last seen '/' */
1713 if (g.gl_matchc == 0)
1716 if (g.gl_matchc > 1)
1717 complete_display(g.gl_pathv, pwdlen);
1720 /* Don't try to extend globs */
1721 if (file == NULL || hadglob)
1724 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1725 tmp = path_strip(tmp2, remote_path);
1731 tmplen = strlen(tmp);
1732 filelen = strlen(file);
1734 if (tmplen > filelen) {
1735 tmp2 = tmp + filelen;
1737 /* quote argument on way out */
1738 for (i = 0; i < len; i++) {
1748 if (quote == '\0' || tmp2[i] == quote) {
1749 if (el_insertstr(el, ins) == -1)
1750 fatal("el_insertstr "
1756 if (el_insertstr(el, ins + 1) == -1)
1757 fatal("el_insertstr failed.");
1764 if (g.gl_matchc == 1) {
1768 if (*(lf->cursor - 1) != '/' &&
1769 (lastarg || *(lf->cursor) != ' '))
1772 if (i > 0 && el_insertstr(el, ins) == -1)
1773 fatal("el_insertstr failed.");
1782 /* tab-completion hook function, called via libedit */
1783 static unsigned char
1784 complete(EditLine *el, int ch)
1786 char **argv, *line, quote;
1787 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1789 struct complete_ctx *complete_ctx;
1792 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1793 fatal("%s: el_get failed", __func__);
1795 /* Figure out which argument the cursor points to */
1796 cursor = lf->cursor - lf->buffer;
1797 line = (char *)xmalloc(cursor + 1);
1798 memcpy(line, lf->buffer, cursor);
1799 line[cursor] = '\0';
1800 argv = makeargv(line, &carg, 1, "e, &terminated);
1803 /* Get all the arguments on the line */
1804 len = lf->lastchar - lf->buffer;
1805 line = (char *)xmalloc(len + 1);
1806 memcpy(line, lf->buffer, len);
1808 argv = makeargv(line, &argc, 1, NULL, NULL);
1810 /* Ensure cursor is at EOL or a argument boundary */
1811 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1812 line[cursor] != '\n') {
1818 /* Show all available commands */
1819 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1821 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1822 /* Handle the command parsing */
1823 if (complete_cmd_parse(el, argv[0], argc == carg,
1824 quote, terminated) != 0)
1826 } else if (carg >= 1) {
1827 /* Handle file parsing */
1828 int remote = complete_is_remote(argv[0]);
1829 char *filematch = NULL;
1831 if (carg > 1 && line[cursor-1] != ' ')
1832 filematch = argv[carg - 1];
1835 complete_match(el, complete_ctx->conn,
1836 *complete_ctx->remote_pathp, filematch,
1837 remote, carg == argc, quote, terminated) != 0)
1844 #endif /* USE_LIBEDIT */
1847 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1852 int err, interactive;
1853 EditLine *el = NULL;
1857 extern char *__progname;
1858 struct complete_ctx complete_ctx;
1860 if (!batchmode && isatty(STDIN_FILENO)) {
1861 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1862 fatal("Couldn't initialise editline");
1863 if ((hl = history_init()) == NULL)
1864 fatal("Couldn't initialise editline history");
1865 history(hl, &hev, H_SETSIZE, 100);
1866 el_set(el, EL_HIST, history, hl);
1868 el_set(el, EL_PROMPT, prompt);
1869 el_set(el, EL_EDITOR, "emacs");
1870 el_set(el, EL_TERMINAL, NULL);
1871 el_set(el, EL_SIGNAL, 1);
1872 el_source(el, NULL);
1874 /* Tab Completion */
1875 el_set(el, EL_ADDFN, "ftp-complete",
1876 "Context senstive argument completion", complete);
1877 complete_ctx.conn = conn;
1878 complete_ctx.remote_pathp = &remote_path;
1879 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1880 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1882 #endif /* USE_LIBEDIT */
1884 remote_path = do_realpath(conn, ".");
1885 if (remote_path == NULL)
1888 if (file1 != NULL) {
1889 dir = xstrdup(file1);
1890 dir = make_absolute(dir, remote_path);
1892 if (remote_is_dir(conn, dir) && file2 == NULL) {
1893 printf("Changing to: %s\n", dir);
1894 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1895 if (parse_dispatch_command(conn, cmd,
1896 &remote_path, 1) != 0) {
1904 snprintf(cmd, sizeof cmd, "get %s", dir);
1906 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1909 err = parse_dispatch_command(conn, cmd,
1919 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1920 setvbuf(stdout, NULL, _IOLBF, 0);
1921 setvbuf(infile, NULL, _IOLBF, 0);
1927 interactive = !batchmode && isatty(STDIN_FILENO);
1932 signal(SIGINT, SIG_IGN);
1937 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1942 if (!interactive) { /* Echo command */
1943 printf("sftp> %s", cmd);
1944 if (strlen(cmd) > 0 &&
1945 cmd[strlen(cmd) - 1] != '\n')
1953 if ((line = el_gets(el, &count)) == NULL ||
1958 history(hl, &hev, H_ENTER, line);
1959 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1960 fprintf(stderr, "Error: input line too long\n");
1963 #endif /* USE_LIBEDIT */
1966 cp = strrchr(cmd, '\n');
1970 /* Handle user interrupts gracefully during commands */
1972 signal(SIGINT, cmd_interrupt);
1974 err = parse_dispatch_command(conn, cmd, &remote_path,
1985 #endif /* USE_LIBEDIT */
1987 /* err == 1 signifies normal "quit" exit */
1988 return (err >= 0 ? 0 : -1);
1992 connect_to_server(char *path, char **args, int *in, int *out)
1997 int pin[2], pout[2];
1999 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2000 fatal("pipe: %s", strerror(errno));
2005 #else /* USE_PIPES */
2008 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2009 fatal("socketpair: %s", strerror(errno));
2010 *in = *out = inout[0];
2011 c_in = c_out = inout[1];
2012 #endif /* USE_PIPES */
2014 if ((sshpid = fork()) == -1)
2015 fatal("fork: %s", strerror(errno));
2016 else if (sshpid == 0) {
2017 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2018 (dup2(c_out, STDOUT_FILENO) == -1)) {
2019 fprintf(stderr, "dup2: %s\n", strerror(errno));
2028 * The underlying ssh is in the same process group, so we must
2029 * ignore SIGINT if we want to gracefully abort commands,
2030 * otherwise the signal will make it to the ssh process and
2031 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2032 * underlying ssh, it must *not* ignore that signal.
2034 signal(SIGINT, SIG_IGN);
2035 signal(SIGTERM, SIG_DFL);
2037 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2041 signal(SIGTERM, killchild);
2042 signal(SIGINT, killchild);
2043 signal(SIGHUP, killchild);
2051 extern char *__progname;
2054 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2055 " [-D sftp_server_path] [-F ssh_config] "
2056 "[-i identity_file]\n"
2057 " [-o ssh_option] [-P port] [-R num_requests] "
2059 " [-s subsystem | sftp_server] host\n"
2060 " %s [user@]host[:file ...]\n"
2061 " %s [user@]host[:dir[/]]\n"
2062 " %s -b batchfile [user@]host\n",
2063 __progname, __progname, __progname, __progname);
2068 main(int argc, char **argv)
2070 int in, out, ch, err;
2071 char *host = NULL, *userhost, *cp, *file2 = NULL;
2072 int debug_level = 0, sshver = 2;
2073 char *file1 = NULL, *sftp_server = NULL;
2074 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2075 LogLevel ll = SYSLOG_LEVEL_INFO;
2078 extern char *optarg;
2079 struct sftp_conn *conn;
2080 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2081 size_t num_requests = DEFAULT_NUM_REQUESTS;
2083 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2086 __progname = ssh_get_progname(argv[0]);
2087 memset(&args, '\0', sizeof(args));
2089 addargs(&args, "%s", ssh_program);
2090 addargs(&args, "-oForwardX11 no");
2091 addargs(&args, "-oForwardAgent no");
2092 addargs(&args, "-oPermitLocalCommand no");
2093 addargs(&args, "-oClearAllForwardings yes");
2095 ll = SYSLOG_LEVEL_INFO;
2098 while ((ch = getopt(argc, argv,
2099 "1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
2101 /* Passed through to ssh(1) */
2105 addargs(&args, "-%c", ch);
2107 /* Passed through to ssh(1) with argument */
2112 addargs(&args, "-%c", ch);
2113 addargs(&args, "%s", optarg);
2117 addargs(&args, "-%c", ch);
2120 addargs(&args, "-oPort %s", optarg);
2123 if (debug_level < 3) {
2124 addargs(&args, "-v");
2125 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2131 if (sftp_server == NULL)
2132 sftp_server = _PATH_SFTP_SERVER;
2138 copy_buffer_len = strtol(optarg, &cp, 10);
2139 if (copy_buffer_len == 0 || *cp != '\0')
2140 fatal("Invalid buffer size \"%s\"", optarg);
2144 fatal("Batch file already specified.");
2146 /* Allow "-" as stdin */
2147 if (strcmp(optarg, "-") != 0 &&
2148 (infile = fopen(optarg, "r")) == NULL)
2149 fatal("%s (%s).", strerror(errno), optarg);
2152 addargs(&args, "-obatchmode yes");
2158 sftp_direct = optarg;
2164 num_requests = strtol(optarg, &cp, 10);
2165 if (num_requests == 0 || *cp != '\0')
2166 fatal("Invalid number of requests \"%s\"",
2170 sftp_server = optarg;
2173 ssh_program = optarg;
2174 replacearg(&args, 0, "%s", ssh_program);
2182 if (!isatty(STDERR_FILENO))
2185 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2187 if (sftp_direct == NULL) {
2188 if (optind == argc || argc > (optind + 2))
2191 userhost = xstrdup(argv[optind]);
2192 file2 = argv[optind+1];
2194 if ((host = strrchr(userhost, '@')) == NULL)
2199 fprintf(stderr, "Missing username\n");
2202 addargs(&args, "-l");
2203 addargs(&args, "%s", userhost);
2206 if ((cp = colon(host)) != NULL) {
2211 host = cleanhostname(host);
2213 fprintf(stderr, "Missing hostname\n");
2217 addargs(&args, "-oProtocol %d", sshver);
2219 /* no subsystem if the server-spec contains a '/' */
2220 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2221 addargs(&args, "-s");
2223 addargs(&args, "--");
2224 addargs(&args, "%s", host);
2225 addargs(&args, "%s", (sftp_server != NULL ?
2226 sftp_server : "sftp"));
2228 connect_to_server(ssh_program, args.list, &in, &out);
2231 addargs(&args, "sftp-server");
2233 connect_to_server(sftp_direct, args.list, &in, &out);
2237 conn = do_init(in, out, copy_buffer_len, num_requests);
2239 fatal("Couldn't initialise connection to server");
2242 if (sftp_direct == NULL)
2243 fprintf(stderr, "Connected to %s.\n", host);
2245 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2248 err = interactive_loop(conn, file1, file2);
2250 #if !defined(USE_PIPES)
2251 shutdown(in, SHUT_RDWR);
2252 shutdown(out, SHUT_RDWR);
2260 while (waitpid(sshpid, NULL, 0) == -1)
2262 fatal("Couldn't wait for ssh process: %s",
2265 exit(err == 0 ? 0 : 1);