1 /* $NetBSD: sftp.c,v 1.11 2013/03/29 16:19:45 christos Exp $ */ 2 /* $OpenBSD: sftp.c,v 1.142 2013/02/08 00:41:12 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.11 2013/03/29 16:19:45 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 */ 58 FILE* infile; 59 60 /* Are we in batchfile mode? */ 61 int batchmode = 0; 62 63 /* PID of ssh transport process */ 64 static pid_t sshpid = -1; 65 66 /* This is set to 0 if the progressmeter is not desired. */ 67 int showprogress = 1; 68 69 /* When this option is set, we always recursively download/upload directories */ 70 int global_rflag = 0; 71 72 /* When this option is set, the file transfers will always preserve times */ 73 int global_pflag = 0; 74 75 /* SIGINT received during command processing */ 76 volatile sig_atomic_t interrupted = 0; 77 78 /* I wish qsort() took a separate ctx for the comparison function...*/ 79 int sort_flag; 80 81 /* Context used for commandline completion */ 82 struct complete_ctx { 83 struct sftp_conn *conn; 84 char **remote_pathp; 85 }; 86 87 int 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 134 struct 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 145 static 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 182 int interactive_loop(struct sftp_conn *, const char *file1, const char *file2); 183 184 /* ARGSUSED */ 185 __dead static void 186 killchild(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 */ 197 static void 198 cmd_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 208 static void 209 help(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 244 static void 245 local_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 282 static void 283 local_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 */ 299 static char * 300 path_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 317 static char * 318 make_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 331 static int 332 parse_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 361 static int 362 parse_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 385 static int 386 parse_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 438 static int 439 parse_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 465 static int 466 is_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 477 static int 478 remote_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 */ 491 static int 492 pathname_is_dir(char *pathname) 493 { 494 size_t l = strlen(pathname); 495 496 return l > 0 && pathname[l - 1] == '/'; 497 } 498 499 static int 500 process_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 567 out: 568 xfree(abs_src); 569 globfree(&g); 570 return(err); 571 } 572 573 static int 574 process_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 650 out: 651 if (abs_dst) 652 xfree(abs_dst); 653 if (tmp_dst) 654 xfree(tmp_dst); 655 globfree(&g); 656 return(err); 657 } 658 659 static int 660 sdirent_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 */ 680 static int 681 do_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 */ 765 static int 766 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, 767 int lflag) 768 { 769 char *fname, *lname; 770 glob_t g; 771 int err; 772 struct winsize ws; 773 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80; 774 struct stat *stp; 775 #ifndef GLOB_KEEPSTAT 776 struct stat st; 777 #define GLOB_KEEPSTAT 0 778 #endif 779 780 memset(&g, 0, sizeof(g)); 781 782 if (remote_glob(conn, path, 783 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT, 784 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++) { 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 861 static int 862 do_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 */ 916 static void 917 undo_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 969 static char ** 970 makeargv(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 ((size_t)argc >= sizeof(argv) / sizeof(*argv)){ 993 error("Too many arguments."); 994 return NULL; 995 } 996 if (isspace((unsigned char)arg[i])) { 997 if (state == MA_UNQUOTED) { 998 /* Terminate current argument */ 999 argvs[j++] = '\0'; 1000 argc++; 1001 state = MA_START; 1002 } else if (state != MA_START) 1003 argvs[j++] = arg[i]; 1004 } else if (arg[i] == '"' || arg[i] == '\'') { 1005 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE; 1006 if (state == MA_START) { 1007 argv[argc] = argvs + j; 1008 state = q; 1009 if (lastquote != NULL) 1010 *lastquote = arg[i]; 1011 } else if (state == MA_UNQUOTED) 1012 state = q; 1013 else if (state == q) 1014 state = MA_UNQUOTED; 1015 else 1016 argvs[j++] = arg[i]; 1017 } else if (arg[i] == '\\') { 1018 if (state == MA_SQUOTE || state == MA_DQUOTE) { 1019 quot = state == MA_SQUOTE ? '\'' : '"'; 1020 /* Unescape quote we are in */ 1021 /* XXX support \n and friends? */ 1022 if (arg[i + 1] == quot) { 1023 i++; 1024 argvs[j++] = arg[i]; 1025 } else if (arg[i + 1] == '?' || 1026 arg[i + 1] == '[' || arg[i + 1] == '*') { 1027 /* 1028 * Special case for sftp: append 1029 * double-escaped glob sequence - 1030 * glob will undo one level of 1031 * escaping. NB. string can grow here. 1032 */ 1033 if (j >= sizeof(argvs) - 5) 1034 goto args_too_longs; 1035 argvs[j++] = '\\'; 1036 argvs[j++] = arg[i++]; 1037 argvs[j++] = '\\'; 1038 argvs[j++] = arg[i]; 1039 } else { 1040 argvs[j++] = arg[i++]; 1041 argvs[j++] = arg[i]; 1042 } 1043 } else { 1044 if (state == MA_START) { 1045 argv[argc] = argvs + j; 1046 state = MA_UNQUOTED; 1047 if (lastquote != NULL) 1048 *lastquote = '\0'; 1049 } 1050 if (arg[i + 1] == '?' || arg[i + 1] == '[' || 1051 arg[i + 1] == '*' || arg[i + 1] == '\\') { 1052 /* 1053 * Special case for sftp: append 1054 * escaped glob sequence - 1055 * glob will undo one level of 1056 * escaping. 1057 */ 1058 argvs[j++] = arg[i++]; 1059 argvs[j++] = arg[i]; 1060 } else { 1061 /* Unescape everything */ 1062 /* XXX support \n and friends? */ 1063 i++; 1064 argvs[j++] = arg[i]; 1065 } 1066 } 1067 } else if (arg[i] == '#') { 1068 if (state == MA_SQUOTE || state == MA_DQUOTE) 1069 argvs[j++] = arg[i]; 1070 else 1071 goto string_done; 1072 } else if (arg[i] == '\0') { 1073 if (state == MA_SQUOTE || state == MA_DQUOTE) { 1074 if (sloppy) { 1075 state = MA_UNQUOTED; 1076 if (terminated != NULL) 1077 *terminated = 0; 1078 goto string_done; 1079 } 1080 error("Unterminated quoted argument"); 1081 return NULL; 1082 } 1083 string_done: 1084 if (state == MA_UNQUOTED) { 1085 argvs[j++] = '\0'; 1086 argc++; 1087 } 1088 break; 1089 } else { 1090 if (state == MA_START) { 1091 argv[argc] = argvs + j; 1092 state = MA_UNQUOTED; 1093 if (lastquote != NULL) 1094 *lastquote = '\0'; 1095 } 1096 if ((state == MA_SQUOTE || state == MA_DQUOTE) && 1097 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { 1098 /* 1099 * Special case for sftp: escape quoted 1100 * glob(3) wildcards. NB. string can grow 1101 * here. 1102 */ 1103 if (j >= sizeof(argvs) - 3) 1104 goto args_too_longs; 1105 argvs[j++] = '\\'; 1106 argvs[j++] = arg[i]; 1107 } else 1108 argvs[j++] = arg[i]; 1109 } 1110 i++; 1111 } 1112 *argcp = argc; 1113 return argv; 1114 } 1115 1116 static int 1117 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, 1118 int *hflag, int *sflag, unsigned long *n_arg, char **path1, char **path2) 1119 { 1120 const char *cmd, *cp = *cpp; 1121 char *cp2, **argv; 1122 int base = 0; 1123 long l; 1124 int i, cmdnum, optidx, argc; 1125 1126 /* Skip leading whitespace */ 1127 cp = cp + strspn(cp, WHITESPACE); 1128 1129 /* Check for leading '-' (disable error processing) */ 1130 *iflag = 0; 1131 if (*cp == '-') { 1132 *iflag = 1; 1133 cp++; 1134 cp = cp + strspn(cp, WHITESPACE); 1135 } 1136 1137 /* Ignore blank lines and lines which begin with comment '#' char */ 1138 if (*cp == '\0' || *cp == '#') 1139 return (0); 1140 1141 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) 1142 return -1; 1143 1144 /* Figure out which command we have */ 1145 for (i = 0; cmds[i].c != NULL; i++) { 1146 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0) 1147 break; 1148 } 1149 cmdnum = cmds[i].n; 1150 cmd = cmds[i].c; 1151 1152 /* Special case */ 1153 if (*cp == '!') { 1154 cp++; 1155 cmdnum = I_SHELL; 1156 } else if (cmdnum == -1) { 1157 error("Invalid command."); 1158 return -1; 1159 } 1160 1161 /* Get arguments and parse flags */ 1162 *lflag = *pflag = *rflag = *hflag = *n_arg = 0; 1163 *path1 = *path2 = NULL; 1164 optidx = 1; 1165 switch (cmdnum) { 1166 case I_GET: 1167 case I_PUT: 1168 if ((optidx = parse_getput_flags(cmd, argv, argc, 1169 pflag, rflag)) == -1) 1170 return -1; 1171 /* Get first pathname (mandatory) */ 1172 if (argc - optidx < 1) { 1173 error("You must specify at least one path after a " 1174 "%s command.", cmd); 1175 return -1; 1176 } 1177 *path1 = xstrdup(argv[optidx]); 1178 /* Get second pathname (optional) */ 1179 if (argc - optidx > 1) { 1180 *path2 = xstrdup(argv[optidx + 1]); 1181 /* Destination is not globbed */ 1182 undo_glob_escape(*path2); 1183 } 1184 break; 1185 case I_LINK: 1186 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1) 1187 return -1; 1188 case I_SYMLINK: 1189 case I_RENAME: 1190 if (argc - optidx < 2) { 1191 error("You must specify two paths after a %s " 1192 "command.", cmd); 1193 return -1; 1194 } 1195 *path1 = xstrdup(argv[optidx]); 1196 *path2 = xstrdup(argv[optidx + 1]); 1197 /* Paths are not globbed */ 1198 undo_glob_escape(*path1); 1199 undo_glob_escape(*path2); 1200 break; 1201 case I_RM: 1202 case I_MKDIR: 1203 case I_RMDIR: 1204 case I_CHDIR: 1205 case I_LCHDIR: 1206 case I_LMKDIR: 1207 /* Get pathname (mandatory) */ 1208 if (argc - optidx < 1) { 1209 error("You must specify a path after a %s command.", 1210 cmd); 1211 return -1; 1212 } 1213 *path1 = xstrdup(argv[optidx]); 1214 /* Only "rm" globs */ 1215 if (cmdnum != I_RM) 1216 undo_glob_escape(*path1); 1217 break; 1218 case I_DF: 1219 if ((optidx = parse_df_flags(cmd, argv, argc, hflag, 1220 iflag)) == -1) 1221 return -1; 1222 /* Default to current directory if no path specified */ 1223 if (argc - optidx < 1) 1224 *path1 = NULL; 1225 else { 1226 *path1 = xstrdup(argv[optidx]); 1227 undo_glob_escape(*path1); 1228 } 1229 break; 1230 case I_LS: 1231 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1) 1232 return(-1); 1233 /* Path is optional */ 1234 if (argc - optidx > 0) 1235 *path1 = xstrdup(argv[optidx]); 1236 break; 1237 case I_LLS: 1238 /* Skip ls command and following whitespace */ 1239 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE); 1240 case I_SHELL: 1241 /* Uses the rest of the line */ 1242 break; 1243 case I_LUMASK: 1244 case I_CHMOD: 1245 base = 8; 1246 case I_CHOWN: 1247 case I_CHGRP: 1248 /* Get numeric arg (mandatory) */ 1249 if (argc - optidx < 1) 1250 goto need_num_arg; 1251 errno = 0; 1252 l = strtol(argv[optidx], &cp2, base); 1253 if (cp2 == argv[optidx] || *cp2 != '\0' || 1254 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) || 1255 l < 0) { 1256 need_num_arg: 1257 error("You must supply a numeric argument " 1258 "to the %s command.", cmd); 1259 return -1; 1260 } 1261 *n_arg = l; 1262 if (cmdnum == I_LUMASK) 1263 break; 1264 /* Get pathname (mandatory) */ 1265 if (argc - optidx < 2) { 1266 error("You must specify a path after a %s command.", 1267 cmd); 1268 return -1; 1269 } 1270 *path1 = xstrdup(argv[optidx + 1]); 1271 break; 1272 case I_QUIT: 1273 case I_PWD: 1274 case I_LPWD: 1275 case I_HELP: 1276 case I_VERSION: 1277 case I_PROGRESS: 1278 break; 1279 default: 1280 fatal("Command not implemented"); 1281 } 1282 1283 *cpp = cp; 1284 return(cmdnum); 1285 } 1286 1287 static int 1288 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 1289 int err_abort) 1290 { 1291 char *path1, *path2, *tmp; 1292 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0; 1293 int cmdnum, i; 1294 unsigned long n_arg = 0; 1295 Attrib a, *aa; 1296 char path_buf[MAXPATHLEN]; 1297 int err = 0; 1298 glob_t g; 1299 1300 pflag = 0; /* XXX gcc */ 1301 lflag = 0; /* XXX gcc */ 1302 iflag = 0; /* XXX gcc */ 1303 hflag = 0; /* XXX gcc */ 1304 n_arg = 0; /* XXX gcc */ 1305 1306 path1 = path2 = NULL; 1307 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, 1308 &sflag, &n_arg, &path1, &path2); 1309 1310 if (iflag != 0) 1311 err_abort = 0; 1312 1313 memset(&g, 0, sizeof(g)); 1314 1315 /* Perform command */ 1316 switch (cmdnum) { 1317 case 0: 1318 /* Blank line */ 1319 break; 1320 case -1: 1321 /* Unrecognized command */ 1322 err = -1; 1323 break; 1324 case I_GET: 1325 err = process_get(conn, path1, path2, *pwd, pflag, rflag); 1326 break; 1327 case I_PUT: 1328 err = process_put(conn, path1, path2, *pwd, pflag, rflag); 1329 break; 1330 case I_RENAME: 1331 path1 = make_absolute(path1, *pwd); 1332 path2 = make_absolute(path2, *pwd); 1333 err = do_rename(conn, path1, path2); 1334 break; 1335 case I_SYMLINK: 1336 sflag = 1; 1337 case I_LINK: 1338 path1 = make_absolute(path1, *pwd); 1339 path2 = make_absolute(path2, *pwd); 1340 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2); 1341 break; 1342 case I_RM: 1343 path1 = make_absolute(path1, *pwd); 1344 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1345 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1346 printf("Removing %s\n", g.gl_pathv[i]); 1347 err = do_rm(conn, g.gl_pathv[i]); 1348 if (err != 0 && err_abort) 1349 break; 1350 } 1351 break; 1352 case I_MKDIR: 1353 path1 = make_absolute(path1, *pwd); 1354 attrib_clear(&a); 1355 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1356 a.perm = 0777; 1357 err = do_mkdir(conn, path1, &a, 1); 1358 break; 1359 case I_RMDIR: 1360 path1 = make_absolute(path1, *pwd); 1361 err = do_rmdir(conn, path1); 1362 break; 1363 case I_CHDIR: 1364 path1 = make_absolute(path1, *pwd); 1365 if ((tmp = do_realpath(conn, path1)) == NULL) { 1366 err = 1; 1367 break; 1368 } 1369 if ((aa = do_stat(conn, tmp, 0)) == NULL) { 1370 xfree(tmp); 1371 err = 1; 1372 break; 1373 } 1374 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { 1375 error("Can't change directory: Can't check target"); 1376 xfree(tmp); 1377 err = 1; 1378 break; 1379 } 1380 if (!S_ISDIR(aa->perm)) { 1381 error("Can't change directory: \"%s\" is not " 1382 "a directory", tmp); 1383 xfree(tmp); 1384 err = 1; 1385 break; 1386 } 1387 xfree(*pwd); 1388 *pwd = tmp; 1389 break; 1390 case I_LS: 1391 if (!path1) { 1392 do_ls_dir(conn, *pwd, *pwd, lflag); 1393 break; 1394 } 1395 1396 /* Strip pwd off beginning of non-absolute paths */ 1397 tmp = NULL; 1398 if (*path1 != '/') 1399 tmp = *pwd; 1400 1401 path1 = make_absolute(path1, *pwd); 1402 err = do_globbed_ls(conn, path1, tmp, lflag); 1403 break; 1404 case I_DF: 1405 /* Default to current directory if no path specified */ 1406 if (path1 == NULL) 1407 path1 = xstrdup(*pwd); 1408 path1 = make_absolute(path1, *pwd); 1409 err = do_df(conn, path1, hflag, iflag); 1410 break; 1411 case I_LCHDIR: 1412 if (chdir(path1) == -1) { 1413 error("Couldn't change local directory to " 1414 "\"%s\": %s", path1, strerror(errno)); 1415 err = 1; 1416 } 1417 break; 1418 case I_LMKDIR: 1419 if (mkdir(path1, 0777) == -1) { 1420 error("Couldn't create local directory " 1421 "\"%s\": %s", path1, strerror(errno)); 1422 err = 1; 1423 } 1424 break; 1425 case I_LLS: 1426 local_do_ls(cmd); 1427 break; 1428 case I_SHELL: 1429 local_do_shell(cmd); 1430 break; 1431 case I_LUMASK: 1432 umask(n_arg); 1433 printf("Local umask: %03lo\n", n_arg); 1434 break; 1435 case I_CHMOD: 1436 path1 = make_absolute(path1, *pwd); 1437 attrib_clear(&a); 1438 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1439 a.perm = n_arg; 1440 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1441 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1442 printf("Changing mode on %s\n", g.gl_pathv[i]); 1443 err = do_setstat(conn, g.gl_pathv[i], &a); 1444 if (err != 0 && err_abort) 1445 break; 1446 } 1447 break; 1448 case I_CHOWN: 1449 case I_CHGRP: 1450 path1 = make_absolute(path1, *pwd); 1451 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1452 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1453 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1454 if (err_abort) { 1455 err = -1; 1456 break; 1457 } else 1458 continue; 1459 } 1460 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1461 error("Can't get current ownership of " 1462 "remote file \"%s\"", g.gl_pathv[i]); 1463 if (err_abort) { 1464 err = -1; 1465 break; 1466 } else 1467 continue; 1468 } 1469 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1470 if (cmdnum == I_CHOWN) { 1471 printf("Changing owner on %s\n", g.gl_pathv[i]); 1472 aa->uid = n_arg; 1473 } else { 1474 printf("Changing group on %s\n", g.gl_pathv[i]); 1475 aa->gid = n_arg; 1476 } 1477 err = do_setstat(conn, g.gl_pathv[i], aa); 1478 if (err != 0 && err_abort) 1479 break; 1480 } 1481 break; 1482 case I_PWD: 1483 printf("Remote working directory: %s\n", *pwd); 1484 break; 1485 case I_LPWD: 1486 if (!getcwd(path_buf, sizeof(path_buf))) { 1487 error("Couldn't get local cwd: %s", strerror(errno)); 1488 err = -1; 1489 break; 1490 } 1491 printf("Local working directory: %s\n", path_buf); 1492 break; 1493 case I_QUIT: 1494 /* Processed below */ 1495 break; 1496 case I_HELP: 1497 help(); 1498 break; 1499 case I_VERSION: 1500 printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1501 break; 1502 case I_PROGRESS: 1503 showprogress = !showprogress; 1504 if (showprogress) 1505 printf("Progress meter enabled\n"); 1506 else 1507 printf("Progress meter disabled\n"); 1508 break; 1509 default: 1510 fatal("%d is not implemented", cmdnum); 1511 } 1512 1513 if (g.gl_pathc) 1514 globfree(&g); 1515 if (path1) 1516 xfree(path1); 1517 if (path2) 1518 xfree(path2); 1519 1520 /* If an unignored error occurs in batch mode we should abort. */ 1521 if (err_abort && err != 0) 1522 return (-1); 1523 else if (cmdnum == I_QUIT) 1524 return (1); 1525 1526 return (0); 1527 } 1528 1529 static const char * 1530 prompt(EditLine *el) 1531 { 1532 return ("sftp> "); 1533 } 1534 1535 /* Display entries in 'list' after skipping the first 'len' chars */ 1536 static void 1537 complete_display(char **list, u_int len) 1538 { 1539 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen; 1540 struct winsize ws; 1541 const char *tmp; 1542 1543 /* Count entries for sort and find longest */ 1544 for (y = 0; list[y]; y++) 1545 m = MAX(m, strlen(list[y])); 1546 1547 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 1548 width = ws.ws_col; 1549 1550 m = m > len ? m - len : 0; 1551 columns = width / (m + 2); 1552 columns = MAX(columns, 1); 1553 colspace = width / columns; 1554 colspace = MIN(colspace, width); 1555 1556 printf("\n"); 1557 m = 1; 1558 for (y = 0; list[y]; y++) { 1559 llen = strlen(list[y]); 1560 tmp = llen > len ? list[y] + len : ""; 1561 printf("%-*s", colspace, tmp); 1562 if (m >= columns) { 1563 printf("\n"); 1564 m = 1; 1565 } else 1566 m++; 1567 } 1568 printf("\n"); 1569 } 1570 1571 /* 1572 * Given a "list" of words that begin with a common prefix of "word", 1573 * attempt to find an autocompletion to extends "word" by the next 1574 * characters common to all entries in "list". 1575 */ 1576 static char * 1577 complete_ambiguous(const char *word, char **list, size_t count) 1578 { 1579 if (word == NULL) 1580 return NULL; 1581 1582 if (count > 0) { 1583 u_int y, matchlen = strlen(list[0]); 1584 1585 /* Find length of common stem */ 1586 for (y = 1; list[y]; y++) { 1587 u_int x; 1588 1589 for (x = 0; x < matchlen; x++) 1590 if (list[0][x] != list[y][x]) 1591 break; 1592 1593 matchlen = x; 1594 } 1595 1596 if (matchlen > strlen(word)) { 1597 char *tmp = xstrdup(list[0]); 1598 1599 tmp[matchlen] = '\0'; 1600 return tmp; 1601 } 1602 } 1603 1604 return xstrdup(word); 1605 } 1606 1607 /* Autocomplete a sftp command */ 1608 static int 1609 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, 1610 int terminated) 1611 { 1612 u_int y, count = 0, cmdlen, tmplen; 1613 char *tmp, **list, argterm[3]; 1614 const LineInfo *lf; 1615 1616 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *)); 1617 1618 /* No command specified: display all available commands */ 1619 if (cmd == NULL) { 1620 for (y = 0; cmds[y].c; y++) 1621 list[count++] = xstrdup(cmds[y].c); 1622 1623 list[count] = NULL; 1624 complete_display(list, 0); 1625 1626 for (y = 0; list[y] != NULL; y++) 1627 xfree(list[y]); 1628 xfree(list); 1629 return count; 1630 } 1631 1632 /* Prepare subset of commands that start with "cmd" */ 1633 cmdlen = strlen(cmd); 1634 for (y = 0; cmds[y].c; y++) { 1635 if (!strncasecmp(cmd, cmds[y].c, cmdlen)) 1636 list[count++] = xstrdup(cmds[y].c); 1637 } 1638 list[count] = NULL; 1639 1640 if (count == 0) { 1641 xfree(list); 1642 return 0; 1643 } 1644 1645 /* Complete ambigious command */ 1646 tmp = complete_ambiguous(cmd, list, count); 1647 if (count > 1) 1648 complete_display(list, 0); 1649 1650 for (y = 0; list[y]; y++) 1651 xfree(list[y]); 1652 xfree(list); 1653 1654 if (tmp != NULL) { 1655 tmplen = strlen(tmp); 1656 cmdlen = strlen(cmd); 1657 /* If cmd may be extended then do so */ 1658 if (tmplen > cmdlen) 1659 if (el_insertstr(el, tmp + cmdlen) == -1) 1660 fatal("el_insertstr failed."); 1661 lf = el_line(el); 1662 /* Terminate argument cleanly */ 1663 if (count == 1) { 1664 y = 0; 1665 if (!terminated) 1666 argterm[y++] = quote; 1667 if (lastarg || *(lf->cursor) != ' ') 1668 argterm[y++] = ' '; 1669 argterm[y] = '\0'; 1670 if (y > 0 && el_insertstr(el, argterm) == -1) 1671 fatal("el_insertstr failed."); 1672 } 1673 xfree(tmp); 1674 } 1675 1676 return count; 1677 } 1678 1679 /* 1680 * Determine whether a particular sftp command's arguments (if any) 1681 * represent local or remote files. 1682 */ 1683 static int 1684 complete_is_remote(char *cmd) { 1685 int i; 1686 1687 if (cmd == NULL) 1688 return -1; 1689 1690 for (i = 0; cmds[i].c; i++) { 1691 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) 1692 return cmds[i].t; 1693 } 1694 1695 return -1; 1696 } 1697 1698 /* Autocomplete a filename "file" */ 1699 static int 1700 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, 1701 char *file, int remote, int lastarg, char quote, int terminated) 1702 { 1703 glob_t g; 1704 char *tmp, *tmp2, ins[3]; 1705 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs; 1706 const LineInfo *lf; 1707 1708 /* Glob from "file" location */ 1709 if (file == NULL) 1710 tmp = xstrdup("*"); 1711 else 1712 xasprintf(&tmp, "%s*", file); 1713 1714 /* Check if the path is absolute. */ 1715 isabs = tmp[0] == '/'; 1716 1717 memset(&g, 0, sizeof(g)); 1718 if (remote != LOCAL) { 1719 tmp = make_absolute(tmp, remote_path); 1720 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); 1721 } else 1722 glob(tmp, GLOB_LIMIT|GLOB_DOOFFS|GLOB_MARK, NULL, &g); 1723 1724 /* Determine length of pwd so we can trim completion display */ 1725 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) { 1726 /* Terminate counting on first unescaped glob metacharacter */ 1727 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') { 1728 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0') 1729 hadglob = 1; 1730 break; 1731 } 1732 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0') 1733 tmplen++; 1734 if (tmp[tmplen] == '/') 1735 pwdlen = tmplen + 1; /* track last seen '/' */ 1736 } 1737 xfree(tmp); 1738 1739 if (g.gl_matchc == 0) 1740 goto out; 1741 1742 if (g.gl_matchc > 1) 1743 complete_display(g.gl_pathv, pwdlen); 1744 1745 tmp = NULL; 1746 /* Don't try to extend globs */ 1747 if (file == NULL || hadglob) 1748 goto out; 1749 1750 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); 1751 tmp = path_strip(tmp2, isabs ? NULL : remote_path); 1752 xfree(tmp2); 1753 1754 if (tmp == NULL) 1755 goto out; 1756 1757 tmplen = strlen(tmp); 1758 filelen = strlen(file); 1759 1760 /* Count the number of escaped characters in the input string. */ 1761 cesc = isesc = 0; 1762 for (i = 0; i < filelen; i++) { 1763 if (!isesc && file[i] == '\\' && i + 1 < filelen){ 1764 isesc = 1; 1765 cesc++; 1766 } else 1767 isesc = 0; 1768 } 1769 1770 if (tmplen > (filelen - cesc)) { 1771 tmp2 = tmp + filelen - cesc; 1772 len = strlen(tmp2); 1773 /* quote argument on way out */ 1774 for (i = 0; i < len; i++) { 1775 ins[0] = '\\'; 1776 ins[1] = tmp2[i]; 1777 ins[2] = '\0'; 1778 switch (tmp2[i]) { 1779 case '\'': 1780 case '"': 1781 case '\\': 1782 case '\t': 1783 case '[': 1784 case ' ': 1785 case '#': 1786 case '*': 1787 if (quote == '\0' || tmp2[i] == quote) { 1788 if (el_insertstr(el, ins) == -1) 1789 fatal("el_insertstr " 1790 "failed."); 1791 break; 1792 } 1793 /* FALLTHROUGH */ 1794 default: 1795 if (el_insertstr(el, ins + 1) == -1) 1796 fatal("el_insertstr failed."); 1797 break; 1798 } 1799 } 1800 } 1801 1802 lf = el_line(el); 1803 if (g.gl_matchc == 1) { 1804 i = 0; 1805 if (!terminated) 1806 ins[i++] = quote; 1807 if (*(lf->cursor - 1) != '/' && 1808 (lastarg || *(lf->cursor) != ' ')) 1809 ins[i++] = ' '; 1810 ins[i] = '\0'; 1811 if (i > 0 && el_insertstr(el, ins) == -1) 1812 fatal("el_insertstr failed."); 1813 } 1814 xfree(tmp); 1815 1816 out: 1817 globfree(&g); 1818 return g.gl_matchc; 1819 } 1820 1821 /* tab-completion hook function, called via libedit */ 1822 static unsigned char 1823 complete(EditLine *el, int ch) 1824 { 1825 char **argv, *line, quote; 1826 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR; 1827 const LineInfo *lf; 1828 struct complete_ctx *complete_ctx; 1829 1830 lf = el_line(el); 1831 if (el_get(el, EL_CLIENTDATA, &complete_ctx) != 0) 1832 fatal("%s: el_get failed", __func__); 1833 1834 /* Figure out which argument the cursor points to */ 1835 cursor = lf->cursor - lf->buffer; 1836 line = (char *)xmalloc(cursor + 1); 1837 memcpy(line, lf->buffer, cursor); 1838 line[cursor] = '\0'; 1839 argv = makeargv(line, &carg, 1, "e, &terminated); 1840 xfree(line); 1841 1842 /* Get all the arguments on the line */ 1843 len = lf->lastchar - lf->buffer; 1844 line = (char *)xmalloc(len + 1); 1845 memcpy(line, lf->buffer, len); 1846 line[len] = '\0'; 1847 argv = makeargv(line, &argc, 1, NULL, NULL); 1848 1849 /* Ensure cursor is at EOL or a argument boundary */ 1850 if (line[cursor] != ' ' && line[cursor] != '\0' && 1851 line[cursor] != '\n') { 1852 xfree(line); 1853 return ret; 1854 } 1855 1856 if (carg == 0) { 1857 /* Show all available commands */ 1858 complete_cmd_parse(el, NULL, argc == carg, '\0', 1); 1859 ret = CC_REDISPLAY; 1860 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') { 1861 /* Handle the command parsing */ 1862 if (complete_cmd_parse(el, argv[0], argc == carg, 1863 quote, terminated) != 0) 1864 ret = CC_REDISPLAY; 1865 } else if (carg >= 1) { 1866 /* Handle file parsing */ 1867 int remote = complete_is_remote(argv[0]); 1868 char *filematch = NULL; 1869 1870 if (carg > 1 && line[cursor-1] != ' ') 1871 filematch = argv[carg - 1]; 1872 1873 if (remote != 0 && 1874 complete_match(el, complete_ctx->conn, 1875 *complete_ctx->remote_pathp, filematch, 1876 remote, carg == argc, quote, terminated) != 0) 1877 ret = CC_REDISPLAY; 1878 } 1879 1880 xfree(line); 1881 return ret; 1882 } 1883 1884 int 1885 interactive_loop(struct sftp_conn *conn, const char *file1, const char *file2) 1886 { 1887 char *remote_path; 1888 char *dir = NULL; 1889 char cmd[2048]; 1890 int err, interactive; 1891 EditLine *el = NULL; 1892 History *hl = NULL; 1893 HistEvent hev; 1894 extern char *__progname; 1895 struct complete_ctx complete_ctx; 1896 1897 if (!batchmode && isatty(STDIN_FILENO)) { 1898 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) 1899 fatal("Couldn't initialise editline"); 1900 if ((hl = history_init()) == NULL) 1901 fatal("Couldn't initialise editline history"); 1902 history(hl, &hev, H_SETSIZE, 100); 1903 el_set(el, EL_HIST, history, hl); 1904 1905 el_set(el, EL_PROMPT, prompt); 1906 el_set(el, EL_EDITOR, "emacs"); 1907 el_set(el, EL_TERMINAL, NULL); 1908 el_set(el, EL_SIGNAL, 1); 1909 el_source(el, NULL); 1910 1911 /* Tab Completion */ 1912 el_set(el, EL_ADDFN, "ftp-complete", 1913 "Context sensitive argument completion", complete); 1914 complete_ctx.conn = conn; 1915 complete_ctx.remote_pathp = &remote_path; 1916 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); 1917 el_set(el, EL_BIND, "^I", "ftp-complete", NULL); 1918 } 1919 1920 remote_path = do_realpath(conn, "."); 1921 if (remote_path == NULL) 1922 fatal("Need cwd"); 1923 1924 if (file1 != NULL) { 1925 dir = xstrdup(file1); 1926 dir = make_absolute(dir, remote_path); 1927 1928 if (remote_is_dir(conn, dir) && file2 == NULL) { 1929 printf("Changing to: %s\n", dir); 1930 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 1931 if (parse_dispatch_command(conn, cmd, 1932 &remote_path, 1) != 0) { 1933 xfree(dir); 1934 xfree(remote_path); 1935 xfree(conn); 1936 return (-1); 1937 } 1938 } else { 1939 /* XXX this is wrong wrt quoting */ 1940 if (file2 == NULL) 1941 snprintf(cmd, sizeof cmd, "get %s", dir); 1942 else 1943 snprintf(cmd, sizeof cmd, "get %s %s", dir, 1944 file2); 1945 1946 err = parse_dispatch_command(conn, cmd, 1947 &remote_path, 1); 1948 xfree(dir); 1949 xfree(remote_path); 1950 xfree(conn); 1951 return (err); 1952 } 1953 xfree(dir); 1954 } 1955 1956 setlinebuf(stdout); 1957 setlinebuf(infile); 1958 1959 interactive = !batchmode && isatty(STDIN_FILENO); 1960 err = 0; 1961 for (;;) { 1962 char *cp; 1963 const char *line; 1964 int count = 0; 1965 1966 signal(SIGINT, SIG_IGN); 1967 1968 if (el == NULL) { 1969 if (interactive) 1970 printf("sftp> "); 1971 if (fgets(cmd, sizeof(cmd), infile) == NULL) { 1972 if (interactive) 1973 printf("\n"); 1974 break; 1975 } 1976 if (!interactive) { /* Echo command */ 1977 printf("sftp> %s", cmd); 1978 if (strlen(cmd) > 0 && 1979 cmd[strlen(cmd) - 1] != '\n') 1980 printf("\n"); 1981 } 1982 } else { 1983 if ((line = el_gets(el, &count)) == NULL || 1984 count <= 0) { 1985 printf("\n"); 1986 break; 1987 } 1988 history(hl, &hev, H_ENTER, line); 1989 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { 1990 fprintf(stderr, "Error: input line too long\n"); 1991 continue; 1992 } 1993 } 1994 1995 cp = strrchr(cmd, '\n'); 1996 if (cp) 1997 *cp = '\0'; 1998 1999 /* Handle user interrupts gracefully during commands */ 2000 interrupted = 0; 2001 signal(SIGINT, cmd_interrupt); 2002 2003 err = parse_dispatch_command(conn, cmd, &remote_path, 2004 batchmode); 2005 if (err != 0) 2006 break; 2007 } 2008 xfree(remote_path); 2009 xfree(conn); 2010 2011 if (el != NULL) 2012 el_end(el); 2013 2014 /* err == 1 signifies normal "quit" exit */ 2015 return (err >= 0 ? 0 : -1); 2016 } 2017 2018 static void 2019 connect_to_server(const char *path, char **args, int *in, int *out) 2020 { 2021 int c_in, c_out; 2022 2023 int inout[2]; 2024 2025 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 2026 fatal("socketpair: %s", strerror(errno)); 2027 *in = *out = inout[0]; 2028 c_in = c_out = inout[1]; 2029 2030 if ((sshpid = fork()) == -1) 2031 fatal("fork: %s", strerror(errno)); 2032 else if (sshpid == 0) { 2033 if ((dup2(c_in, STDIN_FILENO) == -1) || 2034 (dup2(c_out, STDOUT_FILENO) == -1)) { 2035 fprintf(stderr, "dup2: %s\n", strerror(errno)); 2036 _exit(1); 2037 } 2038 close(*in); 2039 close(*out); 2040 close(c_in); 2041 close(c_out); 2042 2043 /* 2044 * The underlying ssh is in the same process group, so we must 2045 * ignore SIGINT if we want to gracefully abort commands, 2046 * otherwise the signal will make it to the ssh process and 2047 * kill it too. Contrawise, since sftp sends SIGTERMs to the 2048 * underlying ssh, it must *not* ignore that signal. 2049 */ 2050 signal(SIGINT, SIG_IGN); 2051 signal(SIGTERM, SIG_DFL); 2052 execvp(path, args); 2053 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 2054 _exit(1); 2055 } 2056 2057 signal(SIGTERM, killchild); 2058 signal(SIGINT, killchild); 2059 signal(SIGHUP, killchild); 2060 close(c_in); 2061 close(c_out); 2062 } 2063 2064 __dead static void 2065 usage(void) 2066 { 2067 extern char *__progname; 2068 2069 fprintf(stderr, 2070 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" 2071 " [-D sftp_server_path] [-F ssh_config] " 2072 "[-i identity_file] [-l limit]\n" 2073 " [-o ssh_option] [-P port] [-R num_requests] " 2074 "[-S program]\n" 2075 " [-s subsystem | sftp_server] host\n" 2076 " %s [user@]host[:file ...]\n" 2077 " %s [user@]host[:dir[/]]\n" 2078 " %s -b batchfile [user@]host\n", 2079 __progname, __progname, __progname, __progname); 2080 exit(1); 2081 } 2082 2083 int 2084 main(int argc, char **argv) 2085 { 2086 int in, out, ch, err; 2087 char *host = NULL, *userhost, *cp, *file2 = NULL; 2088 int debug_level = 0, sshver = 2; 2089 const char *file1 = NULL, *sftp_server = NULL; 2090 const char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 2091 const char *errstr; 2092 LogLevel ll = SYSLOG_LEVEL_INFO; 2093 arglist args; 2094 extern int optind; 2095 extern char *optarg; 2096 struct sftp_conn *conn; 2097 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; 2098 size_t num_requests = DEFAULT_NUM_REQUESTS; 2099 long long limit_kbps = 0; 2100 2101 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 2102 sanitise_stdfd(); 2103 2104 memset(&args, '\0', sizeof(args)); 2105 args.list = NULL; 2106 addargs(&args, "%s", ssh_program); 2107 addargs(&args, "-oForwardX11 no"); 2108 addargs(&args, "-oForwardAgent no"); 2109 addargs(&args, "-oPermitLocalCommand no"); 2110 addargs(&args, "-oClearAllForwardings yes"); 2111 2112 ll = SYSLOG_LEVEL_INFO; 2113 infile = stdin; 2114 2115 while ((ch = getopt(argc, argv, 2116 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) { 2117 switch (ch) { 2118 /* Passed through to ssh(1) */ 2119 case '4': 2120 case '6': 2121 case 'C': 2122 addargs(&args, "-%c", ch); 2123 break; 2124 /* Passed through to ssh(1) with argument */ 2125 case 'F': 2126 case 'c': 2127 case 'i': 2128 case 'o': 2129 addargs(&args, "-%c", ch); 2130 addargs(&args, "%s", optarg); 2131 break; 2132 case 'q': 2133 showprogress = 0; 2134 addargs(&args, "-%c", ch); 2135 break; 2136 case 'P': 2137 addargs(&args, "-oPort %s", optarg); 2138 break; 2139 case 'v': 2140 if (debug_level < 3) { 2141 addargs(&args, "-v"); 2142 ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 2143 } 2144 debug_level++; 2145 break; 2146 case '1': 2147 sshver = 1; 2148 if (sftp_server == NULL) 2149 sftp_server = _PATH_SFTP_SERVER; 2150 break; 2151 case '2': 2152 sshver = 2; 2153 break; 2154 case 'B': 2155 copy_buffer_len = strtol(optarg, &cp, 10); 2156 if (copy_buffer_len == 0 || *cp != '\0') 2157 fatal("Invalid buffer size \"%s\"", optarg); 2158 break; 2159 case 'b': 2160 if (batchmode) 2161 fatal("Batch file already specified."); 2162 2163 /* Allow "-" as stdin */ 2164 if (strcmp(optarg, "-") != 0 && 2165 (infile = fopen(optarg, "r")) == NULL) 2166 fatal("%s (%s).", strerror(errno), optarg); 2167 showprogress = 0; 2168 batchmode = 1; 2169 addargs(&args, "-obatchmode yes"); 2170 break; 2171 case 'p': 2172 global_pflag = 1; 2173 break; 2174 case 'D': 2175 sftp_direct = optarg; 2176 break; 2177 case 'l': 2178 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, 2179 &errstr); 2180 if (errstr != NULL) 2181 usage(); 2182 limit_kbps *= 1024; /* kbps */ 2183 break; 2184 case 'r': 2185 global_rflag = 1; 2186 break; 2187 case 'R': 2188 num_requests = strtol(optarg, &cp, 10); 2189 if (num_requests == 0 || *cp != '\0') 2190 fatal("Invalid number of requests \"%s\"", 2191 optarg); 2192 break; 2193 case 's': 2194 sftp_server = optarg; 2195 break; 2196 case 'S': 2197 ssh_program = optarg; 2198 replacearg(&args, 0, "%s", ssh_program); 2199 break; 2200 case 'h': 2201 default: 2202 usage(); 2203 } 2204 } 2205 2206 if (!isatty(STDERR_FILENO)) 2207 showprogress = 0; 2208 2209 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 2210 2211 if (sftp_direct == NULL) { 2212 if (optind == argc || argc > (optind + 2)) 2213 usage(); 2214 2215 userhost = xstrdup(argv[optind]); 2216 file2 = argv[optind+1]; 2217 2218 if ((host = strrchr(userhost, '@')) == NULL) 2219 host = userhost; 2220 else { 2221 *host++ = '\0'; 2222 if (!userhost[0]) { 2223 fprintf(stderr, "Missing username\n"); 2224 usage(); 2225 } 2226 addargs(&args, "-l"); 2227 addargs(&args, "%s", userhost); 2228 } 2229 2230 if ((cp = colon(host)) != NULL) { 2231 *cp++ = '\0'; 2232 file1 = cp; 2233 } 2234 2235 host = cleanhostname(host); 2236 if (!*host) { 2237 fprintf(stderr, "Missing hostname\n"); 2238 usage(); 2239 } 2240 2241 addargs(&args, "-oProtocol %d", sshver); 2242 2243 /* no subsystem if the server-spec contains a '/' */ 2244 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 2245 addargs(&args, "-s"); 2246 2247 addargs(&args, "--"); 2248 addargs(&args, "%s", host); 2249 addargs(&args, "%s", (sftp_server != NULL ? 2250 sftp_server : "sftp")); 2251 2252 connect_to_server(ssh_program, args.list, &in, &out); 2253 } else { 2254 args.list = NULL; 2255 addargs(&args, "sftp-server"); 2256 2257 connect_to_server(sftp_direct, args.list, &in, &out); 2258 } 2259 freeargs(&args); 2260 2261 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps); 2262 if (conn == NULL) 2263 fatal("Couldn't initialise connection to server"); 2264 2265 if (!batchmode) { 2266 if (sftp_direct == NULL) 2267 fprintf(stderr, "Connected to %s.\n", host); 2268 else 2269 fprintf(stderr, "Attached to %s.\n", sftp_direct); 2270 } 2271 2272 err = interactive_loop(conn, file1, file2); 2273 2274 close(in); 2275 close(out); 2276 if (batchmode) 2277 fclose(infile); 2278 2279 while (waitpid(sshpid, NULL, 0) == -1) 2280 if (errno != EINTR) 2281 fatal("Couldn't wait for ssh process: %s", 2282 strerror(errno)); 2283 2284 exit(err == 0 ? 0 : 1); 2285 } 2286