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