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