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