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);
1331 path1 = make_absolute(path1, *pwd);
1332 path2 = make_absolute(path2, *pwd);
1333 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1336 path1 = make_absolute(path1, *pwd);
1337 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1338 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1339 printf("Removing %s\n", g.gl_pathv[i]);
1340 err = do_rm(conn, g.gl_pathv[i]);
1341 if (err != 0 && err_abort)
1346 path1 = make_absolute(path1, *pwd);
1348 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1350 err = do_mkdir(conn, path1, &a, 1);
1353 path1 = make_absolute(path1, *pwd);
1354 err = do_rmdir(conn, path1);
1357 path1 = make_absolute(path1, *pwd);
1358 if ((tmp = do_realpath(conn, path1)) == NULL) {
1362 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1367 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1368 error("Can't change directory: Can't check target");
1373 if (!S_ISDIR(aa->perm)) {
1374 error("Can't change directory: \"%s\" is not "
1375 "a directory", tmp);
1385 do_ls_dir(conn, *pwd, *pwd, lflag);
1389 /* Strip pwd off beginning of non-absolute paths */
1394 path1 = make_absolute(path1, *pwd);
1395 err = do_globbed_ls(conn, path1, tmp, lflag);
1398 /* Default to current directory if no path specified */
1400 path1 = xstrdup(*pwd);
1401 path1 = make_absolute(path1, *pwd);
1402 err = do_df(conn, path1, hflag, iflag);
1405 if (chdir(path1) == -1) {
1406 error("Couldn't change local directory to "
1407 "\"%s\": %s", path1, strerror(errno));
1412 if (mkdir(path1, 0777) == -1) {
1413 error("Couldn't create local directory "
1414 "\"%s\": %s", path1, strerror(errno));
1422 local_do_shell(cmd);
1426 printf("Local umask: %03lo\n", n_arg);
1429 path1 = make_absolute(path1, *pwd);
1431 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1433 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1434 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1435 printf("Changing mode on %s\n", g.gl_pathv[i]);
1436 err = do_setstat(conn, g.gl_pathv[i], &a);
1437 if (err != 0 && err_abort)
1443 path1 = make_absolute(path1, *pwd);
1444 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1445 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1446 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1453 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1454 error("Can't get current ownership of "
1455 "remote file \"%s\"", g.gl_pathv[i]);
1462 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1463 if (cmdnum == I_CHOWN) {
1464 printf("Changing owner on %s\n", g.gl_pathv[i]);
1467 printf("Changing group on %s\n", g.gl_pathv[i]);
1470 err = do_setstat(conn, g.gl_pathv[i], aa);
1471 if (err != 0 && err_abort)
1476 printf("Remote working directory: %s\n", *pwd);
1479 if (!getcwd(path_buf, sizeof(path_buf))) {
1480 error("Couldn't get local cwd: %s", strerror(errno));
1484 printf("Local working directory: %s\n", path_buf);
1487 /* Processed below */
1493 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1496 showprogress = !showprogress;
1498 printf("Progress meter enabled\n");
1500 printf("Progress meter disabled\n");
1503 fatal("%d is not implemented", cmdnum);
1513 /* If an unignored error occurs in batch mode we should abort. */
1514 if (err_abort && err != 0)
1516 else if (cmdnum == I_QUIT)
1524 prompt(EditLine *el)
1529 /* Display entries in 'list' after skipping the first 'len' chars */
1531 complete_display(char **list, u_int len)
1533 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1537 /* Count entries for sort and find longest */
1538 for (y = 0; list[y]; y++)
1539 m = MAX(m, strlen(list[y]));
1541 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1544 m = m > len ? m - len : 0;
1545 columns = width / (m + 2);
1546 columns = MAX(columns, 1);
1547 colspace = width / columns;
1548 colspace = MIN(colspace, width);
1552 for (y = 0; list[y]; y++) {
1553 llen = strlen(list[y]);
1554 tmp = llen > len ? list[y] + len : "";
1555 printf("%-*s", colspace, tmp);
1566 * Given a "list" of words that begin with a common prefix of "word",
1567 * attempt to find an autocompletion to extends "word" by the next
1568 * characters common to all entries in "list".
1571 complete_ambiguous(const char *word, char **list, size_t count)
1577 u_int y, matchlen = strlen(list[0]);
1579 /* Find length of common stem */
1580 for (y = 1; list[y]; y++) {
1583 for (x = 0; x < matchlen; x++)
1584 if (list[0][x] != list[y][x])
1590 if (matchlen > strlen(word)) {
1591 char *tmp = xstrdup(list[0]);
1593 tmp[matchlen] = '\0';
1598 return xstrdup(word);
1601 /* Autocomplete a sftp command */
1603 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1606 u_int y, count = 0, cmdlen, tmplen;
1607 char *tmp, **list, argterm[3];
1610 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1612 /* No command specified: display all available commands */
1614 for (y = 0; cmds[y].c; y++)
1615 list[count++] = xstrdup(cmds[y].c);
1618 complete_display(list, 0);
1620 for (y = 0; list[y] != NULL; y++)
1626 /* Prepare subset of commands that start with "cmd" */
1627 cmdlen = strlen(cmd);
1628 for (y = 0; cmds[y].c; y++) {
1629 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1630 list[count++] = xstrdup(cmds[y].c);
1639 /* Complete ambigious command */
1640 tmp = complete_ambiguous(cmd, list, count);
1642 complete_display(list, 0);
1644 for (y = 0; list[y]; y++)
1649 tmplen = strlen(tmp);
1650 cmdlen = strlen(cmd);
1651 /* If cmd may be extended then do so */
1652 if (tmplen > cmdlen)
1653 if (el_insertstr(el, tmp + cmdlen) == -1)
1654 fatal("el_insertstr failed.");
1656 /* Terminate argument cleanly */
1660 argterm[y++] = quote;
1661 if (lastarg || *(lf->cursor) != ' ')
1664 if (y > 0 && el_insertstr(el, argterm) == -1)
1665 fatal("el_insertstr failed.");
1674 * Determine whether a particular sftp command's arguments (if any)
1675 * represent local or remote files.
1678 complete_is_remote(char *cmd) {
1684 for (i = 0; cmds[i].c; i++) {
1685 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1692 /* Autocomplete a filename "file" */
1694 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1695 char *file, int remote, int lastarg, char quote, int terminated)
1698 char *tmp, *tmp2, ins[3];
1699 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1702 /* Glob from "file" location */
1706 xasprintf(&tmp, "%s*", file);
1708 /* Check if the path is absolute. */
1709 isabs = tmp[0] == '/';
1711 memset(&g, 0, sizeof(g));
1712 if (remote != LOCAL) {
1713 tmp = make_absolute(tmp, remote_path);
1714 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1716 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1718 /* Determine length of pwd so we can trim completion display */
1719 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1720 /* Terminate counting on first unescaped glob metacharacter */
1721 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1722 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1726 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1728 if (tmp[tmplen] == '/')
1729 pwdlen = tmplen + 1; /* track last seen '/' */
1733 if (g.gl_matchc == 0)
1736 if (g.gl_matchc > 1)
1737 complete_display(g.gl_pathv, pwdlen);
1740 /* Don't try to extend globs */
1741 if (file == NULL || hadglob)
1744 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1745 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1751 tmplen = strlen(tmp);
1752 filelen = strlen(file);
1754 /* Count the number of escaped characters in the input string. */
1756 for (i = 0; i < filelen; i++) {
1757 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1764 if (tmplen > (filelen - cesc)) {
1765 tmp2 = tmp + filelen - cesc;
1767 /* quote argument on way out */
1768 for (i = 0; i < len; i++) {
1781 if (quote == '\0' || tmp2[i] == quote) {
1782 if (el_insertstr(el, ins) == -1)
1783 fatal("el_insertstr "
1789 if (el_insertstr(el, ins + 1) == -1)
1790 fatal("el_insertstr failed.");
1797 if (g.gl_matchc == 1) {
1801 if (*(lf->cursor - 1) != '/' &&
1802 (lastarg || *(lf->cursor) != ' '))
1805 if (i > 0 && el_insertstr(el, ins) == -1)
1806 fatal("el_insertstr failed.");
1815 /* tab-completion hook function, called via libedit */
1816 static unsigned char
1817 complete(EditLine *el, int ch)
1819 char **argv, *line, quote;
1820 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1822 struct complete_ctx *complete_ctx;
1825 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1826 fatal("%s: el_get failed", __func__);
1828 /* Figure out which argument the cursor points to */
1829 cursor = lf->cursor - lf->buffer;
1830 line = (char *)xmalloc(cursor + 1);
1831 memcpy(line, lf->buffer, cursor);
1832 line[cursor] = '\0';
1833 argv = makeargv(line, &carg, 1, "e, &terminated);
1836 /* Get all the arguments on the line */
1837 len = lf->lastchar - lf->buffer;
1838 line = (char *)xmalloc(len + 1);
1839 memcpy(line, lf->buffer, len);
1841 argv = makeargv(line, &argc, 1, NULL, NULL);
1843 /* Ensure cursor is at EOL or a argument boundary */
1844 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1845 line[cursor] != '\n') {
1851 /* Show all available commands */
1852 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1854 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1855 /* Handle the command parsing */
1856 if (complete_cmd_parse(el, argv[0], argc == carg,
1857 quote, terminated) != 0)
1859 } else if (carg >= 1) {
1860 /* Handle file parsing */
1861 int remote = complete_is_remote(argv[0]);
1862 char *filematch = NULL;
1864 if (carg > 1 && line[cursor-1] != ' ')
1865 filematch = argv[carg - 1];
1868 complete_match(el, complete_ctx->conn,
1869 *complete_ctx->remote_pathp, filematch,
1870 remote, carg == argc, quote, terminated) != 0)
1877 #endif /* USE_LIBEDIT */
1880 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1885 int err, interactive;
1886 EditLine *el = NULL;
1890 extern char *__progname;
1891 struct complete_ctx complete_ctx;
1893 if (!batchmode && isatty(STDIN_FILENO)) {
1894 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1895 fatal("Couldn't initialise editline");
1896 if ((hl = history_init()) == NULL)
1897 fatal("Couldn't initialise editline history");
1898 history(hl, &hev, H_SETSIZE, 100);
1899 el_set(el, EL_HIST, history, hl);
1901 el_set(el, EL_PROMPT, prompt);
1902 el_set(el, EL_EDITOR, "emacs");
1903 el_set(el, EL_TERMINAL, NULL);
1904 el_set(el, EL_SIGNAL, 1);
1905 el_source(el, NULL);
1907 /* Tab Completion */
1908 el_set(el, EL_ADDFN, "ftp-complete",
1909 "Context sensitive argument completion", complete);
1910 complete_ctx.conn = conn;
1911 complete_ctx.remote_pathp = &remote_path;
1912 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1913 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1915 #endif /* USE_LIBEDIT */
1917 remote_path = do_realpath(conn, ".");
1918 if (remote_path == NULL)
1921 if (file1 != NULL) {
1922 dir = xstrdup(file1);
1923 dir = make_absolute(dir, remote_path);
1925 if (remote_is_dir(conn, dir) && file2 == NULL) {
1926 printf("Changing to: %s\n", dir);
1927 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1928 if (parse_dispatch_command(conn, cmd,
1929 &remote_path, 1) != 0) {
1936 /* XXX this is wrong wrt quoting */
1938 snprintf(cmd, sizeof cmd, "get %s", dir);
1940 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1943 err = parse_dispatch_command(conn, cmd,
1956 interactive = !batchmode && isatty(STDIN_FILENO);
1961 signal(SIGINT, SIG_IGN);
1966 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1971 if (!interactive) { /* Echo command */
1972 printf("sftp> %s", cmd);
1973 if (strlen(cmd) > 0 &&
1974 cmd[strlen(cmd) - 1] != '\n')
1982 if ((line = el_gets(el, &count)) == NULL ||
1987 history(hl, &hev, H_ENTER, line);
1988 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1989 fprintf(stderr, "Error: input line too long\n");
1992 #endif /* USE_LIBEDIT */
1995 cp = strrchr(cmd, '\n');
1999 /* Handle user interrupts gracefully during commands */
2001 signal(SIGINT, cmd_interrupt);
2003 err = parse_dispatch_command(conn, cmd, &remote_path,
2014 #endif /* USE_LIBEDIT */
2016 /* err == 1 signifies normal "quit" exit */
2017 return (err >= 0 ? 0 : -1);
2021 connect_to_server(char *path, char **args, int *in, int *out)
2026 int pin[2], pout[2];
2028 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2029 fatal("pipe: %s", strerror(errno));
2034 #else /* USE_PIPES */
2037 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2038 fatal("socketpair: %s", strerror(errno));
2039 *in = *out = inout[0];
2040 c_in = c_out = inout[1];
2041 #endif /* USE_PIPES */
2043 if ((sshpid = fork()) == -1)
2044 fatal("fork: %s", strerror(errno));
2045 else if (sshpid == 0) {
2046 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2047 (dup2(c_out, STDOUT_FILENO) == -1)) {
2048 fprintf(stderr, "dup2: %s\n", strerror(errno));
2057 * The underlying ssh is in the same process group, so we must
2058 * ignore SIGINT if we want to gracefully abort commands,
2059 * otherwise the signal will make it to the ssh process and
2060 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2061 * underlying ssh, it must *not* ignore that signal.
2063 signal(SIGINT, SIG_IGN);
2064 signal(SIGTERM, SIG_DFL);
2066 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2070 signal(SIGTERM, killchild);
2071 signal(SIGINT, killchild);
2072 signal(SIGHUP, killchild);
2080 extern char *__progname;
2083 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2084 " [-D sftp_server_path] [-F ssh_config] "
2085 "[-i identity_file] [-l limit]\n"
2086 " [-o ssh_option] [-P port] [-R num_requests] "
2088 " [-s subsystem | sftp_server] host\n"
2089 " %s [user@]host[:file ...]\n"
2090 " %s [user@]host[:dir[/]]\n"
2091 " %s -b batchfile [user@]host\n",
2092 __progname, __progname, __progname, __progname);
2097 main(int argc, char **argv)
2099 int in, out, ch, err;
2100 char *host = NULL, *userhost, *cp, *file2 = NULL;
2101 int debug_level = 0, sshver = 2;
2102 char *file1 = NULL, *sftp_server = NULL;
2103 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2105 LogLevel ll = SYSLOG_LEVEL_INFO;
2108 extern char *optarg;
2109 struct sftp_conn *conn;
2110 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2111 size_t num_requests = DEFAULT_NUM_REQUESTS;
2112 long long limit_kbps = 0;
2114 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2117 __progname = ssh_get_progname(argv[0]);
2118 memset(&args, '\0', sizeof(args));
2120 addargs(&args, "%s", ssh_program);
2121 addargs(&args, "-oForwardX11 no");
2122 addargs(&args, "-oForwardAgent no");
2123 addargs(&args, "-oPermitLocalCommand no");
2124 addargs(&args, "-oClearAllForwardings yes");
2126 ll = SYSLOG_LEVEL_INFO;
2129 while ((ch = getopt(argc, argv,
2130 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2132 /* Passed through to ssh(1) */
2136 addargs(&args, "-%c", ch);
2138 /* Passed through to ssh(1) with argument */
2143 addargs(&args, "-%c", ch);
2144 addargs(&args, "%s", optarg);
2148 addargs(&args, "-%c", ch);
2151 addargs(&args, "-oPort %s", optarg);
2154 if (debug_level < 3) {
2155 addargs(&args, "-v");
2156 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2162 if (sftp_server == NULL)
2163 sftp_server = _PATH_SFTP_SERVER;
2169 copy_buffer_len = strtol(optarg, &cp, 10);
2170 if (copy_buffer_len == 0 || *cp != '\0')
2171 fatal("Invalid buffer size \"%s\"", optarg);
2175 fatal("Batch file already specified.");
2177 /* Allow "-" as stdin */
2178 if (strcmp(optarg, "-") != 0 &&
2179 (infile = fopen(optarg, "r")) == NULL)
2180 fatal("%s (%s).", strerror(errno), optarg);
2183 addargs(&args, "-obatchmode yes");
2189 sftp_direct = optarg;
2192 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2196 limit_kbps *= 1024; /* kbps */
2202 num_requests = strtol(optarg, &cp, 10);
2203 if (num_requests == 0 || *cp != '\0')
2204 fatal("Invalid number of requests \"%s\"",
2208 sftp_server = optarg;
2211 ssh_program = optarg;
2212 replacearg(&args, 0, "%s", ssh_program);
2220 if (!isatty(STDERR_FILENO))
2223 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2225 if (sftp_direct == NULL) {
2226 if (optind == argc || argc > (optind + 2))
2229 userhost = xstrdup(argv[optind]);
2230 file2 = argv[optind+1];
2232 if ((host = strrchr(userhost, '@')) == NULL)
2237 fprintf(stderr, "Missing username\n");
2240 addargs(&args, "-l");
2241 addargs(&args, "%s", userhost);
2244 if ((cp = colon(host)) != NULL) {
2249 host = cleanhostname(host);
2251 fprintf(stderr, "Missing hostname\n");
2255 addargs(&args, "-oProtocol %d", sshver);
2257 /* no subsystem if the server-spec contains a '/' */
2258 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2259 addargs(&args, "-s");
2261 addargs(&args, "--");
2262 addargs(&args, "%s", host);
2263 addargs(&args, "%s", (sftp_server != NULL ?
2264 sftp_server : "sftp"));
2266 connect_to_server(ssh_program, args.list, &in, &out);
2269 addargs(&args, "sftp-server");
2271 connect_to_server(sftp_direct, args.list, &in, &out);
2275 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2277 fatal("Couldn't initialise connection to server");
2280 if (sftp_direct == NULL)
2281 fprintf(stderr, "Connected to %s.\n", host);
2283 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2286 err = interactive_loop(conn, file1, file2);
2288 #if !defined(USE_PIPES)
2289 shutdown(in, SHUT_RDWR);
2290 shutdown(out, SHUT_RDWR);
2298 while (waitpid(sshpid, NULL, 0) == -1)
2300 fatal("Couldn't wait for ssh process: %s",
2303 exit(err == 0 ? 0 : 1);