]> andersk Git - gssapi-openssh.git/blame - openssh/sftp.c
merged OPENSSH_5_2P1_GSSAPI_20090225 to GPT-branch
[gssapi-openssh.git] / openssh / sftp.c
CommitLineData
16eb0a3b 1/* $OpenBSD: sftp.c,v 1.107 2009/02/02 11:15:14 dtucker Exp $ */
3c0ef626 2/*
416fd2a8 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
3c0ef626 4 *
416fd2a8 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 *
416fd2a8 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
2e437378 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>
6f25cbdd 28#ifdef HAVE_SYS_STATVFS_H
29#include <sys/statvfs.h>
30#endif
2e437378 31
bd5d5d2a 32#include <ctype.h>
2e437378 33#include <errno.h>
34fee935 34
2e437378 35#ifdef HAVE_PATHS_H
36# include <paths.h>
37#endif
34fee935 38#ifdef USE_LIBEDIT
39#include <histedit.h>
40#else
41typedef void EditLine;
42#endif
2e437378 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
6f25cbdd 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"
2e437378 64#include "buffer.h"
3c0ef626 65#include "sftp-common.h"
66#include "sftp-client.h"
416fd2a8 67
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 */
6f25cbdd 78size_t num_requests = 256;
416fd2a8 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. */
2a304a95 84int showprogress = 1;
416fd2a8 85
1b56ff3d 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
416fd2a8 92int remote_glob(struct sftp_conn *, const char *, int,
93 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
3c0ef626 94
3c0ef626 95extern char *__progname;
3c0ef626 96
416fd2a8 97/* Separators for interactive commands */
98#define WHITESPACE " \t\r\n"
99
1b56ff3d 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)
416fd2a8 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
6f25cbdd 118#define I_DF 24
416fd2a8 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 },
6f25cbdd 151 { "df", I_DF },
416fd2a8 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};
3c0ef626 180
416fd2a8 181int interactive_loop(int fd_in, int fd_out, char *file1, char *file2);
182
2278ffa1 183/* ARGSUSED */
1b56ff3d 184static void
185killchild(int signo)
186{
34fee935 187 if (sshpid > 1) {
1b56ff3d 188 kill(sshpid, SIGTERM);
34fee935 189 waitpid(sshpid, NULL, 0);
190 }
1b56ff3d 191
192 _exit(1);
193}
194
2278ffa1 195/* ARGSUSED */
1b56ff3d 196static void
197cmd_interrupt(int signo)
198{
199 const char msg[] = "\rInterrupt \n";
34fee935 200 int olderrno = errno;
1b56ff3d 201
202 write(STDERR_FILENO, msg, sizeof(msg) - 1);
203 interrupted = 1;
34fee935 204 errno = olderrno;
1b56ff3d 205}
206
416fd2a8 207static void
208help(void)
209{
16eb0a3b 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");
416fd2a8 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))
2e437378 276 error("Shell exited abnormally");
416fd2a8 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);
34fee935 307 if (strncmp(path, strip, len) == 0) {
416fd2a8 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;
2278ffa1 320 size_t len = strlen(p1) + strlen(p2) + 2;
416fd2a8 321
322 ret = xmalloc(len);
323 strlcpy(ret, p1, len);
2278ffa1 324 if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
416fd2a8 325 strlcat(ret, "/", len);
326 strlcat(ret, p2, len);
327
328 return(ret);
329}
330
331static char *
332make_absolute(char *p, char *pwd)
333{
1b56ff3d 334 char *abs_str;
416fd2a8 335
336 /* Derelativise */
337 if (p && p[0] != '/') {
1b56ff3d 338 abs_str = path_append(pwd, p);
416fd2a8 339 xfree(p);
1b56ff3d 340 return(abs_str);
416fd2a8 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
bd5d5d2a 366parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
416fd2a8 367{
6f25cbdd 368 extern int opterr, optind, optopt, optreset;
bd5d5d2a 369 int ch;
416fd2a8 370
bd5d5d2a 371 optind = optreset = 1;
372 opterr = 0;
373
374 *pflag = 0;
375 while ((ch = getopt(argc, argv, "Pp")) != -1) {
376 switch (ch) {
416fd2a8 377 case 'p':
378 case 'P':
379 *pflag = 1;
380 break;
381 default:
6f25cbdd 382 error("%s: Invalid flag -%c", cmd, optopt);
bd5d5d2a 383 return -1;
416fd2a8 384 }
416fd2a8 385 }
386
bd5d5d2a 387 return optind;
416fd2a8 388}
389
390static int
bd5d5d2a 391parse_ls_flags(char **argv, int argc, int *lflag)
416fd2a8 392{
6f25cbdd 393 extern int opterr, optind, optopt, optreset;
bd5d5d2a 394 int ch;
416fd2a8 395
bd5d5d2a 396 optind = optreset = 1;
397 opterr = 0;
416fd2a8 398
bd5d5d2a 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:
6f25cbdd 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);
bd5d5d2a 460 return -1;
416fd2a8 461 }
416fd2a8 462 }
416fd2a8 463
bd5d5d2a 464 return optind;
416fd2a8 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
2e437378 476 return(S_ISDIR(sb.st_mode));
416fd2a8 477}
478
416fd2a8 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);
2e437378 489 return(S_ISDIR(a->perm));
416fd2a8 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
1b56ff3d 521 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
416fd2a8 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 */
2e437378 529 xfree(tmp);
416fd2a8 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);
416fd2a8 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;
bd5d5d2a 567 struct stat sb;
416fd2a8 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);
bd5d5d2a 576 if (glob(src, GLOB_NOCHECK, NULL, &g)) {
416fd2a8 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
1b56ff3d 590 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
bd5d5d2a 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)) {
416fd2a8 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;
1b56ff3d 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));
416fd2a8 653
1b56ff3d 654 fatal("Unknown ls sort type");
416fd2a8 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{
34fee935 661 int n;
662 u_int c = 1, colspace = 0, columns = 1;
416fd2a8 663 SFTP_DIRENT **d;
664
665 if ((n = do_readdir(conn, path, &d)) != 0)
666 return (n);
667
1b56ff3d 668 if (!(lflag & LS_SHORT_VIEW)) {
34fee935 669 u_int m = 0, width = 80;
416fd2a8 670 struct winsize ws;
671 char *tmp;
672
673 /* Count entries for sort and find longest filename */
1b56ff3d 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 }
416fd2a8 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
1b56ff3d 693 if (lflag & SORT_FLAGS) {
e00da40d 694 for (n = 0; d[n] != NULL; n++)
695 ; /* count entries */
1b56ff3d 696 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
697 qsort(d, n, sizeof(*d), sdirent_comp);
698 }
416fd2a8 699
1b56ff3d 700 for (n = 0; d[n] != NULL && !interrupted; n++) {
416fd2a8 701 char *tmp, *fname;
702
1b56ff3d 703 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
704 continue;
705
416fd2a8 706 tmp = path_append(path, d[n]->filename);
707 fname = path_strip(tmp, strip_path);
708 xfree(tmp);
709
1b56ff3d 710 if (lflag & LS_LONG_VIEW) {
711 if (lflag & LS_NUMERIC_VIEW) {
712 char *lname;
713 struct stat sb;
416fd2a8 714
1b56ff3d 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);
416fd2a8 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
1b56ff3d 734 if (!(lflag & LS_LONG_VIEW) && (c != 1))
416fd2a8 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;
34fee935 747 u_int i, c = 1, colspace = 0, columns = 1;
748 Attrib *a = NULL;
416fd2a8 749
750 memset(&g, 0, sizeof(g));
751
752 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
34fee935 753 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) {
754 if (g.gl_pathc)
755 globfree(&g);
416fd2a8 756 error("Can't ls: \"%s\" not found", path);
757 return (-1);
758 }
759
1b56ff3d 760 if (interrupted)
761 goto out;
762
416fd2a8 763 /*
34fee935 764 * If the glob returns a single match and it is a directory,
765 * then just list its contents.
416fd2a8 766 */
34fee935 767 if (g.gl_matchc == 1) {
768 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) {
416fd2a8 769 globfree(&g);
770 return (-1);
771 }
772 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
773 S_ISDIR(a->perm)) {
34fee935 774 int err;
775
776 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
416fd2a8 777 globfree(&g);
34fee935 778 return (err);
416fd2a8 779 }
780 }
781
1b56ff3d 782 if (!(lflag & LS_SHORT_VIEW)) {
34fee935 783 u_int m = 0, width = 80;
416fd2a8 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
34fee935 798 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
416fd2a8 799 char *fname;
800
801 fname = path_strip(g.gl_pathv[i], strip_path);
802
1b56ff3d 803 if (lflag & LS_LONG_VIEW) {
416fd2a8 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));
34fee935 815 if (a == NULL)
816 a = do_lstat(conn, g.gl_pathv[i], 1);
416fd2a8 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
1b56ff3d 833 if (!(lflag & LS_LONG_VIEW) && (c != 1))
416fd2a8 834 printf("\n");
835
1b56ff3d 836 out:
416fd2a8 837 if (g.gl_pathc)
838 globfree(&g);
839
840 return (0);
841}
842
6f25cbdd 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
bd5d5d2a 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
416fd2a8 1067static int
6f25cbdd 1068parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag,
416fd2a8 1069 unsigned long *n_arg, char **path1, char **path2)
1070{
1071 const char *cmd, *cp = *cpp;
bd5d5d2a 1072 char *cp2, **argv;
416fd2a8 1073 int base = 0;
1074 long l;
bd5d5d2a 1075 int i, cmdnum, optidx, argc;
416fd2a8 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
bd5d5d2a 1091 if ((argv = makeargv(cp, &argc)) == NULL)
1092 return -1;
1093
416fd2a8 1094 /* Figure out which command we have */
bd5d5d2a 1095 for (i = 0; cmds[i].c != NULL; i++) {
1096 if (strcasecmp(cmds[i].c, argv[0]) == 0)
416fd2a8 1097 break;
416fd2a8 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.");
bd5d5d2a 1108 return -1;
416fd2a8 1109 }
1110
1111 /* Get arguments and parse flags */
6f25cbdd 1112 *lflag = *pflag = *hflag = *n_arg = 0;
416fd2a8 1113 *path1 = *path2 = NULL;
bd5d5d2a 1114 optidx = 1;
416fd2a8 1115 switch (cmdnum) {
1116 case I_GET:
1117 case I_PUT:
bd5d5d2a 1118 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1)
1119 return -1;
416fd2a8 1120 /* Get first pathname (mandatory) */
bd5d5d2a 1121 if (argc - optidx < 1) {
416fd2a8 1122 error("You must specify at least one path after a "
1123 "%s command.", cmd);
bd5d5d2a 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);
416fd2a8 1132 }
416fd2a8 1133 break;
1134 case I_RENAME:
1135 case I_SYMLINK:
bd5d5d2a 1136 if (argc - optidx < 2) {
416fd2a8 1137 error("You must specify two paths after a %s "
1138 "command.", cmd);
bd5d5d2a 1139 return -1;
416fd2a8 1140 }
bd5d5d2a 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);
416fd2a8 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) */
bd5d5d2a 1154 if (argc - optidx < 1) {
416fd2a8 1155 error("You must specify a path after a %s command.",
1156 cmd);
bd5d5d2a 1157 return -1;
416fd2a8 1158 }
bd5d5d2a 1159 *path1 = xstrdup(argv[optidx]);
1160 /* Only "rm" globs */
1161 if (cmdnum != I_RM)
1162 undo_glob_escape(*path1);
416fd2a8 1163 break;
6f25cbdd 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;
416fd2a8 1176 case I_LS:
bd5d5d2a 1177 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
416fd2a8 1178 return(-1);
1179 /* Path is optional */
bd5d5d2a 1180 if (argc - optidx > 0)
1181 *path1 = xstrdup(argv[optidx]);
416fd2a8 1182 break;
1183 case I_LLS:
bd5d5d2a 1184 /* Skip ls command and following whitespace */
1185 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
416fd2a8 1186 case I_SHELL:
1187 /* Uses the rest of the line */
1188 break;
1189 case I_LUMASK:
416fd2a8 1190 case I_CHMOD:
1191 base = 8;
1192 case I_CHOWN:
1193 case I_CHGRP:
1194 /* Get numeric arg (mandatory) */
bd5d5d2a 1195 if (argc - optidx < 1)
1196 goto need_num_arg;
2e437378 1197 errno = 0;
bd5d5d2a 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:
416fd2a8 1203 error("You must supply a numeric argument "
1204 "to the %s command.", cmd);
bd5d5d2a 1205 return -1;
416fd2a8 1206 }
416fd2a8 1207 *n_arg = l;
bd5d5d2a 1208 if (cmdnum == I_LUMASK)
416fd2a8 1209 break;
416fd2a8 1210 /* Get pathname (mandatory) */
bd5d5d2a 1211 if (argc - optidx < 2) {
416fd2a8 1212 error("You must specify a path after a %s command.",
1213 cmd);
bd5d5d2a 1214 return -1;
416fd2a8 1215 }
bd5d5d2a 1216 *path1 = xstrdup(argv[optidx + 1]);
416fd2a8 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;
16eb0a3b 1238 int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
1239 unsigned long n_arg = 0;
416fd2a8 1240 Attrib a, *aa;
1241 char path_buf[MAXPATHLEN];
1242 int err = 0;
1243 glob_t g;
1244
1245 path1 = path2 = NULL;
6f25cbdd 1246 cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg,
416fd2a8 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);
1b56ff3d 1281 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
416fd2a8 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;
6f25cbdd 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;
416fd2a8 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);
1b56ff3d 1377 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
416fd2a8 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);
1b56ff3d 1388 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
416fd2a8 1389 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
16eb0a3b 1390 if (err_abort) {
1391 err = -1;
416fd2a8 1392 break;
16eb0a3b 1393 } else
416fd2a8 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]);
16eb0a3b 1399 if (err_abort) {
1400 err = -1;
416fd2a8 1401 break;
16eb0a3b 1402 } else
416fd2a8 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
34fee935 1465#ifdef USE_LIBEDIT
1466static char *
1467prompt(EditLine *el)
1468{
1469 return ("sftp> ");
1470}
1471#endif
1472
416fd2a8 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;
34fee935 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 */
416fd2a8 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);
34fee935 1518 if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) {
1519 xfree(dir);
1520 xfree(pwd);
2e437378 1521 xfree(conn);
416fd2a8 1522 return (-1);
34fee935 1523 }
416fd2a8 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);
2e437378 1534 xfree(conn);
416fd2a8 1535 return (err);
1536 }
1537 xfree(dir);
1538 }
1539
34fee935 1540#if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF)
416fd2a8 1541 setvbuf(stdout, NULL, _IOLBF, 0);
1542 setvbuf(infile, NULL, _IOLBF, 0);
1543#else
34fee935 1544 setlinebuf(stdout);
1545 setlinebuf(infile);
416fd2a8 1546#endif
1547
34fee935 1548 interactive = !batchmode && isatty(STDIN_FILENO);
416fd2a8 1549 err = 0;
1550 for (;;) {
1551 char *cp;
1552
1b56ff3d 1553 signal(SIGINT, SIG_IGN);
1554
34fee935 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;
416fd2a8 1573
34fee935 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 */
416fd2a8 1584 }
1585
416fd2a8 1586 cp = strrchr(cmd, '\n');
1587 if (cp)
1588 *cp = '\0';
1589
1b56ff3d 1590 /* Handle user interrupts gracefully during commands */
1591 interrupted = 0;
1592 signal(SIGINT, cmd_interrupt);
1593
416fd2a8 1594 err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
1595 if (err != 0)
1596 break;
1597 }
1598 xfree(pwd);
2e437378 1599 xfree(conn);
416fd2a8 1600
34fee935 1601#ifdef USE_LIBEDIT
1602 if (el != NULL)
1603 el_end(el);
1604#endif /* USE_LIBEDIT */
1605
416fd2a8 1606 /* err == 1 signifies normal "quit" exit */
1607 return (err >= 0 ? 0 : -1);
1608}
1c14df9e 1609
70791e56 1610static void
1611connect_to_server(char *path, char **args, int *in, int *out)
3c0ef626 1612{
1613 int c_in, c_out;
ff2d7a98 1614
3c0ef626 1615#ifdef USE_PIPES
1616 int pin[2], pout[2];
ff2d7a98 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];
ff2d7a98 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
70791e56 1633 if ((sshpid = fork()) == -1)
3c0ef626 1634 fatal("fork: %s", strerror(errno));
70791e56 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));
1b56ff3d 1639 _exit(1);
3c0ef626 1640 }
1641 close(*in);
1642 close(*out);
1643 close(c_in);
1644 close(c_out);
1b56ff3d 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);
e9a17296 1654 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
1b56ff3d 1655 _exit(1);
3c0ef626 1656 }
1657
70791e56 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{
e9a17296 1668 extern char *__progname;
2980ea68 1669
3c0ef626 1670 fprintf(stderr,
416fd2a8 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"
16eb0a3b 1674 " %s [user@]host[:file ...]\n"
1675 " %s [user@]host[:dir[/]]\n"
416fd2a8 1676 " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname);
3c0ef626 1677 exit(1);
1678}
1679
1680int
1681main(int argc, char **argv)
1682{
1c14df9e 1683 int in, out, ch, err;
1b56ff3d 1684 char *host, *userhost, *cp, *file2 = NULL;
3c0ef626 1685 int debug_level = 0, sshver = 2;
1686 char *file1 = NULL, *sftp_server = NULL;
ae43c103 1687 char *ssh_program = NULL, *sftp_direct = NULL;
3c0ef626 1688 LogLevel ll = SYSLOG_LEVEL_INFO;
1689 arglist args;
1690 extern int optind;
1691 extern char *optarg;
1692
e00da40d 1693 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1694 sanitise_stdfd();
1695
70791e56 1696 __progname = ssh_get_progname(argv[0]);
ae43c103 1697 init_pathnames();
1698 ssh_program = _PATH_SSH_PROGRAM;
e00da40d 1699 memset(&args, '\0', sizeof(args));
3c0ef626 1700 args.list = NULL;
2e437378 1701 addargs(&args, "%s", ssh_program);
3c0ef626 1702 addargs(&args, "-oForwardX11 no");
1703 addargs(&args, "-oForwardAgent no");
e00da40d 1704 addargs(&args, "-oPermitLocalCommand no");
3c0ef626 1705 addargs(&args, "-oClearAllForwardings yes");
416fd2a8 1706
3c0ef626 1707 ll = SYSLOG_LEVEL_INFO;
416fd2a8 1708 infile = stdin;
3c0ef626 1709
e9a17296 1710 while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
3c0ef626 1711 switch (ch) {
1712 case 'C':
1713 addargs(&args, "-C");
1714 break;
1715 case 'v':
1716 if (debug_level < 3) {
1717 addargs(&args, "-v");
1718 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
1719 }
1720 debug_level++;
1721 break;
1722 case 'F':
1723 case 'o':
1724 addargs(&args, "-%c%s", ch, optarg);
1725 break;
1726 case '1':
1727 sshver = 1;
1728 if (sftp_server == NULL)
1729 sftp_server = _PATH_SFTP_SERVER;
1730 break;
1731 case 's':
1732 sftp_server = optarg;
1733 break;
1734 case 'S':
1735 ssh_program = optarg;
e00da40d 1736 replacearg(&args, 0, "%s", ssh_program);
3c0ef626 1737 break;
1738 case 'b':
416fd2a8 1739 if (batchmode)
1740 fatal("Batch file already specified.");
1741
1742 /* Allow "-" as stdin */
1b56ff3d 1743 if (strcmp(optarg, "-") != 0 &&
34fee935 1744 (infile = fopen(optarg, "r")) == NULL)
416fd2a8 1745 fatal("%s (%s).", strerror(errno), optarg);
1c14df9e 1746 showprogress = 0;
416fd2a8 1747 batchmode = 1;
34fee935 1748 addargs(&args, "-obatchmode yes");
3c0ef626 1749 break;
e9a17296 1750 case 'P':
1751 sftp_direct = optarg;
1752 break;
1753 case 'B':
1754 copy_buffer_len = strtol(optarg, &cp, 10);
1755 if (copy_buffer_len == 0 || *cp != '\0')
1756 fatal("Invalid buffer size \"%s\"", optarg);
1757 break;
1758 case 'R':
1759 num_requests = strtol(optarg, &cp, 10);
1760 if (num_requests == 0 || *cp != '\0')
2980ea68 1761 fatal("Invalid number of requests \"%s\"",
e9a17296 1762 optarg);
1763 break;
3c0ef626 1764 case 'h':
1765 default:
1766 usage();
1767 }
1768 }
1769
2a304a95 1770 if (!isatty(STDERR_FILENO))
1771 showprogress = 0;
1772
2980ea68 1773 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
1774
e9a17296 1775 if (sftp_direct == NULL) {
1776 if (optind == argc || argc > (optind + 2))
1777 usage();
3c0ef626 1778
e9a17296 1779 userhost = xstrdup(argv[optind]);
1780 file2 = argv[optind+1];
3c0ef626 1781
1c14df9e 1782 if ((host = strrchr(userhost, '@')) == NULL)
e9a17296 1783 host = userhost;
1784 else {
1785 *host++ = '\0';
1786 if (!userhost[0]) {
1787 fprintf(stderr, "Missing username\n");
1788 usage();
1789 }
2278ffa1 1790 addargs(&args, "-l%s", userhost);
3c0ef626 1791 }
3c0ef626 1792
416fd2a8 1793 if ((cp = colon(host)) != NULL) {
1794 *cp++ = '\0';
1795 file1 = cp;
1796 }
1797
e9a17296 1798 host = cleanhostname(host);
1799 if (!*host) {
1800 fprintf(stderr, "Missing hostname\n");
1801 usage();
1802 }
3c0ef626 1803
e9a17296 1804 addargs(&args, "-oProtocol %d", sshver);
3c0ef626 1805
e9a17296 1806 /* no subsystem if the server-spec contains a '/' */
1807 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
1808 addargs(&args, "-s");
3c0ef626 1809
e9a17296 1810 addargs(&args, "%s", host);
2980ea68 1811 addargs(&args, "%s", (sftp_server != NULL ?
e9a17296 1812 sftp_server : "sftp"));
3c0ef626 1813
416fd2a8 1814 if (!batchmode)
1815 fprintf(stderr, "Connecting to %s...\n", host);
70791e56 1816 connect_to_server(ssh_program, args.list, &in, &out);
e9a17296 1817 } else {
1818 args.list = NULL;
1819 addargs(&args, "sftp-server");
3c0ef626 1820
416fd2a8 1821 if (!batchmode)
1822 fprintf(stderr, "Attaching to %s...\n", sftp_direct);
70791e56 1823 connect_to_server(sftp_direct, args.list, &in, &out);
e9a17296 1824 }
e00da40d 1825 freeargs(&args);
3c0ef626 1826
1c14df9e 1827 err = interactive_loop(in, out, file1, file2);
3c0ef626 1828
1829#if !defined(USE_PIPES)
34fee935 1830 shutdown(in, SHUT_RDWR);
1831 shutdown(out, SHUT_RDWR);
3c0ef626 1832#endif
1833
1834 close(in);
1835 close(out);
416fd2a8 1836 if (batchmode)
3c0ef626 1837 fclose(infile);
1838
2980ea68 1839 while (waitpid(sshpid, NULL, 0) == -1)
1840 if (errno != EINTR)
1841 fatal("Couldn't wait for ssh process: %s",
1842 strerror(errno));
3c0ef626 1843
1c14df9e 1844 exit(err == 0 ? 0 : 1);
3c0ef626 1845}
This page took 0.343571 seconds and 5 git commands to generate.