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