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