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