1 /* $OpenBSD: sftp.c,v 1.174 2016/05/25 23:48:45 schwarze 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/param.h> /* MIN MAX */ 19 #include <sys/types.h> 20 #include <sys/ioctl.h> 21 #include <sys/wait.h> 22 #include <sys/stat.h> 23 #include <sys/socket.h> 24 #include <sys/statvfs.h> 25 26 #include <ctype.h> 27 #include <errno.h> 28 #include <glob.h> 29 #include <histedit.h> 30 #include <paths.h> 31 #include <libgen.h> 32 #include <locale.h> 33 #include <signal.h> 34 #include <stdarg.h> 35 #include <stdlib.h> 36 #include <stdio.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <limits.h> 40 #include <util.h> 41 #include <stdarg.h> 42 43 #include "xmalloc.h" 44 #include "log.h" 45 #include "pathnames.h" 46 #include "misc.h" 47 #include "utf8.h" 48 49 #include "sftp.h" 50 #include "ssherr.h" 51 #include "sshbuf.h" 52 #include "sftp-common.h" 53 #include "sftp-client.h" 54 55 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */ 56 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */ 57 58 /* File to read commands from */ 59 FILE* infile; 60 61 /* Are we in batchfile mode? */ 62 int batchmode = 0; 63 64 /* PID of ssh transport process */ 65 static pid_t sshpid = -1; 66 67 /* Suppress diagnositic messages */ 68 int quiet = 0; 69 70 /* This is set to 0 if the progressmeter is not desired. */ 71 int showprogress = 1; 72 73 /* When this option is set, we always recursively download/upload directories */ 74 int global_rflag = 0; 75 76 /* When this option is set, we resume download or upload if possible */ 77 int global_aflag = 0; 78 79 /* When this option is set, the file transfers will always preserve times */ 80 int global_pflag = 0; 81 82 /* When this option is set, transfers will have fsync() called on each file */ 83 int global_fflag = 0; 84 85 /* SIGINT received during command processing */ 86 volatile sig_atomic_t interrupted = 0; 87 88 /* I wish qsort() took a separate ctx for the comparison function...*/ 89 int sort_flag; 90 91 /* Context used for commandline completion */ 92 struct complete_ctx { 93 struct sftp_conn *conn; 94 char **remote_pathp; 95 }; 96 97 int remote_glob(struct sftp_conn *, const char *, int, 98 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 99 100 /* Separators for interactive commands */ 101 #define WHITESPACE " \t\r\n" 102 103 /* ls flags */ 104 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */ 105 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */ 106 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */ 107 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */ 108 #define LS_TIME_SORT 0x0010 /* Sort by mtime */ 109 #define LS_SIZE_SORT 0x0020 /* Sort by file size */ 110 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */ 111 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */ 112 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */ 113 114 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS) 115 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) 116 117 /* Commands for interactive mode */ 118 enum sftp_command { 119 I_CHDIR = 1, 120 I_CHGRP, 121 I_CHMOD, 122 I_CHOWN, 123 I_DF, 124 I_GET, 125 I_HELP, 126 I_LCHDIR, 127 I_LINK, 128 I_LLS, 129 I_LMKDIR, 130 I_LPWD, 131 I_LS, 132 I_LUMASK, 133 I_MKDIR, 134 I_PUT, 135 I_PWD, 136 I_QUIT, 137 I_REGET, 138 I_RENAME, 139 I_REPUT, 140 I_RM, 141 I_RMDIR, 142 I_SHELL, 143 I_SYMLINK, 144 I_VERSION, 145 I_PROGRESS, 146 }; 147 148 struct CMD { 149 const char *c; 150 const int n; 151 const int t; 152 }; 153 154 /* Type of completion */ 155 #define NOARGS 0 156 #define REMOTE 1 157 #define LOCAL 2 158 159 static const struct CMD cmds[] = { 160 { "bye", I_QUIT, NOARGS }, 161 { "cd", I_CHDIR, REMOTE }, 162 { "chdir", I_CHDIR, REMOTE }, 163 { "chgrp", I_CHGRP, REMOTE }, 164 { "chmod", I_CHMOD, REMOTE }, 165 { "chown", I_CHOWN, REMOTE }, 166 { "df", I_DF, REMOTE }, 167 { "dir", I_LS, REMOTE }, 168 { "exit", I_QUIT, NOARGS }, 169 { "get", I_GET, REMOTE }, 170 { "help", I_HELP, NOARGS }, 171 { "lcd", I_LCHDIR, LOCAL }, 172 { "lchdir", I_LCHDIR, LOCAL }, 173 { "lls", I_LLS, LOCAL }, 174 { "lmkdir", I_LMKDIR, LOCAL }, 175 { "ln", I_LINK, REMOTE }, 176 { "lpwd", I_LPWD, LOCAL }, 177 { "ls", I_LS, REMOTE }, 178 { "lumask", I_LUMASK, NOARGS }, 179 { "mkdir", I_MKDIR, REMOTE }, 180 { "mget", I_GET, REMOTE }, 181 { "mput", I_PUT, LOCAL }, 182 { "progress", I_PROGRESS, NOARGS }, 183 { "put", I_PUT, LOCAL }, 184 { "pwd", I_PWD, REMOTE }, 185 { "quit", I_QUIT, NOARGS }, 186 { "reget", I_REGET, REMOTE }, 187 { "rename", I_RENAME, REMOTE }, 188 { "reput", I_REPUT, LOCAL }, 189 { "rm", I_RM, REMOTE }, 190 { "rmdir", I_RMDIR, REMOTE }, 191 { "symlink", I_SYMLINK, REMOTE }, 192 { "version", I_VERSION, NOARGS }, 193 { "!", I_SHELL, NOARGS }, 194 { "?", I_HELP, NOARGS }, 195 { NULL, -1, -1 } 196 }; 197 198 int interactive_loop(struct sftp_conn *, char *file1, char *file2); 199 200 /* ARGSUSED */ 201 static void 202 killchild(int signo) 203 { 204 if (sshpid > 1) { 205 kill(sshpid, SIGTERM); 206 waitpid(sshpid, NULL, 0); 207 } 208 209 _exit(1); 210 } 211 212 /* ARGSUSED */ 213 static void 214 cmd_interrupt(int signo) 215 { 216 const char msg[] = "\rInterrupt \n"; 217 int olderrno = errno; 218 219 (void)write(STDERR_FILENO, msg, sizeof(msg) - 1); 220 interrupted = 1; 221 errno = olderrno; 222 } 223 224 static void 225 help(void) 226 { 227 printf("Available commands:\n" 228 "bye Quit sftp\n" 229 "cd path Change remote directory to 'path'\n" 230 "chgrp grp path Change group of file 'path' to 'grp'\n" 231 "chmod mode path Change permissions of file 'path' to 'mode'\n" 232 "chown own path Change owner of file 'path' to 'own'\n" 233 "df [-hi] [path] Display statistics for current directory or\n" 234 " filesystem containing 'path'\n" 235 "exit Quit sftp\n" 236 "get [-afPpRr] remote [local] Download file\n" 237 "reget [-fPpRr] remote [local] Resume download file\n" 238 "reput [-fPpRr] [local] remote Resume upload file\n" 239 "help Display this help text\n" 240 "lcd path Change local directory to 'path'\n" 241 "lls [ls-options [path]] Display local directory listing\n" 242 "lmkdir path Create local directory\n" 243 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n" 244 "lpwd Print local working directory\n" 245 "ls [-1afhlnrSt] [path] Display remote directory listing\n" 246 "lumask umask Set local umask to 'umask'\n" 247 "mkdir path Create remote directory\n" 248 "progress Toggle display of progress meter\n" 249 "put [-afPpRr] local [remote] Upload file\n" 250 "pwd Display remote working directory\n" 251 "quit Quit sftp\n" 252 "rename oldpath newpath Rename remote file\n" 253 "rm path Delete remote file\n" 254 "rmdir path Remove remote directory\n" 255 "symlink oldpath newpath Symlink remote file\n" 256 "version Show SFTP version\n" 257 "!command Execute 'command' in local shell\n" 258 "! Escape to local shell\n" 259 "? Synonym for help\n"); 260 } 261 262 static void 263 local_do_shell(const char *args) 264 { 265 int status; 266 char *shell; 267 pid_t pid; 268 269 if (!*args) 270 args = NULL; 271 272 if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 273 shell = _PATH_BSHELL; 274 275 if ((pid = fork()) == -1) 276 fatal("Couldn't fork: %s", strerror(errno)); 277 278 if (pid == 0) { 279 /* XXX: child has pipe fds to ssh subproc open - issue? */ 280 if (args) { 281 debug3("Executing %s -c \"%s\"", shell, args); 282 execl(shell, shell, "-c", args, (char *)NULL); 283 } else { 284 debug3("Executing %s", shell); 285 execl(shell, shell, (char *)NULL); 286 } 287 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, 288 strerror(errno)); 289 _exit(1); 290 } 291 while (waitpid(pid, &status, 0) == -1) 292 if (errno != EINTR) 293 fatal("Couldn't wait for child: %s", strerror(errno)); 294 if (!WIFEXITED(status)) 295 error("Shell exited abnormally"); 296 else if (WEXITSTATUS(status)) 297 error("Shell exited with status %d", WEXITSTATUS(status)); 298 } 299 300 static void 301 local_do_ls(const char *args) 302 { 303 if (!args || !*args) 304 local_do_shell(_PATH_LS); 305 else { 306 int len = strlen(_PATH_LS " ") + strlen(args) + 1; 307 char *buf = xmalloc(len); 308 309 /* XXX: quoting - rip quoting code from ftp? */ 310 snprintf(buf, len, _PATH_LS " %s", args); 311 local_do_shell(buf); 312 free(buf); 313 } 314 } 315 316 /* Strip one path (usually the pwd) from the start of another */ 317 static char * 318 path_strip(char *path, char *strip) 319 { 320 size_t len; 321 322 if (strip == NULL) 323 return (xstrdup(path)); 324 325 len = strlen(strip); 326 if (strncmp(path, strip, len) == 0) { 327 if (strip[len - 1] != '/' && path[len] == '/') 328 len++; 329 return (xstrdup(path + len)); 330 } 331 332 return (xstrdup(path)); 333 } 334 335 static char * 336 make_absolute(char *p, char *pwd) 337 { 338 char *abs_str; 339 340 /* Derelativise */ 341 if (p && p[0] != '/') { 342 abs_str = path_append(pwd, p); 343 free(p); 344 return(abs_str); 345 } else 346 return(p); 347 } 348 349 static int 350 parse_getput_flags(const char *cmd, char **argv, int argc, 351 int *aflag, int *fflag, int *pflag, int *rflag) 352 { 353 extern int opterr, optind, optopt, optreset; 354 int ch; 355 356 optind = optreset = 1; 357 opterr = 0; 358 359 *aflag = *fflag = *rflag = *pflag = 0; 360 while ((ch = getopt(argc, argv, "afPpRr")) != -1) { 361 switch (ch) { 362 case 'a': 363 *aflag = 1; 364 break; 365 case 'f': 366 *fflag = 1; 367 break; 368 case 'p': 369 case 'P': 370 *pflag = 1; 371 break; 372 case 'r': 373 case 'R': 374 *rflag = 1; 375 break; 376 default: 377 error("%s: Invalid flag -%c", cmd, optopt); 378 return -1; 379 } 380 } 381 382 return optind; 383 } 384 385 static int 386 parse_link_flags(const char *cmd, char **argv, int argc, int *sflag) 387 { 388 extern int opterr, optind, optopt, optreset; 389 int ch; 390 391 optind = optreset = 1; 392 opterr = 0; 393 394 *sflag = 0; 395 while ((ch = getopt(argc, argv, "s")) != -1) { 396 switch (ch) { 397 case 's': 398 *sflag = 1; 399 break; 400 default: 401 error("%s: Invalid flag -%c", cmd, optopt); 402 return -1; 403 } 404 } 405 406 return optind; 407 } 408 409 static int 410 parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag) 411 { 412 extern int opterr, optind, optopt, optreset; 413 int ch; 414 415 optind = optreset = 1; 416 opterr = 0; 417 418 *lflag = 0; 419 while ((ch = getopt(argc, argv, "l")) != -1) { 420 switch (ch) { 421 case 'l': 422 *lflag = 1; 423 break; 424 default: 425 error("%s: Invalid flag -%c", cmd, optopt); 426 return -1; 427 } 428 } 429 430 return optind; 431 } 432 433 static int 434 parse_ls_flags(char **argv, int argc, int *lflag) 435 { 436 extern int opterr, optind, optopt, optreset; 437 int ch; 438 439 optind = optreset = 1; 440 opterr = 0; 441 442 *lflag = LS_NAME_SORT; 443 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) { 444 switch (ch) { 445 case '1': 446 *lflag &= ~VIEW_FLAGS; 447 *lflag |= LS_SHORT_VIEW; 448 break; 449 case 'S': 450 *lflag &= ~SORT_FLAGS; 451 *lflag |= LS_SIZE_SORT; 452 break; 453 case 'a': 454 *lflag |= LS_SHOW_ALL; 455 break; 456 case 'f': 457 *lflag &= ~SORT_FLAGS; 458 break; 459 case 'h': 460 *lflag |= LS_SI_UNITS; 461 break; 462 case 'l': 463 *lflag &= ~LS_SHORT_VIEW; 464 *lflag |= LS_LONG_VIEW; 465 break; 466 case 'n': 467 *lflag &= ~LS_SHORT_VIEW; 468 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; 469 break; 470 case 'r': 471 *lflag |= LS_REVERSE_SORT; 472 break; 473 case 't': 474 *lflag &= ~SORT_FLAGS; 475 *lflag |= LS_TIME_SORT; 476 break; 477 default: 478 error("ls: Invalid flag -%c", optopt); 479 return -1; 480 } 481 } 482 483 return optind; 484 } 485 486 static int 487 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag) 488 { 489 extern int opterr, optind, optopt, optreset; 490 int ch; 491 492 optind = optreset = 1; 493 opterr = 0; 494 495 *hflag = *iflag = 0; 496 while ((ch = getopt(argc, argv, "hi")) != -1) { 497 switch (ch) { 498 case 'h': 499 *hflag = 1; 500 break; 501 case 'i': 502 *iflag = 1; 503 break; 504 default: 505 error("%s: Invalid flag -%c", cmd, optopt); 506 return -1; 507 } 508 } 509 510 return optind; 511 } 512 513 static int 514 parse_no_flags(const char *cmd, char **argv, int argc) 515 { 516 extern int opterr, optind, optopt, optreset; 517 int ch; 518 519 optind = optreset = 1; 520 opterr = 0; 521 522 while ((ch = getopt(argc, argv, "")) != -1) { 523 switch (ch) { 524 default: 525 error("%s: Invalid flag -%c", cmd, optopt); 526 return -1; 527 } 528 } 529 530 return optind; 531 } 532 533 static int 534 is_dir(char *path) 535 { 536 struct stat sb; 537 538 /* XXX: report errors? */ 539 if (stat(path, &sb) == -1) 540 return(0); 541 542 return(S_ISDIR(sb.st_mode)); 543 } 544 545 static int 546 remote_is_dir(struct sftp_conn *conn, char *path) 547 { 548 Attrib *a; 549 550 /* XXX: report errors? */ 551 if ((a = do_stat(conn, path, 1)) == NULL) 552 return(0); 553 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) 554 return(0); 555 return(S_ISDIR(a->perm)); 556 } 557 558 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ 559 static int 560 pathname_is_dir(char *pathname) 561 { 562 size_t l = strlen(pathname); 563 564 return l > 0 && pathname[l - 1] == '/'; 565 } 566 567 static int 568 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, 569 int pflag, int rflag, int resume, int fflag) 570 { 571 char *abs_src = NULL; 572 char *abs_dst = NULL; 573 glob_t g; 574 char *filename, *tmp=NULL; 575 int i, r, err = 0; 576 577 abs_src = xstrdup(src); 578 abs_src = make_absolute(abs_src, pwd); 579 memset(&g, 0, sizeof(g)); 580 581 debug3("Looking up %s", abs_src); 582 if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) { 583 if (r == GLOB_NOSPACE) { 584 error("Too many matches for \"%s\".", abs_src); 585 } else { 586 error("File \"%s\" not found.", abs_src); 587 } 588 err = -1; 589 goto out; 590 } 591 592 /* 593 * If multiple matches then dst must be a directory or 594 * unspecified. 595 */ 596 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) { 597 error("Multiple source paths, but destination " 598 "\"%s\" is not a directory", dst); 599 err = -1; 600 goto out; 601 } 602 603 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 604 tmp = xstrdup(g.gl_pathv[i]); 605 if ((filename = basename(tmp)) == NULL) { 606 error("basename %s: %s", tmp, strerror(errno)); 607 free(tmp); 608 err = -1; 609 goto out; 610 } 611 612 if (g.gl_matchc == 1 && dst) { 613 if (is_dir(dst)) { 614 abs_dst = path_append(dst, filename); 615 } else { 616 abs_dst = xstrdup(dst); 617 } 618 } else if (dst) { 619 abs_dst = path_append(dst, filename); 620 } else { 621 abs_dst = xstrdup(filename); 622 } 623 free(tmp); 624 625 resume |= global_aflag; 626 if (!quiet && resume) 627 mprintf("Resuming %s to %s\n", 628 g.gl_pathv[i], abs_dst); 629 else if (!quiet && !resume) 630 mprintf("Fetching %s to %s\n", 631 g.gl_pathv[i], abs_dst); 632 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 633 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, 634 pflag || global_pflag, 1, resume, 635 fflag || global_fflag) == -1) 636 err = -1; 637 } else { 638 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, 639 pflag || global_pflag, resume, 640 fflag || global_fflag) == -1) 641 err = -1; 642 } 643 free(abs_dst); 644 abs_dst = NULL; 645 } 646 647 out: 648 free(abs_src); 649 globfree(&g); 650 return(err); 651 } 652 653 static int 654 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, 655 int pflag, int rflag, int resume, int fflag) 656 { 657 char *tmp_dst = NULL; 658 char *abs_dst = NULL; 659 char *tmp = NULL, *filename = NULL; 660 glob_t g; 661 int err = 0; 662 int i, dst_is_dir = 1; 663 struct stat sb; 664 665 if (dst) { 666 tmp_dst = xstrdup(dst); 667 tmp_dst = make_absolute(tmp_dst, pwd); 668 } 669 670 memset(&g, 0, sizeof(g)); 671 debug3("Looking up %s", src); 672 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) { 673 error("File \"%s\" not found.", src); 674 err = -1; 675 goto out; 676 } 677 678 /* If we aren't fetching to pwd then stash this status for later */ 679 if (tmp_dst != NULL) 680 dst_is_dir = remote_is_dir(conn, tmp_dst); 681 682 /* If multiple matches, dst may be directory or unspecified */ 683 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) { 684 error("Multiple paths match, but destination " 685 "\"%s\" is not a directory", tmp_dst); 686 err = -1; 687 goto out; 688 } 689 690 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 691 if (stat(g.gl_pathv[i], &sb) == -1) { 692 err = -1; 693 error("stat %s: %s", g.gl_pathv[i], strerror(errno)); 694 continue; 695 } 696 697 tmp = xstrdup(g.gl_pathv[i]); 698 if ((filename = basename(tmp)) == NULL) { 699 error("basename %s: %s", tmp, strerror(errno)); 700 free(tmp); 701 err = -1; 702 goto out; 703 } 704 705 if (g.gl_matchc == 1 && tmp_dst) { 706 /* If directory specified, append filename */ 707 if (dst_is_dir) 708 abs_dst = path_append(tmp_dst, filename); 709 else 710 abs_dst = xstrdup(tmp_dst); 711 } else if (tmp_dst) { 712 abs_dst = path_append(tmp_dst, filename); 713 } else { 714 abs_dst = make_absolute(xstrdup(filename), pwd); 715 } 716 free(tmp); 717 718 resume |= global_aflag; 719 if (!quiet && resume) 720 mprintf("Resuming upload of %s to %s\n", 721 g.gl_pathv[i], abs_dst); 722 else if (!quiet && !resume) 723 mprintf("Uploading %s to %s\n", 724 g.gl_pathv[i], abs_dst); 725 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 726 if (upload_dir(conn, g.gl_pathv[i], abs_dst, 727 pflag || global_pflag, 1, resume, 728 fflag || global_fflag) == -1) 729 err = -1; 730 } else { 731 if (do_upload(conn, g.gl_pathv[i], abs_dst, 732 pflag || global_pflag, resume, 733 fflag || global_fflag) == -1) 734 err = -1; 735 } 736 } 737 738 out: 739 free(abs_dst); 740 free(tmp_dst); 741 globfree(&g); 742 return(err); 743 } 744 745 static int 746 sdirent_comp(const void *aa, const void *bb) 747 { 748 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; 749 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; 750 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; 751 752 #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) 753 if (sort_flag & LS_NAME_SORT) 754 return (rmul * strcmp(a->filename, b->filename)); 755 else if (sort_flag & LS_TIME_SORT) 756 return (rmul * NCMP(a->a.mtime, b->a.mtime)); 757 else if (sort_flag & LS_SIZE_SORT) 758 return (rmul * NCMP(a->a.size, b->a.size)); 759 760 fatal("Unknown ls sort type"); 761 } 762 763 /* sftp ls.1 replacement for directories */ 764 static int 765 do_ls_dir(struct sftp_conn *conn, char *path, 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 = MAX(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 = MAX(columns, 1); 795 colspace = width / columns; 796 colspace = MIN(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, char *path, char *strip_path, 851 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 = MAX(m, strlen(g.gl_pathv[i])); 896 897 columns = width / (m + 2); 898 columns = MAX(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, 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 = MAX(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 = MAX(columns, 1); 1656 colspace = width / columns; 1657 colspace = MIN(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