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