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