1 /* $OpenBSD: sftp.c,v 1.142 2013/02/08 00:41:12 djm Exp $ */
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
25 #include <sys/param.h>
26 #include <sys/socket.h>
28 #ifdef HAVE_SYS_STATVFS_H
29 #include <sys/statvfs.h>
44 typedef void EditLine;
59 #include "pathnames.h"
64 #include "sftp-common.h"
65 #include "sftp-client.h"
67 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
68 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
70 /* File to read commands from */
73 /* Are we in batchfile mode? */
76 /* PID of ssh transport process */
77 static pid_t sshpid = -1;
79 /* This is set to 0 if the progressmeter is not desired. */
82 /* When this option is set, we always recursively download/upload directories */
85 /* When this option is set, the file transfers will always preserve times */
88 /* SIGINT received during command processing */
89 volatile sig_atomic_t interrupted = 0;
91 /* I wish qsort() took a separate ctx for the comparison function...*/
94 /* Context used for commandline completion */
96 struct sftp_conn *conn;
100 int remote_glob(struct sftp_conn *, const char *, int,
101 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
103 extern char *__progname;
105 /* Separators for interactive commands */
106 #define WHITESPACE " \t\r\n"
109 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
110 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
111 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
112 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
113 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
114 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
115 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
116 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
117 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
119 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
120 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
122 /* Commands for interactive mode */
147 #define I_PROGRESS 23
155 /* Type of completion */
160 static const struct CMD cmds[] = {
161 { "bye", I_QUIT, NOARGS },
162 { "cd", I_CHDIR, REMOTE },
163 { "chdir", I_CHDIR, REMOTE },
164 { "chgrp", I_CHGRP, REMOTE },
165 { "chmod", I_CHMOD, REMOTE },
166 { "chown", I_CHOWN, REMOTE },
167 { "df", I_DF, REMOTE },
168 { "dir", I_LS, REMOTE },
169 { "exit", I_QUIT, NOARGS },
170 { "get", I_GET, REMOTE },
171 { "help", I_HELP, NOARGS },
172 { "lcd", I_LCHDIR, LOCAL },
173 { "lchdir", I_LCHDIR, LOCAL },
174 { "lls", I_LLS, LOCAL },
175 { "lmkdir", I_LMKDIR, LOCAL },
176 { "ln", I_LINK, REMOTE },
177 { "lpwd", I_LPWD, LOCAL },
178 { "ls", I_LS, REMOTE },
179 { "lumask", I_LUMASK, NOARGS },
180 { "mkdir", I_MKDIR, REMOTE },
181 { "mget", I_GET, REMOTE },
182 { "mput", I_PUT, LOCAL },
183 { "progress", I_PROGRESS, NOARGS },
184 { "put", I_PUT, LOCAL },
185 { "pwd", I_PWD, REMOTE },
186 { "quit", I_QUIT, NOARGS },
187 { "rename", I_RENAME, REMOTE },
188 { "rm", I_RM, REMOTE },
189 { "rmdir", I_RMDIR, REMOTE },
190 { "symlink", I_SYMLINK, REMOTE },
191 { "version", I_VERSION, NOARGS },
192 { "!", I_SHELL, NOARGS },
193 { "?", I_HELP, NOARGS },
197 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
204 kill(sshpid, SIGTERM);
205 waitpid(sshpid, NULL, 0);
213 cmd_interrupt(int signo)
215 const char msg[] = "\rInterrupt \n";
216 int olderrno = errno;
218 write(STDERR_FILENO, msg, sizeof(msg) - 1);
226 printf("Available commands:\n"
228 "cd path Change remote directory to 'path'\n"
229 "chgrp grp path Change group of file 'path' to 'grp'\n"
230 "chmod mode path Change permissions of file 'path' to 'mode'\n"
231 "chown own path Change owner of file 'path' to 'own'\n"
232 "df [-hi] [path] Display statistics for current directory or\n"
233 " filesystem containing 'path'\n"
235 "get [-Ppr] remote [local] Download file\n"
236 "help Display this help text\n"
237 "lcd path Change local directory to 'path'\n"
238 "lls [ls-options [path]] Display local directory listing\n"
239 "lmkdir path Create local directory\n"
240 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
241 "lpwd Print local working directory\n"
242 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
243 "lumask umask Set local umask to 'umask'\n"
244 "mkdir path Create remote directory\n"
245 "progress Toggle display of progress meter\n"
246 "put [-Ppr] local [remote] Upload file\n"
247 "pwd Display remote working directory\n"
249 "rename oldpath newpath Rename remote file\n"
250 "rm path Delete remote file\n"
251 "rmdir path Remove remote directory\n"
252 "symlink oldpath newpath Symlink remote file\n"
253 "version Show SFTP version\n"
254 "!command Execute 'command' in local shell\n"
255 "! Escape to local shell\n"
256 "? Synonym for help\n");
260 local_do_shell(const char *args)
269 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
270 shell = _PATH_BSHELL;
272 if ((pid = fork()) == -1)
273 fatal("Couldn't fork: %s", strerror(errno));
276 /* XXX: child has pipe fds to ssh subproc open - issue? */
278 debug3("Executing %s -c \"%s\"", shell, args);
279 execl(shell, shell, "-c", args, (char *)NULL);
281 debug3("Executing %s", shell);
282 execl(shell, shell, (char *)NULL);
284 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
288 while (waitpid(pid, &status, 0) == -1)
290 fatal("Couldn't wait for child: %s", strerror(errno));
291 if (!WIFEXITED(status))
292 error("Shell exited abnormally");
293 else if (WEXITSTATUS(status))
294 error("Shell exited with status %d", WEXITSTATUS(status));
298 local_do_ls(const char *args)
301 local_do_shell(_PATH_LS);
303 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
304 char *buf = xmalloc(len);
306 /* XXX: quoting - rip quoting code from ftp? */
307 snprintf(buf, len, _PATH_LS " %s", args);
313 /* Strip one path (usually the pwd) from the start of another */
315 path_strip(char *path, char *strip)
320 return (xstrdup(path));
323 if (strncmp(path, strip, len) == 0) {
324 if (strip[len - 1] != '/' && path[len] == '/')
326 return (xstrdup(path + len));
329 return (xstrdup(path));
333 make_absolute(char *p, char *pwd)
338 if (p && p[0] != '/') {
339 abs_str = path_append(pwd, p);
347 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
350 extern int opterr, optind, optopt, optreset;
353 optind = optreset = 1;
357 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
368 error("%s: Invalid flag -%c", cmd, optopt);
377 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
379 extern int opterr, optind, optopt, optreset;
382 optind = optreset = 1;
386 while ((ch = getopt(argc, argv, "s")) != -1) {
392 error("%s: Invalid flag -%c", cmd, optopt);
401 parse_ls_flags(char **argv, int argc, int *lflag)
403 extern int opterr, optind, optopt, optreset;
406 optind = optreset = 1;
409 *lflag = LS_NAME_SORT;
410 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
413 *lflag &= ~VIEW_FLAGS;
414 *lflag |= LS_SHORT_VIEW;
417 *lflag &= ~SORT_FLAGS;
418 *lflag |= LS_SIZE_SORT;
421 *lflag |= LS_SHOW_ALL;
424 *lflag &= ~SORT_FLAGS;
427 *lflag |= LS_SI_UNITS;
430 *lflag &= ~LS_SHORT_VIEW;
431 *lflag |= LS_LONG_VIEW;
434 *lflag &= ~LS_SHORT_VIEW;
435 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
438 *lflag |= LS_REVERSE_SORT;
441 *lflag &= ~SORT_FLAGS;
442 *lflag |= LS_TIME_SORT;
445 error("ls: Invalid flag -%c", optopt);
454 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
456 extern int opterr, optind, optopt, optreset;
459 optind = optreset = 1;
463 while ((ch = getopt(argc, argv, "hi")) != -1) {
472 error("%s: Invalid flag -%c", cmd, optopt);
485 /* XXX: report errors? */
486 if (stat(path, &sb) == -1)
489 return(S_ISDIR(sb.st_mode));
493 remote_is_dir(struct sftp_conn *conn, char *path)
497 /* XXX: report errors? */
498 if ((a = do_stat(conn, path, 1)) == NULL)
500 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
502 return(S_ISDIR(a->perm));
505 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
507 pathname_is_dir(char *pathname)
509 size_t l = strlen(pathname);
511 return l > 0 && pathname[l - 1] == '/';
515 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
516 int pflag, int rflag)
518 char *abs_src = NULL;
519 char *abs_dst = NULL;
521 char *filename, *tmp=NULL;
524 abs_src = xstrdup(src);
525 abs_src = make_absolute(abs_src, pwd);
526 memset(&g, 0, sizeof(g));
528 debug3("Looking up %s", abs_src);
529 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
530 error("File \"%s\" not found.", abs_src);
536 * If multiple matches then dst must be a directory or
539 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
540 error("Multiple source paths, but destination "
541 "\"%s\" is not a directory", dst);
546 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
547 tmp = xstrdup(g.gl_pathv[i]);
548 if ((filename = basename(tmp)) == NULL) {
549 error("basename %s: %s", tmp, strerror(errno));
555 if (g.gl_matchc == 1 && dst) {
557 abs_dst = path_append(dst, filename);
559 abs_dst = xstrdup(dst);
562 abs_dst = path_append(dst, filename);
564 abs_dst = xstrdup(filename);
568 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
569 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
570 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
571 pflag || global_pflag, 1) == -1)
574 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
575 pflag || global_pflag) == -1)
589 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
590 int pflag, int rflag)
592 char *tmp_dst = NULL;
593 char *abs_dst = NULL;
594 char *tmp = NULL, *filename = NULL;
597 int i, dst_is_dir = 1;
601 tmp_dst = xstrdup(dst);
602 tmp_dst = make_absolute(tmp_dst, pwd);
605 memset(&g, 0, sizeof(g));
606 debug3("Looking up %s", src);
607 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
608 error("File \"%s\" not found.", src);
613 /* If we aren't fetching to pwd then stash this status for later */
615 dst_is_dir = remote_is_dir(conn, tmp_dst);
617 /* If multiple matches, dst may be directory or unspecified */
618 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
619 error("Multiple paths match, but destination "
620 "\"%s\" is not a directory", tmp_dst);
625 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
626 if (stat(g.gl_pathv[i], &sb) == -1) {
628 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
632 tmp = xstrdup(g.gl_pathv[i]);
633 if ((filename = basename(tmp)) == NULL) {
634 error("basename %s: %s", tmp, strerror(errno));
640 if (g.gl_matchc == 1 && tmp_dst) {
641 /* If directory specified, append filename */
643 abs_dst = path_append(tmp_dst, filename);
645 abs_dst = xstrdup(tmp_dst);
646 } else if (tmp_dst) {
647 abs_dst = path_append(tmp_dst, filename);
649 abs_dst = make_absolute(xstrdup(filename), pwd);
653 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
654 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
655 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
656 pflag || global_pflag, 1) == -1)
659 if (do_upload(conn, g.gl_pathv[i], abs_dst,
660 pflag || global_pflag) == -1)
675 sdirent_comp(const void *aa, const void *bb)
677 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
678 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
679 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
681 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
682 if (sort_flag & LS_NAME_SORT)
683 return (rmul * strcmp(a->filename, b->filename));
684 else if (sort_flag & LS_TIME_SORT)
685 return (rmul * NCMP(a->a.mtime, b->a.mtime));
686 else if (sort_flag & LS_SIZE_SORT)
687 return (rmul * NCMP(a->a.size, b->a.size));
689 fatal("Unknown ls sort type");
692 /* sftp ls.1 replacement for directories */
694 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
697 u_int c = 1, colspace = 0, columns = 1;
700 if ((n = do_readdir(conn, path, &d)) != 0)
703 if (!(lflag & LS_SHORT_VIEW)) {
704 u_int m = 0, width = 80;
708 /* Count entries for sort and find longest filename */
709 for (n = 0; d[n] != NULL; n++) {
710 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
711 m = MAX(m, strlen(d[n]->filename));
714 /* Add any subpath that also needs to be counted */
715 tmp = path_strip(path, strip_path);
719 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
722 columns = width / (m + 2);
723 columns = MAX(columns, 1);
724 colspace = width / columns;
725 colspace = MIN(colspace, width);
728 if (lflag & SORT_FLAGS) {
729 for (n = 0; d[n] != NULL; n++)
730 ; /* count entries */
731 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
732 qsort(d, n, sizeof(*d), sdirent_comp);
735 for (n = 0; d[n] != NULL && !interrupted; n++) {
738 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
741 tmp = path_append(path, d[n]->filename);
742 fname = path_strip(tmp, strip_path);
745 if (lflag & LS_LONG_VIEW) {
746 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
750 memset(&sb, 0, sizeof(sb));
751 attrib_to_stat(&d[n]->a, &sb);
752 lname = ls_file(fname, &sb, 1,
753 (lflag & LS_SI_UNITS));
754 printf("%s\n", lname);
757 printf("%s\n", d[n]->longname);
759 printf("%-*s", colspace, fname);
770 if (!(lflag & LS_LONG_VIEW) && (c != 1))
773 free_sftp_dirents(d);
777 /* sftp ls.1 replacement which handles path globs */
779 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
786 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
788 memset(&g, 0, sizeof(g));
790 if (remote_glob(conn, path,
791 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
793 (g.gl_pathc && !g.gl_matchc)) {
796 error("Can't ls: \"%s\" not found", path);
804 * If the glob returns a single match and it is a directory,
805 * then just list its contents.
807 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
808 S_ISDIR(g.gl_statv[0]->st_mode)) {
809 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
814 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
817 if (!(lflag & LS_SHORT_VIEW)) {
818 /* Count entries for sort and find longest filename */
819 for (i = 0; g.gl_pathv[i]; i++)
820 m = MAX(m, strlen(g.gl_pathv[i]));
822 columns = width / (m + 2);
823 columns = MAX(columns, 1);
824 colspace = width / columns;
827 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
828 fname = path_strip(g.gl_pathv[i], strip_path);
829 if (lflag & LS_LONG_VIEW) {
830 if (g.gl_statv[i] == NULL) {
831 error("no stat information for %s", fname);
834 lname = ls_file(fname, g.gl_statv[i], 1,
835 (lflag & LS_SI_UNITS));
836 printf("%s\n", lname);
839 printf("%-*s", colspace, fname);
849 if (!(lflag & LS_LONG_VIEW) && (c != 1))
860 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
862 struct sftp_statvfs st;
863 char s_used[FMT_SCALED_STRSIZE];
864 char s_avail[FMT_SCALED_STRSIZE];
865 char s_root[FMT_SCALED_STRSIZE];
866 char s_total[FMT_SCALED_STRSIZE];
867 unsigned long long ffree;
869 if (do_statvfs(conn, path, &st, 1) == -1)
872 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
873 printf(" Inodes Used Avail "
874 "(root) %%Capacity\n");
875 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
876 (unsigned long long)st.f_files,
877 (unsigned long long)(st.f_files - st.f_ffree),
878 (unsigned long long)st.f_favail,
879 (unsigned long long)st.f_ffree, ffree);
881 strlcpy(s_used, "error", sizeof(s_used));
882 strlcpy(s_avail, "error", sizeof(s_avail));
883 strlcpy(s_root, "error", sizeof(s_root));
884 strlcpy(s_total, "error", sizeof(s_total));
885 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
886 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
887 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
888 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
889 printf(" Size Used Avail (root) %%Capacity\n");
890 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
891 s_total, s_used, s_avail, s_root,
892 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
895 printf(" Size Used Avail "
896 "(root) %%Capacity\n");
897 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
898 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
899 (unsigned long long)(st.f_frsize *
900 (st.f_blocks - st.f_bfree) / 1024),
901 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
902 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
903 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
910 * Undo escaping of glob sequences in place. Used to undo extra escaping
911 * applied in makeargv() when the string is destined for a function that
915 undo_glob_escape(char *s)
950 * Split a string into an argument vector using sh(1)-style quoting,
951 * comment and escaping rules, but with some tweaks to handle glob(3)
953 * The "sloppy" flag allows for recovery from missing terminating quote, for
954 * use in parsing incomplete commandlines during tab autocompletion.
956 * Returns NULL on error or a NULL-terminated array of arguments.
958 * If "lastquote" is not NULL, the quoting character used for the last
959 * argument is placed in *lastquote ("\0", "'" or "\"").
961 * If "terminated" is not NULL, *terminated will be set to 1 when the
962 * last argument's quote has been properly terminated or 0 otherwise.
963 * This parameter is only of use if "sloppy" is set.
966 #define MAXARGLEN 8192
968 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
973 static char argvs[MAXARGLEN];
974 static char *argv[MAXARGS + 1];
975 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
978 if (strlen(arg) > sizeof(argvs) - 1) {
980 error("string too long");
983 if (terminated != NULL)
985 if (lastquote != NULL)
990 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
991 error("Too many arguments.");
994 if (isspace(arg[i])) {
995 if (state == MA_UNQUOTED) {
996 /* Terminate current argument */
1000 } else if (state != MA_START)
1001 argvs[j++] = arg[i];
1002 } else if (arg[i] == '"' || arg[i] == '\'') {
1003 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1004 if (state == MA_START) {
1005 argv[argc] = argvs + j;
1007 if (lastquote != NULL)
1008 *lastquote = arg[i];
1009 } else if (state == MA_UNQUOTED)
1011 else if (state == q)
1012 state = MA_UNQUOTED;
1014 argvs[j++] = arg[i];
1015 } else if (arg[i] == '\\') {
1016 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1017 quot = state == MA_SQUOTE ? '\'' : '"';
1018 /* Unescape quote we are in */
1019 /* XXX support \n and friends? */
1020 if (arg[i + 1] == quot) {
1022 argvs[j++] = arg[i];
1023 } else if (arg[i + 1] == '?' ||
1024 arg[i + 1] == '[' || arg[i + 1] == '*') {
1026 * Special case for sftp: append
1027 * double-escaped glob sequence -
1028 * glob will undo one level of
1029 * escaping. NB. string can grow here.
1031 if (j >= sizeof(argvs) - 5)
1032 goto args_too_longs;
1034 argvs[j++] = arg[i++];
1036 argvs[j++] = arg[i];
1038 argvs[j++] = arg[i++];
1039 argvs[j++] = arg[i];
1042 if (state == MA_START) {
1043 argv[argc] = argvs + j;
1044 state = MA_UNQUOTED;
1045 if (lastquote != NULL)
1048 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1049 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1051 * Special case for sftp: append
1052 * escaped glob sequence -
1053 * glob will undo one level of
1056 argvs[j++] = arg[i++];
1057 argvs[j++] = arg[i];
1059 /* Unescape everything */
1060 /* XXX support \n and friends? */
1062 argvs[j++] = arg[i];
1065 } else if (arg[i] == '#') {
1066 if (state == MA_SQUOTE || state == MA_DQUOTE)
1067 argvs[j++] = arg[i];
1070 } else if (arg[i] == '\0') {
1071 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1073 state = MA_UNQUOTED;
1074 if (terminated != NULL)
1078 error("Unterminated quoted argument");
1082 if (state == MA_UNQUOTED) {
1088 if (state == MA_START) {
1089 argv[argc] = argvs + j;
1090 state = MA_UNQUOTED;
1091 if (lastquote != NULL)
1094 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1095 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1097 * Special case for sftp: escape quoted
1098 * glob(3) wildcards. NB. string can grow
1101 if (j >= sizeof(argvs) - 3)
1102 goto args_too_longs;
1104 argvs[j++] = arg[i];
1106 argvs[j++] = arg[i];
1115 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1116 int *hflag, int *sflag, unsigned long *n_arg, char **path1, char **path2)
1118 const char *cmd, *cp = *cpp;
1122 int i, cmdnum, optidx, argc;
1124 /* Skip leading whitespace */
1125 cp = cp + strspn(cp, WHITESPACE);
1127 /* Check for leading '-' (disable error processing) */
1132 cp = cp + strspn(cp, WHITESPACE);
1135 /* Ignore blank lines and lines which begin with comment '#' char */
1136 if (*cp == '\0' || *cp == '#')
1139 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1142 /* Figure out which command we have */
1143 for (i = 0; cmds[i].c != NULL; i++) {
1144 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1154 } else if (cmdnum == -1) {
1155 error("Invalid command.");
1159 /* Get arguments and parse flags */
1160 *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1161 *path1 = *path2 = NULL;
1166 if ((optidx = parse_getput_flags(cmd, argv, argc,
1167 pflag, rflag)) == -1)
1169 /* Get first pathname (mandatory) */
1170 if (argc - optidx < 1) {
1171 error("You must specify at least one path after a "
1172 "%s command.", cmd);
1175 *path1 = xstrdup(argv[optidx]);
1176 /* Get second pathname (optional) */
1177 if (argc - optidx > 1) {
1178 *path2 = xstrdup(argv[optidx + 1]);
1179 /* Destination is not globbed */
1180 undo_glob_escape(*path2);
1184 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1188 if (argc - optidx < 2) {
1189 error("You must specify two paths after a %s "
1193 *path1 = xstrdup(argv[optidx]);
1194 *path2 = xstrdup(argv[optidx + 1]);
1195 /* Paths are not globbed */
1196 undo_glob_escape(*path1);
1197 undo_glob_escape(*path2);
1205 /* Get pathname (mandatory) */
1206 if (argc - optidx < 1) {
1207 error("You must specify a path after a %s command.",
1211 *path1 = xstrdup(argv[optidx]);
1212 /* Only "rm" globs */
1214 undo_glob_escape(*path1);
1217 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1220 /* Default to current directory if no path specified */
1221 if (argc - optidx < 1)
1224 *path1 = xstrdup(argv[optidx]);
1225 undo_glob_escape(*path1);
1229 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1231 /* Path is optional */
1232 if (argc - optidx > 0)
1233 *path1 = xstrdup(argv[optidx]);
1236 /* Skip ls command and following whitespace */
1237 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1239 /* Uses the rest of the line */
1246 /* Get numeric arg (mandatory) */
1247 if (argc - optidx < 1)
1250 l = strtol(argv[optidx], &cp2, base);
1251 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1252 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1255 error("You must supply a numeric argument "
1256 "to the %s command.", cmd);
1260 if (cmdnum == I_LUMASK)
1262 /* Get pathname (mandatory) */
1263 if (argc - optidx < 2) {
1264 error("You must specify a path after a %s command.",
1268 *path1 = xstrdup(argv[optidx + 1]);
1278 fatal("Command not implemented");
1286 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1289 char *path1, *path2, *tmp;
1290 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0;
1292 unsigned long n_arg = 0;
1294 char path_buf[MAXPATHLEN];
1298 path1 = path2 = NULL;
1299 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag,
1300 &sflag, &n_arg, &path1, &path2);
1305 memset(&g, 0, sizeof(g));
1307 /* Perform command */
1313 /* Unrecognized command */
1317 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1320 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1323 path1 = make_absolute(path1, *pwd);
1324 path2 = make_absolute(path2, *pwd);
1325 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);