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