]> andersk Git - openssh.git/blame_incremental - sftp.c
- (dtucker) [openbsd-compat/readpassphrase.c] Update to OpenBSD's r1.21.
[openssh.git] / sftp.c
... / ...
CommitLineData
1/* $OpenBSD: sftp.c,v 1.118 2010/01/09 11:13:02 dtucker 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
98/* Context used for commandline completion */
99struct complete_ctx {
100 struct sftp_conn *conn;
101 char **remote_pathp;
102};
103
104int remote_glob(struct sftp_conn *, const char *, int,
105 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
106
107extern char *__progname;
108
109/* Separators for interactive commands */
110#define WHITESPACE " \t\r\n"
111
112/* ls flags */
113#define LS_LONG_VIEW 0x01 /* Full view ala ls -l */
114#define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */
115#define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */
116#define LS_NAME_SORT 0x08 /* Sort by name (default) */
117#define LS_TIME_SORT 0x10 /* Sort by mtime */
118#define LS_SIZE_SORT 0x20 /* Sort by file size */
119#define LS_REVERSE_SORT 0x40 /* Reverse sort order */
120#define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */
121
122#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
123#define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
124
125/* Commands for interactive mode */
126#define I_CHDIR 1
127#define I_CHGRP 2
128#define I_CHMOD 3
129#define I_CHOWN 4
130#define I_DF 24
131#define I_GET 5
132#define I_HELP 6
133#define I_LCHDIR 7
134#define I_LLS 8
135#define I_LMKDIR 9
136#define I_LPWD 10
137#define I_LS 11
138#define I_LUMASK 12
139#define I_MKDIR 13
140#define I_PUT 14
141#define I_PWD 15
142#define I_QUIT 16
143#define I_RENAME 17
144#define I_RM 18
145#define I_RMDIR 19
146#define I_SHELL 20
147#define I_SYMLINK 21
148#define I_VERSION 22
149#define I_PROGRESS 23
150
151struct CMD {
152 const char *c;
153 const int n;
154 const int t;
155};
156
157/* Type of completion */
158#define NOARGS 0
159#define REMOTE 1
160#define LOCAL 2
161
162static const struct CMD cmds[] = {
163 { "bye", I_QUIT, NOARGS },
164 { "cd", I_CHDIR, REMOTE },
165 { "chdir", I_CHDIR, REMOTE },
166 { "chgrp", I_CHGRP, REMOTE },
167 { "chmod", I_CHMOD, REMOTE },
168 { "chown", I_CHOWN, REMOTE },
169 { "df", I_DF, REMOTE },
170 { "dir", I_LS, REMOTE },
171 { "exit", I_QUIT, NOARGS },
172 { "get", I_GET, REMOTE },
173 { "help", I_HELP, NOARGS },
174 { "lcd", I_LCHDIR, LOCAL },
175 { "lchdir", I_LCHDIR, LOCAL },
176 { "lls", I_LLS, LOCAL },
177 { "lmkdir", I_LMKDIR, LOCAL },
178 { "ln", I_SYMLINK, REMOTE },
179 { "lpwd", I_LPWD, LOCAL },
180 { "ls", I_LS, REMOTE },
181 { "lumask", I_LUMASK, NOARGS },
182 { "mkdir", I_MKDIR, REMOTE },
183 { "progress", I_PROGRESS, NOARGS },
184 { "put", I_PUT, LOCAL },
185 { "pwd", I_PWD, REMOTE },
186 { "quit", I_QUIT, NOARGS },
187 { "rename", I_RENAME, REMOTE },
188 { "rm", I_RM, REMOTE },
189 { "rmdir", I_RMDIR, REMOTE },
190 { "symlink", I_SYMLINK, REMOTE },
191 { "version", I_VERSION, NOARGS },
192 { "!", I_SHELL, NOARGS },
193 { "?", I_HELP, NOARGS },
194 { NULL, -1, -1 }
195};
196
197int interactive_loop(struct sftp_conn *, char *file1, char *file2);
198
199/* ARGSUSED */
200static void
201killchild(int signo)
202{
203 if (sshpid > 1) {
204 kill(sshpid, SIGTERM);
205 waitpid(sshpid, NULL, 0);
206 }
207
208 _exit(1);
209}
210
211/* ARGSUSED */
212static void
213cmd_interrupt(int signo)
214{
215 const char msg[] = "\rInterrupt \n";
216 int olderrno = errno;
217
218 write(STDERR_FILENO, msg, sizeof(msg) - 1);
219 interrupted = 1;
220 errno = olderrno;
221}
222
223static void
224help(void)
225{
226 printf("Available commands:\n"
227 "bye Quit sftp\n"
228 "cd path Change remote directory to 'path'\n"
229 "chgrp grp path Change group of file 'path' to 'grp'\n"
230 "chmod mode path Change permissions of file 'path' to 'mode'\n"
231 "chown own path Change owner of file 'path' to 'own'\n"
232 "df [-hi] [path] Display statistics for current directory or\n"
233 " filesystem containing 'path'\n"
234 "exit Quit sftp\n"
235 "get [-Pr] remote-path [local-path] Download file\n"
236 "help Display this help text\n"
237 "lcd path Change local directory to 'path'\n"
238 "lls [ls-options [path]] Display local directory listing\n"
239 "lmkdir path Create local directory\n"
240 "ln oldpath newpath Symlink remote file\n"
241 "lpwd Print local working directory\n"
242 "ls [-1aflnrSt] [path] Display remote directory listing\n"
243 "lumask umask Set local umask to 'umask'\n"
244 "mkdir path Create remote directory\n"
245 "progress Toggle display of progress meter\n"
246 "put [-Pr] local-path [remote-path] Upload file\n"
247 "pwd Display remote working directory\n"
248 "quit Quit sftp\n"
249 "rename oldpath newpath Rename remote file\n"
250 "rm path Delete remote file\n"
251 "rmdir path Remove remote directory\n"
252 "symlink oldpath newpath Symlink remote file\n"
253 "version Show SFTP version\n"
254 "!command Execute 'command' in local shell\n"
255 "! Escape to local shell\n"
256 "? Synonym for help\n");
257}
258
259static void
260local_do_shell(const char *args)
261{
262 int status;
263 char *shell;
264 pid_t pid;
265
266 if (!*args)
267 args = NULL;
268
269 if ((shell = getenv("SHELL")) == NULL)
270 shell = _PATH_BSHELL;
271
272 if ((pid = fork()) == -1)
273 fatal("Couldn't fork: %s", strerror(errno));
274
275 if (pid == 0) {
276 /* XXX: child has pipe fds to ssh subproc open - issue? */
277 if (args) {
278 debug3("Executing %s -c \"%s\"", shell, args);
279 execl(shell, shell, "-c", args, (char *)NULL);
280 } else {
281 debug3("Executing %s", shell);
282 execl(shell, shell, (char *)NULL);
283 }
284 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
285 strerror(errno));
286 _exit(1);
287 }
288 while (waitpid(pid, &status, 0) == -1)
289 if (errno != EINTR)
290 fatal("Couldn't wait for child: %s", strerror(errno));
291 if (!WIFEXITED(status))
292 error("Shell exited abnormally");
293 else if (WEXITSTATUS(status))
294 error("Shell exited with status %d", WEXITSTATUS(status));
295}
296
297static void
298local_do_ls(const char *args)
299{
300 if (!args || !*args)
301 local_do_shell(_PATH_LS);
302 else {
303 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
304 char *buf = xmalloc(len);
305
306 /* XXX: quoting - rip quoting code from ftp? */
307 snprintf(buf, len, _PATH_LS " %s", args);
308 local_do_shell(buf);
309 xfree(buf);
310 }
311}
312
313/* Strip one path (usually the pwd) from the start of another */
314static char *
315path_strip(char *path, char *strip)
316{
317 size_t len;
318
319 if (strip == NULL)
320 return (xstrdup(path));
321
322 len = strlen(strip);
323 if (strncmp(path, strip, len) == 0) {
324 if (strip[len - 1] != '/' && path[len] == '/')
325 len++;
326 return (xstrdup(path + len));
327 }
328
329 return (xstrdup(path));
330}
331
332static char *
333make_absolute(char *p, char *pwd)
334{
335 char *abs_str;
336
337 /* Derelativise */
338 if (p && p[0] != '/') {
339 abs_str = path_append(pwd, p);
340 xfree(p);
341 return(abs_str);
342 } else
343 return(p);
344}
345
346static int
347parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
348 int *rflag)
349{
350 extern int opterr, optind, optopt, optreset;
351 int ch;
352
353 optind = optreset = 1;
354 opterr = 0;
355
356 *rflag = *pflag = 0;
357 while ((ch = getopt(argc, argv, "PpRr")) != -1) {
358 switch (ch) {
359 case 'p':
360 case 'P':
361 *pflag = 1;
362 break;
363 case 'r':
364 case 'R':
365 *rflag = 1;
366 break;
367 default:
368 error("%s: Invalid flag -%c", cmd, optopt);
369 return -1;
370 }
371 }
372
373 return optind;
374}
375
376static int
377parse_ls_flags(char **argv, int argc, int *lflag)
378{
379 extern int opterr, optind, optopt, optreset;
380 int ch;
381
382 optind = optreset = 1;
383 opterr = 0;
384
385 *lflag = LS_NAME_SORT;
386 while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) {
387 switch (ch) {
388 case '1':
389 *lflag &= ~VIEW_FLAGS;
390 *lflag |= LS_SHORT_VIEW;
391 break;
392 case 'S':
393 *lflag &= ~SORT_FLAGS;
394 *lflag |= LS_SIZE_SORT;
395 break;
396 case 'a':
397 *lflag |= LS_SHOW_ALL;
398 break;
399 case 'f':
400 *lflag &= ~SORT_FLAGS;
401 break;
402 case 'l':
403 *lflag &= ~VIEW_FLAGS;
404 *lflag |= LS_LONG_VIEW;
405 break;
406 case 'n':
407 *lflag &= ~VIEW_FLAGS;
408 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
409 break;
410 case 'r':
411 *lflag |= LS_REVERSE_SORT;
412 break;
413 case 't':
414 *lflag &= ~SORT_FLAGS;
415 *lflag |= LS_TIME_SORT;
416 break;
417 default:
418 error("ls: Invalid flag -%c", optopt);
419 return -1;
420 }
421 }
422
423 return optind;
424}
425
426static int
427parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
428{
429 extern int opterr, optind, optopt, optreset;
430 int ch;
431
432 optind = optreset = 1;
433 opterr = 0;
434
435 *hflag = *iflag = 0;
436 while ((ch = getopt(argc, argv, "hi")) != -1) {
437 switch (ch) {
438 case 'h':
439 *hflag = 1;
440 break;
441 case 'i':
442 *iflag = 1;
443 break;
444 default:
445 error("%s: Invalid flag -%c", cmd, optopt);
446 return -1;
447 }
448 }
449
450 return optind;
451}
452
453static int
454is_dir(char *path)
455{
456 struct stat sb;
457
458 /* XXX: report errors? */
459 if (stat(path, &sb) == -1)
460 return(0);
461
462 return(S_ISDIR(sb.st_mode));
463}
464
465static int
466remote_is_dir(struct sftp_conn *conn, char *path)
467{
468 Attrib *a;
469
470 /* XXX: report errors? */
471 if ((a = do_stat(conn, path, 1)) == NULL)
472 return(0);
473 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
474 return(0);
475 return(S_ISDIR(a->perm));
476}
477
478/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
479static int
480pathname_is_dir(char *pathname)
481{
482 size_t l = strlen(pathname);
483
484 return l > 0 && pathname[l - 1] == '/';
485}
486
487static int
488process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
489 int pflag, int rflag)
490{
491 char *abs_src = NULL;
492 char *abs_dst = NULL;
493 glob_t g;
494 char *filename, *tmp=NULL;
495 int i, err = 0;
496
497 abs_src = xstrdup(src);
498 abs_src = make_absolute(abs_src, pwd);
499 memset(&g, 0, sizeof(g));
500
501 debug3("Looking up %s", abs_src);
502 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
503 error("File \"%s\" not found.", abs_src);
504 err = -1;
505 goto out;
506 }
507
508 /*
509 * If multiple matches then dst must be a directory or
510 * unspecified.
511 */
512 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
513 error("Multiple source paths, but destination "
514 "\"%s\" is not a directory", dst);
515 err = -1;
516 goto out;
517 }
518
519 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
520 tmp = xstrdup(g.gl_pathv[i]);
521 if ((filename = basename(tmp)) == NULL) {
522 error("basename %s: %s", tmp, strerror(errno));
523 xfree(tmp);
524 err = -1;
525 goto out;
526 }
527
528 if (g.gl_matchc == 1 && dst) {
529 if (is_dir(dst)) {
530 abs_dst = path_append(dst, filename);
531 } else {
532 abs_dst = xstrdup(dst);
533 }
534 } else if (dst) {
535 abs_dst = path_append(dst, filename);
536 } else {
537 abs_dst = xstrdup(filename);
538 }
539 xfree(tmp);
540
541 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
542 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
543 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
544 pflag || global_pflag, 1) == -1)
545 err = -1;
546 } else {
547 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
548 pflag || global_pflag) == -1)
549 err = -1;
550 }
551 xfree(abs_dst);
552 abs_dst = NULL;
553 }
554
555out:
556 xfree(abs_src);
557 globfree(&g);
558 return(err);
559}
560
561static int
562process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
563 int pflag, int rflag)
564{
565 char *tmp_dst = NULL;
566 char *abs_dst = NULL;
567 char *tmp = NULL, *filename = NULL;
568 glob_t g;
569 int err = 0;
570 int i, dst_is_dir = 1;
571 struct stat sb;
572
573 if (dst) {
574 tmp_dst = xstrdup(dst);
575 tmp_dst = make_absolute(tmp_dst, pwd);
576 }
577
578 memset(&g, 0, sizeof(g));
579 debug3("Looking up %s", src);
580 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
581 error("File \"%s\" not found.", src);
582 err = -1;
583 goto out;
584 }
585
586 /* If we aren't fetching to pwd then stash this status for later */
587 if (tmp_dst != NULL)
588 dst_is_dir = remote_is_dir(conn, tmp_dst);
589
590 /* If multiple matches, dst may be directory or unspecified */
591 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
592 error("Multiple paths match, but destination "
593 "\"%s\" is not a directory", tmp_dst);
594 err = -1;
595 goto out;
596 }
597
598 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
599 if (stat(g.gl_pathv[i], &sb) == -1) {
600 err = -1;
601 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
602 continue;
603 }
604
605 tmp = xstrdup(g.gl_pathv[i]);
606 if ((filename = basename(tmp)) == NULL) {
607 error("basename %s: %s", tmp, strerror(errno));
608 xfree(tmp);
609 err = -1;
610 goto out;
611 }
612
613 if (g.gl_matchc == 1 && tmp_dst) {
614 /* If directory specified, append filename */
615 if (dst_is_dir)
616 abs_dst = path_append(tmp_dst, filename);
617 else
618 abs_dst = xstrdup(tmp_dst);
619 } else if (tmp_dst) {
620 abs_dst = path_append(tmp_dst, filename);
621 } else {
622 abs_dst = make_absolute(xstrdup(filename), pwd);
623 }
624 xfree(tmp);
625
626 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
627 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
628 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
629 pflag || global_pflag, 1) == -1)
630 err = -1;
631 } else {
632 if (do_upload(conn, g.gl_pathv[i], abs_dst,
633 pflag || global_pflag) == -1)
634 err = -1;
635 }
636 }
637
638out:
639 if (abs_dst)
640 xfree(abs_dst);
641 if (tmp_dst)
642 xfree(tmp_dst);
643 globfree(&g);
644 return(err);
645}
646
647static int
648sdirent_comp(const void *aa, const void *bb)
649{
650 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
651 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
652 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
653
654#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
655 if (sort_flag & LS_NAME_SORT)
656 return (rmul * strcmp(a->filename, b->filename));
657 else if (sort_flag & LS_TIME_SORT)
658 return (rmul * NCMP(a->a.mtime, b->a.mtime));
659 else if (sort_flag & LS_SIZE_SORT)
660 return (rmul * NCMP(a->a.size, b->a.size));
661
662 fatal("Unknown ls sort type");
663}
664
665/* sftp ls.1 replacement for directories */
666static int
667do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
668{
669 int n;
670 u_int c = 1, colspace = 0, columns = 1;
671 SFTP_DIRENT **d;
672
673 if ((n = do_readdir(conn, path, &d)) != 0)
674 return (n);
675
676 if (!(lflag & LS_SHORT_VIEW)) {
677 u_int m = 0, width = 80;
678 struct winsize ws;
679 char *tmp;
680
681 /* Count entries for sort and find longest filename */
682 for (n = 0; d[n] != NULL; n++) {
683 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
684 m = MAX(m, strlen(d[n]->filename));
685 }
686
687 /* Add any subpath that also needs to be counted */
688 tmp = path_strip(path, strip_path);
689 m += strlen(tmp);
690 xfree(tmp);
691
692 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
693 width = ws.ws_col;
694
695 columns = width / (m + 2);
696 columns = MAX(columns, 1);
697 colspace = width / columns;
698 colspace = MIN(colspace, width);
699 }
700
701 if (lflag & SORT_FLAGS) {
702 for (n = 0; d[n] != NULL; n++)
703 ; /* count entries */
704 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
705 qsort(d, n, sizeof(*d), sdirent_comp);
706 }
707
708 for (n = 0; d[n] != NULL && !interrupted; n++) {
709 char *tmp, *fname;
710
711 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
712 continue;
713
714 tmp = path_append(path, d[n]->filename);
715 fname = path_strip(tmp, strip_path);
716 xfree(tmp);
717
718 if (lflag & LS_LONG_VIEW) {
719 if (lflag & LS_NUMERIC_VIEW) {
720 char *lname;
721 struct stat sb;
722
723 memset(&sb, 0, sizeof(sb));
724 attrib_to_stat(&d[n]->a, &sb);
725 lname = ls_file(fname, &sb, 1);
726 printf("%s\n", lname);
727 xfree(lname);
728 } else
729 printf("%s\n", d[n]->longname);
730 } else {
731 printf("%-*s", colspace, fname);
732 if (c >= columns) {
733 printf("\n");
734 c = 1;
735 } else
736 c++;
737 }
738
739 xfree(fname);
740 }
741
742 if (!(lflag & LS_LONG_VIEW) && (c != 1))
743 printf("\n");
744
745 free_sftp_dirents(d);
746 return (0);
747}
748
749/* sftp ls.1 replacement which handles path globs */
750static int
751do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
752 int lflag)
753{
754 glob_t g;
755 u_int i, c = 1, colspace = 0, columns = 1;
756 Attrib *a = NULL;
757
758 memset(&g, 0, sizeof(g));
759
760 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
761 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
762 if (g.gl_pathc)
763 globfree(&g);
764 error("Can't ls: \"%s\" not found", path);
765 return (-1);
766 }
767
768 if (interrupted)
769 goto out;
770
771 /*
772 * If the glob returns a single match and it is a directory,
773 * then just list its contents.
774 */
775 if (g.gl_matchc == 1) {
776 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
777 globfree(&g);
778 return (-1);
779 }
780 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
781 S_ISDIR(a->perm)) {
782 int err;
783
784 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
785 globfree(&g);
786 return (err);
787 }
788 }
789
790 if (!(lflag & LS_SHORT_VIEW)) {
791 u_int m = 0, width = 80;
792 struct winsize ws;
793
794 /* Count entries for sort and find longest filename */
795 for (i = 0; g.gl_pathv[i]; i++)
796 m = MAX(m, strlen(g.gl_pathv[i]));
797
798 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
799 width = ws.ws_col;
800
801 columns = width / (m + 2);
802 columns = MAX(columns, 1);
803 colspace = width / columns;
804 }
805
806 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
807 char *fname;
808
809 fname = path_strip(g.gl_pathv[i], strip_path);
810
811 if (lflag & LS_LONG_VIEW) {
812 char *lname;
813 struct stat sb;
814
815 /*
816 * XXX: this is slow - 1 roundtrip per path
817 * A solution to this is to fork glob() and
818 * build a sftp specific version which keeps the
819 * attribs (which currently get thrown away)
820 * that the server returns as well as the filenames.
821 */
822 memset(&sb, 0, sizeof(sb));
823 if (a == NULL)
824 a = do_lstat(conn, g.gl_pathv[i], 1);
825 if (a != NULL)
826 attrib_to_stat(a, &sb);
827 lname = ls_file(fname, &sb, 1);
828 printf("%s\n", lname);
829 xfree(lname);
830 } else {
831 printf("%-*s", colspace, fname);
832 if (c >= columns) {
833 printf("\n");
834 c = 1;
835 } else
836 c++;
837 }
838 xfree(fname);
839 }
840
841 if (!(lflag & LS_LONG_VIEW) && (c != 1))
842 printf("\n");
843
844 out:
845 if (g.gl_pathc)
846 globfree(&g);
847
848 return (0);
849}
850
851static int
852do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
853{
854 struct sftp_statvfs st;
855 char s_used[FMT_SCALED_STRSIZE];
856 char s_avail[FMT_SCALED_STRSIZE];
857 char s_root[FMT_SCALED_STRSIZE];
858 char s_total[FMT_SCALED_STRSIZE];
859 unsigned long long ffree;
860
861 if (do_statvfs(conn, path, &st, 1) == -1)
862 return -1;
863 if (iflag) {
864 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
865 printf(" Inodes Used Avail "
866 "(root) %%Capacity\n");
867 printf("%11llu %11llu %11llu %11llu %3llu%%\n",
868 (unsigned long long)st.f_files,
869 (unsigned long long)(st.f_files - st.f_ffree),
870 (unsigned long long)st.f_favail,
871 (unsigned long long)st.f_ffree, ffree);
872 } else if (hflag) {
873 strlcpy(s_used, "error", sizeof(s_used));
874 strlcpy(s_avail, "error", sizeof(s_avail));
875 strlcpy(s_root, "error", sizeof(s_root));
876 strlcpy(s_total, "error", sizeof(s_total));
877 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
878 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
879 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
880 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
881 printf(" Size Used Avail (root) %%Capacity\n");
882 printf("%7sB %7sB %7sB %7sB %3llu%%\n",
883 s_total, s_used, s_avail, s_root,
884 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
885 st.f_blocks));
886 } else {
887 printf(" Size Used Avail "
888 "(root) %%Capacity\n");
889 printf("%12llu %12llu %12llu %12llu %3llu%%\n",
890 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
891 (unsigned long long)(st.f_frsize *
892 (st.f_blocks - st.f_bfree) / 1024),
893 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
894 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
895 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
896 st.f_blocks));
897 }
898 return 0;
899}
900
901/*
902 * Undo escaping of glob sequences in place. Used to undo extra escaping
903 * applied in makeargv() when the string is destined for a function that
904 * does not glob it.
905 */
906static void
907undo_glob_escape(char *s)
908{
909 size_t i, j;
910
911 for (i = j = 0;;) {
912 if (s[i] == '\0') {
913 s[j] = '\0';
914 return;
915 }
916 if (s[i] != '\\') {
917 s[j++] = s[i++];
918 continue;
919 }
920 /* s[i] == '\\' */
921 ++i;
922 switch (s[i]) {
923 case '?':
924 case '[':
925 case '*':
926 case '\\':
927 s[j++] = s[i++];
928 break;
929 case '\0':
930 s[j++] = '\\';
931 s[j] = '\0';
932 return;
933 default:
934 s[j++] = '\\';
935 s[j++] = s[i++];
936 break;
937 }
938 }
939}
940
941/*
942 * Split a string into an argument vector using sh(1)-style quoting,
943 * comment and escaping rules, but with some tweaks to handle glob(3)
944 * wildcards.
945 * The "sloppy" flag allows for recovery from missing terminating quote, for
946 * use in parsing incomplete commandlines during tab autocompletion.
947 *
948 * Returns NULL on error or a NULL-terminated array of arguments.
949 *
950 * If "lastquote" is not NULL, the quoting character used for the last
951 * argument is placed in *lastquote ("\0", "'" or "\"").
952 *
953 * If "terminated" is not NULL, *terminated will be set to 1 when the
954 * last argument's quote has been properly terminated or 0 otherwise.
955 * This parameter is only of use if "sloppy" is set.
956 */
957#define MAXARGS 128
958#define MAXARGLEN 8192
959static char **
960makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
961 u_int *terminated)
962{
963 int argc, quot;
964 size_t i, j;
965 static char argvs[MAXARGLEN];
966 static char *argv[MAXARGS + 1];
967 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
968
969 *argcp = argc = 0;
970 if (strlen(arg) > sizeof(argvs) - 1) {
971 args_too_longs:
972 error("string too long");
973 return NULL;
974 }
975 if (terminated != NULL)
976 *terminated = 1;
977 if (lastquote != NULL)
978 *lastquote = '\0';
979 state = MA_START;
980 i = j = 0;
981 for (;;) {
982 if (isspace(arg[i])) {
983 if (state == MA_UNQUOTED) {
984 /* Terminate current argument */
985 argvs[j++] = '\0';
986 argc++;
987 state = MA_START;
988 } else if (state != MA_START)
989 argvs[j++] = arg[i];
990 } else if (arg[i] == '"' || arg[i] == '\'') {
991 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
992 if (state == MA_START) {
993 argv[argc] = argvs + j;
994 state = q;
995 if (lastquote != NULL)
996 *lastquote = arg[i];
997 } else if (state == MA_UNQUOTED)
998 state = q;
999 else if (state == q)
1000 state = MA_UNQUOTED;
1001 else
1002 argvs[j++] = arg[i];
1003 } else if (arg[i] == '\\') {
1004 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1005 quot = state == MA_SQUOTE ? '\'' : '"';
1006 /* Unescape quote we are in */
1007 /* XXX support \n and friends? */
1008 if (arg[i + 1] == quot) {
1009 i++;
1010 argvs[j++] = arg[i];
1011 } else if (arg[i + 1] == '?' ||
1012 arg[i + 1] == '[' || arg[i + 1] == '*') {
1013 /*
1014 * Special case for sftp: append
1015 * double-escaped glob sequence -
1016 * glob will undo one level of
1017 * escaping. NB. string can grow here.
1018 */
1019 if (j >= sizeof(argvs) - 5)
1020 goto args_too_longs;
1021 argvs[j++] = '\\';
1022 argvs[j++] = arg[i++];
1023 argvs[j++] = '\\';
1024 argvs[j++] = arg[i];
1025 } else {
1026 argvs[j++] = arg[i++];
1027 argvs[j++] = arg[i];
1028 }
1029 } else {
1030 if (state == MA_START) {
1031 argv[argc] = argvs + j;
1032 state = MA_UNQUOTED;
1033 if (lastquote != NULL)
1034 *lastquote = '\0';
1035 }
1036 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1037 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1038 /*
1039 * Special case for sftp: append
1040 * escaped glob sequence -
1041 * glob will undo one level of
1042 * escaping.
1043 */
1044 argvs[j++] = arg[i++];
1045 argvs[j++] = arg[i];
1046 } else {
1047 /* Unescape everything */
1048 /* XXX support \n and friends? */
1049 i++;
1050 argvs[j++] = arg[i];
1051 }
1052 }
1053 } else if (arg[i] == '#') {
1054 if (state == MA_SQUOTE || state == MA_DQUOTE)
1055 argvs[j++] = arg[i];
1056 else
1057 goto string_done;
1058 } else if (arg[i] == '\0') {
1059 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1060 if (sloppy) {
1061 state = MA_UNQUOTED;
1062 if (terminated != NULL)
1063 *terminated = 0;
1064 goto string_done;
1065 }
1066 error("Unterminated quoted argument");
1067 return NULL;
1068 }
1069 string_done:
1070 if (state == MA_UNQUOTED) {
1071 argvs[j++] = '\0';
1072 argc++;
1073 }
1074 break;
1075 } else {
1076 if (state == MA_START) {
1077 argv[argc] = argvs + j;
1078 state = MA_UNQUOTED;
1079 if (lastquote != NULL)
1080 *lastquote = '\0';
1081 }
1082 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1083 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1084 /*
1085 * Special case for sftp: escape quoted
1086 * glob(3) wildcards. NB. string can grow
1087 * here.
1088 */
1089 if (j >= sizeof(argvs) - 3)
1090 goto args_too_longs;
1091 argvs[j++] = '\\';
1092 argvs[j++] = arg[i];
1093 } else
1094 argvs[j++] = arg[i];
1095 }
1096 i++;
1097 }
1098 *argcp = argc;
1099 return argv;
1100}
1101
1102static int
1103parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1104 int *hflag, unsigned long *n_arg, char **path1, char **path2)
1105{
1106 const char *cmd, *cp = *cpp;
1107 char *cp2, **argv;
1108 int base = 0;
1109 long l;
1110 int i, cmdnum, optidx, argc;
1111
1112 /* Skip leading whitespace */
1113 cp = cp + strspn(cp, WHITESPACE);
1114
1115 /* Check for leading '-' (disable error processing) */
1116 *iflag = 0;
1117 if (*cp == '-') {
1118 *iflag = 1;
1119 cp++;
1120 cp = cp + strspn(cp, WHITESPACE);
1121 }
1122
1123 /* Ignore blank lines and lines which begin with comment '#' char */
1124 if (*cp == '\0' || *cp == '#')
1125 return (0);
1126
1127 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1128 return -1;
1129
1130 /* Figure out which command we have */
1131 for (i = 0; cmds[i].c != NULL; i++) {
1132 if (strcasecmp(cmds[i].c, argv[0]) == 0)
1133 break;
1134 }
1135 cmdnum = cmds[i].n;
1136 cmd = cmds[i].c;
1137
1138 /* Special case */
1139 if (*cp == '!') {
1140 cp++;
1141 cmdnum = I_SHELL;
1142 } else if (cmdnum == -1) {
1143 error("Invalid command.");
1144 return -1;
1145 }
1146
1147 /* Get arguments and parse flags */
1148 *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1149 *path1 = *path2 = NULL;
1150 optidx = 1;
1151 switch (cmdnum) {
1152 case I_GET:
1153 case I_PUT:
1154 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
1155 return -1;
1156 /* Get first pathname (mandatory) */
1157 if (argc - optidx < 1) {
1158 error("You must specify at least one path after a "
1159 "%s command.", cmd);
1160 return -1;
1161 }
1162 *path1 = xstrdup(argv[optidx]);
1163 /* Get second pathname (optional) */
1164 if (argc - optidx > 1) {
1165 *path2 = xstrdup(argv[optidx + 1]);
1166 /* Destination is not globbed */
1167 undo_glob_escape(*path2);
1168 }
1169 break;
1170 case I_RENAME:
1171 case I_SYMLINK:
1172 if (argc - optidx < 2) {
1173 error("You must specify two paths after a %s "
1174 "command.", cmd);
1175 return -1;
1176 }
1177 *path1 = xstrdup(argv[optidx]);
1178 *path2 = xstrdup(argv[optidx + 1]);
1179 /* Paths are not globbed */
1180 undo_glob_escape(*path1);
1181 undo_glob_escape(*path2);
1182 break;
1183 case I_RM:
1184 case I_MKDIR:
1185 case I_RMDIR:
1186 case I_CHDIR:
1187 case I_LCHDIR:
1188 case I_LMKDIR:
1189 /* Get pathname (mandatory) */
1190 if (argc - optidx < 1) {
1191 error("You must specify a path after a %s command.",
1192 cmd);
1193 return -1;
1194 }
1195 *path1 = xstrdup(argv[optidx]);
1196 /* Only "rm" globs */
1197 if (cmdnum != I_RM)
1198 undo_glob_escape(*path1);
1199 break;
1200 case I_DF:
1201 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1202 iflag)) == -1)
1203 return -1;
1204 /* Default to current directory if no path specified */
1205 if (argc - optidx < 1)
1206 *path1 = NULL;
1207 else {
1208 *path1 = xstrdup(argv[optidx]);
1209 undo_glob_escape(*path1);
1210 }
1211 break;
1212 case I_LS:
1213 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1214 return(-1);
1215 /* Path is optional */
1216 if (argc - optidx > 0)
1217 *path1 = xstrdup(argv[optidx]);
1218 break;
1219 case I_LLS:
1220 /* Skip ls command and following whitespace */
1221 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1222 case I_SHELL:
1223 /* Uses the rest of the line */
1224 break;
1225 case I_LUMASK:
1226 case I_CHMOD:
1227 base = 8;
1228 case I_CHOWN:
1229 case I_CHGRP:
1230 /* Get numeric arg (mandatory) */
1231 if (argc - optidx < 1)
1232 goto need_num_arg;
1233 errno = 0;
1234 l = strtol(argv[optidx], &cp2, base);
1235 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1236 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1237 l < 0) {
1238 need_num_arg:
1239 error("You must supply a numeric argument "
1240 "to the %s command.", cmd);
1241 return -1;
1242 }
1243 *n_arg = l;
1244 if (cmdnum == I_LUMASK)
1245 break;
1246 /* Get pathname (mandatory) */
1247 if (argc - optidx < 2) {
1248 error("You must specify a path after a %s command.",
1249 cmd);
1250 return -1;
1251 }
1252 *path1 = xstrdup(argv[optidx + 1]);
1253 break;
1254 case I_QUIT:
1255 case I_PWD:
1256 case I_LPWD:
1257 case I_HELP:
1258 case I_VERSION:
1259 case I_PROGRESS:
1260 break;
1261 default:
1262 fatal("Command not implemented");
1263 }
1264
1265 *cpp = cp;
1266 return(cmdnum);
1267}
1268
1269static int
1270parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1271 int err_abort)
1272{
1273 char *path1, *path2, *tmp;
1274 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1275 unsigned long n_arg = 0;
1276 Attrib a, *aa;
1277 char path_buf[MAXPATHLEN];
1278 int err = 0;
1279 glob_t g;
1280
1281 path1 = path2 = NULL;
1282 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
1283 &path1, &path2);
1284
1285 if (iflag != 0)
1286 err_abort = 0;
1287
1288 memset(&g, 0, sizeof(g));
1289
1290 /* Perform command */
1291 switch (cmdnum) {
1292 case 0:
1293 /* Blank line */
1294 break;
1295 case -1:
1296 /* Unrecognized command */
1297 err = -1;
1298 break;
1299 case I_GET:
1300 err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1301 break;
1302 case I_PUT:
1303 err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1304 break;
1305 case I_RENAME:
1306 path1 = make_absolute(path1, *pwd);
1307 path2 = make_absolute(path2, *pwd);
1308 err = do_rename(conn, path1, path2);
1309 break;
1310 case I_SYMLINK:
1311 path2 = make_absolute(path2, *pwd);
1312 err = do_symlink(conn, path1, path2);
1313 break;
1314 case I_RM:
1315 path1 = make_absolute(path1, *pwd);
1316 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1317 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1318 printf("Removing %s\n", g.gl_pathv[i]);
1319 err = do_rm(conn, g.gl_pathv[i]);
1320 if (err != 0 && err_abort)
1321 break;
1322 }
1323 break;
1324 case I_MKDIR:
1325 path1 = make_absolute(path1, *pwd);
1326 attrib_clear(&a);
1327 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1328 a.perm = 0777;
1329 err = do_mkdir(conn, path1, &a, 1);
1330 break;
1331 case I_RMDIR:
1332 path1 = make_absolute(path1, *pwd);
1333 err = do_rmdir(conn, path1);
1334 break;
1335 case I_CHDIR:
1336 path1 = make_absolute(path1, *pwd);
1337 if ((tmp = do_realpath(conn, path1)) == NULL) {
1338 err = 1;
1339 break;
1340 }
1341 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1342 xfree(tmp);
1343 err = 1;
1344 break;
1345 }
1346 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1347 error("Can't change directory: Can't check target");
1348 xfree(tmp);
1349 err = 1;
1350 break;
1351 }
1352 if (!S_ISDIR(aa->perm)) {
1353 error("Can't change directory: \"%s\" is not "
1354 "a directory", tmp);
1355 xfree(tmp);
1356 err = 1;
1357 break;
1358 }
1359 xfree(*pwd);
1360 *pwd = tmp;
1361 break;
1362 case I_LS:
1363 if (!path1) {
1364 do_globbed_ls(conn, *pwd, *pwd, lflag);
1365 break;
1366 }
1367
1368 /* Strip pwd off beginning of non-absolute paths */
1369 tmp = NULL;
1370 if (*path1 != '/')
1371 tmp = *pwd;
1372
1373 path1 = make_absolute(path1, *pwd);
1374 err = do_globbed_ls(conn, path1, tmp, lflag);
1375 break;
1376 case I_DF:
1377 /* Default to current directory if no path specified */
1378 if (path1 == NULL)
1379 path1 = xstrdup(*pwd);
1380 path1 = make_absolute(path1, *pwd);
1381 err = do_df(conn, path1, hflag, iflag);
1382 break;
1383 case I_LCHDIR:
1384 if (chdir(path1) == -1) {
1385 error("Couldn't change local directory to "
1386 "\"%s\": %s", path1, strerror(errno));
1387 err = 1;
1388 }
1389 break;
1390 case I_LMKDIR:
1391 if (mkdir(path1, 0777) == -1) {
1392 error("Couldn't create local directory "
1393 "\"%s\": %s", path1, strerror(errno));
1394 err = 1;
1395 }
1396 break;
1397 case I_LLS:
1398 local_do_ls(cmd);
1399 break;
1400 case I_SHELL:
1401 local_do_shell(cmd);
1402 break;
1403 case I_LUMASK:
1404 umask(n_arg);
1405 printf("Local umask: %03lo\n", n_arg);
1406 break;
1407 case I_CHMOD:
1408 path1 = make_absolute(path1, *pwd);
1409 attrib_clear(&a);
1410 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1411 a.perm = n_arg;
1412 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1413 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1414 printf("Changing mode on %s\n", g.gl_pathv[i]);
1415 err = do_setstat(conn, g.gl_pathv[i], &a);
1416 if (err != 0 && err_abort)
1417 break;
1418 }
1419 break;
1420 case I_CHOWN:
1421 case I_CHGRP:
1422 path1 = make_absolute(path1, *pwd);
1423 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1424 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1425 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1426 if (err_abort) {
1427 err = -1;
1428 break;
1429 } else
1430 continue;
1431 }
1432 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1433 error("Can't get current ownership of "
1434 "remote file \"%s\"", g.gl_pathv[i]);
1435 if (err_abort) {
1436 err = -1;
1437 break;
1438 } else
1439 continue;
1440 }
1441 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1442 if (cmdnum == I_CHOWN) {
1443 printf("Changing owner on %s\n", g.gl_pathv[i]);
1444 aa->uid = n_arg;
1445 } else {
1446 printf("Changing group on %s\n", g.gl_pathv[i]);
1447 aa->gid = n_arg;
1448 }
1449 err = do_setstat(conn, g.gl_pathv[i], aa);
1450 if (err != 0 && err_abort)
1451 break;
1452 }
1453 break;
1454 case I_PWD:
1455 printf("Remote working directory: %s\n", *pwd);
1456 break;
1457 case I_LPWD:
1458 if (!getcwd(path_buf, sizeof(path_buf))) {
1459 error("Couldn't get local cwd: %s", strerror(errno));
1460 err = -1;
1461 break;
1462 }
1463 printf("Local working directory: %s\n", path_buf);
1464 break;
1465 case I_QUIT:
1466 /* Processed below */
1467 break;
1468 case I_HELP:
1469 help();
1470 break;
1471 case I_VERSION:
1472 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1473 break;
1474 case I_PROGRESS:
1475 showprogress = !showprogress;
1476 if (showprogress)
1477 printf("Progress meter enabled\n");
1478 else
1479 printf("Progress meter disabled\n");
1480 break;
1481 default:
1482 fatal("%d is not implemented", cmdnum);
1483 }
1484
1485 if (g.gl_pathc)
1486 globfree(&g);
1487 if (path1)
1488 xfree(path1);
1489 if (path2)
1490 xfree(path2);
1491
1492 /* If an unignored error occurs in batch mode we should abort. */
1493 if (err_abort && err != 0)
1494 return (-1);
1495 else if (cmdnum == I_QUIT)
1496 return (1);
1497
1498 return (0);
1499}
1500
1501#ifdef USE_LIBEDIT
1502static char *
1503prompt(EditLine *el)
1504{
1505 return ("sftp> ");
1506}
1507
1508/* Display entries in 'list' after skipping the first 'len' chars */
1509static void
1510complete_display(char **list, u_int len)
1511{
1512 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1513 struct winsize ws;
1514 char *tmp;
1515
1516 /* Count entries for sort and find longest */
1517 for (y = 0; list[y]; y++)
1518 m = MAX(m, strlen(list[y]));
1519
1520 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1521 width = ws.ws_col;
1522
1523 m = m > len ? m - len : 0;
1524 columns = width / (m + 2);
1525 columns = MAX(columns, 1);
1526 colspace = width / columns;
1527 colspace = MIN(colspace, width);
1528
1529 printf("\n");
1530 m = 1;
1531 for (y = 0; list[y]; y++) {
1532 llen = strlen(list[y]);
1533 tmp = llen > len ? list[y] + len : "";
1534 printf("%-*s", colspace, tmp);
1535 if (m >= columns) {
1536 printf("\n");
1537 m = 1;
1538 } else
1539 m++;
1540 }
1541 printf("\n");
1542}
1543
1544/*
1545 * Given a "list" of words that begin with a common prefix of "word",
1546 * attempt to find an autocompletion to extends "word" by the next
1547 * characters common to all entries in "list".
1548 */
1549static char *
1550complete_ambiguous(const char *word, char **list, size_t count)
1551{
1552 if (word == NULL)
1553 return NULL;
1554
1555 if (count > 0) {
1556 u_int y, matchlen = strlen(list[0]);
1557
1558 /* Find length of common stem */
1559 for (y = 1; list[y]; y++) {
1560 u_int x;
1561
1562 for (x = 0; x < matchlen; x++)
1563 if (list[0][x] != list[y][x])
1564 break;
1565
1566 matchlen = x;
1567 }
1568
1569 if (matchlen > strlen(word)) {
1570 char *tmp = xstrdup(list[0]);
1571
1572 tmp[matchlen] = '\0';
1573 return tmp;
1574 }
1575 }
1576
1577 return xstrdup(word);
1578}
1579
1580/* Autocomplete a sftp command */
1581static int
1582complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1583 int terminated)
1584{
1585 u_int y, count = 0, cmdlen, tmplen;
1586 char *tmp, **list, argterm[3];
1587 const LineInfo *lf;
1588
1589 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1590
1591 /* No command specified: display all available commands */
1592 if (cmd == NULL) {
1593 for (y = 0; cmds[y].c; y++)
1594 list[count++] = xstrdup(cmds[y].c);
1595
1596 list[count] = NULL;
1597 complete_display(list, 0);
1598
1599 for (y = 0; list[y] != NULL; y++)
1600 xfree(list[y]);
1601 xfree(list);
1602 return count;
1603 }
1604
1605 /* Prepare subset of commands that start with "cmd" */
1606 cmdlen = strlen(cmd);
1607 for (y = 0; cmds[y].c; y++) {
1608 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1609 list[count++] = xstrdup(cmds[y].c);
1610 }
1611 list[count] = NULL;
1612
1613 if (count == 0)
1614 return 0;
1615
1616 /* Complete ambigious command */
1617 tmp = complete_ambiguous(cmd, list, count);
1618 if (count > 1)
1619 complete_display(list, 0);
1620
1621 for (y = 0; list[y]; y++)
1622 xfree(list[y]);
1623 xfree(list);
1624
1625 if (tmp != NULL) {
1626 tmplen = strlen(tmp);
1627 cmdlen = strlen(cmd);
1628 /* If cmd may be extended then do so */
1629 if (tmplen > cmdlen)
1630 if (el_insertstr(el, tmp + cmdlen) == -1)
1631 fatal("el_insertstr failed.");
1632 lf = el_line(el);
1633 /* Terminate argument cleanly */
1634 if (count == 1) {
1635 y = 0;
1636 if (!terminated)
1637 argterm[y++] = quote;
1638 if (lastarg || *(lf->cursor) != ' ')
1639 argterm[y++] = ' ';
1640 argterm[y] = '\0';
1641 if (y > 0 && el_insertstr(el, argterm) == -1)
1642 fatal("el_insertstr failed.");
1643 }
1644 xfree(tmp);
1645 }
1646
1647 return count;
1648}
1649
1650/*
1651 * Determine whether a particular sftp command's arguments (if any)
1652 * represent local or remote files.
1653 */
1654static int
1655complete_is_remote(char *cmd) {
1656 int i;
1657
1658 if (cmd == NULL)
1659 return -1;
1660
1661 for (i = 0; cmds[i].c; i++) {
1662 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1663 return cmds[i].t;
1664 }
1665
1666 return -1;
1667}
1668
1669/* Autocomplete a filename "file" */
1670static int
1671complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1672 char *file, int remote, int lastarg, char quote, int terminated)
1673{
1674 glob_t g;
1675 char *tmp, *tmp2, ins[3];
1676 u_int i, hadglob, pwdlen, len, tmplen, filelen;
1677 const LineInfo *lf;
1678
1679 /* Glob from "file" location */
1680 if (file == NULL)
1681 tmp = xstrdup("*");
1682 else
1683 xasprintf(&tmp, "%s*", file);
1684
1685 memset(&g, 0, sizeof(g));
1686 if (remote != LOCAL) {
1687 tmp = make_absolute(tmp, remote_path);
1688 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1689 } else
1690 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1691
1692 /* Determine length of pwd so we can trim completion display */
1693 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1694 /* Terminate counting on first unescaped glob metacharacter */
1695 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1696 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1697 hadglob = 1;
1698 break;
1699 }
1700 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1701 tmplen++;
1702 if (tmp[tmplen] == '/')
1703 pwdlen = tmplen + 1; /* track last seen '/' */
1704 }
1705 xfree(tmp);
1706
1707 if (g.gl_matchc == 0)
1708 goto out;
1709
1710 if (g.gl_matchc > 1)
1711 complete_display(g.gl_pathv, pwdlen);
1712
1713 tmp = NULL;
1714 /* Don't try to extend globs */
1715 if (file == NULL || hadglob)
1716 goto out;
1717
1718 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1719 tmp = path_strip(tmp2, remote_path);
1720 xfree(tmp2);
1721
1722 if (tmp == NULL)
1723 goto out;
1724
1725 tmplen = strlen(tmp);
1726 filelen = strlen(file);
1727
1728 if (tmplen > filelen) {
1729 tmp2 = tmp + filelen;
1730 len = strlen(tmp2);
1731 /* quote argument on way out */
1732 for (i = 0; i < len; i++) {
1733 ins[0] = '\\';
1734 ins[1] = tmp2[i];
1735 ins[2] = '\0';
1736 switch (tmp2[i]) {
1737 case '\'':
1738 case '"':
1739 case '\\':
1740 case '\t':
1741 case ' ':
1742 if (quote == '\0' || tmp2[i] == quote) {
1743 if (el_insertstr(el, ins) == -1)
1744 fatal("el_insertstr "
1745 "failed.");
1746 break;
1747 }
1748 /* FALLTHROUGH */
1749 default:
1750 if (el_insertstr(el, ins + 1) == -1)
1751 fatal("el_insertstr failed.");
1752 break;
1753 }
1754 }
1755 }
1756
1757 lf = el_line(el);
1758 /*
1759 * XXX should we really extend here? the user may not be done if
1760 * the filename is a directory.
1761 */
1762 if (g.gl_matchc == 1) {
1763 i = 0;
1764 if (!terminated)
1765 ins[i++] = quote;
1766 if (lastarg || *(lf->cursor) != ' ')
1767 ins[i++] = ' ';
1768 ins[i] = '\0';
1769 if (i > 0 && el_insertstr(el, ins) == -1)
1770 fatal("el_insertstr failed.");
1771 }
1772 xfree(tmp);
1773
1774 out:
1775 globfree(&g);
1776 return g.gl_matchc;
1777}
1778
1779/* tab-completion hook function, called via libedit */
1780static unsigned char
1781complete(EditLine *el, int ch)
1782{
1783 char **argv, *line, quote;
1784 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1785 const LineInfo *lf;
1786 struct complete_ctx *complete_ctx;
1787
1788 lf = el_line(el);
1789 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
1790 fatal("%s: el_get failed", __func__);
1791
1792 /* Figure out which argument the cursor points to */
1793 cursor = lf->cursor - lf->buffer;
1794 line = (char *)xmalloc(cursor + 1);
1795 memcpy(line, lf->buffer, cursor);
1796 line[cursor] = '\0';
1797 argv = makeargv(line, &carg, 1, &quote, &terminated);
1798 xfree(line);
1799
1800 /* Get all the arguments on the line */
1801 len = lf->lastchar - lf->buffer;
1802 line = (char *)xmalloc(len + 1);
1803 memcpy(line, lf->buffer, len);
1804 line[len] = '\0';
1805 argv = makeargv(line, &argc, 1, NULL, NULL);
1806
1807 /* Ensure cursor is at EOL or a argument boundary */
1808 if (line[cursor] != ' ' && line[cursor] != '\0' &&
1809 line[cursor] != '\n') {
1810 xfree(line);
1811 return ret;
1812 }
1813
1814 if (carg == 0) {
1815 /* Show all available commands */
1816 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1817 ret = CC_REDISPLAY;
1818 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1819 /* Handle the command parsing */
1820 if (complete_cmd_parse(el, argv[0], argc == carg,
1821 quote, terminated) != 0)
1822 ret = CC_REDISPLAY;
1823 } else if (carg >= 1) {
1824 /* Handle file parsing */
1825 int remote = complete_is_remote(argv[0]);
1826 char *filematch = NULL;
1827
1828 if (carg > 1 && line[cursor-1] != ' ')
1829 filematch = argv[carg - 1];
1830
1831 if (remote != 0 &&
1832 complete_match(el, complete_ctx->conn,
1833 *complete_ctx->remote_pathp, filematch,
1834 remote, carg == argc, quote, terminated) != 0)
1835 ret = CC_REDISPLAY;
1836 }
1837
1838 xfree(line);
1839 return ret;
1840}
1841#endif /* USE_LIBEDIT */
1842
1843int
1844interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
1845{
1846 char *remote_path;
1847 char *dir = NULL;
1848 char cmd[2048];
1849 int err, interactive;
1850 EditLine *el = NULL;
1851#ifdef USE_LIBEDIT
1852 History *hl = NULL;
1853 HistEvent hev;
1854 extern char *__progname;
1855 struct complete_ctx complete_ctx;
1856
1857 if (!batchmode && isatty(STDIN_FILENO)) {
1858 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1859 fatal("Couldn't initialise editline");
1860 if ((hl = history_init()) == NULL)
1861 fatal("Couldn't initialise editline history");
1862 history(hl, &hev, H_SETSIZE, 100);
1863 el_set(el, EL_HIST, history, hl);
1864
1865 el_set(el, EL_PROMPT, prompt);
1866 el_set(el, EL_EDITOR, "emacs");
1867 el_set(el, EL_TERMINAL, NULL);
1868 el_set(el, EL_SIGNAL, 1);
1869 el_source(el, NULL);
1870
1871 /* Tab Completion */
1872 el_set(el, EL_ADDFN, "ftp-complete",
1873 "Context senstive argument completion", complete);
1874 complete_ctx.conn = conn;
1875 complete_ctx.remote_pathp = &remote_path;
1876 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1877 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1878 }
1879#endif /* USE_LIBEDIT */
1880
1881 remote_path = do_realpath(conn, ".");
1882 if (remote_path == NULL)
1883 fatal("Need cwd");
1884
1885 if (file1 != NULL) {
1886 dir = xstrdup(file1);
1887 dir = make_absolute(dir, remote_path);
1888
1889 if (remote_is_dir(conn, dir) && file2 == NULL) {
1890 printf("Changing to: %s\n", dir);
1891 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1892 if (parse_dispatch_command(conn, cmd,
1893 &remote_path, 1) != 0) {
1894 xfree(dir);
1895 xfree(remote_path);
1896 xfree(conn);
1897 return (-1);
1898 }
1899 } else {
1900 if (file2 == NULL)
1901 snprintf(cmd, sizeof cmd, "get %s", dir);
1902 else
1903 snprintf(cmd, sizeof cmd, "get %s %s", dir,
1904 file2);
1905
1906 err = parse_dispatch_command(conn, cmd,
1907 &remote_path, 1);
1908 xfree(dir);
1909 xfree(remote_path);
1910 xfree(conn);
1911 return (err);
1912 }
1913 xfree(dir);
1914 }
1915
1916#if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
1917 setvbuf(stdout, NULL, _IOLBF, 0);
1918 setvbuf(infile, NULL, _IOLBF, 0);
1919#else
1920 setlinebuf(stdout);
1921 setlinebuf(infile);
1922#endif
1923
1924 interactive = !batchmode && isatty(STDIN_FILENO);
1925 err = 0;
1926 for (;;) {
1927 char *cp;
1928
1929 signal(SIGINT, SIG_IGN);
1930
1931 if (el == NULL) {
1932 if (interactive)
1933 printf("sftp> ");
1934 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1935 if (interactive)
1936 printf("\n");
1937 break;
1938 }
1939 if (!interactive) { /* Echo command */
1940 printf("sftp> %s", cmd);
1941 if (strlen(cmd) > 0 &&
1942 cmd[strlen(cmd) - 1] != '\n')
1943 printf("\n");
1944 }
1945 } else {
1946#ifdef USE_LIBEDIT
1947 const char *line;
1948 int count = 0;
1949
1950 if ((line = el_gets(el, &count)) == NULL ||
1951 count <= 0) {
1952 printf("\n");
1953 break;
1954 }
1955 history(hl, &hev, H_ENTER, line);
1956 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1957 fprintf(stderr, "Error: input line too long\n");
1958 continue;
1959 }
1960#endif /* USE_LIBEDIT */
1961 }
1962
1963 cp = strrchr(cmd, '\n');
1964 if (cp)
1965 *cp = '\0';
1966
1967 /* Handle user interrupts gracefully during commands */
1968 interrupted = 0;
1969 signal(SIGINT, cmd_interrupt);
1970
1971 err = parse_dispatch_command(conn, cmd, &remote_path,
1972 batchmode);
1973 if (err != 0)
1974 break;
1975 }
1976 xfree(remote_path);
1977 xfree(conn);
1978
1979#ifdef USE_LIBEDIT
1980 if (el != NULL)
1981 el_end(el);
1982#endif /* USE_LIBEDIT */
1983
1984 /* err == 1 signifies normal "quit" exit */
1985 return (err >= 0 ? 0 : -1);
1986}
1987
1988static void
1989connect_to_server(char *path, char **args, int *in, int *out)
1990{
1991 int c_in, c_out;
1992
1993#ifdef USE_PIPES
1994 int pin[2], pout[2];
1995
1996 if ((pipe(pin) == -1) || (pipe(pout) == -1))
1997 fatal("pipe: %s", strerror(errno));
1998 *in = pin[0];
1999 *out = pout[1];
2000 c_in = pout[0];
2001 c_out = pin[1];
2002#else /* USE_PIPES */
2003 int inout[2];
2004
2005 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2006 fatal("socketpair: %s", strerror(errno));
2007 *in = *out = inout[0];
2008 c_in = c_out = inout[1];
2009#endif /* USE_PIPES */
2010
2011 if ((sshpid = fork()) == -1)
2012 fatal("fork: %s", strerror(errno));
2013 else if (sshpid == 0) {
2014 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2015 (dup2(c_out, STDOUT_FILENO) == -1)) {
2016 fprintf(stderr, "dup2: %s\n", strerror(errno));
2017 _exit(1);
2018 }
2019 close(*in);
2020 close(*out);
2021 close(c_in);
2022 close(c_out);
2023
2024 /*
2025 * The underlying ssh is in the same process group, so we must
2026 * ignore SIGINT if we want to gracefully abort commands,
2027 * otherwise the signal will make it to the ssh process and
2028 * kill it too
2029 */
2030 signal(SIGINT, SIG_IGN);
2031 execvp(path, args);
2032 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2033 _exit(1);
2034 }
2035
2036 signal(SIGTERM, killchild);
2037 signal(SIGINT, killchild);
2038 signal(SIGHUP, killchild);
2039 close(c_in);
2040 close(c_out);
2041}
2042
2043static void
2044usage(void)
2045{
2046 extern char *__progname;
2047
2048 fprintf(stderr,
2049 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2050 " [-D sftp_server_path] [-F ssh_config] "
2051 "[-i identity_file]\n"
2052 " [-o ssh_option] [-P port] [-R num_requests] "
2053 "[-S program]\n"
2054 " [-s subsystem | sftp_server] host\n"
2055 " %s [user@]host[:file ...]\n"
2056 " %s [user@]host[:dir[/]]\n"
2057 " %s -b batchfile [user@]host\n",
2058 __progname, __progname, __progname, __progname);
2059 exit(1);
2060}
2061
2062int
2063main(int argc, char **argv)
2064{
2065 int in, out, ch, err;
2066 char *host = NULL, *userhost, *cp, *file2 = NULL;
2067 int debug_level = 0, sshver = 2;
2068 char *file1 = NULL, *sftp_server = NULL;
2069 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2070 LogLevel ll = SYSLOG_LEVEL_INFO;
2071 arglist args;
2072 extern int optind;
2073 extern char *optarg;
2074 struct sftp_conn *conn;
2075 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2076 size_t num_requests = DEFAULT_NUM_REQUESTS;
2077
2078 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2079 sanitise_stdfd();
2080
2081 __progname = ssh_get_progname(argv[0]);
2082 memset(&args, '\0', sizeof(args));
2083 args.list = NULL;
2084 addargs(&args, "%s", ssh_program);
2085 addargs(&args, "-oForwardX11 no");
2086 addargs(&args, "-oForwardAgent no");
2087 addargs(&args, "-oPermitLocalCommand no");
2088 addargs(&args, "-oClearAllForwardings yes");
2089
2090 ll = SYSLOG_LEVEL_INFO;
2091 infile = stdin;
2092
2093 while ((ch = getopt(argc, argv,
2094 "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) {
2095 switch (ch) {
2096 /* Passed through to ssh(1) */
2097 case '4':
2098 case '6':
2099 case 'C':
2100 addargs(&args, "-%c", ch);
2101 break;
2102 /* Passed through to ssh(1) with argument */
2103 case 'F':
2104 case 'c':
2105 case 'i':
2106 case 'o':
2107 addargs(&args, "-%c", ch);
2108 addargs(&args, "%s", optarg);
2109 break;
2110 case 'q':
2111 showprogress = 0;
2112 addargs(&args, "-%c", ch);
2113 break;
2114 case 'P':
2115 addargs(&args, "-oPort %s", optarg);
2116 break;
2117 case 'v':
2118 if (debug_level < 3) {
2119 addargs(&args, "-v");
2120 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2121 }
2122 debug_level++;
2123 break;
2124 case '1':
2125 sshver = 1;
2126 if (sftp_server == NULL)
2127 sftp_server = _PATH_SFTP_SERVER;
2128 break;
2129 case '2':
2130 sshver = 2;
2131 break;
2132 case 'B':
2133 copy_buffer_len = strtol(optarg, &cp, 10);
2134 if (copy_buffer_len == 0 || *cp != '\0')
2135 fatal("Invalid buffer size \"%s\"", optarg);
2136 break;
2137 case 'b':
2138 if (batchmode)
2139 fatal("Batch file already specified.");
2140
2141 /* Allow "-" as stdin */
2142 if (strcmp(optarg, "-") != 0 &&
2143 (infile = fopen(optarg, "r")) == NULL)
2144 fatal("%s (%s).", strerror(errno), optarg);
2145 showprogress = 0;
2146 batchmode = 1;
2147 addargs(&args, "-obatchmode yes");
2148 break;
2149 case 'p':
2150 global_pflag = 1;
2151 break;
2152 case 'D':
2153 sftp_direct = optarg;
2154 break;
2155 case 'r':
2156 global_rflag = 1;
2157 break;
2158 case 'R':
2159 num_requests = strtol(optarg, &cp, 10);
2160 if (num_requests == 0 || *cp != '\0')
2161 fatal("Invalid number of requests \"%s\"",
2162 optarg);
2163 break;
2164 case 's':
2165 sftp_server = optarg;
2166 break;
2167 case 'S':
2168 ssh_program = optarg;
2169 replacearg(&args, 0, "%s", ssh_program);
2170 break;
2171 case 'h':
2172 default:
2173 usage();
2174 }
2175 }
2176
2177 if (!isatty(STDERR_FILENO))
2178 showprogress = 0;
2179
2180 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2181
2182 if (sftp_direct == NULL) {
2183 if (optind == argc || argc > (optind + 2))
2184 usage();
2185
2186 userhost = xstrdup(argv[optind]);
2187 file2 = argv[optind+1];
2188
2189 if ((host = strrchr(userhost, '@')) == NULL)
2190 host = userhost;
2191 else {
2192 *host++ = '\0';
2193 if (!userhost[0]) {
2194 fprintf(stderr, "Missing username\n");
2195 usage();
2196 }
2197 addargs(&args, "-l");
2198 addargs(&args, "%s", userhost);
2199 }
2200
2201 if ((cp = colon(host)) != NULL) {
2202 *cp++ = '\0';
2203 file1 = cp;
2204 }
2205
2206 host = cleanhostname(host);
2207 if (!*host) {
2208 fprintf(stderr, "Missing hostname\n");
2209 usage();
2210 }
2211
2212 addargs(&args, "-oProtocol %d", sshver);
2213
2214 /* no subsystem if the server-spec contains a '/' */
2215 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2216 addargs(&args, "-s");
2217
2218 addargs(&args, "--");
2219 addargs(&args, "%s", host);
2220 addargs(&args, "%s", (sftp_server != NULL ?
2221 sftp_server : "sftp"));
2222
2223 connect_to_server(ssh_program, args.list, &in, &out);
2224 } else {
2225 args.list = NULL;
2226 addargs(&args, "sftp-server");
2227
2228 connect_to_server(sftp_direct, args.list, &in, &out);
2229 }
2230 freeargs(&args);
2231
2232 conn = do_init(in, out, copy_buffer_len, num_requests);
2233 if (conn == NULL)
2234 fatal("Couldn't initialise connection to server");
2235
2236 if (!batchmode) {
2237 if (sftp_direct == NULL)
2238 fprintf(stderr, "Connected to %s.\n", host);
2239 else
2240 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2241 }
2242
2243 err = interactive_loop(conn, file1, file2);
2244
2245#if !defined(USE_PIPES)
2246 shutdown(in, SHUT_RDWR);
2247 shutdown(out, SHUT_RDWR);
2248#endif
2249
2250 close(in);
2251 close(out);
2252 if (batchmode)
2253 fclose(infile);
2254
2255 while (waitpid(sshpid, NULL, 0) == -1)
2256 if (errno != EINTR)
2257 fatal("Couldn't wait for ssh process: %s",
2258 strerror(errno));
2259
2260 exit(err == 0 ? 0 : 1);
2261}
This page took 0.067148 seconds and 5 git commands to generate.