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