1 /* $OpenBSD: sftp.c,v 1.158 2013/11/20 20:54:10 deraadt 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>
48 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 /* Suppress diagnositic messages */
86 /* This is set to 0 if the progressmeter is not desired. */
89 /* When this option is set, we always recursively download/upload directories */
92 /* When this option is set, we resume download if possible */
95 /* When this option is set, the file transfers will always preserve times */
98 /* When this option is set, transfers will have fsync() called on each file */
101 /* SIGINT received during command processing */
102 volatile sig_atomic_t interrupted = 0;
104 /* I wish qsort() took a separate ctx for the comparison function...*/
107 /* Context used for commandline completion */
108 struct complete_ctx {
109 struct sftp_conn *conn;
113 int remote_glob(struct sftp_conn *, const char *, int,
114 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
116 extern char *__progname;
118 /* Separators for interactive commands */
119 #define WHITESPACE " \t\r\n"
122 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
123 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
124 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
125 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
126 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
127 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
128 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
129 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
130 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
132 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
133 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
135 /* Commands for interactive mode */
171 /* Type of completion */
176 static const struct CMD cmds[] = {
177 { "bye", I_QUIT, NOARGS },
178 { "cd", I_CHDIR, REMOTE },
179 { "chdir", I_CHDIR, REMOTE },
180 { "chgrp", I_CHGRP, REMOTE },
181 { "chmod", I_CHMOD, REMOTE },
182 { "chown", I_CHOWN, REMOTE },
183 { "df", I_DF, REMOTE },
184 { "dir", I_LS, REMOTE },
185 { "exit", I_QUIT, NOARGS },
186 { "get", I_GET, REMOTE },
187 { "help", I_HELP, NOARGS },
188 { "lcd", I_LCHDIR, LOCAL },
189 { "lchdir", I_LCHDIR, LOCAL },
190 { "lls", I_LLS, LOCAL },
191 { "lmkdir", I_LMKDIR, LOCAL },
192 { "ln", I_LINK, REMOTE },
193 { "lpwd", I_LPWD, LOCAL },
194 { "ls", I_LS, REMOTE },
195 { "lumask", I_LUMASK, NOARGS },
196 { "mkdir", I_MKDIR, REMOTE },
197 { "mget", I_GET, REMOTE },
198 { "mput", I_PUT, LOCAL },
199 { "progress", I_PROGRESS, NOARGS },
200 { "put", I_PUT, LOCAL },
201 { "pwd", I_PWD, REMOTE },
202 { "quit", I_QUIT, NOARGS },
203 { "reget", I_REGET, REMOTE },
204 { "rename", I_RENAME, REMOTE },
205 { "rm", I_RM, REMOTE },
206 { "rmdir", I_RMDIR, REMOTE },
207 { "symlink", I_SYMLINK, REMOTE },
208 { "version", I_VERSION, NOARGS },
209 { "!", I_SHELL, NOARGS },
210 { "?", I_HELP, NOARGS },
214 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
221 kill(sshpid, SIGTERM);
222 waitpid(sshpid, NULL, 0);
230 cmd_interrupt(int signo)
232 const char msg[] = "\rInterrupt \n";
233 int olderrno = errno;
235 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
243 printf("Available commands:\n"
245 "cd path Change remote directory to 'path'\n"
246 "chgrp grp path Change group of file 'path' to 'grp'\n"
247 "chmod mode path Change permissions of file 'path' to 'mode'\n"
248 "chown own path Change owner of file 'path' to 'own'\n"
249 "df [-hi] [path] Display statistics for current directory or\n"
250 " filesystem containing 'path'\n"
252 "get [-Ppr] remote [local] Download file\n"
253 "reget remote [local] Resume download file\n"
254 "help Display this help text\n"
255 "lcd path Change local directory to 'path'\n"
256 "lls [ls-options [path]] Display local directory listing\n"
257 "lmkdir path Create local directory\n"
258 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
259 "lpwd Print local working directory\n"
260 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
261 "lumask umask Set local umask to 'umask'\n"
262 "mkdir path Create remote directory\n"
263 "progress Toggle display of progress meter\n"
264 "put [-Ppr] local [remote] Upload file\n"
265 "pwd Display remote working directory\n"
267 "rename oldpath newpath Rename remote file\n"
268 "rm path Delete remote file\n"
269 "rmdir path Remove remote directory\n"
270 "symlink oldpath newpath Symlink remote file\n"
271 "version Show SFTP version\n"
272 "!command Execute 'command' in local shell\n"
273 "! Escape to local shell\n"
274 "? Synonym for help\n");
278 local_do_shell(const char *args)
287 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
288 shell = _PATH_BSHELL;
290 if ((pid = fork()) == -1)
291 fatal("Couldn't fork: %s", strerror(errno));
294 /* XXX: child has pipe fds to ssh subproc open - issue? */
296 debug3("Executing %s -c \"%s\"", shell, args);
297 execl(shell, shell, "-c", args, (char *)NULL);
299 debug3("Executing %s", shell);
300 execl(shell, shell, (char *)NULL);
302 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
306 while (waitpid(pid, &status, 0) == -1)
308 fatal("Couldn't wait for child: %s", strerror(errno));
309 if (!WIFEXITED(status))
310 error("Shell exited abnormally");
311 else if (WEXITSTATUS(status))
312 error("Shell exited with status %d", WEXITSTATUS(status));
316 local_do_ls(const char *args)
319 local_do_shell(_PATH_LS);
321 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
322 char *buf = xmalloc(len);
324 /* XXX: quoting - rip quoting code from ftp? */
325 snprintf(buf, len, _PATH_LS " %s", args);
331 /* Strip one path (usually the pwd) from the start of another */
333 path_strip(char *path, char *strip)
338 return (xstrdup(path));
341 if (strncmp(path, strip, len) == 0) {
342 if (strip[len - 1] != '/' && path[len] == '/')
344 return (xstrdup(path + len));
347 return (xstrdup(path));
351 make_absolute(char *p, char *pwd)
356 if (p && p[0] != '/') {
357 abs_str = path_append(pwd, p);
365 parse_getput_flags(const char *cmd, char **argv, int argc,
366 int *aflag, int *fflag, int *pflag, int *rflag)
368 extern int opterr, optind, optopt, optreset;
371 optind = optreset = 1;
374 *aflag = *fflag = *rflag = *pflag = 0;
375 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
392 error("%s: Invalid flag -%c", cmd, optopt);
401 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
403 extern int opterr, optind, optopt, optreset;
406 optind = optreset = 1;
410 while ((ch = getopt(argc, argv, "s")) != -1) {
416 error("%s: Invalid flag -%c", cmd, optopt);
425 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
427 extern int opterr, optind, optopt, optreset;
430 optind = optreset = 1;
434 while ((ch = getopt(argc, argv, "l")) != -1) {
440 error("%s: Invalid flag -%c", cmd, optopt);
449 parse_ls_flags(char **argv, int argc, int *lflag)
451 extern int opterr, optind, optopt, optreset;
454 optind = optreset = 1;
457 *lflag = LS_NAME_SORT;
458 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
461 *lflag &= ~VIEW_FLAGS;
462 *lflag |= LS_SHORT_VIEW;
465 *lflag &= ~SORT_FLAGS;
466 *lflag |= LS_SIZE_SORT;
469 *lflag |= LS_SHOW_ALL;
472 *lflag &= ~SORT_FLAGS;
475 *lflag |= LS_SI_UNITS;
478 *lflag &= ~LS_SHORT_VIEW;
479 *lflag |= LS_LONG_VIEW;
482 *lflag &= ~LS_SHORT_VIEW;
483 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
486 *lflag |= LS_REVERSE_SORT;
489 *lflag &= ~SORT_FLAGS;
490 *lflag |= LS_TIME_SORT;
493 error("ls: Invalid flag -%c", optopt);
502 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
504 extern int opterr, optind, optopt, optreset;
507 optind = optreset = 1;
511 while ((ch = getopt(argc, argv, "hi")) != -1) {
520 error("%s: Invalid flag -%c", cmd, optopt);
529 parse_no_flags(const char *cmd, char **argv, int argc)
531 extern int opterr, optind, optopt, optreset;
534 optind = optreset = 1;
537 while ((ch = getopt(argc, argv, "")) != -1) {
540 error("%s: Invalid flag -%c", cmd, optopt);
553 /* XXX: report errors? */
554 if (stat(path, &sb) == -1)
557 return(S_ISDIR(sb.st_mode));
561 remote_is_dir(struct sftp_conn *conn, char *path)
565 /* XXX: report errors? */
566 if ((a = do_stat(conn, path, 1)) == NULL)
568 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
570 return(S_ISDIR(a->perm));
573 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
575 pathname_is_dir(char *pathname)
577 size_t l = strlen(pathname);
579 return l > 0 && pathname[l - 1] == '/';
583 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
584 int pflag, int rflag, int resume, int fflag)
586 char *abs_src = NULL;
587 char *abs_dst = NULL;
589 char *filename, *tmp=NULL;
592 abs_src = xstrdup(src);
593 abs_src = make_absolute(abs_src, pwd);
594 memset(&g, 0, sizeof(g));
596 debug3("Looking up %s", abs_src);
597 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
598 error("File \"%s\" not found.", abs_src);
604 * If multiple matches then dst must be a directory or
607 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
608 error("Multiple source paths, but destination "
609 "\"%s\" is not a directory", dst);
614 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
615 tmp = xstrdup(g.gl_pathv[i]);
616 if ((filename = basename(tmp)) == NULL) {
617 error("basename %s: %s", tmp, strerror(errno));
623 if (g.gl_matchc == 1 && dst) {
625 abs_dst = path_append(dst, filename);
627 abs_dst = xstrdup(dst);
630 abs_dst = path_append(dst, filename);
632 abs_dst = xstrdup(filename);
636 resume |= global_aflag;
637 if (!quiet && resume)
638 printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
639 else if (!quiet && !resume)
640 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
641 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
642 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
643 pflag || global_pflag, 1, resume,
644 fflag || global_fflag) == -1)
647 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
648 pflag || global_pflag, resume,
649 fflag || global_fflag) == -1)
663 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
664 int pflag, int rflag, int fflag)
666 char *tmp_dst = NULL;
667 char *abs_dst = NULL;
668 char *tmp = NULL, *filename = NULL;
671 int i, dst_is_dir = 1;
675 tmp_dst = xstrdup(dst);
676 tmp_dst = make_absolute(tmp_dst, pwd);
679 memset(&g, 0, sizeof(g));
680 debug3("Looking up %s", src);
681 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
682 error("File \"%s\" not found.", src);
687 /* If we aren't fetching to pwd then stash this status for later */
689 dst_is_dir = remote_is_dir(conn, tmp_dst);
691 /* If multiple matches, dst may be directory or unspecified */
692 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
693 error("Multiple paths match, but destination "
694 "\"%s\" is not a directory", tmp_dst);
699 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
700 if (stat(g.gl_pathv[i], &sb) == -1) {
702 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
706 tmp = xstrdup(g.gl_pathv[i]);
707 if ((filename = basename(tmp)) == NULL) {
708 error("basename %s: %s", tmp, strerror(errno));
714 if (g.gl_matchc == 1 && tmp_dst) {
715 /* If directory specified, append filename */
717 abs_dst = path_append(tmp_dst, filename);
719 abs_dst = xstrdup(tmp_dst);
720 } else if (tmp_dst) {
721 abs_dst = path_append(tmp_dst, filename);
723 abs_dst = make_absolute(xstrdup(filename), pwd);
728 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
729 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
730 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
731 pflag || global_pflag, 1,
732 fflag || global_fflag) == -1)
735 if (do_upload(conn, g.gl_pathv[i], abs_dst,
736 pflag || global_pflag,
737 fflag || global_fflag) == -1)
750 sdirent_comp(const void *aa, const void *bb)
752 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
753 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
754 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
756 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
757 if (sort_flag & LS_NAME_SORT)
758 return (rmul * strcmp(a->filename, b->filename));
759 else if (sort_flag & LS_TIME_SORT)
760 return (rmul * NCMP(a->a.mtime, b->a.mtime));
761 else if (sort_flag & LS_SIZE_SORT)
762 return (rmul * NCMP(a->a.size, b->a.size));
764 fatal("Unknown ls sort type");
767 /* sftp ls.1 replacement for directories */
769 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
772 u_int c = 1, colspace = 0, columns = 1;
775 if ((n = do_readdir(conn, path, &d)) != 0)
778 if (!(lflag & LS_SHORT_VIEW)) {
779 u_int m = 0, width = 80;
783 /* Count entries for sort and find longest filename */
784 for (n = 0; d[n] != NULL; n++) {
785 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
786 m = MAX(m, strlen(d[n]->filename));
789 /* Add any subpath that also needs to be counted */
790 tmp = path_strip(path, strip_path);
794 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
797 columns = width / (m + 2);
798 columns = MAX(columns, 1);
799 colspace = width / columns;
800 colspace = MIN(colspace, width);
803 if (lflag & SORT_FLAGS) {
804 for (n = 0; d[n] != NULL; n++)
805 ; /* count entries */
806 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
807 qsort(d, n, sizeof(*d), sdirent_comp);
810 for (n = 0; d[n] != NULL && !interrupted; n++) {
813 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
816 tmp = path_append(path, d[n]->filename);
817 fname = path_strip(tmp, strip_path);
820 if (lflag & LS_LONG_VIEW) {
821 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
825 memset(&sb, 0, sizeof(sb));
826 attrib_to_stat(&d[n]->a, &sb);
827 lname = ls_file(fname, &sb, 1,
828 (lflag & LS_SI_UNITS));
829 printf("%s\n", lname);
832 printf("%s\n", d[n]->longname);
834 printf("%-*s", colspace, fname);
845 if (!(lflag & LS_LONG_VIEW) && (c != 1))
848 free_sftp_dirents(d);
852 /* sftp ls.1 replacement which handles path globs */
854 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
861 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
863 memset(&g, 0, sizeof(g));
865 if (remote_glob(conn, path,
866 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
868 (g.gl_pathc && !g.gl_matchc)) {
871 error("Can't ls: \"%s\" not found", path);
879 * If the glob returns a single match and it is a directory,
880 * then just list its contents.
882 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
883 S_ISDIR(g.gl_statv[0]->st_mode)) {
884 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
889 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
892 if (!(lflag & LS_SHORT_VIEW)) {
893 /* Count entries for sort and find longest filename */
894 for (i = 0; g.gl_pathv[i]; i++)
895 m = MAX(m, strlen(g.gl_pathv[i]));
897 columns = width / (m + 2);
898 columns = MAX(columns, 1);
899 colspace = width / columns;
902 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
903 fname = path_strip(g.gl_pathv[i], strip_path);
904 if (lflag & LS_LONG_VIEW) {
905 if (g.gl_statv[i] == NULL) {
906 error("no stat information for %s", fname);
909 lname = ls_file(fname, g.gl_statv[i], 1,
910 (lflag & LS_SI_UNITS));
911 printf("%s\n", lname);
914 printf("%-*s", colspace, fname);
924 if (!(lflag & LS_LONG_VIEW) && (c != 1))
935 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
937 struct sftp_statvfs st;
938 char s_used[FMT_SCALED_STRSIZE];
939 char s_avail[FMT_SCALED_STRSIZE];
940 char s_root[FMT_SCALED_STRSIZE];
941 char s_total[FMT_SCALED_STRSIZE];
942 unsigned long long ffree;
944 if (do_statvfs(conn, path, &st, 1) == -1)
947 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
948 printf(" Inodes Used Avail "
949 "(root) %%Capacity\n");
950 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
951 (unsigned long long)st.f_files,
952 (unsigned long long)(st.f_files - st.f_ffree),
953 (unsigned long long)st.f_favail,
954 (unsigned long long)st.f_ffree, ffree);
956 strlcpy(s_used, "error", sizeof(s_used));
957 strlcpy(s_avail, "error", sizeof(s_avail));
958 strlcpy(s_root, "error", sizeof(s_root));
959 strlcpy(s_total, "error", sizeof(s_total));
960 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
961 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
962 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
963 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
964 printf(" Size Used Avail (root) %%Capacity\n");
965 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
966 s_total, s_used, s_avail, s_root,
967 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
970 printf(" Size Used Avail "
971 "(root) %%Capacity\n");
972 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
973 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
974 (unsigned long long)(st.f_frsize *
975 (st.f_blocks - st.f_bfree) / 1024),
976 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
977 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
978 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
985 * Undo escaping of glob sequences in place. Used to undo extra escaping
986 * applied in makeargv() when the string is destined for a function that
990 undo_glob_escape(char *s)
1025 * Split a string into an argument vector using sh(1)-style quoting,
1026 * comment and escaping rules, but with some tweaks to handle glob(3)
1028 * The "sloppy" flag allows for recovery from missing terminating quote, for
1029 * use in parsing incomplete commandlines during tab autocompletion.
1031 * Returns NULL on error or a NULL-terminated array of arguments.
1033 * If "lastquote" is not NULL, the quoting character used for the last
1034 * argument is placed in *lastquote ("\0", "'" or "\"").
1036 * If "terminated" is not NULL, *terminated will be set to 1 when the
1037 * last argument's quote has been properly terminated or 0 otherwise.
1038 * This parameter is only of use if "sloppy" is set.
1041 #define MAXARGLEN 8192
1043 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1048 static char argvs[MAXARGLEN];
1049 static char *argv[MAXARGS + 1];
1050 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1053 if (strlen(arg) > sizeof(argvs) - 1) {
1055 error("string too long");
1058 if (terminated != NULL)
1060 if (lastquote != NULL)
1065 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1066 error("Too many arguments.");
1069 if (isspace((unsigned char)arg[i])) {
1070 if (state == MA_UNQUOTED) {
1071 /* Terminate current argument */
1075 } else if (state != MA_START)
1076 argvs[j++] = arg[i];
1077 } else if (arg[i] == '"' || arg[i] == '\'') {
1078 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1079 if (state == MA_START) {
1080 argv[argc] = argvs + j;
1082 if (lastquote != NULL)
1083 *lastquote = arg[i];
1084 } else if (state == MA_UNQUOTED)
1086 else if (state == q)
1087 state = MA_UNQUOTED;
1089 argvs[j++] = arg[i];
1090 } else if (arg[i] == '\\') {
1091 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1092 quot = state == MA_SQUOTE ? '\'' : '"';
1093 /* Unescape quote we are in */
1094 /* XXX support \n and friends? */
1095 if (arg[i + 1] == quot) {
1097 argvs[j++] = arg[i];
1098 } else if (arg[i + 1] == '?' ||
1099 arg[i + 1] == '[' || arg[i + 1] == '*') {
1101 * Special case for sftp: append
1102 * double-escaped glob sequence -
1103 * glob will undo one level of
1104 * escaping. NB. string can grow here.
1106 if (j >= sizeof(argvs) - 5)
1107 goto args_too_longs;
1109 argvs[j++] = arg[i++];
1111 argvs[j++] = arg[i];
1113 argvs[j++] = arg[i++];
1114 argvs[j++] = arg[i];
1117 if (state == MA_START) {
1118 argv[argc] = argvs + j;
1119 state = MA_UNQUOTED;
1120 if (lastquote != NULL)
1123 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1124 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1126 * Special case for sftp: append
1127 * escaped glob sequence -
1128 * glob will undo one level of
1131 argvs[j++] = arg[i++];
1132 argvs[j++] = arg[i];
1134 /* Unescape everything */
1135 /* XXX support \n and friends? */
1137 argvs[j++] = arg[i];
1140 } else if (arg[i] == '#') {
1141 if (state == MA_SQUOTE || state == MA_DQUOTE)
1142 argvs[j++] = arg[i];
1145 } else if (arg[i] == '\0') {
1146 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1148 state = MA_UNQUOTED;
1149 if (terminated != NULL)
1153 error("Unterminated quoted argument");
1157 if (state == MA_UNQUOTED) {
1163 if (state == MA_START) {
1164 argv[argc] = argvs + j;
1165 state = MA_UNQUOTED;
1166 if (lastquote != NULL)
1169 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1170 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1172 * Special case for sftp: escape quoted
1173 * glob(3) wildcards. NB. string can grow
1176 if (j >= sizeof(argvs) - 3)
1177 goto args_too_longs;
1179 argvs[j++] = arg[i];
1181 argvs[j++] = arg[i];
1190 parse_args(const char **cpp, int *ignore_errors, int *aflag, int *fflag,
1191 int *hflag, int *iflag, int *lflag, int *pflag, int *rflag, int *sflag,
1192 unsigned long *n_arg, char **path1, char **path2)
1194 const char *cmd, *cp = *cpp;
1198 int i, cmdnum, optidx, argc;
1200 /* Skip leading whitespace */
1201 cp = cp + strspn(cp, WHITESPACE);
1203 /* Check for leading '-' (disable error processing) */
1208 cp = cp + strspn(cp, WHITESPACE);
1211 /* Ignore blank lines and lines which begin with comment '#' char */
1212 if (*cp == '\0' || *cp == '#')
1215 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1218 /* Figure out which command we have */
1219 for (i = 0; cmds[i].c != NULL; i++) {
1220 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1230 } else if (cmdnum == -1) {
1231 error("Invalid command.");
1235 /* Get arguments and parse flags */
1236 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1237 *rflag = *sflag = 0;
1238 *path1 = *path2 = NULL;
1244 if ((optidx = parse_getput_flags(cmd, argv, argc,
1245 aflag, fflag, pflag, rflag)) == -1)
1247 /* Get first pathname (mandatory) */
1248 if (argc - optidx < 1) {
1249 error("You must specify at least one path after a "
1250 "%s command.", cmd);
1253 *path1 = xstrdup(argv[optidx]);
1254 /* Get second pathname (optional) */
1255 if (argc - optidx > 1) {
1256 *path2 = xstrdup(argv[optidx + 1]);
1257 /* Destination is not globbed */
1258 undo_glob_escape(*path2);
1260 if (*aflag && cmdnum == I_PUT) {
1261 /* XXX implement resume for uploads */
1262 error("Resume is not supported for uploads");
1267 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1269 goto parse_two_paths;
1271 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1273 goto parse_two_paths;
1275 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1278 if (argc - optidx < 2) {
1279 error("You must specify two paths after a %s "
1283 *path1 = xstrdup(argv[optidx]);
1284 *path2 = xstrdup(argv[optidx + 1]);
1285 /* Paths are not globbed */
1286 undo_glob_escape(*path1);
1287 undo_glob_escape(*path2);
1295 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1297 /* Get pathname (mandatory) */
1298 if (argc - optidx < 1) {
1299 error("You must specify a path after a %s command.",
1303 *path1 = xstrdup(argv[optidx]);
1304 /* Only "rm" globs */
1306 undo_glob_escape(*path1);
1309 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1312 /* Default to current directory if no path specified */
1313 if (argc - optidx < 1)
1316 *path1 = xstrdup(argv[optidx]);
1317 undo_glob_escape(*path1);
1321 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1323 /* Path is optional */
1324 if (argc - optidx > 0)
1325 *path1 = xstrdup(argv[optidx]);
1328 /* Skip ls command and following whitespace */
1329 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1331 /* Uses the rest of the line */
1338 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1340 /* Get numeric arg (mandatory) */
1341 if (argc - optidx < 1)
1344 l = strtol(argv[optidx], &cp2, base);
1345 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1346 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1349 error("You must supply a numeric argument "
1350 "to the %s command.", cmd);
1354 if (cmdnum == I_LUMASK)
1356 /* Get pathname (mandatory) */
1357 if (argc - optidx < 2) {
1358 error("You must specify a path after a %s command.",
1362 *path1 = xstrdup(argv[optidx + 1]);
1370 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1374 fatal("Command not implemented");
1382 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1385 char *path1, *path2, *tmp;
1386 int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1387 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1389 unsigned long n_arg = 0;
1391 char path_buf[MAXPATHLEN];
1395 path1 = path2 = NULL;
1396 cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1397 &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1398 if (ignore_errors != 0)
1401 memset(&g, 0, sizeof(g));
1403 /* Perform command */
1409 /* Unrecognized command */
1416 err = process_get(conn, path1, path2, *pwd, pflag,
1417 rflag, aflag, fflag);
1420 err = process_put(conn, path1, path2, *pwd, pflag,
1424 path1 = make_absolute(path1, *pwd);
1425 path2 = make_absolute(path2, *pwd);
1426 err = do_rename(conn, path1, path2, lflag);
1432 path1 = make_absolute(path1, *pwd);
1433 path2 = make_absolute(path2, *pwd);
1434 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1437 path1 = make_absolute(path1, *pwd);
1438 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1439 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1441 printf("Removing %s\n", g.gl_pathv[i]);
1442 err = do_rm(conn, g.gl_pathv[i]);
1443 if (err != 0 && err_abort)
1448 path1 = make_absolute(path1, *pwd);
1450 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1452 err = do_mkdir(conn, path1, &a, 1);
1455 path1 = make_absolute(path1, *pwd);
1456 err = do_rmdir(conn, path1);
1459 path1 = make_absolute(path1, *pwd);
1460 if ((tmp = do_realpath(conn, path1)) == NULL) {
1464 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1469 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1470 error("Can't change directory: Can't check target");
1475 if (!S_ISDIR(aa->perm)) {
1476 error("Can't change directory: \"%s\" is not "
1477 "a directory", tmp);
1487 do_ls_dir(conn, *pwd, *pwd, lflag);
1491 /* Strip pwd off beginning of non-absolute paths */
1496 path1 = make_absolute(path1, *pwd);
1497 err = do_globbed_ls(conn, path1, tmp, lflag);
1500 /* Default to current directory if no path specified */
1502 path1 = xstrdup(*pwd);
1503 path1 = make_absolute(path1, *pwd);
1504 err = do_df(conn, path1, hflag, iflag);
1507 if (chdir(path1) == -1) {
1508 error("Couldn't change local directory to "
1509 "\"%s\": %s", path1, strerror(errno));
1514 if (mkdir(path1, 0777) == -1) {
1515 error("Couldn't create local directory "
1516 "\"%s\": %s", path1, strerror(errno));
1524 local_do_shell(cmd);
1528 printf("Local umask: %03lo\n", n_arg);
1531 path1 = make_absolute(path1, *pwd);
1533 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1535 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1536 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1538 printf("Changing mode on %s\n", g.gl_pathv[i]);
1539 err = do_setstat(conn, g.gl_pathv[i], &a);
1540 if (err != 0 && err_abort)
1546 path1 = make_absolute(path1, *pwd);
1547 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1548 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1549 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1556 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1557 error("Can't get current ownership of "
1558 "remote file \"%s\"", g.gl_pathv[i]);
1565 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1566 if (cmdnum == I_CHOWN) {
1568 printf("Changing owner on %s\n",
1573 printf("Changing group on %s\n",
1577 err = do_setstat(conn, g.gl_pathv[i], aa);
1578 if (err != 0 && err_abort)
1583 printf("Remote working directory: %s\n", *pwd);
1586 if (!getcwd(path_buf, sizeof(path_buf))) {
1587 error("Couldn't get local cwd: %s", strerror(errno));
1591 printf("Local working directory: %s\n", path_buf);
1594 /* Processed below */
1600 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1603 showprogress = !showprogress;
1605 printf("Progress meter enabled\n");
1607 printf("Progress meter disabled\n");
1610 fatal("%d is not implemented", cmdnum);
1618 /* If an unignored error occurs in batch mode we should abort. */
1619 if (err_abort && err != 0)
1621 else if (cmdnum == I_QUIT)
1629 prompt(EditLine *el)
1634 /* Display entries in 'list' after skipping the first 'len' chars */
1636 complete_display(char **list, u_int len)
1638 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1642 /* Count entries for sort and find longest */
1643 for (y = 0; list[y]; y++)
1644 m = MAX(m, strlen(list[y]));
1646 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1649 m = m > len ? m - len : 0;
1650 columns = width / (m + 2);
1651 columns = MAX(columns, 1);
1652 colspace = width / columns;
1653 colspace = MIN(colspace, width);
1657 for (y = 0; list[y]; y++) {
1658 llen = strlen(list[y]);
1659 tmp = llen > len ? list[y] + len : "";
1660 printf("%-*s", colspace, tmp);
1671 * Given a "list" of words that begin with a common prefix of "word",
1672 * attempt to find an autocompletion to extends "word" by the next
1673 * characters common to all entries in "list".
1676 complete_ambiguous(const char *word, char **list, size_t count)
1682 u_int y, matchlen = strlen(list[0]);
1684 /* Find length of common stem */
1685 for (y = 1; list[y]; y++) {
1688 for (x = 0; x < matchlen; x++)
1689 if (list[0][x] != list[y][x])
1695 if (matchlen > strlen(word)) {
1696 char *tmp = xstrdup(list[0]);
1698 tmp[matchlen] = '\0';
1703 return xstrdup(word);
1706 /* Autocomplete a sftp command */
1708 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1711 u_int y, count = 0, cmdlen, tmplen;
1712 char *tmp, **list, argterm[3];
1715 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1717 /* No command specified: display all available commands */
1719 for (y = 0; cmds[y].c; y++)
1720 list[count++] = xstrdup(cmds[y].c);
1723 complete_display(list, 0);
1725 for (y = 0; list[y] != NULL; y++)
1731 /* Prepare subset of commands that start with "cmd" */
1732 cmdlen = strlen(cmd);
1733 for (y = 0; cmds[y].c; y++) {
1734 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1735 list[count++] = xstrdup(cmds[y].c);
1744 /* Complete ambigious command */
1745 tmp = complete_ambiguous(cmd, list, count);
1747 complete_display(list, 0);
1749 for (y = 0; list[y]; y++)
1754 tmplen = strlen(tmp);
1755 cmdlen = strlen(cmd);
1756 /* If cmd may be extended then do so */
1757 if (tmplen > cmdlen)
1758 if (el_insertstr(el, tmp + cmdlen) == -1)
1759 fatal("el_insertstr failed.");
1761 /* Terminate argument cleanly */
1765 argterm[y++] = quote;
1766 if (lastarg || *(lf->cursor) != ' ')
1769 if (y > 0 && el_insertstr(el, argterm) == -1)
1770 fatal("el_insertstr failed.");
1779 * Determine whether a particular sftp command's arguments (if any)
1780 * represent local or remote files.
1783 complete_is_remote(char *cmd) {
1789 for (i = 0; cmds[i].c; i++) {
1790 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1797 /* Autocomplete a filename "file" */
1799 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1800 char *file, int remote, int lastarg, char quote, int terminated)
1803 char *tmp, *tmp2, ins[8];
1804 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1808 /* Glob from "file" location */
1812 xasprintf(&tmp, "%s*", file);
1814 /* Check if the path is absolute. */
1815 isabs = tmp[0] == '/';
1817 memset(&g, 0, sizeof(g));
1818 if (remote != LOCAL) {
1819 tmp = make_absolute(tmp, remote_path);
1820 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1822 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1824 /* Determine length of pwd so we can trim completion display */
1825 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1826 /* Terminate counting on first unescaped glob metacharacter */
1827 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1828 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1832 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1834 if (tmp[tmplen] == '/')
1835 pwdlen = tmplen + 1; /* track last seen '/' */
1839 if (g.gl_matchc == 0)
1842 if (g.gl_matchc > 1)
1843 complete_display(g.gl_pathv, pwdlen);
1846 /* Don't try to extend globs */
1847 if (file == NULL || hadglob)
1850 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1851 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1857 tmplen = strlen(tmp);
1858 filelen = strlen(file);
1860 /* Count the number of escaped characters in the input string. */
1862 for (i = 0; i < filelen; i++) {
1863 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1870 if (tmplen > (filelen - cesc)) {
1871 tmp2 = tmp + filelen - cesc;
1873 /* quote argument on way out */
1874 for (i = 0; i < len; i += clen) {
1875 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1876 (size_t)clen > sizeof(ins) - 2)
1877 fatal("invalid multibyte character");
1879 memcpy(ins + 1, tmp2 + i, clen);
1880 ins[clen + 1] = '\0';
1890 if (quote == '\0' || tmp2[i] == quote) {
1891 if (el_insertstr(el, ins) == -1)
1892 fatal("el_insertstr "
1898 if (el_insertstr(el, ins + 1) == -1)
1899 fatal("el_insertstr failed.");
1906 if (g.gl_matchc == 1) {
1910 if (*(lf->cursor - 1) != '/' &&
1911 (lastarg || *(lf->cursor) != ' '))
1914 if (i > 0 && el_insertstr(el, ins) == -1)
1915 fatal("el_insertstr failed.");
1924 /* tab-completion hook function, called via libedit */
1925 static unsigned char
1926 complete(EditLine *el, int ch)
1928 char **argv, *line, quote;
1930 u_int cursor, len, terminated, ret = CC_ERROR;
1932 struct complete_ctx *complete_ctx;
1935 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1936 fatal("%s: el_get failed", __func__);
1938 /* Figure out which argument the cursor points to */
1939 cursor = lf->cursor - lf->buffer;
1940 line = (char *)xmalloc(cursor + 1);
1941 memcpy(line, lf->buffer, cursor);
1942 line[cursor] = '\0';
1943 argv = makeargv(line, &carg, 1, "e, &terminated);
1946 /* Get all the arguments on the line */
1947 len = lf->lastchar - lf->buffer;
1948 line = (char *)xmalloc(len + 1);
1949 memcpy(line, lf->buffer, len);
1951 argv = makeargv(line, &argc, 1, NULL, NULL);
1953 /* Ensure cursor is at EOL or a argument boundary */
1954 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1955 line[cursor] != '\n') {
1961 /* Show all available commands */
1962 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1964 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1965 /* Handle the command parsing */
1966 if (complete_cmd_parse(el, argv[0], argc == carg,
1967 quote, terminated) != 0)
1969 } else if (carg >= 1) {
1970 /* Handle file parsing */
1971 int remote = complete_is_remote(argv[0]);
1972 char *filematch = NULL;
1974 if (carg > 1 && line[cursor-1] != ' ')
1975 filematch = argv[carg - 1];
1978 complete_match(el, complete_ctx->conn,
1979 *complete_ctx->remote_pathp, filematch,
1980 remote, carg == argc, quote, terminated) != 0)
1987 #endif /* USE_LIBEDIT */
1990 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1995 int err, interactive;
1996 EditLine *el = NULL;
2000 extern char *__progname;
2001 struct complete_ctx complete_ctx;
2003 if (!batchmode && isatty(STDIN_FILENO)) {
2004 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2005 fatal("Couldn't initialise editline");
2006 if ((hl = history_init()) == NULL)
2007 fatal("Couldn't initialise editline history");
2008 history(hl, &hev, H_SETSIZE, 100);
2009 el_set(el, EL_HIST, history, hl);
2011 el_set(el, EL_PROMPT, prompt);
2012 el_set(el, EL_EDITOR, "emacs");
2013 el_set(el, EL_TERMINAL, NULL);
2014 el_set(el, EL_SIGNAL, 1);
2015 el_source(el, NULL);
2017 /* Tab Completion */
2018 el_set(el, EL_ADDFN, "ftp-complete",
2019 "Context sensitive argument completion", complete);
2020 complete_ctx.conn = conn;
2021 complete_ctx.remote_pathp = &remote_path;
2022 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2023 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2024 /* enable ctrl-left-arrow and ctrl-right-arrow */
2025 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2026 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2027 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2028 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2029 /* make ^w match ksh behaviour */
2030 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2032 #endif /* USE_LIBEDIT */
2034 remote_path = do_realpath(conn, ".");
2035 if (remote_path == NULL)
2038 if (file1 != NULL) {
2039 dir = xstrdup(file1);
2040 dir = make_absolute(dir, remote_path);
2042 if (remote_is_dir(conn, dir) && file2 == NULL) {
2044 printf("Changing to: %s\n", dir);
2045 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2046 if (parse_dispatch_command(conn, cmd,
2047 &remote_path, 1) != 0) {
2054 /* XXX this is wrong wrt quoting */
2055 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2056 global_aflag ? " -a" : "", dir,
2057 file2 == NULL ? "" : " ",
2058 file2 == NULL ? "" : file2);
2059 err = parse_dispatch_command(conn, cmd,
2072 interactive = !batchmode && isatty(STDIN_FILENO);
2077 signal(SIGINT, SIG_IGN);
2082 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2087 if (!interactive) { /* Echo command */
2088 printf("sftp> %s", cmd);
2089 if (strlen(cmd) > 0 &&
2090 cmd[strlen(cmd) - 1] != '\n')
2098 if ((line = el_gets(el, &count)) == NULL ||
2103 history(hl, &hev, H_ENTER, line);
2104 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2105 fprintf(stderr, "Error: input line too long\n");
2108 #endif /* USE_LIBEDIT */
2111 cp = strrchr(cmd, '\n');
2115 /* Handle user interrupts gracefully during commands */
2117 signal(SIGINT, cmd_interrupt);
2119 err = parse_dispatch_command(conn, cmd, &remote_path,
2130 #endif /* USE_LIBEDIT */
2132 /* err == 1 signifies normal "quit" exit */
2133 return (err >= 0 ? 0 : -1);
2137 connect_to_server(char *path, char **args, int *in, int *out)
2142 int pin[2], pout[2];
2144 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2145 fatal("pipe: %s", strerror(errno));
2150 #else /* USE_PIPES */
2153 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2154 fatal("socketpair: %s", strerror(errno));
2155 *in = *out = inout[0];
2156 c_in = c_out = inout[1];
2157 #endif /* USE_PIPES */
2159 if ((sshpid = fork()) == -1)
2160 fatal("fork: %s", strerror(errno));
2161 else if (sshpid == 0) {
2162 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2163 (dup2(c_out, STDOUT_FILENO) == -1)) {
2164 fprintf(stderr, "dup2: %s\n", strerror(errno));
2173 * The underlying ssh is in the same process group, so we must
2174 * ignore SIGINT if we want to gracefully abort commands,
2175 * otherwise the signal will make it to the ssh process and
2176 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2177 * underlying ssh, it must *not* ignore that signal.
2179 signal(SIGINT, SIG_IGN);
2180 signal(SIGTERM, SIG_DFL);
2182 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2186 signal(SIGTERM, killchild);
2187 signal(SIGINT, killchild);
2188 signal(SIGHUP, killchild);
2196 extern char *__progname;
2199 "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2200 " [-D sftp_server_path] [-F ssh_config] "
2201 "[-i identity_file] [-l limit]\n"
2202 " [-o ssh_option] [-P port] [-R num_requests] "
2204 " [-s subsystem | sftp_server] host\n"
2205 " %s [user@]host[:file ...]\n"
2206 " %s [user@]host[:dir[/]]\n"
2207 " %s -b batchfile [user@]host\n",
2208 __progname, __progname, __progname, __progname);
2213 main(int argc, char **argv)
2215 int in, out, ch, err;
2216 char *host = NULL, *userhost, *cp, *file2 = NULL;
2217 int debug_level = 0, sshver = 2;
2218 char *file1 = NULL, *sftp_server = NULL;
2219 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2221 LogLevel ll = SYSLOG_LEVEL_INFO;
2224 extern char *optarg;
2225 struct sftp_conn *conn;
2226 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2227 size_t num_requests = DEFAULT_NUM_REQUESTS;
2228 long long limit_kbps = 0;
2230 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2232 setlocale(LC_CTYPE, "");
2234 __progname = ssh_get_progname(argv[0]);
2235 memset(&args, '\0', sizeof(args));
2237 addargs(&args, "%s", ssh_program);
2238 addargs(&args, "-oForwardX11 no");
2239 addargs(&args, "-oForwardAgent no");
2240 addargs(&args, "-oPermitLocalCommand no");
2241 addargs(&args, "-oClearAllForwardings yes");
2243 ll = SYSLOG_LEVEL_INFO;
2246 while ((ch = getopt(argc, argv,
2247 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2249 /* Passed through to ssh(1) */
2253 addargs(&args, "-%c", ch);
2255 /* Passed through to ssh(1) with argument */
2260 addargs(&args, "-%c", ch);
2261 addargs(&args, "%s", optarg);
2264 ll = SYSLOG_LEVEL_ERROR;
2267 addargs(&args, "-%c", ch);
2270 addargs(&args, "-oPort %s", optarg);
2273 if (debug_level < 3) {
2274 addargs(&args, "-v");
2275 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2281 if (sftp_server == NULL)
2282 sftp_server = _PATH_SFTP_SERVER;
2291 copy_buffer_len = strtol(optarg, &cp, 10);
2292 if (copy_buffer_len == 0 || *cp != '\0')
2293 fatal("Invalid buffer size \"%s\"", optarg);
2297 fatal("Batch file already specified.");
2299 /* Allow "-" as stdin */
2300 if (strcmp(optarg, "-") != 0 &&
2301 (infile = fopen(optarg, "r")) == NULL)
2302 fatal("%s (%s).", strerror(errno), optarg);
2304 quiet = batchmode = 1;
2305 addargs(&args, "-obatchmode yes");
2314 sftp_direct = optarg;
2317 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2321 limit_kbps *= 1024; /* kbps */
2327 num_requests = strtol(optarg, &cp, 10);
2328 if (num_requests == 0 || *cp != '\0')
2329 fatal("Invalid number of requests \"%s\"",
2333 sftp_server = optarg;
2336 ssh_program = optarg;
2337 replacearg(&args, 0, "%s", ssh_program);
2345 if (!isatty(STDERR_FILENO))
2348 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2350 if (sftp_direct == NULL) {
2351 if (optind == argc || argc > (optind + 2))
2354 userhost = xstrdup(argv[optind]);
2355 file2 = argv[optind+1];
2357 if ((host = strrchr(userhost, '@')) == NULL)
2362 fprintf(stderr, "Missing username\n");
2365 addargs(&args, "-l");
2366 addargs(&args, "%s", userhost);
2369 if ((cp = colon(host)) != NULL) {
2374 host = cleanhostname(host);
2376 fprintf(stderr, "Missing hostname\n");
2380 addargs(&args, "-oProtocol %d", sshver);
2382 /* no subsystem if the server-spec contains a '/' */
2383 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2384 addargs(&args, "-s");
2386 addargs(&args, "--");
2387 addargs(&args, "%s", host);
2388 addargs(&args, "%s", (sftp_server != NULL ?
2389 sftp_server : "sftp"));
2391 connect_to_server(ssh_program, args.list, &in, &out);
2394 addargs(&args, "sftp-server");
2396 connect_to_server(sftp_direct, args.list, &in, &out);
2400 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2402 fatal("Couldn't initialise connection to server");
2405 if (sftp_direct == NULL)
2406 fprintf(stderr, "Connected to %s.\n", host);
2408 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2411 err = interactive_loop(conn, file1, file2);
2413 #if !defined(USE_PIPES)
2414 shutdown(in, SHUT_RDWR);
2415 shutdown(out, SHUT_RDWR);
2423 while (waitpid(sshpid, NULL, 0) == -1)
2425 fatal("Couldn't wait for ssh process: %s",
2428 exit(err == 0 ? 0 : 1);