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