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