1 /* $OpenBSD: sftp.c,v 1.148 2013/07/25 00:56:52 djm Exp $ */
4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #ifdef HAVE_SYS_STAT_H
24 # include <sys/stat.h>
26 #include <sys/param.h>
27 #include <sys/socket.h>
29 #ifdef HAVE_SYS_STATVFS_H
30 #include <sys/statvfs.h>
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 /* SIGINT received during command processing */
99 volatile sig_atomic_t interrupted = 0;
101 /* I wish qsort() took a separate ctx for the comparison function...*/
104 /* Context used for commandline completion */
105 struct complete_ctx {
106 struct sftp_conn *conn;
110 int remote_glob(struct sftp_conn *, const char *, int,
111 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
113 extern char *__progname;
115 /* Separators for interactive commands */
116 #define WHITESPACE " \t\r\n"
119 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
120 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
121 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
122 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
123 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
124 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
125 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
126 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
127 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
129 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
130 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
132 /* Commands for interactive mode */
157 #define I_PROGRESS 23
166 /* Type of completion */
171 static const struct CMD cmds[] = {
172 { "bye", I_QUIT, NOARGS },
173 { "cd", I_CHDIR, REMOTE },
174 { "chdir", I_CHDIR, REMOTE },
175 { "chgrp", I_CHGRP, REMOTE },
176 { "chmod", I_CHMOD, REMOTE },
177 { "chown", I_CHOWN, REMOTE },
178 { "df", I_DF, REMOTE },
179 { "dir", I_LS, REMOTE },
180 { "exit", I_QUIT, NOARGS },
181 { "get", I_GET, REMOTE },
182 { "help", I_HELP, NOARGS },
183 { "lcd", I_LCHDIR, LOCAL },
184 { "lchdir", I_LCHDIR, LOCAL },
185 { "lls", I_LLS, LOCAL },
186 { "lmkdir", I_LMKDIR, LOCAL },
187 { "ln", I_LINK, REMOTE },
188 { "lpwd", I_LPWD, LOCAL },
189 { "ls", I_LS, REMOTE },
190 { "lumask", I_LUMASK, NOARGS },
191 { "mkdir", I_MKDIR, REMOTE },
192 { "mget", I_GET, REMOTE },
193 { "mput", I_PUT, LOCAL },
194 { "progress", I_PROGRESS, NOARGS },
195 { "put", I_PUT, LOCAL },
196 { "pwd", I_PWD, REMOTE },
197 { "quit", I_QUIT, NOARGS },
198 { "reget", I_REGET, REMOTE },
199 { "rename", I_RENAME, REMOTE },
200 { "rm", I_RM, REMOTE },
201 { "rmdir", I_RMDIR, REMOTE },
202 { "symlink", I_SYMLINK, REMOTE },
203 { "version", I_VERSION, NOARGS },
204 { "!", I_SHELL, NOARGS },
205 { "?", I_HELP, NOARGS },
209 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
216 kill(sshpid, SIGTERM);
217 waitpid(sshpid, NULL, 0);
225 cmd_interrupt(int signo)
227 const char msg[] = "\rInterrupt \n";
228 int olderrno = errno;
230 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
238 printf("Available commands:\n"
240 "cd path Change remote directory to 'path'\n"
241 "chgrp grp path Change group of file 'path' to 'grp'\n"
242 "chmod mode path Change permissions of file 'path' to 'mode'\n"
243 "chown own path Change owner of file 'path' to 'own'\n"
244 "df [-hi] [path] Display statistics for current directory or\n"
245 " filesystem containing 'path'\n"
247 "get [-Ppr] remote [local] Download file\n"
248 "reget remote [local] Resume download file\n"
249 "help Display this help text\n"
250 "lcd path Change local directory to 'path'\n"
251 "lls [ls-options [path]] Display local directory listing\n"
252 "lmkdir path Create local directory\n"
253 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
254 "lpwd Print local working directory\n"
255 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
256 "lumask umask Set local umask to 'umask'\n"
257 "mkdir path Create remote directory\n"
258 "progress Toggle display of progress meter\n"
259 "put [-Ppr] local [remote] Upload file\n"
260 "pwd Display remote working directory\n"
262 "rename oldpath newpath Rename remote file\n"
263 "rm path Delete remote file\n"
264 "rmdir path Remove remote directory\n"
265 "symlink oldpath newpath Symlink remote file\n"
266 "version Show SFTP version\n"
267 "!command Execute 'command' in local shell\n"
268 "! Escape to local shell\n"
269 "? Synonym for help\n");
273 local_do_shell(const char *args)
282 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
283 shell = _PATH_BSHELL;
285 if ((pid = fork()) == -1)
286 fatal("Couldn't fork: %s", strerror(errno));
289 /* XXX: child has pipe fds to ssh subproc open - issue? */
291 debug3("Executing %s -c \"%s\"", shell, args);
292 execl(shell, shell, "-c", args, (char *)NULL);
294 debug3("Executing %s", shell);
295 execl(shell, shell, (char *)NULL);
297 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
301 while (waitpid(pid, &status, 0) == -1)
303 fatal("Couldn't wait for child: %s", strerror(errno));
304 if (!WIFEXITED(status))
305 error("Shell exited abnormally");
306 else if (WEXITSTATUS(status))
307 error("Shell exited with status %d", WEXITSTATUS(status));
311 local_do_ls(const char *args)
314 local_do_shell(_PATH_LS);
316 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
317 char *buf = xmalloc(len);
319 /* XXX: quoting - rip quoting code from ftp? */
320 snprintf(buf, len, _PATH_LS " %s", args);
326 /* Strip one path (usually the pwd) from the start of another */
328 path_strip(char *path, char *strip)
333 return (xstrdup(path));
336 if (strncmp(path, strip, len) == 0) {
337 if (strip[len - 1] != '/' && path[len] == '/')
339 return (xstrdup(path + len));
342 return (xstrdup(path));
346 make_absolute(char *p, char *pwd)
351 if (p && p[0] != '/') {
352 abs_str = path_append(pwd, p);
360 parse_getput_flags(const char *cmd, char **argv, int argc,
361 int *aflag, int *pflag, int *rflag)
363 extern int opterr, optind, optopt, optreset;
366 optind = optreset = 1;
369 *aflag = *rflag = *pflag = 0;
370 while ((ch = getopt(argc, argv, "aPpRr")) != -1) {
384 error("%s: Invalid flag -%c", cmd, optopt);
393 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
395 extern int opterr, optind, optopt, optreset;
398 optind = optreset = 1;
402 while ((ch = getopt(argc, argv, "s")) != -1) {
408 error("%s: Invalid flag -%c", cmd, optopt);
417 parse_ls_flags(char **argv, int argc, int *lflag)
419 extern int opterr, optind, optopt, optreset;
422 optind = optreset = 1;
425 *lflag = LS_NAME_SORT;
426 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
429 *lflag &= ~VIEW_FLAGS;
430 *lflag |= LS_SHORT_VIEW;
433 *lflag &= ~SORT_FLAGS;
434 *lflag |= LS_SIZE_SORT;
437 *lflag |= LS_SHOW_ALL;
440 *lflag &= ~SORT_FLAGS;
443 *lflag |= LS_SI_UNITS;
446 *lflag &= ~LS_SHORT_VIEW;
447 *lflag |= LS_LONG_VIEW;
450 *lflag &= ~LS_SHORT_VIEW;
451 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
454 *lflag |= LS_REVERSE_SORT;
457 *lflag &= ~SORT_FLAGS;
458 *lflag |= LS_TIME_SORT;
461 error("ls: Invalid flag -%c", optopt);
470 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
472 extern int opterr, optind, optopt, optreset;
475 optind = optreset = 1;
479 while ((ch = getopt(argc, argv, "hi")) != -1) {
488 error("%s: Invalid flag -%c", cmd, optopt);
501 /* XXX: report errors? */
502 if (stat(path, &sb) == -1)
505 return(S_ISDIR(sb.st_mode));
509 remote_is_dir(struct sftp_conn *conn, char *path)
513 /* XXX: report errors? */
514 if ((a = do_stat(conn, path, 1)) == NULL)
516 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
518 return(S_ISDIR(a->perm));
521 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
523 pathname_is_dir(char *pathname)
525 size_t l = strlen(pathname);
527 return l > 0 && pathname[l - 1] == '/';
531 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
532 int pflag, int rflag, int resume)
534 char *abs_src = NULL;
535 char *abs_dst = NULL;
537 char *filename, *tmp=NULL;
540 abs_src = xstrdup(src);
541 abs_src = make_absolute(abs_src, pwd);
542 memset(&g, 0, sizeof(g));
544 debug3("Looking up %s", abs_src);
545 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
546 error("File \"%s\" not found.", abs_src);
552 * If multiple matches then dst must be a directory or
555 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
556 error("Multiple source paths, but destination "
557 "\"%s\" is not a directory", dst);
562 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
563 tmp = xstrdup(g.gl_pathv[i]);
564 if ((filename = basename(tmp)) == NULL) {
565 error("basename %s: %s", tmp, strerror(errno));
571 if (g.gl_matchc == 1 && dst) {
573 abs_dst = path_append(dst, filename);
575 abs_dst = xstrdup(dst);
578 abs_dst = path_append(dst, filename);
580 abs_dst = xstrdup(filename);
584 resume |= global_aflag;
585 if (!quiet && resume)
586 printf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst);
587 else if (!quiet && !resume)
588 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
589 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
590 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
591 pflag || global_pflag, 1, resume) == -1)
594 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
595 pflag || global_pflag, resume) == -1)
609 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
610 int pflag, int rflag)
612 char *tmp_dst = NULL;
613 char *abs_dst = NULL;
614 char *tmp = NULL, *filename = NULL;
617 int i, dst_is_dir = 1;
621 tmp_dst = xstrdup(dst);
622 tmp_dst = make_absolute(tmp_dst, pwd);
625 memset(&g, 0, sizeof(g));
626 debug3("Looking up %s", src);
627 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
628 error("File \"%s\" not found.", src);
633 /* If we aren't fetching to pwd then stash this status for later */
635 dst_is_dir = remote_is_dir(conn, tmp_dst);
637 /* If multiple matches, dst may be directory or unspecified */
638 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
639 error("Multiple paths match, but destination "
640 "\"%s\" is not a directory", tmp_dst);
645 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
646 if (stat(g.gl_pathv[i], &sb) == -1) {
648 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
652 tmp = xstrdup(g.gl_pathv[i]);
653 if ((filename = basename(tmp)) == NULL) {
654 error("basename %s: %s", tmp, strerror(errno));
660 if (g.gl_matchc == 1 && tmp_dst) {
661 /* If directory specified, append filename */
663 abs_dst = path_append(tmp_dst, filename);
665 abs_dst = xstrdup(tmp_dst);
666 } else if (tmp_dst) {
667 abs_dst = path_append(tmp_dst, filename);
669 abs_dst = make_absolute(xstrdup(filename), pwd);
674 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
675 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
676 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
677 pflag || global_pflag, 1) == -1)
680 if (do_upload(conn, g.gl_pathv[i], abs_dst,
681 pflag || global_pflag) == -1)
694 sdirent_comp(const void *aa, const void *bb)
696 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
697 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
698 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
700 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
701 if (sort_flag & LS_NAME_SORT)
702 return (rmul * strcmp(a->filename, b->filename));
703 else if (sort_flag & LS_TIME_SORT)
704 return (rmul * NCMP(a->a.mtime, b->a.mtime));
705 else if (sort_flag & LS_SIZE_SORT)
706 return (rmul * NCMP(a->a.size, b->a.size));
708 fatal("Unknown ls sort type");
711 /* sftp ls.1 replacement for directories */
713 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
716 u_int c = 1, colspace = 0, columns = 1;
719 if ((n = do_readdir(conn, path, &d)) != 0)
722 if (!(lflag & LS_SHORT_VIEW)) {
723 u_int m = 0, width = 80;
727 /* Count entries for sort and find longest filename */
728 for (n = 0; d[n] != NULL; n++) {
729 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
730 m = MAX(m, strlen(d[n]->filename));
733 /* Add any subpath that also needs to be counted */
734 tmp = path_strip(path, strip_path);
738 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
741 columns = width / (m + 2);
742 columns = MAX(columns, 1);
743 colspace = width / columns;
744 colspace = MIN(colspace, width);
747 if (lflag & SORT_FLAGS) {
748 for (n = 0; d[n] != NULL; n++)
749 ; /* count entries */
750 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
751 qsort(d, n, sizeof(*d), sdirent_comp);
754 for (n = 0; d[n] != NULL && !interrupted; n++) {
757 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
760 tmp = path_append(path, d[n]->filename);
761 fname = path_strip(tmp, strip_path);
764 if (lflag & LS_LONG_VIEW) {
765 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
769 memset(&sb, 0, sizeof(sb));
770 attrib_to_stat(&d[n]->a, &sb);
771 lname = ls_file(fname, &sb, 1,
772 (lflag & LS_SI_UNITS));
773 printf("%s\n", lname);
776 printf("%s\n", d[n]->longname);
778 printf("%-*s", colspace, fname);
789 if (!(lflag & LS_LONG_VIEW) && (c != 1))
792 free_sftp_dirents(d);
796 /* sftp ls.1 replacement which handles path globs */
798 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
805 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
807 memset(&g, 0, sizeof(g));
809 if (remote_glob(conn, path,
810 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
812 (g.gl_pathc && !g.gl_matchc)) {
815 error("Can't ls: \"%s\" not found", path);
823 * If the glob returns a single match and it is a directory,
824 * then just list its contents.
826 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
827 S_ISDIR(g.gl_statv[0]->st_mode)) {
828 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
833 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
836 if (!(lflag & LS_SHORT_VIEW)) {
837 /* Count entries for sort and find longest filename */
838 for (i = 0; g.gl_pathv[i]; i++)
839 m = MAX(m, strlen(g.gl_pathv[i]));
841 columns = width / (m + 2);
842 columns = MAX(columns, 1);
843 colspace = width / columns;
846 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
847 fname = path_strip(g.gl_pathv[i], strip_path);
848 if (lflag & LS_LONG_VIEW) {
849 if (g.gl_statv[i] == NULL) {
850 error("no stat information for %s", fname);
853 lname = ls_file(fname, g.gl_statv[i], 1,
854 (lflag & LS_SI_UNITS));
855 printf("%s\n", lname);
858 printf("%-*s", colspace, fname);
868 if (!(lflag & LS_LONG_VIEW) && (c != 1))
879 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
881 struct sftp_statvfs st;
882 char s_used[FMT_SCALED_STRSIZE];
883 char s_avail[FMT_SCALED_STRSIZE];
884 char s_root[FMT_SCALED_STRSIZE];
885 char s_total[FMT_SCALED_STRSIZE];
886 unsigned long long ffree;
888 if (do_statvfs(conn, path, &st, 1) == -1)
891 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
892 printf(" Inodes Used Avail "
893 "(root) %%Capacity\n");
894 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
895 (unsigned long long)st.f_files,
896 (unsigned long long)(st.f_files - st.f_ffree),
897 (unsigned long long)st.f_favail,
898 (unsigned long long)st.f_ffree, ffree);
900 strlcpy(s_used, "error", sizeof(s_used));
901 strlcpy(s_avail, "error", sizeof(s_avail));
902 strlcpy(s_root, "error", sizeof(s_root));
903 strlcpy(s_total, "error", sizeof(s_total));
904 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
905 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
906 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
907 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
908 printf(" Size Used Avail (root) %%Capacity\n");
909 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
910 s_total, s_used, s_avail, s_root,
911 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
914 printf(" Size Used Avail "
915 "(root) %%Capacity\n");
916 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
917 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
918 (unsigned long long)(st.f_frsize *
919 (st.f_blocks - st.f_bfree) / 1024),
920 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
921 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
922 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
929 * Undo escaping of glob sequences in place. Used to undo extra escaping
930 * applied in makeargv() when the string is destined for a function that
934 undo_glob_escape(char *s)
969 * Split a string into an argument vector using sh(1)-style quoting,
970 * comment and escaping rules, but with some tweaks to handle glob(3)
972 * The "sloppy" flag allows for recovery from missing terminating quote, for
973 * use in parsing incomplete commandlines during tab autocompletion.
975 * Returns NULL on error or a NULL-terminated array of arguments.
977 * If "lastquote" is not NULL, the quoting character used for the last
978 * argument is placed in *lastquote ("\0", "'" or "\"").
980 * If "terminated" is not NULL, *terminated will be set to 1 when the
981 * last argument's quote has been properly terminated or 0 otherwise.
982 * This parameter is only of use if "sloppy" is set.
985 #define MAXARGLEN 8192
987 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
992 static char argvs[MAXARGLEN];
993 static char *argv[MAXARGS + 1];
994 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
997 if (strlen(arg) > sizeof(argvs) - 1) {
999 error("string too long");
1002 if (terminated != NULL)
1004 if (lastquote != NULL)
1009 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1010 error("Too many arguments.");
1013 if (isspace(arg[i])) {
1014 if (state == MA_UNQUOTED) {
1015 /* Terminate current argument */
1019 } else if (state != MA_START)
1020 argvs[j++] = arg[i];
1021 } else if (arg[i] == '"' || arg[i] == '\'') {
1022 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1023 if (state == MA_START) {
1024 argv[argc] = argvs + j;
1026 if (lastquote != NULL)
1027 *lastquote = arg[i];
1028 } else if (state == MA_UNQUOTED)
1030 else if (state == q)
1031 state = MA_UNQUOTED;
1033 argvs[j++] = arg[i];
1034 } else if (arg[i] == '\\') {
1035 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1036 quot = state == MA_SQUOTE ? '\'' : '"';
1037 /* Unescape quote we are in */
1038 /* XXX support \n and friends? */
1039 if (arg[i + 1] == quot) {
1041 argvs[j++] = arg[i];
1042 } else if (arg[i + 1] == '?' ||
1043 arg[i + 1] == '[' || arg[i + 1] == '*') {
1045 * Special case for sftp: append
1046 * double-escaped glob sequence -
1047 * glob will undo one level of
1048 * escaping. NB. string can grow here.
1050 if (j >= sizeof(argvs) - 5)
1051 goto args_too_longs;
1053 argvs[j++] = arg[i++];
1055 argvs[j++] = arg[i];
1057 argvs[j++] = arg[i++];
1058 argvs[j++] = arg[i];
1061 if (state == MA_START) {
1062 argv[argc] = argvs + j;
1063 state = MA_UNQUOTED;
1064 if (lastquote != NULL)
1067 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1068 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1070 * Special case for sftp: append
1071 * escaped glob sequence -
1072 * glob will undo one level of
1075 argvs[j++] = arg[i++];
1076 argvs[j++] = arg[i];
1078 /* Unescape everything */
1079 /* XXX support \n and friends? */
1081 argvs[j++] = arg[i];
1084 } else if (arg[i] == '#') {
1085 if (state == MA_SQUOTE || state == MA_DQUOTE)
1086 argvs[j++] = arg[i];
1089 } else if (arg[i] == '\0') {
1090 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1092 state = MA_UNQUOTED;
1093 if (terminated != NULL)
1097 error("Unterminated quoted argument");
1101 if (state == MA_UNQUOTED) {
1107 if (state == MA_START) {
1108 argv[argc] = argvs + j;
1109 state = MA_UNQUOTED;
1110 if (lastquote != NULL)
1113 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1114 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1116 * Special case for sftp: escape quoted
1117 * glob(3) wildcards. NB. string can grow
1120 if (j >= sizeof(argvs) - 3)
1121 goto args_too_longs;
1123 argvs[j++] = arg[i];
1125 argvs[j++] = arg[i];
1134 parse_args(const char **cpp, int *aflag, int *hflag, int *iflag, int *lflag,
1135 int *pflag, int *rflag, int *sflag, unsigned long *n_arg,
1136 char **path1, char **path2)
1138 const char *cmd, *cp = *cpp;
1142 int i, cmdnum, optidx, argc;
1144 /* Skip leading whitespace */
1145 cp = cp + strspn(cp, WHITESPACE);
1147 /* Check for leading '-' (disable error processing) */
1152 cp = cp + strspn(cp, WHITESPACE);
1155 /* Ignore blank lines and lines which begin with comment '#' char */
1156 if (*cp == '\0' || *cp == '#')
1159 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1162 /* Figure out which command we have */
1163 for (i = 0; cmds[i].c != NULL; i++) {
1164 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1174 } else if (cmdnum == -1) {
1175 error("Invalid command.");
1179 /* Get arguments and parse flags */
1180 *aflag = *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1181 *path1 = *path2 = NULL;
1187 if ((optidx = parse_getput_flags(cmd, argv, argc,
1188 aflag, pflag, rflag)) == -1)
1190 /* Get first pathname (mandatory) */
1191 if (argc - optidx < 1) {
1192 error("You must specify at least one path after a "
1193 "%s command.", cmd);
1196 *path1 = xstrdup(argv[optidx]);
1197 /* Get second pathname (optional) */
1198 if (argc - optidx > 1) {
1199 *path2 = xstrdup(argv[optidx + 1]);
1200 /* Destination is not globbed */
1201 undo_glob_escape(*path2);
1203 if (*aflag && cmdnum == I_PUT) {
1204 /* XXX implement resume for uploads */
1205 error("Resume is not supported for uploads");
1210 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1214 if (argc - optidx < 2) {
1215 error("You must specify two paths after a %s "
1219 *path1 = xstrdup(argv[optidx]);
1220 *path2 = xstrdup(argv[optidx + 1]);
1221 /* Paths are not globbed */
1222 undo_glob_escape(*path1);
1223 undo_glob_escape(*path2);
1231 /* Get pathname (mandatory) */
1232 if (argc - optidx < 1) {
1233 error("You must specify a path after a %s command.",
1237 *path1 = xstrdup(argv[optidx]);
1238 /* Only "rm" globs */
1240 undo_glob_escape(*path1);
1243 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1246 /* Default to current directory if no path specified */
1247 if (argc - optidx < 1)
1250 *path1 = xstrdup(argv[optidx]);
1251 undo_glob_escape(*path1);
1255 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1257 /* Path is optional */
1258 if (argc - optidx > 0)
1259 *path1 = xstrdup(argv[optidx]);
1262 /* Skip ls command and following whitespace */
1263 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1265 /* Uses the rest of the line */
1272 /* Get numeric arg (mandatory) */
1273 if (argc - optidx < 1)
1276 l = strtol(argv[optidx], &cp2, base);
1277 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1278 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1281 error("You must supply a numeric argument "
1282 "to the %s command.", cmd);
1286 if (cmdnum == I_LUMASK)
1288 /* Get pathname (mandatory) */
1289 if (argc - optidx < 2) {
1290 error("You must specify a path after a %s command.",
1294 *path1 = xstrdup(argv[optidx + 1]);
1304 fatal("Command not implemented");
1312 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1315 char *path1, *path2, *tmp;
1316 int aflag = 0, hflag = 0, iflag = 0, lflag = 0, pflag = 0;
1317 int rflag = 0, sflag = 0;
1319 unsigned long n_arg = 0;
1321 char path_buf[MAXPATHLEN];
1325 path1 = path2 = NULL;
1326 cmdnum = parse_args(&cmd, &aflag, &hflag, &iflag, &lflag, &pflag,
1327 &rflag, &sflag, &n_arg, &path1, &path2);
1331 memset(&g, 0, sizeof(g));
1333 /* Perform command */
1339 /* Unrecognized command */
1346 err = process_get(conn, path1, path2, *pwd, pflag,
1350 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1353 path1 = make_absolute(path1, *pwd);
1354 path2 = make_absolute(path2, *pwd);
1355 err = do_rename(conn, path1, path2);
1360 path1 = make_absolute(path1, *pwd);
1361 path2 = make_absolute(path2, *pwd);
1362 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1365 path1 = make_absolute(path1, *pwd);
1366 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1367 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1369 printf("Removing %s\n", g.gl_pathv[i]);
1370 err = do_rm(conn, g.gl_pathv[i]);
1371 if (err != 0 && err_abort)
1376 path1 = make_absolute(path1, *pwd);
1378 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1380 err = do_mkdir(conn, path1, &a, 1);
1383 path1 = make_absolute(path1, *pwd);
1384 err = do_rmdir(conn, path1);
1387 path1 = make_absolute(path1, *pwd);
1388 if ((tmp = do_realpath(conn, path1)) == NULL) {
1392 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1397 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1398 error("Can't change directory: Can't check target");
1403 if (!S_ISDIR(aa->perm)) {
1404 error("Can't change directory: \"%s\" is not "
1405 "a directory", tmp);
1415 do_ls_dir(conn, *pwd, *pwd, lflag);
1419 /* Strip pwd off beginning of non-absolute paths */
1424 path1 = make_absolute(path1, *pwd);
1425 err = do_globbed_ls(conn, path1, tmp, lflag);
1428 /* Default to current directory if no path specified */
1430 path1 = xstrdup(*pwd);
1431 path1 = make_absolute(path1, *pwd);
1432 err = do_df(conn, path1, hflag, iflag);
1435 if (chdir(path1) == -1) {
1436 error("Couldn't change local directory to "
1437 "\"%s\": %s", path1, strerror(errno));
1442 if (mkdir(path1, 0777) == -1) {
1443 error("Couldn't create local directory "
1444 "\"%s\": %s", path1, strerror(errno));
1452 local_do_shell(cmd);
1456 printf("Local umask: %03lo\n", n_arg);
1459 path1 = make_absolute(path1, *pwd);
1461 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1463 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1464 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1466 printf("Changing mode on %s\n", g.gl_pathv[i]);
1467 err = do_setstat(conn, g.gl_pathv[i], &a);
1468 if (err != 0 && err_abort)
1474 path1 = make_absolute(path1, *pwd);
1475 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1476 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1477 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1484 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1485 error("Can't get current ownership of "
1486 "remote file \"%s\"", g.gl_pathv[i]);
1493 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1494 if (cmdnum == I_CHOWN) {
1496 printf("Changing owner on %s\n",
1501 printf("Changing group on %s\n",
1505 err = do_setstat(conn, g.gl_pathv[i], aa);
1506 if (err != 0 && err_abort)
1511 printf("Remote working directory: %s\n", *pwd);
1514 if (!getcwd(path_buf, sizeof(path_buf))) {
1515 error("Couldn't get local cwd: %s", strerror(errno));
1519 printf("Local working directory: %s\n", path_buf);
1522 /* Processed below */
1528 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1531 showprogress = !showprogress;
1533 printf("Progress meter enabled\n");
1535 printf("Progress meter disabled\n");
1538 fatal("%d is not implemented", cmdnum);
1546 /* If an unignored error occurs in batch mode we should abort. */
1547 if (err_abort && err != 0)
1549 else if (cmdnum == I_QUIT)
1557 prompt(EditLine *el)
1562 /* Display entries in 'list' after skipping the first 'len' chars */
1564 complete_display(char **list, u_int len)
1566 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1570 /* Count entries for sort and find longest */
1571 for (y = 0; list[y]; y++)
1572 m = MAX(m, strlen(list[y]));
1574 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1577 m = m > len ? m - len : 0;
1578 columns = width / (m + 2);
1579 columns = MAX(columns, 1);
1580 colspace = width / columns;
1581 colspace = MIN(colspace, width);
1585 for (y = 0; list[y]; y++) {
1586 llen = strlen(list[y]);
1587 tmp = llen > len ? list[y] + len : "";
1588 printf("%-*s", colspace, tmp);
1599 * Given a "list" of words that begin with a common prefix of "word",
1600 * attempt to find an autocompletion to extends "word" by the next
1601 * characters common to all entries in "list".
1604 complete_ambiguous(const char *word, char **list, size_t count)
1610 u_int y, matchlen = strlen(list[0]);
1612 /* Find length of common stem */
1613 for (y = 1; list[y]; y++) {
1616 for (x = 0; x < matchlen; x++)
1617 if (list[0][x] != list[y][x])
1623 if (matchlen > strlen(word)) {
1624 char *tmp = xstrdup(list[0]);
1626 tmp[matchlen] = '\0';
1631 return xstrdup(word);
1634 /* Autocomplete a sftp command */
1636 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1639 u_int y, count = 0, cmdlen, tmplen;
1640 char *tmp, **list, argterm[3];
1643 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1645 /* No command specified: display all available commands */
1647 for (y = 0; cmds[y].c; y++)
1648 list[count++] = xstrdup(cmds[y].c);
1651 complete_display(list, 0);
1653 for (y = 0; list[y] != NULL; y++)
1659 /* Prepare subset of commands that start with "cmd" */
1660 cmdlen = strlen(cmd);
1661 for (y = 0; cmds[y].c; y++) {
1662 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1663 list[count++] = xstrdup(cmds[y].c);
1672 /* Complete ambigious command */
1673 tmp = complete_ambiguous(cmd, list, count);
1675 complete_display(list, 0);
1677 for (y = 0; list[y]; y++)
1682 tmplen = strlen(tmp);
1683 cmdlen = strlen(cmd);
1684 /* If cmd may be extended then do so */
1685 if (tmplen > cmdlen)
1686 if (el_insertstr(el, tmp + cmdlen) == -1)
1687 fatal("el_insertstr failed.");
1689 /* Terminate argument cleanly */
1693 argterm[y++] = quote;
1694 if (lastarg || *(lf->cursor) != ' ')
1697 if (y > 0 && el_insertstr(el, argterm) == -1)
1698 fatal("el_insertstr failed.");
1707 * Determine whether a particular sftp command's arguments (if any)
1708 * represent local or remote files.
1711 complete_is_remote(char *cmd) {
1717 for (i = 0; cmds[i].c; i++) {
1718 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1725 /* Autocomplete a filename "file" */
1727 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1728 char *file, int remote, int lastarg, char quote, int terminated)
1731 char *tmp, *tmp2, ins[8];
1732 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1736 /* Glob from "file" location */
1740 xasprintf(&tmp, "%s*", file);
1742 /* Check if the path is absolute. */
1743 isabs = tmp[0] == '/';
1745 memset(&g, 0, sizeof(g));
1746 if (remote != LOCAL) {
1747 tmp = make_absolute(tmp, remote_path);
1748 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1750 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1752 /* Determine length of pwd so we can trim completion display */
1753 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1754 /* Terminate counting on first unescaped glob metacharacter */
1755 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1756 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1760 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1762 if (tmp[tmplen] == '/')
1763 pwdlen = tmplen + 1; /* track last seen '/' */
1767 if (g.gl_matchc == 0)
1770 if (g.gl_matchc > 1)
1771 complete_display(g.gl_pathv, pwdlen);
1774 /* Don't try to extend globs */
1775 if (file == NULL || hadglob)
1778 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1779 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1785 tmplen = strlen(tmp);
1786 filelen = strlen(file);
1788 /* Count the number of escaped characters in the input string. */
1790 for (i = 0; i < filelen; i++) {
1791 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1798 if (tmplen > (filelen - cesc)) {
1799 tmp2 = tmp + filelen - cesc;
1801 /* quote argument on way out */
1802 for (i = 0; i < len; i += clen) {
1803 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1804 (size_t)clen > sizeof(ins) - 2)
1805 fatal("invalid multibyte character");
1807 memcpy(ins + 1, tmp2 + i, clen);
1808 ins[clen + 1] = '\0';
1818 if (quote == '\0' || tmp2[i] == quote) {
1819 if (el_insertstr(el, ins) == -1)
1820 fatal("el_insertstr "
1826 if (el_insertstr(el, ins + 1) == -1)
1827 fatal("el_insertstr failed.");
1834 if (g.gl_matchc == 1) {
1838 if (*(lf->cursor - 1) != '/' &&
1839 (lastarg || *(lf->cursor) != ' '))
1842 if (i > 0 && el_insertstr(el, ins) == -1)
1843 fatal("el_insertstr failed.");
1852 /* tab-completion hook function, called via libedit */
1853 static unsigned char
1854 complete(EditLine *el, int ch)
1856 char **argv, *line, quote;
1858 u_int cursor, len, terminated, ret = CC_ERROR;
1860 struct complete_ctx *complete_ctx;
1863 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1864 fatal("%s: el_get failed", __func__);
1866 /* Figure out which argument the cursor points to */
1867 cursor = lf->cursor - lf->buffer;
1868 line = (char *)xmalloc(cursor + 1);
1869 memcpy(line, lf->buffer, cursor);
1870 line[cursor] = '\0';
1871 argv = makeargv(line, &carg, 1, "e, &terminated);
1874 /* Get all the arguments on the line */
1875 len = lf->lastchar - lf->buffer;
1876 line = (char *)xmalloc(len + 1);
1877 memcpy(line, lf->buffer, len);
1879 argv = makeargv(line, &argc, 1, NULL, NULL);
1881 /* Ensure cursor is at EOL or a argument boundary */
1882 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1883 line[cursor] != '\n') {
1889 /* Show all available commands */
1890 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1892 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1893 /* Handle the command parsing */
1894 if (complete_cmd_parse(el, argv[0], argc == carg,
1895 quote, terminated) != 0)
1897 } else if (carg >= 1) {
1898 /* Handle file parsing */
1899 int remote = complete_is_remote(argv[0]);
1900 char *filematch = NULL;
1902 if (carg > 1 && line[cursor-1] != ' ')
1903 filematch = argv[carg - 1];
1906 complete_match(el, complete_ctx->conn,
1907 *complete_ctx->remote_pathp, filematch,
1908 remote, carg == argc, quote, terminated) != 0)
1915 #endif /* USE_LIBEDIT */
1918 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1923 int err, interactive;
1924 EditLine *el = NULL;
1928 extern char *__progname;
1929 struct complete_ctx complete_ctx;
1931 if (!batchmode && isatty(STDIN_FILENO)) {
1932 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1933 fatal("Couldn't initialise editline");
1934 if ((hl = history_init()) == NULL)
1935 fatal("Couldn't initialise editline history");
1936 history(hl, &hev, H_SETSIZE, 100);
1937 el_set(el, EL_HIST, history, hl);
1939 el_set(el, EL_PROMPT, prompt);
1940 el_set(el, EL_EDITOR, "emacs");
1941 el_set(el, EL_TERMINAL, NULL);
1942 el_set(el, EL_SIGNAL, 1);
1943 el_source(el, NULL);
1945 /* Tab Completion */
1946 el_set(el, EL_ADDFN, "ftp-complete",
1947 "Context sensitive argument completion", complete);
1948 complete_ctx.conn = conn;
1949 complete_ctx.remote_pathp = &remote_path;
1950 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1951 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1953 #endif /* USE_LIBEDIT */
1955 remote_path = do_realpath(conn, ".");
1956 if (remote_path == NULL)
1959 if (file1 != NULL) {
1960 dir = xstrdup(file1);
1961 dir = make_absolute(dir, remote_path);
1963 if (remote_is_dir(conn, dir) && file2 == NULL) {
1965 printf("Changing to: %s\n", dir);
1966 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1967 if (parse_dispatch_command(conn, cmd,
1968 &remote_path, 1) != 0) {
1975 /* XXX this is wrong wrt quoting */
1976 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
1977 global_aflag ? " -a" : "", dir,
1978 file2 == NULL ? "" : " ",
1979 file2 == NULL ? "" : file2);
1980 err = parse_dispatch_command(conn, cmd,
1993 interactive = !batchmode && isatty(STDIN_FILENO);
1998 signal(SIGINT, SIG_IGN);
2003 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2008 if (!interactive) { /* Echo command */
2009 printf("sftp> %s", cmd);
2010 if (strlen(cmd) > 0 &&
2011 cmd[strlen(cmd) - 1] != '\n')
2019 if ((line = el_gets(el, &count)) == NULL ||
2024 history(hl, &hev, H_ENTER, line);
2025 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2026 fprintf(stderr, "Error: input line too long\n");
2029 #endif /* USE_LIBEDIT */
2032 cp = strrchr(cmd, '\n');
2036 /* Handle user interrupts gracefully during commands */
2038 signal(SIGINT, cmd_interrupt);
2040 err = parse_dispatch_command(conn, cmd, &remote_path,
2051 #endif /* USE_LIBEDIT */
2053 /* err == 1 signifies normal "quit" exit */
2054 return (err >= 0 ? 0 : -1);
2058 connect_to_server(char *path, char **args, int *in, int *out)
2063 int pin[2], pout[2];
2065 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2066 fatal("pipe: %s", strerror(errno));
2071 #else /* USE_PIPES */
2074 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2075 fatal("socketpair: %s", strerror(errno));
2076 *in = *out = inout[0];
2077 c_in = c_out = inout[1];
2078 #endif /* USE_PIPES */
2080 if ((sshpid = fork()) == -1)
2081 fatal("fork: %s", strerror(errno));
2082 else if (sshpid == 0) {
2083 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2084 (dup2(c_out, STDOUT_FILENO) == -1)) {
2085 fprintf(stderr, "dup2: %s\n", strerror(errno));
2094 * The underlying ssh is in the same process group, so we must
2095 * ignore SIGINT if we want to gracefully abort commands,
2096 * otherwise the signal will make it to the ssh process and
2097 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2098 * underlying ssh, it must *not* ignore that signal.
2100 signal(SIGINT, SIG_IGN);
2101 signal(SIGTERM, SIG_DFL);
2103 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2107 signal(SIGTERM, killchild);
2108 signal(SIGINT, killchild);
2109 signal(SIGHUP, killchild);
2117 extern char *__progname;
2120 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2121 " [-D sftp_server_path] [-F ssh_config] "
2122 "[-i identity_file] [-l limit]\n"
2123 " [-o ssh_option] [-P port] [-R num_requests] "
2125 " [-s subsystem | sftp_server] host\n"
2126 " %s [user@]host[:file ...]\n"
2127 " %s [user@]host[:dir[/]]\n"
2128 " %s -b batchfile [user@]host\n",
2129 __progname, __progname, __progname, __progname);
2134 main(int argc, char **argv)
2136 int in, out, ch, err;
2137 char *host = NULL, *userhost, *cp, *file2 = NULL;
2138 int debug_level = 0, sshver = 2;
2139 char *file1 = NULL, *sftp_server = NULL;
2140 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2142 LogLevel ll = SYSLOG_LEVEL_INFO;
2145 extern char *optarg;
2146 struct sftp_conn *conn;
2147 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2148 size_t num_requests = DEFAULT_NUM_REQUESTS;
2149 long long limit_kbps = 0;
2151 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2153 setlocale(LC_CTYPE, "");
2155 __progname = ssh_get_progname(argv[0]);
2156 memset(&args, '\0', sizeof(args));
2158 addargs(&args, "%s", ssh_program);
2159 addargs(&args, "-oForwardX11 no");
2160 addargs(&args, "-oForwardAgent no");
2161 addargs(&args, "-oPermitLocalCommand no");
2162 addargs(&args, "-oClearAllForwardings yes");
2164 ll = SYSLOG_LEVEL_INFO;
2167 while ((ch = getopt(argc, argv,
2168 "1246ahpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2170 /* Passed through to ssh(1) */
2174 addargs(&args, "-%c", ch);
2176 /* Passed through to ssh(1) with argument */
2181 addargs(&args, "-%c", ch);
2182 addargs(&args, "%s", optarg);
2185 ll = SYSLOG_LEVEL_ERROR;
2188 addargs(&args, "-%c", ch);
2191 addargs(&args, "-oPort %s", optarg);
2194 if (debug_level < 3) {
2195 addargs(&args, "-v");
2196 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2202 if (sftp_server == NULL)
2203 sftp_server = _PATH_SFTP_SERVER;
2212 copy_buffer_len = strtol(optarg, &cp, 10);
2213 if (copy_buffer_len == 0 || *cp != '\0')
2214 fatal("Invalid buffer size \"%s\"", optarg);
2218 fatal("Batch file already specified.");
2220 /* Allow "-" as stdin */
2221 if (strcmp(optarg, "-") != 0 &&
2222 (infile = fopen(optarg, "r")) == NULL)
2223 fatal("%s (%s).", strerror(errno), optarg);
2225 quiet = batchmode = 1;
2226 addargs(&args, "-obatchmode yes");
2232 sftp_direct = optarg;
2235 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2239 limit_kbps *= 1024; /* kbps */
2245 num_requests = strtol(optarg, &cp, 10);
2246 if (num_requests == 0 || *cp != '\0')
2247 fatal("Invalid number of requests \"%s\"",
2251 sftp_server = optarg;
2254 ssh_program = optarg;
2255 replacearg(&args, 0, "%s", ssh_program);
2263 if (!isatty(STDERR_FILENO))
2266 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2268 if (sftp_direct == NULL) {
2269 if (optind == argc || argc > (optind + 2))
2272 userhost = xstrdup(argv[optind]);
2273 file2 = argv[optind+1];
2275 if ((host = strrchr(userhost, '@')) == NULL)
2280 fprintf(stderr, "Missing username\n");
2283 addargs(&args, "-l");
2284 addargs(&args, "%s", userhost);
2287 if ((cp = colon(host)) != NULL) {
2292 host = cleanhostname(host);
2294 fprintf(stderr, "Missing hostname\n");
2298 addargs(&args, "-oProtocol %d", sshver);
2300 /* no subsystem if the server-spec contains a '/' */
2301 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2302 addargs(&args, "-s");
2304 addargs(&args, "--");
2305 addargs(&args, "%s", host);
2306 addargs(&args, "%s", (sftp_server != NULL ?
2307 sftp_server : "sftp"));
2309 connect_to_server(ssh_program, args.list, &in, &out);
2312 addargs(&args, "sftp-server");
2314 connect_to_server(sftp_direct, args.list, &in, &out);
2318 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2320 fatal("Couldn't initialise connection to server");
2323 if (sftp_direct == NULL)
2324 fprintf(stderr, "Connected to %s.\n", host);
2326 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2329 err = interactive_loop(conn, file1, file2);
2331 #if !defined(USE_PIPES)
2332 shutdown(in, SHUT_RDWR);
2333 shutdown(out, SHUT_RDWR);
2341 while (waitpid(sshpid, NULL, 0) == -1)
2343 fatal("Couldn't wait for ssh process: %s",
2346 exit(err == 0 ? 0 : 1);