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