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