if_vtnet - Use ifsq_watchdog_* functions as the watchdog.
[dragonfly.git] / crypto / openssh / sftp.c
blob67110f738f79e176d46627b4c15c4836b2363a44
1 /* $OpenBSD: sftp.c,v 1.180 2017/06/10 06:33:34 djm Exp $ */
2 /*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include "includes.h"
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_STAT_H
23 # include <sys/stat.h>
24 #endif
25 #include <sys/param.h>
26 #include <sys/socket.h>
27 #include <sys/wait.h>
28 #ifdef HAVE_SYS_STATVFS_H
29 #include <sys/statvfs.h>
30 #endif
32 #include <ctype.h>
33 #include <errno.h>
35 #ifdef HAVE_PATHS_H
36 # include <paths.h>
37 #endif
38 #ifdef HAVE_LIBGEN_H
39 #include <libgen.h>
40 #endif
41 #ifdef HAVE_LOCALE_H
42 # include <locale.h>
43 #endif
44 #ifdef USE_LIBEDIT
45 #include <histedit.h>
46 #else
47 typedef void EditLine;
48 #endif
49 #include <limits.h>
50 #include <signal.h>
51 #include <stdarg.h>
52 #include <stdlib.h>
53 #include <stdio.h>
54 #include <string.h>
55 #include <unistd.h>
56 #include <stdarg.h>
58 #ifdef HAVE_UTIL_H
59 # include <util.h>
60 #endif
62 #include "xmalloc.h"
63 #include "log.h"
64 #include "pathnames.h"
65 #include "misc.h"
66 #include "utf8.h"
68 #include "sftp.h"
69 #include "ssherr.h"
70 #include "sshbuf.h"
71 #include "sftp-common.h"
72 #include "sftp-client.h"
74 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
75 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
77 /* File to read commands from */
78 FILE* infile;
80 /* Are we in batchfile mode? */
81 int batchmode = 0;
83 /* PID of ssh transport process */
84 static pid_t sshpid = -1;
86 /* Suppress diagnositic messages */
87 int quiet = 0;
89 /* This is set to 0 if the progressmeter is not desired. */
90 int showprogress = 1;
92 /* When this option is set, we always recursively download/upload directories */
93 int global_rflag = 0;
95 /* When this option is set, we resume download or upload if possible */
96 int global_aflag = 0;
98 /* When this option is set, the file transfers will always preserve times */
99 int global_pflag = 0;
101 /* When this option is set, transfers will have fsync() called on each file */
102 int global_fflag = 0;
104 /* SIGINT received during command processing */
105 volatile sig_atomic_t interrupted = 0;
107 /* I wish qsort() took a separate ctx for the comparison function...*/
108 int sort_flag;
109 glob_t *sort_glob;
111 /* Context used for commandline completion */
112 struct complete_ctx {
113 struct sftp_conn *conn;
114 char **remote_pathp;
117 int remote_glob(struct sftp_conn *, const char *, int,
118 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
120 extern char *__progname;
122 /* Separators for interactive commands */
123 #define WHITESPACE " \t\r\n"
125 /* ls flags */
126 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
127 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
128 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
129 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
130 #define LS_TIME_SORT 0x0010 /* Sort by mtime */
131 #define LS_SIZE_SORT 0x0020 /* Sort by file size */
132 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
133 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
134 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
136 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
137 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
139 /* Commands for interactive mode */
140 enum sftp_command {
141 I_CHDIR = 1,
142 I_CHGRP,
143 I_CHMOD,
144 I_CHOWN,
145 I_DF,
146 I_GET,
147 I_HELP,
148 I_LCHDIR,
149 I_LINK,
150 I_LLS,
151 I_LMKDIR,
152 I_LPWD,
153 I_LS,
154 I_LUMASK,
155 I_MKDIR,
156 I_PUT,
157 I_PWD,
158 I_QUIT,
159 I_REGET,
160 I_RENAME,
161 I_REPUT,
162 I_RM,
163 I_RMDIR,
164 I_SHELL,
165 I_SYMLINK,
166 I_VERSION,
167 I_PROGRESS,
170 struct CMD {
171 const char *c;
172 const int n;
173 const int t;
176 /* Type of completion */
177 #define NOARGS 0
178 #define REMOTE 1
179 #define LOCAL 2
181 static const struct CMD cmds[] = {
182 { "bye", I_QUIT, NOARGS },
183 { "cd", I_CHDIR, REMOTE },
184 { "chdir", I_CHDIR, REMOTE },
185 { "chgrp", I_CHGRP, REMOTE },
186 { "chmod", I_CHMOD, REMOTE },
187 { "chown", I_CHOWN, REMOTE },
188 { "df", I_DF, REMOTE },
189 { "dir", I_LS, REMOTE },
190 { "exit", I_QUIT, NOARGS },
191 { "get", I_GET, REMOTE },
192 { "help", I_HELP, NOARGS },
193 { "lcd", I_LCHDIR, LOCAL },
194 { "lchdir", I_LCHDIR, LOCAL },
195 { "lls", I_LLS, LOCAL },
196 { "lmkdir", I_LMKDIR, LOCAL },
197 { "ln", I_LINK, REMOTE },
198 { "lpwd", I_LPWD, LOCAL },
199 { "ls", I_LS, REMOTE },
200 { "lumask", I_LUMASK, NOARGS },
201 { "mkdir", I_MKDIR, REMOTE },
202 { "mget", I_GET, REMOTE },
203 { "mput", I_PUT, LOCAL },
204 { "progress", I_PROGRESS, NOARGS },
205 { "put", I_PUT, LOCAL },
206 { "pwd", I_PWD, REMOTE },
207 { "quit", I_QUIT, NOARGS },
208 { "reget", I_REGET, REMOTE },
209 { "rename", I_RENAME, REMOTE },
210 { "reput", I_REPUT, LOCAL },
211 { "rm", I_RM, REMOTE },
212 { "rmdir", I_RMDIR, REMOTE },
213 { "symlink", I_SYMLINK, REMOTE },
214 { "version", I_VERSION, NOARGS },
215 { "!", I_SHELL, NOARGS },
216 { "?", I_HELP, NOARGS },
217 { NULL, -1, -1 }
220 int interactive_loop(struct sftp_conn *, char *file1, char *file2);
222 /* ARGSUSED */
223 static void
224 killchild(int signo)
226 if (sshpid > 1) {
227 kill(sshpid, SIGTERM);
228 waitpid(sshpid, NULL, 0);
231 _exit(1);
234 /* ARGSUSED */
235 static void
236 suspchild(int signo)
238 if (sshpid > 1) {
239 kill(sshpid, signo);
240 while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR)
241 continue;
243 kill(getpid(), SIGSTOP);
246 /* ARGSUSED */
247 static void
248 cmd_interrupt(int signo)
250 const char msg[] = "\rInterrupt \n";
251 int olderrno = errno;
253 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1);
254 interrupted = 1;
255 errno = olderrno;
258 static void
259 help(void)
261 printf("Available commands:\n"
262 "bye Quit sftp\n"
263 "cd path Change remote directory to 'path'\n"
264 "chgrp grp path Change group of file 'path' to 'grp'\n"
265 "chmod mode path Change permissions of file 'path' to 'mode'\n"
266 "chown own path Change owner of file 'path' to 'own'\n"
267 "df [-hi] [path] Display statistics for current directory or\n"
268 " filesystem containing 'path'\n"
269 "exit Quit sftp\n"
270 "get [-afPpRr] remote [local] Download file\n"
271 "reget [-fPpRr] remote [local] Resume download file\n"
272 "reput [-fPpRr] [local] remote Resume upload file\n"
273 "help Display this help text\n"
274 "lcd path Change local directory to 'path'\n"
275 "lls [ls-options [path]] Display local directory listing\n"
276 "lmkdir path Create local directory\n"
277 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
278 "lpwd Print local working directory\n"
279 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
280 "lumask umask Set local umask to 'umask'\n"
281 "mkdir path Create remote directory\n"
282 "progress Toggle display of progress meter\n"
283 "put [-afPpRr] local [remote] Upload file\n"
284 "pwd Display remote working directory\n"
285 "quit Quit sftp\n"
286 "rename oldpath newpath Rename remote file\n"
287 "rm path Delete remote file\n"
288 "rmdir path Remove remote directory\n"
289 "symlink oldpath newpath Symlink remote file\n"
290 "version Show SFTP version\n"
291 "!command Execute 'command' in local shell\n"
292 "! Escape to local shell\n"
293 "? Synonym for help\n");
296 static void
297 local_do_shell(const char *args)
299 int status;
300 char *shell;
301 pid_t pid;
303 if (!*args)
304 args = NULL;
306 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
307 shell = _PATH_BSHELL;
309 if ((pid = fork()) == -1)
310 fatal("Couldn't fork: %s", strerror(errno));
312 if (pid == 0) {
313 /* XXX: child has pipe fds to ssh subproc open - issue? */
314 if (args) {
315 debug3("Executing %s -c \"%s\"", shell, args);
316 execl(shell, shell, "-c", args, (char *)NULL);
317 } else {
318 debug3("Executing %s", shell);
319 execl(shell, shell, (char *)NULL);
321 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
322 strerror(errno));
323 _exit(1);
325 while (waitpid(pid, &status, 0) == -1)
326 if (errno != EINTR)
327 fatal("Couldn't wait for child: %s", strerror(errno));
328 if (!WIFEXITED(status))
329 error("Shell exited abnormally");
330 else if (WEXITSTATUS(status))
331 error("Shell exited with status %d", WEXITSTATUS(status));
334 static void
335 local_do_ls(const char *args)
337 if (!args || !*args)
338 local_do_shell(_PATH_LS);
339 else {
340 int len = strlen(_PATH_LS " ") + strlen(args) + 1;
341 char *buf = xmalloc(len);
343 /* XXX: quoting - rip quoting code from ftp? */
344 snprintf(buf, len, _PATH_LS " %s", args);
345 local_do_shell(buf);
346 free(buf);
350 /* Strip one path (usually the pwd) from the start of another */
351 static char *
352 path_strip(const char *path, const char *strip)
354 size_t len;
356 if (strip == NULL)
357 return (xstrdup(path));
359 len = strlen(strip);
360 if (strncmp(path, strip, len) == 0) {
361 if (strip[len - 1] != '/' && path[len] == '/')
362 len++;
363 return (xstrdup(path + len));
366 return (xstrdup(path));
369 static char *
370 make_absolute(char *p, const char *pwd)
372 char *abs_str;
374 /* Derelativise */
375 if (p && p[0] != '/') {
376 abs_str = path_append(pwd, p);
377 free(p);
378 return(abs_str);
379 } else
380 return(p);
383 static int
384 parse_getput_flags(const char *cmd, char **argv, int argc,
385 int *aflag, int *fflag, int *pflag, int *rflag)
387 extern int opterr, optind, optopt, optreset;
388 int ch;
390 optind = optreset = 1;
391 opterr = 0;
393 *aflag = *fflag = *rflag = *pflag = 0;
394 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
395 switch (ch) {
396 case 'a':
397 *aflag = 1;
398 break;
399 case 'f':
400 *fflag = 1;
401 break;
402 case 'p':
403 case 'P':
404 *pflag = 1;
405 break;
406 case 'r':
407 case 'R':
408 *rflag = 1;
409 break;
410 default:
411 error("%s: Invalid flag -%c", cmd, optopt);
412 return -1;
416 return optind;
419 static int
420 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
422 extern int opterr, optind, optopt, optreset;
423 int ch;
425 optind = optreset = 1;
426 opterr = 0;
428 *sflag = 0;
429 while ((ch = getopt(argc, argv, "s")) != -1) {
430 switch (ch) {
431 case 's':
432 *sflag = 1;
433 break;
434 default:
435 error("%s: Invalid flag -%c", cmd, optopt);
436 return -1;
440 return optind;
443 static int
444 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
446 extern int opterr, optind, optopt, optreset;
447 int ch;
449 optind = optreset = 1;
450 opterr = 0;
452 *lflag = 0;
453 while ((ch = getopt(argc, argv, "l")) != -1) {
454 switch (ch) {
455 case 'l':
456 *lflag = 1;
457 break;
458 default:
459 error("%s: Invalid flag -%c", cmd, optopt);
460 return -1;
464 return optind;
467 static int
468 parse_ls_flags(char **argv, int argc, int *lflag)
470 extern int opterr, optind, optopt, optreset;
471 int ch;
473 optind = optreset = 1;
474 opterr = 0;
476 *lflag = LS_NAME_SORT;
477 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
478 switch (ch) {
479 case '1':
480 *lflag &= ~VIEW_FLAGS;
481 *lflag |= LS_SHORT_VIEW;
482 break;
483 case 'S':
484 *lflag &= ~SORT_FLAGS;
485 *lflag |= LS_SIZE_SORT;
486 break;
487 case 'a':
488 *lflag |= LS_SHOW_ALL;
489 break;
490 case 'f':
491 *lflag &= ~SORT_FLAGS;
492 break;
493 case 'h':
494 *lflag |= LS_SI_UNITS;
495 break;
496 case 'l':
497 *lflag &= ~LS_SHORT_VIEW;
498 *lflag |= LS_LONG_VIEW;
499 break;
500 case 'n':
501 *lflag &= ~LS_SHORT_VIEW;
502 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
503 break;
504 case 'r':
505 *lflag |= LS_REVERSE_SORT;
506 break;
507 case 't':
508 *lflag &= ~SORT_FLAGS;
509 *lflag |= LS_TIME_SORT;
510 break;
511 default:
512 error("ls: Invalid flag -%c", optopt);
513 return -1;
517 return optind;
520 static int
521 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
523 extern int opterr, optind, optopt, optreset;
524 int ch;
526 optind = optreset = 1;
527 opterr = 0;
529 *hflag = *iflag = 0;
530 while ((ch = getopt(argc, argv, "hi")) != -1) {
531 switch (ch) {
532 case 'h':
533 *hflag = 1;
534 break;
535 case 'i':
536 *iflag = 1;
537 break;
538 default:
539 error("%s: Invalid flag -%c", cmd, optopt);
540 return -1;
544 return optind;
547 static int
548 parse_no_flags(const char *cmd, char **argv, int argc)
550 extern int opterr, optind, optopt, optreset;
551 int ch;
553 optind = optreset = 1;
554 opterr = 0;
556 while ((ch = getopt(argc, argv, "")) != -1) {
557 switch (ch) {
558 default:
559 error("%s: Invalid flag -%c", cmd, optopt);
560 return -1;
564 return optind;
567 static int
568 is_dir(const char *path)
570 struct stat sb;
572 /* XXX: report errors? */
573 if (stat(path, &sb) == -1)
574 return(0);
576 return(S_ISDIR(sb.st_mode));
579 static int
580 remote_is_dir(struct sftp_conn *conn, const char *path)
582 Attrib *a;
584 /* XXX: report errors? */
585 if ((a = do_stat(conn, path, 1)) == NULL)
586 return(0);
587 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
588 return(0);
589 return(S_ISDIR(a->perm));
592 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
593 static int
594 pathname_is_dir(const char *pathname)
596 size_t l = strlen(pathname);
598 return l > 0 && pathname[l - 1] == '/';
601 static int
602 process_get(struct sftp_conn *conn, const char *src, const char *dst,
603 const char *pwd, int pflag, int rflag, int resume, int fflag)
605 char *abs_src = NULL;
606 char *abs_dst = NULL;
607 glob_t g;
608 char *filename, *tmp=NULL;
609 int i, r, err = 0;
611 abs_src = xstrdup(src);
612 abs_src = make_absolute(abs_src, pwd);
613 memset(&g, 0, sizeof(g));
615 debug3("Looking up %s", abs_src);
616 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
617 if (r == GLOB_NOSPACE) {
618 error("Too many matches for \"%s\".", abs_src);
619 } else {
620 error("File \"%s\" not found.", abs_src);
622 err = -1;
623 goto out;
627 * If multiple matches then dst must be a directory or
628 * unspecified.
630 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
631 error("Multiple source paths, but destination "
632 "\"%s\" is not a directory", dst);
633 err = -1;
634 goto out;
637 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
638 tmp = xstrdup(g.gl_pathv[i]);
639 if ((filename = basename(tmp)) == NULL) {
640 error("basename %s: %s", tmp, strerror(errno));
641 free(tmp);
642 err = -1;
643 goto out;
646 if (g.gl_matchc == 1 && dst) {
647 if (is_dir(dst)) {
648 abs_dst = path_append(dst, filename);
649 } else {
650 abs_dst = xstrdup(dst);
652 } else if (dst) {
653 abs_dst = path_append(dst, filename);
654 } else {
655 abs_dst = xstrdup(filename);
657 free(tmp);
659 resume |= global_aflag;
660 if (!quiet && resume)
661 mprintf("Resuming %s to %s\n",
662 g.gl_pathv[i], abs_dst);
663 else if (!quiet && !resume)
664 mprintf("Fetching %s to %s\n",
665 g.gl_pathv[i], abs_dst);
666 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
667 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
668 pflag || global_pflag, 1, resume,
669 fflag || global_fflag) == -1)
670 err = -1;
671 } else {
672 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
673 pflag || global_pflag, resume,
674 fflag || global_fflag) == -1)
675 err = -1;
677 free(abs_dst);
678 abs_dst = NULL;
681 out:
682 free(abs_src);
683 globfree(&g);
684 return(err);
687 static int
688 process_put(struct sftp_conn *conn, const char *src, const char *dst,
689 const char *pwd, int pflag, int rflag, int resume, int fflag)
691 char *tmp_dst = NULL;
692 char *abs_dst = NULL;
693 char *tmp = NULL, *filename = NULL;
694 glob_t g;
695 int err = 0;
696 int i, dst_is_dir = 1;
697 struct stat sb;
699 if (dst) {
700 tmp_dst = xstrdup(dst);
701 tmp_dst = make_absolute(tmp_dst, pwd);
704 memset(&g, 0, sizeof(g));
705 debug3("Looking up %s", src);
706 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) {
707 error("File \"%s\" not found.", src);
708 err = -1;
709 goto out;
712 /* If we aren't fetching to pwd then stash this status for later */
713 if (tmp_dst != NULL)
714 dst_is_dir = remote_is_dir(conn, tmp_dst);
716 /* If multiple matches, dst may be directory or unspecified */
717 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
718 error("Multiple paths match, but destination "
719 "\"%s\" is not a directory", tmp_dst);
720 err = -1;
721 goto out;
724 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
725 if (stat(g.gl_pathv[i], &sb) == -1) {
726 err = -1;
727 error("stat %s: %s", g.gl_pathv[i], strerror(errno));
728 continue;
731 tmp = xstrdup(g.gl_pathv[i]);
732 if ((filename = basename(tmp)) == NULL) {
733 error("basename %s: %s", tmp, strerror(errno));
734 free(tmp);
735 err = -1;
736 goto out;
739 if (g.gl_matchc == 1 && tmp_dst) {
740 /* If directory specified, append filename */
741 if (dst_is_dir)
742 abs_dst = path_append(tmp_dst, filename);
743 else
744 abs_dst = xstrdup(tmp_dst);
745 } else if (tmp_dst) {
746 abs_dst = path_append(tmp_dst, filename);
747 } else {
748 abs_dst = make_absolute(xstrdup(filename), pwd);
750 free(tmp);
752 resume |= global_aflag;
753 if (!quiet && resume)
754 mprintf("Resuming upload of %s to %s\n",
755 g.gl_pathv[i], abs_dst);
756 else if (!quiet && !resume)
757 mprintf("Uploading %s to %s\n",
758 g.gl_pathv[i], abs_dst);
759 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
760 if (upload_dir(conn, g.gl_pathv[i], abs_dst,
761 pflag || global_pflag, 1, resume,
762 fflag || global_fflag) == -1)
763 err = -1;
764 } else {
765 if (do_upload(conn, g.gl_pathv[i], abs_dst,
766 pflag || global_pflag, resume,
767 fflag || global_fflag) == -1)
768 err = -1;
772 out:
773 free(abs_dst);
774 free(tmp_dst);
775 globfree(&g);
776 return(err);
779 static int
780 sdirent_comp(const void *aa, const void *bb)
782 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
783 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
784 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
786 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
787 if (sort_flag & LS_NAME_SORT)
788 return (rmul * strcmp(a->filename, b->filename));
789 else if (sort_flag & LS_TIME_SORT)
790 return (rmul * NCMP(a->a.mtime, b->a.mtime));
791 else if (sort_flag & LS_SIZE_SORT)
792 return (rmul * NCMP(a->a.size, b->a.size));
794 fatal("Unknown ls sort type");
797 /* sftp ls.1 replacement for directories */
798 static int
799 do_ls_dir(struct sftp_conn *conn, const char *path,
800 const char *strip_path, int lflag)
802 int n;
803 u_int c = 1, colspace = 0, columns = 1;
804 SFTP_DIRENT **d;
806 if ((n = do_readdir(conn, path, &d)) != 0)
807 return (n);
809 if (!(lflag & LS_SHORT_VIEW)) {
810 u_int m = 0, width = 80;
811 struct winsize ws;
812 char *tmp;
814 /* Count entries for sort and find longest filename */
815 for (n = 0; d[n] != NULL; n++) {
816 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
817 m = MAXIMUM(m, strlen(d[n]->filename));
820 /* Add any subpath that also needs to be counted */
821 tmp = path_strip(path, strip_path);
822 m += strlen(tmp);
823 free(tmp);
825 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
826 width = ws.ws_col;
828 columns = width / (m + 2);
829 columns = MAXIMUM(columns, 1);
830 colspace = width / columns;
831 colspace = MINIMUM(colspace, width);
834 if (lflag & SORT_FLAGS) {
835 for (n = 0; d[n] != NULL; n++)
836 ; /* count entries */
837 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
838 qsort(d, n, sizeof(*d), sdirent_comp);
841 for (n = 0; d[n] != NULL && !interrupted; n++) {
842 char *tmp, *fname;
844 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
845 continue;
847 tmp = path_append(path, d[n]->filename);
848 fname = path_strip(tmp, strip_path);
849 free(tmp);
851 if (lflag & LS_LONG_VIEW) {
852 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
853 char *lname;
854 struct stat sb;
856 memset(&sb, 0, sizeof(sb));
857 attrib_to_stat(&d[n]->a, &sb);
858 lname = ls_file(fname, &sb, 1,
859 (lflag & LS_SI_UNITS));
860 mprintf("%s\n", lname);
861 free(lname);
862 } else
863 mprintf("%s\n", d[n]->longname);
864 } else {
865 mprintf("%-*s", colspace, fname);
866 if (c >= columns) {
867 printf("\n");
868 c = 1;
869 } else
870 c++;
873 free(fname);
876 if (!(lflag & LS_LONG_VIEW) && (c != 1))
877 printf("\n");
879 free_sftp_dirents(d);
880 return (0);
883 static int
884 sglob_comp(const void *aa, const void *bb)
886 u_int a = *(const u_int *)aa;
887 u_int b = *(const u_int *)bb;
888 const char *ap = sort_glob->gl_pathv[a];
889 const char *bp = sort_glob->gl_pathv[b];
890 const struct stat *as = sort_glob->gl_statv[a];
891 const struct stat *bs = sort_glob->gl_statv[b];
892 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
894 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
895 if (sort_flag & LS_NAME_SORT)
896 return (rmul * strcmp(ap, bp));
897 else if (sort_flag & LS_TIME_SORT) {
898 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
899 return (rmul * timespeccmp(&as->st_mtim, &bs->st_mtim, <));
900 #elif defined(HAVE_STRUCT_STAT_ST_MTIME)
901 return (rmul * NCMP(as->st_mtime, bs->st_mtime));
902 #else
903 return rmul * 1;
904 #endif
905 } else if (sort_flag & LS_SIZE_SORT)
906 return (rmul * NCMP(as->st_size, bs->st_size));
908 fatal("Unknown ls sort type");
911 /* sftp ls.1 replacement which handles path globs */
912 static int
913 do_globbed_ls(struct sftp_conn *conn, const char *path,
914 const char *strip_path, int lflag)
916 char *fname, *lname;
917 glob_t g;
918 int err, r;
919 struct winsize ws;
920 u_int i, j, nentries, *indices = NULL, c = 1;
921 u_int colspace = 0, columns = 1, m = 0, width = 80;
923 memset(&g, 0, sizeof(g));
925 if ((r = remote_glob(conn, path,
926 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
927 NULL, &g)) != 0 ||
928 (g.gl_pathc && !g.gl_matchc)) {
929 if (g.gl_pathc)
930 globfree(&g);
931 if (r == GLOB_NOSPACE) {
932 error("Can't ls: Too many matches for \"%s\"", path);
933 } else {
934 error("Can't ls: \"%s\" not found", path);
936 return -1;
939 if (interrupted)
940 goto out;
943 * If the glob returns a single match and it is a directory,
944 * then just list its contents.
946 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL &&
947 S_ISDIR(g.gl_statv[0]->st_mode)) {
948 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
949 globfree(&g);
950 return err;
953 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
954 width = ws.ws_col;
956 if (!(lflag & LS_SHORT_VIEW)) {
957 /* Count entries for sort and find longest filename */
958 for (i = 0; g.gl_pathv[i]; i++)
959 m = MAXIMUM(m, strlen(g.gl_pathv[i]));
961 columns = width / (m + 2);
962 columns = MAXIMUM(columns, 1);
963 colspace = width / columns;
967 * Sorting: rather than mess with the contents of glob_t, prepare
968 * an array of indices into it and sort that. For the usual
969 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
971 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++)
972 ; /* count entries */
973 indices = calloc(nentries, sizeof(*indices));
974 for (i = 0; i < nentries; i++)
975 indices[i] = i;
977 if (lflag & SORT_FLAGS) {
978 sort_glob = &g;
979 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
980 qsort(indices, nentries, sizeof(*indices), sglob_comp);
981 sort_glob = NULL;
984 for (j = 0; j < nentries && !interrupted; j++) {
985 i = indices[j];
986 fname = path_strip(g.gl_pathv[i], strip_path);
987 if (lflag & LS_LONG_VIEW) {
988 if (g.gl_statv[i] == NULL) {
989 error("no stat information for %s", fname);
990 continue;
992 lname = ls_file(fname, g.gl_statv[i], 1,
993 (lflag & LS_SI_UNITS));
994 mprintf("%s\n", lname);
995 free(lname);
996 } else {
997 mprintf("%-*s", colspace, fname);
998 if (c >= columns) {
999 printf("\n");
1000 c = 1;
1001 } else
1002 c++;
1004 free(fname);
1007 if (!(lflag & LS_LONG_VIEW) && (c != 1))
1008 printf("\n");
1010 out:
1011 if (g.gl_pathc)
1012 globfree(&g);
1013 free(indices);
1015 return 0;
1018 static int
1019 do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
1021 struct sftp_statvfs st;
1022 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE];
1023 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE];
1024 char s_icapacity[16], s_dcapacity[16];
1026 if (do_statvfs(conn, path, &st, 1) == -1)
1027 return -1;
1028 if (st.f_files == 0)
1029 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1030 else {
1031 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1032 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1033 st.f_files));
1035 if (st.f_blocks == 0)
1036 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1037 else {
1038 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1039 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1040 st.f_blocks));
1042 if (iflag) {
1043 printf(" Inodes Used Avail "
1044 "(root) %%Capacity\n");
1045 printf("%11llu %11llu %11llu %11llu %s\n",
1046 (unsigned long long)st.f_files,
1047 (unsigned long long)(st.f_files - st.f_ffree),
1048 (unsigned long long)st.f_favail,
1049 (unsigned long long)st.f_ffree, s_icapacity);
1050 } else if (hflag) {
1051 strlcpy(s_used, "error", sizeof(s_used));
1052 strlcpy(s_avail, "error", sizeof(s_avail));
1053 strlcpy(s_root, "error", sizeof(s_root));
1054 strlcpy(s_total, "error", sizeof(s_total));
1055 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1056 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1057 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1058 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1059 printf(" Size Used Avail (root) %%Capacity\n");
1060 printf("%7sB %7sB %7sB %7sB %s\n",
1061 s_total, s_used, s_avail, s_root, s_dcapacity);
1062 } else {
1063 printf(" Size Used Avail "
1064 "(root) %%Capacity\n");
1065 printf("%12llu %12llu %12llu %12llu %s\n",
1066 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1067 (unsigned long long)(st.f_frsize *
1068 (st.f_blocks - st.f_bfree) / 1024),
1069 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1070 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1071 s_dcapacity);
1073 return 0;
1077 * Undo escaping of glob sequences in place. Used to undo extra escaping
1078 * applied in makeargv() when the string is destined for a function that
1079 * does not glob it.
1081 static void
1082 undo_glob_escape(char *s)
1084 size_t i, j;
1086 for (i = j = 0;;) {
1087 if (s[i] == '\0') {
1088 s[j] = '\0';
1089 return;
1091 if (s[i] != '\\') {
1092 s[j++] = s[i++];
1093 continue;
1095 /* s[i] == '\\' */
1096 ++i;
1097 switch (s[i]) {
1098 case '?':
1099 case '[':
1100 case '*':
1101 case '\\':
1102 s[j++] = s[i++];
1103 break;
1104 case '\0':
1105 s[j++] = '\\';
1106 s[j] = '\0';
1107 return;
1108 default:
1109 s[j++] = '\\';
1110 s[j++] = s[i++];
1111 break;
1117 * Split a string into an argument vector using sh(1)-style quoting,
1118 * comment and escaping rules, but with some tweaks to handle glob(3)
1119 * wildcards.
1120 * The "sloppy" flag allows for recovery from missing terminating quote, for
1121 * use in parsing incomplete commandlines during tab autocompletion.
1123 * Returns NULL on error or a NULL-terminated array of arguments.
1125 * If "lastquote" is not NULL, the quoting character used for the last
1126 * argument is placed in *lastquote ("\0", "'" or "\"").
1128 * If "terminated" is not NULL, *terminated will be set to 1 when the
1129 * last argument's quote has been properly terminated or 0 otherwise.
1130 * This parameter is only of use if "sloppy" is set.
1132 #define MAXARGS 128
1133 #define MAXARGLEN 8192
1134 static char **
1135 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1136 u_int *terminated)
1138 int argc, quot;
1139 size_t i, j;
1140 static char argvs[MAXARGLEN];
1141 static char *argv[MAXARGS + 1];
1142 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1144 *argcp = argc = 0;
1145 if (strlen(arg) > sizeof(argvs) - 1) {
1146 args_too_longs:
1147 error("string too long");
1148 return NULL;
1150 if (terminated != NULL)
1151 *terminated = 1;
1152 if (lastquote != NULL)
1153 *lastquote = '\0';
1154 state = MA_START;
1155 i = j = 0;
1156 for (;;) {
1157 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1158 error("Too many arguments.");
1159 return NULL;
1161 if (isspace((unsigned char)arg[i])) {
1162 if (state == MA_UNQUOTED) {
1163 /* Terminate current argument */
1164 argvs[j++] = '\0';
1165 argc++;
1166 state = MA_START;
1167 } else if (state != MA_START)
1168 argvs[j++] = arg[i];
1169 } else if (arg[i] == '"' || arg[i] == '\'') {
1170 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1171 if (state == MA_START) {
1172 argv[argc] = argvs + j;
1173 state = q;
1174 if (lastquote != NULL)
1175 *lastquote = arg[i];
1176 } else if (state == MA_UNQUOTED)
1177 state = q;
1178 else if (state == q)
1179 state = MA_UNQUOTED;
1180 else
1181 argvs[j++] = arg[i];
1182 } else if (arg[i] == '\\') {
1183 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1184 quot = state == MA_SQUOTE ? '\'' : '"';
1185 /* Unescape quote we are in */
1186 /* XXX support \n and friends? */
1187 if (arg[i + 1] == quot) {
1188 i++;
1189 argvs[j++] = arg[i];
1190 } else if (arg[i + 1] == '?' ||
1191 arg[i + 1] == '[' || arg[i + 1] == '*') {
1193 * Special case for sftp: append
1194 * double-escaped glob sequence -
1195 * glob will undo one level of
1196 * escaping. NB. string can grow here.
1198 if (j >= sizeof(argvs) - 5)
1199 goto args_too_longs;
1200 argvs[j++] = '\\';
1201 argvs[j++] = arg[i++];
1202 argvs[j++] = '\\';
1203 argvs[j++] = arg[i];
1204 } else {
1205 argvs[j++] = arg[i++];
1206 argvs[j++] = arg[i];
1208 } else {
1209 if (state == MA_START) {
1210 argv[argc] = argvs + j;
1211 state = MA_UNQUOTED;
1212 if (lastquote != NULL)
1213 *lastquote = '\0';
1215 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1216 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1218 * Special case for sftp: append
1219 * escaped glob sequence -
1220 * glob will undo one level of
1221 * escaping.
1223 argvs[j++] = arg[i++];
1224 argvs[j++] = arg[i];
1225 } else {
1226 /* Unescape everything */
1227 /* XXX support \n and friends? */
1228 i++;
1229 argvs[j++] = arg[i];
1232 } else if (arg[i] == '#') {
1233 if (state == MA_SQUOTE || state == MA_DQUOTE)
1234 argvs[j++] = arg[i];
1235 else
1236 goto string_done;
1237 } else if (arg[i] == '\0') {
1238 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1239 if (sloppy) {
1240 state = MA_UNQUOTED;
1241 if (terminated != NULL)
1242 *terminated = 0;
1243 goto string_done;
1245 error("Unterminated quoted argument");
1246 return NULL;
1248 string_done:
1249 if (state == MA_UNQUOTED) {
1250 argvs[j++] = '\0';
1251 argc++;
1253 break;
1254 } else {
1255 if (state == MA_START) {
1256 argv[argc] = argvs + j;
1257 state = MA_UNQUOTED;
1258 if (lastquote != NULL)
1259 *lastquote = '\0';
1261 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1262 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1264 * Special case for sftp: escape quoted
1265 * glob(3) wildcards. NB. string can grow
1266 * here.
1268 if (j >= sizeof(argvs) - 3)
1269 goto args_too_longs;
1270 argvs[j++] = '\\';
1271 argvs[j++] = arg[i];
1272 } else
1273 argvs[j++] = arg[i];
1275 i++;
1277 *argcp = argc;
1278 return argv;
1281 static int
1282 parse_args(const char **cpp, int *ignore_errors, int *aflag,
1283 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1284 int *rflag, int *sflag,
1285 unsigned long *n_arg, char **path1, char **path2)
1287 const char *cmd, *cp = *cpp;
1288 char *cp2, **argv;
1289 int base = 0;
1290 long l;
1291 int i, cmdnum, optidx, argc;
1293 /* Skip leading whitespace */
1294 cp = cp + strspn(cp, WHITESPACE);
1296 /* Check for leading '-' (disable error processing) */
1297 *ignore_errors = 0;
1298 if (*cp == '-') {
1299 *ignore_errors = 1;
1300 cp++;
1301 cp = cp + strspn(cp, WHITESPACE);
1304 /* Ignore blank lines and lines which begin with comment '#' char */
1305 if (*cp == '\0' || *cp == '#')
1306 return (0);
1308 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1309 return -1;
1311 /* Figure out which command we have */
1312 for (i = 0; cmds[i].c != NULL; i++) {
1313 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0)
1314 break;
1316 cmdnum = cmds[i].n;
1317 cmd = cmds[i].c;
1319 /* Special case */
1320 if (*cp == '!') {
1321 cp++;
1322 cmdnum = I_SHELL;
1323 } else if (cmdnum == -1) {
1324 error("Invalid command.");
1325 return -1;
1328 /* Get arguments and parse flags */
1329 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1330 *rflag = *sflag = 0;
1331 *path1 = *path2 = NULL;
1332 optidx = 1;
1333 switch (cmdnum) {
1334 case I_GET:
1335 case I_REGET:
1336 case I_REPUT:
1337 case I_PUT:
1338 if ((optidx = parse_getput_flags(cmd, argv, argc,
1339 aflag, fflag, pflag, rflag)) == -1)
1340 return -1;
1341 /* Get first pathname (mandatory) */
1342 if (argc - optidx < 1) {
1343 error("You must specify at least one path after a "
1344 "%s command.", cmd);
1345 return -1;
1347 *path1 = xstrdup(argv[optidx]);
1348 /* Get second pathname (optional) */
1349 if (argc - optidx > 1) {
1350 *path2 = xstrdup(argv[optidx + 1]);
1351 /* Destination is not globbed */
1352 undo_glob_escape(*path2);
1354 break;
1355 case I_LINK:
1356 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1357 return -1;
1358 goto parse_two_paths;
1359 case I_RENAME:
1360 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1361 return -1;
1362 goto parse_two_paths;
1363 case I_SYMLINK:
1364 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1365 return -1;
1366 parse_two_paths:
1367 if (argc - optidx < 2) {
1368 error("You must specify two paths after a %s "
1369 "command.", cmd);
1370 return -1;
1372 *path1 = xstrdup(argv[optidx]);
1373 *path2 = xstrdup(argv[optidx + 1]);
1374 /* Paths are not globbed */
1375 undo_glob_escape(*path1);
1376 undo_glob_escape(*path2);
1377 break;
1378 case I_RM:
1379 case I_MKDIR:
1380 case I_RMDIR:
1381 case I_CHDIR:
1382 case I_LCHDIR:
1383 case I_LMKDIR:
1384 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1385 return -1;
1386 /* Get pathname (mandatory) */
1387 if (argc - optidx < 1) {
1388 error("You must specify a path after a %s command.",
1389 cmd);
1390 return -1;
1392 *path1 = xstrdup(argv[optidx]);
1393 /* Only "rm" globs */
1394 if (cmdnum != I_RM)
1395 undo_glob_escape(*path1);
1396 break;
1397 case I_DF:
1398 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1399 iflag)) == -1)
1400 return -1;
1401 /* Default to current directory if no path specified */
1402 if (argc - optidx < 1)
1403 *path1 = NULL;
1404 else {
1405 *path1 = xstrdup(argv[optidx]);
1406 undo_glob_escape(*path1);
1408 break;
1409 case I_LS:
1410 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1411 return(-1);
1412 /* Path is optional */
1413 if (argc - optidx > 0)
1414 *path1 = xstrdup(argv[optidx]);
1415 break;
1416 case I_LLS:
1417 /* Skip ls command and following whitespace */
1418 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1419 case I_SHELL:
1420 /* Uses the rest of the line */
1421 break;
1422 case I_LUMASK:
1423 case I_CHMOD:
1424 base = 8;
1425 case I_CHOWN:
1426 case I_CHGRP:
1427 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1428 return -1;
1429 /* Get numeric arg (mandatory) */
1430 if (argc - optidx < 1)
1431 goto need_num_arg;
1432 errno = 0;
1433 l = strtol(argv[optidx], &cp2, base);
1434 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1435 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1436 l < 0) {
1437 need_num_arg:
1438 error("You must supply a numeric argument "
1439 "to the %s command.", cmd);
1440 return -1;
1442 *n_arg = l;
1443 if (cmdnum == I_LUMASK)
1444 break;
1445 /* Get pathname (mandatory) */
1446 if (argc - optidx < 2) {
1447 error("You must specify a path after a %s command.",
1448 cmd);
1449 return -1;
1451 *path1 = xstrdup(argv[optidx + 1]);
1452 break;
1453 case I_QUIT:
1454 case I_PWD:
1455 case I_LPWD:
1456 case I_HELP:
1457 case I_VERSION:
1458 case I_PROGRESS:
1459 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1460 return -1;
1461 break;
1462 default:
1463 fatal("Command not implemented");
1466 *cpp = cp;
1467 return(cmdnum);
1470 static int
1471 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1472 int err_abort)
1474 char *path1, *path2, *tmp;
1475 int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0,
1476 iflag = 0;
1477 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1478 int cmdnum, i;
1479 unsigned long n_arg = 0;
1480 Attrib a, *aa;
1481 char path_buf[PATH_MAX];
1482 int err = 0;
1483 glob_t g;
1485 path1 = path2 = NULL;
1486 cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag,
1487 &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2);
1488 if (ignore_errors != 0)
1489 err_abort = 0;
1491 memset(&g, 0, sizeof(g));
1493 /* Perform command */
1494 switch (cmdnum) {
1495 case 0:
1496 /* Blank line */
1497 break;
1498 case -1:
1499 /* Unrecognized command */
1500 err = -1;
1501 break;
1502 case I_REGET:
1503 aflag = 1;
1504 /* FALLTHROUGH */
1505 case I_GET:
1506 err = process_get(conn, path1, path2, *pwd, pflag,
1507 rflag, aflag, fflag);
1508 break;
1509 case I_REPUT:
1510 aflag = 1;
1511 /* FALLTHROUGH */
1512 case I_PUT:
1513 err = process_put(conn, path1, path2, *pwd, pflag,
1514 rflag, aflag, fflag);
1515 break;
1516 case I_RENAME:
1517 path1 = make_absolute(path1, *pwd);
1518 path2 = make_absolute(path2, *pwd);
1519 err = do_rename(conn, path1, path2, lflag);
1520 break;
1521 case I_SYMLINK:
1522 sflag = 1;
1523 case I_LINK:
1524 if (!sflag)
1525 path1 = make_absolute(path1, *pwd);
1526 path2 = make_absolute(path2, *pwd);
1527 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1528 break;
1529 case I_RM:
1530 path1 = make_absolute(path1, *pwd);
1531 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1532 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1533 if (!quiet)
1534 mprintf("Removing %s\n", g.gl_pathv[i]);
1535 err = do_rm(conn, g.gl_pathv[i]);
1536 if (err != 0 && err_abort)
1537 break;
1539 break;
1540 case I_MKDIR:
1541 path1 = make_absolute(path1, *pwd);
1542 attrib_clear(&a);
1543 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1544 a.perm = 0777;
1545 err = do_mkdir(conn, path1, &a, 1);
1546 break;
1547 case I_RMDIR:
1548 path1 = make_absolute(path1, *pwd);
1549 err = do_rmdir(conn, path1);
1550 break;
1551 case I_CHDIR:
1552 path1 = make_absolute(path1, *pwd);
1553 if ((tmp = do_realpath(conn, path1)) == NULL) {
1554 err = 1;
1555 break;
1557 if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1558 free(tmp);
1559 err = 1;
1560 break;
1562 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1563 error("Can't change directory: Can't check target");
1564 free(tmp);
1565 err = 1;
1566 break;
1568 if (!S_ISDIR(aa->perm)) {
1569 error("Can't change directory: \"%s\" is not "
1570 "a directory", tmp);
1571 free(tmp);
1572 err = 1;
1573 break;
1575 free(*pwd);
1576 *pwd = tmp;
1577 break;
1578 case I_LS:
1579 if (!path1) {
1580 do_ls_dir(conn, *pwd, *pwd, lflag);
1581 break;
1584 /* Strip pwd off beginning of non-absolute paths */
1585 tmp = NULL;
1586 if (*path1 != '/')
1587 tmp = *pwd;
1589 path1 = make_absolute(path1, *pwd);
1590 err = do_globbed_ls(conn, path1, tmp, lflag);
1591 break;
1592 case I_DF:
1593 /* Default to current directory if no path specified */
1594 if (path1 == NULL)
1595 path1 = xstrdup(*pwd);
1596 path1 = make_absolute(path1, *pwd);
1597 err = do_df(conn, path1, hflag, iflag);
1598 break;
1599 case I_LCHDIR:
1600 tmp = tilde_expand_filename(path1, getuid());
1601 free(path1);
1602 path1 = tmp;
1603 if (chdir(path1) == -1) {
1604 error("Couldn't change local directory to "
1605 "\"%s\": %s", path1, strerror(errno));
1606 err = 1;
1608 break;
1609 case I_LMKDIR:
1610 if (mkdir(path1, 0777) == -1) {
1611 error("Couldn't create local directory "
1612 "\"%s\": %s", path1, strerror(errno));
1613 err = 1;
1615 break;
1616 case I_LLS:
1617 local_do_ls(cmd);
1618 break;
1619 case I_SHELL:
1620 local_do_shell(cmd);
1621 break;
1622 case I_LUMASK:
1623 umask(n_arg);
1624 printf("Local umask: %03lo\n", n_arg);
1625 break;
1626 case I_CHMOD:
1627 path1 = make_absolute(path1, *pwd);
1628 attrib_clear(&a);
1629 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1630 a.perm = n_arg;
1631 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1632 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1633 if (!quiet)
1634 mprintf("Changing mode on %s\n",
1635 g.gl_pathv[i]);
1636 err = do_setstat(conn, g.gl_pathv[i], &a);
1637 if (err != 0 && err_abort)
1638 break;
1640 break;
1641 case I_CHOWN:
1642 case I_CHGRP:
1643 path1 = make_absolute(path1, *pwd);
1644 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1645 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1646 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1647 if (err_abort) {
1648 err = -1;
1649 break;
1650 } else
1651 continue;
1653 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1654 error("Can't get current ownership of "
1655 "remote file \"%s\"", g.gl_pathv[i]);
1656 if (err_abort) {
1657 err = -1;
1658 break;
1659 } else
1660 continue;
1662 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1663 if (cmdnum == I_CHOWN) {
1664 if (!quiet)
1665 mprintf("Changing owner on %s\n",
1666 g.gl_pathv[i]);
1667 aa->uid = n_arg;
1668 } else {
1669 if (!quiet)
1670 mprintf("Changing group on %s\n",
1671 g.gl_pathv[i]);
1672 aa->gid = n_arg;
1674 err = do_setstat(conn, g.gl_pathv[i], aa);
1675 if (err != 0 && err_abort)
1676 break;
1678 break;
1679 case I_PWD:
1680 mprintf("Remote working directory: %s\n", *pwd);
1681 break;
1682 case I_LPWD:
1683 if (!getcwd(path_buf, sizeof(path_buf))) {
1684 error("Couldn't get local cwd: %s", strerror(errno));
1685 err = -1;
1686 break;
1688 mprintf("Local working directory: %s\n", path_buf);
1689 break;
1690 case I_QUIT:
1691 /* Processed below */
1692 break;
1693 case I_HELP:
1694 help();
1695 break;
1696 case I_VERSION:
1697 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1698 break;
1699 case I_PROGRESS:
1700 showprogress = !showprogress;
1701 if (showprogress)
1702 printf("Progress meter enabled\n");
1703 else
1704 printf("Progress meter disabled\n");
1705 break;
1706 default:
1707 fatal("%d is not implemented", cmdnum);
1710 if (g.gl_pathc)
1711 globfree(&g);
1712 free(path1);
1713 free(path2);
1715 /* If an unignored error occurs in batch mode we should abort. */
1716 if (err_abort && err != 0)
1717 return (-1);
1718 else if (cmdnum == I_QUIT)
1719 return (1);
1721 return (0);
1724 #ifdef USE_LIBEDIT
1725 static char *
1726 prompt(EditLine *el)
1728 return ("sftp> ");
1731 /* Display entries in 'list' after skipping the first 'len' chars */
1732 static void
1733 complete_display(char **list, u_int len)
1735 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1736 struct winsize ws;
1737 char *tmp;
1739 /* Count entries for sort and find longest */
1740 for (y = 0; list[y]; y++)
1741 m = MAXIMUM(m, strlen(list[y]));
1743 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1744 width = ws.ws_col;
1746 m = m > len ? m - len : 0;
1747 columns = width / (m + 2);
1748 columns = MAXIMUM(columns, 1);
1749 colspace = width / columns;
1750 colspace = MINIMUM(colspace, width);
1752 printf("\n");
1753 m = 1;
1754 for (y = 0; list[y]; y++) {
1755 llen = strlen(list[y]);
1756 tmp = llen > len ? list[y] + len : "";
1757 mprintf("%-*s", colspace, tmp);
1758 if (m >= columns) {
1759 printf("\n");
1760 m = 1;
1761 } else
1762 m++;
1764 printf("\n");
1768 * Given a "list" of words that begin with a common prefix of "word",
1769 * attempt to find an autocompletion to extends "word" by the next
1770 * characters common to all entries in "list".
1772 static char *
1773 complete_ambiguous(const char *word, char **list, size_t count)
1775 if (word == NULL)
1776 return NULL;
1778 if (count > 0) {
1779 u_int y, matchlen = strlen(list[0]);
1781 /* Find length of common stem */
1782 for (y = 1; list[y]; y++) {
1783 u_int x;
1785 for (x = 0; x < matchlen; x++)
1786 if (list[0][x] != list[y][x])
1787 break;
1789 matchlen = x;
1792 if (matchlen > strlen(word)) {
1793 char *tmp = xstrdup(list[0]);
1795 tmp[matchlen] = '\0';
1796 return tmp;
1800 return xstrdup(word);
1803 /* Autocomplete a sftp command */
1804 static int
1805 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1806 int terminated)
1808 u_int y, count = 0, cmdlen, tmplen;
1809 char *tmp, **list, argterm[3];
1810 const LineInfo *lf;
1812 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1814 /* No command specified: display all available commands */
1815 if (cmd == NULL) {
1816 for (y = 0; cmds[y].c; y++)
1817 list[count++] = xstrdup(cmds[y].c);
1819 list[count] = NULL;
1820 complete_display(list, 0);
1822 for (y = 0; list[y] != NULL; y++)
1823 free(list[y]);
1824 free(list);
1825 return count;
1828 /* Prepare subset of commands that start with "cmd" */
1829 cmdlen = strlen(cmd);
1830 for (y = 0; cmds[y].c; y++) {
1831 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1832 list[count++] = xstrdup(cmds[y].c);
1834 list[count] = NULL;
1836 if (count == 0) {
1837 free(list);
1838 return 0;
1841 /* Complete ambigious command */
1842 tmp = complete_ambiguous(cmd, list, count);
1843 if (count > 1)
1844 complete_display(list, 0);
1846 for (y = 0; list[y]; y++)
1847 free(list[y]);
1848 free(list);
1850 if (tmp != NULL) {
1851 tmplen = strlen(tmp);
1852 cmdlen = strlen(cmd);
1853 /* If cmd may be extended then do so */
1854 if (tmplen > cmdlen)
1855 if (el_insertstr(el, tmp + cmdlen) == -1)
1856 fatal("el_insertstr failed.");
1857 lf = el_line(el);
1858 /* Terminate argument cleanly */
1859 if (count == 1) {
1860 y = 0;
1861 if (!terminated)
1862 argterm[y++] = quote;
1863 if (lastarg || *(lf->cursor) != ' ')
1864 argterm[y++] = ' ';
1865 argterm[y] = '\0';
1866 if (y > 0 && el_insertstr(el, argterm) == -1)
1867 fatal("el_insertstr failed.");
1869 free(tmp);
1872 return count;
1876 * Determine whether a particular sftp command's arguments (if any)
1877 * represent local or remote files.
1879 static int
1880 complete_is_remote(char *cmd) {
1881 int i;
1883 if (cmd == NULL)
1884 return -1;
1886 for (i = 0; cmds[i].c; i++) {
1887 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1888 return cmds[i].t;
1891 return -1;
1894 /* Autocomplete a filename "file" */
1895 static int
1896 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1897 char *file, int remote, int lastarg, char quote, int terminated)
1899 glob_t g;
1900 char *tmp, *tmp2, ins[8];
1901 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1902 int clen;
1903 const LineInfo *lf;
1905 /* Glob from "file" location */
1906 if (file == NULL)
1907 tmp = xstrdup("*");
1908 else
1909 xasprintf(&tmp, "%s*", file);
1911 /* Check if the path is absolute. */
1912 isabs = tmp[0] == '/';
1914 memset(&g, 0, sizeof(g));
1915 if (remote != LOCAL) {
1916 tmp = make_absolute(tmp, remote_path);
1917 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1918 } else
1919 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1921 /* Determine length of pwd so we can trim completion display */
1922 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1923 /* Terminate counting on first unescaped glob metacharacter */
1924 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1925 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1926 hadglob = 1;
1927 break;
1929 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1930 tmplen++;
1931 if (tmp[tmplen] == '/')
1932 pwdlen = tmplen + 1; /* track last seen '/' */
1934 free(tmp);
1935 tmp = NULL;
1937 if (g.gl_matchc == 0)
1938 goto out;
1940 if (g.gl_matchc > 1)
1941 complete_display(g.gl_pathv, pwdlen);
1943 /* Don't try to extend globs */
1944 if (file == NULL || hadglob)
1945 goto out;
1947 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1948 tmp = path_strip(tmp2, isabs ? NULL : remote_path);
1949 free(tmp2);
1951 if (tmp == NULL)
1952 goto out;
1954 tmplen = strlen(tmp);
1955 filelen = strlen(file);
1957 /* Count the number of escaped characters in the input string. */
1958 cesc = isesc = 0;
1959 for (i = 0; i < filelen; i++) {
1960 if (!isesc && file[i] == '\\' && i + 1 < filelen){
1961 isesc = 1;
1962 cesc++;
1963 } else
1964 isesc = 0;
1967 if (tmplen > (filelen - cesc)) {
1968 tmp2 = tmp + filelen - cesc;
1969 len = strlen(tmp2);
1970 /* quote argument on way out */
1971 for (i = 0; i < len; i += clen) {
1972 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
1973 (size_t)clen > sizeof(ins) - 2)
1974 fatal("invalid multibyte character");
1975 ins[0] = '\\';
1976 memcpy(ins + 1, tmp2 + i, clen);
1977 ins[clen + 1] = '\0';
1978 switch (tmp2[i]) {
1979 case '\'':
1980 case '"':
1981 case '\\':
1982 case '\t':
1983 case '[':
1984 case ' ':
1985 case '#':
1986 case '*':
1987 if (quote == '\0' || tmp2[i] == quote) {
1988 if (el_insertstr(el, ins) == -1)
1989 fatal("el_insertstr "
1990 "failed.");
1991 break;
1993 /* FALLTHROUGH */
1994 default:
1995 if (el_insertstr(el, ins + 1) == -1)
1996 fatal("el_insertstr failed.");
1997 break;
2002 lf = el_line(el);
2003 if (g.gl_matchc == 1) {
2004 i = 0;
2005 if (!terminated && quote != '\0')
2006 ins[i++] = quote;
2007 if (*(lf->cursor - 1) != '/' &&
2008 (lastarg || *(lf->cursor) != ' '))
2009 ins[i++] = ' ';
2010 ins[i] = '\0';
2011 if (i > 0 && el_insertstr(el, ins) == -1)
2012 fatal("el_insertstr failed.");
2014 free(tmp);
2016 out:
2017 globfree(&g);
2018 return g.gl_matchc;
2021 /* tab-completion hook function, called via libedit */
2022 static unsigned char
2023 complete(EditLine *el, int ch)
2025 char **argv, *line, quote;
2026 int argc, carg;
2027 u_int cursor, len, terminated, ret = CC_ERROR;
2028 const LineInfo *lf;
2029 struct complete_ctx *complete_ctx;
2031 lf = el_line(el);
2032 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0)
2033 fatal("%s: el_get failed", __func__);
2035 /* Figure out which argument the cursor points to */
2036 cursor = lf->cursor - lf->buffer;
2037 line = xmalloc(cursor + 1);
2038 memcpy(line, lf->buffer, cursor);
2039 line[cursor] = '\0';
2040 argv = makeargv(line, &carg, 1, &quote, &terminated);
2041 free(line);
2043 /* Get all the arguments on the line */
2044 len = lf->lastchar - lf->buffer;
2045 line = xmalloc(len + 1);
2046 memcpy(line, lf->buffer, len);
2047 line[len] = '\0';
2048 argv = makeargv(line, &argc, 1, NULL, NULL);
2050 /* Ensure cursor is at EOL or a argument boundary */
2051 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2052 line[cursor] != '\n') {
2053 free(line);
2054 return ret;
2057 if (carg == 0) {
2058 /* Show all available commands */
2059 complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
2060 ret = CC_REDISPLAY;
2061 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2062 /* Handle the command parsing */
2063 if (complete_cmd_parse(el, argv[0], argc == carg,
2064 quote, terminated) != 0)
2065 ret = CC_REDISPLAY;
2066 } else if (carg >= 1) {
2067 /* Handle file parsing */
2068 int remote = complete_is_remote(argv[0]);
2069 char *filematch = NULL;
2071 if (carg > 1 && line[cursor-1] != ' ')
2072 filematch = argv[carg - 1];
2074 if (remote != 0 &&
2075 complete_match(el, complete_ctx->conn,
2076 *complete_ctx->remote_pathp, filematch,
2077 remote, carg == argc, quote, terminated) != 0)
2078 ret = CC_REDISPLAY;
2081 free(line);
2082 return ret;
2084 #endif /* USE_LIBEDIT */
2087 interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2089 char *remote_path;
2090 char *dir = NULL;
2091 char cmd[2048];
2092 int err, interactive;
2093 EditLine *el = NULL;
2094 #ifdef USE_LIBEDIT
2095 History *hl = NULL;
2096 HistEvent hev;
2097 extern char *__progname;
2098 struct complete_ctx complete_ctx;
2100 if (!batchmode && isatty(STDIN_FILENO)) {
2101 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
2102 fatal("Couldn't initialise editline");
2103 if ((hl = history_init()) == NULL)
2104 fatal("Couldn't initialise editline history");
2105 history(hl, &hev, H_SETSIZE, 100);
2106 el_set(el, EL_HIST, history, hl);
2108 el_set(el, EL_PROMPT, prompt);
2109 el_set(el, EL_EDITOR, "emacs");
2110 el_set(el, EL_TERMINAL, NULL);
2111 el_set(el, EL_SIGNAL, 1);
2112 el_source(el, NULL);
2114 /* Tab Completion */
2115 el_set(el, EL_ADDFN, "ftp-complete",
2116 "Context sensitive argument completion", complete);
2117 complete_ctx.conn = conn;
2118 complete_ctx.remote_pathp = &remote_path;
2119 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
2120 el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
2121 /* enable ctrl-left-arrow and ctrl-right-arrow */
2122 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
2123 el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL);
2124 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
2125 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
2126 /* make ^w match ksh behaviour */
2127 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL);
2129 #endif /* USE_LIBEDIT */
2131 remote_path = do_realpath(conn, ".");
2132 if (remote_path == NULL)
2133 fatal("Need cwd");
2135 if (file1 != NULL) {
2136 dir = xstrdup(file1);
2137 dir = make_absolute(dir, remote_path);
2139 if (remote_is_dir(conn, dir) && file2 == NULL) {
2140 if (!quiet)
2141 mprintf("Changing to: %s\n", dir);
2142 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2143 if (parse_dispatch_command(conn, cmd,
2144 &remote_path, 1) != 0) {
2145 free(dir);
2146 free(remote_path);
2147 free(conn);
2148 return (-1);
2150 } else {
2151 /* XXX this is wrong wrt quoting */
2152 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2153 global_aflag ? " -a" : "", dir,
2154 file2 == NULL ? "" : " ",
2155 file2 == NULL ? "" : file2);
2156 err = parse_dispatch_command(conn, cmd,
2157 &remote_path, 1);
2158 free(dir);
2159 free(remote_path);
2160 free(conn);
2161 return (err);
2163 free(dir);
2166 setvbuf(stdout, NULL, _IOLBF, 0);
2167 setvbuf(infile, NULL, _IOLBF, 0);
2169 interactive = !batchmode && isatty(STDIN_FILENO);
2170 err = 0;
2171 for (;;) {
2172 char *cp;
2174 signal(SIGINT, SIG_IGN);
2176 if (el == NULL) {
2177 if (interactive)
2178 printf("sftp> ");
2179 if (fgets(cmd, sizeof(cmd), infile) == NULL) {
2180 if (interactive)
2181 printf("\n");
2182 break;
2184 if (!interactive) { /* Echo command */
2185 mprintf("sftp> %s", cmd);
2186 if (strlen(cmd) > 0 &&
2187 cmd[strlen(cmd) - 1] != '\n')
2188 printf("\n");
2190 } else {
2191 #ifdef USE_LIBEDIT
2192 const char *line;
2193 int count = 0;
2195 if ((line = el_gets(el, &count)) == NULL ||
2196 count <= 0) {
2197 printf("\n");
2198 break;
2200 history(hl, &hev, H_ENTER, line);
2201 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2202 fprintf(stderr, "Error: input line too long\n");
2203 continue;
2205 #endif /* USE_LIBEDIT */
2208 cp = strrchr(cmd, '\n');
2209 if (cp)
2210 *cp = '\0';
2212 /* Handle user interrupts gracefully during commands */
2213 interrupted = 0;
2214 signal(SIGINT, cmd_interrupt);
2216 err = parse_dispatch_command(conn, cmd, &remote_path,
2217 batchmode);
2218 if (err != 0)
2219 break;
2221 free(remote_path);
2222 free(conn);
2224 #ifdef USE_LIBEDIT
2225 if (el != NULL)
2226 el_end(el);
2227 #endif /* USE_LIBEDIT */
2229 /* err == 1 signifies normal "quit" exit */
2230 return (err >= 0 ? 0 : -1);
2233 static void
2234 connect_to_server(char *path, char **args, int *in, int *out)
2236 int c_in, c_out;
2238 #ifdef USE_PIPES
2239 int pin[2], pout[2];
2241 if ((pipe(pin) == -1) || (pipe(pout) == -1))
2242 fatal("pipe: %s", strerror(errno));
2243 *in = pin[0];
2244 *out = pout[1];
2245 c_in = pout[0];
2246 c_out = pin[1];
2247 #else /* USE_PIPES */
2248 int inout[2];
2250 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2251 fatal("socketpair: %s", strerror(errno));
2252 *in = *out = inout[0];
2253 c_in = c_out = inout[1];
2254 #endif /* USE_PIPES */
2256 if ((sshpid = fork()) == -1)
2257 fatal("fork: %s", strerror(errno));
2258 else if (sshpid == 0) {
2259 if ((dup2(c_in, STDIN_FILENO) == -1) ||
2260 (dup2(c_out, STDOUT_FILENO) == -1)) {
2261 fprintf(stderr, "dup2: %s\n", strerror(errno));
2262 _exit(1);
2264 close(*in);
2265 close(*out);
2266 close(c_in);
2267 close(c_out);
2270 * The underlying ssh is in the same process group, so we must
2271 * ignore SIGINT if we want to gracefully abort commands,
2272 * otherwise the signal will make it to the ssh process and
2273 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2274 * underlying ssh, it must *not* ignore that signal.
2276 signal(SIGINT, SIG_IGN);
2277 signal(SIGTERM, SIG_DFL);
2278 execvp(path, args);
2279 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2280 _exit(1);
2283 signal(SIGTERM, killchild);
2284 signal(SIGINT, killchild);
2285 signal(SIGHUP, killchild);
2286 signal(SIGTSTP, suspchild);
2287 signal(SIGTTIN, suspchild);
2288 signal(SIGTTOU, suspchild);
2289 close(c_in);
2290 close(c_out);
2293 static void
2294 usage(void)
2296 extern char *__progname;
2298 fprintf(stderr,
2299 "usage: %s [-46aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2300 " [-D sftp_server_path] [-F ssh_config] "
2301 "[-i identity_file] [-l limit]\n"
2302 " [-o ssh_option] [-P port] [-R num_requests] "
2303 "[-S program]\n"
2304 " [-s subsystem | sftp_server] host\n"
2305 " %s [user@]host[:file ...]\n"
2306 " %s [user@]host[:dir[/]]\n"
2307 " %s -b batchfile [user@]host\n",
2308 __progname, __progname, __progname, __progname);
2309 exit(1);
2313 main(int argc, char **argv)
2315 int in, out, ch, err;
2316 char *host = NULL, *userhost, *cp, *file2 = NULL;
2317 int debug_level = 0, sshver = 2;
2318 char *file1 = NULL, *sftp_server = NULL;
2319 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2320 const char *errstr;
2321 LogLevel ll = SYSLOG_LEVEL_INFO;
2322 arglist args;
2323 extern int optind;
2324 extern char *optarg;
2325 struct sftp_conn *conn;
2326 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2327 size_t num_requests = DEFAULT_NUM_REQUESTS;
2328 long long limit_kbps = 0;
2330 ssh_malloc_init(); /* must be called before any mallocs */
2331 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2332 sanitise_stdfd();
2333 msetlocale();
2335 __progname = ssh_get_progname(argv[0]);
2336 memset(&args, '\0', sizeof(args));
2337 args.list = NULL;
2338 addargs(&args, "%s", ssh_program);
2339 addargs(&args, "-oForwardX11 no");
2340 addargs(&args, "-oForwardAgent no");
2341 addargs(&args, "-oPermitLocalCommand no");
2342 addargs(&args, "-oClearAllForwardings yes");
2344 ll = SYSLOG_LEVEL_INFO;
2345 infile = stdin;
2347 while ((ch = getopt(argc, argv,
2348 "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2349 switch (ch) {
2350 /* Passed through to ssh(1) */
2351 case '4':
2352 case '6':
2353 case 'C':
2354 addargs(&args, "-%c", ch);
2355 break;
2356 /* Passed through to ssh(1) with argument */
2357 case 'F':
2358 case 'c':
2359 case 'i':
2360 case 'o':
2361 addargs(&args, "-%c", ch);
2362 addargs(&args, "%s", optarg);
2363 break;
2364 case 'q':
2365 ll = SYSLOG_LEVEL_ERROR;
2366 quiet = 1;
2367 showprogress = 0;
2368 addargs(&args, "-%c", ch);
2369 break;
2370 case 'P':
2371 addargs(&args, "-oPort %s", optarg);
2372 break;
2373 case 'v':
2374 if (debug_level < 3) {
2375 addargs(&args, "-v");
2376 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2378 debug_level++;
2379 break;
2380 case '1':
2381 sshver = 1;
2382 if (sftp_server == NULL)
2383 sftp_server = _PATH_SFTP_SERVER;
2384 break;
2385 case '2':
2386 sshver = 2;
2387 break;
2388 case 'a':
2389 global_aflag = 1;
2390 break;
2391 case 'B':
2392 copy_buffer_len = strtol(optarg, &cp, 10);
2393 if (copy_buffer_len == 0 || *cp != '\0')
2394 fatal("Invalid buffer size \"%s\"", optarg);
2395 break;
2396 case 'b':
2397 if (batchmode)
2398 fatal("Batch file already specified.");
2400 /* Allow "-" as stdin */
2401 if (strcmp(optarg, "-") != 0 &&
2402 (infile = fopen(optarg, "r")) == NULL)
2403 fatal("%s (%s).", strerror(errno), optarg);
2404 showprogress = 0;
2405 quiet = batchmode = 1;
2406 addargs(&args, "-obatchmode yes");
2407 break;
2408 case 'f':
2409 global_fflag = 1;
2410 break;
2411 case 'p':
2412 global_pflag = 1;
2413 break;
2414 case 'D':
2415 sftp_direct = optarg;
2416 break;
2417 case 'l':
2418 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2419 &errstr);
2420 if (errstr != NULL)
2421 usage();
2422 limit_kbps *= 1024; /* kbps */
2423 break;
2424 case 'r':
2425 global_rflag = 1;
2426 break;
2427 case 'R':
2428 num_requests = strtol(optarg, &cp, 10);
2429 if (num_requests == 0 || *cp != '\0')
2430 fatal("Invalid number of requests \"%s\"",
2431 optarg);
2432 break;
2433 case 's':
2434 sftp_server = optarg;
2435 break;
2436 case 'S':
2437 ssh_program = optarg;
2438 replacearg(&args, 0, "%s", ssh_program);
2439 break;
2440 case 'h':
2441 default:
2442 usage();
2446 if (!isatty(STDERR_FILENO))
2447 showprogress = 0;
2449 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2451 if (sftp_direct == NULL) {
2452 if (optind == argc || argc > (optind + 2))
2453 usage();
2455 userhost = xstrdup(argv[optind]);
2456 file2 = argv[optind+1];
2458 if ((host = strrchr(userhost, '@')) == NULL)
2459 host = userhost;
2460 else {
2461 *host++ = '\0';
2462 if (!userhost[0]) {
2463 fprintf(stderr, "Missing username\n");
2464 usage();
2466 addargs(&args, "-l");
2467 addargs(&args, "%s", userhost);
2470 if ((cp = colon(host)) != NULL) {
2471 *cp++ = '\0';
2472 file1 = cp;
2475 host = cleanhostname(host);
2476 if (!*host) {
2477 fprintf(stderr, "Missing hostname\n");
2478 usage();
2481 addargs(&args, "-oProtocol %d", sshver);
2483 /* no subsystem if the server-spec contains a '/' */
2484 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2485 addargs(&args, "-s");
2487 addargs(&args, "--");
2488 addargs(&args, "%s", host);
2489 addargs(&args, "%s", (sftp_server != NULL ?
2490 sftp_server : "sftp"));
2492 connect_to_server(ssh_program, args.list, &in, &out);
2493 } else {
2494 args.list = NULL;
2495 addargs(&args, "sftp-server");
2497 connect_to_server(sftp_direct, args.list, &in, &out);
2499 freeargs(&args);
2501 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2502 if (conn == NULL)
2503 fatal("Couldn't initialise connection to server");
2505 if (!quiet) {
2506 if (sftp_direct == NULL)
2507 fprintf(stderr, "Connected to %s.\n", host);
2508 else
2509 fprintf(stderr, "Attached to %s.\n", sftp_direct);
2512 err = interactive_loop(conn, file1, file2);
2514 #if !defined(USE_PIPES)
2515 shutdown(in, SHUT_RDWR);
2516 shutdown(out, SHUT_RDWR);
2517 #endif
2519 close(in);
2520 close(out);
2521 if (batchmode)
2522 fclose(infile);
2524 while (waitpid(sshpid, NULL, 0) == -1)
2525 if (errno != EINTR)
2526 fatal("Couldn't wait for ssh process: %s",
2527 strerror(errno));
2529 exit(err == 0 ? 0 : 1);