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