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