vnconfig.8: Describe autocloning & improve markup
[dragonfly.git] / crypto / openssh / sftp.c
blob66bd111b122c47ad5ca7380cda5261a424f675e0
1 /* $OpenBSD: sftp.c,v 1.107 2009/02/02 11:15:14 dtucker 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 USE_LIBEDIT
39 #include <histedit.h>
40 #else
41 typedef void EditLine;
42 #endif
43 #include <signal.h>
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <stdarg.h>
50 #ifdef HAVE_UTIL_H
51 # include <util.h>
52 #endif
54 #ifdef HAVE_LIBUTIL_H
55 # include <libutil.h>
56 #endif
58 #include "xmalloc.h"
59 #include "log.h"
60 #include "pathnames.h"
61 #include "misc.h"
63 #include "sftp.h"
64 #include "buffer.h"
65 #include "sftp-common.h"
66 #include "sftp-client.h"
68 /* File to read commands from */
69 FILE* infile;
71 /* Are we in batchfile mode? */
72 int batchmode = 0;
74 /* Size of buffer used when copying files */
75 size_t copy_buffer_len = 32768;
77 /* Number of concurrent outstanding requests */
78 size_t num_requests = 64;
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 /* SIGINT received during command processing */
87 volatile sig_atomic_t interrupted = 0;
89 /* I wish qsort() took a separate ctx for the comparison function...*/
90 int sort_flag;
92 int remote_glob(struct sftp_conn *, const char *, int,
93 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
95 extern char *__progname;
97 /* Separators for interactive commands */
98 #define WHITESPACE " \t\r\n"
100 /* ls flags */
101 #define LS_LONG_VIEW 0x01 /* Full view ala ls -l */
102 #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */
103 #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */
104 #define LS_NAME_SORT 0x08 /* Sort by name (default) */
105 #define LS_TIME_SORT 0x10 /* Sort by mtime */
106 #define LS_SIZE_SORT 0x20 /* Sort by file size */
107 #define LS_REVERSE_SORT 0x40 /* Reverse sort order */
108 #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */
110 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
111 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
113 /* Commands for interactive mode */
114 #define I_CHDIR 1
115 #define I_CHGRP 2
116 #define I_CHMOD 3
117 #define I_CHOWN 4
118 #define I_DF 24
119 #define I_GET 5
120 #define I_HELP 6
121 #define I_LCHDIR 7
122 #define I_LLS 8
123 #define I_LMKDIR 9
124 #define I_LPWD 10
125 #define I_LS 11
126 #define I_LUMASK 12
127 #define I_MKDIR 13
128 #define I_PUT 14
129 #define I_PWD 15
130 #define I_QUIT 16
131 #define I_RENAME 17
132 #define I_RM 18
133 #define I_RMDIR 19
134 #define I_SHELL 20
135 #define I_SYMLINK 21
136 #define I_VERSION 22
137 #define I_PROGRESS 23
139 struct CMD {
140 const char *c;
141 const int n;
144 static const struct CMD cmds[] = {
145 { "bye", I_QUIT },
146 { "cd", I_CHDIR },
147 { "chdir", I_CHDIR },
148 { "chgrp", I_CHGRP },
149 { "chmod", I_CHMOD },
150 { "chown", I_CHOWN },
151 { "df", I_DF },
152 { "dir", I_LS },
153 { "exit", I_QUIT },
154 { "get", I_GET },
155 { "mget", I_GET },
156 { "help", I_HELP },
157 { "lcd", I_LCHDIR },
158 { "lchdir", I_LCHDIR },
159 { "lls", I_LLS },
160 { "lmkdir", I_LMKDIR },
161 { "ln", I_SYMLINK },
162 { "lpwd", I_LPWD },
163 { "ls", I_LS },
164 { "lumask", I_LUMASK },
165 { "mkdir", I_MKDIR },
166 { "progress", I_PROGRESS },
167 { "put", I_PUT },
168 { "mput", I_PUT },
169 { "pwd", I_PWD },
170 { "quit", I_QUIT },
171 { "rename", I_RENAME },
172 { "rm", I_RM },
173 { "rmdir", I_RMDIR },
174 { "symlink", I_SYMLINK },
175 { "version", I_VERSION },
176 { "!", I_SHELL },
177 { "?", I_HELP },
178 { NULL, -1}
181 int interactive_loop(int fd_in, int fd_out, char *file1, char *file2);
183 /* ARGSUSED */
184 static void
185 killchild(int signo)
187 if (sshpid > 1) {
188 kill(sshpid, SIGTERM);
189 waitpid(sshpid, NULL, 0);
192 _exit(1);
195 /* ARGSUSED */
196 static void
197 cmd_interrupt(int signo)
199 const char msg[] = "\rInterrupt \n";
200 int olderrno = errno;
202 write(STDERR_FILENO, msg, sizeof(msg) - 1);
203 interrupted = 1;
204 errno = olderrno;
207 static void
208 help(void)
210 printf("Available commands:\n"
211 "bye Quit sftp\n"
212 "cd path Change remote directory to 'path'\n"
213 "chgrp grp path Change group of file 'path' to 'grp'\n"
214 "chmod mode path Change permissions of file 'path' to 'mode'\n"
215 "chown own path Change owner of file 'path' to 'own'\n"
216 "df [-hi] [path] Display statistics for current directory or\n"
217 " filesystem containing 'path'\n"
218 "exit Quit sftp\n"
219 "get [-P] remote-path [local-path] Download file\n"
220 "help Display this help text\n"
221 "lcd path Change local directory to 'path'\n"
222 "lls [ls-options [path]] Display local directory listing\n"
223 "lmkdir path Create local directory\n"
224 "ln oldpath newpath Symlink remote file\n"
225 "lpwd Print local working directory\n"
226 "ls [-1aflnrSt] [path] Display remote directory listing\n"
227 "lumask umask Set local umask to 'umask'\n"
228 "mkdir path Create remote directory\n"
229 "progress Toggle display of progress meter\n"
230 "put [-P] local-path [remote-path] Upload file\n"
231 "pwd Display remote working directory\n"
232 "quit Quit sftp\n"
233 "rename oldpath newpath Rename remote file\n"
234 "rm path Delete remote file\n"
235 "rmdir path Remove remote directory\n"
236 "symlink oldpath newpath Symlink remote file\n"
237 "version Show SFTP version\n"
238 "!command Execute 'command' in local shell\n"
239 "! Escape to local shell\n"
240 "? Synonym for help\n");
243 static void
244 local_do_shell(const char *args)
246 int status;
247 char *shell;
248 pid_t pid;
250 if (!*args)
251 args = NULL;
253 if ((shell = getenv("SHELL")) == NULL)
254 shell = _PATH_BSHELL;
256 if ((pid = fork()) == -1)
257 fatal("Couldn't fork: %s", strerror(errno));
259 if (pid == 0) {
260 /* XXX: child has pipe fds to ssh subproc open - issue? */
261 if (args) {
262 debug3("Executing %s -c \"%s\"", shell, args);
263 execl(shell, shell, "-c", args, (char *)NULL);
264 } else {
265 debug3("Executing %s", shell);
266 execl(shell, shell, (char *)NULL);
268 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
269 strerror(errno));
270 _exit(1);
272 while (waitpid(pid, &status, 0) == -1)
273 if (errno != EINTR)
274 fatal("Couldn't wait for child: %s", strerror(errno));
275 if (!WIFEXITED(status))
276 error("Shell exited abnormally");
277 else if (WEXITSTATUS(status))
278 error("Shell exited with status %d", WEXITSTATUS(status));
281 static void
282 local_do_ls(const char *args)
284 if (!args || !*args)
285 local_do_shell(_PATH_LS);
286 else {
287 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
288 char *buf = xmalloc(len);
290 /* XXX: quoting - rip quoting code from ftp? */
291 snprintf(buf, len, _PATH_LS " %s", args);
292 local_do_shell(buf);
293 xfree(buf);
297 /* Strip one path (usually the pwd) from the start of another */
298 static char *
299 path_strip(char *path, char *strip)
301 size_t len;
303 if (strip == NULL)
304 return (xstrdup(path));
306 len = strlen(strip);
307 if (strncmp(path, strip, len) == 0) {
308 if (strip[len - 1] != '/' && path[len] == '/')
309 len++;
310 return (xstrdup(path + len));
313 return (xstrdup(path));
316 static char *
317 path_append(char *p1, char *p2)
319 char *ret;
320 size_t len = strlen(p1) + strlen(p2) + 2;
322 ret = xmalloc(len);
323 strlcpy(ret, p1, len);
324 if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
325 strlcat(ret, "/", len);
326 strlcat(ret, p2, len);
328 return(ret);
331 static char *
332 make_absolute(char *p, char *pwd)
334 char *abs_str;
336 /* Derelativise */
337 if (p && p[0] != '/') {
338 abs_str = path_append(pwd, p);
339 xfree(p);
340 return(abs_str);
341 } else
342 return(p);
345 static int
346 infer_path(const char *p, char **ifp)
348 char *cp;
350 cp = strrchr(p, '/');
351 if (cp == NULL) {
352 *ifp = xstrdup(p);
353 return(0);
356 if (!cp[1]) {
357 error("Invalid path");
358 return(-1);
361 *ifp = xstrdup(cp + 1);
362 return(0);
365 static int
366 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
368 extern int opterr, optind, optopt, optreset;
369 int ch;
371 optind = optreset = 1;
372 opterr = 0;
374 *pflag = 0;
375 while ((ch = getopt(argc, argv, "Pp")) != -1) {
376 switch (ch) {
377 case 'p':
378 case 'P':
379 *pflag = 1;
380 break;
381 default:
382 error("%s: Invalid flag -%c", cmd, optopt);
383 return -1;
387 return optind;
390 static int
391 parse_ls_flags(char **argv, int argc, int *lflag)
393 extern int opterr, optind, optopt, optreset;
394 int ch;
396 optind = optreset = 1;
397 opterr = 0;
399 *lflag = LS_NAME_SORT;
400 while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) {
401 switch (ch) {
402 case '1':
403 *lflag &= ~VIEW_FLAGS;
404 *lflag |= LS_SHORT_VIEW;
405 break;
406 case 'S':
407 *lflag &= ~SORT_FLAGS;
408 *lflag |= LS_SIZE_SORT;
409 break;
410 case 'a':
411 *lflag |= LS_SHOW_ALL;
412 break;
413 case 'f':
414 *lflag &= ~SORT_FLAGS;
415 break;
416 case 'l':
417 *lflag &= ~VIEW_FLAGS;
418 *lflag |= LS_LONG_VIEW;
419 break;
420 case 'n':
421 *lflag &= ~VIEW_FLAGS;
422 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
423 break;
424 case 'r':
425 *lflag |= LS_REVERSE_SORT;
426 break;
427 case 't':
428 *lflag &= ~SORT_FLAGS;
429 *lflag |= LS_TIME_SORT;
430 break;
431 default:
432 error("ls: Invalid flag -%c", optopt);
433 return -1;
437 return optind;
440 static int
441 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
443 extern int opterr, optind, optopt, optreset;
444 int ch;
446 optind = optreset = 1;
447 opterr = 0;
449 *hflag = *iflag = 0;
450 while ((ch = getopt(argc, argv, "hi")) != -1) {
451 switch (ch) {
452 case 'h':
453 *hflag = 1;
454 break;
455 case 'i':
456 *iflag = 1;
457 break;
458 default:
459 error("%s: Invalid flag -%c", cmd, optopt);
460 return -1;
464 return optind;
467 static int
468 is_dir(char *path)
470 struct stat sb;
472 /* XXX: report errors? */
473 if (stat(path, &sb) == -1)
474 return(0);
476 return(S_ISDIR(sb.st_mode));
479 static int
480 remote_is_dir(struct sftp_conn *conn, char *path)
482 Attrib *a;
484 /* XXX: report errors? */
485 if ((a = do_stat(conn, path, 1)) == NULL)
486 return(0);
487 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
488 return(0);
489 return(S_ISDIR(a->perm));
492 static int
493 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
495 char *abs_src = NULL;
496 char *abs_dst = NULL;
497 char *tmp;
498 glob_t g;
499 int err = 0;
500 int i;
502 abs_src = xstrdup(src);
503 abs_src = make_absolute(abs_src, pwd);
505 memset(&g, 0, sizeof(g));
506 debug3("Looking up %s", abs_src);
507 if (remote_glob(conn, abs_src, 0, NULL, &g)) {
508 error("File \"%s\" not found.", abs_src);
509 err = -1;
510 goto out;
513 /* If multiple matches, dst must be a directory or unspecified */
514 if (g.gl_matchc > 1 && dst && !is_dir(dst)) {
515 error("Multiple files match, but \"%s\" is not a directory",
516 dst);
517 err = -1;
518 goto out;
521 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
522 if (infer_path(g.gl_pathv[i], &tmp)) {
523 err = -1;
524 goto out;
527 if (g.gl_matchc == 1 && dst) {
528 /* If directory specified, append filename */
529 xfree(tmp);
530 if (is_dir(dst)) {
531 if (infer_path(g.gl_pathv[0], &tmp)) {
532 err = 1;
533 goto out;
535 abs_dst = path_append(dst, tmp);
536 xfree(tmp);
537 } else
538 abs_dst = xstrdup(dst);
539 } else if (dst) {
540 abs_dst = path_append(dst, tmp);
541 xfree(tmp);
542 } else
543 abs_dst = tmp;
545 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
546 if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
547 err = -1;
548 xfree(abs_dst);
549 abs_dst = NULL;
552 out:
553 xfree(abs_src);
554 globfree(&g);
555 return(err);
558 static int
559 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
561 char *tmp_dst = NULL;
562 char *abs_dst = NULL;
563 char *tmp;
564 glob_t g;
565 int err = 0;
566 int i;
567 struct stat sb;
569 if (dst) {
570 tmp_dst = xstrdup(dst);
571 tmp_dst = make_absolute(tmp_dst, pwd);
574 memset(&g, 0, sizeof(g));
575 debug3("Looking up %s", src);
576 if (glob(src, GLOB_NOCHECK, NULL, &g)) {
577 error("File \"%s\" not found.", src);
578 err = -1;
579 goto out;
582 /* If multiple matches, dst may be directory or unspecified */
583 if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) {
584 error("Multiple files match, but \"%s\" is not a directory",
585 tmp_dst);
586 err = -1;
587 goto out;
590 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
591 if (stat(g.gl_pathv[i], &sb) == -1) {
592 err = -1;
593 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
594 continue;
597 if (!S_ISREG(sb.st_mode)) {
598 error("skipping non-regular file %s",
599 g.gl_pathv[i]);
600 continue;
602 if (infer_path(g.gl_pathv[i], &tmp)) {
603 err = -1;
604 goto out;
607 if (g.gl_matchc == 1 && tmp_dst) {
608 /* If directory specified, append filename */
609 if (remote_is_dir(conn, tmp_dst)) {
610 if (infer_path(g.gl_pathv[0], &tmp)) {
611 err = 1;
612 goto out;
614 abs_dst = path_append(tmp_dst, tmp);
615 xfree(tmp);
616 } else
617 abs_dst = xstrdup(tmp_dst);
619 } else if (tmp_dst) {
620 abs_dst = path_append(tmp_dst, tmp);
621 xfree(tmp);
622 } else
623 abs_dst = make_absolute(tmp, pwd);
625 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
626 if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
627 err = -1;
630 out:
631 if (abs_dst)
632 xfree(abs_dst);
633 if (tmp_dst)
634 xfree(tmp_dst);
635 globfree(&g);
636 return(err);
639 static int
640 sdirent_comp(const void *aa, const void *bb)
642 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
643 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
644 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
646 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
647 if (sort_flag & LS_NAME_SORT)
648 return (rmul * strcmp(a->filename, b->filename));
649 else if (sort_flag & LS_TIME_SORT)
650 return (rmul * NCMP(a->a.mtime, b->a.mtime));
651 else if (sort_flag & LS_SIZE_SORT)
652 return (rmul * NCMP(a->a.size, b->a.size));
654 fatal("Unknown ls sort type");
657 /* sftp ls.1 replacement for directories */
658 static int
659 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
661 int n;
662 u_int c = 1, colspace = 0, columns = 1;
663 SFTP_DIRENT **d;
665 if ((n = do_readdir(conn, path, &d)) != 0)
666 return (n);
668 if (!(lflag & LS_SHORT_VIEW)) {
669 u_int m = 0, width = 80;
670 struct winsize ws;
671 char *tmp;
673 /* Count entries for sort and find longest filename */
674 for (n = 0; d[n] != NULL; n++) {
675 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
676 m = MAX(m, strlen(d[n]->filename));
679 /* Add any subpath that also needs to be counted */
680 tmp = path_strip(path, strip_path);
681 m += strlen(tmp);
682 xfree(tmp);
684 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
685 width = ws.ws_col;
687 columns = width / (m + 2);
688 columns = MAX(columns, 1);
689 colspace = width / columns;
690 colspace = MIN(colspace, width);
693 if (lflag & SORT_FLAGS) {
694 for (n = 0; d[n] != NULL; n++)
695 ; /* count entries */
696 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
697 qsort(d, n, sizeof(*d), sdirent_comp);
700 for (n = 0; d[n] != NULL && !interrupted; n++) {
701 char *tmp, *fname;
703 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
704 continue;
706 tmp = path_append(path, d[n]->filename);
707 fname = path_strip(tmp, strip_path);
708 xfree(tmp);
710 if (lflag & LS_LONG_VIEW) {
711 if (lflag & LS_NUMERIC_VIEW) {
712 char *lname;
713 struct stat sb;
715 memset(&sb, 0, sizeof(sb));
716 attrib_to_stat(&d[n]->a, &sb);
717 lname = ls_file(fname, &sb, 1);
718 printf("%s\n", lname);
719 xfree(lname);
720 } else
721 printf("%s\n", d[n]->longname);
722 } else {
723 printf("%-*s", colspace, fname);
724 if (c >= columns) {
725 printf("\n");
726 c = 1;
727 } else
728 c++;
731 xfree(fname);
734 if (!(lflag & LS_LONG_VIEW) && (c != 1))
735 printf("\n");
737 free_sftp_dirents(d);
738 return (0);
741 /* sftp ls.1 replacement which handles path globs */
742 static int
743 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
744 int lflag)
746 glob_t g;
747 u_int i, c = 1, colspace = 0, columns = 1;
748 Attrib *a = NULL;
750 memset(&g, 0, sizeof(g));
752 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
753 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
754 if (g.gl_pathc)
755 globfree(&g);
756 error("Can't ls: \"%s\" not found", path);
757 return (-1);
760 if (interrupted)
761 goto out;
764 * If the glob returns a single match and it is a directory,
765 * then just list its contents.
767 if (g.gl_matchc == 1) {
768 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
769 globfree(&g);
770 return (-1);
772 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
773 S_ISDIR(a->perm)) {
774 int err;
776 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
777 globfree(&g);
778 return (err);
782 if (!(lflag & LS_SHORT_VIEW)) {
783 u_int m = 0, width = 80;
784 struct winsize ws;
786 /* Count entries for sort and find longest filename */
787 for (i = 0; g.gl_pathv[i]; i++)
788 m = MAX(m, strlen(g.gl_pathv[i]));
790 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
791 width = ws.ws_col;
793 columns = width / (m + 2);
794 columns = MAX(columns, 1);
795 colspace = width / columns;
798 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
799 char *fname;
801 fname = path_strip(g.gl_pathv[i], strip_path);
803 if (lflag & LS_LONG_VIEW) {
804 char *lname;
805 struct stat sb;
808 * XXX: this is slow - 1 roundtrip per path
809 * A solution to this is to fork glob() and
810 * build a sftp specific version which keeps the
811 * attribs (which currently get thrown away)
812 * that the server returns as well as the filenames.
814 memset(&sb, 0, sizeof(sb));
815 if (a == NULL)
816 a = do_lstat(conn, g.gl_pathv[i], 1);
817 if (a != NULL)
818 attrib_to_stat(a, &sb);
819 lname = ls_file(fname, &sb, 1);
820 printf("%s\n", lname);
821 xfree(lname);
822 } else {
823 printf("%-*s", colspace, fname);
824 if (c >= columns) {
825 printf("\n");
826 c = 1;
827 } else
828 c++;
830 xfree(fname);
833 if (!(lflag & LS_LONG_VIEW) && (c != 1))
834 printf("\n");
836 out:
837 if (g.gl_pathc)
838 globfree(&g);
840 return (0);
843 static int
844 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
846 struct sftp_statvfs st;
847 char s_used[FMT_SCALED_STRSIZE];
848 char s_avail[FMT_SCALED_STRSIZE];
849 char s_root[FMT_SCALED_STRSIZE];
850 char s_total[FMT_SCALED_STRSIZE];
852 if (do_statvfs(conn, path, &st, 1) == -1)
853 return -1;
854 if (iflag) {
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,
862 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
863 st.f_files));
864 } else if (hflag) {
865 strlcpy(s_used, "error", sizeof(s_used));
866 strlcpy(s_avail, "error", sizeof(s_avail));
867 strlcpy(s_root, "error", sizeof(s_root));
868 strlcpy(s_total, "error", sizeof(s_total));
869 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
870 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
871 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
872 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
873 printf(" Size Used Avail (root) %%Capacity\n");
874 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
875 s_total, s_used, s_avail, s_root,
876 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
877 st.f_blocks));
878 } else {
879 printf(" Size Used Avail "
880 "(root) %%Capacity\n");
881 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
882 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
883 (unsigned long long)(st.f_frsize *
884 (st.f_blocks - st.f_bfree) / 1024),
885 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
886 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
887 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
888 st.f_blocks));
890 return 0;
894 * Undo escaping of glob sequences in place. Used to undo extra escaping
895 * applied in makeargv() when the string is destined for a function that
896 * does not glob it.
898 static void
899 undo_glob_escape(char *s)
901 size_t i, j;
903 for (i = j = 0;;) {
904 if (s[i] == '\0') {
905 s[j] = '\0';
906 return;
908 if (s[i] != '\\') {
909 s[j++] = s[i++];
910 continue;
912 /* s[i] == '\\' */
913 ++i;
914 switch (s[i]) {
915 case '?':
916 case '[':
917 case '*':
918 case '\\':
919 s[j++] = s[i++];
920 break;
921 case '\0':
922 s[j++] = '\\';
923 s[j] = '\0';
924 return;
925 default:
926 s[j++] = '\\';
927 s[j++] = s[i++];
928 break;
934 * Split a string into an argument vector using sh(1)-style quoting,
935 * comment and escaping rules, but with some tweaks to handle glob(3)
936 * wildcards.
937 * Returns NULL on error or a NULL-terminated array of arguments.
939 #define MAXARGS 128
940 #define MAXARGLEN 8192
941 static char **
942 makeargv(const char *arg, int *argcp)
944 int argc, quot;
945 size_t i, j;
946 static char argvs[MAXARGLEN];
947 static char *argv[MAXARGS + 1];
948 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
950 *argcp = argc = 0;
951 if (strlen(arg) > sizeof(argvs) - 1) {
952 args_too_longs:
953 error("string too long");
954 return NULL;
956 state = MA_START;
957 i = j = 0;
958 for (;;) {
959 if (isspace(arg[i])) {
960 if (state == MA_UNQUOTED) {
961 /* Terminate current argument */
962 argvs[j++] = '\0';
963 argc++;
964 state = MA_START;
965 } else if (state != MA_START)
966 argvs[j++] = arg[i];
967 } else if (arg[i] == '"' || arg[i] == '\'') {
968 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
969 if (state == MA_START) {
970 argv[argc] = argvs + j;
971 state = q;
972 } else if (state == MA_UNQUOTED)
973 state = q;
974 else if (state == q)
975 state = MA_UNQUOTED;
976 else
977 argvs[j++] = arg[i];
978 } else if (arg[i] == '\\') {
979 if (state == MA_SQUOTE || state == MA_DQUOTE) {
980 quot = state == MA_SQUOTE ? '\'' : '"';
981 /* Unescape quote we are in */
982 /* XXX support \n and friends? */
983 if (arg[i + 1] == quot) {
984 i++;
985 argvs[j++] = arg[i];
986 } else if (arg[i + 1] == '?' ||
987 arg[i + 1] == '[' || arg[i + 1] == '*') {
989 * Special case for sftp: append
990 * double-escaped glob sequence -
991 * glob will undo one level of
992 * escaping. NB. string can grow here.
994 if (j >= sizeof(argvs) - 5)
995 goto args_too_longs;
996 argvs[j++] = '\\';
997 argvs[j++] = arg[i++];
998 argvs[j++] = '\\';
999 argvs[j++] = arg[i];
1000 } else {
1001 argvs[j++] = arg[i++];
1002 argvs[j++] = arg[i];
1004 } else {
1005 if (state == MA_START) {
1006 argv[argc] = argvs + j;
1007 state = MA_UNQUOTED;
1009 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1010 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1012 * Special case for sftp: append
1013 * escaped glob sequence -
1014 * glob will undo one level of
1015 * escaping.
1017 argvs[j++] = arg[i++];
1018 argvs[j++] = arg[i];
1019 } else {
1020 /* Unescape everything */
1021 /* XXX support \n and friends? */
1022 i++;
1023 argvs[j++] = arg[i];
1026 } else if (arg[i] == '#') {
1027 if (state == MA_SQUOTE || state == MA_DQUOTE)
1028 argvs[j++] = arg[i];
1029 else
1030 goto string_done;
1031 } else if (arg[i] == '\0') {
1032 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1033 error("Unterminated quoted argument");
1034 return NULL;
1036 string_done:
1037 if (state == MA_UNQUOTED) {
1038 argvs[j++] = '\0';
1039 argc++;
1041 break;
1042 } else {
1043 if (state == MA_START) {
1044 argv[argc] = argvs + j;
1045 state = MA_UNQUOTED;
1047 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1048 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1050 * Special case for sftp: escape quoted
1051 * glob(3) wildcards. NB. string can grow
1052 * here.
1054 if (j >= sizeof(argvs) - 3)
1055 goto args_too_longs;
1056 argvs[j++] = '\\';
1057 argvs[j++] = arg[i];
1058 } else
1059 argvs[j++] = arg[i];
1061 i++;
1063 *argcp = argc;
1064 return argv;
1067 static int
1068 parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag,
1069 unsigned long *n_arg, char **path1, char **path2)
1071 const char *cmd, *cp = *cpp;
1072 char *cp2, **argv;
1073 int base = 0;
1074 long l;
1075 int i, cmdnum, optidx, argc;
1077 /* Skip leading whitespace */
1078 cp = cp + strspn(cp, WHITESPACE);
1080 /* Ignore blank lines and lines which begin with comment '#' char */
1081 if (*cp == '\0' || *cp == '#')
1082 return (0);
1084 /* Check for leading '-' (disable error processing) */
1085 *iflag = 0;
1086 if (*cp == '-') {
1087 *iflag = 1;
1088 cp++;
1091 if ((argv = makeargv(cp, &argc)) == NULL)
1092 return -1;
1094 /* Figure out which command we have */
1095 for (i = 0; cmds[i].c != NULL; i++) {
1096 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1097 break;
1099 cmdnum = cmds[i].n;
1100 cmd = cmds[i].c;
1102 /* Special case */
1103 if (*cp == '!') {
1104 cp++;
1105 cmdnum = I_SHELL;
1106 } else if (cmdnum == -1) {
1107 error("Invalid command.");
1108 return -1;
1111 /* Get arguments and parse flags */
1112 *lflag = *pflag = *hflag = *n_arg = 0;
1113 *path1 = *path2 = NULL;
1114 optidx = 1;
1115 switch (cmdnum) {
1116 case I_GET:
1117 case I_PUT:
1118 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1)
1119 return -1;
1120 /* Get first pathname (mandatory) */
1121 if (argc - optidx < 1) {
1122 error("You must specify at least one path after a "
1123 "%s command.", cmd);
1124 return -1;
1126 *path1 = xstrdup(argv[optidx]);
1127 /* Get second pathname (optional) */
1128 if (argc - optidx > 1) {
1129 *path2 = xstrdup(argv[optidx + 1]);
1130 /* Destination is not globbed */
1131 undo_glob_escape(*path2);
1133 break;
1134 case I_RENAME:
1135 case I_SYMLINK:
1136 if (argc - optidx < 2) {
1137 error("You must specify two paths after a %s "
1138 "command.", cmd);
1139 return -1;
1141 *path1 = xstrdup(argv[optidx]);
1142 *path2 = xstrdup(argv[optidx + 1]);
1143 /* Paths are not globbed */
1144 undo_glob_escape(*path1);
1145 undo_glob_escape(*path2);
1146 break;
1147 case I_RM:
1148 case I_MKDIR:
1149 case I_RMDIR:
1150 case I_CHDIR:
1151 case I_LCHDIR:
1152 case I_LMKDIR:
1153 /* Get pathname (mandatory) */
1154 if (argc - optidx < 1) {
1155 error("You must specify a path after a %s command.",
1156 cmd);
1157 return -1;
1159 *path1 = xstrdup(argv[optidx]);
1160 /* Only "rm" globs */
1161 if (cmdnum != I_RM)
1162 undo_glob_escape(*path1);
1163 break;
1164 case I_DF:
1165 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1166 iflag)) == -1)
1167 return -1;
1168 /* Default to current directory if no path specified */
1169 if (argc - optidx < 1)
1170 *path1 = NULL;
1171 else {
1172 *path1 = xstrdup(argv[optidx]);
1173 undo_glob_escape(*path1);
1175 break;
1176 case I_LS:
1177 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1178 return(-1);
1179 /* Path is optional */
1180 if (argc - optidx > 0)
1181 *path1 = xstrdup(argv[optidx]);
1182 break;
1183 case I_LLS:
1184 /* Skip ls command and following whitespace */
1185 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1186 case I_SHELL:
1187 /* Uses the rest of the line */
1188 break;
1189 case I_LUMASK:
1190 case I_CHMOD:
1191 base = 8;
1192 case I_CHOWN:
1193 case I_CHGRP:
1194 /* Get numeric arg (mandatory) */
1195 if (argc - optidx < 1)
1196 goto need_num_arg;
1197 errno = 0;
1198 l = strtol(argv[optidx], &cp2, base);
1199 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1200 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1201 l < 0) {
1202 need_num_arg:
1203 error("You must supply a numeric argument "
1204 "to the %s command.", cmd);
1205 return -1;
1207 *n_arg = l;
1208 if (cmdnum == I_LUMASK)
1209 break;
1210 /* Get pathname (mandatory) */
1211 if (argc - optidx < 2) {
1212 error("You must specify a path after a %s command.",
1213 cmd);
1214 return -1;
1216 *path1 = xstrdup(argv[optidx + 1]);
1217 break;
1218 case I_QUIT:
1219 case I_PWD:
1220 case I_LPWD:
1221 case I_HELP:
1222 case I_VERSION:
1223 case I_PROGRESS:
1224 break;
1225 default:
1226 fatal("Command not implemented");
1229 *cpp = cp;
1230 return(cmdnum);
1233 static int
1234 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1235 int err_abort)
1237 char *path1, *path2, *tmp;
1238 int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1239 unsigned long n_arg = 0;
1240 Attrib a, *aa;
1241 char path_buf[MAXPATHLEN];
1242 int err = 0;
1243 glob_t g;
1245 path1 = path2 = NULL;
1246 cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg,
1247 &path1, &path2);
1249 if (iflag != 0)
1250 err_abort = 0;
1252 memset(&g, 0, sizeof(g));
1254 /* Perform command */
1255 switch (cmdnum) {
1256 case 0:
1257 /* Blank line */
1258 break;
1259 case -1:
1260 /* Unrecognized command */
1261 err = -1;
1262 break;
1263 case I_GET:
1264 err = process_get(conn, path1, path2, *pwd, pflag);
1265 break;
1266 case I_PUT:
1267 err = process_put(conn, path1, path2, *pwd, pflag);
1268 break;
1269 case I_RENAME:
1270 path1 = make_absolute(path1, *pwd);
1271 path2 = make_absolute(path2, *pwd);
1272 err = do_rename(conn, path1, path2);
1273 break;
1274 case I_SYMLINK:
1275 path2 = make_absolute(path2, *pwd);
1276 err = do_symlink(conn, path1, path2);
1277 break;
1278 case I_RM:
1279 path1 = make_absolute(path1, *pwd);
1280 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1281 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1282 printf("Removing %s\n", g.gl_pathv[i]);
1283 err = do_rm(conn, g.gl_pathv[i]);
1284 if (err != 0 && err_abort)
1285 break;
1287 break;
1288 case I_MKDIR:
1289 path1 = make_absolute(path1, *pwd);
1290 attrib_clear(&a);
1291 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1292 a.perm = 0777;
1293 err = do_mkdir(conn, path1, &a);
1294 break;
1295 case I_RMDIR:
1296 path1 = make_absolute(path1, *pwd);
1297 err = do_rmdir(conn, path1);
1298 break;
1299 case I_CHDIR:
1300 path1 = make_absolute(path1, *pwd);
1301 if ((tmp = do_realpath(conn, path1)) == NULL) {
1302 err = 1;
1303 break;
1305 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1306 xfree(tmp);
1307 err = 1;
1308 break;
1310 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1311 error("Can't change directory: Can't check target");
1312 xfree(tmp);
1313 err = 1;
1314 break;
1316 if (!S_ISDIR(aa->perm)) {
1317 error("Can't change directory: \"%s\" is not "
1318 "a directory", tmp);
1319 xfree(tmp);
1320 err = 1;
1321 break;
1323 xfree(*pwd);
1324 *pwd = tmp;
1325 break;
1326 case I_LS:
1327 if (!path1) {
1328 do_globbed_ls(conn, *pwd, *pwd, lflag);
1329 break;
1332 /* Strip pwd off beginning of non-absolute paths */
1333 tmp = NULL;
1334 if (*path1 != '/')
1335 tmp = *pwd;
1337 path1 = make_absolute(path1, *pwd);
1338 err = do_globbed_ls(conn, path1, tmp, lflag);
1339 break;
1340 case I_DF:
1341 /* Default to current directory if no path specified */
1342 if (path1 == NULL)
1343 path1 = xstrdup(*pwd);
1344 path1 = make_absolute(path1, *pwd);
1345 err = do_df(conn, path1, hflag, iflag);
1346 break;
1347 case I_LCHDIR:
1348 if (chdir(path1) == -1) {
1349 error("Couldn't change local directory to "
1350 "\"%s\": %s", path1, strerror(errno));
1351 err = 1;
1353 break;
1354 case I_LMKDIR:
1355 if (mkdir(path1, 0777) == -1) {
1356 error("Couldn't create local directory "
1357 "\"%s\": %s", path1, strerror(errno));
1358 err = 1;
1360 break;
1361 case I_LLS:
1362 local_do_ls(cmd);
1363 break;
1364 case I_SHELL:
1365 local_do_shell(cmd);
1366 break;
1367 case I_LUMASK:
1368 umask(n_arg);
1369 printf("Local umask: %03lo\n", n_arg);
1370 break;
1371 case I_CHMOD:
1372 path1 = make_absolute(path1, *pwd);
1373 attrib_clear(&a);
1374 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1375 a.perm = n_arg;
1376 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1377 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1378 printf("Changing mode on %s\n", g.gl_pathv[i]);
1379 err = do_setstat(conn, g.gl_pathv[i], &a);
1380 if (err != 0 && err_abort)
1381 break;
1383 break;
1384 case I_CHOWN:
1385 case I_CHGRP:
1386 path1 = make_absolute(path1, *pwd);
1387 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1388 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1389 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1390 if (err_abort) {
1391 err = -1;
1392 break;
1393 } else
1394 continue;
1396 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1397 error("Can't get current ownership of "
1398 "remote file \"%s\"", g.gl_pathv[i]);
1399 if (err_abort) {
1400 err = -1;
1401 break;
1402 } else
1403 continue;
1405 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1406 if (cmdnum == I_CHOWN) {
1407 printf("Changing owner on %s\n", g.gl_pathv[i]);
1408 aa->uid = n_arg;
1409 } else {
1410 printf("Changing group on %s\n", g.gl_pathv[i]);
1411 aa->gid = n_arg;
1413 err = do_setstat(conn, g.gl_pathv[i], aa);
1414 if (err != 0 && err_abort)
1415 break;
1417 break;
1418 case I_PWD:
1419 printf("Remote working directory: %s\n", *pwd);
1420 break;
1421 case I_LPWD:
1422 if (!getcwd(path_buf, sizeof(path_buf))) {
1423 error("Couldn't get local cwd: %s", strerror(errno));
1424 err = -1;
1425 break;
1427 printf("Local working directory: %s\n", path_buf);
1428 break;
1429 case I_QUIT:
1430 /* Processed below */
1431 break;
1432 case I_HELP:
1433 help();
1434 break;
1435 case I_VERSION:
1436 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1437 break;
1438 case I_PROGRESS:
1439 showprogress = !showprogress;
1440 if (showprogress)
1441 printf("Progress meter enabled\n");
1442 else
1443 printf("Progress meter disabled\n");
1444 break;
1445 default:
1446 fatal("%d is not implemented", cmdnum);
1449 if (g.gl_pathc)
1450 globfree(&g);
1451 if (path1)
1452 xfree(path1);
1453 if (path2)
1454 xfree(path2);
1456 /* If an unignored error occurs in batch mode we should abort. */
1457 if (err_abort && err != 0)
1458 return (-1);
1459 else if (cmdnum == I_QUIT)
1460 return (1);
1462 return (0);
1465 #ifdef USE_LIBEDIT
1466 static char *
1467 prompt(EditLine *el)
1469 return ("sftp> ");
1471 #endif
1474 interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1476 char *pwd;
1477 char *dir = NULL;
1478 char cmd[2048];
1479 struct sftp_conn *conn;
1480 int err, interactive;
1481 EditLine *el = NULL;
1482 #ifdef USE_LIBEDIT
1483 History *hl = NULL;
1484 HistEvent hev;
1485 extern char *__progname;
1487 if (!batchmode && isatty(STDIN_FILENO)) {
1488 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1489 fatal("Couldn't initialise editline");
1490 if ((hl = history_init()) == NULL)
1491 fatal("Couldn't initialise editline history");
1492 history(hl, &hev, H_SETSIZE, 100);
1493 el_set(el, EL_HIST, history, hl);
1495 el_set(el, EL_PROMPT, prompt);
1496 el_set(el, EL_EDITOR, "emacs");
1497 el_set(el, EL_TERMINAL, NULL);
1498 el_set(el, EL_SIGNAL, 1);
1499 el_source(el, NULL);
1501 #endif /* USE_LIBEDIT */
1503 conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests);
1504 if (conn == NULL)
1505 fatal("Couldn't initialise connection to server");
1507 pwd = do_realpath(conn, ".");
1508 if (pwd == NULL)
1509 fatal("Need cwd");
1511 if (file1 != NULL) {
1512 dir = xstrdup(file1);
1513 dir = make_absolute(dir, pwd);
1515 if (remote_is_dir(conn, dir) && file2 == NULL) {
1516 printf("Changing to: %s\n", dir);
1517 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1518 if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) {
1519 xfree(dir);
1520 xfree(pwd);
1521 xfree(conn);
1522 return (-1);
1524 } else {
1525 if (file2 == NULL)
1526 snprintf(cmd, sizeof cmd, "get %s", dir);
1527 else
1528 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1529 file2);
1531 err = parse_dispatch_command(conn, cmd, &pwd, 1);
1532 xfree(dir);
1533 xfree(pwd);
1534 xfree(conn);
1535 return (err);
1537 xfree(dir);
1540 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1541 setvbuf(stdout, NULL, _IOLBF, 0);
1542 setvbuf(infile, NULL, _IOLBF, 0);
1543 #else
1544 setlinebuf(stdout);
1545 setlinebuf(infile);
1546 #endif
1548 interactive = !batchmode && isatty(STDIN_FILENO);
1549 err = 0;
1550 for (;;) {
1551 char *cp;
1553 signal(SIGINT, SIG_IGN);
1555 if (el == NULL) {
1556 if (interactive)
1557 printf("sftp> ");
1558 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1559 if (interactive)
1560 printf("\n");
1561 break;
1563 if (!interactive) { /* Echo command */
1564 printf("sftp> %s", cmd);
1565 if (strlen(cmd) > 0 &&
1566 cmd[strlen(cmd) - 1] != '\n')
1567 printf("\n");
1569 } else {
1570 #ifdef USE_LIBEDIT
1571 const char *line;
1572 int count = 0;
1574 if ((line = el_gets(el, &count)) == NULL || count <= 0) {
1575 printf("\n");
1576 break;
1578 history(hl, &hev, H_ENTER, line);
1579 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1580 fprintf(stderr, "Error: input line too long\n");
1581 continue;
1583 #endif /* USE_LIBEDIT */
1586 cp = strrchr(cmd, '\n');
1587 if (cp)
1588 *cp = '\0';
1590 /* Handle user interrupts gracefully during commands */
1591 interrupted = 0;
1592 signal(SIGINT, cmd_interrupt);
1594 err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
1595 if (err != 0)
1596 break;
1598 xfree(pwd);
1599 xfree(conn);
1601 #ifdef USE_LIBEDIT
1602 if (el != NULL)
1603 el_end(el);
1604 #endif /* USE_LIBEDIT */
1606 /* err == 1 signifies normal "quit" exit */
1607 return (err >= 0 ? 0 : -1);
1610 static void
1611 connect_to_server(char *path, char **args, int *in, int *out)
1613 int c_in, c_out;
1615 #ifdef USE_PIPES
1616 int pin[2], pout[2];
1618 if ((pipe(pin) == -1) || (pipe(pout) == -1))
1619 fatal("pipe: %s", strerror(errno));
1620 *in = pin[0];
1621 *out = pout[1];
1622 c_in = pout[0];
1623 c_out = pin[1];
1624 #else /* USE_PIPES */
1625 int inout[2];
1627 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
1628 fatal("socketpair: %s", strerror(errno));
1629 *in = *out = inout[0];
1630 c_in = c_out = inout[1];
1631 #endif /* USE_PIPES */
1633 if ((sshpid = fork()) == -1)
1634 fatal("fork: %s", strerror(errno));
1635 else if (sshpid == 0) {
1636 if ((dup2(c_in, STDIN_FILENO) == -1) ||
1637 (dup2(c_out, STDOUT_FILENO) == -1)) {
1638 fprintf(stderr, "dup2: %s\n", strerror(errno));
1639 _exit(1);
1641 close(*in);
1642 close(*out);
1643 close(c_in);
1644 close(c_out);
1647 * The underlying ssh is in the same process group, so we must
1648 * ignore SIGINT if we want to gracefully abort commands,
1649 * otherwise the signal will make it to the ssh process and
1650 * kill it too
1652 signal(SIGINT, SIG_IGN);
1653 execvp(path, args);
1654 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
1655 _exit(1);
1658 signal(SIGTERM, killchild);
1659 signal(SIGINT, killchild);
1660 signal(SIGHUP, killchild);
1661 close(c_in);
1662 close(c_out);
1665 static void
1666 usage(void)
1668 extern char *__progname;
1670 fprintf(stderr,
1671 "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n"
1672 " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n"
1673 " [-S program] [-s subsystem | sftp_server] host\n"
1674 " %s [user@]host[:file ...]\n"
1675 " %s [user@]host[:dir[/]]\n"
1676 " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname);
1677 exit(1);
1681 main(int argc, char **argv)
1683 int in, out, ch, err;
1684 char *host, *userhost, *cp, *file2 = NULL;
1685 int debug_level = 0, sshver = 2;
1686 char *file1 = NULL, *sftp_server = NULL;
1687 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
1688 LogLevel ll = SYSLOG_LEVEL_INFO;
1689 arglist args;
1690 extern int optind;
1691 extern char *optarg;
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, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
1709 switch (ch) {
1710 case 'C':
1711 addargs(&args, "-C");
1712 break;
1713 case 'v':
1714 if (debug_level < 3) {
1715 addargs(&args, "-v");
1716 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
1718 debug_level++;
1719 break;
1720 case 'F':
1721 case 'o':
1722 addargs(&args, "-%c%s", ch, optarg);
1723 break;
1724 case '1':
1725 sshver = 1;
1726 if (sftp_server == NULL)
1727 sftp_server = _PATH_SFTP_SERVER;
1728 break;
1729 case 's':
1730 sftp_server = optarg;
1731 break;
1732 case 'S':
1733 ssh_program = optarg;
1734 replacearg(&args, 0, "%s", ssh_program);
1735 break;
1736 case 'b':
1737 if (batchmode)
1738 fatal("Batch file already specified.");
1740 /* Allow "-" as stdin */
1741 if (strcmp(optarg, "-") != 0 &&
1742 (infile = fopen(optarg, "r")) == NULL)
1743 fatal("%s (%s).", strerror(errno), optarg);
1744 showprogress = 0;
1745 batchmode = 1;
1746 addargs(&args, "-obatchmode yes");
1747 break;
1748 case 'P':
1749 sftp_direct = optarg;
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 'R':
1757 num_requests = strtol(optarg, &cp, 10);
1758 if (num_requests == 0 || *cp != '\0')
1759 fatal("Invalid number of requests \"%s\"",
1760 optarg);
1761 break;
1762 case 'h':
1763 default:
1764 usage();
1768 if (!isatty(STDERR_FILENO))
1769 showprogress = 0;
1771 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
1773 if (sftp_direct == NULL) {
1774 if (optind == argc || argc > (optind + 2))
1775 usage();
1777 userhost = xstrdup(argv[optind]);
1778 file2 = argv[optind+1];
1780 if ((host = strrchr(userhost, '@')) == NULL)
1781 host = userhost;
1782 else {
1783 *host++ = '\0';
1784 if (!userhost[0]) {
1785 fprintf(stderr, "Missing username\n");
1786 usage();
1788 addargs(&args, "-l%s", userhost);
1791 if ((cp = colon(host)) != NULL) {
1792 *cp++ = '\0';
1793 file1 = cp;
1796 host = cleanhostname(host);
1797 if (!*host) {
1798 fprintf(stderr, "Missing hostname\n");
1799 usage();
1802 addargs(&args, "-oProtocol %d", sshver);
1804 /* no subsystem if the server-spec contains a '/' */
1805 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
1806 addargs(&args, "-s");
1808 addargs(&args, "%s", host);
1809 addargs(&args, "%s", (sftp_server != NULL ?
1810 sftp_server : "sftp"));
1812 if (!batchmode)
1813 fprintf(stderr, "Connecting to %s...\n", host);
1814 connect_to_server(ssh_program, args.list, &in, &out);
1815 } else {
1816 args.list = NULL;
1817 addargs(&args, "sftp-server");
1819 if (!batchmode)
1820 fprintf(stderr, "Attaching to %s...\n", sftp_direct);
1821 connect_to_server(sftp_direct, args.list, &in, &out);
1823 freeargs(&args);
1825 err = interactive_loop(in, out, file1, file2);
1827 #if !defined(USE_PIPES)
1828 shutdown(in, SHUT_RDWR);
1829 shutdown(out, SHUT_RDWR);
1830 #endif
1832 close(in);
1833 close(out);
1834 if (batchmode)
1835 fclose(infile);
1837 while (waitpid(sshpid, NULL, 0) == -1)
1838 if (errno != EINTR)
1839 fatal("Couldn't wait for ssh process: %s",
1840 strerror(errno));
1842 exit(err == 0 ? 0 : 1);