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