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