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