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