]> andersk Git - openssh.git/blame_incremental - sftp.c
- halex@cvs.openbsd.org 2009/11/22 13:18:00
[openssh.git] / sftp.c
... / ...
CommitLineData
1/* $OpenBSD: sftp.c,v 1.113 2009/11/22 13:18:00 halex Exp $ */
2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 *
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.
8 *
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.
16 */
17
18#include "includes.h"
19
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
31
32#include <ctype.h>
33#include <errno.h>
34
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
44typedef 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>
52
53#ifdef HAVE_UTIL_H
54# include <util.h>
55#endif
56
57#ifdef HAVE_LIBUTIL_H
58# include <libutil.h>
59#endif
60
61#include "xmalloc.h"
62#include "log.h"
63#include "pathnames.h"
64#include "misc.h"
65
66#include "sftp.h"
67#include "buffer.h"
68#include "sftp-common.h"
69#include "sftp-client.h"
70
71#define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
72#define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
73
74/* File to read commands from */
75FILE* infile;
76
77/* Are we in batchfile mode? */
78int batchmode = 0;
79
80/* PID of ssh transport process */
81static pid_t sshpid = -1;
82
83/* This is set to 0 if the progressmeter is not desired. */
84int showprogress = 1;
85
86/* When this option is set, we always recursively download/upload directories */
87int global_rflag = 0;
88
89/* When this option is set, the file transfers will always preserve times */
90int global_pflag = 0;
91
92/* SIGINT received during command processing */
93volatile sig_atomic_t interrupted = 0;
94
95/* I wish qsort() took a separate ctx for the comparison function...*/
96int sort_flag;
97
98int remote_glob(struct sftp_conn *, const char *, int,
99 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
100
101extern char *__progname;
102
103/* Separators for interactive commands */
104#define WHITESPACE " \t\r\n"
105
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 '.' */
115
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)
118
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
144
145struct CMD {
146 const char *c;
147 const int n;
148};
149
150static 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}
185};
186
187int interactive_loop(struct sftp_conn *, char *file1, char *file2);
188
189/* ARGSUSED */
190static void
191killchild(int signo)
192{
193 if (sshpid > 1) {
194 kill(sshpid, SIGTERM);
195 waitpid(sshpid, NULL, 0);
196 }
197
198 _exit(1);
199}
200
201/* ARGSUSED */
202static void
203cmd_interrupt(int signo)
204{
205 const char msg[] = "\rInterrupt \n";
206 int olderrno = errno;
207
208 write(STDERR_FILENO, msg, sizeof(msg) - 1);
209 interrupted = 1;
210 errno = olderrno;
211}
212
213static void
214help(void)
215{
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");
247}
248
249static void
250local_do_shell(const char *args)
251{
252 int status;
253 char *shell;
254 pid_t pid;
255
256 if (!*args)
257 args = NULL;
258
259 if ((shell = getenv("SHELL")) == NULL)
260 shell = _PATH_BSHELL;
261
262 if ((pid = fork()) == -1)
263 fatal("Couldn't fork: %s", strerror(errno));
264
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);
273 }
274 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
275 strerror(errno));
276 _exit(1);
277 }
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));
285}
286
287static void
288local_do_ls(const char *args)
289{
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);
295
296 /* XXX: quoting - rip quoting code from ftp? */
297 snprintf(buf, len, _PATH_LS " %s", args);
298 local_do_shell(buf);
299 xfree(buf);
300 }
301}
302
303/* Strip one path (usually the pwd) from the start of another */
304static char *
305path_strip(char *path, char *strip)
306{
307 size_t len;
308
309 if (strip == NULL)
310 return (xstrdup(path));
311
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));
317 }
318
319 return (xstrdup(path));
320}
321
322static char *
323make_absolute(char *p, char *pwd)
324{
325 char *abs_str;
326
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);
334}
335
336static int
337parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
338 int *rflag)
339{
340 extern int opterr, optind, optopt, optreset;
341 int ch;
342
343 optind = optreset = 1;
344 opterr = 0;
345
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;
360 }
361 }
362
363 return optind;
364}
365
366static int
367parse_ls_flags(char **argv, int argc, int *lflag)
368{
369 extern int opterr, optind, optopt, optreset;
370 int ch;
371
372 optind = optreset = 1;
373 opterr = 0;
374
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;
410 }
411 }
412
413 return optind;
414}
415
416static int
417parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
418{
419 extern int opterr, optind, optopt, optreset;
420 int ch;
421
422 optind = optreset = 1;
423 opterr = 0;
424
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;
437 }
438 }
439
440 return optind;
441}
442
443static int
444is_dir(char *path)
445{
446 struct stat sb;
447
448 /* XXX: report errors? */
449 if (stat(path, &sb) == -1)
450 return(0);
451
452 return(S_ISDIR(sb.st_mode));
453}
454
455static int
456remote_is_dir(struct sftp_conn *conn, char *path)
457{
458 Attrib *a;
459
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));
466}
467
468/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
469static int
470pathname_is_dir(char *pathname)
471{
472 size_t l = strlen(pathname);
473
474 return l > 0 && pathname[l - 1] == '/';
475}
476
477static int
478process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
479 int pflag, int rflag)
480{
481 char *abs_src = NULL;
482 char *abs_dst = NULL;
483 glob_t g;
484 char *filename, *tmp=NULL;
485 int i, err = 0;
486
487 abs_src = xstrdup(src);
488 abs_src = make_absolute(abs_src, pwd);
489 memset(&g, 0, sizeof(g));
490
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;
496 }
497
498 /*
499 * If multiple matches then dst must be a directory or
500 * unspecified.
501 */
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;
507 }
508
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;
516 }
517
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);
523 }
524 } else if (dst) {
525 abs_dst = path_append(dst, filename);
526 } else {
527 abs_dst = xstrdup(filename);
528 }
529 xfree(tmp);
530
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;
540 }
541 xfree(abs_dst);
542 abs_dst = NULL;
543 }
544
545out:
546 xfree(abs_src);
547 globfree(&g);
548 return(err);
549}
550
551static int
552process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
553 int pflag, int rflag)
554{
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;
562
563 if (dst) {
564 tmp_dst = xstrdup(dst);
565 tmp_dst = make_absolute(tmp_dst, pwd);
566 }
567
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;
574 }
575
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);
579
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;
586 }
587
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;
593 }
594
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;
601 }
602
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);
613 }
614 xfree(tmp);
615
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;
625 }
626 }
627
628out:
629 if (abs_dst)
630 xfree(abs_dst);
631 if (tmp_dst)
632 xfree(tmp_dst);
633 globfree(&g);
634 return(err);
635}
636
637static int
638sdirent_comp(const void *aa, const void *bb)
639{
640 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
641 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
642 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
643
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));
651
652 fatal("Unknown ls sort type");
653}
654
655/* sftp ls.1 replacement for directories */
656static int
657do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
658{
659 int n;
660 u_int c = 1, colspace = 0, columns = 1;
661 SFTP_DIRENT **d;
662
663 if ((n = do_readdir(conn, path, &d)) != 0)
664 return (n);
665
666 if (!(lflag & LS_SHORT_VIEW)) {
667 u_int m = 0, width = 80;
668 struct winsize ws;
669 char *tmp;
670
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));
675 }
676
677 /* Add any subpath that also needs to be counted */
678 tmp = path_strip(path, strip_path);
679 m += strlen(tmp);
680 xfree(tmp);
681
682 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
683 width = ws.ws_col;
684
685 columns = width / (m + 2);
686 columns = MAX(columns, 1);
687 colspace = width / columns;
688 colspace = MIN(colspace, width);
689 }
690
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);
696 }
697
698 for (n = 0; d[n] != NULL && !interrupted; n++) {
699 char *tmp, *fname;
700
701 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
702 continue;
703
704 tmp = path_append(path, d[n]->filename);
705 fname = path_strip(tmp, strip_path);
706 xfree(tmp);
707
708 if (lflag & LS_LONG_VIEW) {
709 if (lflag & LS_NUMERIC_VIEW) {
710 char *lname;
711 struct stat sb;
712
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++;
727 }
728
729 xfree(fname);
730 }
731
732 if (!(lflag & LS_LONG_VIEW) && (c != 1))
733 printf("\n");
734
735 free_sftp_dirents(d);
736 return (0);
737}
738
739/* sftp ls.1 replacement which handles path globs */
740static int
741do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
742 int lflag)
743{
744 glob_t g;
745 u_int i, c = 1, colspace = 0, columns = 1;
746 Attrib *a = NULL;
747
748 memset(&g, 0, sizeof(g));
749
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);
756 }
757
758 if (interrupted)
759 goto out;
760
761 /*
762 * If the glob returns a single match and it is a directory,
763 * then just list its contents.
764 */
765 if (g.gl_matchc == 1) {
766 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
767 globfree(&g);
768 return (-1);
769 }
770 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
771 S_ISDIR(a->perm)) {
772 int err;
773
774 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
775 globfree(&g);
776 return (err);
777 }
778 }
779
780 if (!(lflag & LS_SHORT_VIEW)) {
781 u_int m = 0, width = 80;
782 struct winsize ws;
783
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]));
787
788 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
789 width = ws.ws_col;
790
791 columns = width / (m + 2);
792 columns = MAX(columns, 1);
793 colspace = width / columns;
794 }
795
796 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
797 char *fname;
798
799 fname = path_strip(g.gl_pathv[i], strip_path);
800
801 if (lflag & LS_LONG_VIEW) {
802 char *lname;
803 struct stat sb;
804
805 /*
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.
811 */
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++;
827 }
828 xfree(fname);
829 }
830
831 if (!(lflag & LS_LONG_VIEW) && (c != 1))
832 printf("\n");
833
834 out:
835 if (g.gl_pathc)
836 globfree(&g);
837
838 return (0);
839}
840
841static int
842do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
843{
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
850 if (do_statvfs(conn, path, &st, 1) == -1)
851 return -1;
852 if (iflag) {
853 printf(" Inodes Used Avail "
854 "(root) %%Capacity\n");
855 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
856 (unsigned long long)st.f_files,
857 (unsigned long long)(st.f_files - st.f_ffree),
858 (unsigned long long)st.f_favail,
859 (unsigned long long)st.f_ffree,
860 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
861 st.f_files));
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));
887 }
888 return 0;
889}
890
891/*
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.
895 */
896static void
897undo_glob_escape(char *s)
898{
899 size_t i, j;
900
901 for (i = j = 0;;) {
902 if (s[i] == '\0') {
903 s[j] = '\0';
904 return;
905 }
906 if (s[i] != '\\') {
907 s[j++] = s[i++];
908 continue;
909 }
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;
927 }
928 }
929}
930
931/*
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.
936 */
937#define MAXARGS 128
938#define MAXARGLEN 8192
939static char **
940makeargv(const char *arg, int *argcp)
941{
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;
947
948 *argcp = argc = 0;
949 if (strlen(arg) > sizeof(argvs) - 1) {
950 args_too_longs:
951 error("string too long");
952 return NULL;
953 }
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] == '*') {
986 /*
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.
991 */
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];
1001 }
1002 } else {
1003 if (state == MA_START) {
1004 argv[argc] = argvs + j;
1005 state = MA_UNQUOTED;
1006 }
1007 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1008 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1009 /*
1010 * Special case for sftp: append
1011 * escaped glob sequence -
1012 * glob will undo one level of
1013 * escaping.
1014 */
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];
1022 }
1023 }
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;
1033 }
1034 string_done:
1035 if (state == MA_UNQUOTED) {
1036 argvs[j++] = '\0';
1037 argc++;
1038 }
1039 break;
1040 } else {
1041 if (state == MA_START) {
1042 argv[argc] = argvs + j;
1043 state = MA_UNQUOTED;
1044 }
1045 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1046 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1047 /*
1048 * Special case for sftp: escape quoted
1049 * glob(3) wildcards. NB. string can grow
1050 * here.
1051 */
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];
1058 }
1059 i++;
1060 }
1061 *argcp = argc;
1062 return argv;
1063}
1064
1065static int
1066parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, int *hflag,
1067 unsigned long *n_arg, char **path1, char **path2)
1068{
1069 const char *cmd, *cp = *cpp;
1070 char *cp2, **argv;
1071 int base = 0;
1072 long l;
1073 int i, cmdnum, optidx, argc;
1074
1075 /* Skip leading whitespace */
1076 cp = cp + strspn(cp, WHITESPACE);
1077
1078 /* Ignore blank lines and lines which begin with comment '#' char */
1079 if (*cp == '\0' || *cp == '#')
1080 return (0);
1081
1082 /* Check for leading '-' (disable error processing) */
1083 *iflag = 0;
1084 if (*cp == '-') {
1085 *iflag = 1;
1086 cp++;
1087 }
1088
1089 if ((argv = makeargv(cp, &argc)) == NULL)
1090 return -1;
1091
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;
1096 }
1097 cmdnum = cmds[i].n;
1098 cmd = cmds[i].c;
1099
1100 /* Special case */
1101 if (*cp == '!') {
1102 cp++;
1103 cmdnum = I_SHELL;
1104 } else if (cmdnum == -1) {
1105 error("Invalid command.");
1106 return -1;
1107 }
1108
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;
1123 }
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);
1130 }
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;
1138 }
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;
1156 }
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);
1172 }
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;
1204 }
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;
1213 }
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");
1225 }
1226
1227 *cpp = cp;
1228 return(cmdnum);
1229}
1230
1231static int
1232parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1233 int err_abort)
1234{
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;
1242
1243 path1 = path2 = NULL;
1244 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1245 &path1, &path2);
1246
1247 if (iflag != 0)
1248 err_abort = 0;
1249
1250 memset(&g, 0, sizeof(g));
1251
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;
1284 }
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;
1302 }
1303 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1304 xfree(tmp);
1305 err = 1;
1306 break;
1307 }
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;
1313 }
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;
1320 }
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;
1328 }
1329
1330 /* Strip pwd off beginning of non-absolute paths */
1331 tmp = NULL;
1332 if (*path1 != '/')
1333 tmp = *pwd;
1334
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;
1350 }
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;
1357 }
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;
1380 }
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;
1393 }
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;
1402 }
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;
1410 }
1411 err = do_setstat(conn, g.gl_pathv[i], aa);
1412 if (err != 0 && err_abort)
1413 break;
1414 }
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;
1424 }
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);
1445 }
1446
1447 if (g.gl_pathc)
1448 globfree(&g);
1449 if (path1)
1450 xfree(path1);
1451 if (path2)
1452 xfree(path2);
1453
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);
1459
1460 return (0);
1461}
1462
1463#ifdef USE_LIBEDIT
1464static char *
1465prompt(EditLine *el)
1466{
1467 return ("sftp> ");
1468}
1469#endif
1470
1471int
1472interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1473{
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;
1483
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);
1491
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);
1497 }
1498#endif /* USE_LIBEDIT */
1499
1500 pwd = do_realpath(conn, ".");
1501 if (pwd == NULL)
1502 fatal("Need cwd");
1503
1504 if (file1 != NULL) {
1505 dir = xstrdup(file1);
1506 dir = make_absolute(dir, pwd);
1507
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);
1516 }
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);
1523
1524 err = parse_dispatch_command(conn, cmd, &pwd, 1);
1525 xfree(dir);
1526 xfree(pwd);
1527 xfree(conn);
1528 return (err);
1529 }
1530 xfree(dir);
1531 }
1532
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
1540
1541 interactive = !batchmode && isatty(STDIN_FILENO);
1542 err = 0;
1543 for (;;) {
1544 char *cp;
1545
1546 signal(SIGINT, SIG_IGN);
1547
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;
1555 }
1556 if (!interactive) { /* Echo command */
1557 printf("sftp> %s", cmd);
1558 if (strlen(cmd) > 0 &&
1559 cmd[strlen(cmd) - 1] != '\n')
1560 printf("\n");
1561 }
1562 } else {
1563#ifdef USE_LIBEDIT
1564 const char *line;
1565 int count = 0;
1566
1567 if ((line = el_gets(el, &count)) == NULL || count <= 0) {
1568 printf("\n");
1569 break;
1570 }
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;
1575 }
1576#endif /* USE_LIBEDIT */
1577 }
1578
1579 cp = strrchr(cmd, '\n');
1580 if (cp)
1581 *cp = '\0';
1582
1583 /* Handle user interrupts gracefully during commands */
1584 interrupted = 0;
1585 signal(SIGINT, cmd_interrupt);
1586
1587 err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
1588 if (err != 0)
1589 break;
1590 }
1591 xfree(pwd);
1592 xfree(conn);
1593
1594#ifdef USE_LIBEDIT
1595 if (el != NULL)
1596 el_end(el);
1597#endif /* USE_LIBEDIT */
1598
1599 /* err == 1 signifies normal "quit" exit */
1600 return (err >= 0 ? 0 : -1);
1601}
1602
1603static void
1604connect_to_server(char *path, char **args, int *in, int *out)
1605{
1606 int c_in, c_out;
1607
1608#ifdef USE_PIPES
1609 int pin[2], pout[2];
1610
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];
1619
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 */
1625
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);
1633 }
1634 close(*in);
1635 close(*out);
1636 close(c_in);
1637 close(c_out);
1638
1639 /*
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
1644 */
1645 signal(SIGINT, SIG_IGN);
1646 execvp(path, args);
1647 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
1648 _exit(1);
1649 }
1650
1651 signal(SIGTERM, killchild);
1652 signal(SIGINT, killchild);
1653 signal(SIGHUP, killchild);
1654 close(c_in);
1655 close(c_out);
1656}
1657
1658static void
1659usage(void)
1660{
1661 extern char *__progname;
1662
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);
1675}
1676
1677int
1678main(int argc, char **argv)
1679{
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;
1692
1693 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1694 sanitise_stdfd();
1695
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");
1704
1705 ll = SYSLOG_LEVEL_INFO;
1706 infile = stdin;
1707
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;
1736 }
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.");
1755
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();
1789 }
1790 }
1791
1792 if (!isatty(STDERR_FILENO))
1793 showprogress = 0;
1794
1795 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
1796
1797 if (sftp_direct == NULL) {
1798 if (optind == argc || argc > (optind + 2))
1799 usage();
1800
1801 userhost = xstrdup(argv[optind]);
1802 file2 = argv[optind+1];
1803
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();
1811 }
1812 addargs(&args, "-l%s", userhost);
1813 }
1814
1815 if ((cp = colon(host)) != NULL) {
1816 *cp++ = '\0';
1817 file1 = cp;
1818 }
1819
1820 host = cleanhostname(host);
1821 if (!*host) {
1822 fprintf(stderr, "Missing hostname\n");
1823 usage();
1824 }
1825
1826 addargs(&args, "-oProtocol %d", sshver);
1827
1828 /* no subsystem if the server-spec contains a '/' */
1829 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
1830 addargs(&args, "-s");
1831
1832 addargs(&args, "%s", host);
1833 addargs(&args, "%s", (sftp_server != NULL ?
1834 sftp_server : "sftp"));
1835
1836 connect_to_server(ssh_program, args.list, &in, &out);
1837 } else {
1838 args.list = NULL;
1839 addargs(&args, "sftp-server");
1840
1841 connect_to_server(sftp_direct, args.list, &in, &out);
1842 }
1843 freeargs(&args);
1844
1845 conn = do_init(in, out, copy_buffer_len, num_requests);
1846 if (conn == NULL)
1847 fatal("Couldn't initialise connection to server");
1848
1849 if (!batchmode) {
1850 if (sftp_direct == NULL)
1851 fprintf(stderr, "Connected to %s.\n", host);
1852 else
1853 fprintf(stderr, "Attached to %s.\n", sftp_direct);
1854 }
1855
1856 err = interactive_loop(conn, file1, file2);
1857
1858#if !defined(USE_PIPES)
1859 shutdown(in, SHUT_RDWR);
1860 shutdown(out, SHUT_RDWR);
1861#endif
1862
1863 close(in);
1864 close(out);
1865 if (batchmode)
1866 fclose(infile);
1867
1868 while (waitpid(sshpid, NULL, 0) == -1)
1869 if (errno != EINTR)
1870 fatal("Couldn't wait for ssh process: %s",
1871 strerror(errno));
1872
1873 exit(err == 0 ? 0 : 1);
1874}
This page took 0.07425 seconds and 5 git commands to generate.