No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / external / bsd / openssh / dist / sftp.c
blob52292aa4e860cc3d76f3147d9170fab10ff68500
1 /* $NetBSD$ */
2 /* $OpenBSD: sftp.c,v 1.107 2009/02/02 11:15:14 dtucker Exp $ */
3 /*
4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include "includes.h"
20 __RCSID("$NetBSD: sftp.c,v 1.24 2009/02/16 20:53:55 christos Exp $");
21 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #include <sys/wait.h>
24 #include <sys/stat.h>
25 #include <sys/socket.h>
26 #include <sys/param.h>
27 #include <sys/statvfs.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <glob.h>
32 #include <histedit.h>
33 #include <paths.h>
34 #include <signal.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <util.h>
40 #include <stdarg.h>
42 #include "xmalloc.h"
43 #include "log.h"
44 #include "pathnames.h"
45 #include "misc.h"
47 #include "sftp.h"
48 #include "buffer.h"
49 #include "sftp-common.h"
50 #include "sftp-client.h"
51 #include "fmt_scaled.h"
53 /* File to read commands from */
54 FILE* infile;
56 /* Are we in batchfile mode? */
57 int batchmode = 0;
59 /* Size of buffer used when copying files */
60 size_t copy_buffer_len = 32768;
62 /* Number of concurrent outstanding requests */
63 size_t num_requests = 256;
65 /* PID of ssh transport process */
66 static pid_t sshpid = -1;
68 /* This is set to 0 if the progressmeter is not desired. */
69 int showprogress = 1;
71 /* SIGINT received during command processing */
72 volatile sig_atomic_t interrupted = 0;
74 /* I wish qsort() took a separate ctx for the comparison function...*/
75 int sort_flag;
77 int remote_glob(struct sftp_conn *, const char *, int,
78 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
80 /* Separators for interactive commands */
81 #define WHITESPACE " \t\r\n"
83 /* ls flags */
84 #define LS_LONG_VIEW 0x01 /* Full view ala ls -l */
85 #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */
86 #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */
87 #define LS_NAME_SORT 0x08 /* Sort by name (default) */
88 #define LS_TIME_SORT 0x10 /* Sort by mtime */
89 #define LS_SIZE_SORT 0x20 /* Sort by file size */
90 #define LS_REVERSE_SORT 0x40 /* Reverse sort order */
91 #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */
93 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
94 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
96 /* Commands for interactive mode */
97 #define I_CHDIR 1
98 #define I_CHGRP 2
99 #define I_CHMOD 3
100 #define I_CHOWN 4
101 #define I_DF 24
102 #define I_GET 5
103 #define I_HELP 6
104 #define I_LCHDIR 7
105 #define I_LLS 8
106 #define I_LMKDIR 9
107 #define I_LPWD 10
108 #define I_LS 11
109 #define I_LUMASK 12
110 #define I_MKDIR 13
111 #define I_PUT 14
112 #define I_PWD 15
113 #define I_QUIT 16
114 #define I_RENAME 17
115 #define I_RM 18
116 #define I_RMDIR 19
117 #define I_SHELL 20
118 #define I_SYMLINK 21
119 #define I_VERSION 22
120 #define I_PROGRESS 23
122 struct CMD {
123 const char *c;
124 const int n;
127 static const struct CMD cmds[] = {
128 { "bye", I_QUIT },
129 { "cd", I_CHDIR },
130 { "chdir", I_CHDIR },
131 { "chgrp", I_CHGRP },
132 { "chmod", I_CHMOD },
133 { "chown", I_CHOWN },
134 { "df", I_DF },
135 { "dir", I_LS },
136 { "exit", I_QUIT },
137 { "get", I_GET },
138 { "mget", I_GET },
139 { "help", I_HELP },
140 { "lcd", I_LCHDIR },
141 { "lchdir", I_LCHDIR },
142 { "lls", I_LLS },
143 { "lmkdir", I_LMKDIR },
144 { "ln", I_SYMLINK },
145 { "lpwd", I_LPWD },
146 { "ls", I_LS },
147 { "lumask", I_LUMASK },
148 { "mkdir", I_MKDIR },
149 { "progress", I_PROGRESS },
150 { "put", I_PUT },
151 { "mput", I_PUT },
152 { "pwd", I_PWD },
153 { "quit", I_QUIT },
154 { "rename", I_RENAME },
155 { "rm", I_RM },
156 { "rmdir", I_RMDIR },
157 { "symlink", I_SYMLINK },
158 { "version", I_VERSION },
159 { "!", I_SHELL },
160 { "?", I_HELP },
161 { NULL, -1}
164 int interactive_loop(int fd_in, int fd_out, char *file1, char *file2);
166 /* ARGSUSED */
167 static void
168 killchild(int signo)
170 if (sshpid > 1) {
171 kill(sshpid, SIGTERM);
172 waitpid(sshpid, NULL, 0);
175 _exit(1);
178 /* ARGSUSED */
179 static void
180 cmd_interrupt(int signo)
182 const char msg[] = "\rInterrupt \n";
183 int olderrno = errno;
185 write(STDERR_FILENO, msg, sizeof(msg) - 1);
186 interrupted = 1;
187 errno = olderrno;
190 static void
191 help(void)
193 printf("Available commands:\n"
194 "bye Quit sftp\n"
195 "cd path Change remote directory to 'path'\n"
196 "chgrp grp path Change group of file 'path' to 'grp'\n"
197 "chmod mode path Change permissions of file 'path' to 'mode'\n"
198 "chown own path Change owner of file 'path' to 'own'\n"
199 "df [-hi] [path] Display statistics for current directory or\n"
200 " filesystem containing 'path'\n"
201 "exit Quit sftp\n"
202 "get [-P] remote-path [local-path] Download file\n"
203 "help Display this help text\n"
204 "lcd path Change local directory to 'path'\n"
205 "lls [ls-options [path]] Display local directory listing\n"
206 "lmkdir path Create local directory\n"
207 "ln oldpath newpath Symlink remote file\n"
208 "lpwd Print local working directory\n"
209 "ls [-1aflnrSt] [path] Display remote directory listing\n"
210 "lumask umask Set local umask to 'umask'\n"
211 "mkdir path Create remote directory\n"
212 "progress Toggle display of progress meter\n"
213 "put [-P] local-path [remote-path] Upload file\n"
214 "pwd Display remote working directory\n"
215 "quit Quit sftp\n"
216 "rename oldpath newpath Rename remote file\n"
217 "rm path Delete remote file\n"
218 "rmdir path Remove remote directory\n"
219 "symlink oldpath newpath Symlink remote file\n"
220 "version Show SFTP version\n"
221 "!command Execute 'command' in local shell\n"
222 "! Escape to local shell\n"
223 "? Synonym for help\n");
226 static void
227 local_do_shell(const char *args)
229 int status;
230 char *shell;
231 pid_t pid;
233 if (!*args)
234 args = NULL;
236 if ((shell = getenv("SHELL")) == NULL)
237 shell = _PATH_BSHELL;
239 if ((pid = fork()) == -1)
240 fatal("Couldn't fork: %s", strerror(errno));
242 if (pid == 0) {
243 /* XXX: child has pipe fds to ssh subproc open - issue? */
244 if (args) {
245 debug3("Executing %s -c \"%s\"", shell, args);
246 execl(shell, shell, "-c", args, (char *)NULL);
247 } else {
248 debug3("Executing %s", shell);
249 execl(shell, shell, (char *)NULL);
251 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
252 strerror(errno));
253 _exit(1);
255 while (waitpid(pid, &status, 0) == -1)
256 if (errno != EINTR)
257 fatal("Couldn't wait for child: %s", strerror(errno));
258 if (!WIFEXITED(status))
259 error("Shell exited abnormally");
260 else if (WEXITSTATUS(status))
261 error("Shell exited with status %d", WEXITSTATUS(status));
264 static void
265 local_do_ls(const char *args)
267 if (!args || !*args)
268 local_do_shell(_PATH_LS);
269 else {
270 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
271 char *buf = xmalloc(len);
273 /* XXX: quoting - rip quoting code from ftp? */
274 snprintf(buf, len, _PATH_LS " %s", args);
275 local_do_shell(buf);
276 xfree(buf);
280 /* Strip one path (usually the pwd) from the start of another */
281 static char *
282 path_strip(char *path, char *strip)
284 size_t len;
286 if (strip == NULL)
287 return (xstrdup(path));
289 len = strlen(strip);
290 if (strncmp(path, strip, len) == 0) {
291 if (strip[len - 1] != '/' && path[len] == '/')
292 len++;
293 return (xstrdup(path + len));
296 return (xstrdup(path));
299 static char *
300 path_append(char *p1, char *p2)
302 char *ret;
303 size_t len = strlen(p1) + strlen(p2) + 2;
305 ret = xmalloc(len);
306 strlcpy(ret, p1, len);
307 if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
308 strlcat(ret, "/", len);
309 strlcat(ret, p2, len);
311 return(ret);
314 static char *
315 make_absolute(char *p, char *pwd)
317 char *abs_str;
319 /* Derelativise */
320 if (p && p[0] != '/') {
321 abs_str = path_append(pwd, p);
322 xfree(p);
323 return(abs_str);
324 } else
325 return(p);
328 static int
329 infer_path(const char *p, char **ifp)
331 char *cp;
333 cp = strrchr(p, '/');
334 if (cp == NULL) {
335 *ifp = xstrdup(p);
336 return(0);
339 if (!cp[1]) {
340 error("Invalid path");
341 return(-1);
344 *ifp = xstrdup(cp + 1);
345 return(0);
348 static int
349 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
351 extern int opterr, optind, optopt, optreset;
352 int ch;
354 optind = optreset = 1;
355 opterr = 0;
357 *pflag = 0;
358 while ((ch = getopt(argc, argv, "Pp")) != -1) {
359 switch (ch) {
360 case 'p':
361 case 'P':
362 *pflag = 1;
363 break;
364 default:
365 error("%s: Invalid flag -%c", cmd, optopt);
366 return -1;
370 return optind;
373 static int
374 parse_ls_flags(char **argv, int argc, int *lflag)
376 extern int opterr, optind, optopt, optreset;
377 int ch;
379 optind = optreset = 1;
380 opterr = 0;
382 *lflag = LS_NAME_SORT;
383 while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) {
384 switch (ch) {
385 case '1':
386 *lflag &= ~VIEW_FLAGS;
387 *lflag |= LS_SHORT_VIEW;
388 break;
389 case 'S':
390 *lflag &= ~SORT_FLAGS;
391 *lflag |= LS_SIZE_SORT;
392 break;
393 case 'a':
394 *lflag |= LS_SHOW_ALL;
395 break;
396 case 'f':
397 *lflag &= ~SORT_FLAGS;
398 break;
399 case 'l':
400 *lflag &= ~VIEW_FLAGS;
401 *lflag |= LS_LONG_VIEW;
402 break;
403 case 'n':
404 *lflag &= ~VIEW_FLAGS;
405 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
406 break;
407 case 'r':
408 *lflag |= LS_REVERSE_SORT;
409 break;
410 case 't':
411 *lflag &= ~SORT_FLAGS;
412 *lflag |= LS_TIME_SORT;
413 break;
414 default:
415 error("ls: Invalid flag -%c", optopt);
416 return -1;
420 return optind;
423 static int
424 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
426 extern int opterr, optind, optopt, optreset;
427 int ch;
429 optind = optreset = 1;
430 opterr = 0;
432 *hflag = *iflag = 0;
433 while ((ch = getopt(argc, argv, "hi")) != -1) {
434 switch (ch) {
435 case 'h':
436 *hflag = 1;
437 break;
438 case 'i':
439 *iflag = 1;
440 break;
441 default:
442 error("%s: Invalid flag -%c", cmd, optopt);
443 return -1;
447 return optind;
450 static int
451 is_dir(char *path)
453 struct stat sb;
455 /* XXX: report errors? */
456 if (stat(path, &sb) == -1)
457 return(0);
459 return(S_ISDIR(sb.st_mode));
462 static int
463 remote_is_dir(struct sftp_conn *conn, char *path)
465 Attrib *a;
467 /* XXX: report errors? */
468 if ((a = do_stat(conn, path, 1)) == NULL)
469 return(0);
470 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
471 return(0);
472 return(S_ISDIR(a->perm));
475 static int
476 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
478 char *abs_src = NULL;
479 char *abs_dst = NULL;
480 char *tmp;
481 glob_t g;
482 int err = 0;
483 int i;
485 abs_src = xstrdup(src);
486 abs_src = make_absolute(abs_src, pwd);
488 memset(&g, 0, sizeof(g));
489 debug3("Looking up %s", abs_src);
490 if (remote_glob(conn, abs_src, 0, NULL, &g)) {
491 error("File \"%s\" not found.", abs_src);
492 err = -1;
493 goto out;
496 /* If multiple matches, dst must be a directory or unspecified */
497 if (g.gl_matchc > 1 && dst && !is_dir(dst)) {
498 error("Multiple files match, but \"%s\" is not a directory",
499 dst);
500 err = -1;
501 goto out;
504 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
505 if (infer_path(g.gl_pathv[i], &tmp)) {
506 err = -1;
507 goto out;
510 if (g.gl_matchc == 1 && dst) {
511 /* If directory specified, append filename */
512 xfree(tmp);
513 if (is_dir(dst)) {
514 if (infer_path(g.gl_pathv[0], &tmp)) {
515 err = 1;
516 goto out;
518 abs_dst = path_append(dst, tmp);
519 xfree(tmp);
520 } else
521 abs_dst = xstrdup(dst);
522 } else if (dst) {
523 abs_dst = path_append(dst, tmp);
524 xfree(tmp);
525 } else
526 abs_dst = tmp;
528 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
529 if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
530 err = -1;
531 xfree(abs_dst);
532 abs_dst = NULL;
535 out:
536 xfree(abs_src);
537 globfree(&g);
538 return(err);
541 static int
542 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
544 char *tmp_dst = NULL;
545 char *abs_dst = NULL;
546 char *tmp;
547 glob_t g;
548 int err = 0;
549 int i;
550 struct stat sb;
552 if (dst) {
553 tmp_dst = xstrdup(dst);
554 tmp_dst = make_absolute(tmp_dst, pwd);
557 memset(&g, 0, sizeof(g));
558 debug3("Looking up %s", src);
559 if (glob(src, GLOB_NOCHECK, NULL, &g)) {
560 error("File \"%s\" not found.", src);
561 err = -1;
562 goto out;
565 /* If multiple matches, dst may be directory or unspecified */
566 if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) {
567 error("Multiple files match, but \"%s\" is not a directory",
568 tmp_dst);
569 err = -1;
570 goto out;
573 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
574 if (stat(g.gl_pathv[i], &sb) == -1) {
575 err = -1;
576 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
577 continue;
580 if (!S_ISREG(sb.st_mode)) {
581 error("skipping non-regular file %s",
582 g.gl_pathv[i]);
583 continue;
585 if (infer_path(g.gl_pathv[i], &tmp)) {
586 err = -1;
587 goto out;
590 if (g.gl_matchc == 1 && tmp_dst) {
591 /* If directory specified, append filename */
592 if (remote_is_dir(conn, tmp_dst)) {
593 if (infer_path(g.gl_pathv[0], &tmp)) {
594 err = 1;
595 goto out;
597 abs_dst = path_append(tmp_dst, tmp);
598 xfree(tmp);
599 } else
600 abs_dst = xstrdup(tmp_dst);
602 } else if (tmp_dst) {
603 abs_dst = path_append(tmp_dst, tmp);
604 xfree(tmp);
605 } else
606 abs_dst = make_absolute(tmp, pwd);
608 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
609 if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
610 err = -1;
613 out:
614 if (abs_dst)
615 xfree(abs_dst);
616 if (tmp_dst)
617 xfree(tmp_dst);
618 globfree(&g);
619 return(err);
622 static int
623 sdirent_comp(const void *aa, const void *bb)
625 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
626 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
627 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
629 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
630 if (sort_flag & LS_NAME_SORT)
631 return (rmul * strcmp(a->filename, b->filename));
632 else if (sort_flag & LS_TIME_SORT)
633 return (rmul * NCMP(a->a.mtime, b->a.mtime));
634 else if (sort_flag & LS_SIZE_SORT)
635 return (rmul * NCMP(a->a.size, b->a.size));
637 fatal("Unknown ls sort type");
638 /*NOTREACHED*/
639 return 0;
642 /* sftp ls.1 replacement for directories */
643 static int
644 do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
646 int n;
647 u_int c = 1, colspace = 0, columns = 1;
648 SFTP_DIRENT **d;
650 if ((n = do_readdir(conn, path, &d)) != 0)
651 return (n);
653 if (!(lflag & LS_SHORT_VIEW)) {
654 u_int m = 0, width = 80;
655 struct winsize ws;
656 char *tmp;
658 /* Count entries for sort and find longest filename */
659 for (n = 0; d[n] != NULL; n++) {
660 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
661 m = MAX(m, strlen(d[n]->filename));
664 /* Add any subpath that also needs to be counted */
665 tmp = path_strip(path, strip_path);
666 m += strlen(tmp);
667 xfree(tmp);
669 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
670 width = ws.ws_col;
672 columns = width / (m + 2);
673 columns = MAX(columns, 1);
674 colspace = width / columns;
675 colspace = MIN(colspace, width);
678 if (lflag & SORT_FLAGS) {
679 for (n = 0; d[n] != NULL; n++)
680 ; /* count entries */
681 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
682 qsort(d, n, sizeof(*d), sdirent_comp);
685 for (n = 0; d[n] != NULL && !interrupted; n++) {
686 char *tmp, *fname;
688 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
689 continue;
691 tmp = path_append(path, d[n]->filename);
692 fname = path_strip(tmp, strip_path);
693 xfree(tmp);
695 if (lflag & LS_LONG_VIEW) {
696 if (lflag & LS_NUMERIC_VIEW) {
697 char *lname;
698 struct stat sb;
700 memset(&sb, 0, sizeof(sb));
701 attrib_to_stat(&d[n]->a, &sb);
702 lname = ls_file(fname, &sb, 1);
703 printf("%s\n", lname);
704 xfree(lname);
705 } else
706 printf("%s\n", d[n]->longname);
707 } else {
708 printf("%-*s", colspace, fname);
709 if (c >= columns) {
710 printf("\n");
711 c = 1;
712 } else
713 c++;
716 xfree(fname);
719 if (!(lflag & LS_LONG_VIEW) && (c != 1))
720 printf("\n");
722 free_sftp_dirents(d);
723 return (0);
726 /* sftp ls.1 replacement which handles path globs */
727 static int
728 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
729 int lflag)
731 glob_t g;
732 u_int i, c = 1, colspace = 0, columns = 1;
733 Attrib *a = NULL;
735 memset(&g, 0, sizeof(g));
737 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
738 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
739 if (g.gl_pathc)
740 globfree(&g);
741 error("Can't ls: \"%s\" not found", path);
742 return (-1);
745 if (interrupted)
746 goto out;
749 * If the glob returns a single match and it is a directory,
750 * then just list its contents.
752 if (g.gl_matchc == 1) {
753 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
754 globfree(&g);
755 return (-1);
757 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
758 S_ISDIR(a->perm)) {
759 int err;
761 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
762 globfree(&g);
763 return (err);
767 if (!(lflag & LS_SHORT_VIEW)) {
768 u_int m = 0, width = 80;
769 struct winsize ws;
771 /* Count entries for sort and find longest filename */
772 for (i = 0; g.gl_pathv[i]; i++)
773 m = MAX(m, strlen(g.gl_pathv[i]));
775 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
776 width = ws.ws_col;
778 columns = width / (m + 2);
779 columns = MAX(columns, 1);
780 colspace = width / columns;
783 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
784 char *fname;
786 fname = path_strip(g.gl_pathv[i], strip_path);
788 if (lflag & LS_LONG_VIEW) {
789 char *lname;
790 struct stat sb;
793 * XXX: this is slow - 1 roundtrip per path
794 * A solution to this is to fork glob() and
795 * build a sftp specific version which keeps the
796 * attribs (which currently get thrown away)
797 * that the server returns as well as the filenames.
799 memset(&sb, 0, sizeof(sb));
800 if (a == NULL)
801 a = do_lstat(conn, g.gl_pathv[i], 1);
802 if (a != NULL)
803 attrib_to_stat(a, &sb);
804 lname = ls_file(fname, &sb, 1);
805 printf("%s\n", lname);
806 xfree(lname);
807 } else {
808 printf("%-*s", colspace, fname);
809 if (c >= columns) {
810 printf("\n");
811 c = 1;
812 } else
813 c++;
815 xfree(fname);
818 if (!(lflag & LS_LONG_VIEW) && (c != 1))
819 printf("\n");
821 out:
822 if (g.gl_pathc)
823 globfree(&g);
825 return (0);
828 static int
829 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
831 struct sftp_statvfs st;
832 char s_used[FMT_SCALED_STRSIZE];
833 char s_avail[FMT_SCALED_STRSIZE];
834 char s_root[FMT_SCALED_STRSIZE];
835 char s_total[FMT_SCALED_STRSIZE];
837 if (do_statvfs(conn, path, &st, 1) == -1)
838 return -1;
839 if (iflag) {
840 printf(" Inodes Used Avail "
841 "(root) %%Capacity\n");
842 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
843 (unsigned long long)st.f_files,
844 (unsigned long long)(st.f_files - st.f_ffree),
845 (unsigned long long)st.f_favail,
846 (unsigned long long)st.f_ffree,
847 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
848 st.f_files));
849 } else if (hflag) {
850 strlcpy(s_used, "error", sizeof(s_used));
851 strlcpy(s_avail, "error", sizeof(s_avail));
852 strlcpy(s_root, "error", sizeof(s_root));
853 strlcpy(s_total, "error", sizeof(s_total));
854 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
855 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
856 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
857 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
858 printf(" Size Used Avail (root) %%Capacity\n");
859 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
860 s_total, s_used, s_avail, s_root,
861 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
862 st.f_blocks));
863 } else {
864 printf(" Size Used Avail "
865 "(root) %%Capacity\n");
866 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
867 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
868 (unsigned long long)(st.f_frsize *
869 (st.f_blocks - st.f_bfree) / 1024),
870 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
871 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
872 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
873 st.f_blocks));
875 return 0;
879 * Undo escaping of glob sequences in place. Used to undo extra escaping
880 * applied in makeargv() when the string is destined for a function that
881 * does not glob it.
883 static void
884 undo_glob_escape(char *s)
886 size_t i, j;
888 for (i = j = 0;;) {
889 if (s[i] == '\0') {
890 s[j] = '\0';
891 return;
893 if (s[i] != '\\') {
894 s[j++] = s[i++];
895 continue;
897 /* s[i] == '\\' */
898 ++i;
899 switch (s[i]) {
900 case '?':
901 case '[':
902 case '*':
903 case '\\':
904 s[j++] = s[i++];
905 break;
906 case '\0':
907 s[j++] = '\\';
908 s[j] = '\0';
909 return;
910 default:
911 s[j++] = '\\';
912 s[j++] = s[i++];
913 break;
919 * Split a string into an argument vector using sh(1)-style quoting,
920 * comment and escaping rules, but with some tweaks to handle glob(3)
921 * wildcards.
922 * Returns NULL on error or a NULL-terminated array of arguments.
924 #define MAXARGS 128
925 #define MAXARGLEN 8192
926 static char **
927 makeargv(const char *arg, int *argcp)
929 int argc, quot;
930 size_t i, j;
931 static char argvs[MAXARGLEN];
932 static char *argv[MAXARGS + 1];
933 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
935 *argcp = argc = 0;
936 if (strlen(arg) > sizeof(argvs) - 1) {
937 args_too_longs:
938 error("string too long");
939 return NULL;
941 state = MA_START;
942 i = j = 0;
943 for (;;) {
944 if (isspace((unsigned char)arg[i])) {
945 if (state == MA_UNQUOTED) {
946 /* Terminate current argument */
947 argvs[j++] = '\0';
948 argc++;
949 state = MA_START;
950 } else if (state != MA_START)
951 argvs[j++] = arg[i];
952 } else if (arg[i] == '"' || arg[i] == '\'') {
953 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
954 if (state == MA_START) {
955 argv[argc] = argvs + j;
956 state = q;
957 } else if (state == MA_UNQUOTED)
958 state = q;
959 else if (state == q)
960 state = MA_UNQUOTED;
961 else
962 argvs[j++] = arg[i];
963 } else if (arg[i] == '\\') {
964 if (state == MA_SQUOTE || state == MA_DQUOTE) {
965 quot = state == MA_SQUOTE ? '\'' : '"';
966 /* Unescape quote we are in */
967 /* XXX support \n and friends? */
968 if (arg[i + 1] == quot) {
969 i++;
970 argvs[j++] = arg[i];
971 } else if (arg[i + 1] == '?' ||
972 arg[i + 1] == '[' || arg[i + 1] == '*') {
974 * Special case for sftp: append
975 * double-escaped glob sequence -
976 * glob will undo one level of
977 * escaping. NB. string can grow here.
979 if (j >= sizeof(argvs) - 5)
980 goto args_too_longs;
981 argvs[j++] = '\\';
982 argvs[j++] = arg[i++];
983 argvs[j++] = '\\';
984 argvs[j++] = arg[i];
985 } else {
986 argvs[j++] = arg[i++];
987 argvs[j++] = arg[i];
989 } else {
990 if (state == MA_START) {
991 argv[argc] = argvs + j;
992 state = MA_UNQUOTED;
994 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
995 arg[i + 1] == '*' || arg[i + 1] == '\\') {
997 * Special case for sftp: append
998 * escaped glob sequence -
999 * glob will undo one level of
1000 * escaping.
1002 argvs[j++] = arg[i++];
1003 argvs[j++] = arg[i];
1004 } else {
1005 /* Unescape everything */
1006 /* XXX support \n and friends? */
1007 i++;
1008 argvs[j++] = arg[i];
1011 } else if (arg[i] == '#') {
1012 if (state == MA_SQUOTE || state == MA_DQUOTE)
1013 argvs[j++] = arg[i];
1014 else
1015 goto string_done;
1016 } else if (arg[i] == '\0') {
1017 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1018 error("Unterminated quoted argument");
1019 return NULL;
1021 string_done:
1022 if (state == MA_UNQUOTED) {
1023 argvs[j++] = '\0';
1024 argc++;
1026 break;
1027 } else {
1028 if (state == MA_START) {
1029 argv[argc] = argvs + j;
1030 state = MA_UNQUOTED;
1032 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1033 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1035 * Special case for sftp: escape quoted
1036 * glob(3) wildcards. NB. string can grow
1037 * here.
1039 if (j >= sizeof(argvs) - 3)
1040 goto args_too_longs;
1041 argvs[j++] = '\\';
1042 argvs[j++] = arg[i];
1043 } else
1044 argvs[j++] = arg[i];
1046 i++;
1048 *argcp = argc;
1049 return argv;
1052 static int
1053 parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag,
1054 unsigned long *n_arg, char **path1, char **path2)
1056 const char *cmd, *cp = *cpp;
1057 char *cp2, **argv;
1058 int base = 0;
1059 long l;
1060 int i, cmdnum, optidx, argc;
1062 /* Skip leading whitespace */
1063 cp = cp + strspn(cp, WHITESPACE);
1065 /* Ignore blank lines and lines which begin with comment '#' char */
1066 if (*cp == '\0' || *cp == '#')
1067 return (0);
1069 /* Check for leading '-' (disable error processing) */
1070 *iflag = 0;
1071 if (*cp == '-') {
1072 *iflag = 1;
1073 cp++;
1076 if ((argv = makeargv(cp, &argc)) == NULL)
1077 return -1;
1079 /* Figure out which command we have */
1080 for (i = 0; cmds[i].c != NULL; i++) {
1081 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1082 break;
1084 cmdnum = cmds[i].n;
1085 cmd = cmds[i].c;
1087 /* Special case */
1088 if (*cp == '!') {
1089 cp++;
1090 cmdnum = I_SHELL;
1091 } else if (cmdnum == -1) {
1092 error("Invalid command.");
1093 return -1;
1096 /* Get arguments and parse flags */
1097 *lflag = *pflag = *hflag = *n_arg = 0;
1098 *path1 = *path2 = NULL;
1099 optidx = 1;
1100 switch (cmdnum) {
1101 case I_GET:
1102 case I_PUT:
1103 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1)
1104 return -1;
1105 /* Get first pathname (mandatory) */
1106 if (argc - optidx < 1) {
1107 error("You must specify at least one path after a "
1108 "%s command.", cmd);
1109 return -1;
1111 *path1 = xstrdup(argv[optidx]);
1112 /* Get second pathname (optional) */
1113 if (argc - optidx > 1) {
1114 *path2 = xstrdup(argv[optidx + 1]);
1115 /* Destination is not globbed */
1116 undo_glob_escape(*path2);
1118 break;
1119 case I_RENAME:
1120 case I_SYMLINK:
1121 if (argc - optidx < 2) {
1122 error("You must specify two paths after a %s "
1123 "command.", cmd);
1124 return -1;
1126 *path1 = xstrdup(argv[optidx]);
1127 *path2 = xstrdup(argv[optidx + 1]);
1128 /* Paths are not globbed */
1129 undo_glob_escape(*path1);
1130 undo_glob_escape(*path2);
1131 break;
1132 case I_RM:
1133 case I_MKDIR:
1134 case I_RMDIR:
1135 case I_CHDIR:
1136 case I_LCHDIR:
1137 case I_LMKDIR:
1138 /* Get pathname (mandatory) */
1139 if (argc - optidx < 1) {
1140 error("You must specify a path after a %s command.",
1141 cmd);
1142 return -1;
1144 *path1 = xstrdup(argv[optidx]);
1145 /* Only "rm" globs */
1146 if (cmdnum != I_RM)
1147 undo_glob_escape(*path1);
1148 break;
1149 case I_DF:
1150 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1151 iflag)) == -1)
1152 return -1;
1153 /* Default to current directory if no path specified */
1154 if (argc - optidx < 1)
1155 *path1 = NULL;
1156 else {
1157 *path1 = xstrdup(argv[optidx]);
1158 undo_glob_escape(*path1);
1160 break;
1161 case I_LS:
1162 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1163 return(-1);
1164 /* Path is optional */
1165 if (argc - optidx > 0)
1166 *path1 = xstrdup(argv[optidx]);
1167 break;
1168 case I_LLS:
1169 /* Skip ls command and following whitespace */
1170 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1171 case I_SHELL:
1172 /* Uses the rest of the line */
1173 break;
1174 case I_LUMASK:
1175 case I_CHMOD:
1176 base = 8;
1177 case I_CHOWN:
1178 case I_CHGRP:
1179 /* Get numeric arg (mandatory) */
1180 if (argc - optidx < 1)
1181 goto need_num_arg;
1182 errno = 0;
1183 l = strtol(argv[optidx], &cp2, base);
1184 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1185 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1186 l < 0) {
1187 need_num_arg:
1188 error("You must supply a numeric argument "
1189 "to the %s command.", cmd);
1190 return -1;
1192 *n_arg = l;
1193 if (cmdnum == I_LUMASK)
1194 break;
1195 /* Get pathname (mandatory) */
1196 if (argc - optidx < 2) {
1197 error("You must specify a path after a %s command.",
1198 cmd);
1199 return -1;
1201 *path1 = xstrdup(argv[optidx + 1]);
1202 break;
1203 case I_QUIT:
1204 case I_PWD:
1205 case I_LPWD:
1206 case I_HELP:
1207 case I_VERSION:
1208 case I_PROGRESS:
1209 break;
1210 default:
1211 fatal("Command not implemented");
1214 *cpp = cp;
1215 return(cmdnum);
1218 static int
1219 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1220 int err_abort)
1222 char *path1, *path2, *tmp;
1223 int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1224 unsigned long n_arg = 0;
1225 Attrib a, *aa;
1226 char path_buf[MAXPATHLEN];
1227 int err = 0;
1228 glob_t g;
1230 pflag = 0; /* XXX gcc */
1231 lflag = 0; /* XXX gcc */
1232 iflag = 0; /* XXX gcc */
1233 hflag = 0; /* XXX gcc */
1234 n_arg = 0; /* XXX gcc */
1236 path1 = path2 = NULL;
1237 cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg,
1238 &path1, &path2);
1240 if (iflag != 0)
1241 err_abort = 0;
1243 memset(&g, 0, sizeof(g));
1245 /* Perform command */
1246 switch (cmdnum) {
1247 case 0:
1248 /* Blank line */
1249 break;
1250 case -1:
1251 /* Unrecognized command */
1252 err = -1;
1253 break;
1254 case I_GET:
1255 err = process_get(conn, path1, path2, *pwd, pflag);
1256 break;
1257 case I_PUT:
1258 err = process_put(conn, path1, path2, *pwd, pflag);
1259 break;
1260 case I_RENAME:
1261 path1 = make_absolute(path1, *pwd);
1262 path2 = make_absolute(path2, *pwd);
1263 err = do_rename(conn, path1, path2);
1264 break;
1265 case I_SYMLINK:
1266 path2 = make_absolute(path2, *pwd);
1267 err = do_symlink(conn, path1, path2);
1268 break;
1269 case I_RM:
1270 path1 = make_absolute(path1, *pwd);
1271 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1272 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1273 printf("Removing %s\n", g.gl_pathv[i]);
1274 err = do_rm(conn, g.gl_pathv[i]);
1275 if (err != 0 && err_abort)
1276 break;
1278 break;
1279 case I_MKDIR:
1280 path1 = make_absolute(path1, *pwd);
1281 attrib_clear(&a);
1282 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1283 a.perm = 0777;
1284 err = do_mkdir(conn, path1, &a);
1285 break;
1286 case I_RMDIR:
1287 path1 = make_absolute(path1, *pwd);
1288 err = do_rmdir(conn, path1);
1289 break;
1290 case I_CHDIR:
1291 path1 = make_absolute(path1, *pwd);
1292 if ((tmp = do_realpath(conn, path1)) == NULL) {
1293 err = 1;
1294 break;
1296 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1297 xfree(tmp);
1298 err = 1;
1299 break;
1301 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1302 error("Can't change directory: Can't check target");
1303 xfree(tmp);
1304 err = 1;
1305 break;
1307 if (!S_ISDIR(aa->perm)) {
1308 error("Can't change directory: \"%s\" is not "
1309 "a directory", tmp);
1310 xfree(tmp);
1311 err = 1;
1312 break;
1314 xfree(*pwd);
1315 *pwd = tmp;
1316 break;
1317 case I_LS:
1318 if (!path1) {
1319 do_globbed_ls(conn, *pwd, *pwd, lflag);
1320 break;
1323 /* Strip pwd off beginning of non-absolute paths */
1324 tmp = NULL;
1325 if (*path1 != '/')
1326 tmp = *pwd;
1328 path1 = make_absolute(path1, *pwd);
1329 err = do_globbed_ls(conn, path1, tmp, lflag);
1330 break;
1331 case I_DF:
1332 /* Default to current directory if no path specified */
1333 if (path1 == NULL)
1334 path1 = xstrdup(*pwd);
1335 path1 = make_absolute(path1, *pwd);
1336 err = do_df(conn, path1, hflag, iflag);
1337 break;
1338 case I_LCHDIR:
1339 if (chdir(path1) == -1) {
1340 error("Couldn't change local directory to "
1341 "\"%s\": %s", path1, strerror(errno));
1342 err = 1;
1344 break;
1345 case I_LMKDIR:
1346 if (mkdir(path1, 0777) == -1) {
1347 error("Couldn't create local directory "
1348 "\"%s\": %s", path1, strerror(errno));
1349 err = 1;
1351 break;
1352 case I_LLS:
1353 local_do_ls(cmd);
1354 break;
1355 case I_SHELL:
1356 local_do_shell(cmd);
1357 break;
1358 case I_LUMASK:
1359 umask(n_arg);
1360 printf("Local umask: %03lo\n", n_arg);
1361 break;
1362 case I_CHMOD:
1363 path1 = make_absolute(path1, *pwd);
1364 attrib_clear(&a);
1365 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1366 a.perm = n_arg;
1367 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1368 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1369 printf("Changing mode on %s\n", g.gl_pathv[i]);
1370 err = do_setstat(conn, g.gl_pathv[i], &a);
1371 if (err != 0 && err_abort)
1372 break;
1374 break;
1375 case I_CHOWN:
1376 case I_CHGRP:
1377 path1 = make_absolute(path1, *pwd);
1378 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1379 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1380 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1381 if (err_abort) {
1382 err = -1;
1383 break;
1384 } else
1385 continue;
1387 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1388 error("Can't get current ownership of "
1389 "remote file \"%s\"", g.gl_pathv[i]);
1390 if (err_abort) {
1391 err = -1;
1392 break;
1393 } else
1394 continue;
1396 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1397 if (cmdnum == I_CHOWN) {
1398 printf("Changing owner on %s\n", g.gl_pathv[i]);
1399 aa->uid = n_arg;
1400 } else {
1401 printf("Changing group on %s\n", g.gl_pathv[i]);
1402 aa->gid = n_arg;
1404 err = do_setstat(conn, g.gl_pathv[i], aa);
1405 if (err != 0 && err_abort)
1406 break;
1408 break;
1409 case I_PWD:
1410 printf("Remote working directory: %s\n", *pwd);
1411 break;
1412 case I_LPWD:
1413 if (!getcwd(path_buf, sizeof(path_buf))) {
1414 error("Couldn't get local cwd: %s", strerror(errno));
1415 err = -1;
1416 break;
1418 printf("Local working directory: %s\n", path_buf);
1419 break;
1420 case I_QUIT:
1421 /* Processed below */
1422 break;
1423 case I_HELP:
1424 help();
1425 break;
1426 case I_VERSION:
1427 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1428 break;
1429 case I_PROGRESS:
1430 showprogress = !showprogress;
1431 if (showprogress)
1432 printf("Progress meter enabled\n");
1433 else
1434 printf("Progress meter disabled\n");
1435 break;
1436 default:
1437 fatal("%d is not implemented", cmdnum);
1440 if (g.gl_pathc)
1441 globfree(&g);
1442 if (path1)
1443 xfree(path1);
1444 if (path2)
1445 xfree(path2);
1447 /* If an unignored error occurs in batch mode we should abort. */
1448 if (err_abort && err != 0)
1449 return (-1);
1450 else if (cmdnum == I_QUIT)
1451 return (1);
1453 return (0);
1456 static char *
1457 prompt(EditLine *el)
1459 return ("sftp> ");
1463 interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1465 char *pwd;
1466 char *dir = NULL;
1467 char cmd[2048];
1468 struct sftp_conn *conn;
1469 int err, interactive;
1470 EditLine *el = NULL;
1471 History *hl = NULL;
1472 HistEvent hev;
1473 extern char *__progname;
1475 if (!batchmode && isatty(STDIN_FILENO)) {
1476 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1477 fatal("Couldn't initialise editline");
1478 if ((hl = history_init()) == NULL)
1479 fatal("Couldn't initialise editline history");
1480 history(hl, &hev, H_SETSIZE, 100);
1481 el_set(el, EL_HIST, history, hl);
1483 el_set(el, EL_PROMPT, prompt);
1484 el_set(el, EL_EDITOR, "emacs");
1485 el_set(el, EL_TERMINAL, NULL);
1486 el_set(el, EL_SIGNAL, 1);
1487 el_source(el, NULL);
1490 conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests);
1491 if (conn == NULL)
1492 fatal("Couldn't initialise connection to server");
1494 pwd = do_realpath(conn, ".");
1495 if (pwd == NULL)
1496 fatal("Need cwd");
1498 if (file1 != NULL) {
1499 dir = xstrdup(file1);
1500 dir = make_absolute(dir, pwd);
1502 if (remote_is_dir(conn, dir) && file2 == NULL) {
1503 printf("Changing to: %s\n", dir);
1504 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1505 if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) {
1506 xfree(dir);
1507 xfree(pwd);
1508 xfree(conn);
1509 return (-1);
1511 } else {
1512 if (file2 == NULL)
1513 snprintf(cmd, sizeof cmd, "get %s", dir);
1514 else
1515 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1516 file2);
1518 err = parse_dispatch_command(conn, cmd, &pwd, 1);
1519 xfree(dir);
1520 xfree(pwd);
1521 xfree(conn);
1522 return (err);
1524 xfree(dir);
1527 setvbuf(stdout, NULL, _IOLBF, 0);
1528 setvbuf(infile, NULL, _IOLBF, 0);
1530 interactive = !batchmode && isatty(STDIN_FILENO);
1531 err = 0;
1532 for (;;) {
1533 char *cp;
1534 const char *line;
1535 int count = 0;
1537 signal(SIGINT, SIG_IGN);
1539 if (el == NULL) {
1540 if (interactive)
1541 printf("sftp> ");
1542 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1543 if (interactive)
1544 printf("\n");
1545 break;
1547 if (!interactive) { /* Echo command */
1548 printf("sftp> %s", cmd);
1549 if (strlen(cmd) > 0 &&
1550 cmd[strlen(cmd) - 1] != '\n')
1551 printf("\n");
1553 } else {
1554 if ((line = el_gets(el, &count)) == NULL || count <= 0) {
1555 printf("\n");
1556 break;
1558 history(hl, &hev, H_ENTER, line);
1559 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1560 fprintf(stderr, "Error: input line too long\n");
1561 continue;
1565 cp = strrchr(cmd, '\n');
1566 if (cp)
1567 *cp = '\0';
1569 /* Handle user interrupts gracefully during commands */
1570 interrupted = 0;
1571 signal(SIGINT, cmd_interrupt);
1573 err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
1574 if (err != 0)
1575 break;
1577 xfree(pwd);
1578 xfree(conn);
1580 if (el != NULL)
1581 el_end(el);
1583 /* err == 1 signifies normal "quit" exit */
1584 return (err >= 0 ? 0 : -1);
1587 static void
1588 connect_to_server(char *path, char **args, int *in, int *out)
1590 int c_in, c_out;
1592 int inout[2];
1594 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
1595 fatal("socketpair: %s", strerror(errno));
1596 *in = *out = inout[0];
1597 c_in = c_out = inout[1];
1599 if ((sshpid = fork()) == -1)
1600 fatal("fork: %s", strerror(errno));
1601 else if (sshpid == 0) {
1602 if ((dup2(c_in, STDIN_FILENO) == -1) ||
1603 (dup2(c_out, STDOUT_FILENO) == -1)) {
1604 fprintf(stderr, "dup2: %s\n", strerror(errno));
1605 _exit(1);
1607 close(*in);
1608 close(*out);
1609 close(c_in);
1610 close(c_out);
1613 * The underlying ssh is in the same process group, so we must
1614 * ignore SIGINT if we want to gracefully abort commands,
1615 * otherwise the signal will make it to the ssh process and
1616 * kill it too
1618 signal(SIGINT, SIG_IGN);
1619 execvp(path, args);
1620 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
1621 _exit(1);
1624 signal(SIGTERM, killchild);
1625 signal(SIGINT, killchild);
1626 signal(SIGHUP, killchild);
1627 close(c_in);
1628 close(c_out);
1631 static void
1632 usage(void)
1634 extern char *__progname;
1636 fprintf(stderr,
1637 "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n"
1638 " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n"
1639 " [-S program] [-s subsystem | sftp_server] host\n"
1640 " %s [user@]host[:file ...]\n"
1641 " %s [user@]host[:dir[/]]\n"
1642 " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname);
1643 exit(1);
1647 main(int argc, char **argv)
1649 int in, out, ch, err;
1650 char *host, *userhost, *cp, *file2 = NULL;
1651 int debug_level = 0, sshver = 2;
1652 char *file1 = NULL, *sftp_server = NULL;
1653 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
1654 LogLevel ll = SYSLOG_LEVEL_INFO;
1655 arglist args;
1656 extern int optind;
1657 extern char *optarg;
1659 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1660 sanitise_stdfd();
1662 memset(&args, '\0', sizeof(args));
1663 args.list = NULL;
1664 addargs(&args, "%s", ssh_program);
1665 addargs(&args, "-oForwardX11 no");
1666 addargs(&args, "-oForwardAgent no");
1667 addargs(&args, "-oPermitLocalCommand no");
1668 addargs(&args, "-oClearAllForwardings yes");
1670 ll = SYSLOG_LEVEL_INFO;
1671 infile = stdin;
1673 while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
1674 switch (ch) {
1675 case 'C':
1676 addargs(&args, "-C");
1677 break;
1678 case 'v':
1679 if (debug_level < 3) {
1680 addargs(&args, "-v");
1681 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
1683 debug_level++;
1684 break;
1685 case 'F':
1686 case 'o':
1687 addargs(&args, "-%c%s", ch, optarg);
1688 break;
1689 case '1':
1690 sshver = 1;
1691 if (sftp_server == NULL)
1692 sftp_server = _PATH_SFTP_SERVER;
1693 break;
1694 case 's':
1695 sftp_server = optarg;
1696 break;
1697 case 'S':
1698 ssh_program = optarg;
1699 replacearg(&args, 0, "%s", ssh_program);
1700 break;
1701 case 'b':
1702 if (batchmode)
1703 fatal("Batch file already specified.");
1705 /* Allow "-" as stdin */
1706 if (strcmp(optarg, "-") != 0 &&
1707 (infile = fopen(optarg, "r")) == NULL)
1708 fatal("%s (%s).", strerror(errno), optarg);
1709 showprogress = 0;
1710 batchmode = 1;
1711 addargs(&args, "-obatchmode yes");
1712 break;
1713 case 'P':
1714 sftp_direct = optarg;
1715 break;
1716 case 'B':
1717 copy_buffer_len = strtol(optarg, &cp, 10);
1718 if (copy_buffer_len == 0 || *cp != '\0')
1719 fatal("Invalid buffer size \"%s\"", optarg);
1720 break;
1721 case 'R':
1722 num_requests = strtol(optarg, &cp, 10);
1723 if (num_requests == 0 || *cp != '\0')
1724 fatal("Invalid number of requests \"%s\"",
1725 optarg);
1726 break;
1727 case 'h':
1728 default:
1729 usage();
1733 if (!isatty(STDERR_FILENO))
1734 showprogress = 0;
1736 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
1738 if (sftp_direct == NULL) {
1739 if (optind == argc || argc > (optind + 2))
1740 usage();
1742 userhost = xstrdup(argv[optind]);
1743 file2 = argv[optind+1];
1745 if ((host = strrchr(userhost, '@')) == NULL)
1746 host = userhost;
1747 else {
1748 *host++ = '\0';
1749 if (!userhost[0]) {
1750 fprintf(stderr, "Missing username\n");
1751 usage();
1753 addargs(&args, "-l%s", userhost);
1756 if ((cp = colon(host)) != NULL) {
1757 *cp++ = '\0';
1758 file1 = cp;
1761 host = cleanhostname(host);
1762 if (!*host) {
1763 fprintf(stderr, "Missing hostname\n");
1764 usage();
1767 addargs(&args, "-oProtocol %d", sshver);
1769 /* no subsystem if the server-spec contains a '/' */
1770 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
1771 addargs(&args, "-s");
1773 addargs(&args, "%s", host);
1774 addargs(&args, "%s", (sftp_server != NULL ?
1775 sftp_server : "sftp"));
1777 if (!batchmode)
1778 fprintf(stderr, "Connecting to %s...\n", host);
1779 connect_to_server(ssh_program, args.list, &in, &out);
1780 } else {
1781 args.list = NULL;
1782 addargs(&args, "sftp-server");
1784 if (!batchmode)
1785 fprintf(stderr, "Attaching to %s...\n", sftp_direct);
1786 connect_to_server(sftp_direct, args.list, &in, &out);
1788 freeargs(&args);
1790 err = interactive_loop(in, out, file1, file2);
1792 close(in);
1793 close(out);
1794 if (batchmode)
1795 fclose(infile);
1797 while (waitpid(sshpid, NULL, 0) == -1)
1798 if (errno != EINTR)
1799 fatal("Couldn't wait for ssh process: %s",
1800 strerror(errno));
1802 exit(err == 0 ? 0 : 1);