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