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