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