1 /* $OpenBSD: sftp.c,v 1.136 2012/06/22 14:36:33 dtucker Exp $ */
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
25 #include <sys/param.h>
26 #include <sys/socket.h>
28 #ifdef HAVE_SYS_STATVFS_H
29 #include <sys/statvfs.h>
44 typedef void EditLine;
63 #include "pathnames.h"
68 #include "sftp-common.h"
69 #include "sftp-client.h"
71 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
72 #define DEFAULT_NUM_REQUESTS 256 /* # concurrent outstanding requests */
74 /* File to read commands from */
77 /* Are we in batchfile mode? */
80 /* PID of ssh transport process */
81 static pid_t sshpid = -1;
83 /* This is set to 0 if the progressmeter is not desired. */
86 /* When this option is set, we always recursively download/upload directories */
89 /* When this option is set, the file transfers will always preserve times */
92 /* SIGINT received during command processing */
93 volatile sig_atomic_t interrupted = 0;
95 /* I wish qsort() took a separate ctx for the comparison function...*/
98 /* Context used for commandline completion */
100 struct sftp_conn *conn;
104 int remote_glob(struct sftp_conn *, const char *, int,
105 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
107 extern char *__progname;
109 /* Separators for interactive commands */
110 #define WHITESPACE " \t\r\n"
113 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
114 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
115 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
116 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
117 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
118 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
119 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
120 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
121 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
123 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
124 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
126 /* Commands for interactive mode */
151 #define I_PROGRESS 23
159 /* Type of completion */
164 static const struct CMD cmds[] = {
165 { "bye", I_QUIT, NOARGS },
166 { "cd", I_CHDIR, REMOTE },
167 { "chdir", I_CHDIR, REMOTE },
168 { "chgrp", I_CHGRP, REMOTE },
169 { "chmod", I_CHMOD, REMOTE },
170 { "chown", I_CHOWN, REMOTE },
171 { "df", I_DF, REMOTE },
172 { "dir", I_LS, REMOTE },
173 { "exit", I_QUIT, NOARGS },
174 { "get", I_GET, REMOTE },
175 { "help", I_HELP, NOARGS },
176 { "lcd", I_LCHDIR, LOCAL },
177 { "lchdir", I_LCHDIR, LOCAL },
178 { "lls", I_LLS, LOCAL },
179 { "lmkdir", I_LMKDIR, LOCAL },
180 { "ln", I_LINK, REMOTE },
181 { "lpwd", I_LPWD, LOCAL },
182 { "ls", I_LS, REMOTE },
183 { "lumask", I_LUMASK, NOARGS },
184 { "mkdir", I_MKDIR, REMOTE },
185 { "mget", I_GET, REMOTE },
186 { "mput", I_PUT, LOCAL },
187 { "progress", I_PROGRESS, NOARGS },
188 { "put", I_PUT, LOCAL },
189 { "pwd", I_PWD, REMOTE },
190 { "quit", I_QUIT, NOARGS },
191 { "rename", I_RENAME, REMOTE },
192 { "rm", I_RM, REMOTE },
193 { "rmdir", I_RMDIR, REMOTE },
194 { "symlink", I_SYMLINK, REMOTE },
195 { "version", I_VERSION, NOARGS },
196 { "!", I_SHELL, NOARGS },
197 { "?", I_HELP, NOARGS },
201 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
208 kill(sshpid, SIGTERM);
209 waitpid(sshpid, NULL, 0);
217 cmd_interrupt(int signo)
219 const char msg[] = "\rInterrupt \n";
220 int olderrno = errno;
222 write(STDERR_FILENO, msg, sizeof(msg) - 1);
230 printf("Available commands:\n"
232 "cd path Change remote directory to 'path'\n"
233 "chgrp grp path Change group of file 'path' to 'grp'\n"
234 "chmod mode path Change permissions of file 'path' to 'mode'\n"
235 "chown own path Change owner of file 'path' to 'own'\n"
236 "df [-hi] [path] Display statistics for current directory or\n"
237 " filesystem containing 'path'\n"
239 "get [-Ppr] remote [local] Download file\n"
240 "help Display this help text\n"
241 "lcd path Change local directory to 'path'\n"
242 "lls [ls-options [path]] Display local directory listing\n"
243 "lmkdir path Create local directory\n"
244 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
245 "lpwd Print local working directory\n"
246 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
247 "lumask umask Set local umask to 'umask'\n"
248 "mkdir path Create remote directory\n"
249 "progress Toggle display of progress meter\n"
250 "put [-Ppr] local [remote] Upload file\n"
251 "pwd Display remote working directory\n"
253 "rename oldpath newpath Rename remote file\n"
254 "rm path Delete remote file\n"
255 "rmdir path Remove remote directory\n"
256 "symlink oldpath newpath Symlink remote file\n"
257 "version Show SFTP version\n"
258 "!command Execute 'command' in local shell\n"
259 "! Escape to local shell\n"
260 "? Synonym for help\n");
264 local_do_shell(const char *args)
273 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
274 shell = _PATH_BSHELL;
276 if ((pid = fork()) == -1)
277 fatal("Couldn't fork: %s", strerror(errno));
280 /* XXX: child has pipe fds to ssh subproc open - issue? */
282 debug3("Executing %s -c \"%s\"", shell, args);
283 execl(shell, shell, "-c", args, (char *)NULL);
285 debug3("Executing %s", shell);
286 execl(shell, shell, (char *)NULL);
288 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
292 while (waitpid(pid, &status, 0) == -1)
294 fatal("Couldn't wait for child: %s", strerror(errno));
295 if (!WIFEXITED(status))
296 error("Shell exited abnormally");
297 else if (WEXITSTATUS(status))
298 error("Shell exited with status %d", WEXITSTATUS(status));
302 local_do_ls(const char *args)
305 local_do_shell(_PATH_LS);
307 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
308 char *buf = xmalloc(len);
310 /* XXX: quoting - rip quoting code from ftp? */
311 snprintf(buf, len, _PATH_LS " %s", args);
317 /* Strip one path (usually the pwd) from the start of another */
319 path_strip(char *path, char *strip)
324 return (xstrdup(path));
327 if (strncmp(path, strip, len) == 0) {
328 if (strip[len - 1] != '/' && path[len] == '/')
330 return (xstrdup(path + len));
333 return (xstrdup(path));
337 make_absolute(char *p, char *pwd)
342 if (p && p[0] != '/') {
343 abs_str = path_append(pwd, p);
351 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
354 extern int opterr, optind, optopt, optreset;
357 optind = optreset = 1;
361 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
372 error("%s: Invalid flag -%c", cmd, optopt);
381 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
383 extern int opterr, optind, optopt, optreset;
386 optind = optreset = 1;
390 while ((ch = getopt(argc, argv, "s")) != -1) {
396 error("%s: Invalid flag -%c", cmd, optopt);
405 parse_ls_flags(char **argv, int argc, int *lflag)
407 extern int opterr, optind, optopt, optreset;
410 optind = optreset = 1;
413 *lflag = LS_NAME_SORT;
414 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
417 *lflag &= ~VIEW_FLAGS;
418 *lflag |= LS_SHORT_VIEW;
421 *lflag &= ~SORT_FLAGS;
422 *lflag |= LS_SIZE_SORT;
425 *lflag |= LS_SHOW_ALL;
428 *lflag &= ~SORT_FLAGS;
431 *lflag |= LS_SI_UNITS;
434 *lflag &= ~LS_SHORT_VIEW;
435 *lflag |= LS_LONG_VIEW;
438 *lflag &= ~LS_SHORT_VIEW;
439 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
442 *lflag |= LS_REVERSE_SORT;
445 *lflag &= ~SORT_FLAGS;
446 *lflag |= LS_TIME_SORT;
449 error("ls: Invalid flag -%c", optopt);
458 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
460 extern int opterr, optind, optopt, optreset;
463 optind = optreset = 1;
467 while ((ch = getopt(argc, argv, "hi")) != -1) {
476 error("%s: Invalid flag -%c", cmd, optopt);
489 /* XXX: report errors? */
490 if (stat(path, &sb) == -1)
493 return(S_ISDIR(sb.st_mode));
497 remote_is_dir(struct sftp_conn *conn, char *path)
501 /* XXX: report errors? */
502 if ((a = do_stat(conn, path, 1)) == NULL)
504 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
506 return(S_ISDIR(a->perm));
509 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
511 pathname_is_dir(char *pathname)
513 size_t l = strlen(pathname);
515 return l > 0 && pathname[l - 1] == '/';
519 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
520 int pflag, int rflag)
522 char *abs_src = NULL;
523 char *abs_dst = NULL;
525 char *filename, *tmp=NULL;
528 abs_src = xstrdup(src);
529 abs_src = make_absolute(abs_src, pwd);
530 memset(&g, 0, sizeof(g));
532 debug3("Looking up %s", abs_src);
533 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
534 error("File \"%s\" not found.", abs_src);
540 * If multiple matches then dst must be a directory or
543 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
544 error("Multiple source paths, but destination "
545 "\"%s\" is not a directory", dst);
550 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
551 tmp = xstrdup(g.gl_pathv[i]);
552 if ((filename = basename(tmp)) == NULL) {
553 error("basename %s: %s", tmp, strerror(errno));
559 if (g.gl_matchc == 1 && dst) {
561 abs_dst = path_append(dst, filename);
563 abs_dst = xstrdup(dst);
566 abs_dst = path_append(dst, filename);
568 abs_dst = xstrdup(filename);
572 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
573 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
574 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
575 pflag || global_pflag, 1) == -1)
578 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
579 pflag || global_pflag) == -1)
593 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
594 int pflag, int rflag)
596 char *tmp_dst = NULL;
597 char *abs_dst = NULL;
598 char *tmp = NULL, *filename = NULL;
601 int i, dst_is_dir = 1;
605 tmp_dst = xstrdup(dst);
606 tmp_dst = make_absolute(tmp_dst, pwd);
609 memset(&g, 0, sizeof(g));
610 debug3("Looking up %s", src);
611 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
612 error("File \"%s\" not found.", src);
617 /* If we aren't fetching to pwd then stash this status for later */
619 dst_is_dir = remote_is_dir(conn, tmp_dst);
621 /* If multiple matches, dst may be directory or unspecified */
622 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
623 error("Multiple paths match, but destination "
624 "\"%s\" is not a directory", tmp_dst);
629 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
630 if (stat(g.gl_pathv[i], &sb) == -1) {
632 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
636 tmp = xstrdup(g.gl_pathv[i]);
637 if ((filename = basename(tmp)) == NULL) {
638 error("basename %s: %s", tmp, strerror(errno));
644 if (g.gl_matchc == 1 && tmp_dst) {
645 /* If directory specified, append filename */
647 abs_dst = path_append(tmp_dst, filename);
649 abs_dst = xstrdup(tmp_dst);
650 } else if (tmp_dst) {
651 abs_dst = path_append(tmp_dst, filename);
653 abs_dst = make_absolute(xstrdup(filename), pwd);
657 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
658 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
659 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
660 pflag || global_pflag, 1) == -1)
663 if (do_upload(conn, g.gl_pathv[i], abs_dst,
664 pflag || global_pflag) == -1)
679 sdirent_comp(const void *aa, const void *bb)
681 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
682 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
683 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
685 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
686 if (sort_flag & LS_NAME_SORT)
687 return (rmul * strcmp(a->filename, b->filename));
688 else if (sort_flag & LS_TIME_SORT)
689 return (rmul * NCMP(a->a.mtime, b->a.mtime));
690 else if (sort_flag & LS_SIZE_SORT)
691 return (rmul * NCMP(a->a.size, b->a.size));
693 fatal("Unknown ls sort type");
696 /* sftp ls.1 replacement for directories */
698 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
701 u_int c = 1, colspace = 0, columns = 1;
704 if ((n = do_readdir(conn, path, &d)) != 0)
707 if (!(lflag & LS_SHORT_VIEW)) {
708 u_int m = 0, width = 80;
712 /* Count entries for sort and find longest filename */
713 for (n = 0; d[n] != NULL; n++) {
714 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
715 m = MAX(m, strlen(d[n]->filename));
718 /* Add any subpath that also needs to be counted */
719 tmp = path_strip(path, strip_path);
723 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
726 columns = width / (m + 2);
727 columns = MAX(columns, 1);
728 colspace = width / columns;
729 colspace = MIN(colspace, width);
732 if (lflag & SORT_FLAGS) {
733 for (n = 0; d[n] != NULL; n++)
734 ; /* count entries */
735 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
736 qsort(d, n, sizeof(*d), sdirent_comp);
739 for (n = 0; d[n] != NULL && !interrupted; n++) {
742 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
745 tmp = path_append(path, d[n]->filename);
746 fname = path_strip(tmp, strip_path);
749 if (lflag & LS_LONG_VIEW) {
750 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
754 memset(&sb, 0, sizeof(sb));
755 attrib_to_stat(&d[n]->a, &sb);
756 lname = ls_file(fname, &sb, 1,
757 (lflag & LS_SI_UNITS));
758 printf("%s\n", lname);
761 printf("%s\n", d[n]->longname);
763 printf("%-*s", colspace, fname);
774 if (!(lflag & LS_LONG_VIEW) && (c != 1))
777 free_sftp_dirents(d);
781 /* sftp ls.1 replacement which handles path globs */
783 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
790 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
792 memset(&g, 0, sizeof(g));
794 if (remote_glob(conn, path,
795 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
797 (g.gl_pathc && !g.gl_matchc)) {
800 error("Can't ls: \"%s\" not found", path);
808 * If the glob returns a single match and it is a directory,
809 * then just list its contents.
811 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
812 S_ISDIR(g.gl_statv[0]->st_mode)) {
813 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
818 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
821 if (!(lflag & LS_SHORT_VIEW)) {
822 /* Count entries for sort and find longest filename */
823 for (i = 0; g.gl_pathv[i]; i++)
824 m = MAX(m, strlen(g.gl_pathv[i]));
826 columns = width / (m + 2);
827 columns = MAX(columns, 1);
828 colspace = width / columns;
831 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
832 fname = path_strip(g.gl_pathv[i], strip_path);
833 if (lflag & LS_LONG_VIEW) {
834 if (g.gl_statv[i] == NULL) {
835 error("no stat information for %s", fname);
838 lname = ls_file(fname, g.gl_statv[i], 1,
839 (lflag & LS_SI_UNITS));
840 printf("%s\n", lname);
843 printf("%-*s", colspace, fname);
853 if (!(lflag & LS_LONG_VIEW) && (c != 1))
864 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
866 struct sftp_statvfs st;
867 char s_used[FMT_SCALED_STRSIZE];
868 char s_avail[FMT_SCALED_STRSIZE];
869 char s_root[FMT_SCALED_STRSIZE];
870 char s_total[FMT_SCALED_STRSIZE];
871 unsigned long long ffree;
873 if (do_statvfs(conn, path, &st, 1) == -1)
876 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
877 printf(" Inodes Used Avail "
878 "(root) %%Capacity\n");
879 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
880 (unsigned long long)st.f_files,
881 (unsigned long long)(st.f_files - st.f_ffree),
882 (unsigned long long)st.f_favail,
883 (unsigned long long)st.f_ffree, ffree);
885 strlcpy(s_used, "error", sizeof(s_used));
886 strlcpy(s_avail, "error", sizeof(s_avail));
887 strlcpy(s_root, "error", sizeof(s_root));
888 strlcpy(s_total, "error", sizeof(s_total));
889 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
890 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
891 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
892 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
893 printf(" Size Used Avail (root) %%Capacity\n");
894 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
895 s_total, s_used, s_avail, s_root,
896 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
899 printf(" Size Used Avail "
900 "(root) %%Capacity\n");
901 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
902 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
903 (unsigned long long)(st.f_frsize *
904 (st.f_blocks - st.f_bfree) / 1024),
905 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
906 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
907 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
914 * Undo escaping of glob sequences in place. Used to undo extra escaping
915 * applied in makeargv() when the string is destined for a function that
919 undo_glob_escape(char *s)
954 * Split a string into an argument vector using sh(1)-style quoting,
955 * comment and escaping rules, but with some tweaks to handle glob(3)
957 * The "sloppy" flag allows for recovery from missing terminating quote, for
958 * use in parsing incomplete commandlines during tab autocompletion.
960 * Returns NULL on error or a NULL-terminated array of arguments.
962 * If "lastquote" is not NULL, the quoting character used for the last
963 * argument is placed in *lastquote ("\0", "'" or "\"").
965 * If "terminated" is not NULL, *terminated will be set to 1 when the
966 * last argument's quote has been properly terminated or 0 otherwise.
967 * This parameter is only of use if "sloppy" is set.
970 #define MAXARGLEN 8192
972 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
977 static char argvs[MAXARGLEN];
978 static char *argv[MAXARGS + 1];
979 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
982 if (strlen(arg) > sizeof(argvs) - 1) {
984 error("string too long");
987 if (terminated != NULL)
989 if (lastquote != NULL)
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 (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);
1330 path1 = make_absolute(path1, *pwd);
1331 path2 = make_absolute(path2, *pwd);
1332 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1335 path1 = make_absolute(path1, *pwd);
1336 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1337 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1338 printf("Removing %s\n", g.gl_pathv[i]);
1339 err = do_rm(conn, g.gl_pathv[i]);
1340 if (err != 0 && err_abort)
1345 path1 = make_absolute(path1, *pwd);
1347 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1349 err = do_mkdir(conn, path1, &a, 1);
1352 path1 = make_absolute(path1, *pwd);
1353 err = do_rmdir(conn, path1);
1356 path1 = make_absolute(path1, *pwd);
1357 if ((tmp = do_realpath(conn, path1)) == NULL) {
1361 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1366 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1367 error("Can't change directory: Can't check target");
1372 if (!S_ISDIR(aa->perm)) {
1373 error("Can't change directory: \"%s\" is not "
1374 "a directory", tmp);
1384 do_ls_dir(conn, *pwd, *pwd, lflag);
1388 /* Strip pwd off beginning of non-absolute paths */
1393 path1 = make_absolute(path1, *pwd);
1394 err = do_globbed_ls(conn, path1, tmp, lflag);
1397 /* Default to current directory if no path specified */
1399 path1 = xstrdup(*pwd);
1400 path1 = make_absolute(path1, *pwd);
1401 err = do_df(conn, path1, hflag, iflag);
1404 if (chdir(path1) == -1) {
1405 error("Couldn't change local directory to "
1406 "\"%s\": %s", path1, strerror(errno));
1411 if (mkdir(path1, 0777) == -1) {
1412 error("Couldn't create local directory "
1413 "\"%s\": %s", path1, strerror(errno));
1421 local_do_shell(cmd);
1425 printf("Local umask: %03lo\n", n_arg);
1428 path1 = make_absolute(path1, *pwd);
1430 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1432 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1433 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1434 printf("Changing mode on %s\n", g.gl_pathv[i]);
1435 err = do_setstat(conn, g.gl_pathv[i], &a);
1436 if (err != 0 && err_abort)
1442 path1 = make_absolute(path1, *pwd);
1443 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1444 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1445 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1452 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1453 error("Can't get current ownership of "
1454 "remote file \"%s\"", g.gl_pathv[i]);
1461 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1462 if (cmdnum == I_CHOWN) {
1463 printf("Changing owner on %s\n", g.gl_pathv[i]);
1466 printf("Changing group on %s\n", g.gl_pathv[i]);
1469 err = do_setstat(conn, g.gl_pathv[i], aa);
1470 if (err != 0 && err_abort)
1475 printf("Remote working directory: %s\n", *pwd);
1478 if (!getcwd(path_buf, sizeof(path_buf))) {
1479 error("Couldn't get local cwd: %s", strerror(errno));
1483 printf("Local working directory: %s\n", path_buf);
1486 /* Processed below */
1492 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1495 showprogress = !showprogress;
1497 printf("Progress meter enabled\n");
1499 printf("Progress meter disabled\n");
1502 fatal("%d is not implemented", cmdnum);
1512 /* If an unignored error occurs in batch mode we should abort. */
1513 if (err_abort && err != 0)
1515 else if (cmdnum == I_QUIT)
1523 prompt(EditLine *el)
1528 /* Display entries in 'list' after skipping the first 'len' chars */
1530 complete_display(char **list, u_int len)
1532 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1536 /* Count entries for sort and find longest */
1537 for (y = 0; list[y]; y++)
1538 m = MAX(m, strlen(list[y]));
1540 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1543 m = m > len ? m - len : 0;
1544 columns = width / (m + 2);
1545 columns = MAX(columns, 1);
1546 colspace = width / columns;
1547 colspace = MIN(colspace, width);
1551 for (y = 0; list[y]; y++) {
1552 llen = strlen(list[y]);
1553 tmp = llen > len ? list[y] + len : "";
1554 printf("%-*s", colspace, tmp);
1565 * Given a "list" of words that begin with a common prefix of "word",
1566 * attempt to find an autocompletion to extends "word" by the next
1567 * characters common to all entries in "list".
1570 complete_ambiguous(const char *word, char **list, size_t count)
1576 u_int y, matchlen = strlen(list[0]);
1578 /* Find length of common stem */
1579 for (y = 1; list[y]; y++) {
1582 for (x = 0; x < matchlen; x++)
1583 if (list[0][x] != list[y][x])
1589 if (matchlen > strlen(word)) {
1590 char *tmp = xstrdup(list[0]);
1592 tmp[matchlen] = '\0';
1597 return xstrdup(word);
1600 /* Autocomplete a sftp command */
1602 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1605 u_int y, count = 0, cmdlen, tmplen;
1606 char *tmp, **list, argterm[3];
1609 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1611 /* No command specified: display all available commands */
1613 for (y = 0; cmds[y].c; y++)
1614 list[count++] = xstrdup(cmds[y].c);
1617 complete_display(list, 0);
1619 for (y = 0; list[y] != NULL; y++)
1625 /* Prepare subset of commands that start with "cmd" */
1626 cmdlen = strlen(cmd);
1627 for (y = 0; cmds[y].c; y++) {
1628 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1629 list[count++] = xstrdup(cmds[y].c);
1638 /* Complete ambigious command */
1639 tmp = complete_ambiguous(cmd, list, count);
1641 complete_display(list, 0);
1643 for (y = 0; list[y]; y++)
1648 tmplen = strlen(tmp);
1649 cmdlen = strlen(cmd);
1650 /* If cmd may be extended then do so */
1651 if (tmplen > cmdlen)
1652 if (el_insertstr(el, tmp + cmdlen) == -1)
1653 fatal("el_insertstr failed.");
1655 /* Terminate argument cleanly */
1659 argterm[y++] = quote;
1660 if (lastarg || *(lf->cursor) != ' ')
1663 if (y > 0 && el_insertstr(el, argterm) == -1)
1664 fatal("el_insertstr failed.");
1673 * Determine whether a particular sftp command's arguments (if any)
1674 * represent local or remote files.
1677 complete_is_remote(char *cmd) {
1683 for (i = 0; cmds[i].c; i++) {
1684 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1691 /* Autocomplete a filename "file" */
1693 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1694 char *file, int remote, int lastarg, char quote, int terminated)
1697 char *tmp, *tmp2, ins[3];
1698 u_int i, hadglob, pwdlen, len, tmplen, filelen;
1701 /* Glob from "file" location */
1705 xasprintf(&tmp, "%s*", file);
1707 memset(&g, 0, sizeof(g));
1708 if (remote != LOCAL) {
1709 tmp = make_absolute(tmp, remote_path);
1710 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1712 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1714 /* Determine length of pwd so we can trim completion display */
1715 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1716 /* Terminate counting on first unescaped glob metacharacter */
1717 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1718 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1722 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1724 if (tmp[tmplen] == '/')
1725 pwdlen = tmplen + 1; /* track last seen '/' */
1729 if (g.gl_matchc == 0)
1732 if (g.gl_matchc > 1)
1733 complete_display(g.gl_pathv, pwdlen);
1736 /* Don't try to extend globs */
1737 if (file == NULL || hadglob)
1740 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1741 tmp = path_strip(tmp2, remote_path);
1747 tmplen = strlen(tmp);
1748 filelen = strlen(file);
1750 if (tmplen > filelen) {
1751 tmp2 = tmp + filelen;
1753 /* quote argument on way out */
1754 for (i = 0; i < len; i++) {
1765 if (quote == '\0' || tmp2[i] == quote) {
1766 if (el_insertstr(el, ins) == -1)
1767 fatal("el_insertstr "
1773 if (el_insertstr(el, ins + 1) == -1)
1774 fatal("el_insertstr failed.");
1781 if (g.gl_matchc == 1) {
1785 if (*(lf->cursor - 1) != '/' &&
1786 (lastarg || *(lf->cursor) != ' '))
1789 if (i > 0 && el_insertstr(el, ins) == -1)
1790 fatal("el_insertstr failed.");
1799 /* tab-completion hook function, called via libedit */
1800 static unsigned char
1801 complete(EditLine *el, int ch)
1803 char **argv, *line, quote;
1804 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1806 struct complete_ctx *complete_ctx;
1809 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1810 fatal("%s: el_get failed", __func__);
1812 /* Figure out which argument the cursor points to */
1813 cursor = lf->cursor - lf->buffer;
1814 line = (char *)xmalloc(cursor + 1);
1815 memcpy(line, lf->buffer, cursor);
1816 line[cursor] = '\0';
1817 argv = makeargv(line, &carg, 1, "e, &terminated);
1820 /* Get all the arguments on the line */
1821 len = lf->lastchar - lf->buffer;
1822 line = (char *)xmalloc(len + 1);
1823 memcpy(line, lf->buffer, len);
1825 argv = makeargv(line, &argc, 1, NULL, NULL);
1827 /* Ensure cursor is at EOL or a argument boundary */
1828 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1829 line[cursor] != '\n') {
1835 /* Show all available commands */
1836 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1838 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1839 /* Handle the command parsing */
1840 if (complete_cmd_parse(el, argv[0], argc == carg,
1841 quote, terminated) != 0)
1843 } else if (carg >= 1) {
1844 /* Handle file parsing */
1845 int remote = complete_is_remote(argv[0]);
1846 char *filematch = NULL;
1848 if (carg > 1 && line[cursor-1] != ' ')
1849 filematch = argv[carg - 1];
1852 complete_match(el, complete_ctx->conn,
1853 *complete_ctx->remote_pathp, filematch,
1854 remote, carg == argc, quote, terminated) != 0)
1861 #endif /* USE_LIBEDIT */
1864 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1869 int err, interactive;
1870 EditLine *el = NULL;
1874 extern char *__progname;
1875 struct complete_ctx complete_ctx;
1877 if (!batchmode && isatty(STDIN_FILENO)) {
1878 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1879 fatal("Couldn't initialise editline");
1880 if ((hl = history_init()) == NULL)
1881 fatal("Couldn't initialise editline history");
1882 history(hl, &hev, H_SETSIZE, 100);
1883 el_set(el, EL_HIST, history, hl);
1885 el_set(el, EL_PROMPT, prompt);
1886 el_set(el, EL_EDITOR, "emacs");
1887 el_set(el, EL_TERMINAL, NULL);
1888 el_set(el, EL_SIGNAL, 1);
1889 el_source(el, NULL);
1891 /* Tab Completion */
1892 el_set(el, EL_ADDFN, "ftp-complete",
1893 "Context sensitive argument completion", complete);
1894 complete_ctx.conn = conn;
1895 complete_ctx.remote_pathp = &remote_path;
1896 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1897 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1899 #endif /* USE_LIBEDIT */
1901 remote_path = do_realpath(conn, ".");
1902 if (remote_path == NULL)
1905 if (file1 != NULL) {
1906 dir = xstrdup(file1);
1907 dir = make_absolute(dir, remote_path);
1909 if (remote_is_dir(conn, dir) && file2 == NULL) {
1910 printf("Changing to: %s\n", dir);
1911 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1912 if (parse_dispatch_command(conn, cmd,
1913 &remote_path, 1) != 0) {
1921 snprintf(cmd, sizeof cmd, "get %s", dir);
1923 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1926 err = parse_dispatch_command(conn, cmd,
1939 interactive = !batchmode && isatty(STDIN_FILENO);
1944 signal(SIGINT, SIG_IGN);
1949 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1954 if (!interactive) { /* Echo command */
1955 printf("sftp> %s", cmd);
1956 if (strlen(cmd) > 0 &&
1957 cmd[strlen(cmd) - 1] != '\n')
1965 if ((line = el_gets(el, &count)) == NULL ||
1970 history(hl, &hev, H_ENTER, line);
1971 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1972 fprintf(stderr, "Error: input line too long\n");
1975 #endif /* USE_LIBEDIT */
1978 cp = strrchr(cmd, '\n');
1982 /* Handle user interrupts gracefully during commands */
1984 signal(SIGINT, cmd_interrupt);
1986 err = parse_dispatch_command(conn, cmd, &remote_path,
1997 #endif /* USE_LIBEDIT */
1999 /* err == 1 signifies normal "quit" exit */
2000 return (err >= 0 ? 0 : -1);
2004 connect_to_server(char *path, char **args, int *in, int *out)
2009 int pin[2], pout[2];
2011 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2012 fatal("pipe: %s", strerror(errno));
2017 #else /* USE_PIPES */
2020 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2021 fatal("socketpair: %s", strerror(errno));
2022 *in = *out = inout[0];
2023 c_in = c_out = inout[1];
2024 #endif /* USE_PIPES */
2026 if ((sshpid = fork()) == -1)
2027 fatal("fork: %s", strerror(errno));
2028 else if (sshpid == 0) {
2029 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2030 (dup2(c_out, STDOUT_FILENO) == -1)) {
2031 fprintf(stderr, "dup2: %s\n", strerror(errno));
2040 * The underlying ssh is in the same process group, so we must
2041 * ignore SIGINT if we want to gracefully abort commands,
2042 * otherwise the signal will make it to the ssh process and
2043 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2044 * underlying ssh, it must *not* ignore that signal.
2046 signal(SIGINT, SIG_IGN);
2047 signal(SIGTERM, SIG_DFL);
2049 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2053 signal(SIGTERM, killchild);
2054 signal(SIGINT, killchild);
2055 signal(SIGHUP, killchild);
2063 extern char *__progname;
2066 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2067 " [-D sftp_server_path] [-F ssh_config] "
2068 "[-i identity_file] [-l limit]\n"
2069 " [-o ssh_option] [-P port] [-R num_requests] "
2071 " [-s subsystem | sftp_server] host\n"
2072 " %s [user@]host[:file ...]\n"
2073 " %s [user@]host[:dir[/]]\n"
2074 " %s -b batchfile [user@]host\n",
2075 __progname, __progname, __progname, __progname);
2080 main(int argc, char **argv)
2082 int in, out, ch, err;
2083 char *host = NULL, *userhost, *cp, *file2 = NULL;
2084 int debug_level = 0, sshver = 2;
2085 char *file1 = NULL, *sftp_server = NULL;
2086 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2088 LogLevel ll = SYSLOG_LEVEL_INFO;
2091 extern char *optarg;
2092 struct sftp_conn *conn;
2093 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2094 size_t num_requests = DEFAULT_NUM_REQUESTS;
2095 long long limit_kbps = 0;
2097 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2100 __progname = ssh_get_progname(argv[0]);
2101 memset(&args, '\0', sizeof(args));
2103 addargs(&args, "%s", ssh_program);
2104 addargs(&args, "-oForwardX11 no");
2105 addargs(&args, "-oForwardAgent no");
2106 addargs(&args, "-oPermitLocalCommand no");
2107 addargs(&args, "-oClearAllForwardings yes");
2109 ll = SYSLOG_LEVEL_INFO;
2112 while ((ch = getopt(argc, argv,
2113 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2115 /* Passed through to ssh(1) */
2119 addargs(&args, "-%c", ch);
2121 /* Passed through to ssh(1) with argument */
2126 addargs(&args, "-%c", ch);
2127 addargs(&args, "%s", optarg);
2131 addargs(&args, "-%c", ch);
2134 addargs(&args, "-oPort %s", optarg);
2137 if (debug_level < 3) {
2138 addargs(&args, "-v");
2139 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2145 if (sftp_server == NULL)
2146 sftp_server = _PATH_SFTP_SERVER;
2152 copy_buffer_len = strtol(optarg, &cp, 10);
2153 if (copy_buffer_len == 0 || *cp != '\0')
2154 fatal("Invalid buffer size \"%s\"", optarg);
2158 fatal("Batch file already specified.");
2160 /* Allow "-" as stdin */
2161 if (strcmp(optarg, "-") != 0 &&
2162 (infile = fopen(optarg, "r")) == NULL)
2163 fatal("%s (%s).", strerror(errno), optarg);
2166 addargs(&args, "-obatchmode yes");
2172 sftp_direct = optarg;
2175 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2179 limit_kbps *= 1024; /* kbps */
2185 num_requests = strtol(optarg, &cp, 10);
2186 if (num_requests == 0 || *cp != '\0')
2187 fatal("Invalid number of requests \"%s\"",
2191 sftp_server = optarg;
2194 ssh_program = optarg;
2195 replacearg(&args, 0, "%s", ssh_program);
2203 if (!isatty(STDERR_FILENO))
2206 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2208 if (sftp_direct == NULL) {
2209 if (optind == argc || argc > (optind + 2))
2212 userhost = xstrdup(argv[optind]);
2213 file2 = argv[optind+1];
2215 if ((host = strrchr(userhost, '@')) == NULL)
2220 fprintf(stderr, "Missing username\n");
2223 addargs(&args, "-l");
2224 addargs(&args, "%s", userhost);
2227 if ((cp = colon(host)) != NULL) {
2232 host = cleanhostname(host);
2234 fprintf(stderr, "Missing hostname\n");
2238 addargs(&args, "-oProtocol %d", sshver);
2240 /* no subsystem if the server-spec contains a '/' */
2241 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2242 addargs(&args, "-s");
2244 addargs(&args, "--");
2245 addargs(&args, "%s", host);
2246 addargs(&args, "%s", (sftp_server != NULL ?
2247 sftp_server : "sftp"));
2249 connect_to_server(ssh_program, args.list, &in, &out);
2252 addargs(&args, "sftp-server");
2254 connect_to_server(sftp_direct, args.list, &in, &out);
2258 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2260 fatal("Couldn't initialise connection to server");
2263 if (sftp_direct == NULL)
2264 fprintf(stderr, "Connected to %s.\n", host);
2266 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2269 err = interactive_loop(conn, file1, file2);
2271 #if !defined(USE_PIPES)
2272 shutdown(in, SHUT_RDWR);
2273 shutdown(out, SHUT_RDWR);
2281 while (waitpid(sshpid, NULL, 0) == -1)
2283 fatal("Couldn't wait for ssh process: %s",
2286 exit(err == 0 ? 0 : 1);