- dtucker@cvs.openbsd.org 2010/01/04 01:45:30
[openssh-git.git] / sftp.c
blobd8728cc25e0c94a9393a7eac32f5ebb682b68d27
1 /* $OpenBSD: sftp.c,v 1.115 2009/12/20 07:28:36 guenther 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 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
72 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
74 /* File to read commands from */
75 FILE* infile;
77 /* Are we in batchfile mode? */
78 int batchmode = 0;
80 /* PID of ssh transport process */
81 static pid_t sshpid = -1;
83 /* This is set to 0 if the progressmeter is not desired. */
84 int showprogress = 1;
86 /* When this option is set, we always recursively download/upload directories */
87 int global_rflag = 0;
89 /* When this option is set, the file transfers will always preserve times */
90 int global_pflag = 0;
92 /* SIGINT received during command processing */
93 volatile sig_atomic_t interrupted = 0;
95 /* I wish qsort() took a separate ctx for the comparison function...*/
96 int sort_flag;
98 int remote_glob(struct sftp_conn *, const char *, int,
99 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
101 extern char *__progname;
103 /* Separators for interactive commands */
104 #define WHITESPACE " \t\r\n"
106 /* ls flags */
107 #define LS_LONG_VIEW 0x01 /* Full view ala ls -l */
108 #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */
109 #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */
110 #define LS_NAME_SORT 0x08 /* Sort by name (default) */
111 #define LS_TIME_SORT 0x10 /* Sort by mtime */
112 #define LS_SIZE_SORT 0x20 /* Sort by file size */
113 #define LS_REVERSE_SORT 0x40 /* Reverse sort order */
114 #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */
116 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
117 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
119 /* Commands for interactive mode */
120 #define I_CHDIR 1
121 #define I_CHGRP 2
122 #define I_CHMOD 3
123 #define I_CHOWN 4
124 #define I_DF 24
125 #define I_GET 5
126 #define I_HELP 6
127 #define I_LCHDIR 7
128 #define I_LLS 8
129 #define I_LMKDIR 9
130 #define I_LPWD 10
131 #define I_LS 11
132 #define I_LUMASK 12
133 #define I_MKDIR 13
134 #define I_PUT 14
135 #define I_PWD 15
136 #define I_QUIT 16
137 #define I_RENAME 17
138 #define I_RM 18
139 #define I_RMDIR 19
140 #define I_SHELL 20
141 #define I_SYMLINK 21
142 #define I_VERSION 22
143 #define I_PROGRESS 23
145 struct CMD {
146 const char *c;
147 const int n;
150 static const struct CMD cmds[] = {
151 { "bye", I_QUIT },
152 { "cd", I_CHDIR },
153 { "chdir", I_CHDIR },
154 { "chgrp", I_CHGRP },
155 { "chmod", I_CHMOD },
156 { "chown", I_CHOWN },
157 { "df", I_DF },
158 { "dir", I_LS },
159 { "exit", I_QUIT },
160 { "get", I_GET },
161 { "mget", I_GET },
162 { "help", I_HELP },
163 { "lcd", I_LCHDIR },
164 { "lchdir", I_LCHDIR },
165 { "lls", I_LLS },
166 { "lmkdir", I_LMKDIR },
167 { "ln", I_SYMLINK },
168 { "lpwd", I_LPWD },
169 { "ls", I_LS },
170 { "lumask", I_LUMASK },
171 { "mkdir", I_MKDIR },
172 { "progress", I_PROGRESS },
173 { "put", I_PUT },
174 { "mput", I_PUT },
175 { "pwd", I_PWD },
176 { "quit", I_QUIT },
177 { "rename", I_RENAME },
178 { "rm", I_RM },
179 { "rmdir", I_RMDIR },
180 { "symlink", I_SYMLINK },
181 { "version", I_VERSION },
182 { "!", I_SHELL },
183 { "?", I_HELP },
184 { NULL, -1}
187 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
189 /* ARGSUSED */
190 static void
191 killchild(int signo)
193 if (sshpid > 1) {
194 kill(sshpid, SIGTERM);
195 waitpid(sshpid, NULL, 0);
198 _exit(1);
201 /* ARGSUSED */
202 static void
203 cmd_interrupt(int signo)
205 const char msg[] = "\rInterrupt \n";
206 int olderrno = errno;
208 write(STDERR_FILENO, msg, sizeof(msg) - 1);
209 interrupted = 1;
210 errno = olderrno;
213 static void
214 help(void)
216 printf("Available commands:\n"
217 "bye Quit sftp\n"
218 "cd path Change remote directory to 'path'\n"
219 "chgrp grp path Change group of file 'path' to 'grp'\n"
220 "chmod mode path Change permissions of file 'path' to 'mode'\n"
221 "chown own path Change owner of file 'path' to 'own'\n"
222 "df [-hi] [path] Display statistics for current directory or\n"
223 " filesystem containing 'path'\n"
224 "exit Quit sftp\n"
225 "get [-Pr] remote-path [local-path] Download file\n"
226 "help Display this help text\n"
227 "lcd path Change local directory to 'path'\n"
228 "lls [ls-options [path]] Display local directory listing\n"
229 "lmkdir path Create local directory\n"
230 "ln oldpath newpath Symlink remote file\n"
231 "lpwd Print local working directory\n"
232 "ls [-1aflnrSt] [path] Display remote directory listing\n"
233 "lumask umask Set local umask to 'umask'\n"
234 "mkdir path Create remote directory\n"
235 "progress Toggle display of progress meter\n"
236 "put [-Pr] local-path [remote-path] Upload file\n"
237 "pwd Display remote working directory\n"
238 "quit Quit sftp\n"
239 "rename oldpath newpath Rename remote file\n"
240 "rm path Delete remote file\n"
241 "rmdir path Remove remote directory\n"
242 "symlink oldpath newpath Symlink remote file\n"
243 "version Show SFTP version\n"
244 "!command Execute 'command' in local shell\n"
245 "! Escape to local shell\n"
246 "? Synonym for help\n");
249 static void
250 local_do_shell(const char *args)
252 int status;
253 char *shell;
254 pid_t pid;
256 if (!*args)
257 args = NULL;
259 if ((shell = getenv("SHELL")) == NULL)
260 shell = _PATH_BSHELL;
262 if ((pid = fork()) == -1)
263 fatal("Couldn't fork: %s", strerror(errno));
265 if (pid == 0) {
266 /* XXX: child has pipe fds to ssh subproc open - issue? */
267 if (args) {
268 debug3("Executing %s -c \"%s\"", shell, args);
269 execl(shell, shell, "-c", args, (char *)NULL);
270 } else {
271 debug3("Executing %s", shell);
272 execl(shell, shell, (char *)NULL);
274 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
275 strerror(errno));
276 _exit(1);
278 while (waitpid(pid, &status, 0) == -1)
279 if (errno != EINTR)
280 fatal("Couldn't wait for child: %s", strerror(errno));
281 if (!WIFEXITED(status))
282 error("Shell exited abnormally");
283 else if (WEXITSTATUS(status))
284 error("Shell exited with status %d", WEXITSTATUS(status));
287 static void
288 local_do_ls(const char *args)
290 if (!args || !*args)
291 local_do_shell(_PATH_LS);
292 else {
293 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
294 char *buf = xmalloc(len);
296 /* XXX: quoting - rip quoting code from ftp? */
297 snprintf(buf, len, _PATH_LS " %s", args);
298 local_do_shell(buf);
299 xfree(buf);
303 /* Strip one path (usually the pwd) from the start of another */
304 static char *
305 path_strip(char *path, char *strip)
307 size_t len;
309 if (strip == NULL)
310 return (xstrdup(path));
312 len = strlen(strip);
313 if (strncmp(path, strip, len) == 0) {
314 if (strip[len - 1] != '/' && path[len] == '/')
315 len++;
316 return (xstrdup(path + len));
319 return (xstrdup(path));
322 static char *
323 make_absolute(char *p, char *pwd)
325 char *abs_str;
327 /* Derelativise */
328 if (p && p[0] != '/') {
329 abs_str = path_append(pwd, p);
330 xfree(p);
331 return(abs_str);
332 } else
333 return(p);
336 static int
337 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
338 int *rflag)
340 extern int opterr, optind, optopt, optreset;
341 int ch;
343 optind = optreset = 1;
344 opterr = 0;
346 *rflag = *pflag = 0;
347 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
348 switch (ch) {
349 case 'p':
350 case 'P':
351 *pflag = 1;
352 break;
353 case 'r':
354 case 'R':
355 *rflag = 1;
356 break;
357 default:
358 error("%s: Invalid flag -%c", cmd, optopt);
359 return -1;
363 return optind;
366 static int
367 parse_ls_flags(char **argv, int argc, int *lflag)
369 extern int opterr, optind, optopt, optreset;
370 int ch;
372 optind = optreset = 1;
373 opterr = 0;
375 *lflag = LS_NAME_SORT;
376 while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) {
377 switch (ch) {
378 case '1':
379 *lflag &= ~VIEW_FLAGS;
380 *lflag |= LS_SHORT_VIEW;
381 break;
382 case 'S':
383 *lflag &= ~SORT_FLAGS;
384 *lflag |= LS_SIZE_SORT;
385 break;
386 case 'a':
387 *lflag |= LS_SHOW_ALL;
388 break;
389 case 'f':
390 *lflag &= ~SORT_FLAGS;
391 break;
392 case 'l':
393 *lflag &= ~VIEW_FLAGS;
394 *lflag |= LS_LONG_VIEW;
395 break;
396 case 'n':
397 *lflag &= ~VIEW_FLAGS;
398 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
399 break;
400 case 'r':
401 *lflag |= LS_REVERSE_SORT;
402 break;
403 case 't':
404 *lflag &= ~SORT_FLAGS;
405 *lflag |= LS_TIME_SORT;
406 break;
407 default:
408 error("ls: Invalid flag -%c", optopt);
409 return -1;
413 return optind;
416 static int
417 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
419 extern int opterr, optind, optopt, optreset;
420 int ch;
422 optind = optreset = 1;
423 opterr = 0;
425 *hflag = *iflag = 0;
426 while ((ch = getopt(argc, argv, "hi")) != -1) {
427 switch (ch) {
428 case 'h':
429 *hflag = 1;
430 break;
431 case 'i':
432 *iflag = 1;
433 break;
434 default:
435 error("%s: Invalid flag -%c", cmd, optopt);
436 return -1;
440 return optind;
443 static int
444 is_dir(char *path)
446 struct stat sb;
448 /* XXX: report errors? */
449 if (stat(path, &sb) == -1)
450 return(0);
452 return(S_ISDIR(sb.st_mode));
455 static int
456 remote_is_dir(struct sftp_conn *conn, char *path)
458 Attrib *a;
460 /* XXX: report errors? */
461 if ((a = do_stat(conn, path, 1)) == NULL)
462 return(0);
463 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
464 return(0);
465 return(S_ISDIR(a->perm));
468 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
469 static int
470 pathname_is_dir(char *pathname)
472 size_t l = strlen(pathname);
474 return l > 0 && pathname[l - 1] == '/';
477 static int
478 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
479 int pflag, int rflag)
481 char *abs_src = NULL;
482 char *abs_dst = NULL;
483 glob_t g;
484 char *filename, *tmp=NULL;
485 int i, err = 0;
487 abs_src = xstrdup(src);
488 abs_src = make_absolute(abs_src, pwd);
489 memset(&g, 0, sizeof(g));
491 debug3("Looking up %s", abs_src);
492 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
493 error("File \"%s\" not found.", abs_src);
494 err = -1;
495 goto out;
499 * If multiple matches then dst must be a directory or
500 * unspecified.
502 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
503 error("Multiple source paths, but destination "
504 "\"%s\" is not a directory", dst);
505 err = -1;
506 goto out;
509 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
510 tmp = xstrdup(g.gl_pathv[i]);
511 if ((filename = basename(tmp)) == NULL) {
512 error("basename %s: %s", tmp, strerror(errno));
513 xfree(tmp);
514 err = -1;
515 goto out;
518 if (g.gl_matchc == 1 && dst) {
519 if (is_dir(dst)) {
520 abs_dst = path_append(dst, filename);
521 } else {
522 abs_dst = xstrdup(dst);
524 } else if (dst) {
525 abs_dst = path_append(dst, filename);
526 } else {
527 abs_dst = xstrdup(filename);
529 xfree(tmp);
531 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
532 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
533 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
534 pflag || global_pflag, 1) == -1)
535 err = -1;
536 } else {
537 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
538 pflag || global_pflag) == -1)
539 err = -1;
541 xfree(abs_dst);
542 abs_dst = NULL;
545 out:
546 xfree(abs_src);
547 globfree(&g);
548 return(err);
551 static int
552 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
553 int pflag, int rflag)
555 char *tmp_dst = NULL;
556 char *abs_dst = NULL;
557 char *tmp = NULL, *filename = NULL;
558 glob_t g;
559 int err = 0;
560 int i, dst_is_dir = 1;
561 struct stat sb;
563 if (dst) {
564 tmp_dst = xstrdup(dst);
565 tmp_dst = make_absolute(tmp_dst, pwd);
568 memset(&g, 0, sizeof(g));
569 debug3("Looking up %s", src);
570 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
571 error("File \"%s\" not found.", src);
572 err = -1;
573 goto out;
576 /* If we aren't fetching to pwd then stash this status for later */
577 if (tmp_dst != NULL)
578 dst_is_dir = remote_is_dir(conn, tmp_dst);
580 /* If multiple matches, dst may be directory or unspecified */
581 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
582 error("Multiple paths match, but destination "
583 "\"%s\" is not a directory", tmp_dst);
584 err = -1;
585 goto out;
588 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
589 if (stat(g.gl_pathv[i], &sb) == -1) {
590 err = -1;
591 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
592 continue;
595 tmp = xstrdup(g.gl_pathv[i]);
596 if ((filename = basename(tmp)) == NULL) {
597 error("basename %s: %s", tmp, strerror(errno));
598 xfree(tmp);
599 err = -1;
600 goto out;
603 if (g.gl_matchc == 1 && tmp_dst) {
604 /* If directory specified, append filename */
605 if (dst_is_dir)
606 abs_dst = path_append(tmp_dst, filename);
607 else
608 abs_dst = xstrdup(tmp_dst);
609 } else if (tmp_dst) {
610 abs_dst = path_append(tmp_dst, filename);
611 } else {
612 abs_dst = make_absolute(xstrdup(filename), pwd);
614 xfree(tmp);
616 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
617 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
618 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
619 pflag || global_pflag, 1) == -1)
620 err = -1;
621 } else {
622 if (do_upload(conn, g.gl_pathv[i], abs_dst,
623 pflag || global_pflag) == -1)
624 err = -1;
628 out:
629 if (abs_dst)
630 xfree(abs_dst);
631 if (tmp_dst)
632 xfree(tmp_dst);
633 globfree(&g);
634 return(err);
637 static int
638 sdirent_comp(const void *aa, const void *bb)
640 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
641 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
642 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
644 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
645 if (sort_flag & LS_NAME_SORT)
646 return (rmul * strcmp(a->filename, b->filename));
647 else if (sort_flag & LS_TIME_SORT)
648 return (rmul * NCMP(a->a.mtime, b->a.mtime));
649 else if (sort_flag & LS_SIZE_SORT)
650 return (rmul * NCMP(a->a.size, b->a.size));
652 fatal("Unknown ls sort type");
655 /* sftp ls.1 replacement for directories */
656 static int
657 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
659 int n;
660 u_int c = 1, colspace = 0, columns = 1;
661 SFTP_DIRENT **d;
663 if ((n = do_readdir(conn, path, &d)) != 0)
664 return (n);
666 if (!(lflag & LS_SHORT_VIEW)) {
667 u_int m = 0, width = 80;
668 struct winsize ws;
669 char *tmp;
671 /* Count entries for sort and find longest filename */
672 for (n = 0; d[n] != NULL; n++) {
673 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
674 m = MAX(m, strlen(d[n]->filename));
677 /* Add any subpath that also needs to be counted */
678 tmp = path_strip(path, strip_path);
679 m += strlen(tmp);
680 xfree(tmp);
682 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
683 width = ws.ws_col;
685 columns = width / (m + 2);
686 columns = MAX(columns, 1);
687 colspace = width / columns;
688 colspace = MIN(colspace, width);
691 if (lflag & SORT_FLAGS) {
692 for (n = 0; d[n] != NULL; n++)
693 ; /* count entries */
694 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
695 qsort(d, n, sizeof(*d), sdirent_comp);
698 for (n = 0; d[n] != NULL && !interrupted; n++) {
699 char *tmp, *fname;
701 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
702 continue;
704 tmp = path_append(path, d[n]->filename);
705 fname = path_strip(tmp, strip_path);
706 xfree(tmp);
708 if (lflag & LS_LONG_VIEW) {
709 if (lflag & LS_NUMERIC_VIEW) {
710 char *lname;
711 struct stat sb;
713 memset(&sb, 0, sizeof(sb));
714 attrib_to_stat(&d[n]->a, &sb);
715 lname = ls_file(fname, &sb, 1);
716 printf("%s\n", lname);
717 xfree(lname);
718 } else
719 printf("%s\n", d[n]->longname);
720 } else {
721 printf("%-*s", colspace, fname);
722 if (c >= columns) {
723 printf("\n");
724 c = 1;
725 } else
726 c++;
729 xfree(fname);
732 if (!(lflag & LS_LONG_VIEW) && (c != 1))
733 printf("\n");
735 free_sftp_dirents(d);
736 return (0);
739 /* sftp ls.1 replacement which handles path globs */
740 static int
741 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
742 int lflag)
744 glob_t g;
745 u_int i, c = 1, colspace = 0, columns = 1;
746 Attrib *a = NULL;
748 memset(&g, 0, sizeof(g));
750 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
751 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
752 if (g.gl_pathc)
753 globfree(&g);
754 error("Can't ls: \"%s\" not found", path);
755 return (-1);
758 if (interrupted)
759 goto out;
762 * If the glob returns a single match and it is a directory,
763 * then just list its contents.
765 if (g.gl_matchc == 1) {
766 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
767 globfree(&g);
768 return (-1);
770 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
771 S_ISDIR(a->perm)) {
772 int err;
774 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
775 globfree(&g);
776 return (err);
780 if (!(lflag & LS_SHORT_VIEW)) {
781 u_int m = 0, width = 80;
782 struct winsize ws;
784 /* Count entries for sort and find longest filename */
785 for (i = 0; g.gl_pathv[i]; i++)
786 m = MAX(m, strlen(g.gl_pathv[i]));
788 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
789 width = ws.ws_col;
791 columns = width / (m + 2);
792 columns = MAX(columns, 1);
793 colspace = width / columns;
796 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
797 char *fname;
799 fname = path_strip(g.gl_pathv[i], strip_path);
801 if (lflag & LS_LONG_VIEW) {
802 char *lname;
803 struct stat sb;
806 * XXX: this is slow - 1 roundtrip per path
807 * A solution to this is to fork glob() and
808 * build a sftp specific version which keeps the
809 * attribs (which currently get thrown away)
810 * that the server returns as well as the filenames.
812 memset(&sb, 0, sizeof(sb));
813 if (a == NULL)
814 a = do_lstat(conn, g.gl_pathv[i], 1);
815 if (a != NULL)
816 attrib_to_stat(a, &sb);
817 lname = ls_file(fname, &sb, 1);
818 printf("%s\n", lname);
819 xfree(lname);
820 } else {
821 printf("%-*s", colspace, fname);
822 if (c >= columns) {
823 printf("\n");
824 c = 1;
825 } else
826 c++;
828 xfree(fname);
831 if (!(lflag & LS_LONG_VIEW) && (c != 1))
832 printf("\n");
834 out:
835 if (g.gl_pathc)
836 globfree(&g);
838 return (0);
841 static int
842 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
844 struct sftp_statvfs st;
845 char s_used[FMT_SCALED_STRSIZE];
846 char s_avail[FMT_SCALED_STRSIZE];
847 char s_root[FMT_SCALED_STRSIZE];
848 char s_total[FMT_SCALED_STRSIZE];
849 unsigned long long ffree;
851 if (do_statvfs(conn, path, &st, 1) == -1)
852 return -1;
853 if (iflag) {
854 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
855 printf(" Inodes Used Avail "
856 "(root) %%Capacity\n");
857 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
858 (unsigned long long)st.f_files,
859 (unsigned long long)(st.f_files - st.f_ffree),
860 (unsigned long long)st.f_favail,
861 (unsigned long long)st.f_ffree, ffree);
862 } else if (hflag) {
863 strlcpy(s_used, "error", sizeof(s_used));
864 strlcpy(s_avail, "error", sizeof(s_avail));
865 strlcpy(s_root, "error", sizeof(s_root));
866 strlcpy(s_total, "error", sizeof(s_total));
867 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
868 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
869 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
870 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
871 printf(" Size Used Avail (root) %%Capacity\n");
872 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
873 s_total, s_used, s_avail, s_root,
874 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
875 st.f_blocks));
876 } else {
877 printf(" Size Used Avail "
878 "(root) %%Capacity\n");
879 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
880 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
881 (unsigned long long)(st.f_frsize *
882 (st.f_blocks - st.f_bfree) / 1024),
883 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
884 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
885 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
886 st.f_blocks));
888 return 0;
892 * Undo escaping of glob sequences in place. Used to undo extra escaping
893 * applied in makeargv() when the string is destined for a function that
894 * does not glob it.
896 static void
897 undo_glob_escape(char *s)
899 size_t i, j;
901 for (i = j = 0;;) {
902 if (s[i] == '\0') {
903 s[j] = '\0';
904 return;
906 if (s[i] != '\\') {
907 s[j++] = s[i++];
908 continue;
910 /* s[i] == '\\' */
911 ++i;
912 switch (s[i]) {
913 case '?':
914 case '[':
915 case '*':
916 case '\\':
917 s[j++] = s[i++];
918 break;
919 case '\0':
920 s[j++] = '\\';
921 s[j] = '\0';
922 return;
923 default:
924 s[j++] = '\\';
925 s[j++] = s[i++];
926 break;
932 * Split a string into an argument vector using sh(1)-style quoting,
933 * comment and escaping rules, but with some tweaks to handle glob(3)
934 * wildcards.
935 * Returns NULL on error or a NULL-terminated array of arguments.
937 #define MAXARGS 128
938 #define MAXARGLEN 8192
939 static char **
940 makeargv(const char *arg, int *argcp)
942 int argc, quot;
943 size_t i, j;
944 static char argvs[MAXARGLEN];
945 static char *argv[MAXARGS + 1];
946 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
948 *argcp = argc = 0;
949 if (strlen(arg) > sizeof(argvs) - 1) {
950 args_too_longs:
951 error("string too long");
952 return NULL;
954 state = MA_START;
955 i = j = 0;
956 for (;;) {
957 if (isspace(arg[i])) {
958 if (state == MA_UNQUOTED) {
959 /* Terminate current argument */
960 argvs[j++] = '\0';
961 argc++;
962 state = MA_START;
963 } else if (state != MA_START)
964 argvs[j++] = arg[i];
965 } else if (arg[i] == '"' || arg[i] == '\'') {
966 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
967 if (state == MA_START) {
968 argv[argc] = argvs + j;
969 state = q;
970 } else if (state == MA_UNQUOTED)
971 state = q;
972 else if (state == q)
973 state = MA_UNQUOTED;
974 else
975 argvs[j++] = arg[i];
976 } else if (arg[i] == '\\') {
977 if (state == MA_SQUOTE || state == MA_DQUOTE) {
978 quot = state == MA_SQUOTE ? '\'' : '"';
979 /* Unescape quote we are in */
980 /* XXX support \n and friends? */
981 if (arg[i + 1] == quot) {
982 i++;
983 argvs[j++] = arg[i];
984 } else if (arg[i + 1] == '?' ||
985 arg[i + 1] == '[' || arg[i + 1] == '*') {
987 * Special case for sftp: append
988 * double-escaped glob sequence -
989 * glob will undo one level of
990 * escaping. NB. string can grow here.
992 if (j >= sizeof(argvs) - 5)
993 goto args_too_longs;
994 argvs[j++] = '\\';
995 argvs[j++] = arg[i++];
996 argvs[j++] = '\\';
997 argvs[j++] = arg[i];
998 } else {
999 argvs[j++] = arg[i++];
1000 argvs[j++] = arg[i];
1002 } else {
1003 if (state == MA_START) {
1004 argv[argc] = argvs + j;
1005 state = MA_UNQUOTED;
1007 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1008 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1010 * Special case for sftp: append
1011 * escaped glob sequence -
1012 * glob will undo one level of
1013 * escaping.
1015 argvs[j++] = arg[i++];
1016 argvs[j++] = arg[i];
1017 } else {
1018 /* Unescape everything */
1019 /* XXX support \n and friends? */
1020 i++;
1021 argvs[j++] = arg[i];
1024 } else if (arg[i] == '#') {
1025 if (state == MA_SQUOTE || state == MA_DQUOTE)
1026 argvs[j++] = arg[i];
1027 else
1028 goto string_done;
1029 } else if (arg[i] == '\0') {
1030 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1031 error("Unterminated quoted argument");
1032 return NULL;
1034 string_done:
1035 if (state == MA_UNQUOTED) {
1036 argvs[j++] = '\0';
1037 argc++;
1039 break;
1040 } else {
1041 if (state == MA_START) {
1042 argv[argc] = argvs + j;
1043 state = MA_UNQUOTED;
1045 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1046 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1048 * Special case for sftp: escape quoted
1049 * glob(3) wildcards. NB. string can grow
1050 * here.
1052 if (j >= sizeof(argvs) - 3)
1053 goto args_too_longs;
1054 argvs[j++] = '\\';
1055 argvs[j++] = arg[i];
1056 } else
1057 argvs[j++] = arg[i];
1059 i++;
1061 *argcp = argc;
1062 return argv;
1065 static int
1066 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, int *hflag,
1067 unsigned long *n_arg, char **path1, char **path2)
1069 const char *cmd, *cp = *cpp;
1070 char *cp2, **argv;
1071 int base = 0;
1072 long l;
1073 int i, cmdnum, optidx, argc;
1075 /* Skip leading whitespace */
1076 cp = cp + strspn(cp, WHITESPACE);
1078 /* Ignore blank lines and lines which begin with comment '#' char */
1079 if (*cp == '\0' || *cp == '#')
1080 return (0);
1082 /* Check for leading '-' (disable error processing) */
1083 *iflag = 0;
1084 if (*cp == '-') {
1085 *iflag = 1;
1086 cp++;
1089 if ((argv = makeargv(cp, &argc)) == NULL)
1090 return -1;
1092 /* Figure out which command we have */
1093 for (i = 0; cmds[i].c != NULL; i++) {
1094 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1095 break;
1097 cmdnum = cmds[i].n;
1098 cmd = cmds[i].c;
1100 /* Special case */
1101 if (*cp == '!') {
1102 cp++;
1103 cmdnum = I_SHELL;
1104 } else if (cmdnum == -1) {
1105 error("Invalid command.");
1106 return -1;
1109 /* Get arguments and parse flags */
1110 *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1111 *path1 = *path2 = NULL;
1112 optidx = 1;
1113 switch (cmdnum) {
1114 case I_GET:
1115 case I_PUT:
1116 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
1117 return -1;
1118 /* Get first pathname (mandatory) */
1119 if (argc - optidx < 1) {
1120 error("You must specify at least one path after a "
1121 "%s command.", cmd);
1122 return -1;
1124 *path1 = xstrdup(argv[optidx]);
1125 /* Get second pathname (optional) */
1126 if (argc - optidx > 1) {
1127 *path2 = xstrdup(argv[optidx + 1]);
1128 /* Destination is not globbed */
1129 undo_glob_escape(*path2);
1131 break;
1132 case I_RENAME:
1133 case I_SYMLINK:
1134 if (argc - optidx < 2) {
1135 error("You must specify two paths after a %s "
1136 "command.", cmd);
1137 return -1;
1139 *path1 = xstrdup(argv[optidx]);
1140 *path2 = xstrdup(argv[optidx + 1]);
1141 /* Paths are not globbed */
1142 undo_glob_escape(*path1);
1143 undo_glob_escape(*path2);
1144 break;
1145 case I_RM:
1146 case I_MKDIR:
1147 case I_RMDIR:
1148 case I_CHDIR:
1149 case I_LCHDIR:
1150 case I_LMKDIR:
1151 /* Get pathname (mandatory) */
1152 if (argc - optidx < 1) {
1153 error("You must specify a path after a %s command.",
1154 cmd);
1155 return -1;
1157 *path1 = xstrdup(argv[optidx]);
1158 /* Only "rm" globs */
1159 if (cmdnum != I_RM)
1160 undo_glob_escape(*path1);
1161 break;
1162 case I_DF:
1163 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1164 iflag)) == -1)
1165 return -1;
1166 /* Default to current directory if no path specified */
1167 if (argc - optidx < 1)
1168 *path1 = NULL;
1169 else {
1170 *path1 = xstrdup(argv[optidx]);
1171 undo_glob_escape(*path1);
1173 break;
1174 case I_LS:
1175 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1176 return(-1);
1177 /* Path is optional */
1178 if (argc - optidx > 0)
1179 *path1 = xstrdup(argv[optidx]);
1180 break;
1181 case I_LLS:
1182 /* Skip ls command and following whitespace */
1183 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1184 case I_SHELL:
1185 /* Uses the rest of the line */
1186 break;
1187 case I_LUMASK:
1188 case I_CHMOD:
1189 base = 8;
1190 case I_CHOWN:
1191 case I_CHGRP:
1192 /* Get numeric arg (mandatory) */
1193 if (argc - optidx < 1)
1194 goto need_num_arg;
1195 errno = 0;
1196 l = strtol(argv[optidx], &cp2, base);
1197 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1198 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1199 l < 0) {
1200 need_num_arg:
1201 error("You must supply a numeric argument "
1202 "to the %s command.", cmd);
1203 return -1;
1205 *n_arg = l;
1206 if (cmdnum == I_LUMASK)
1207 break;
1208 /* Get pathname (mandatory) */
1209 if (argc - optidx < 2) {
1210 error("You must specify a path after a %s command.",
1211 cmd);
1212 return -1;
1214 *path1 = xstrdup(argv[optidx + 1]);
1215 break;
1216 case I_QUIT:
1217 case I_PWD:
1218 case I_LPWD:
1219 case I_HELP:
1220 case I_VERSION:
1221 case I_PROGRESS:
1222 break;
1223 default:
1224 fatal("Command not implemented");
1227 *cpp = cp;
1228 return(cmdnum);
1231 static int
1232 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1233 int err_abort)
1235 char *path1, *path2, *tmp;
1236 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1237 unsigned long n_arg = 0;
1238 Attrib a, *aa;
1239 char path_buf[MAXPATHLEN];
1240 int err = 0;
1241 glob_t g;
1243 path1 = path2 = NULL;
1244 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1245 &path1, &path2);
1247 if (iflag != 0)
1248 err_abort = 0;
1250 memset(&g, 0, sizeof(g));
1252 /* Perform command */
1253 switch (cmdnum) {
1254 case 0:
1255 /* Blank line */
1256 break;
1257 case -1:
1258 /* Unrecognized command */
1259 err = -1;
1260 break;
1261 case I_GET:
1262 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1263 break;
1264 case I_PUT:
1265 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1266 break;
1267 case I_RENAME:
1268 path1 = make_absolute(path1, *pwd);
1269 path2 = make_absolute(path2, *pwd);
1270 err = do_rename(conn, path1, path2);
1271 break;
1272 case I_SYMLINK:
1273 path2 = make_absolute(path2, *pwd);
1274 err = do_symlink(conn, path1, path2);
1275 break;
1276 case I_RM:
1277 path1 = make_absolute(path1, *pwd);
1278 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1279 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1280 printf("Removing %s\n", g.gl_pathv[i]);
1281 err = do_rm(conn, g.gl_pathv[i]);
1282 if (err != 0 && err_abort)
1283 break;
1285 break;
1286 case I_MKDIR:
1287 path1 = make_absolute(path1, *pwd);
1288 attrib_clear(&a);
1289 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1290 a.perm = 0777;
1291 err = do_mkdir(conn, path1, &a, 1);
1292 break;
1293 case I_RMDIR:
1294 path1 = make_absolute(path1, *pwd);
1295 err = do_rmdir(conn, path1);
1296 break;
1297 case I_CHDIR:
1298 path1 = make_absolute(path1, *pwd);
1299 if ((tmp = do_realpath(conn, path1)) == NULL) {
1300 err = 1;
1301 break;
1303 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1304 xfree(tmp);
1305 err = 1;
1306 break;
1308 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1309 error("Can't change directory: Can't check target");
1310 xfree(tmp);
1311 err = 1;
1312 break;
1314 if (!S_ISDIR(aa->perm)) {
1315 error("Can't change directory: \"%s\" is not "
1316 "a directory", tmp);
1317 xfree(tmp);
1318 err = 1;
1319 break;
1321 xfree(*pwd);
1322 *pwd = tmp;
1323 break;
1324 case I_LS:
1325 if (!path1) {
1326 do_globbed_ls(conn, *pwd, *pwd, lflag);
1327 break;
1330 /* Strip pwd off beginning of non-absolute paths */
1331 tmp = NULL;
1332 if (*path1 != '/')
1333 tmp = *pwd;
1335 path1 = make_absolute(path1, *pwd);
1336 err = do_globbed_ls(conn, path1, tmp, lflag);
1337 break;
1338 case I_DF:
1339 /* Default to current directory if no path specified */
1340 if (path1 == NULL)
1341 path1 = xstrdup(*pwd);
1342 path1 = make_absolute(path1, *pwd);
1343 err = do_df(conn, path1, hflag, iflag);
1344 break;
1345 case I_LCHDIR:
1346 if (chdir(path1) == -1) {
1347 error("Couldn't change local directory to "
1348 "\"%s\": %s", path1, strerror(errno));
1349 err = 1;
1351 break;
1352 case I_LMKDIR:
1353 if (mkdir(path1, 0777) == -1) {
1354 error("Couldn't create local directory "
1355 "\"%s\": %s", path1, strerror(errno));
1356 err = 1;
1358 break;
1359 case I_LLS:
1360 local_do_ls(cmd);
1361 break;
1362 case I_SHELL:
1363 local_do_shell(cmd);
1364 break;
1365 case I_LUMASK:
1366 umask(n_arg);
1367 printf("Local umask: %03lo\n", n_arg);
1368 break;
1369 case I_CHMOD:
1370 path1 = make_absolute(path1, *pwd);
1371 attrib_clear(&a);
1372 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1373 a.perm = n_arg;
1374 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1375 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1376 printf("Changing mode on %s\n", g.gl_pathv[i]);
1377 err = do_setstat(conn, g.gl_pathv[i], &a);
1378 if (err != 0 && err_abort)
1379 break;
1381 break;
1382 case I_CHOWN:
1383 case I_CHGRP:
1384 path1 = make_absolute(path1, *pwd);
1385 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1386 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1387 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1388 if (err_abort) {
1389 err = -1;
1390 break;
1391 } else
1392 continue;
1394 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1395 error("Can't get current ownership of "
1396 "remote file \"%s\"", g.gl_pathv[i]);
1397 if (err_abort) {
1398 err = -1;
1399 break;
1400 } else
1401 continue;
1403 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1404 if (cmdnum == I_CHOWN) {
1405 printf("Changing owner on %s\n", g.gl_pathv[i]);
1406 aa->uid = n_arg;
1407 } else {
1408 printf("Changing group on %s\n", g.gl_pathv[i]);
1409 aa->gid = n_arg;
1411 err = do_setstat(conn, g.gl_pathv[i], aa);
1412 if (err != 0 && err_abort)
1413 break;
1415 break;
1416 case I_PWD:
1417 printf("Remote working directory: %s\n", *pwd);
1418 break;
1419 case I_LPWD:
1420 if (!getcwd(path_buf, sizeof(path_buf))) {
1421 error("Couldn't get local cwd: %s", strerror(errno));
1422 err = -1;
1423 break;
1425 printf("Local working directory: %s\n", path_buf);
1426 break;
1427 case I_QUIT:
1428 /* Processed below */
1429 break;
1430 case I_HELP:
1431 help();
1432 break;
1433 case I_VERSION:
1434 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1435 break;
1436 case I_PROGRESS:
1437 showprogress = !showprogress;
1438 if (showprogress)
1439 printf("Progress meter enabled\n");
1440 else
1441 printf("Progress meter disabled\n");
1442 break;
1443 default:
1444 fatal("%d is not implemented", cmdnum);
1447 if (g.gl_pathc)
1448 globfree(&g);
1449 if (path1)
1450 xfree(path1);
1451 if (path2)
1452 xfree(path2);
1454 /* If an unignored error occurs in batch mode we should abort. */
1455 if (err_abort && err != 0)
1456 return (-1);
1457 else if (cmdnum == I_QUIT)
1458 return (1);
1460 return (0);
1463 #ifdef USE_LIBEDIT
1464 static char *
1465 prompt(EditLine *el)
1467 return ("sftp> ");
1469 #endif
1472 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1474 char *pwd;
1475 char *dir = NULL;
1476 char cmd[2048];
1477 int err, interactive;
1478 EditLine *el = NULL;
1479 #ifdef USE_LIBEDIT
1480 History *hl = NULL;
1481 HistEvent hev;
1482 extern char *__progname;
1484 if (!batchmode && isatty(STDIN_FILENO)) {
1485 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1486 fatal("Couldn't initialise editline");
1487 if ((hl = history_init()) == NULL)
1488 fatal("Couldn't initialise editline history");
1489 history(hl, &hev, H_SETSIZE, 100);
1490 el_set(el, EL_HIST, history, hl);
1492 el_set(el, EL_PROMPT, prompt);
1493 el_set(el, EL_EDITOR, "emacs");
1494 el_set(el, EL_TERMINAL, NULL);
1495 el_set(el, EL_SIGNAL, 1);
1496 el_source(el, NULL);
1498 #endif /* USE_LIBEDIT */
1500 pwd = do_realpath(conn, ".");
1501 if (pwd == NULL)
1502 fatal("Need cwd");
1504 if (file1 != NULL) {
1505 dir = xstrdup(file1);
1506 dir = make_absolute(dir, pwd);
1508 if (remote_is_dir(conn, dir) && file2 == NULL) {
1509 printf("Changing to: %s\n", dir);
1510 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1511 if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) {
1512 xfree(dir);
1513 xfree(pwd);
1514 xfree(conn);
1515 return (-1);
1517 } else {
1518 if (file2 == NULL)
1519 snprintf(cmd, sizeof cmd, "get %s", dir);
1520 else
1521 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1522 file2);
1524 err = parse_dispatch_command(conn, cmd, &pwd, 1);
1525 xfree(dir);
1526 xfree(pwd);
1527 xfree(conn);
1528 return (err);
1530 xfree(dir);
1533 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1534 setvbuf(stdout, NULL, _IOLBF, 0);
1535 setvbuf(infile, NULL, _IOLBF, 0);
1536 #else
1537 setlinebuf(stdout);
1538 setlinebuf(infile);
1539 #endif
1541 interactive = !batchmode && isatty(STDIN_FILENO);
1542 err = 0;
1543 for (;;) {
1544 char *cp;
1546 signal(SIGINT, SIG_IGN);
1548 if (el == NULL) {
1549 if (interactive)
1550 printf("sftp> ");
1551 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1552 if (interactive)
1553 printf("\n");
1554 break;
1556 if (!interactive) { /* Echo command */
1557 printf("sftp> %s", cmd);
1558 if (strlen(cmd) > 0 &&
1559 cmd[strlen(cmd) - 1] != '\n')
1560 printf("\n");
1562 } else {
1563 #ifdef USE_LIBEDIT
1564 const char *line;
1565 int count = 0;
1567 if ((line = el_gets(el, &count)) == NULL || count <= 0) {
1568 printf("\n");
1569 break;
1571 history(hl, &hev, H_ENTER, line);
1572 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1573 fprintf(stderr, "Error: input line too long\n");
1574 continue;
1576 #endif /* USE_LIBEDIT */
1579 cp = strrchr(cmd, '\n');
1580 if (cp)
1581 *cp = '\0';
1583 /* Handle user interrupts gracefully during commands */
1584 interrupted = 0;
1585 signal(SIGINT, cmd_interrupt);
1587 err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
1588 if (err != 0)
1589 break;
1591 xfree(pwd);
1592 xfree(conn);
1594 #ifdef USE_LIBEDIT
1595 if (el != NULL)
1596 el_end(el);
1597 #endif /* USE_LIBEDIT */
1599 /* err == 1 signifies normal "quit" exit */
1600 return (err >= 0 ? 0 : -1);
1603 static void
1604 connect_to_server(char *path, char **args, int *in, int *out)
1606 int c_in, c_out;
1608 #ifdef USE_PIPES
1609 int pin[2], pout[2];
1611 if ((pipe(pin) == -1) || (pipe(pout) == -1))
1612 fatal("pipe: %s", strerror(errno));
1613 *in = pin[0];
1614 *out = pout[1];
1615 c_in = pout[0];
1616 c_out = pin[1];
1617 #else /* USE_PIPES */
1618 int inout[2];
1620 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
1621 fatal("socketpair: %s", strerror(errno));
1622 *in = *out = inout[0];
1623 c_in = c_out = inout[1];
1624 #endif /* USE_PIPES */
1626 if ((sshpid = fork()) == -1)
1627 fatal("fork: %s", strerror(errno));
1628 else if (sshpid == 0) {
1629 if ((dup2(c_in, STDIN_FILENO) == -1) ||
1630 (dup2(c_out, STDOUT_FILENO) == -1)) {
1631 fprintf(stderr, "dup2: %s\n", strerror(errno));
1632 _exit(1);
1634 close(*in);
1635 close(*out);
1636 close(c_in);
1637 close(c_out);
1640 * The underlying ssh is in the same process group, so we must
1641 * ignore SIGINT if we want to gracefully abort commands,
1642 * otherwise the signal will make it to the ssh process and
1643 * kill it too
1645 signal(SIGINT, SIG_IGN);
1646 execvp(path, args);
1647 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
1648 _exit(1);
1651 signal(SIGTERM, killchild);
1652 signal(SIGINT, killchild);
1653 signal(SIGHUP, killchild);
1654 close(c_in);
1655 close(c_out);
1658 static void
1659 usage(void)
1661 extern char *__progname;
1663 fprintf(stderr,
1664 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
1665 " [-D sftp_server_path] [-F ssh_config] "
1666 "[-i identity_file]\n"
1667 " [-o ssh_option] [-P port] [-R num_requests] "
1668 "[-S program]\n"
1669 " [-s subsystem | sftp_server] host\n"
1670 " %s [user@]host[:file ...]\n"
1671 " %s [user@]host[:dir[/]]\n"
1672 " %s -b batchfile [user@]host\n",
1673 __progname, __progname, __progname, __progname);
1674 exit(1);
1678 main(int argc, char **argv)
1680 int in, out, ch, err;
1681 char *host, *userhost, *cp, *file2 = NULL;
1682 int debug_level = 0, sshver = 2;
1683 char *file1 = NULL, *sftp_server = NULL;
1684 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
1685 LogLevel ll = SYSLOG_LEVEL_INFO;
1686 arglist args;
1687 extern int optind;
1688 extern char *optarg;
1689 struct sftp_conn *conn;
1690 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
1691 size_t num_requests = DEFAULT_NUM_REQUESTS;
1693 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1694 sanitise_stdfd();
1696 __progname = ssh_get_progname(argv[0]);
1697 memset(&args, '\0', sizeof(args));
1698 args.list = NULL;
1699 addargs(&args, "%s", ssh_program);
1700 addargs(&args, "-oForwardX11 no");
1701 addargs(&args, "-oForwardAgent no");
1702 addargs(&args, "-oPermitLocalCommand no");
1703 addargs(&args, "-oClearAllForwardings yes");
1705 ll = SYSLOG_LEVEL_INFO;
1706 infile = stdin;
1708 while ((ch = getopt(argc, argv,
1709 "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
1710 switch (ch) {
1711 /* Passed through to ssh(1) */
1712 case '4':
1713 case '6':
1714 case 'C':
1715 addargs(&args, "-%c", ch);
1716 break;
1717 /* Passed through to ssh(1) with argument */
1718 case 'F':
1719 case 'c':
1720 case 'i':
1721 case 'o':
1722 addargs(&args, "-%c", ch);
1723 addargs(&args, "%s", optarg);
1724 break;
1725 case 'q':
1726 showprogress = 0;
1727 addargs(&args, "-%c", ch);
1728 break;
1729 case 'P':
1730 addargs(&args, "-oPort %s", optarg);
1731 break;
1732 case 'v':
1733 if (debug_level < 3) {
1734 addargs(&args, "-v");
1735 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
1737 debug_level++;
1738 break;
1739 case '1':
1740 sshver = 1;
1741 if (sftp_server == NULL)
1742 sftp_server = _PATH_SFTP_SERVER;
1743 break;
1744 case '2':
1745 sshver = 2;
1746 break;
1747 case 'B':
1748 copy_buffer_len = strtol(optarg, &cp, 10);
1749 if (copy_buffer_len == 0 || *cp != '\0')
1750 fatal("Invalid buffer size \"%s\"", optarg);
1751 break;
1752 case 'b':
1753 if (batchmode)
1754 fatal("Batch file already specified.");
1756 /* Allow "-" as stdin */
1757 if (strcmp(optarg, "-") != 0 &&
1758 (infile = fopen(optarg, "r")) == NULL)
1759 fatal("%s (%s).", strerror(errno), optarg);
1760 showprogress = 0;
1761 batchmode = 1;
1762 addargs(&args, "-obatchmode yes");
1763 break;
1764 case 'p':
1765 global_pflag = 1;
1766 break;
1767 case 'D':
1768 sftp_direct = optarg;
1769 break;
1770 case 'r':
1771 global_rflag = 1;
1772 break;
1773 case 'R':
1774 num_requests = strtol(optarg, &cp, 10);
1775 if (num_requests == 0 || *cp != '\0')
1776 fatal("Invalid number of requests \"%s\"",
1777 optarg);
1778 break;
1779 case 's':
1780 sftp_server = optarg;
1781 break;
1782 case 'S':
1783 ssh_program = optarg;
1784 replacearg(&args, 0, "%s", ssh_program);
1785 break;
1786 case 'h':
1787 default:
1788 usage();
1792 if (!isatty(STDERR_FILENO))
1793 showprogress = 0;
1795 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
1797 if (sftp_direct == NULL) {
1798 if (optind == argc || argc > (optind + 2))
1799 usage();
1801 userhost = xstrdup(argv[optind]);
1802 file2 = argv[optind+1];
1804 if ((host = strrchr(userhost, '@')) == NULL)
1805 host = userhost;
1806 else {
1807 *host++ = '\0';
1808 if (!userhost[0]) {
1809 fprintf(stderr, "Missing username\n");
1810 usage();
1812 addargs(&args, "-l");
1813 addargs(&args, "%s", userhost);
1816 if ((cp = colon(host)) != NULL) {
1817 *cp++ = '\0';
1818 file1 = cp;
1821 host = cleanhostname(host);
1822 if (!*host) {
1823 fprintf(stderr, "Missing hostname\n");
1824 usage();
1827 addargs(&args, "-oProtocol %d", sshver);
1829 /* no subsystem if the server-spec contains a '/' */
1830 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
1831 addargs(&args, "-s");
1833 addargs(&args, "--");
1834 addargs(&args, "%s", host);
1835 addargs(&args, "%s", (sftp_server != NULL ?
1836 sftp_server : "sftp"));
1838 connect_to_server(ssh_program, args.list, &in, &out);
1839 } else {
1840 args.list = NULL;
1841 addargs(&args, "sftp-server");
1843 connect_to_server(sftp_direct, args.list, &in, &out);
1845 freeargs(&args);
1847 conn = do_init(in, out, copy_buffer_len, num_requests);
1848 if (conn == NULL)
1849 fatal("Couldn't initialise connection to server");
1851 if (!batchmode) {
1852 if (sftp_direct == NULL)
1853 fprintf(stderr, "Connected to %s.\n", host);
1854 else
1855 fprintf(stderr, "Attached to %s.\n", sftp_direct);
1858 err = interactive_loop(conn, file1, file2);
1860 #if !defined(USE_PIPES)
1861 shutdown(in, SHUT_RDWR);
1862 shutdown(out, SHUT_RDWR);
1863 #endif
1865 close(in);
1866 close(out);
1867 if (batchmode)
1868 fclose(infile);
1870 while (waitpid(sshpid, NULL, 0) == -1)
1871 if (errno != EINTR)
1872 fatal("Couldn't wait for ssh process: %s",
1873 strerror(errno));
1875 exit(err == 0 ? 0 : 1);