sftp.c revision 147001
1/* 2 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17#include "includes.h" 18 19RCSID("$OpenBSD: sftp.c,v 1.63 2005/03/10 22:01:05 deraadt Exp $"); 20 21#ifdef USE_LIBEDIT 22#include <histedit.h> 23#else 24typedef void EditLine; 25#endif 26 27#include "buffer.h" 28#include "xmalloc.h" 29#include "log.h" 30#include "pathnames.h" 31#include "misc.h" 32 33#include "sftp.h" 34#include "sftp-common.h" 35#include "sftp-client.h" 36 37/* File to read commands from */ 38FILE* infile; 39 40/* Are we in batchfile mode? */ 41int batchmode = 0; 42 43/* Size of buffer used when copying files */ 44size_t copy_buffer_len = 32768; 45 46/* Number of concurrent outstanding requests */ 47size_t num_requests = 16; 48 49/* PID of ssh transport process */ 50static pid_t sshpid = -1; 51 52/* This is set to 0 if the progressmeter is not desired. */ 53int showprogress = 1; 54 55/* SIGINT received during command processing */ 56volatile sig_atomic_t interrupted = 0; 57 58/* I wish qsort() took a separate ctx for the comparison function...*/ 59int sort_flag; 60 61int remote_glob(struct sftp_conn *, const char *, int, 62 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 63 64extern char *__progname; 65 66/* Separators for interactive commands */ 67#define WHITESPACE " \t\r\n" 68 69/* ls flags */ 70#define LS_LONG_VIEW 0x01 /* Full view ala ls -l */ 71#define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */ 72#define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */ 73#define LS_NAME_SORT 0x08 /* Sort by name (default) */ 74#define LS_TIME_SORT 0x10 /* Sort by mtime */ 75#define LS_SIZE_SORT 0x20 /* Sort by file size */ 76#define LS_REVERSE_SORT 0x40 /* Reverse sort order */ 77#define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */ 78 79#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW) 80#define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) 81 82/* Commands for interactive mode */ 83#define I_CHDIR 1 84#define I_CHGRP 2 85#define I_CHMOD 3 86#define I_CHOWN 4 87#define I_GET 5 88#define I_HELP 6 89#define I_LCHDIR 7 90#define I_LLS 8 91#define I_LMKDIR 9 92#define I_LPWD 10 93#define I_LS 11 94#define I_LUMASK 12 95#define I_MKDIR 13 96#define I_PUT 14 97#define I_PWD 15 98#define I_QUIT 16 99#define I_RENAME 17 100#define I_RM 18 101#define I_RMDIR 19 102#define I_SHELL 20 103#define I_SYMLINK 21 104#define I_VERSION 22 105#define I_PROGRESS 23 106 107struct CMD { 108 const char *c; 109 const int n; 110}; 111 112static const struct CMD cmds[] = { 113 { "bye", I_QUIT }, 114 { "cd", I_CHDIR }, 115 { "chdir", I_CHDIR }, 116 { "chgrp", I_CHGRP }, 117 { "chmod", I_CHMOD }, 118 { "chown", I_CHOWN }, 119 { "dir", I_LS }, 120 { "exit", I_QUIT }, 121 { "get", I_GET }, 122 { "mget", I_GET }, 123 { "help", I_HELP }, 124 { "lcd", I_LCHDIR }, 125 { "lchdir", I_LCHDIR }, 126 { "lls", I_LLS }, 127 { "lmkdir", I_LMKDIR }, 128 { "ln", I_SYMLINK }, 129 { "lpwd", I_LPWD }, 130 { "ls", I_LS }, 131 { "lumask", I_LUMASK }, 132 { "mkdir", I_MKDIR }, 133 { "progress", I_PROGRESS }, 134 { "put", I_PUT }, 135 { "mput", I_PUT }, 136 { "pwd", I_PWD }, 137 { "quit", I_QUIT }, 138 { "rename", I_RENAME }, 139 { "rm", I_RM }, 140 { "rmdir", I_RMDIR }, 141 { "symlink", I_SYMLINK }, 142 { "version", I_VERSION }, 143 { "!", I_SHELL }, 144 { "?", I_HELP }, 145 { NULL, -1} 146}; 147 148int interactive_loop(int fd_in, int fd_out, char *file1, char *file2); 149 150static void 151killchild(int signo) 152{ 153 if (sshpid > 1) { 154 kill(sshpid, SIGTERM); 155 waitpid(sshpid, NULL, 0); 156 } 157 158 _exit(1); 159} 160 161static void 162cmd_interrupt(int signo) 163{ 164 const char msg[] = "\rInterrupt \n"; 165 int olderrno = errno; 166 167 write(STDERR_FILENO, msg, sizeof(msg) - 1); 168 interrupted = 1; 169 errno = olderrno; 170} 171 172static void 173help(void) 174{ 175 printf("Available commands:\n"); 176 printf("cd path Change remote directory to 'path'\n"); 177 printf("lcd path Change local directory to 'path'\n"); 178 printf("chgrp grp path Change group of file 'path' to 'grp'\n"); 179 printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); 180 printf("chown own path Change owner of file 'path' to 'own'\n"); 181 printf("help Display this help text\n"); 182 printf("get remote-path [local-path] Download file\n"); 183 printf("lls [ls-options [path]] Display local directory listing\n"); 184 printf("ln oldpath newpath Symlink remote file\n"); 185 printf("lmkdir path Create local directory\n"); 186 printf("lpwd Print local working directory\n"); 187 printf("ls [path] Display remote directory listing\n"); 188 printf("lumask umask Set local umask to 'umask'\n"); 189 printf("mkdir path Create remote directory\n"); 190 printf("progress Toggle display of progress meter\n"); 191 printf("put local-path [remote-path] Upload file\n"); 192 printf("pwd Display remote working directory\n"); 193 printf("exit Quit sftp\n"); 194 printf("quit Quit sftp\n"); 195 printf("rename oldpath newpath Rename remote file\n"); 196 printf("rmdir path Remove remote directory\n"); 197 printf("rm path Delete remote file\n"); 198 printf("symlink oldpath newpath Symlink remote file\n"); 199 printf("version Show SFTP version\n"); 200 printf("!command Execute 'command' in local shell\n"); 201 printf("! Escape to local shell\n"); 202 printf("? Synonym for help\n"); 203} 204 205static void 206local_do_shell(const char *args) 207{ 208 int status; 209 char *shell; 210 pid_t pid; 211 212 if (!*args) 213 args = NULL; 214 215 if ((shell = getenv("SHELL")) == NULL) 216 shell = _PATH_BSHELL; 217 218 if ((pid = fork()) == -1) 219 fatal("Couldn't fork: %s", strerror(errno)); 220 221 if (pid == 0) { 222 /* XXX: child has pipe fds to ssh subproc open - issue? */ 223 if (args) { 224 debug3("Executing %s -c \"%s\"", shell, args); 225 execl(shell, shell, "-c", args, (char *)NULL); 226 } else { 227 debug3("Executing %s", shell); 228 execl(shell, shell, (char *)NULL); 229 } 230 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, 231 strerror(errno)); 232 _exit(1); 233 } 234 while (waitpid(pid, &status, 0) == -1) 235 if (errno != EINTR) 236 fatal("Couldn't wait for child: %s", strerror(errno)); 237 if (!WIFEXITED(status)) 238 error("Shell exited abormally"); 239 else if (WEXITSTATUS(status)) 240 error("Shell exited with status %d", WEXITSTATUS(status)); 241} 242 243static void 244local_do_ls(const char *args) 245{ 246 if (!args || !*args) 247 local_do_shell(_PATH_LS); 248 else { 249 int len = strlen(_PATH_LS " ") + strlen(args) + 1; 250 char *buf = xmalloc(len); 251 252 /* XXX: quoting - rip quoting code from ftp? */ 253 snprintf(buf, len, _PATH_LS " %s", args); 254 local_do_shell(buf); 255 xfree(buf); 256 } 257} 258 259/* Strip one path (usually the pwd) from the start of another */ 260static char * 261path_strip(char *path, char *strip) 262{ 263 size_t len; 264 265 if (strip == NULL) 266 return (xstrdup(path)); 267 268 len = strlen(strip); 269 if (strncmp(path, strip, len) == 0) { 270 if (strip[len - 1] != '/' && path[len] == '/') 271 len++; 272 return (xstrdup(path + len)); 273 } 274 275 return (xstrdup(path)); 276} 277 278static char * 279path_append(char *p1, char *p2) 280{ 281 char *ret; 282 int len = strlen(p1) + strlen(p2) + 2; 283 284 ret = xmalloc(len); 285 strlcpy(ret, p1, len); 286 if (p1[strlen(p1) - 1] != '/') 287 strlcat(ret, "/", len); 288 strlcat(ret, p2, len); 289 290 return(ret); 291} 292 293static char * 294make_absolute(char *p, char *pwd) 295{ 296 char *abs_str; 297 298 /* Derelativise */ 299 if (p && p[0] != '/') { 300 abs_str = path_append(pwd, p); 301 xfree(p); 302 return(abs_str); 303 } else 304 return(p); 305} 306 307static int 308infer_path(const char *p, char **ifp) 309{ 310 char *cp; 311 312 cp = strrchr(p, '/'); 313 if (cp == NULL) { 314 *ifp = xstrdup(p); 315 return(0); 316 } 317 318 if (!cp[1]) { 319 error("Invalid path"); 320 return(-1); 321 } 322 323 *ifp = xstrdup(cp + 1); 324 return(0); 325} 326 327static int 328parse_getput_flags(const char **cpp, int *pflag) 329{ 330 const char *cp = *cpp; 331 332 /* Check for flags */ 333 if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { 334 switch (cp[1]) { 335 case 'p': 336 case 'P': 337 *pflag = 1; 338 break; 339 default: 340 error("Invalid flag -%c", cp[1]); 341 return(-1); 342 } 343 cp += 2; 344 *cpp = cp + strspn(cp, WHITESPACE); 345 } 346 347 return(0); 348} 349 350static int 351parse_ls_flags(const char **cpp, int *lflag) 352{ 353 const char *cp = *cpp; 354 355 /* Defaults */ 356 *lflag = LS_NAME_SORT; 357 358 /* Check for flags */ 359 if (cp++[0] == '-') { 360 for (; strchr(WHITESPACE, *cp) == NULL; cp++) { 361 switch (*cp) { 362 case 'l': 363 *lflag &= ~VIEW_FLAGS; 364 *lflag |= LS_LONG_VIEW; 365 break; 366 case '1': 367 *lflag &= ~VIEW_FLAGS; 368 *lflag |= LS_SHORT_VIEW; 369 break; 370 case 'n': 371 *lflag &= ~VIEW_FLAGS; 372 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; 373 break; 374 case 'S': 375 *lflag &= ~SORT_FLAGS; 376 *lflag |= LS_SIZE_SORT; 377 break; 378 case 't': 379 *lflag &= ~SORT_FLAGS; 380 *lflag |= LS_TIME_SORT; 381 break; 382 case 'r': 383 *lflag |= LS_REVERSE_SORT; 384 break; 385 case 'f': 386 *lflag &= ~SORT_FLAGS; 387 break; 388 case 'a': 389 *lflag |= LS_SHOW_ALL; 390 break; 391 default: 392 error("Invalid flag -%c", *cp); 393 return(-1); 394 } 395 } 396 *cpp = cp + strspn(cp, WHITESPACE); 397 } 398 399 return(0); 400} 401 402static int 403get_pathname(const char **cpp, char **path) 404{ 405 const char *cp = *cpp, *end; 406 char quot; 407 int i, j; 408 409 cp += strspn(cp, WHITESPACE); 410 if (!*cp) { 411 *cpp = cp; 412 *path = NULL; 413 return (0); 414 } 415 416 *path = xmalloc(strlen(cp) + 1); 417 418 /* Check for quoted filenames */ 419 if (*cp == '\"' || *cp == '\'') { 420 quot = *cp++; 421 422 /* Search for terminating quote, unescape some chars */ 423 for (i = j = 0; i <= strlen(cp); i++) { 424 if (cp[i] == quot) { /* Found quote */ 425 i++; 426 (*path)[j] = '\0'; 427 break; 428 } 429 if (cp[i] == '\0') { /* End of string */ 430 error("Unterminated quote"); 431 goto fail; 432 } 433 if (cp[i] == '\\') { /* Escaped characters */ 434 i++; 435 if (cp[i] != '\'' && cp[i] != '\"' && 436 cp[i] != '\\') { 437 error("Bad escaped character '\\%c'", 438 cp[i]); 439 goto fail; 440 } 441 } 442 (*path)[j++] = cp[i]; 443 } 444 445 if (j == 0) { 446 error("Empty quotes"); 447 goto fail; 448 } 449 *cpp = cp + i + strspn(cp + i, WHITESPACE); 450 } else { 451 /* Read to end of filename */ 452 end = strpbrk(cp, WHITESPACE); 453 if (end == NULL) 454 end = strchr(cp, '\0'); 455 *cpp = end + strspn(end, WHITESPACE); 456 457 memcpy(*path, cp, end - cp); 458 (*path)[end - cp] = '\0'; 459 } 460 return (0); 461 462 fail: 463 xfree(*path); 464 *path = NULL; 465 return (-1); 466} 467 468static int 469is_dir(char *path) 470{ 471 struct stat sb; 472 473 /* XXX: report errors? */ 474 if (stat(path, &sb) == -1) 475 return(0); 476 477 return(sb.st_mode & S_IFDIR); 478} 479 480static int 481is_reg(char *path) 482{ 483 struct stat sb; 484 485 if (stat(path, &sb) == -1) 486 fatal("stat %s: %s", path, strerror(errno)); 487 488 return(S_ISREG(sb.st_mode)); 489} 490 491static int 492remote_is_dir(struct sftp_conn *conn, char *path) 493{ 494 Attrib *a; 495 496 /* XXX: report errors? */ 497 if ((a = do_stat(conn, path, 1)) == NULL) 498 return(0); 499 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) 500 return(0); 501 return(a->perm & S_IFDIR); 502} 503 504static int 505process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 506{ 507 char *abs_src = NULL; 508 char *abs_dst = NULL; 509 char *tmp; 510 glob_t g; 511 int err = 0; 512 int i; 513 514 abs_src = xstrdup(src); 515 abs_src = make_absolute(abs_src, pwd); 516 517 memset(&g, 0, sizeof(g)); 518 debug3("Looking up %s", abs_src); 519 if (remote_glob(conn, abs_src, 0, NULL, &g)) { 520 error("File \"%s\" not found.", abs_src); 521 err = -1; 522 goto out; 523 } 524 525 /* If multiple matches, dst must be a directory or unspecified */ 526 if (g.gl_matchc > 1 && dst && !is_dir(dst)) { 527 error("Multiple files match, but \"%s\" is not a directory", 528 dst); 529 err = -1; 530 goto out; 531 } 532 533 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 534 if (infer_path(g.gl_pathv[i], &tmp)) { 535 err = -1; 536 goto out; 537 } 538 539 if (g.gl_matchc == 1 && dst) { 540 /* If directory specified, append filename */ 541 if (is_dir(dst)) { 542 if (infer_path(g.gl_pathv[0], &tmp)) { 543 err = 1; 544 goto out; 545 } 546 abs_dst = path_append(dst, tmp); 547 xfree(tmp); 548 } else 549 abs_dst = xstrdup(dst); 550 } else if (dst) { 551 abs_dst = path_append(dst, tmp); 552 xfree(tmp); 553 } else 554 abs_dst = tmp; 555 556 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 557 if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 558 err = -1; 559 xfree(abs_dst); 560 abs_dst = NULL; 561 } 562 563out: 564 xfree(abs_src); 565 if (abs_dst) 566 xfree(abs_dst); 567 globfree(&g); 568 return(err); 569} 570 571static int 572process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 573{ 574 char *tmp_dst = NULL; 575 char *abs_dst = NULL; 576 char *tmp; 577 glob_t g; 578 int err = 0; 579 int i; 580 581 if (dst) { 582 tmp_dst = xstrdup(dst); 583 tmp_dst = make_absolute(tmp_dst, pwd); 584 } 585 586 memset(&g, 0, sizeof(g)); 587 debug3("Looking up %s", src); 588 if (glob(src, 0, NULL, &g)) { 589 error("File \"%s\" not found.", src); 590 err = -1; 591 goto out; 592 } 593 594 /* If multiple matches, dst may be directory or unspecified */ 595 if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { 596 error("Multiple files match, but \"%s\" is not a directory", 597 tmp_dst); 598 err = -1; 599 goto out; 600 } 601 602 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 603 if (!is_reg(g.gl_pathv[i])) { 604 error("skipping non-regular file %s", 605 g.gl_pathv[i]); 606 continue; 607 } 608 if (infer_path(g.gl_pathv[i], &tmp)) { 609 err = -1; 610 goto out; 611 } 612 613 if (g.gl_matchc == 1 && tmp_dst) { 614 /* If directory specified, append filename */ 615 if (remote_is_dir(conn, tmp_dst)) { 616 if (infer_path(g.gl_pathv[0], &tmp)) { 617 err = 1; 618 goto out; 619 } 620 abs_dst = path_append(tmp_dst, tmp); 621 xfree(tmp); 622 } else 623 abs_dst = xstrdup(tmp_dst); 624 625 } else if (tmp_dst) { 626 abs_dst = path_append(tmp_dst, tmp); 627 xfree(tmp); 628 } else 629 abs_dst = make_absolute(tmp, pwd); 630 631 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 632 if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 633 err = -1; 634 } 635 636out: 637 if (abs_dst) 638 xfree(abs_dst); 639 if (tmp_dst) 640 xfree(tmp_dst); 641 globfree(&g); 642 return(err); 643} 644 645static int 646sdirent_comp(const void *aa, const void *bb) 647{ 648 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; 649 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; 650 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; 651 652#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) 653 if (sort_flag & LS_NAME_SORT) 654 return (rmul * strcmp(a->filename, b->filename)); 655 else if (sort_flag & LS_TIME_SORT) 656 return (rmul * NCMP(a->a.mtime, b->a.mtime)); 657 else if (sort_flag & LS_SIZE_SORT) 658 return (rmul * NCMP(a->a.size, b->a.size)); 659 660 fatal("Unknown ls sort type"); 661} 662 663/* sftp ls.1 replacement for directories */ 664static int 665do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) 666{ 667 int n, c = 1, colspace = 0, columns = 1; 668 SFTP_DIRENT **d; 669 670 if ((n = do_readdir(conn, path, &d)) != 0) 671 return (n); 672 673 if (!(lflag & LS_SHORT_VIEW)) { 674 int m = 0, width = 80; 675 struct winsize ws; 676 char *tmp; 677 678 /* Count entries for sort and find longest filename */ 679 for (n = 0; d[n] != NULL; n++) { 680 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL)) 681 m = MAX(m, strlen(d[n]->filename)); 682 } 683 684 /* Add any subpath that also needs to be counted */ 685 tmp = path_strip(path, strip_path); 686 m += strlen(tmp); 687 xfree(tmp); 688 689 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 690 width = ws.ws_col; 691 692 columns = width / (m + 2); 693 columns = MAX(columns, 1); 694 colspace = width / columns; 695 colspace = MIN(colspace, width); 696 } 697 698 if (lflag & SORT_FLAGS) { 699 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); 700 qsort(d, n, sizeof(*d), sdirent_comp); 701 } 702 703 for (n = 0; d[n] != NULL && !interrupted; n++) { 704 char *tmp, *fname; 705 706 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL)) 707 continue; 708 709 tmp = path_append(path, d[n]->filename); 710 fname = path_strip(tmp, strip_path); 711 xfree(tmp); 712 713 if (lflag & LS_LONG_VIEW) { 714 if (lflag & LS_NUMERIC_VIEW) { 715 char *lname; 716 struct stat sb; 717 718 memset(&sb, 0, sizeof(sb)); 719 attrib_to_stat(&d[n]->a, &sb); 720 lname = ls_file(fname, &sb, 1); 721 printf("%s\n", lname); 722 xfree(lname); 723 } else 724 printf("%s\n", d[n]->longname); 725 } else { 726 printf("%-*s", colspace, fname); 727 if (c >= columns) { 728 printf("\n"); 729 c = 1; 730 } else 731 c++; 732 } 733 734 xfree(fname); 735 } 736 737 if (!(lflag & LS_LONG_VIEW) && (c != 1)) 738 printf("\n"); 739 740 free_sftp_dirents(d); 741 return (0); 742} 743 744/* sftp ls.1 replacement which handles path globs */ 745static int 746do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, 747 int lflag) 748{ 749 glob_t g; 750 int i, c = 1, colspace = 0, columns = 1; 751 Attrib *a = NULL; 752 753 memset(&g, 0, sizeof(g)); 754 755 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, 756 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) { 757 if (g.gl_pathc) 758 globfree(&g); 759 error("Can't ls: \"%s\" not found", path); 760 return (-1); 761 } 762 763 if (interrupted) 764 goto out; 765 766 /* 767 * If the glob returns a single match and it is a directory, 768 * then just list its contents. 769 */ 770 if (g.gl_matchc == 1) { 771 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) { 772 globfree(&g); 773 return (-1); 774 } 775 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 776 S_ISDIR(a->perm)) { 777 int err; 778 779 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); 780 globfree(&g); 781 return (err); 782 } 783 } 784 785 if (!(lflag & LS_SHORT_VIEW)) { 786 int m = 0, width = 80; 787 struct winsize ws; 788 789 /* Count entries for sort and find longest filename */ 790 for (i = 0; g.gl_pathv[i]; i++) 791 m = MAX(m, strlen(g.gl_pathv[i])); 792 793 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 794 width = ws.ws_col; 795 796 columns = width / (m + 2); 797 columns = MAX(columns, 1); 798 colspace = width / columns; 799 } 800 801 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) { 802 char *fname; 803 804 fname = path_strip(g.gl_pathv[i], strip_path); 805 806 if (lflag & LS_LONG_VIEW) { 807 char *lname; 808 struct stat sb; 809 810 /* 811 * XXX: this is slow - 1 roundtrip per path 812 * A solution to this is to fork glob() and 813 * build a sftp specific version which keeps the 814 * attribs (which currently get thrown away) 815 * that the server returns as well as the filenames. 816 */ 817 memset(&sb, 0, sizeof(sb)); 818 if (a == NULL) 819 a = do_lstat(conn, g.gl_pathv[i], 1); 820 if (a != NULL) 821 attrib_to_stat(a, &sb); 822 lname = ls_file(fname, &sb, 1); 823 printf("%s\n", lname); 824 xfree(lname); 825 } else { 826 printf("%-*s", colspace, fname); 827 if (c >= columns) { 828 printf("\n"); 829 c = 1; 830 } else 831 c++; 832 } 833 xfree(fname); 834 } 835 836 if (!(lflag & LS_LONG_VIEW) && (c != 1)) 837 printf("\n"); 838 839 out: 840 if (g.gl_pathc) 841 globfree(&g); 842 843 return (0); 844} 845 846static int 847parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, 848 unsigned long *n_arg, char **path1, char **path2) 849{ 850 const char *cmd, *cp = *cpp; 851 char *cp2; 852 int base = 0; 853 long l; 854 int i, cmdnum; 855 856 /* Skip leading whitespace */ 857 cp = cp + strspn(cp, WHITESPACE); 858 859 /* Ignore blank lines and lines which begin with comment '#' char */ 860 if (*cp == '\0' || *cp == '#') 861 return (0); 862 863 /* Check for leading '-' (disable error processing) */ 864 *iflag = 0; 865 if (*cp == '-') { 866 *iflag = 1; 867 cp++; 868 } 869 870 /* Figure out which command we have */ 871 for (i = 0; cmds[i].c; i++) { 872 int cmdlen = strlen(cmds[i].c); 873 874 /* Check for command followed by whitespace */ 875 if (!strncasecmp(cp, cmds[i].c, cmdlen) && 876 strchr(WHITESPACE, cp[cmdlen])) { 877 cp += cmdlen; 878 cp = cp + strspn(cp, WHITESPACE); 879 break; 880 } 881 } 882 cmdnum = cmds[i].n; 883 cmd = cmds[i].c; 884 885 /* Special case */ 886 if (*cp == '!') { 887 cp++; 888 cmdnum = I_SHELL; 889 } else if (cmdnum == -1) { 890 error("Invalid command."); 891 return (-1); 892 } 893 894 /* Get arguments and parse flags */ 895 *lflag = *pflag = *n_arg = 0; 896 *path1 = *path2 = NULL; 897 switch (cmdnum) { 898 case I_GET: 899 case I_PUT: 900 if (parse_getput_flags(&cp, pflag)) 901 return(-1); 902 /* Get first pathname (mandatory) */ 903 if (get_pathname(&cp, path1)) 904 return(-1); 905 if (*path1 == NULL) { 906 error("You must specify at least one path after a " 907 "%s command.", cmd); 908 return(-1); 909 } 910 /* Try to get second pathname (optional) */ 911 if (get_pathname(&cp, path2)) 912 return(-1); 913 break; 914 case I_RENAME: 915 case I_SYMLINK: 916 if (get_pathname(&cp, path1)) 917 return(-1); 918 if (get_pathname(&cp, path2)) 919 return(-1); 920 if (!*path1 || !*path2) { 921 error("You must specify two paths after a %s " 922 "command.", cmd); 923 return(-1); 924 } 925 break; 926 case I_RM: 927 case I_MKDIR: 928 case I_RMDIR: 929 case I_CHDIR: 930 case I_LCHDIR: 931 case I_LMKDIR: 932 /* Get pathname (mandatory) */ 933 if (get_pathname(&cp, path1)) 934 return(-1); 935 if (*path1 == NULL) { 936 error("You must specify a path after a %s command.", 937 cmd); 938 return(-1); 939 } 940 break; 941 case I_LS: 942 if (parse_ls_flags(&cp, lflag)) 943 return(-1); 944 /* Path is optional */ 945 if (get_pathname(&cp, path1)) 946 return(-1); 947 break; 948 case I_LLS: 949 case I_SHELL: 950 /* Uses the rest of the line */ 951 break; 952 case I_LUMASK: 953 base = 8; 954 case I_CHMOD: 955 base = 8; 956 case I_CHOWN: 957 case I_CHGRP: 958 /* Get numeric arg (mandatory) */ 959 l = strtol(cp, &cp2, base); 960 if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) && 961 errno == ERANGE) || l < 0) { 962 error("You must supply a numeric argument " 963 "to the %s command.", cmd); 964 return(-1); 965 } 966 cp = cp2; 967 *n_arg = l; 968 if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp)) 969 break; 970 if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) { 971 error("You must supply a numeric argument " 972 "to the %s command.", cmd); 973 return(-1); 974 } 975 cp += strspn(cp, WHITESPACE); 976 977 /* Get pathname (mandatory) */ 978 if (get_pathname(&cp, path1)) 979 return(-1); 980 if (*path1 == NULL) { 981 error("You must specify a path after a %s command.", 982 cmd); 983 return(-1); 984 } 985 break; 986 case I_QUIT: 987 case I_PWD: 988 case I_LPWD: 989 case I_HELP: 990 case I_VERSION: 991 case I_PROGRESS: 992 break; 993 default: 994 fatal("Command not implemented"); 995 } 996 997 *cpp = cp; 998 return(cmdnum); 999} 1000 1001static int 1002parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 1003 int err_abort) 1004{ 1005 char *path1, *path2, *tmp; 1006 int pflag, lflag, iflag, cmdnum, i; 1007 unsigned long n_arg; 1008 Attrib a, *aa; 1009 char path_buf[MAXPATHLEN]; 1010 int err = 0; 1011 glob_t g; 1012 1013 path1 = path2 = NULL; 1014 cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg, 1015 &path1, &path2); 1016 1017 if (iflag != 0) 1018 err_abort = 0; 1019 1020 memset(&g, 0, sizeof(g)); 1021 1022 /* Perform command */ 1023 switch (cmdnum) { 1024 case 0: 1025 /* Blank line */ 1026 break; 1027 case -1: 1028 /* Unrecognized command */ 1029 err = -1; 1030 break; 1031 case I_GET: 1032 err = process_get(conn, path1, path2, *pwd, pflag); 1033 break; 1034 case I_PUT: 1035 err = process_put(conn, path1, path2, *pwd, pflag); 1036 break; 1037 case I_RENAME: 1038 path1 = make_absolute(path1, *pwd); 1039 path2 = make_absolute(path2, *pwd); 1040 err = do_rename(conn, path1, path2); 1041 break; 1042 case I_SYMLINK: 1043 path2 = make_absolute(path2, *pwd); 1044 err = do_symlink(conn, path1, path2); 1045 break; 1046 case I_RM: 1047 path1 = make_absolute(path1, *pwd); 1048 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1049 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1050 printf("Removing %s\n", g.gl_pathv[i]); 1051 err = do_rm(conn, g.gl_pathv[i]); 1052 if (err != 0 && err_abort) 1053 break; 1054 } 1055 break; 1056 case I_MKDIR: 1057 path1 = make_absolute(path1, *pwd); 1058 attrib_clear(&a); 1059 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1060 a.perm = 0777; 1061 err = do_mkdir(conn, path1, &a); 1062 break; 1063 case I_RMDIR: 1064 path1 = make_absolute(path1, *pwd); 1065 err = do_rmdir(conn, path1); 1066 break; 1067 case I_CHDIR: 1068 path1 = make_absolute(path1, *pwd); 1069 if ((tmp = do_realpath(conn, path1)) == NULL) { 1070 err = 1; 1071 break; 1072 } 1073 if ((aa = do_stat(conn, tmp, 0)) == NULL) { 1074 xfree(tmp); 1075 err = 1; 1076 break; 1077 } 1078 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { 1079 error("Can't change directory: Can't check target"); 1080 xfree(tmp); 1081 err = 1; 1082 break; 1083 } 1084 if (!S_ISDIR(aa->perm)) { 1085 error("Can't change directory: \"%s\" is not " 1086 "a directory", tmp); 1087 xfree(tmp); 1088 err = 1; 1089 break; 1090 } 1091 xfree(*pwd); 1092 *pwd = tmp; 1093 break; 1094 case I_LS: 1095 if (!path1) { 1096 do_globbed_ls(conn, *pwd, *pwd, lflag); 1097 break; 1098 } 1099 1100 /* Strip pwd off beginning of non-absolute paths */ 1101 tmp = NULL; 1102 if (*path1 != '/') 1103 tmp = *pwd; 1104 1105 path1 = make_absolute(path1, *pwd); 1106 err = do_globbed_ls(conn, path1, tmp, lflag); 1107 break; 1108 case I_LCHDIR: 1109 if (chdir(path1) == -1) { 1110 error("Couldn't change local directory to " 1111 "\"%s\": %s", path1, strerror(errno)); 1112 err = 1; 1113 } 1114 break; 1115 case I_LMKDIR: 1116 if (mkdir(path1, 0777) == -1) { 1117 error("Couldn't create local directory " 1118 "\"%s\": %s", path1, strerror(errno)); 1119 err = 1; 1120 } 1121 break; 1122 case I_LLS: 1123 local_do_ls(cmd); 1124 break; 1125 case I_SHELL: 1126 local_do_shell(cmd); 1127 break; 1128 case I_LUMASK: 1129 umask(n_arg); 1130 printf("Local umask: %03lo\n", n_arg); 1131 break; 1132 case I_CHMOD: 1133 path1 = make_absolute(path1, *pwd); 1134 attrib_clear(&a); 1135 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1136 a.perm = n_arg; 1137 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1138 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1139 printf("Changing mode on %s\n", g.gl_pathv[i]); 1140 err = do_setstat(conn, g.gl_pathv[i], &a); 1141 if (err != 0 && err_abort) 1142 break; 1143 } 1144 break; 1145 case I_CHOWN: 1146 case I_CHGRP: 1147 path1 = make_absolute(path1, *pwd); 1148 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1149 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1150 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1151 if (err != 0 && err_abort) 1152 break; 1153 else 1154 continue; 1155 } 1156 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1157 error("Can't get current ownership of " 1158 "remote file \"%s\"", g.gl_pathv[i]); 1159 if (err != 0 && err_abort) 1160 break; 1161 else 1162 continue; 1163 } 1164 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1165 if (cmdnum == I_CHOWN) { 1166 printf("Changing owner on %s\n", g.gl_pathv[i]); 1167 aa->uid = n_arg; 1168 } else { 1169 printf("Changing group on %s\n", g.gl_pathv[i]); 1170 aa->gid = n_arg; 1171 } 1172 err = do_setstat(conn, g.gl_pathv[i], aa); 1173 if (err != 0 && err_abort) 1174 break; 1175 } 1176 break; 1177 case I_PWD: 1178 printf("Remote working directory: %s\n", *pwd); 1179 break; 1180 case I_LPWD: 1181 if (!getcwd(path_buf, sizeof(path_buf))) { 1182 error("Couldn't get local cwd: %s", strerror(errno)); 1183 err = -1; 1184 break; 1185 } 1186 printf("Local working directory: %s\n", path_buf); 1187 break; 1188 case I_QUIT: 1189 /* Processed below */ 1190 break; 1191 case I_HELP: 1192 help(); 1193 break; 1194 case I_VERSION: 1195 printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1196 break; 1197 case I_PROGRESS: 1198 showprogress = !showprogress; 1199 if (showprogress) 1200 printf("Progress meter enabled\n"); 1201 else 1202 printf("Progress meter disabled\n"); 1203 break; 1204 default: 1205 fatal("%d is not implemented", cmdnum); 1206 } 1207 1208 if (g.gl_pathc) 1209 globfree(&g); 1210 if (path1) 1211 xfree(path1); 1212 if (path2) 1213 xfree(path2); 1214 1215 /* If an unignored error occurs in batch mode we should abort. */ 1216 if (err_abort && err != 0) 1217 return (-1); 1218 else if (cmdnum == I_QUIT) 1219 return (1); 1220 1221 return (0); 1222} 1223 1224#ifdef USE_LIBEDIT 1225static char * 1226prompt(EditLine *el) 1227{ 1228 return ("sftp> "); 1229} 1230#endif 1231 1232int 1233interactive_loop(int fd_in, int fd_out, char *file1, char *file2) 1234{ 1235 char *pwd; 1236 char *dir = NULL; 1237 char cmd[2048]; 1238 struct sftp_conn *conn; 1239 int err; 1240 EditLine *el = NULL; 1241#ifdef USE_LIBEDIT 1242 History *hl = NULL; 1243 HistEvent hev; 1244 extern char *__progname; 1245 1246 if (!batchmode && isatty(STDIN_FILENO)) { 1247 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) 1248 fatal("Couldn't initialise editline"); 1249 if ((hl = history_init()) == NULL) 1250 fatal("Couldn't initialise editline history"); 1251 history(hl, &hev, H_SETSIZE, 100); 1252 el_set(el, EL_HIST, history, hl); 1253 1254 el_set(el, EL_PROMPT, prompt); 1255 el_set(el, EL_EDITOR, "emacs"); 1256 el_set(el, EL_TERMINAL, NULL); 1257 el_set(el, EL_SIGNAL, 1); 1258 el_source(el, NULL); 1259 } 1260#endif /* USE_LIBEDIT */ 1261 1262 conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); 1263 if (conn == NULL) 1264 fatal("Couldn't initialise connection to server"); 1265 1266 pwd = do_realpath(conn, "."); 1267 if (pwd == NULL) 1268 fatal("Need cwd"); 1269 1270 if (file1 != NULL) { 1271 dir = xstrdup(file1); 1272 dir = make_absolute(dir, pwd); 1273 1274 if (remote_is_dir(conn, dir) && file2 == NULL) { 1275 printf("Changing to: %s\n", dir); 1276 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 1277 if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) { 1278 xfree(dir); 1279 xfree(pwd); 1280 return (-1); 1281 } 1282 } else { 1283 if (file2 == NULL) 1284 snprintf(cmd, sizeof cmd, "get %s", dir); 1285 else 1286 snprintf(cmd, sizeof cmd, "get %s %s", dir, 1287 file2); 1288 1289 err = parse_dispatch_command(conn, cmd, &pwd, 1); 1290 xfree(dir); 1291 xfree(pwd); 1292 return (err); 1293 } 1294 xfree(dir); 1295 } 1296 1297#if HAVE_SETVBUF 1298 setvbuf(stdout, NULL, _IOLBF, 0); 1299 setvbuf(infile, NULL, _IOLBF, 0); 1300#else 1301 setlinebuf(stdout); 1302 setlinebuf(infile); 1303#endif 1304 1305 err = 0; 1306 for (;;) { 1307 char *cp; 1308 1309 signal(SIGINT, SIG_IGN); 1310 1311 if (el == NULL) { 1312 printf("sftp> "); 1313 if (fgets(cmd, sizeof(cmd), infile) == NULL) { 1314 printf("\n"); 1315 break; 1316 } 1317 if (batchmode) /* Echo command */ 1318 printf("%s", cmd); 1319 } else { 1320#ifdef USE_LIBEDIT 1321 const char *line; 1322 int count = 0; 1323 1324 if ((line = el_gets(el, &count)) == NULL || count <= 0) 1325 break; 1326 history(hl, &hev, H_ENTER, line); 1327 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { 1328 fprintf(stderr, "Error: input line too long\n"); 1329 continue; 1330 } 1331#endif /* USE_LIBEDIT */ 1332 } 1333 1334 cp = strrchr(cmd, '\n'); 1335 if (cp) 1336 *cp = '\0'; 1337 1338 /* Handle user interrupts gracefully during commands */ 1339 interrupted = 0; 1340 signal(SIGINT, cmd_interrupt); 1341 1342 err = parse_dispatch_command(conn, cmd, &pwd, batchmode); 1343 if (err != 0) 1344 break; 1345 } 1346 xfree(pwd); 1347 1348 /* err == 1 signifies normal "quit" exit */ 1349 return (err >= 0 ? 0 : -1); 1350} 1351 1352static void 1353connect_to_server(char *path, char **args, int *in, int *out) 1354{ 1355 int c_in, c_out; 1356 1357#ifdef USE_PIPES 1358 int pin[2], pout[2]; 1359 1360 if ((pipe(pin) == -1) || (pipe(pout) == -1)) 1361 fatal("pipe: %s", strerror(errno)); 1362 *in = pin[0]; 1363 *out = pout[1]; 1364 c_in = pout[0]; 1365 c_out = pin[1]; 1366#else /* USE_PIPES */ 1367 int inout[2]; 1368 1369 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 1370 fatal("socketpair: %s", strerror(errno)); 1371 *in = *out = inout[0]; 1372 c_in = c_out = inout[1]; 1373#endif /* USE_PIPES */ 1374 1375 if ((sshpid = fork()) == -1) 1376 fatal("fork: %s", strerror(errno)); 1377 else if (sshpid == 0) { 1378 if ((dup2(c_in, STDIN_FILENO) == -1) || 1379 (dup2(c_out, STDOUT_FILENO) == -1)) { 1380 fprintf(stderr, "dup2: %s\n", strerror(errno)); 1381 _exit(1); 1382 } 1383 close(*in); 1384 close(*out); 1385 close(c_in); 1386 close(c_out); 1387 1388 /* 1389 * The underlying ssh is in the same process group, so we must 1390 * ignore SIGINT if we want to gracefully abort commands, 1391 * otherwise the signal will make it to the ssh process and 1392 * kill it too 1393 */ 1394 signal(SIGINT, SIG_IGN); 1395 execvp(path, args); 1396 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 1397 _exit(1); 1398 } 1399 1400 signal(SIGTERM, killchild); 1401 signal(SIGINT, killchild); 1402 signal(SIGHUP, killchild); 1403 close(c_in); 1404 close(c_out); 1405} 1406 1407static void 1408usage(void) 1409{ 1410 extern char *__progname; 1411 1412 fprintf(stderr, 1413 "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" 1414 " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" 1415 " [-S program] [-s subsystem | sftp_server] host\n" 1416 " %s [[user@]host[:file [file]]]\n" 1417 " %s [[user@]host[:dir[/]]]\n" 1418 " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); 1419 exit(1); 1420} 1421 1422int 1423main(int argc, char **argv) 1424{ 1425 int in, out, ch, err; 1426 char *host, *userhost, *cp, *file2 = NULL; 1427 int debug_level = 0, sshver = 2; 1428 char *file1 = NULL, *sftp_server = NULL; 1429 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 1430 LogLevel ll = SYSLOG_LEVEL_INFO; 1431 arglist args; 1432 extern int optind; 1433 extern char *optarg; 1434 1435 __progname = ssh_get_progname(argv[0]); 1436 args.list = NULL; 1437 addargs(&args, "ssh"); /* overwritten with ssh_program */ 1438 addargs(&args, "-oForwardX11 no"); 1439 addargs(&args, "-oForwardAgent no"); 1440 addargs(&args, "-oClearAllForwardings yes"); 1441 1442 ll = SYSLOG_LEVEL_INFO; 1443 infile = stdin; 1444 1445 while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { 1446 switch (ch) { 1447 case 'C': 1448 addargs(&args, "-C"); 1449 break; 1450 case 'v': 1451 if (debug_level < 3) { 1452 addargs(&args, "-v"); 1453 ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 1454 } 1455 debug_level++; 1456 break; 1457 case 'F': 1458 case 'o': 1459 addargs(&args, "-%c%s", ch, optarg); 1460 break; 1461 case '1': 1462 sshver = 1; 1463 if (sftp_server == NULL) 1464 sftp_server = _PATH_SFTP_SERVER; 1465 break; 1466 case 's': 1467 sftp_server = optarg; 1468 break; 1469 case 'S': 1470 ssh_program = optarg; 1471 break; 1472 case 'b': 1473 if (batchmode) 1474 fatal("Batch file already specified."); 1475 1476 /* Allow "-" as stdin */ 1477 if (strcmp(optarg, "-") != 0 && 1478 (infile = fopen(optarg, "r")) == NULL) 1479 fatal("%s (%s).", strerror(errno), optarg); 1480 showprogress = 0; 1481 batchmode = 1; 1482 addargs(&args, "-obatchmode yes"); 1483 break; 1484 case 'P': 1485 sftp_direct = optarg; 1486 break; 1487 case 'B': 1488 copy_buffer_len = strtol(optarg, &cp, 10); 1489 if (copy_buffer_len == 0 || *cp != '\0') 1490 fatal("Invalid buffer size \"%s\"", optarg); 1491 break; 1492 case 'R': 1493 num_requests = strtol(optarg, &cp, 10); 1494 if (num_requests == 0 || *cp != '\0') 1495 fatal("Invalid number of requests \"%s\"", 1496 optarg); 1497 break; 1498 case 'h': 1499 default: 1500 usage(); 1501 } 1502 } 1503 1504 if (!isatty(STDERR_FILENO)) 1505 showprogress = 0; 1506 1507 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 1508 1509 if (sftp_direct == NULL) { 1510 if (optind == argc || argc > (optind + 2)) 1511 usage(); 1512 1513 userhost = xstrdup(argv[optind]); 1514 file2 = argv[optind+1]; 1515 1516 if ((host = strrchr(userhost, '@')) == NULL) 1517 host = userhost; 1518 else { 1519 *host++ = '\0'; 1520 if (!userhost[0]) { 1521 fprintf(stderr, "Missing username\n"); 1522 usage(); 1523 } 1524 addargs(&args, "-l%s",userhost); 1525 } 1526 1527 if ((cp = colon(host)) != NULL) { 1528 *cp++ = '\0'; 1529 file1 = cp; 1530 } 1531 1532 host = cleanhostname(host); 1533 if (!*host) { 1534 fprintf(stderr, "Missing hostname\n"); 1535 usage(); 1536 } 1537 1538 addargs(&args, "-oProtocol %d", sshver); 1539 1540 /* no subsystem if the server-spec contains a '/' */ 1541 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 1542 addargs(&args, "-s"); 1543 1544 addargs(&args, "%s", host); 1545 addargs(&args, "%s", (sftp_server != NULL ? 1546 sftp_server : "sftp")); 1547 args.list[0] = ssh_program; 1548 1549 if (!batchmode) 1550 fprintf(stderr, "Connecting to %s...\n", host); 1551 connect_to_server(ssh_program, args.list, &in, &out); 1552 } else { 1553 args.list = NULL; 1554 addargs(&args, "sftp-server"); 1555 1556 if (!batchmode) 1557 fprintf(stderr, "Attaching to %s...\n", sftp_direct); 1558 connect_to_server(sftp_direct, args.list, &in, &out); 1559 } 1560 1561 err = interactive_loop(in, out, file1, file2); 1562 1563#if !defined(USE_PIPES) 1564 shutdown(in, SHUT_RDWR); 1565 shutdown(out, SHUT_RDWR); 1566#endif 1567 1568 close(in); 1569 close(out); 1570 if (batchmode) 1571 fclose(infile); 1572 1573 while (waitpid(sshpid, NULL, 0) == -1) 1574 if (errno != EINTR) 1575 fatal("Couldn't wait for ssh process: %s", 1576 strerror(errno)); 1577 1578 exit(err == 0 ? 0 : 1); 1579} 1580