1 /* $OpenBSD: sftp.c,v 1.142 2013/02/08 00:41:12 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;
60 #include "pathnames.h"
65 #include "sftp-common.h"
66 #include "sftp-client.h"
68 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
69 #define DEFAULT_NUM_REQUESTS 256 /* # concurrent outstanding requests */
71 /* File to read commands from */
74 /* Are we in batchfile mode? */
77 /* PID of ssh transport process */
78 static pid_t sshpid = -1;
80 /* This is set to 0 if the progressmeter is not desired. */
83 /* When this option is set, we always recursively download/upload directories */
86 /* When this option is set, the file transfers will always preserve times */
89 /* SIGINT received during command processing */
90 volatile sig_atomic_t interrupted = 0;
92 /* I wish qsort() took a separate ctx for the comparison function...*/
95 /* Context used for commandline completion */
97 struct sftp_conn *conn;
101 int remote_glob(struct sftp_conn *, const char *, int,
102 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
104 extern char *__progname;
106 /* Separators for interactive commands */
107 #define WHITESPACE " \t\r\n"
110 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
111 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
112 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
113 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
114 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
115 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
116 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
117 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
118 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
120 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
121 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
123 /* Commands for interactive mode */
148 #define I_PROGRESS 23
156 /* Type of completion */
161 static const struct CMD cmds[] = {
162 { "bye", I_QUIT, NOARGS },
163 { "cd", I_CHDIR, REMOTE },
164 { "chdir", I_CHDIR, REMOTE },
165 { "chgrp", I_CHGRP, REMOTE },
166 { "chmod", I_CHMOD, REMOTE },
167 { "chown", I_CHOWN, REMOTE },
168 { "df", I_DF, REMOTE },
169 { "dir", I_LS, REMOTE },
170 { "exit", I_QUIT, NOARGS },
171 { "get", I_GET, REMOTE },
172 { "help", I_HELP, NOARGS },
173 { "lcd", I_LCHDIR, LOCAL },
174 { "lchdir", I_LCHDIR, LOCAL },
175 { "lls", I_LLS, LOCAL },
176 { "lmkdir", I_LMKDIR, LOCAL },
177 { "ln", I_LINK, REMOTE },
178 { "lpwd", I_LPWD, LOCAL },
179 { "ls", I_LS, REMOTE },
180 { "lumask", I_LUMASK, NOARGS },
181 { "mkdir", I_MKDIR, REMOTE },
182 { "mget", I_GET, REMOTE },
183 { "mput", I_PUT, LOCAL },
184 { "progress", I_PROGRESS, NOARGS },
185 { "put", I_PUT, LOCAL },
186 { "pwd", I_PWD, REMOTE },
187 { "quit", I_QUIT, NOARGS },
188 { "rename", I_RENAME, REMOTE },
189 { "rm", I_RM, REMOTE },
190 { "rmdir", I_RMDIR, REMOTE },
191 { "symlink", I_SYMLINK, REMOTE },
192 { "version", I_VERSION, NOARGS },
193 { "!", I_SHELL, NOARGS },
194 { "?", I_HELP, NOARGS },
198 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
205 kill(sshpid, SIGTERM);
206 waitpid(sshpid, NULL, 0);
214 cmd_interrupt(int signo)
216 const char msg[] = "\rInterrupt \n";
217 int olderrno = errno;
219 write(STDERR_FILENO, msg, sizeof(msg) - 1);
227 printf("Available commands:\n"
229 "cd path Change remote directory to 'path'\n"
230 "chgrp grp path Change group of file 'path' to 'grp'\n"
231 "chmod mode path Change permissions of file 'path' to 'mode'\n"
232 "chown own path Change owner of file 'path' to 'own'\n"
233 "df [-hi] [path] Display statistics for current directory or\n"
234 " filesystem containing 'path'\n"
236 "get [-Ppr] remote [local] Download file\n"
237 "help Display this help text\n"
238 "lcd path Change local directory to 'path'\n"
239 "lls [ls-options [path]] Display local directory listing\n"
240 "lmkdir path Create local directory\n"
241 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
242 "lpwd Print local working directory\n"
243 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
244 "lumask umask Set local umask to 'umask'\n"
245 "mkdir path Create remote directory\n"
246 "progress Toggle display of progress meter\n"
247 "put [-Ppr] local [remote] Upload file\n"
248 "pwd Display remote working directory\n"
250 "rename oldpath newpath Rename remote file\n"
251 "rm path Delete remote file\n"
252 "rmdir path Remove remote directory\n"
253 "symlink oldpath newpath Symlink remote file\n"
254 "version Show SFTP version\n"
255 "!command Execute 'command' in local shell\n"
256 "! Escape to local shell\n"
257 "? Synonym for help\n");
261 local_do_shell(const char *args)
270 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
271 shell = _PATH_BSHELL;
273 if ((pid = fork()) == -1)
274 fatal("Couldn't fork: %s", strerror(errno));
277 /* XXX: child has pipe fds to ssh subproc open - issue? */
279 debug3("Executing %s -c \"%s\"", shell, args);
280 execl(shell, shell, "-c", args, (char *)NULL);
282 debug3("Executing %s", shell);
283 execl(shell, shell, (char *)NULL);
285 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
289 while (waitpid(pid, &status, 0) == -1)
291 fatal("Couldn't wait for child: %s", strerror(errno));
292 if (!WIFEXITED(status))
293 error("Shell exited abnormally");
294 else if (WEXITSTATUS(status))
295 error("Shell exited with status %d", WEXITSTATUS(status));
299 local_do_ls(const char *args)
302 local_do_shell(_PATH_LS);
304 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
305 char *buf = xmalloc(len);
307 /* XXX: quoting - rip quoting code from ftp? */
308 snprintf(buf, len, _PATH_LS " %s", args);
314 /* Strip one path (usually the pwd) from the start of another */
316 path_strip(char *path, char *strip)
321 return (xstrdup(path));
324 if (strncmp(path, strip, len) == 0) {
325 if (strip[len - 1] != '/' && path[len] == '/')
327 return (xstrdup(path + len));
330 return (xstrdup(path));
334 make_absolute(char *p, char *pwd)
339 if (p && p[0] != '/') {
340 abs_str = path_append(pwd, p);
348 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
351 extern int opterr, optind, optopt, optreset;
354 optind = optreset = 1;
358 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
369 error("%s: Invalid flag -%c", cmd, optopt);
378 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
380 extern int opterr, optind, optopt, optreset;
383 optind = optreset = 1;
387 while ((ch = getopt(argc, argv, "s")) != -1) {
393 error("%s: Invalid flag -%c", cmd, optopt);
402 parse_ls_flags(char **argv, int argc, int *lflag)
404 extern int opterr, optind, optopt, optreset;
407 optind = optreset = 1;
410 *lflag = LS_NAME_SORT;
411 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
414 *lflag &= ~VIEW_FLAGS;
415 *lflag |= LS_SHORT_VIEW;
418 *lflag &= ~SORT_FLAGS;
419 *lflag |= LS_SIZE_SORT;
422 *lflag |= LS_SHOW_ALL;
425 *lflag &= ~SORT_FLAGS;
428 *lflag |= LS_SI_UNITS;
431 *lflag &= ~LS_SHORT_VIEW;
432 *lflag |= LS_LONG_VIEW;
435 *lflag &= ~LS_SHORT_VIEW;
436 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
439 *lflag |= LS_REVERSE_SORT;
442 *lflag &= ~SORT_FLAGS;
443 *lflag |= LS_TIME_SORT;
446 error("ls: Invalid flag -%c", optopt);
455 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
457 extern int opterr, optind, optopt, optreset;
460 optind = optreset = 1;
464 while ((ch = getopt(argc, argv, "hi")) != -1) {
473 error("%s: Invalid flag -%c", cmd, optopt);
486 /* XXX: report errors? */
487 if (stat(path, &sb) == -1)
490 return(S_ISDIR(sb.st_mode));
494 remote_is_dir(struct sftp_conn *conn, char *path)
498 /* XXX: report errors? */
499 if ((a = do_stat(conn, path, 1)) == NULL)
501 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
503 return(S_ISDIR(a->perm));
506 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
508 pathname_is_dir(char *pathname)
510 size_t l = strlen(pathname);
512 return l > 0 && pathname[l - 1] == '/';
516 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
517 int pflag, int rflag)
519 char *abs_src = NULL;
520 char *abs_dst = NULL;
522 char *filename, *tmp=NULL;
525 abs_src = xstrdup(src);
526 abs_src = make_absolute(abs_src, pwd);
527 memset(&g, 0, sizeof(g));
529 debug3("Looking up %s", abs_src);
530 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
531 error("File \"%s\" not found.", abs_src);
537 * If multiple matches then dst must be a directory or
540 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
541 error("Multiple source paths, but destination "
542 "\"%s\" is not a directory", dst);
547 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
548 tmp = xstrdup(g.gl_pathv[i]);
549 if ((filename = basename(tmp)) == NULL) {
550 error("basename %s: %s", tmp, strerror(errno));
556 if (g.gl_matchc == 1 && dst) {
558 abs_dst = path_append(dst, filename);
560 abs_dst = xstrdup(dst);
563 abs_dst = path_append(dst, filename);
565 abs_dst = xstrdup(filename);
569 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
570 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
571 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
572 pflag || global_pflag, 1) == -1)
575 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
576 pflag || global_pflag) == -1)
590 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
591 int pflag, int rflag)
593 char *tmp_dst = NULL;
594 char *abs_dst = NULL;
595 char *tmp = NULL, *filename = NULL;
598 int i, dst_is_dir = 1;
602 tmp_dst = xstrdup(dst);
603 tmp_dst = make_absolute(tmp_dst, pwd);
606 memset(&g, 0, sizeof(g));
607 debug3("Looking up %s", src);
608 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
609 error("File \"%s\" not found.", src);
614 /* If we aren't fetching to pwd then stash this status for later */
616 dst_is_dir = remote_is_dir(conn, tmp_dst);
618 /* If multiple matches, dst may be directory or unspecified */
619 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
620 error("Multiple paths match, but destination "
621 "\"%s\" is not a directory", tmp_dst);
626 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
627 if (stat(g.gl_pathv[i], &sb) == -1) {
629 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
633 tmp = xstrdup(g.gl_pathv[i]);
634 if ((filename = basename(tmp)) == NULL) {
635 error("basename %s: %s", tmp, strerror(errno));
641 if (g.gl_matchc == 1 && tmp_dst) {
642 /* If directory specified, append filename */
644 abs_dst = path_append(tmp_dst, filename);
646 abs_dst = xstrdup(tmp_dst);
647 } else if (tmp_dst) {
648 abs_dst = path_append(tmp_dst, filename);
650 abs_dst = make_absolute(xstrdup(filename), pwd);
654 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
655 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
656 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
657 pflag || global_pflag, 1) == -1)
660 if (do_upload(conn, g.gl_pathv[i], abs_dst,
661 pflag || global_pflag) == -1)
676 sdirent_comp(const void *aa, const void *bb)
678 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
679 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
680 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
682 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
683 if (sort_flag & LS_NAME_SORT)
684 return (rmul * strcmp(a->filename, b->filename));
685 else if (sort_flag & LS_TIME_SORT)
686 return (rmul * NCMP(a->a.mtime, b->a.mtime));
687 else if (sort_flag & LS_SIZE_SORT)
688 return (rmul * NCMP(a->a.size, b->a.size));
690 fatal("Unknown ls sort type");
693 /* sftp ls.1 replacement for directories */
695 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
698 u_int c = 1, colspace = 0, columns = 1;
701 if ((n = do_readdir(conn, path, &d)) != 0)
704 if (!(lflag & LS_SHORT_VIEW)) {
705 u_int m = 0, width = 80;
709 /* Count entries for sort and find longest filename */
710 for (n = 0; d[n] != NULL; n++) {
711 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
712 m = MAX(m, strlen(d[n]->filename));
715 /* Add any subpath that also needs to be counted */
716 tmp = path_strip(path, strip_path);
720 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
723 columns = width / (m + 2);
724 columns = MAX(columns, 1);
725 colspace = width / columns;
726 colspace = MIN(colspace, width);
729 if (lflag & SORT_FLAGS) {
730 for (n = 0; d[n] != NULL; n++)
731 ; /* count entries */
732 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
733 qsort(d, n, sizeof(*d), sdirent_comp);
736 for (n = 0; d[n] != NULL && !interrupted; n++) {
739 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
742 tmp = path_append(path, d[n]->filename);
743 fname = path_strip(tmp, strip_path);
746 if (lflag & LS_LONG_VIEW) {
747 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
751 memset(&sb, 0, sizeof(sb));
752 attrib_to_stat(&d[n]->a, &sb);
753 lname = ls_file(fname, &sb, 1,
754 (lflag & LS_SI_UNITS));
755 printf("%s\n", lname);
758 printf("%s\n", d[n]->longname);
760 printf("%-*s", colspace, fname);
771 if (!(lflag & LS_LONG_VIEW) && (c != 1))
774 free_sftp_dirents(d);
778 /* sftp ls.1 replacement which handles path globs */
780 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
787 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
789 memset(&g, 0, sizeof(g));
791 if (remote_glob(conn, path,
792 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
794 (g.gl_pathc && !g.gl_matchc)) {
797 error("Can't ls: \"%s\" not found", path);
805 * If the glob returns a single match and it is a directory,
806 * then just list its contents.
808 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
809 S_ISDIR(g.gl_statv[0]->st_mode)) {
810 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
815 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
818 if (!(lflag & LS_SHORT_VIEW)) {
819 /* Count entries for sort and find longest filename */
820 for (i = 0; g.gl_pathv[i]; i++)
821 m = MAX(m, strlen(g.gl_pathv[i]));
823 columns = width / (m + 2);
824 columns = MAX(columns, 1);
825 colspace = width / columns;
828 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
829 fname = path_strip(g.gl_pathv[i], strip_path);
830 if (lflag & LS_LONG_VIEW) {
831 if (g.gl_statv[i] == NULL) {
832 error("no stat information for %s", fname);
835 lname = ls_file(fname, g.gl_statv[i], 1,
836 (lflag & LS_SI_UNITS));
837 printf("%s\n", lname);
840 printf("%-*s", colspace, fname);
850 if (!(lflag & LS_LONG_VIEW) && (c != 1))
861 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
863 struct sftp_statvfs st;
864 char s_used[FMT_SCALED_STRSIZE];
865 char s_avail[FMT_SCALED_STRSIZE];
866 char s_root[FMT_SCALED_STRSIZE];
867 char s_total[FMT_SCALED_STRSIZE];
868 unsigned long long ffree;
870 if (do_statvfs(conn, path, &st, 1) == -1)
873 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
874 printf(" Inodes Used Avail "
875 "(root) %%Capacity\n");
876 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
877 (unsigned long long)st.f_files,
878 (unsigned long long)(st.f_files - st.f_ffree),
879 (unsigned long long)st.f_favail,
880 (unsigned long long)st.f_ffree, ffree);
882 strlcpy(s_used, "error", sizeof(s_used));
883 strlcpy(s_avail, "error", sizeof(s_avail));
884 strlcpy(s_root, "error", sizeof(s_root));
885 strlcpy(s_total, "error", sizeof(s_total));
886 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
887 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
888 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
889 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
890 printf(" Size Used Avail (root) %%Capacity\n");
891 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
892 s_total, s_used, s_avail, s_root,
893 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
896 printf(" Size Used Avail "
897 "(root) %%Capacity\n");
898 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
899 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
900 (unsigned long long)(st.f_frsize *
901 (st.f_blocks - st.f_bfree) / 1024),
902 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
903 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
904 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
911 * Undo escaping of glob sequences in place. Used to undo extra escaping
912 * applied in makeargv() when the string is destined for a function that
916 undo_glob_escape(char *s)
951 * Split a string into an argument vector using sh(1)-style quoting,
952 * comment and escaping rules, but with some tweaks to handle glob(3)
954 * The "sloppy" flag allows for recovery from missing terminating quote, for
955 * use in parsing incomplete commandlines during tab autocompletion.
957 * Returns NULL on error or a NULL-terminated array of arguments.
959 * If "lastquote" is not NULL, the quoting character used for the last
960 * argument is placed in *lastquote ("\0", "'" or "\"").
962 * If "terminated" is not NULL, *terminated will be set to 1 when the
963 * last argument's quote has been properly terminated or 0 otherwise.
964 * This parameter is only of use if "sloppy" is set.
967 #define MAXARGLEN 8192
969 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
974 static char argvs[MAXARGLEN];
975 static char *argv[MAXARGS + 1];
976 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
979 if (strlen(arg) > sizeof(argvs) - 1) {
981 error("string too long");
984 if (terminated != NULL)
986 if (lastquote != NULL)
991 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
992 error("Too many arguments.");
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 (argv[0] != NULL && 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);
1332 path1 = make_absolute(path1, *pwd);
1333 path2 = make_absolute(path2, *pwd);
1334 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1337 path1 = make_absolute(path1, *pwd);
1338 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1339 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1340 printf("Removing %s\n", g.gl_pathv[i]);
1341 err = do_rm(conn, g.gl_pathv[i]);
1342 if (err != 0 && err_abort)
1347 path1 = make_absolute(path1, *pwd);
1349 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1351 err = do_mkdir(conn, path1, &a, 1);
1354 path1 = make_absolute(path1, *pwd);
1355 err = do_rmdir(conn, path1);
1358 path1 = make_absolute(path1, *pwd);
1359 if ((tmp = do_realpath(conn, path1)) == NULL) {
1363 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1368 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1369 error("Can't change directory: Can't check target");
1374 if (!S_ISDIR(aa->perm)) {
1375 error("Can't change directory: \"%s\" is not "
1376 "a directory", tmp);
1386 do_ls_dir(conn, *pwd, *pwd, lflag);
1390 /* Strip pwd off beginning of non-absolute paths */
1395 path1 = make_absolute(path1, *pwd);
1396 err = do_globbed_ls(conn, path1, tmp, lflag);
1399 /* Default to current directory if no path specified */
1401 path1 = xstrdup(*pwd);
1402 path1 = make_absolute(path1, *pwd);
1403 err = do_df(conn, path1, hflag, iflag);
1406 if (chdir(path1) == -1) {
1407 error("Couldn't change local directory to "
1408 "\"%s\": %s", path1, strerror(errno));
1413 if (mkdir(path1, 0777) == -1) {
1414 error("Couldn't create local directory "
1415 "\"%s\": %s", path1, strerror(errno));
1423 local_do_shell(cmd);
1427 printf("Local umask: %03lo\n", n_arg);
1430 path1 = make_absolute(path1, *pwd);
1432 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1434 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1435 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1436 printf("Changing mode on %s\n", g.gl_pathv[i]);
1437 err = do_setstat(conn, g.gl_pathv[i], &a);
1438 if (err != 0 && err_abort)
1444 path1 = make_absolute(path1, *pwd);
1445 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1446 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1447 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1454 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1455 error("Can't get current ownership of "
1456 "remote file \"%s\"", g.gl_pathv[i]);
1463 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1464 if (cmdnum == I_CHOWN) {
1465 printf("Changing owner on %s\n", g.gl_pathv[i]);
1468 printf("Changing group on %s\n", g.gl_pathv[i]);
1471 err = do_setstat(conn, g.gl_pathv[i], aa);
1472 if (err != 0 && err_abort)
1477 printf("Remote working directory: %s\n", *pwd);
1480 if (!getcwd(path_buf, sizeof(path_buf))) {
1481 error("Couldn't get local cwd: %s", strerror(errno));
1485 printf("Local working directory: %s\n", path_buf);
1488 /* Processed below */
1494 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1497 showprogress = !showprogress;
1499 printf("Progress meter enabled\n");
1501 printf("Progress meter disabled\n");
1504 fatal("%d is not implemented", cmdnum);
1514 /* If an unignored error occurs in batch mode we should abort. */
1515 if (err_abort && err != 0)
1517 else if (cmdnum == I_QUIT)
1525 prompt(EditLine *el)
1530 /* Display entries in 'list' after skipping the first 'len' chars */
1532 complete_display(char **list, u_int len)
1534 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1538 /* Count entries for sort and find longest */
1539 for (y = 0; list[y]; y++)
1540 m = MAX(m, strlen(list[y]));
1542 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1545 m = m > len ? m - len : 0;
1546 columns = width / (m + 2);
1547 columns = MAX(columns, 1);
1548 colspace = width / columns;
1549 colspace = MIN(colspace, width);
1553 for (y = 0; list[y]; y++) {
1554 llen = strlen(list[y]);
1555 tmp = llen > len ? list[y] + len : "";
1556 printf("%-*s", colspace, tmp);
1567 * Given a "list" of words that begin with a common prefix of "word",
1568 * attempt to find an autocompletion to extends "word" by the next
1569 * characters common to all entries in "list".
1572 complete_ambiguous(const char *word, char **list, size_t count)
1578 u_int y, matchlen = strlen(list[0]);
1580 /* Find length of common stem */
1581 for (y = 1; list[y]; y++) {
1584 for (x = 0; x < matchlen; x++)
1585 if (list[0][x] != list[y][x])
1591 if (matchlen > strlen(word)) {
1592 char *tmp = xstrdup(list[0]);
1594 tmp[matchlen] = '\0';
1599 return xstrdup(word);
1602 /* Autocomplete a sftp command */
1604 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1607 u_int y, count = 0, cmdlen, tmplen;
1608 char *tmp, **list, argterm[3];
1611 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1613 /* No command specified: display all available commands */
1615 for (y = 0; cmds[y].c; y++)
1616 list[count++] = xstrdup(cmds[y].c);
1619 complete_display(list, 0);
1621 for (y = 0; list[y] != NULL; y++)
1627 /* Prepare subset of commands that start with "cmd" */
1628 cmdlen = strlen(cmd);
1629 for (y = 0; cmds[y].c; y++) {
1630 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1631 list[count++] = xstrdup(cmds[y].c);
1640 /* Complete ambigious command */
1641 tmp = complete_ambiguous(cmd, list, count);
1643 complete_display(list, 0);
1645 for (y = 0; list[y]; y++)
1650 tmplen = strlen(tmp);
1651 cmdlen = strlen(cmd);
1652 /* If cmd may be extended then do so */
1653 if (tmplen > cmdlen)
1654 if (el_insertstr(el, tmp + cmdlen) == -1)
1655 fatal("el_insertstr failed.");
1657 /* Terminate argument cleanly */
1661 argterm[y++] = quote;
1662 if (lastarg || *(lf->cursor) != ' ')
1665 if (y > 0 && el_insertstr(el, argterm) == -1)
1666 fatal("el_insertstr failed.");
1675 * Determine whether a particular sftp command's arguments (if any)
1676 * represent local or remote files.
1679 complete_is_remote(char *cmd) {
1685 for (i = 0; cmds[i].c; i++) {
1686 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1693 /* Autocomplete a filename "file" */
1695 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1696 char *file, int remote, int lastarg, char quote, int terminated)
1699 char *tmp, *tmp2, ins[3];
1700 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1703 /* Glob from "file" location */
1707 xasprintf(&tmp, "%s*", file);
1709 /* Check if the path is absolute. */
1710 isabs = tmp[0] == '/';
1712 memset(&g, 0, sizeof(g));
1713 if (remote != LOCAL) {
1714 tmp = make_absolute(tmp, remote_path);
1715 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1717 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1719 /* Determine length of pwd so we can trim completion display */
1720 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1721 /* Terminate counting on first unescaped glob metacharacter */
1722 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1723 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1727 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1729 if (tmp[tmplen] == '/')
1730 pwdlen = tmplen + 1; /* track last seen '/' */
1734 if (g.gl_matchc == 0)
1737 if (g.gl_matchc > 1)
1738 complete_display(g.gl_pathv, pwdlen);
1741 /* Don't try to extend globs */
1742 if (file == NULL || hadglob)
1745 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1746 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1752 tmplen = strlen(tmp);
1753 filelen = strlen(file);
1755 /* Count the number of escaped characters in the input string. */
1757 for (i = 0; i < filelen; i++) {
1758 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1765 if (tmplen > (filelen - cesc)) {
1766 tmp2 = tmp + filelen - cesc;
1768 /* quote argument on way out */
1769 for (i = 0; i < len; i++) {
1782 if (quote == '\0' || tmp2[i] == quote) {
1783 if (el_insertstr(el, ins) == -1)
1784 fatal("el_insertstr "
1790 if (el_insertstr(el, ins + 1) == -1)
1791 fatal("el_insertstr failed.");
1798 if (g.gl_matchc == 1) {
1802 if (*(lf->cursor - 1) != '/' &&
1803 (lastarg || *(lf->cursor) != ' '))
1806 if (i > 0 && el_insertstr(el, ins) == -1)
1807 fatal("el_insertstr failed.");
1816 /* tab-completion hook function, called via libedit */
1817 static unsigned char
1818 complete(EditLine *el, int ch)
1820 char **argv, *line, quote;
1821 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1823 struct complete_ctx *complete_ctx;
1826 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1827 fatal("%s: el_get failed", __func__);
1829 /* Figure out which argument the cursor points to */
1830 cursor = lf->cursor - lf->buffer;
1831 line = (char *)xmalloc(cursor + 1);
1832 memcpy(line, lf->buffer, cursor);
1833 line[cursor] = '\0';
1834 argv = makeargv(line, &carg, 1, "e, &terminated);
1837 /* Get all the arguments on the line */
1838 len = lf->lastchar - lf->buffer;
1839 line = (char *)xmalloc(len + 1);
1840 memcpy(line, lf->buffer, len);
1842 argv = makeargv(line, &argc, 1, NULL, NULL);
1844 /* Ensure cursor is at EOL or a argument boundary */
1845 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1846 line[cursor] != '\n') {
1852 /* Show all available commands */
1853 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1855 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1856 /* Handle the command parsing */
1857 if (complete_cmd_parse(el, argv[0], argc == carg,
1858 quote, terminated) != 0)
1860 } else if (carg >= 1) {
1861 /* Handle file parsing */
1862 int remote = complete_is_remote(argv[0]);
1863 char *filematch = NULL;
1865 if (carg > 1 && line[cursor-1] != ' ')
1866 filematch = argv[carg - 1];
1869 complete_match(el, complete_ctx->conn,
1870 *complete_ctx->remote_pathp, filematch,
1871 remote, carg == argc, quote, terminated) != 0)
1878 #endif /* USE_LIBEDIT */
1881 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1886 int err, interactive;
1887 EditLine *el = NULL;
1891 extern char *__progname;
1892 struct complete_ctx complete_ctx;
1894 if (!batchmode && isatty(STDIN_FILENO)) {
1895 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1896 fatal("Couldn't initialise editline");
1897 if ((hl = history_init()) == NULL)
1898 fatal("Couldn't initialise editline history");
1899 history(hl, &hev, H_SETSIZE, 100);
1900 el_set(el, EL_HIST, history, hl);
1902 el_set(el, EL_PROMPT, prompt);
1903 el_set(el, EL_EDITOR, "emacs");
1904 el_set(el, EL_TERMINAL, NULL);
1905 el_set(el, EL_SIGNAL, 1);
1906 el_source(el, NULL);
1908 /* Tab Completion */
1909 el_set(el, EL_ADDFN, "ftp-complete",
1910 "Context sensitive argument completion", complete);
1911 complete_ctx.conn = conn;
1912 complete_ctx.remote_pathp = &remote_path;
1913 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1914 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1916 #endif /* USE_LIBEDIT */
1918 remote_path = do_realpath(conn, ".");
1919 if (remote_path == NULL)
1922 if (file1 != NULL) {
1923 dir = xstrdup(file1);
1924 dir = make_absolute(dir, remote_path);
1926 if (remote_is_dir(conn, dir) && file2 == NULL) {
1927 printf("Changing to: %s\n", dir);
1928 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1929 if (parse_dispatch_command(conn, cmd,
1930 &remote_path, 1) != 0) {
1937 /* XXX this is wrong wrt quoting */
1939 snprintf(cmd, sizeof cmd, "get %s", dir);
1941 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1944 err = parse_dispatch_command(conn, cmd,
1957 interactive = !batchmode && isatty(STDIN_FILENO);
1962 signal(SIGINT, SIG_IGN);
1967 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1972 if (!interactive) { /* Echo command */
1973 printf("sftp> %s", cmd);
1974 if (strlen(cmd) > 0 &&
1975 cmd[strlen(cmd) - 1] != '\n')
1983 if ((line = el_gets(el, &count)) == NULL ||
1988 history(hl, &hev, H_ENTER, line);
1989 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1990 fprintf(stderr, "Error: input line too long\n");
1993 #endif /* USE_LIBEDIT */
1996 cp = strrchr(cmd, '\n');
2000 /* Handle user interrupts gracefully during commands */
2002 signal(SIGINT, cmd_interrupt);
2004 err = parse_dispatch_command(conn, cmd, &remote_path,
2015 #endif /* USE_LIBEDIT */
2017 /* err == 1 signifies normal "quit" exit */
2018 return (err >= 0 ? 0 : -1);
2022 connect_to_server(char *path, char **args, int *in, int *out)
2027 int pin[2], pout[2];
2029 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2030 fatal("pipe: %s", strerror(errno));
2035 #else /* USE_PIPES */
2038 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2039 fatal("socketpair: %s", strerror(errno));
2040 *in = *out = inout[0];
2041 c_in = c_out = inout[1];
2042 #endif /* USE_PIPES */
2044 if ((sshpid = fork()) == -1)
2045 fatal("fork: %s", strerror(errno));
2046 else if (sshpid == 0) {
2047 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2048 (dup2(c_out, STDOUT_FILENO) == -1)) {
2049 fprintf(stderr, "dup2: %s\n", strerror(errno));
2058 * The underlying ssh is in the same process group, so we must
2059 * ignore SIGINT if we want to gracefully abort commands,
2060 * otherwise the signal will make it to the ssh process and
2061 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2062 * underlying ssh, it must *not* ignore that signal.
2064 signal(SIGINT, SIG_IGN);
2065 signal(SIGTERM, SIG_DFL);
2067 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2071 signal(SIGTERM, killchild);
2072 signal(SIGINT, killchild);
2073 signal(SIGHUP, killchild);
2081 extern char *__progname;
2084 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2085 " [-D sftp_server_path] [-F ssh_config] "
2086 "[-i identity_file] [-l limit]\n"
2087 " [-o ssh_option] [-P port] [-R num_requests] "
2089 " [-s subsystem | sftp_server] host\n"
2090 " %s [user@]host[:file ...]\n"
2091 " %s [user@]host[:dir[/]]\n"
2092 " %s -b batchfile [user@]host\n",
2093 __progname, __progname, __progname, __progname);
2098 main(int argc, char **argv)
2100 int in, out, ch, err;
2101 char *host = NULL, *userhost, *cp, *file2 = NULL;
2102 int debug_level = 0, sshver = 2;
2103 char *file1 = NULL, *sftp_server = NULL;
2104 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2106 LogLevel ll = SYSLOG_LEVEL_INFO;
2109 extern char *optarg;
2110 struct sftp_conn *conn;
2111 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2112 size_t num_requests = DEFAULT_NUM_REQUESTS;
2113 long long limit_kbps = 0;
2115 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2118 __progname = ssh_get_progname(argv[0]);
2119 memset(&args, '\0', sizeof(args));
2121 addargs(&args, "%s", ssh_program);
2122 addargs(&args, "-oForwardX11 no");
2123 addargs(&args, "-oForwardAgent no");
2124 addargs(&args, "-oPermitLocalCommand no");
2125 addargs(&args, "-oClearAllForwardings yes");
2127 ll = SYSLOG_LEVEL_INFO;
2130 while ((ch = getopt(argc, argv,
2131 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2133 /* Passed through to ssh(1) */
2137 addargs(&args, "-%c", ch);
2139 /* Passed through to ssh(1) with argument */
2144 addargs(&args, "-%c", ch);
2145 addargs(&args, "%s", optarg);
2149 addargs(&args, "-%c", ch);
2152 addargs(&args, "-oPort %s", optarg);
2155 if (debug_level < 3) {
2156 addargs(&args, "-v");
2157 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2163 if (sftp_server == NULL)
2164 sftp_server = _PATH_SFTP_SERVER;
2170 copy_buffer_len = strtol(optarg, &cp, 10);
2171 if (copy_buffer_len == 0 || *cp != '\0')
2172 fatal("Invalid buffer size \"%s\"", optarg);
2176 fatal("Batch file already specified.");
2178 /* Allow "-" as stdin */
2179 if (strcmp(optarg, "-") != 0 &&
2180 (infile = fopen(optarg, "r")) == NULL)
2181 fatal("%s (%s).", strerror(errno), optarg);
2184 addargs(&args, "-obatchmode yes");
2190 sftp_direct = optarg;
2193 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2197 limit_kbps *= 1024; /* kbps */
2203 num_requests = strtol(optarg, &cp, 10);
2204 if (num_requests == 0 || *cp != '\0')
2205 fatal("Invalid number of requests \"%s\"",
2209 sftp_server = optarg;
2212 ssh_program = optarg;
2213 replacearg(&args, 0, "%s", ssh_program);
2221 if (!isatty(STDERR_FILENO))
2224 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2226 if (sftp_direct == NULL) {
2227 if (optind == argc || argc > (optind + 2))
2230 userhost = xstrdup(argv[optind]);
2231 file2 = argv[optind+1];
2233 if ((host = strrchr(userhost, '@')) == NULL)
2238 fprintf(stderr, "Missing username\n");
2241 addargs(&args, "-l");
2242 addargs(&args, "%s", userhost);
2245 if ((cp = colon(host)) != NULL) {
2250 host = cleanhostname(host);
2252 fprintf(stderr, "Missing hostname\n");
2256 addargs(&args, "-oProtocol %d", sshver);
2258 /* no subsystem if the server-spec contains a '/' */
2259 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2260 addargs(&args, "-s");
2262 addargs(&args, "--");
2263 addargs(&args, "%s", host);
2264 addargs(&args, "%s", (sftp_server != NULL ?
2265 sftp_server : "sftp"));
2267 connect_to_server(ssh_program, args.list, &in, &out);
2270 addargs(&args, "sftp-server");
2272 connect_to_server(sftp_direct, args.list, &in, &out);
2276 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2278 fatal("Couldn't initialise connection to server");
2281 if (sftp_direct == NULL)
2282 fprintf(stderr, "Connected to %s.\n", host);
2284 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2287 err = interactive_loop(conn, file1, file2);
2289 #if !defined(USE_PIPES)
2290 shutdown(in, SHUT_RDWR);
2291 shutdown(out, SHUT_RDWR);
2299 while (waitpid(sshpid, NULL, 0) == -1)
2301 fatal("Couldn't wait for ssh process: %s",
2304 exit(err == 0 ? 0 : 1);