- djm@cvs.openbsd.org 2008/12/07 22:17:48
[openssh-git.git] / sftp.c
blob75b16b27ef254ffbfce83e6cced0651b736959af
1 /* $OpenBSD: sftp.c,v 1.111 2009/08/18 18:36:21 djm Exp $ */
2 /*
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.
18 #include "includes.h"
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
24 #endif
25 #include <sys/param.h>
26 #include <sys/socket.h>
27 #include <sys/wait.h>
28 #ifdef HAVE_SYS_STATVFS_H
29 #include <sys/statvfs.h>
30 #endif
32 #include <ctype.h>
33 #include <errno.h>
35 #ifdef HAVE_PATHS_H
36 # include <paths.h>
37 #endif
38 #ifdef HAVE_LIBGEN_H
39 #include <libgen.h>
40 #endif
41 #ifdef USE_LIBEDIT
42 #include <histedit.h>
43 #else
44 typedef void EditLine;
45 #endif
46 #include <signal.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <stdarg.h>
53 #ifdef HAVE_UTIL_H
54 # include <util.h>
55 #endif
57 #ifdef HAVE_LIBUTIL_H
58 # include <libutil.h>
59 #endif
61 #include "xmalloc.h"
62 #include "log.h"
63 #include "pathnames.h"
64 #include "misc.h"
66 #include "sftp.h"
67 #include "buffer.h"
68 #include "sftp-common.h"
69 #include "sftp-client.h"
71 /* File to read commands from */
72 FILE* infile;
74 /* Are we in batchfile mode? */
75 int batchmode = 0;
77 /* Size of buffer used when copying files */
78 size_t copy_buffer_len = 32768;
80 /* Number of concurrent outstanding requests */
81 size_t num_requests = 64;
83 /* PID of ssh transport process */
84 static pid_t sshpid = -1;
86 /* This is set to 0 if the progressmeter is not desired. */
87 int showprogress = 1;
89 /* When this option is set, we always recursively download/upload directories */
90 int global_rflag = 0;
92 /* When this option is set, the file transfers will always preserve times */
93 int global_pflag = 0;
95 /* SIGINT received during command processing */
96 volatile sig_atomic_t interrupted = 0;
98 /* I wish qsort() took a separate ctx for the comparison function...*/
99 int sort_flag;
101 int remote_glob(struct sftp_conn *, const char *, int,
102 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
104 extern char *__progname;
106 /* Separators for interactive commands */
107 #define WHITESPACE " \t\r\n"
109 /* ls flags */
110 #define LS_LONG_VIEW 0x01 /* Full view ala ls -l */
111 #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */
112 #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */
113 #define LS_NAME_SORT 0x08 /* Sort by name (default) */
114 #define LS_TIME_SORT 0x10 /* Sort by mtime */
115 #define LS_SIZE_SORT 0x20 /* Sort by file size */
116 #define LS_REVERSE_SORT 0x40 /* Reverse sort order */
117 #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */
119 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
120 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
122 /* Commands for interactive mode */
123 #define I_CHDIR 1
124 #define I_CHGRP 2
125 #define I_CHMOD 3
126 #define I_CHOWN 4
127 #define I_DF 24
128 #define I_GET 5
129 #define I_HELP 6
130 #define I_LCHDIR 7
131 #define I_LLS 8
132 #define I_LMKDIR 9
133 #define I_LPWD 10
134 #define I_LS 11
135 #define I_LUMASK 12
136 #define I_MKDIR 13
137 #define I_PUT 14
138 #define I_PWD 15
139 #define I_QUIT 16
140 #define I_RENAME 17
141 #define I_RM 18
142 #define I_RMDIR 19
143 #define I_SHELL 20
144 #define I_SYMLINK 21
145 #define I_VERSION 22
146 #define I_PROGRESS 23
148 struct CMD {
149 const char *c;
150 const int n;
153 static const struct CMD cmds[] = {
154 { "bye", I_QUIT },
155 { "cd", I_CHDIR },
156 { "chdir", I_CHDIR },
157 { "chgrp", I_CHGRP },
158 { "chmod", I_CHMOD },
159 { "chown", I_CHOWN },
160 { "df", I_DF },
161 { "dir", I_LS },
162 { "exit", I_QUIT },
163 { "get", I_GET },
164 { "mget", I_GET },
165 { "help", I_HELP },
166 { "lcd", I_LCHDIR },
167 { "lchdir", I_LCHDIR },
168 { "lls", I_LLS },
169 { "lmkdir", I_LMKDIR },
170 { "ln", I_SYMLINK },
171 { "lpwd", I_LPWD },
172 { "ls", I_LS },
173 { "lumask", I_LUMASK },
174 { "mkdir", I_MKDIR },
175 { "progress", I_PROGRESS },
176 { "put", I_PUT },
177 { "mput", I_PUT },
178 { "pwd", I_PWD },
179 { "quit", I_QUIT },
180 { "rename", I_RENAME },
181 { "rm", I_RM },
182 { "rmdir", I_RMDIR },
183 { "symlink", I_SYMLINK },
184 { "version", I_VERSION },
185 { "!", I_SHELL },
186 { "?", I_HELP },
187 { NULL, -1}
190 int interactive_loop(int fd_in, int fd_out, char *file1, char *file2);
192 /* ARGSUSED */
193 static void
194 killchild(int signo)
196 if (sshpid > 1) {
197 kill(sshpid, SIGTERM);
198 waitpid(sshpid, NULL, 0);
201 _exit(1);
204 /* ARGSUSED */
205 static void
206 cmd_interrupt(int signo)
208 const char msg[] = "\rInterrupt \n";
209 int olderrno = errno;
211 write(STDERR_FILENO, msg, sizeof(msg) - 1);
212 interrupted = 1;
213 errno = olderrno;
216 static void
217 help(void)
219 printf("Available commands:\n"
220 "bye Quit sftp\n"
221 "cd path Change remote directory to 'path'\n"
222 "chgrp grp path Change group of file 'path' to 'grp'\n"
223 "chmod mode path Change permissions of file 'path' to 'mode'\n"
224 "chown own path Change owner of file 'path' to 'own'\n"
225 "df [-hi] [path] Display statistics for current directory or\n"
226 " filesystem containing 'path'\n"
227 "exit Quit sftp\n"
228 "get [-Pr] remote-path [local-path] Download file\n"
229 "help Display this help text\n"
230 "lcd path Change local directory to 'path'\n"
231 "lls [ls-options [path]] Display local directory listing\n"
232 "lmkdir path Create local directory\n"
233 "ln oldpath newpath Symlink remote file\n"
234 "lpwd Print local working directory\n"
235 "ls [-1aflnrSt] [path] Display remote directory listing\n"
236 "lumask umask Set local umask to 'umask'\n"
237 "mkdir path Create remote directory\n"
238 "progress Toggle display of progress meter\n"
239 "put [-Pr] local-path [remote-path] Upload file\n"
240 "pwd Display remote working directory\n"
241 "quit Quit sftp\n"
242 "rename oldpath newpath Rename remote file\n"
243 "rm path Delete remote file\n"
244 "rmdir path Remove remote directory\n"
245 "symlink oldpath newpath Symlink remote file\n"
246 "version Show SFTP version\n"
247 "!command Execute 'command' in local shell\n"
248 "! Escape to local shell\n"
249 "? Synonym for help\n");
252 static void
253 local_do_shell(const char *args)
255 int status;
256 char *shell;
257 pid_t pid;
259 if (!*args)
260 args = NULL;
262 if ((shell = getenv("SHELL")) == NULL)
263 shell = _PATH_BSHELL;
265 if ((pid = fork()) == -1)
266 fatal("Couldn't fork: %s", strerror(errno));
268 if (pid == 0) {
269 /* XXX: child has pipe fds to ssh subproc open - issue? */
270 if (args) {
271 debug3("Executing %s -c \"%s\"", shell, args);
272 execl(shell, shell, "-c", args, (char *)NULL);
273 } else {
274 debug3("Executing %s", shell);
275 execl(shell, shell, (char *)NULL);
277 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
278 strerror(errno));
279 _exit(1);
281 while (waitpid(pid, &status, 0) == -1)
282 if (errno != EINTR)
283 fatal("Couldn't wait for child: %s", strerror(errno));
284 if (!WIFEXITED(status))
285 error("Shell exited abnormally");
286 else if (WEXITSTATUS(status))
287 error("Shell exited with status %d", WEXITSTATUS(status));
290 static void
291 local_do_ls(const char *args)
293 if (!args || !*args)
294 local_do_shell(_PATH_LS);
295 else {
296 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
297 char *buf = xmalloc(len);
299 /* XXX: quoting - rip quoting code from ftp? */
300 snprintf(buf, len, _PATH_LS " %s", args);
301 local_do_shell(buf);
302 xfree(buf);
306 /* Strip one path (usually the pwd) from the start of another */
307 static char *
308 path_strip(char *path, char *strip)
310 size_t len;
312 if (strip == NULL)
313 return (xstrdup(path));
315 len = strlen(strip);
316 if (strncmp(path, strip, len) == 0) {
317 if (strip[len - 1] != '/' && path[len] == '/')
318 len++;
319 return (xstrdup(path + len));
322 return (xstrdup(path));
325 static char *
326 make_absolute(char *p, char *pwd)
328 char *abs_str;
330 /* Derelativise */
331 if (p && p[0] != '/') {
332 abs_str = path_append(pwd, p);
333 xfree(p);
334 return(abs_str);
335 } else
336 return(p);
339 static int
340 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
341 int *rflag)
343 extern int opterr, optind, optopt, optreset;
344 int ch;
346 optind = optreset = 1;
347 opterr = 0;
349 *rflag = *pflag = 0;
350 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
351 switch (ch) {
352 case 'p':
353 case 'P':
354 *pflag = 1;
355 break;
356 case 'r':
357 case 'R':
358 *rflag = 1;
359 break;
360 default:
361 error("%s: Invalid flag -%c", cmd, optopt);
362 return -1;
366 return optind;
369 static int
370 parse_ls_flags(char **argv, int argc, int *lflag)
372 extern int opterr, optind, optopt, optreset;
373 int ch;
375 optind = optreset = 1;
376 opterr = 0;
378 *lflag = LS_NAME_SORT;
379 while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) {
380 switch (ch) {
381 case '1':
382 *lflag &= ~VIEW_FLAGS;
383 *lflag |= LS_SHORT_VIEW;
384 break;
385 case 'S':
386 *lflag &= ~SORT_FLAGS;
387 *lflag |= LS_SIZE_SORT;
388 break;
389 case 'a':
390 *lflag |= LS_SHOW_ALL;
391 break;
392 case 'f':
393 *lflag &= ~SORT_FLAGS;
394 break;
395 case 'l':
396 *lflag &= ~VIEW_FLAGS;
397 *lflag |= LS_LONG_VIEW;
398 break;
399 case 'n':
400 *lflag &= ~VIEW_FLAGS;
401 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
402 break;
403 case 'r':
404 *lflag |= LS_REVERSE_SORT;
405 break;
406 case 't':
407 *lflag &= ~SORT_FLAGS;
408 *lflag |= LS_TIME_SORT;
409 break;
410 default:
411 error("ls: Invalid flag -%c", optopt);
412 return -1;
416 return optind;
419 static int
420 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
422 extern int opterr, optind, optopt, optreset;
423 int ch;
425 optind = optreset = 1;
426 opterr = 0;
428 *hflag = *iflag = 0;
429 while ((ch = getopt(argc, argv, "hi")) != -1) {
430 switch (ch) {
431 case 'h':
432 *hflag = 1;
433 break;
434 case 'i':
435 *iflag = 1;
436 break;
437 default:
438 error("%s: Invalid flag -%c", cmd, optopt);
439 return -1;
443 return optind;
446 static int
447 is_dir(char *path)
449 struct stat sb;
451 /* XXX: report errors? */
452 if (stat(path, &sb) == -1)
453 return(0);
455 return(S_ISDIR(sb.st_mode));
458 static int
459 remote_is_dir(struct sftp_conn *conn, char *path)
461 Attrib *a;
463 /* XXX: report errors? */
464 if ((a = do_stat(conn, path, 1)) == NULL)
465 return(0);
466 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
467 return(0);
468 return(S_ISDIR(a->perm));
471 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
472 static int
473 pathname_is_dir(char *pathname)
475 size_t l = strlen(pathname);
477 return l > 0 && pathname[l - 1] == '/';
480 static int
481 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
482 int pflag, int rflag)
484 char *abs_src = NULL;
485 char *abs_dst = NULL;
486 glob_t g;
487 char *filename, *tmp=NULL;
488 int i, err = 0;
490 abs_src = xstrdup(src);
491 abs_src = make_absolute(abs_src, pwd);
492 memset(&g, 0, sizeof(g));
494 debug3("Looking up %s", abs_src);
495 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
496 error("File \"%s\" not found.", abs_src);
497 err = -1;
498 goto out;
502 * If multiple matches then dst must be a directory or
503 * unspecified.
505 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
506 error("Multiple source paths, but destination "
507 "\"%s\" is not a directory", dst);
508 err = -1;
509 goto out;
512 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
513 tmp = xstrdup(g.gl_pathv[i]);
514 if ((filename = basename(tmp)) == NULL) {
515 error("basename %s: %s", tmp, strerror(errno));
516 xfree(tmp);
517 err = -1;
518 goto out;
521 if (g.gl_matchc == 1 && dst) {
522 if (is_dir(dst)) {
523 abs_dst = path_append(dst, filename);
524 } else {
525 abs_dst = xstrdup(dst);
527 } else if (dst) {
528 abs_dst = path_append(dst, filename);
529 } else {
530 abs_dst = xstrdup(filename);
532 xfree(tmp);
534 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
535 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
536 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
537 pflag || global_pflag, 1) == -1)
538 err = -1;
539 } else {
540 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
541 pflag || global_pflag) == -1)
542 err = -1;
544 xfree(abs_dst);
545 abs_dst = NULL;
548 out:
549 xfree(abs_src);
550 globfree(&g);
551 return(err);
554 static int
555 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
556 int pflag, int rflag)
558 char *tmp_dst = NULL;
559 char *abs_dst = NULL;
560 char *tmp = NULL, *filename = NULL;
561 glob_t g;
562 int err = 0;
563 int i, dst_is_dir = 1;
564 struct stat sb;
566 if (dst) {
567 tmp_dst = xstrdup(dst);
568 tmp_dst = make_absolute(tmp_dst, pwd);
571 memset(&g, 0, sizeof(g));
572 debug3("Looking up %s", src);
573 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
574 error("File \"%s\" not found.", src);
575 err = -1;
576 goto out;
579 /* If we aren't fetching to pwd then stash this status for later */
580 if (tmp_dst != NULL)
581 dst_is_dir = remote_is_dir(conn, tmp_dst);
583 /* If multiple matches, dst may be directory or unspecified */
584 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
585 error("Multiple paths match, but destination "
586 "\"%s\" is not a directory", tmp_dst);
587 err = -1;
588 goto out;
591 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
592 if (stat(g.gl_pathv[i], &sb) == -1) {
593 err = -1;
594 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
595 continue;
598 tmp = xstrdup(g.gl_pathv[i]);
599 if ((filename = basename(tmp)) == NULL) {
600 error("basename %s: %s", tmp, strerror(errno));
601 xfree(tmp);
602 err = -1;
603 goto out;
606 if (g.gl_matchc == 1 && tmp_dst) {
607 /* If directory specified, append filename */
608 if (dst_is_dir)
609 abs_dst = path_append(tmp_dst, filename);
610 else
611 abs_dst = xstrdup(tmp_dst);
612 } else if (tmp_dst) {
613 abs_dst = path_append(tmp_dst, filename);
614 } else {
615 abs_dst = make_absolute(xstrdup(filename), pwd);
617 xfree(tmp);
619 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
620 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
621 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
622 pflag || global_pflag, 1) == -1)
623 err = -1;
624 } else {
625 if (do_upload(conn, g.gl_pathv[i], abs_dst,
626 pflag || global_pflag) == -1)
627 err = -1;
631 out:
632 if (abs_dst)
633 xfree(abs_dst);
634 if (tmp_dst)
635 xfree(tmp_dst);
636 globfree(&g);
637 return(err);
640 static int
641 sdirent_comp(const void *aa, const void *bb)
643 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
644 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
645 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
647 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
648 if (sort_flag & LS_NAME_SORT)
649 return (rmul * strcmp(a->filename, b->filename));
650 else if (sort_flag & LS_TIME_SORT)
651 return (rmul * NCMP(a->a.mtime, b->a.mtime));
652 else if (sort_flag & LS_SIZE_SORT)
653 return (rmul * NCMP(a->a.size, b->a.size));
655 fatal("Unknown ls sort type");
658 /* sftp ls.1 replacement for directories */
659 static int
660 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
662 int n;
663 u_int c = 1, colspace = 0, columns = 1;
664 SFTP_DIRENT **d;
666 if ((n = do_readdir(conn, path, &d)) != 0)
667 return (n);
669 if (!(lflag & LS_SHORT_VIEW)) {
670 u_int m = 0, width = 80;
671 struct winsize ws;
672 char *tmp;
674 /* Count entries for sort and find longest filename */
675 for (n = 0; d[n] != NULL; n++) {
676 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
677 m = MAX(m, strlen(d[n]->filename));
680 /* Add any subpath that also needs to be counted */
681 tmp = path_strip(path, strip_path);
682 m += strlen(tmp);
683 xfree(tmp);
685 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
686 width = ws.ws_col;
688 columns = width / (m + 2);
689 columns = MAX(columns, 1);
690 colspace = width / columns;
691 colspace = MIN(colspace, width);
694 if (lflag & SORT_FLAGS) {
695 for (n = 0; d[n] != NULL; n++)
696 ; /* count entries */
697 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
698 qsort(d, n, sizeof(*d), sdirent_comp);
701 for (n = 0; d[n] != NULL && !interrupted; n++) {
702 char *tmp, *fname;
704 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
705 continue;
707 tmp = path_append(path, d[n]->filename);
708 fname = path_strip(tmp, strip_path);
709 xfree(tmp);
711 if (lflag & LS_LONG_VIEW) {
712 if (lflag & LS_NUMERIC_VIEW) {
713 char *lname;
714 struct stat sb;
716 memset(&sb, 0, sizeof(sb));
717 attrib_to_stat(&d[n]->a, &sb);
718 lname = ls_file(fname, &sb, 1);
719 printf("%s\n", lname);
720 xfree(lname);
721 } else
722 printf("%s\n", d[n]->longname);
723 } else {
724 printf("%-*s", colspace, fname);
725 if (c >= columns) {
726 printf("\n");
727 c = 1;
728 } else
729 c++;
732 xfree(fname);
735 if (!(lflag & LS_LONG_VIEW) && (c != 1))
736 printf("\n");
738 free_sftp_dirents(d);
739 return (0);
742 /* sftp ls.1 replacement which handles path globs */
743 static int
744 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
745 int lflag)
747 glob_t g;
748 u_int i, c = 1, colspace = 0, columns = 1;
749 Attrib *a = NULL;
751 memset(&g, 0, sizeof(g));
753 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
754 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
755 if (g.gl_pathc)
756 globfree(&g);
757 error("Can't ls: \"%s\" not found", path);
758 return (-1);
761 if (interrupted)
762 goto out;
765 * If the glob returns a single match and it is a directory,
766 * then just list its contents.
768 if (g.gl_matchc == 1) {
769 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
770 globfree(&g);
771 return (-1);
773 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
774 S_ISDIR(a->perm)) {
775 int err;
777 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
778 globfree(&g);
779 return (err);
783 if (!(lflag & LS_SHORT_VIEW)) {
784 u_int m = 0, width = 80;
785 struct winsize ws;
787 /* Count entries for sort and find longest filename */
788 for (i = 0; g.gl_pathv[i]; i++)
789 m = MAX(m, strlen(g.gl_pathv[i]));
791 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
792 width = ws.ws_col;
794 columns = width / (m + 2);
795 columns = MAX(columns, 1);
796 colspace = width / columns;
799 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
800 char *fname;
802 fname = path_strip(g.gl_pathv[i], strip_path);
804 if (lflag & LS_LONG_VIEW) {
805 char *lname;
806 struct stat sb;
809 * XXX: this is slow - 1 roundtrip per path
810 * A solution to this is to fork glob() and
811 * build a sftp specific version which keeps the
812 * attribs (which currently get thrown away)
813 * that the server returns as well as the filenames.
815 memset(&sb, 0, sizeof(sb));
816 if (a == NULL)
817 a = do_lstat(conn, g.gl_pathv[i], 1);
818 if (a != NULL)
819 attrib_to_stat(a, &sb);
820 lname = ls_file(fname, &sb, 1);
821 printf("%s\n", lname);
822 xfree(lname);
823 } else {
824 printf("%-*s", colspace, fname);
825 if (c >= columns) {
826 printf("\n");
827 c = 1;
828 } else
829 c++;
831 xfree(fname);
834 if (!(lflag & LS_LONG_VIEW) && (c != 1))
835 printf("\n");
837 out:
838 if (g.gl_pathc)
839 globfree(&g);
841 return (0);
844 static int
845 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
847 struct sftp_statvfs st;
848 char s_used[FMT_SCALED_STRSIZE];
849 char s_avail[FMT_SCALED_STRSIZE];
850 char s_root[FMT_SCALED_STRSIZE];
851 char s_total[FMT_SCALED_STRSIZE];
853 if (do_statvfs(conn, path, &st, 1) == -1)
854 return -1;
855 if (iflag) {
856 printf(" Inodes Used Avail "
857 "(root) %%Capacity\n");
858 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
859 (unsigned long long)st.f_files,
860 (unsigned long long)(st.f_files - st.f_ffree),
861 (unsigned long long)st.f_favail,
862 (unsigned long long)st.f_ffree,
863 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
864 st.f_files));
865 } else if (hflag) {
866 strlcpy(s_used, "error", sizeof(s_used));
867 strlcpy(s_avail, "error", sizeof(s_avail));
868 strlcpy(s_root, "error", sizeof(s_root));
869 strlcpy(s_total, "error", sizeof(s_total));
870 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
871 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
872 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
873 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
874 printf(" Size Used Avail (root) %%Capacity\n");
875 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
876 s_total, s_used, s_avail, s_root,
877 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
878 st.f_blocks));
879 } else {
880 printf(" Size Used Avail "
881 "(root) %%Capacity\n");
882 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
883 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
884 (unsigned long long)(st.f_frsize *
885 (st.f_blocks - st.f_bfree) / 1024),
886 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
887 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
888 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
889 st.f_blocks));
891 return 0;
895 * Undo escaping of glob sequences in place. Used to undo extra escaping
896 * applied in makeargv() when the string is destined for a function that
897 * does not glob it.
899 static void
900 undo_glob_escape(char *s)
902 size_t i, j;
904 for (i = j = 0;;) {
905 if (s[i] == '\0') {
906 s[j] = '\0';
907 return;
909 if (s[i] != '\\') {
910 s[j++] = s[i++];
911 continue;
913 /* s[i] == '\\' */
914 ++i;
915 switch (s[i]) {
916 case '?':
917 case '[':
918 case '*':
919 case '\\':
920 s[j++] = s[i++];
921 break;
922 case '\0':
923 s[j++] = '\\';
924 s[j] = '\0';
925 return;
926 default:
927 s[j++] = '\\';
928 s[j++] = s[i++];
929 break;
935 * Split a string into an argument vector using sh(1)-style quoting,
936 * comment and escaping rules, but with some tweaks to handle glob(3)
937 * wildcards.
938 * Returns NULL on error or a NULL-terminated array of arguments.
940 #define MAXARGS 128
941 #define MAXARGLEN 8192
942 static char **
943 makeargv(const char *arg, int *argcp)
945 int argc, quot;
946 size_t i, j;
947 static char argvs[MAXARGLEN];
948 static char *argv[MAXARGS + 1];
949 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
951 *argcp = argc = 0;
952 if (strlen(arg) > sizeof(argvs) - 1) {
953 args_too_longs:
954 error("string too long");
955 return NULL;
957 state = MA_START;
958 i = j = 0;
959 for (;;) {
960 if (isspace(arg[i])) {
961 if (state == MA_UNQUOTED) {
962 /* Terminate current argument */
963 argvs[j++] = '\0';
964 argc++;
965 state = MA_START;
966 } else if (state != MA_START)
967 argvs[j++] = arg[i];
968 } else if (arg[i] == '"' || arg[i] == '\'') {
969 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
970 if (state == MA_START) {
971 argv[argc] = argvs + j;
972 state = q;
973 } else if (state == MA_UNQUOTED)
974 state = q;
975 else if (state == q)
976 state = MA_UNQUOTED;
977 else
978 argvs[j++] = arg[i];
979 } else if (arg[i] == '\\') {
980 if (state == MA_SQUOTE || state == MA_DQUOTE) {
981 quot = state == MA_SQUOTE ? '\'' : '"';
982 /* Unescape quote we are in */
983 /* XXX support \n and friends? */
984 if (arg[i + 1] == quot) {
985 i++;
986 argvs[j++] = arg[i];
987 } else if (arg[i + 1] == '?' ||
988 arg[i + 1] == '[' || arg[i + 1] == '*') {
990 * Special case for sftp: append
991 * double-escaped glob sequence -
992 * glob will undo one level of
993 * escaping. NB. string can grow here.
995 if (j >= sizeof(argvs) - 5)
996 goto args_too_longs;
997 argvs[j++] = '\\';
998 argvs[j++] = arg[i++];
999 argvs[j++] = '\\';
1000 argvs[j++] = arg[i];
1001 } else {
1002 argvs[j++] = arg[i++];
1003 argvs[j++] = arg[i];
1005 } else {
1006 if (state == MA_START) {
1007 argv[argc] = argvs + j;
1008 state = MA_UNQUOTED;
1010 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1011 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1013 * Special case for sftp: append
1014 * escaped glob sequence -
1015 * glob will undo one level of
1016 * escaping.
1018 argvs[j++] = arg[i++];
1019 argvs[j++] = arg[i];
1020 } else {
1021 /* Unescape everything */
1022 /* XXX support \n and friends? */
1023 i++;
1024 argvs[j++] = arg[i];
1027 } else if (arg[i] == '#') {
1028 if (state == MA_SQUOTE || state == MA_DQUOTE)
1029 argvs[j++] = arg[i];
1030 else
1031 goto string_done;
1032 } else if (arg[i] == '\0') {
1033 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1034 error("Unterminated quoted argument");
1035 return NULL;
1037 string_done:
1038 if (state == MA_UNQUOTED) {
1039 argvs[j++] = '\0';
1040 argc++;
1042 break;
1043 } else {
1044 if (state == MA_START) {
1045 argv[argc] = argvs + j;
1046 state = MA_UNQUOTED;
1048 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1049 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1051 * Special case for sftp: escape quoted
1052 * glob(3) wildcards. NB. string can grow
1053 * here.
1055 if (j >= sizeof(argvs) - 3)
1056 goto args_too_longs;
1057 argvs[j++] = '\\';
1058 argvs[j++] = arg[i];
1059 } else
1060 argvs[j++] = arg[i];
1062 i++;
1064 *argcp = argc;
1065 return argv;
1068 static int
1069 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, int *hflag,
1070 unsigned long *n_arg, char **path1, char **path2)
1072 const char *cmd, *cp = *cpp;
1073 char *cp2, **argv;
1074 int base = 0;
1075 long l;
1076 int i, cmdnum, optidx, argc;
1078 /* Skip leading whitespace */
1079 cp = cp + strspn(cp, WHITESPACE);
1081 /* Ignore blank lines and lines which begin with comment '#' char */
1082 if (*cp == '\0' || *cp == '#')
1083 return (0);
1085 /* Check for leading '-' (disable error processing) */
1086 *iflag = 0;
1087 if (*cp == '-') {
1088 *iflag = 1;
1089 cp++;
1092 if ((argv = makeargv(cp, &argc)) == NULL)
1093 return -1;
1095 /* Figure out which command we have */
1096 for (i = 0; cmds[i].c != NULL; i++) {
1097 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1098 break;
1100 cmdnum = cmds[i].n;
1101 cmd = cmds[i].c;
1103 /* Special case */
1104 if (*cp == '!') {
1105 cp++;
1106 cmdnum = I_SHELL;
1107 } else if (cmdnum == -1) {
1108 error("Invalid command.");
1109 return -1;
1112 /* Get arguments and parse flags */
1113 *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1114 *path1 = *path2 = NULL;
1115 optidx = 1;
1116 switch (cmdnum) {
1117 case I_GET:
1118 case I_PUT:
1119 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
1120 return -1;
1121 /* Get first pathname (mandatory) */
1122 if (argc - optidx < 1) {
1123 error("You must specify at least one path after a "
1124 "%s command.", cmd);
1125 return -1;
1127 *path1 = xstrdup(argv[optidx]);
1128 /* Get second pathname (optional) */
1129 if (argc - optidx > 1) {
1130 *path2 = xstrdup(argv[optidx + 1]);
1131 /* Destination is not globbed */
1132 undo_glob_escape(*path2);
1134 break;
1135 case I_RENAME:
1136 case I_SYMLINK:
1137 if (argc - optidx < 2) {
1138 error("You must specify two paths after a %s "
1139 "command.", cmd);
1140 return -1;
1142 *path1 = xstrdup(argv[optidx]);
1143 *path2 = xstrdup(argv[optidx + 1]);
1144 /* Paths are not globbed */
1145 undo_glob_escape(*path1);
1146 undo_glob_escape(*path2);
1147 break;
1148 case I_RM:
1149 case I_MKDIR:
1150 case I_RMDIR:
1151 case I_CHDIR:
1152 case I_LCHDIR:
1153 case I_LMKDIR:
1154 /* Get pathname (mandatory) */
1155 if (argc - optidx < 1) {
1156 error("You must specify a path after a %s command.",
1157 cmd);
1158 return -1;
1160 *path1 = xstrdup(argv[optidx]);
1161 /* Only "rm" globs */
1162 if (cmdnum != I_RM)
1163 undo_glob_escape(*path1);
1164 break;
1165 case I_DF:
1166 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1167 iflag)) == -1)
1168 return -1;
1169 /* Default to current directory if no path specified */
1170 if (argc - optidx < 1)
1171 *path1 = NULL;
1172 else {
1173 *path1 = xstrdup(argv[optidx]);
1174 undo_glob_escape(*path1);
1176 break;
1177 case I_LS:
1178 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1179 return(-1);
1180 /* Path is optional */
1181 if (argc - optidx > 0)
1182 *path1 = xstrdup(argv[optidx]);
1183 break;
1184 case I_LLS:
1185 /* Skip ls command and following whitespace */
1186 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1187 case I_SHELL:
1188 /* Uses the rest of the line */
1189 break;
1190 case I_LUMASK:
1191 case I_CHMOD:
1192 base = 8;
1193 case I_CHOWN:
1194 case I_CHGRP:
1195 /* Get numeric arg (mandatory) */
1196 if (argc - optidx < 1)
1197 goto need_num_arg;
1198 errno = 0;
1199 l = strtol(argv[optidx], &cp2, base);
1200 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1201 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1202 l < 0) {
1203 need_num_arg:
1204 error("You must supply a numeric argument "
1205 "to the %s command.", cmd);
1206 return -1;
1208 *n_arg = l;
1209 if (cmdnum == I_LUMASK)
1210 break;
1211 /* Get pathname (mandatory) */
1212 if (argc - optidx < 2) {
1213 error("You must specify a path after a %s command.",
1214 cmd);
1215 return -1;
1217 *path1 = xstrdup(argv[optidx + 1]);
1218 break;
1219 case I_QUIT:
1220 case I_PWD:
1221 case I_LPWD:
1222 case I_HELP:
1223 case I_VERSION:
1224 case I_PROGRESS:
1225 break;
1226 default:
1227 fatal("Command not implemented");
1230 *cpp = cp;
1231 return(cmdnum);
1234 static int
1235 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1236 int err_abort)
1238 char *path1, *path2, *tmp;
1239 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1240 unsigned long n_arg = 0;
1241 Attrib a, *aa;
1242 char path_buf[MAXPATHLEN];
1243 int err = 0;
1244 glob_t g;
1246 path1 = path2 = NULL;
1247 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1248 &path1, &path2);
1250 if (iflag != 0)
1251 err_abort = 0;
1253 memset(&g, 0, sizeof(g));
1255 /* Perform command */
1256 switch (cmdnum) {
1257 case 0:
1258 /* Blank line */
1259 break;
1260 case -1:
1261 /* Unrecognized command */
1262 err = -1;
1263 break;
1264 case I_GET:
1265 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1266 break;
1267 case I_PUT:
1268 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1269 break;
1270 case I_RENAME:
1271 path1 = make_absolute(path1, *pwd);
1272 path2 = make_absolute(path2, *pwd);
1273 err = do_rename(conn, path1, path2);
1274 break;
1275 case I_SYMLINK:
1276 path2 = make_absolute(path2, *pwd);
1277 err = do_symlink(conn, path1, path2);
1278 break;
1279 case I_RM:
1280 path1 = make_absolute(path1, *pwd);
1281 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1282 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1283 printf("Removing %s\n", g.gl_pathv[i]);
1284 err = do_rm(conn, g.gl_pathv[i]);
1285 if (err != 0 && err_abort)
1286 break;
1288 break;
1289 case I_MKDIR:
1290 path1 = make_absolute(path1, *pwd);
1291 attrib_clear(&a);
1292 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1293 a.perm = 0777;
1294 err = do_mkdir(conn, path1, &a, 1);
1295 break;
1296 case I_RMDIR:
1297 path1 = make_absolute(path1, *pwd);
1298 err = do_rmdir(conn, path1);
1299 break;
1300 case I_CHDIR:
1301 path1 = make_absolute(path1, *pwd);
1302 if ((tmp = do_realpath(conn, path1)) == NULL) {
1303 err = 1;
1304 break;
1306 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1307 xfree(tmp);
1308 err = 1;
1309 break;
1311 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1312 error("Can't change directory: Can't check target");
1313 xfree(tmp);
1314 err = 1;
1315 break;
1317 if (!S_ISDIR(aa->perm)) {
1318 error("Can't change directory: \"%s\" is not "
1319 "a directory", tmp);
1320 xfree(tmp);
1321 err = 1;
1322 break;
1324 xfree(*pwd);
1325 *pwd = tmp;
1326 break;
1327 case I_LS:
1328 if (!path1) {
1329 do_globbed_ls(conn, *pwd, *pwd, lflag);
1330 break;
1333 /* Strip pwd off beginning of non-absolute paths */
1334 tmp = NULL;
1335 if (*path1 != '/')
1336 tmp = *pwd;
1338 path1 = make_absolute(path1, *pwd);
1339 err = do_globbed_ls(conn, path1, tmp, lflag);
1340 break;
1341 case I_DF:
1342 /* Default to current directory if no path specified */
1343 if (path1 == NULL)
1344 path1 = xstrdup(*pwd);
1345 path1 = make_absolute(path1, *pwd);
1346 err = do_df(conn, path1, hflag, iflag);
1347 break;
1348 case I_LCHDIR:
1349 if (chdir(path1) == -1) {
1350 error("Couldn't change local directory to "
1351 "\"%s\": %s", path1, strerror(errno));
1352 err = 1;
1354 break;
1355 case I_LMKDIR:
1356 if (mkdir(path1, 0777) == -1) {
1357 error("Couldn't create local directory "
1358 "\"%s\": %s", path1, strerror(errno));
1359 err = 1;
1361 break;
1362 case I_LLS:
1363 local_do_ls(cmd);
1364 break;
1365 case I_SHELL:
1366 local_do_shell(cmd);
1367 break;
1368 case I_LUMASK:
1369 umask(n_arg);
1370 printf("Local umask: %03lo\n", n_arg);
1371 break;
1372 case I_CHMOD:
1373 path1 = make_absolute(path1, *pwd);
1374 attrib_clear(&a);
1375 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1376 a.perm = n_arg;
1377 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1378 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1379 printf("Changing mode on %s\n", g.gl_pathv[i]);
1380 err = do_setstat(conn, g.gl_pathv[i], &a);
1381 if (err != 0 && err_abort)
1382 break;
1384 break;
1385 case I_CHOWN:
1386 case I_CHGRP:
1387 path1 = make_absolute(path1, *pwd);
1388 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1389 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1390 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1391 if (err_abort) {
1392 err = -1;
1393 break;
1394 } else
1395 continue;
1397 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1398 error("Can't get current ownership of "
1399 "remote file \"%s\"", g.gl_pathv[i]);
1400 if (err_abort) {
1401 err = -1;
1402 break;
1403 } else
1404 continue;
1406 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1407 if (cmdnum == I_CHOWN) {
1408 printf("Changing owner on %s\n", g.gl_pathv[i]);
1409 aa->uid = n_arg;
1410 } else {
1411 printf("Changing group on %s\n", g.gl_pathv[i]);
1412 aa->gid = n_arg;
1414 err = do_setstat(conn, g.gl_pathv[i], aa);
1415 if (err != 0 && err_abort)
1416 break;
1418 break;
1419 case I_PWD:
1420 printf("Remote working directory: %s\n", *pwd);
1421 break;
1422 case I_LPWD:
1423 if (!getcwd(path_buf, sizeof(path_buf))) {
1424 error("Couldn't get local cwd: %s", strerror(errno));
1425 err = -1;
1426 break;
1428 printf("Local working directory: %s\n", path_buf);
1429 break;
1430 case I_QUIT:
1431 /* Processed below */
1432 break;
1433 case I_HELP:
1434 help();
1435 break;
1436 case I_VERSION:
1437 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1438 break;
1439 case I_PROGRESS:
1440 showprogress = !showprogress;
1441 if (showprogress)
1442 printf("Progress meter enabled\n");
1443 else
1444 printf("Progress meter disabled\n");
1445 break;
1446 default:
1447 fatal("%d is not implemented", cmdnum);
1450 if (g.gl_pathc)
1451 globfree(&g);
1452 if (path1)
1453 xfree(path1);
1454 if (path2)
1455 xfree(path2);
1457 /* If an unignored error occurs in batch mode we should abort. */
1458 if (err_abort && err != 0)
1459 return (-1);
1460 else if (cmdnum == I_QUIT)
1461 return (1);
1463 return (0);
1466 #ifdef USE_LIBEDIT
1467 static char *
1468 prompt(EditLine *el)
1470 return ("sftp> ");
1472 #endif
1475 interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1477 char *pwd;
1478 char *dir = NULL;
1479 char cmd[2048];
1480 struct sftp_conn *conn;
1481 int err, interactive;
1482 EditLine *el = NULL;
1483 #ifdef USE_LIBEDIT
1484 History *hl = NULL;
1485 HistEvent hev;
1486 extern char *__progname;
1488 if (!batchmode && isatty(STDIN_FILENO)) {
1489 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1490 fatal("Couldn't initialise editline");
1491 if ((hl = history_init()) == NULL)
1492 fatal("Couldn't initialise editline history");
1493 history(hl, &hev, H_SETSIZE, 100);
1494 el_set(el, EL_HIST, history, hl);
1496 el_set(el, EL_PROMPT, prompt);
1497 el_set(el, EL_EDITOR, "emacs");
1498 el_set(el, EL_TERMINAL, NULL);
1499 el_set(el, EL_SIGNAL, 1);
1500 el_source(el, NULL);
1502 #endif /* USE_LIBEDIT */
1504 conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests);
1505 if (conn == NULL)
1506 fatal("Couldn't initialise connection to server");
1508 pwd = do_realpath(conn, ".");
1509 if (pwd == NULL)
1510 fatal("Need cwd");
1512 if (file1 != NULL) {
1513 dir = xstrdup(file1);
1514 dir = make_absolute(dir, pwd);
1516 if (remote_is_dir(conn, dir) && file2 == NULL) {
1517 printf("Changing to: %s\n", dir);
1518 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1519 if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) {
1520 xfree(dir);
1521 xfree(pwd);
1522 xfree(conn);
1523 return (-1);
1525 } else {
1526 if (file2 == NULL)
1527 snprintf(cmd, sizeof cmd, "get %s", dir);
1528 else
1529 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1530 file2);
1532 err = parse_dispatch_command(conn, cmd, &pwd, 1);
1533 xfree(dir);
1534 xfree(pwd);
1535 xfree(conn);
1536 return (err);
1538 xfree(dir);
1541 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1542 setvbuf(stdout, NULL, _IOLBF, 0);
1543 setvbuf(infile, NULL, _IOLBF, 0);
1544 #else
1545 setlinebuf(stdout);
1546 setlinebuf(infile);
1547 #endif
1549 interactive = !batchmode && isatty(STDIN_FILENO);
1550 err = 0;
1551 for (;;) {
1552 char *cp;
1554 signal(SIGINT, SIG_IGN);
1556 if (el == NULL) {
1557 if (interactive)
1558 printf("sftp> ");
1559 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1560 if (interactive)
1561 printf("\n");
1562 break;
1564 if (!interactive) { /* Echo command */
1565 printf("sftp> %s", cmd);
1566 if (strlen(cmd) > 0 &&
1567 cmd[strlen(cmd) - 1] != '\n')
1568 printf("\n");
1570 } else {
1571 #ifdef USE_LIBEDIT
1572 const char *line;
1573 int count = 0;
1575 if ((line = el_gets(el, &count)) == NULL || count <= 0) {
1576 printf("\n");
1577 break;
1579 history(hl, &hev, H_ENTER, line);
1580 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1581 fprintf(stderr, "Error: input line too long\n");
1582 continue;
1584 #endif /* USE_LIBEDIT */
1587 cp = strrchr(cmd, '\n');
1588 if (cp)
1589 *cp = '\0';
1591 /* Handle user interrupts gracefully during commands */
1592 interrupted = 0;
1593 signal(SIGINT, cmd_interrupt);
1595 err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
1596 if (err != 0)
1597 break;
1599 xfree(pwd);
1600 xfree(conn);
1602 #ifdef USE_LIBEDIT
1603 if (el != NULL)
1604 el_end(el);
1605 #endif /* USE_LIBEDIT */
1607 /* err == 1 signifies normal "quit" exit */
1608 return (err >= 0 ? 0 : -1);
1611 static void
1612 connect_to_server(char *path, char **args, int *in, int *out)
1614 int c_in, c_out;
1616 #ifdef USE_PIPES
1617 int pin[2], pout[2];
1619 if ((pipe(pin) == -1) || (pipe(pout) == -1))
1620 fatal("pipe: %s", strerror(errno));
1621 *in = pin[0];
1622 *out = pout[1];
1623 c_in = pout[0];
1624 c_out = pin[1];
1625 #else /* USE_PIPES */
1626 int inout[2];
1628 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
1629 fatal("socketpair: %s", strerror(errno));
1630 *in = *out = inout[0];
1631 c_in = c_out = inout[1];
1632 #endif /* USE_PIPES */
1634 if ((sshpid = fork()) == -1)
1635 fatal("fork: %s", strerror(errno));
1636 else if (sshpid == 0) {
1637 if ((dup2(c_in, STDIN_FILENO) == -1) ||
1638 (dup2(c_out, STDOUT_FILENO) == -1)) {
1639 fprintf(stderr, "dup2: %s\n", strerror(errno));
1640 _exit(1);
1642 close(*in);
1643 close(*out);
1644 close(c_in);
1645 close(c_out);
1648 * The underlying ssh is in the same process group, so we must
1649 * ignore SIGINT if we want to gracefully abort commands,
1650 * otherwise the signal will make it to the ssh process and
1651 * kill it too
1653 signal(SIGINT, SIG_IGN);
1654 execvp(path, args);
1655 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
1656 _exit(1);
1659 signal(SIGTERM, killchild);
1660 signal(SIGINT, killchild);
1661 signal(SIGHUP, killchild);
1662 close(c_in);
1663 close(c_out);
1666 static void
1667 usage(void)
1669 extern char *__progname;
1671 fprintf(stderr,
1672 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
1673 " [-D sftp_server_path] [-F ssh_config] "
1674 "[-i identity_file]\n"
1675 " [-o ssh_option] [-P port] [-R num_requests] "
1676 "[-S program]\n"
1677 " [-s subsystem | sftp_server] host\n"
1678 " %s [user@]host[:file ...]\n"
1679 " %s [user@]host[:dir[/]]\n"
1680 " %s -b batchfile [user@]host\n",
1681 __progname, __progname, __progname, __progname);
1682 exit(1);
1686 main(int argc, char **argv)
1688 int in, out, ch, err;
1689 char *host, *userhost, *cp, *file2 = NULL;
1690 int debug_level = 0, sshver = 2;
1691 char *file1 = NULL, *sftp_server = NULL;
1692 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
1693 LogLevel ll = SYSLOG_LEVEL_INFO;
1694 arglist args;
1695 extern int optind;
1696 extern char *optarg;
1698 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1699 sanitise_stdfd();
1701 __progname = ssh_get_progname(argv[0]);
1702 memset(&args, '\0', sizeof(args));
1703 args.list = NULL;
1704 addargs(&args, "%s", ssh_program);
1705 addargs(&args, "-oForwardX11 no");
1706 addargs(&args, "-oForwardAgent no");
1707 addargs(&args, "-oPermitLocalCommand no");
1708 addargs(&args, "-oClearAllForwardings yes");
1710 ll = SYSLOG_LEVEL_INFO;
1711 infile = stdin;
1713 while ((ch = getopt(argc, argv,
1714 "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
1715 switch (ch) {
1716 /* Passed through to ssh(1) */
1717 case '4':
1718 case '6':
1719 case 'C':
1720 addargs(&args, "-%c", ch);
1721 break;
1722 /* Passed through to ssh(1) with argument */
1723 case 'F':
1724 case 'c':
1725 case 'i':
1726 case 'o':
1727 addargs(&args, "-%c%s", ch, optarg);
1728 break;
1729 case 'q':
1730 showprogress = 0;
1731 addargs(&args, "-%c", ch);
1732 break;
1733 case 'P':
1734 addargs(&args, "-oPort %s", optarg);
1735 break;
1736 case 'v':
1737 if (debug_level < 3) {
1738 addargs(&args, "-v");
1739 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
1741 debug_level++;
1742 break;
1743 case '1':
1744 sshver = 1;
1745 if (sftp_server == NULL)
1746 sftp_server = _PATH_SFTP_SERVER;
1747 break;
1748 case '2':
1749 sshver = 2;
1750 break;
1751 case 'B':
1752 copy_buffer_len = strtol(optarg, &cp, 10);
1753 if (copy_buffer_len == 0 || *cp != '\0')
1754 fatal("Invalid buffer size \"%s\"", optarg);
1755 break;
1756 case 'b':
1757 if (batchmode)
1758 fatal("Batch file already specified.");
1760 /* Allow "-" as stdin */
1761 if (strcmp(optarg, "-") != 0 &&
1762 (infile = fopen(optarg, "r")) == NULL)
1763 fatal("%s (%s).", strerror(errno), optarg);
1764 showprogress = 0;
1765 batchmode = 1;
1766 addargs(&args, "-obatchmode yes");
1767 break;
1768 case 'p':
1769 global_pflag = 1;
1770 break;
1771 case 'D':
1772 sftp_direct = optarg;
1773 break;
1774 case 'r':
1775 global_rflag = 1;
1776 break;
1777 case 'R':
1778 num_requests = strtol(optarg, &cp, 10);
1779 if (num_requests == 0 || *cp != '\0')
1780 fatal("Invalid number of requests \"%s\"",
1781 optarg);
1782 break;
1783 case 's':
1784 sftp_server = optarg;
1785 break;
1786 case 'S':
1787 ssh_program = optarg;
1788 replacearg(&args, 0, "%s", ssh_program);
1789 break;
1790 case 'h':
1791 default:
1792 usage();
1796 if (!isatty(STDERR_FILENO))
1797 showprogress = 0;
1799 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
1801 if (sftp_direct == NULL) {
1802 if (optind == argc || argc > (optind + 2))
1803 usage();
1805 userhost = xstrdup(argv[optind]);
1806 file2 = argv[optind+1];
1808 if ((host = strrchr(userhost, '@')) == NULL)
1809 host = userhost;
1810 else {
1811 *host++ = '\0';
1812 if (!userhost[0]) {
1813 fprintf(stderr, "Missing username\n");
1814 usage();
1816 addargs(&args, "-l%s", userhost);
1819 if ((cp = colon(host)) != NULL) {
1820 *cp++ = '\0';
1821 file1 = cp;
1824 host = cleanhostname(host);
1825 if (!*host) {
1826 fprintf(stderr, "Missing hostname\n");
1827 usage();
1830 addargs(&args, "-oProtocol %d", sshver);
1832 /* no subsystem if the server-spec contains a '/' */
1833 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
1834 addargs(&args, "-s");
1836 addargs(&args, "%s", host);
1837 addargs(&args, "%s", (sftp_server != NULL ?
1838 sftp_server : "sftp"));
1840 if (!batchmode)
1841 fprintf(stderr, "Connecting to %s...\n", host);
1842 connect_to_server(ssh_program, args.list, &in, &out);
1843 } else {
1844 args.list = NULL;
1845 addargs(&args, "sftp-server");
1847 if (!batchmode)
1848 fprintf(stderr, "Attaching to %s...\n", sftp_direct);
1849 connect_to_server(sftp_direct, args.list, &in, &out);
1851 freeargs(&args);
1853 err = interactive_loop(in, out, file1, file2);
1855 #if !defined(USE_PIPES)
1856 shutdown(in, SHUT_RDWR);
1857 shutdown(out, SHUT_RDWR);
1858 #endif
1860 close(in);
1861 close(out);
1862 if (batchmode)
1863 fclose(infile);
1865 while (waitpid(sshpid, NULL, 0) == -1)
1866 if (errno != EINTR)
1867 fatal("Couldn't wait for ssh process: %s",
1868 strerror(errno));
1870 exit(err == 0 ? 0 : 1);