1 /* $OpenBSD: sftp.c,v 1.206 2021/01/08 02:44:14 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 if (timespeccmp(&as->st_mtim, &bs->st_mtim, ==)) 872 return 0; 873 return timespeccmp(&as->st_mtim, &bs->st_mtim, <) ? 874 rmul : -rmul; 875 } else if (sort_flag & LS_SIZE_SORT) 876 return (rmul * NCMP(as->st_size, bs->st_size)); 877 878 fatal("Unknown ls sort type"); 879 } 880 881 /* sftp ls.1 replacement which handles path globs */ 882 static int 883 do_globbed_ls(struct sftp_conn *conn, const char *path, 884 const char *strip_path, int lflag) 885 { 886 char *fname, *lname; 887 glob_t g; 888 int err, r; 889 struct winsize ws; 890 u_int i, j, nentries, *indices = NULL, c = 1; 891 u_int colspace = 0, columns = 1, m = 0, width = 80; 892 893 memset(&g, 0, sizeof(g)); 894 895 if ((r = remote_glob(conn, path, 896 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT, 897 NULL, &g)) != 0 || 898 (g.gl_pathc && !g.gl_matchc)) { 899 if (g.gl_pathc) 900 globfree(&g); 901 if (r == GLOB_NOSPACE) { 902 error("Can't ls: Too many matches for \"%s\"", path); 903 } else { 904 error("Can't ls: \"%s\" not found", path); 905 } 906 return -1; 907 } 908 909 if (interrupted) 910 goto out; 911 912 /* 913 * If the glob returns a single match and it is a directory, 914 * then just list its contents. 915 */ 916 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL && 917 S_ISDIR(g.gl_statv[0]->st_mode)) { 918 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); 919 globfree(&g); 920 return err; 921 } 922 923 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 924 width = ws.ws_col; 925 926 if (!(lflag & LS_SHORT_VIEW)) { 927 /* Count entries for sort and find longest filename */ 928 for (i = 0; g.gl_pathv[i]; i++) 929 m = MAXIMUM(m, strlen(g.gl_pathv[i])); 930 931 columns = width / (m + 2); 932 columns = MAXIMUM(columns, 1); 933 colspace = width / columns; 934 } 935 936 /* 937 * Sorting: rather than mess with the contents of glob_t, prepare 938 * an array of indices into it and sort that. For the usual 939 * unsorted case, the indices are just the identity 1=1, 2=2, etc. 940 */ 941 for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++) 942 ; /* count entries */ 943 indices = calloc(nentries, sizeof(*indices)); 944 for (i = 0; i < nentries; i++) 945 indices[i] = i; 946 947 if (lflag & SORT_FLAGS) { 948 sort_glob = &g; 949 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); 950 qsort(indices, nentries, sizeof(*indices), sglob_comp); 951 sort_glob = NULL; 952 } 953 954 for (j = 0; j < nentries && !interrupted; j++) { 955 i = indices[j]; 956 fname = path_strip(g.gl_pathv[i], strip_path); 957 if (lflag & LS_LONG_VIEW) { 958 if (g.gl_statv[i] == NULL) { 959 error("no stat information for %s", fname); 960 continue; 961 } 962 lname = ls_file(fname, g.gl_statv[i], 1, 963 (lflag & LS_SI_UNITS)); 964 mprintf("%s\n", lname); 965 free(lname); 966 } else { 967 mprintf("%-*s", colspace, fname); 968 if (c >= columns) { 969 printf("\n"); 970 c = 1; 971 } else 972 c++; 973 } 974 free(fname); 975 } 976 977 if (!(lflag & LS_LONG_VIEW) && (c != 1)) 978 printf("\n"); 979 980 out: 981 if (g.gl_pathc) 982 globfree(&g); 983 free(indices); 984 985 return 0; 986 } 987 988 static int 989 do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag) 990 { 991 struct sftp_statvfs st; 992 char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE]; 993 char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE]; 994 char s_icapacity[16], s_dcapacity[16]; 995 996 if (do_statvfs(conn, path, &st, 1) == -1) 997 return -1; 998 if (st.f_files == 0) 999 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity)); 1000 else { 1001 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%", 1002 (unsigned long long)(100 * (st.f_files - st.f_ffree) / 1003 st.f_files)); 1004 } 1005 if (st.f_blocks == 0) 1006 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity)); 1007 else { 1008 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%", 1009 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / 1010 st.f_blocks)); 1011 } 1012 if (iflag) { 1013 printf(" Inodes Used Avail " 1014 "(root) %%Capacity\n"); 1015 printf("%11llu %11llu %11llu %11llu %s\n", 1016 (unsigned long long)st.f_files, 1017 (unsigned long long)(st.f_files - st.f_ffree), 1018 (unsigned long long)st.f_favail, 1019 (unsigned long long)st.f_ffree, s_icapacity); 1020 } else if (hflag) { 1021 strlcpy(s_used, "error", sizeof(s_used)); 1022 strlcpy(s_avail, "error", sizeof(s_avail)); 1023 strlcpy(s_root, "error", sizeof(s_root)); 1024 strlcpy(s_total, "error", sizeof(s_total)); 1025 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used); 1026 fmt_scaled(st.f_bavail * st.f_frsize, s_avail); 1027 fmt_scaled(st.f_bfree * st.f_frsize, s_root); 1028 fmt_scaled(st.f_blocks * st.f_frsize, s_total); 1029 printf(" Size Used Avail (root) %%Capacity\n"); 1030 printf("%7sB %7sB %7sB %7sB %s\n", 1031 s_total, s_used, s_avail, s_root, s_dcapacity); 1032 } else { 1033 printf(" Size Used Avail " 1034 "(root) %%Capacity\n"); 1035 printf("%12llu %12llu %12llu %12llu %s\n", 1036 (unsigned long long)(st.f_frsize * st.f_blocks / 1024), 1037 (unsigned long long)(st.f_frsize * 1038 (st.f_blocks - st.f_bfree) / 1024), 1039 (unsigned long long)(st.f_frsize * st.f_bavail / 1024), 1040 (unsigned long long)(st.f_frsize * st.f_bfree / 1024), 1041 s_dcapacity); 1042 } 1043 return 0; 1044 } 1045 1046 /* 1047 * Undo escaping of glob sequences in place. Used to undo extra escaping 1048 * applied in makeargv() when the string is destined for a function that 1049 * does not glob it. 1050 */ 1051 static void 1052 undo_glob_escape(char *s) 1053 { 1054 size_t i, j; 1055 1056 for (i = j = 0;;) { 1057 if (s[i] == '\0') { 1058 s[j] = '\0'; 1059 return; 1060 } 1061 if (s[i] != '\\') { 1062 s[j++] = s[i++]; 1063 continue; 1064 } 1065 /* s[i] == '\\' */ 1066 ++i; 1067 switch (s[i]) { 1068 case '?': 1069 case '[': 1070 case '*': 1071 case '\\': 1072 s[j++] = s[i++]; 1073 break; 1074 case '\0': 1075 s[j++] = '\\'; 1076 s[j] = '\0'; 1077 return; 1078 default: 1079 s[j++] = '\\'; 1080 s[j++] = s[i++]; 1081 break; 1082 } 1083 } 1084 } 1085 1086 /* 1087 * Split a string into an argument vector using sh(1)-style quoting, 1088 * comment and escaping rules, but with some tweaks to handle glob(3) 1089 * wildcards. 1090 * The "sloppy" flag allows for recovery from missing terminating quote, for 1091 * use in parsing incomplete commandlines during tab autocompletion. 1092 * 1093 * Returns NULL on error or a NULL-terminated array of arguments. 1094 * 1095 * If "lastquote" is not NULL, the quoting character used for the last 1096 * argument is placed in *lastquote ("\0", "'" or "\""). 1097 * 1098 * If "terminated" is not NULL, *terminated will be set to 1 when the 1099 * last argument's quote has been properly terminated or 0 otherwise. 1100 * This parameter is only of use if "sloppy" is set. 1101 */ 1102 #define MAXARGS 128 1103 #define MAXARGLEN 8192 1104 static char ** 1105 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, 1106 u_int *terminated) 1107 { 1108 int argc, quot; 1109 size_t i, j; 1110 static char argvs[MAXARGLEN]; 1111 static char *argv[MAXARGS + 1]; 1112 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q; 1113 1114 *argcp = argc = 0; 1115 if (strlen(arg) > sizeof(argvs) - 1) { 1116 args_too_longs: 1117 error("string too long"); 1118 return NULL; 1119 } 1120 if (terminated != NULL) 1121 *terminated = 1; 1122 if (lastquote != NULL) 1123 *lastquote = '\0'; 1124 state = MA_START; 1125 i = j = 0; 1126 for (;;) { 1127 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){ 1128 error("Too many arguments."); 1129 return NULL; 1130 } 1131 if (isspace((unsigned char)arg[i])) { 1132 if (state == MA_UNQUOTED) { 1133 /* Terminate current argument */ 1134 argvs[j++] = '\0'; 1135 argc++; 1136 state = MA_START; 1137 } else if (state != MA_START) 1138 argvs[j++] = arg[i]; 1139 } else if (arg[i] == '"' || arg[i] == '\'') { 1140 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE; 1141 if (state == MA_START) { 1142 argv[argc] = argvs + j; 1143 state = q; 1144 if (lastquote != NULL) 1145 *lastquote = arg[i]; 1146 } else if (state == MA_UNQUOTED) 1147 state = q; 1148 else if (state == q) 1149 state = MA_UNQUOTED; 1150 else 1151 argvs[j++] = arg[i]; 1152 } else if (arg[i] == '\\') { 1153 if (state == MA_SQUOTE || state == MA_DQUOTE) { 1154 quot = state == MA_SQUOTE ? '\'' : '"'; 1155 /* Unescape quote we are in */ 1156 /* XXX support \n and friends? */ 1157 if (arg[i + 1] == quot) { 1158 i++; 1159 argvs[j++] = arg[i]; 1160 } else if (arg[i + 1] == '?' || 1161 arg[i + 1] == '[' || arg[i + 1] == '*') { 1162 /* 1163 * Special case for sftp: append 1164 * double-escaped glob sequence - 1165 * glob will undo one level of 1166 * escaping. NB. string can grow here. 1167 */ 1168 if (j >= sizeof(argvs) - 5) 1169 goto args_too_longs; 1170 argvs[j++] = '\\'; 1171 argvs[j++] = arg[i++]; 1172 argvs[j++] = '\\'; 1173 argvs[j++] = arg[i]; 1174 } else { 1175 argvs[j++] = arg[i++]; 1176 argvs[j++] = arg[i]; 1177 } 1178 } else { 1179 if (state == MA_START) { 1180 argv[argc] = argvs + j; 1181 state = MA_UNQUOTED; 1182 if (lastquote != NULL) 1183 *lastquote = '\0'; 1184 } 1185 if (arg[i + 1] == '?' || arg[i + 1] == '[' || 1186 arg[i + 1] == '*' || arg[i + 1] == '\\') { 1187 /* 1188 * Special case for sftp: append 1189 * escaped glob sequence - 1190 * glob will undo one level of 1191 * escaping. 1192 */ 1193 argvs[j++] = arg[i++]; 1194 argvs[j++] = arg[i]; 1195 } else { 1196 /* Unescape everything */ 1197 /* XXX support \n and friends? */ 1198 i++; 1199 argvs[j++] = arg[i]; 1200 } 1201 } 1202 } else if (arg[i] == '#') { 1203 if (state == MA_SQUOTE || state == MA_DQUOTE) 1204 argvs[j++] = arg[i]; 1205 else 1206 goto string_done; 1207 } else if (arg[i] == '\0') { 1208 if (state == MA_SQUOTE || state == MA_DQUOTE) { 1209 if (sloppy) { 1210 state = MA_UNQUOTED; 1211 if (terminated != NULL) 1212 *terminated = 0; 1213 goto string_done; 1214 } 1215 error("Unterminated quoted argument"); 1216 return NULL; 1217 } 1218 string_done: 1219 if (state == MA_UNQUOTED) { 1220 argvs[j++] = '\0'; 1221 argc++; 1222 } 1223 break; 1224 } else { 1225 if (state == MA_START) { 1226 argv[argc] = argvs + j; 1227 state = MA_UNQUOTED; 1228 if (lastquote != NULL) 1229 *lastquote = '\0'; 1230 } 1231 if ((state == MA_SQUOTE || state == MA_DQUOTE) && 1232 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { 1233 /* 1234 * Special case for sftp: escape quoted 1235 * glob(3) wildcards. NB. string can grow 1236 * here. 1237 */ 1238 if (j >= sizeof(argvs) - 3) 1239 goto args_too_longs; 1240 argvs[j++] = '\\'; 1241 argvs[j++] = arg[i]; 1242 } else 1243 argvs[j++] = arg[i]; 1244 } 1245 i++; 1246 } 1247 *argcp = argc; 1248 return argv; 1249 } 1250 1251 static int 1252 parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag, 1253 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag, 1254 int *rflag, int *sflag, 1255 unsigned long *n_arg, char **path1, char **path2) 1256 { 1257 const char *cmd, *cp = *cpp; 1258 char *cp2, **argv; 1259 int base = 0; 1260 long long ll; 1261 int path1_mandatory = 0, i, cmdnum, optidx, argc; 1262 1263 /* Skip leading whitespace */ 1264 cp = cp + strspn(cp, WHITESPACE); 1265 1266 /* 1267 * Check for leading '-' (disable error processing) and '@' (suppress 1268 * command echo) 1269 */ 1270 *ignore_errors = 0; 1271 *disable_echo = 0; 1272 for (;*cp != '\0'; cp++) { 1273 if (*cp == '-') { 1274 *ignore_errors = 1; 1275 } else if (*cp == '@') { 1276 *disable_echo = 1; 1277 } else { 1278 /* all other characters terminate prefix processing */ 1279 break; 1280 } 1281 } 1282 cp = cp + strspn(cp, WHITESPACE); 1283 1284 /* Ignore blank lines and lines which begin with comment '#' char */ 1285 if (*cp == '\0' || *cp == '#') 1286 return (0); 1287 1288 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) 1289 return -1; 1290 1291 /* Figure out which command we have */ 1292 for (i = 0; cmds[i].c != NULL; i++) { 1293 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0) 1294 break; 1295 } 1296 cmdnum = cmds[i].n; 1297 cmd = cmds[i].c; 1298 1299 /* Special case */ 1300 if (*cp == '!') { 1301 cp++; 1302 cmdnum = I_SHELL; 1303 } else if (cmdnum == -1) { 1304 error("Invalid command."); 1305 return -1; 1306 } 1307 1308 /* Get arguments and parse flags */ 1309 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0; 1310 *rflag = *sflag = 0; 1311 *path1 = *path2 = NULL; 1312 optidx = 1; 1313 switch (cmdnum) { 1314 case I_GET: 1315 case I_REGET: 1316 case I_REPUT: 1317 case I_PUT: 1318 if ((optidx = parse_getput_flags(cmd, argv, argc, 1319 aflag, fflag, pflag, rflag)) == -1) 1320 return -1; 1321 /* Get first pathname (mandatory) */ 1322 if (argc - optidx < 1) { 1323 error("You must specify at least one path after a " 1324 "%s command.", cmd); 1325 return -1; 1326 } 1327 *path1 = xstrdup(argv[optidx]); 1328 /* Get second pathname (optional) */ 1329 if (argc - optidx > 1) { 1330 *path2 = xstrdup(argv[optidx + 1]); 1331 /* Destination is not globbed */ 1332 undo_glob_escape(*path2); 1333 } 1334 break; 1335 case I_LINK: 1336 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1) 1337 return -1; 1338 goto parse_two_paths; 1339 case I_RENAME: 1340 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1) 1341 return -1; 1342 goto parse_two_paths; 1343 case I_SYMLINK: 1344 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) 1345 return -1; 1346 parse_two_paths: 1347 if (argc - optidx < 2) { 1348 error("You must specify two paths after a %s " 1349 "command.", cmd); 1350 return -1; 1351 } 1352 *path1 = xstrdup(argv[optidx]); 1353 *path2 = xstrdup(argv[optidx + 1]); 1354 /* Paths are not globbed */ 1355 undo_glob_escape(*path1); 1356 undo_glob_escape(*path2); 1357 break; 1358 case I_RM: 1359 case I_MKDIR: 1360 case I_RMDIR: 1361 case I_LMKDIR: 1362 path1_mandatory = 1; 1363 /* FALLTHROUGH */ 1364 case I_CHDIR: 1365 case I_LCHDIR: 1366 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) 1367 return -1; 1368 /* Get pathname (mandatory) */ 1369 if (argc - optidx < 1) { 1370 if (!path1_mandatory) 1371 break; /* return a NULL path1 */ 1372 error("You must specify a path after a %s command.", 1373 cmd); 1374 return -1; 1375 } 1376 *path1 = xstrdup(argv[optidx]); 1377 /* Only "rm" globs */ 1378 if (cmdnum != I_RM) 1379 undo_glob_escape(*path1); 1380 break; 1381 case I_DF: 1382 if ((optidx = parse_df_flags(cmd, argv, argc, hflag, 1383 iflag)) == -1) 1384 return -1; 1385 /* Default to current directory if no path specified */ 1386 if (argc - optidx < 1) 1387 *path1 = NULL; 1388 else { 1389 *path1 = xstrdup(argv[optidx]); 1390 undo_glob_escape(*path1); 1391 } 1392 break; 1393 case I_LS: 1394 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1) 1395 return(-1); 1396 /* Path is optional */ 1397 if (argc - optidx > 0) 1398 *path1 = xstrdup(argv[optidx]); 1399 break; 1400 case I_LLS: 1401 /* Skip ls command and following whitespace */ 1402 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE); 1403 case I_SHELL: 1404 /* Uses the rest of the line */ 1405 break; 1406 case I_LUMASK: 1407 case I_CHMOD: 1408 base = 8; 1409 /* FALLTHROUGH */ 1410 case I_CHOWN: 1411 case I_CHGRP: 1412 if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1) 1413 return -1; 1414 /* Get numeric arg (mandatory) */ 1415 if (argc - optidx < 1) 1416 goto need_num_arg; 1417 errno = 0; 1418 ll = strtoll(argv[optidx], &cp2, base); 1419 if (cp2 == argv[optidx] || *cp2 != '\0' || 1420 ((ll == LLONG_MIN || ll == LLONG_MAX) && errno == ERANGE) || 1421 ll < 0 || ll > UINT32_MAX) { 1422 need_num_arg: 1423 error("You must supply a numeric argument " 1424 "to the %s command.", cmd); 1425 return -1; 1426 } 1427 *n_arg = ll; 1428 if (cmdnum == I_LUMASK) 1429 break; 1430 /* Get pathname (mandatory) */ 1431 if (argc - optidx < 2) { 1432 error("You must specify a path after a %s command.", 1433 cmd); 1434 return -1; 1435 } 1436 *path1 = xstrdup(argv[optidx + 1]); 1437 break; 1438 case I_QUIT: 1439 case I_PWD: 1440 case I_LPWD: 1441 case I_HELP: 1442 case I_VERSION: 1443 case I_PROGRESS: 1444 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) 1445 return -1; 1446 break; 1447 default: 1448 fatal("Command not implemented"); 1449 } 1450 1451 *cpp = cp; 1452 return(cmdnum); 1453 } 1454 1455 static int 1456 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 1457 const char *startdir, int err_abort, int echo_command) 1458 { 1459 const char *ocmd = cmd; 1460 char *path1, *path2, *tmp; 1461 int ignore_errors = 0, disable_echo = 1; 1462 int aflag = 0, fflag = 0, hflag = 0, iflag = 0; 1463 int lflag = 0, pflag = 0, rflag = 0, sflag = 0; 1464 int cmdnum, i; 1465 unsigned long n_arg = 0; 1466 Attrib a, *aa; 1467 char path_buf[PATH_MAX]; 1468 int err = 0; 1469 glob_t g; 1470 1471 path1 = path2 = NULL; 1472 cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag, 1473 &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, 1474 &path1, &path2); 1475 if (ignore_errors != 0) 1476 err_abort = 0; 1477 1478 if (echo_command && !disable_echo) 1479 mprintf("sftp> %s\n", ocmd); 1480 1481 memset(&g, 0, sizeof(g)); 1482 1483 /* Perform command */ 1484 switch (cmdnum) { 1485 case 0: 1486 /* Blank line */ 1487 break; 1488 case -1: 1489 /* Unrecognized command */ 1490 err = -1; 1491 break; 1492 case I_REGET: 1493 aflag = 1; 1494 /* FALLTHROUGH */ 1495 case I_GET: 1496 err = process_get(conn, path1, path2, *pwd, pflag, 1497 rflag, aflag, fflag); 1498 break; 1499 case I_REPUT: 1500 aflag = 1; 1501 /* FALLTHROUGH */ 1502 case I_PUT: 1503 err = process_put(conn, path1, path2, *pwd, pflag, 1504 rflag, aflag, fflag); 1505 break; 1506 case I_RENAME: 1507 path1 = make_absolute(path1, *pwd); 1508 path2 = make_absolute(path2, *pwd); 1509 err = do_rename(conn, path1, path2, lflag); 1510 break; 1511 case I_SYMLINK: 1512 sflag = 1; 1513 /* FALLTHROUGH */ 1514 case I_LINK: 1515 if (!sflag) 1516 path1 = make_absolute(path1, *pwd); 1517 path2 = make_absolute(path2, *pwd); 1518 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2); 1519 break; 1520 case I_RM: 1521 path1 = make_absolute(path1, *pwd); 1522 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1523 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1524 if (!quiet) 1525 mprintf("Removing %s\n", g.gl_pathv[i]); 1526 err = do_rm(conn, g.gl_pathv[i]); 1527 if (err != 0 && err_abort) 1528 break; 1529 } 1530 break; 1531 case I_MKDIR: 1532 path1 = make_absolute(path1, *pwd); 1533 attrib_clear(&a); 1534 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1535 a.perm = 0777; 1536 err = do_mkdir(conn, path1, &a, 1); 1537 break; 1538 case I_RMDIR: 1539 path1 = make_absolute(path1, *pwd); 1540 err = do_rmdir(conn, path1); 1541 break; 1542 case I_CHDIR: 1543 if (path1 == NULL || *path1 == '\0') 1544 path1 = xstrdup(startdir); 1545 path1 = make_absolute(path1, *pwd); 1546 if ((tmp = do_realpath(conn, path1)) == NULL) { 1547 err = 1; 1548 break; 1549 } 1550 if ((aa = do_stat(conn, tmp, 0)) == NULL) { 1551 free(tmp); 1552 err = 1; 1553 break; 1554 } 1555 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { 1556 error("Can't change directory: Can't check target"); 1557 free(tmp); 1558 err = 1; 1559 break; 1560 } 1561 if (!S_ISDIR(aa->perm)) { 1562 error("Can't change directory: \"%s\" is not " 1563 "a directory", tmp); 1564 free(tmp); 1565 err = 1; 1566 break; 1567 } 1568 free(*pwd); 1569 *pwd = tmp; 1570 break; 1571 case I_LS: 1572 if (!path1) { 1573 do_ls_dir(conn, *pwd, *pwd, lflag); 1574 break; 1575 } 1576 1577 /* Strip pwd off beginning of non-absolute paths */ 1578 tmp = NULL; 1579 if (!path_absolute(path1)) 1580 tmp = *pwd; 1581 1582 path1 = make_absolute(path1, *pwd); 1583 err = do_globbed_ls(conn, path1, tmp, lflag); 1584 break; 1585 case I_DF: 1586 /* Default to current directory if no path specified */ 1587 if (path1 == NULL) 1588 path1 = xstrdup(*pwd); 1589 path1 = make_absolute(path1, *pwd); 1590 err = do_df(conn, path1, hflag, iflag); 1591 break; 1592 case I_LCHDIR: 1593 if (path1 == NULL || *path1 == '\0') 1594 path1 = xstrdup("~"); 1595 tmp = tilde_expand_filename(path1, getuid()); 1596 free(path1); 1597 path1 = tmp; 1598 if (chdir(path1) == -1) { 1599 error("Couldn't change local directory to " 1600 "\"%s\": %s", path1, strerror(errno)); 1601 err = 1; 1602 } 1603 break; 1604 case I_LMKDIR: 1605 if (mkdir(path1, 0777) == -1) { 1606 error("Couldn't create local directory " 1607 "\"%s\": %s", path1, strerror(errno)); 1608 err = 1; 1609 } 1610 break; 1611 case I_LLS: 1612 local_do_ls(cmd); 1613 break; 1614 case I_SHELL: 1615 local_do_shell(cmd); 1616 break; 1617 case I_LUMASK: 1618 umask(n_arg); 1619 printf("Local umask: %03lo\n", n_arg); 1620 break; 1621 case I_CHMOD: 1622 path1 = make_absolute(path1, *pwd); 1623 attrib_clear(&a); 1624 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1625 a.perm = n_arg; 1626 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1627 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1628 if (!quiet) 1629 mprintf("Changing mode on %s\n", 1630 g.gl_pathv[i]); 1631 err = (hflag ? do_lsetstat : do_setstat)(conn, 1632 g.gl_pathv[i], &a); 1633 if (err != 0 && err_abort) 1634 break; 1635 } 1636 break; 1637 case I_CHOWN: 1638 case I_CHGRP: 1639 path1 = make_absolute(path1, *pwd); 1640 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1641 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1642 if (!(aa = (hflag ? do_lstat : do_stat)(conn, 1643 g.gl_pathv[i], 0))) { 1644 if (err_abort) { 1645 err = -1; 1646 break; 1647 } else 1648 continue; 1649 } 1650 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1651 error("Can't get current ownership of " 1652 "remote file \"%s\"", g.gl_pathv[i]); 1653 if (err_abort) { 1654 err = -1; 1655 break; 1656 } else 1657 continue; 1658 } 1659 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1660 if (cmdnum == I_CHOWN) { 1661 if (!quiet) 1662 mprintf("Changing owner on %s\n", 1663 g.gl_pathv[i]); 1664 aa->uid = n_arg; 1665 } else { 1666 if (!quiet) 1667 mprintf("Changing group on %s\n", 1668 g.gl_pathv[i]); 1669 aa->gid = n_arg; 1670 } 1671 err = (hflag ? do_lsetstat : do_setstat)(conn, 1672 g.gl_pathv[i], aa); 1673 if (err != 0 && err_abort) 1674 break; 1675 } 1676 break; 1677 case I_PWD: 1678 mprintf("Remote working directory: %s\n", *pwd); 1679 break; 1680 case I_LPWD: 1681 if (!getcwd(path_buf, sizeof(path_buf))) { 1682 error("Couldn't get local cwd: %s", strerror(errno)); 1683 err = -1; 1684 break; 1685 } 1686 mprintf("Local working directory: %s\n", path_buf); 1687 break; 1688 case I_QUIT: 1689 /* Processed below */ 1690 break; 1691 case I_HELP: 1692 help(); 1693 break; 1694 case I_VERSION: 1695 printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1696 break; 1697 case I_PROGRESS: 1698 showprogress = !showprogress; 1699 if (showprogress) 1700 printf("Progress meter enabled\n"); 1701 else 1702 printf("Progress meter disabled\n"); 1703 break; 1704 default: 1705 fatal("%d is not implemented", cmdnum); 1706 } 1707 1708 if (g.gl_pathc) 1709 globfree(&g); 1710 free(path1); 1711 free(path2); 1712 1713 /* If an unignored error occurs in batch mode we should abort. */ 1714 if (err_abort && err != 0) 1715 return (-1); 1716 else if (cmdnum == I_QUIT) 1717 return (1); 1718 1719 return (0); 1720 } 1721 1722 static char * 1723 prompt(EditLine *el) 1724 { 1725 return ("sftp> "); 1726 } 1727 1728 /* Display entries in 'list' after skipping the first 'len' chars */ 1729 static void 1730 complete_display(char **list, u_int len) 1731 { 1732 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen; 1733 struct winsize ws; 1734 char *tmp; 1735 1736 /* Count entries for sort and find longest */ 1737 for (y = 0; list[y]; y++) 1738 m = MAXIMUM(m, strlen(list[y])); 1739 1740 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 1741 width = ws.ws_col; 1742 1743 m = m > len ? m - len : 0; 1744 columns = width / (m + 2); 1745 columns = MAXIMUM(columns, 1); 1746 colspace = width / columns; 1747 colspace = MINIMUM(colspace, width); 1748 1749 printf("\n"); 1750 m = 1; 1751 for (y = 0; list[y]; y++) { 1752 llen = strlen(list[y]); 1753 tmp = llen > len ? list[y] + len : ""; 1754 mprintf("%-*s", colspace, tmp); 1755 if (m >= columns) { 1756 printf("\n"); 1757 m = 1; 1758 } else 1759 m++; 1760 } 1761 printf("\n"); 1762 } 1763 1764 /* 1765 * Given a "list" of words that begin with a common prefix of "word", 1766 * attempt to find an autocompletion to extends "word" by the next 1767 * characters common to all entries in "list". 1768 */ 1769 static char * 1770 complete_ambiguous(const char *word, char **list, size_t count) 1771 { 1772 if (word == NULL) 1773 return NULL; 1774 1775 if (count > 0) { 1776 u_int y, matchlen = strlen(list[0]); 1777 1778 /* Find length of common stem */ 1779 for (y = 1; list[y]; y++) { 1780 u_int x; 1781 1782 for (x = 0; x < matchlen; x++) 1783 if (list[0][x] != list[y][x]) 1784 break; 1785 1786 matchlen = x; 1787 } 1788 1789 if (matchlen > strlen(word)) { 1790 char *tmp = xstrdup(list[0]); 1791 1792 tmp[matchlen] = '\0'; 1793 return tmp; 1794 } 1795 } 1796 1797 return xstrdup(word); 1798 } 1799 1800 /* Autocomplete a sftp command */ 1801 static int 1802 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, 1803 int terminated) 1804 { 1805 u_int y, count = 0, cmdlen, tmplen; 1806 char *tmp, **list, argterm[3]; 1807 const LineInfo *lf; 1808 1809 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *)); 1810 1811 /* No command specified: display all available commands */ 1812 if (cmd == NULL) { 1813 for (y = 0; cmds[y].c; y++) 1814 list[count++] = xstrdup(cmds[y].c); 1815 1816 list[count] = NULL; 1817 complete_display(list, 0); 1818 1819 for (y = 0; list[y] != NULL; y++) 1820 free(list[y]); 1821 free(list); 1822 return count; 1823 } 1824 1825 /* Prepare subset of commands that start with "cmd" */ 1826 cmdlen = strlen(cmd); 1827 for (y = 0; cmds[y].c; y++) { 1828 if (!strncasecmp(cmd, cmds[y].c, cmdlen)) 1829 list[count++] = xstrdup(cmds[y].c); 1830 } 1831 list[count] = NULL; 1832 1833 if (count == 0) { 1834 free(list); 1835 return 0; 1836 } 1837 1838 /* Complete ambiguous command */ 1839 tmp = complete_ambiguous(cmd, list, count); 1840 if (count > 1) 1841 complete_display(list, 0); 1842 1843 for (y = 0; list[y]; y++) 1844 free(list[y]); 1845 free(list); 1846 1847 if (tmp != NULL) { 1848 tmplen = strlen(tmp); 1849 cmdlen = strlen(cmd); 1850 /* If cmd may be extended then do so */ 1851 if (tmplen > cmdlen) 1852 if (el_insertstr(el, tmp + cmdlen) == -1) 1853 fatal("el_insertstr failed."); 1854 lf = el_line(el); 1855 /* Terminate argument cleanly */ 1856 if (count == 1) { 1857 y = 0; 1858 if (!terminated) 1859 argterm[y++] = quote; 1860 if (lastarg || *(lf->cursor) != ' ') 1861 argterm[y++] = ' '; 1862 argterm[y] = '\0'; 1863 if (y > 0 && el_insertstr(el, argterm) == -1) 1864 fatal("el_insertstr failed."); 1865 } 1866 free(tmp); 1867 } 1868 1869 return count; 1870 } 1871 1872 /* 1873 * Determine whether a particular sftp command's arguments (if any) 1874 * represent local or remote files. 1875 */ 1876 static int 1877 complete_is_remote(char *cmd) { 1878 int i; 1879 1880 if (cmd == NULL) 1881 return -1; 1882 1883 for (i = 0; cmds[i].c; i++) { 1884 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) 1885 return cmds[i].t; 1886 } 1887 1888 return -1; 1889 } 1890 1891 /* Autocomplete a filename "file" */ 1892 static int 1893 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, 1894 char *file, int remote, int lastarg, char quote, int terminated) 1895 { 1896 glob_t g; 1897 char *tmp, *tmp2, ins[8]; 1898 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs; 1899 int clen; 1900 const LineInfo *lf; 1901 1902 /* Glob from "file" location */ 1903 if (file == NULL) 1904 tmp = xstrdup("*"); 1905 else 1906 xasprintf(&tmp, "%s*", file); 1907 1908 /* Check if the path is absolute. */ 1909 isabs = path_absolute(tmp); 1910 1911 memset(&g, 0, sizeof(g)); 1912 if (remote != LOCAL) { 1913 tmp = make_absolute(tmp, remote_path); 1914 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); 1915 } else 1916 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); 1917 1918 /* Determine length of pwd so we can trim completion display */ 1919 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) { 1920 /* Terminate counting on first unescaped glob metacharacter */ 1921 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') { 1922 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0') 1923 hadglob = 1; 1924 break; 1925 } 1926 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0') 1927 tmplen++; 1928 if (tmp[tmplen] == '/') 1929 pwdlen = tmplen + 1; /* track last seen '/' */ 1930 } 1931 free(tmp); 1932 tmp = NULL; 1933 1934 if (g.gl_matchc == 0) 1935 goto out; 1936 1937 if (g.gl_matchc > 1) 1938 complete_display(g.gl_pathv, pwdlen); 1939 1940 /* Don't try to extend globs */ 1941 if (file == NULL || hadglob) 1942 goto out; 1943 1944 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); 1945 tmp = path_strip(tmp2, isabs ? NULL : remote_path); 1946 free(tmp2); 1947 1948 if (tmp == NULL) 1949 goto out; 1950 1951 tmplen = strlen(tmp); 1952 filelen = strlen(file); 1953 1954 /* Count the number of escaped characters in the input string. */ 1955 cesc = isesc = 0; 1956 for (i = 0; i < filelen; i++) { 1957 if (!isesc && file[i] == '\\' && i + 1 < filelen){ 1958 isesc = 1; 1959 cesc++; 1960 } else 1961 isesc = 0; 1962 } 1963 1964 if (tmplen > (filelen - cesc)) { 1965 tmp2 = tmp + filelen - cesc; 1966 len = strlen(tmp2); 1967 /* quote argument on way out */ 1968 for (i = 0; i < len; i += clen) { 1969 if ((clen = mblen(tmp2 + i, len - i)) < 0 || 1970 (size_t)clen > sizeof(ins) - 2) 1971 fatal("invalid multibyte character"); 1972 ins[0] = '\\'; 1973 memcpy(ins + 1, tmp2 + i, clen); 1974 ins[clen + 1] = '\0'; 1975 switch (tmp2[i]) { 1976 case '\'': 1977 case '"': 1978 case '\\': 1979 case '\t': 1980 case '[': 1981 case ' ': 1982 case '#': 1983 case '*': 1984 if (quote == '\0' || tmp2[i] == quote) { 1985 if (el_insertstr(el, ins) == -1) 1986 fatal("el_insertstr " 1987 "failed."); 1988 break; 1989 } 1990 /* FALLTHROUGH */ 1991 default: 1992 if (el_insertstr(el, ins + 1) == -1) 1993 fatal("el_insertstr failed."); 1994 break; 1995 } 1996 } 1997 } 1998 1999 lf = el_line(el); 2000 if (g.gl_matchc == 1) { 2001 i = 0; 2002 if (!terminated && quote != '\0') 2003 ins[i++] = quote; 2004 if (*(lf->cursor - 1) != '/' && 2005 (lastarg || *(lf->cursor) != ' ')) 2006 ins[i++] = ' '; 2007 ins[i] = '\0'; 2008 if (i > 0 && el_insertstr(el, ins) == -1) 2009 fatal("el_insertstr failed."); 2010 } 2011 free(tmp); 2012 2013 out: 2014 globfree(&g); 2015 return g.gl_matchc; 2016 } 2017 2018 /* tab-completion hook function, called via libedit */ 2019 static unsigned char 2020 complete(EditLine *el, int ch) 2021 { 2022 char **argv, *line, quote; 2023 int argc, carg; 2024 u_int cursor, len, terminated, ret = CC_ERROR; 2025 const LineInfo *lf; 2026 struct complete_ctx *complete_ctx; 2027 2028 lf = el_line(el); 2029 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0) 2030 fatal_f("el_get failed"); 2031 2032 /* Figure out which argument the cursor points to */ 2033 cursor = lf->cursor - lf->buffer; 2034 line = xmalloc(cursor + 1); 2035 memcpy(line, lf->buffer, cursor); 2036 line[cursor] = '\0'; 2037 argv = makeargv(line, &carg, 1, "e, &terminated); 2038 free(line); 2039 2040 /* Get all the arguments on the line */ 2041 len = lf->lastchar - lf->buffer; 2042 line = xmalloc(len + 1); 2043 memcpy(line, lf->buffer, len); 2044 line[len] = '\0'; 2045 argv = makeargv(line, &argc, 1, NULL, NULL); 2046 2047 /* Ensure cursor is at EOL or a argument boundary */ 2048 if (line[cursor] != ' ' && line[cursor] != '\0' && 2049 line[cursor] != '\n') { 2050 free(line); 2051 return ret; 2052 } 2053 2054 if (carg == 0) { 2055 /* Show all available commands */ 2056 complete_cmd_parse(el, NULL, argc == carg, '\0', 1); 2057 ret = CC_REDISPLAY; 2058 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') { 2059 /* Handle the command parsing */ 2060 if (complete_cmd_parse(el, argv[0], argc == carg, 2061 quote, terminated) != 0) 2062 ret = CC_REDISPLAY; 2063 } else if (carg >= 1) { 2064 /* Handle file parsing */ 2065 int remote = complete_is_remote(argv[0]); 2066 char *filematch = NULL; 2067 2068 if (carg > 1 && line[cursor-1] != ' ') 2069 filematch = argv[carg - 1]; 2070 2071 if (remote != 0 && 2072 complete_match(el, complete_ctx->conn, 2073 *complete_ctx->remote_pathp, filematch, 2074 remote, carg == argc, quote, terminated) != 0) 2075 ret = CC_REDISPLAY; 2076 } 2077 2078 free(line); 2079 return ret; 2080 } 2081 2082 static int 2083 interactive_loop(struct sftp_conn *conn, char *file1, char *file2) 2084 { 2085 char *remote_path; 2086 char *dir = NULL, *startdir = NULL; 2087 char cmd[2048]; 2088 int err, interactive; 2089 EditLine *el = NULL; 2090 History *hl = NULL; 2091 HistEvent hev; 2092 extern char *__progname; 2093 struct complete_ctx complete_ctx; 2094 2095 if (!batchmode && isatty(STDIN_FILENO)) { 2096 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) 2097 fatal("Couldn't initialise editline"); 2098 if ((hl = history_init()) == NULL) 2099 fatal("Couldn't initialise editline history"); 2100 history(hl, &hev, H_SETSIZE, 100); 2101 el_set(el, EL_HIST, history, hl); 2102 2103 el_set(el, EL_PROMPT, prompt); 2104 el_set(el, EL_EDITOR, "emacs"); 2105 el_set(el, EL_TERMINAL, NULL); 2106 el_set(el, EL_SIGNAL, 1); 2107 el_source(el, NULL); 2108 2109 /* Tab Completion */ 2110 el_set(el, EL_ADDFN, "ftp-complete", 2111 "Context sensitive argument completion", complete); 2112 complete_ctx.conn = conn; 2113 complete_ctx.remote_pathp = &remote_path; 2114 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); 2115 el_set(el, EL_BIND, "^I", "ftp-complete", NULL); 2116 /* enable ctrl-left-arrow and ctrl-right-arrow */ 2117 el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL); 2118 el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL); 2119 el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL); 2120 el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL); 2121 /* make ^w match ksh behaviour */ 2122 el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL); 2123 } 2124 2125 remote_path = do_realpath(conn, "."); 2126 if (remote_path == NULL) 2127 fatal("Need cwd"); 2128 startdir = xstrdup(remote_path); 2129 2130 if (file1 != NULL) { 2131 dir = xstrdup(file1); 2132 dir = make_absolute(dir, remote_path); 2133 2134 if (remote_is_dir(conn, dir) && file2 == NULL) { 2135 if (!quiet) 2136 mprintf("Changing to: %s\n", dir); 2137 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 2138 if (parse_dispatch_command(conn, cmd, 2139 &remote_path, startdir, 1, 0) != 0) { 2140 free(dir); 2141 free(startdir); 2142 free(remote_path); 2143 free(conn); 2144 return (-1); 2145 } 2146 } else { 2147 /* XXX this is wrong wrt quoting */ 2148 snprintf(cmd, sizeof cmd, "get%s %s%s%s", 2149 global_aflag ? " -a" : "", dir, 2150 file2 == NULL ? "" : " ", 2151 file2 == NULL ? "" : file2); 2152 err = parse_dispatch_command(conn, cmd, 2153 &remote_path, startdir, 1, 0); 2154 free(dir); 2155 free(startdir); 2156 free(remote_path); 2157 free(conn); 2158 return (err); 2159 } 2160 free(dir); 2161 } 2162 2163 setvbuf(stdout, NULL, _IOLBF, 0); 2164 setvbuf(infile, NULL, _IOLBF, 0); 2165 2166 interactive = !batchmode && isatty(STDIN_FILENO); 2167 err = 0; 2168 for (;;) { 2169 const char *line; 2170 int count = 0; 2171 2172 ssh_signal(SIGINT, SIG_IGN); 2173 2174 if (el == NULL) { 2175 if (interactive) 2176 printf("sftp> "); 2177 if (fgets(cmd, sizeof(cmd), infile) == NULL) { 2178 if (interactive) 2179 printf("\n"); 2180 break; 2181 } 2182 } else { 2183 if ((line = el_gets(el, &count)) == NULL || 2184 count <= 0) { 2185 printf("\n"); 2186 break; 2187 } 2188 history(hl, &hev, H_ENTER, line); 2189 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { 2190 fprintf(stderr, "Error: input line too long\n"); 2191 continue; 2192 } 2193 } 2194 2195 cmd[strcspn(cmd, "\n")] = '\0'; 2196 2197 /* Handle user interrupts gracefully during commands */ 2198 interrupted = 0; 2199 ssh_signal(SIGINT, cmd_interrupt); 2200 2201 err = parse_dispatch_command(conn, cmd, &remote_path, 2202 startdir, batchmode, !interactive && el == NULL); 2203 if (err != 0) 2204 break; 2205 } 2206 ssh_signal(SIGCHLD, SIG_DFL); 2207 free(remote_path); 2208 free(startdir); 2209 free(conn); 2210 2211 if (el != NULL) 2212 el_end(el); 2213 2214 /* err == 1 signifies normal "quit" exit */ 2215 return (err >= 0 ? 0 : -1); 2216 } 2217 2218 static void 2219 connect_to_server(char *path, char **args, int *in, int *out) 2220 { 2221 int c_in, c_out; 2222 2223 int inout[2]; 2224 2225 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 2226 fatal("socketpair: %s", strerror(errno)); 2227 *in = *out = inout[0]; 2228 c_in = c_out = inout[1]; 2229 2230 if ((sshpid = fork()) == -1) 2231 fatal("fork: %s", strerror(errno)); 2232 else if (sshpid == 0) { 2233 if ((dup2(c_in, STDIN_FILENO) == -1) || 2234 (dup2(c_out, STDOUT_FILENO) == -1)) { 2235 fprintf(stderr, "dup2: %s\n", strerror(errno)); 2236 _exit(1); 2237 } 2238 close(*in); 2239 close(*out); 2240 close(c_in); 2241 close(c_out); 2242 2243 /* 2244 * The underlying ssh is in the same process group, so we must 2245 * ignore SIGINT if we want to gracefully abort commands, 2246 * otherwise the signal will make it to the ssh process and 2247 * kill it too. Contrawise, since sftp sends SIGTERMs to the 2248 * underlying ssh, it must *not* ignore that signal. 2249 */ 2250 ssh_signal(SIGINT, SIG_IGN); 2251 ssh_signal(SIGTERM, SIG_DFL); 2252 execvp(path, args); 2253 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 2254 _exit(1); 2255 } 2256 2257 ssh_signal(SIGTERM, killchild); 2258 ssh_signal(SIGINT, killchild); 2259 ssh_signal(SIGHUP, killchild); 2260 ssh_signal(SIGTSTP, suspchild); 2261 ssh_signal(SIGTTIN, suspchild); 2262 ssh_signal(SIGTTOU, suspchild); 2263 ssh_signal(SIGCHLD, sigchld_handler); 2264 close(c_in); 2265 close(c_out); 2266 } 2267 2268 static void 2269 usage(void) 2270 { 2271 extern char *__progname; 2272 2273 fprintf(stderr, 2274 "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" 2275 " [-D sftp_server_path] [-F ssh_config] [-i identity_file]\n" 2276 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n" 2277 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n" 2278 " destination\n", 2279 __progname); 2280 exit(1); 2281 } 2282 2283 int 2284 main(int argc, char **argv) 2285 { 2286 int in, out, ch, err, tmp, port = -1, noisy = 0; 2287 char *host = NULL, *user, *cp, *file2 = NULL; 2288 int debug_level = 0; 2289 char *file1 = NULL, *sftp_server = NULL; 2290 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 2291 const char *errstr; 2292 LogLevel ll = SYSLOG_LEVEL_INFO; 2293 arglist args; 2294 extern int optind; 2295 extern char *optarg; 2296 struct sftp_conn *conn; 2297 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; 2298 size_t num_requests = DEFAULT_NUM_REQUESTS; 2299 long long limit_kbps = 0; 2300 2301 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 2302 sanitise_stdfd(); 2303 setlocale(LC_CTYPE, ""); 2304 2305 memset(&args, '\0', sizeof(args)); 2306 args.list = NULL; 2307 addargs(&args, "%s", ssh_program); 2308 addargs(&args, "-oForwardX11 no"); 2309 addargs(&args, "-oPermitLocalCommand no"); 2310 addargs(&args, "-oClearAllForwardings yes"); 2311 2312 ll = SYSLOG_LEVEL_INFO; 2313 infile = stdin; 2314 2315 while ((ch = getopt(argc, argv, 2316 "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:")) != -1) { 2317 switch (ch) { 2318 /* Passed through to ssh(1) */ 2319 case 'A': 2320 case '4': 2321 case '6': 2322 case 'C': 2323 addargs(&args, "-%c", ch); 2324 break; 2325 /* Passed through to ssh(1) with argument */ 2326 case 'F': 2327 case 'J': 2328 case 'c': 2329 case 'i': 2330 case 'o': 2331 addargs(&args, "-%c", ch); 2332 addargs(&args, "%s", optarg); 2333 break; 2334 case 'q': 2335 ll = SYSLOG_LEVEL_ERROR; 2336 quiet = 1; 2337 showprogress = 0; 2338 addargs(&args, "-%c", ch); 2339 break; 2340 case 'P': 2341 port = a2port(optarg); 2342 if (port <= 0) 2343 fatal("Bad port \"%s\"\n", optarg); 2344 break; 2345 case 'v': 2346 if (debug_level < 3) { 2347 addargs(&args, "-v"); 2348 ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 2349 } 2350 debug_level++; 2351 break; 2352 case '1': 2353 fatal("SSH protocol v.1 is no longer supported"); 2354 break; 2355 case '2': 2356 /* accept silently */ 2357 break; 2358 case 'a': 2359 global_aflag = 1; 2360 break; 2361 case 'B': 2362 copy_buffer_len = strtol(optarg, &cp, 10); 2363 if (copy_buffer_len == 0 || *cp != '\0') 2364 fatal("Invalid buffer size \"%s\"", optarg); 2365 break; 2366 case 'b': 2367 if (batchmode) 2368 fatal("Batch file already specified."); 2369 2370 /* Allow "-" as stdin */ 2371 if (strcmp(optarg, "-") != 0 && 2372 (infile = fopen(optarg, "r")) == NULL) 2373 fatal("%s (%s).", strerror(errno), optarg); 2374 showprogress = 0; 2375 quiet = batchmode = 1; 2376 addargs(&args, "-obatchmode yes"); 2377 break; 2378 case 'f': 2379 global_fflag = 1; 2380 break; 2381 case 'N': 2382 noisy = 1; /* Used to clear quiet mode after getopt */ 2383 break; 2384 case 'p': 2385 global_pflag = 1; 2386 break; 2387 case 'D': 2388 sftp_direct = optarg; 2389 break; 2390 case 'l': 2391 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, 2392 &errstr); 2393 if (errstr != NULL) 2394 usage(); 2395 limit_kbps *= 1024; /* kbps */ 2396 break; 2397 case 'r': 2398 global_rflag = 1; 2399 break; 2400 case 'R': 2401 num_requests = strtol(optarg, &cp, 10); 2402 if (num_requests == 0 || *cp != '\0') 2403 fatal("Invalid number of requests \"%s\"", 2404 optarg); 2405 break; 2406 case 's': 2407 sftp_server = optarg; 2408 break; 2409 case 'S': 2410 ssh_program = optarg; 2411 replacearg(&args, 0, "%s", ssh_program); 2412 break; 2413 case 'h': 2414 default: 2415 usage(); 2416 } 2417 } 2418 2419 /* Do this last because we want the user to be able to override it */ 2420 addargs(&args, "-oForwardAgent no"); 2421 2422 if (!isatty(STDERR_FILENO)) 2423 showprogress = 0; 2424 2425 if (noisy) 2426 quiet = 0; 2427 2428 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 2429 2430 if (sftp_direct == NULL) { 2431 if (optind == argc || argc > (optind + 2)) 2432 usage(); 2433 argv += optind; 2434 2435 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) { 2436 case -1: 2437 usage(); 2438 break; 2439 case 0: 2440 if (tmp != -1) 2441 port = tmp; 2442 break; 2443 default: 2444 /* Try with user, host and path. */ 2445 if (parse_user_host_path(*argv, &user, &host, 2446 &file1) == 0) 2447 break; 2448 /* Try with user and host. */ 2449 if (parse_user_host_port(*argv, &user, &host, NULL) 2450 == 0) 2451 break; 2452 /* Treat as a plain hostname. */ 2453 host = xstrdup(*argv); 2454 host = cleanhostname(host); 2455 break; 2456 } 2457 file2 = *(argv + 1); 2458 2459 if (!*host) { 2460 fprintf(stderr, "Missing hostname\n"); 2461 usage(); 2462 } 2463 2464 if (port != -1) 2465 addargs(&args, "-oPort %d", port); 2466 if (user != NULL) { 2467 addargs(&args, "-l"); 2468 addargs(&args, "%s", user); 2469 } 2470 2471 /* no subsystem if the server-spec contains a '/' */ 2472 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 2473 addargs(&args, "-s"); 2474 2475 addargs(&args, "--"); 2476 addargs(&args, "%s", host); 2477 addargs(&args, "%s", (sftp_server != NULL ? 2478 sftp_server : "sftp")); 2479 2480 connect_to_server(ssh_program, args.list, &in, &out); 2481 } else { 2482 args.list = NULL; 2483 addargs(&args, "sftp-server"); 2484 2485 connect_to_server(sftp_direct, args.list, &in, &out); 2486 } 2487 freeargs(&args); 2488 2489 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps); 2490 if (conn == NULL) 2491 fatal("Couldn't initialise connection to server"); 2492 2493 if (!quiet) { 2494 if (sftp_direct == NULL) 2495 fprintf(stderr, "Connected to %s.\n", host); 2496 else 2497 fprintf(stderr, "Attached to %s.\n", sftp_direct); 2498 } 2499 2500 err = interactive_loop(conn, file1, file2); 2501 2502 close(in); 2503 close(out); 2504 if (batchmode) 2505 fclose(infile); 2506 2507 while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1) 2508 if (errno != EINTR) 2509 fatal("Couldn't wait for ssh process: %s", 2510 strerror(errno)); 2511 2512 exit(err == 0 ? 0 : 1); 2513 } 2514