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