1 /* $OpenBSD: sftp.c,v 1.93 2006/09/30 17:48:22 ray 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 37 typedef 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 */ 57 FILE* infile; 58 59 /* Are we in batchfile mode? */ 60 int batchmode = 0; 61 62 /* Size of buffer used when copying files */ 63 size_t copy_buffer_len = 32768; 64 65 /* Number of concurrent outstanding requests */ 66 size_t num_requests = 16; 67 68 /* PID of ssh transport process */ 69 static pid_t sshpid = -1; 70 71 /* This is set to 0 if the progressmeter is not desired. */ 72 int showprogress = 1; 73 74 /* SIGINT received during command processing */ 75 volatile sig_atomic_t interrupted = 0; 76 77 /* I wish qsort() took a separate ctx for the comparison function...*/ 78 int sort_flag; 79 80 int remote_glob(struct sftp_conn *, const char *, int, 81 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 82 83 extern 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 126 struct CMD { 127 const char *c; 128 const int n; 129 }; 130 131 static 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 167 int interactive_loop(int fd_in, int fd_out, char *file1, char *file2); 168 169 static void 170 killchild(int signo) 171 { 172 if (sshpid > 1) { 173 kill(sshpid, SIGTERM); 174 waitpid(sshpid, NULL, 0); 175 } 176 177 _exit(1); 178 } 179 180 static void 181 cmd_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 191 static void 192 help(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 224 static void 225 local_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 262 static void 263 local_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 */ 279 static char * 280 path_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 297 static char * 298 path_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 312 static char * 313 make_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 326 static int 327 infer_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 346 static int 347 parse_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 369 static int 370 parse_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 421 static int 422 get_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 487 static int 488 is_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 499 static int 500 is_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 510 static int 511 remote_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 523 static int 524 process_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 583 out: 584 xfree(abs_src); 585 globfree(&g); 586 return(err); 587 } 588 589 static int 590 process_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 654 out: 655 if (abs_dst) 656 xfree(abs_dst); 657 if (tmp_dst) 658 xfree(tmp_dst); 659 globfree(&g); 660 return(err); 661 } 662 663 static int 664 sdirent_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 */ 682 static int 683 do_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 */ 766 static int 767 do_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 867 static int 868 parse_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 errno = 0; 981 l = strtol(cp, &cp2, base); 982 if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) && 983 errno == ERANGE) || l < 0) { 984 error("You must supply a numeric argument " 985 "to the %s command.", cmd); 986 return(-1); 987 } 988 cp = cp2; 989 *n_arg = l; 990 if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp)) 991 break; 992 if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) { 993 error("You must supply a numeric argument " 994 "to the %s command.", cmd); 995 return(-1); 996 } 997 cp += strspn(cp, WHITESPACE); 998 999 /* Get pathname (mandatory) */ 1000 if (get_pathname(&cp, path1)) 1001 return(-1); 1002 if (*path1 == NULL) { 1003 error("You must specify a path after a %s command.", 1004 cmd); 1005 return(-1); 1006 } 1007 break; 1008 case I_QUIT: 1009 case I_PWD: 1010 case I_LPWD: 1011 case I_HELP: 1012 case I_VERSION: 1013 case I_PROGRESS: 1014 break; 1015 default: 1016 fatal("Command not implemented"); 1017 } 1018 1019 *cpp = cp; 1020 return(cmdnum); 1021 } 1022 1023 static int 1024 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 1025 int err_abort) 1026 { 1027 char *path1, *path2, *tmp; 1028 int pflag, lflag, iflag, cmdnum, i; 1029 unsigned long n_arg; 1030 Attrib a, *aa; 1031 char path_buf[MAXPATHLEN]; 1032 int err = 0; 1033 glob_t g; 1034 1035 path1 = path2 = NULL; 1036 cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg, 1037 &path1, &path2); 1038 1039 if (iflag != 0) 1040 err_abort = 0; 1041 1042 memset(&g, 0, sizeof(g)); 1043 1044 /* Perform command */ 1045 switch (cmdnum) { 1046 case 0: 1047 /* Blank line */ 1048 break; 1049 case -1: 1050 /* Unrecognized command */ 1051 err = -1; 1052 break; 1053 case I_GET: 1054 err = process_get(conn, path1, path2, *pwd, pflag); 1055 break; 1056 case I_PUT: 1057 err = process_put(conn, path1, path2, *pwd, pflag); 1058 break; 1059 case I_RENAME: 1060 path1 = make_absolute(path1, *pwd); 1061 path2 = make_absolute(path2, *pwd); 1062 err = do_rename(conn, path1, path2); 1063 break; 1064 case I_SYMLINK: 1065 path2 = make_absolute(path2, *pwd); 1066 err = do_symlink(conn, path1, path2); 1067 break; 1068 case I_RM: 1069 path1 = make_absolute(path1, *pwd); 1070 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1071 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1072 printf("Removing %s\n", g.gl_pathv[i]); 1073 err = do_rm(conn, g.gl_pathv[i]); 1074 if (err != 0 && err_abort) 1075 break; 1076 } 1077 break; 1078 case I_MKDIR: 1079 path1 = make_absolute(path1, *pwd); 1080 attrib_clear(&a); 1081 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1082 a.perm = 0777; 1083 err = do_mkdir(conn, path1, &a); 1084 break; 1085 case I_RMDIR: 1086 path1 = make_absolute(path1, *pwd); 1087 err = do_rmdir(conn, path1); 1088 break; 1089 case I_CHDIR: 1090 path1 = make_absolute(path1, *pwd); 1091 if ((tmp = do_realpath(conn, path1)) == NULL) { 1092 err = 1; 1093 break; 1094 } 1095 if ((aa = do_stat(conn, tmp, 0)) == NULL) { 1096 xfree(tmp); 1097 err = 1; 1098 break; 1099 } 1100 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { 1101 error("Can't change directory: Can't check target"); 1102 xfree(tmp); 1103 err = 1; 1104 break; 1105 } 1106 if (!S_ISDIR(aa->perm)) { 1107 error("Can't change directory: \"%s\" is not " 1108 "a directory", tmp); 1109 xfree(tmp); 1110 err = 1; 1111 break; 1112 } 1113 xfree(*pwd); 1114 *pwd = tmp; 1115 break; 1116 case I_LS: 1117 if (!path1) { 1118 do_globbed_ls(conn, *pwd, *pwd, lflag); 1119 break; 1120 } 1121 1122 /* Strip pwd off beginning of non-absolute paths */ 1123 tmp = NULL; 1124 if (*path1 != '/') 1125 tmp = *pwd; 1126 1127 path1 = make_absolute(path1, *pwd); 1128 err = do_globbed_ls(conn, path1, tmp, lflag); 1129 break; 1130 case I_LCHDIR: 1131 if (chdir(path1) == -1) { 1132 error("Couldn't change local directory to " 1133 "\"%s\": %s", path1, strerror(errno)); 1134 err = 1; 1135 } 1136 break; 1137 case I_LMKDIR: 1138 if (mkdir(path1, 0777) == -1) { 1139 error("Couldn't create local directory " 1140 "\"%s\": %s", path1, strerror(errno)); 1141 err = 1; 1142 } 1143 break; 1144 case I_LLS: 1145 local_do_ls(cmd); 1146 break; 1147 case I_SHELL: 1148 local_do_shell(cmd); 1149 break; 1150 case I_LUMASK: 1151 umask(n_arg); 1152 printf("Local umask: %03lo\n", n_arg); 1153 break; 1154 case I_CHMOD: 1155 path1 = make_absolute(path1, *pwd); 1156 attrib_clear(&a); 1157 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1158 a.perm = n_arg; 1159 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1160 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1161 printf("Changing mode on %s\n", g.gl_pathv[i]); 1162 err = do_setstat(conn, g.gl_pathv[i], &a); 1163 if (err != 0 && err_abort) 1164 break; 1165 } 1166 break; 1167 case I_CHOWN: 1168 case I_CHGRP: 1169 path1 = make_absolute(path1, *pwd); 1170 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1171 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1172 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1173 if (err != 0 && err_abort) 1174 break; 1175 else 1176 continue; 1177 } 1178 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1179 error("Can't get current ownership of " 1180 "remote file \"%s\"", g.gl_pathv[i]); 1181 if (err != 0 && err_abort) 1182 break; 1183 else 1184 continue; 1185 } 1186 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1187 if (cmdnum == I_CHOWN) { 1188 printf("Changing owner on %s\n", g.gl_pathv[i]); 1189 aa->uid = n_arg; 1190 } else { 1191 printf("Changing group on %s\n", g.gl_pathv[i]); 1192 aa->gid = n_arg; 1193 } 1194 err = do_setstat(conn, g.gl_pathv[i], aa); 1195 if (err != 0 && err_abort) 1196 break; 1197 } 1198 break; 1199 case I_PWD: 1200 printf("Remote working directory: %s\n", *pwd); 1201 break; 1202 case I_LPWD: 1203 if (!getcwd(path_buf, sizeof(path_buf))) { 1204 error("Couldn't get local cwd: %s", strerror(errno)); 1205 err = -1; 1206 break; 1207 } 1208 printf("Local working directory: %s\n", path_buf); 1209 break; 1210 case I_QUIT: 1211 /* Processed below */ 1212 break; 1213 case I_HELP: 1214 help(); 1215 break; 1216 case I_VERSION: 1217 printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1218 break; 1219 case I_PROGRESS: 1220 showprogress = !showprogress; 1221 if (showprogress) 1222 printf("Progress meter enabled\n"); 1223 else 1224 printf("Progress meter disabled\n"); 1225 break; 1226 default: 1227 fatal("%d is not implemented", cmdnum); 1228 } 1229 1230 if (g.gl_pathc) 1231 globfree(&g); 1232 if (path1) 1233 xfree(path1); 1234 if (path2) 1235 xfree(path2); 1236 1237 /* If an unignored error occurs in batch mode we should abort. */ 1238 if (err_abort && err != 0) 1239 return (-1); 1240 else if (cmdnum == I_QUIT) 1241 return (1); 1242 1243 return (0); 1244 } 1245 1246 #ifdef USE_LIBEDIT 1247 static char * 1248 prompt(EditLine *el) 1249 { 1250 return ("sftp> "); 1251 } 1252 #endif 1253 1254 int 1255 interactive_loop(int fd_in, int fd_out, char *file1, char *file2) 1256 { 1257 char *pwd; 1258 char *dir = NULL; 1259 char cmd[2048]; 1260 struct sftp_conn *conn; 1261 int err, interactive; 1262 EditLine *el = NULL; 1263 #ifdef USE_LIBEDIT 1264 History *hl = NULL; 1265 HistEvent hev; 1266 extern char *__progname; 1267 1268 if (!batchmode && isatty(STDIN_FILENO)) { 1269 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) 1270 fatal("Couldn't initialise editline"); 1271 if ((hl = history_init()) == NULL) 1272 fatal("Couldn't initialise editline history"); 1273 history(hl, &hev, H_SETSIZE, 100); 1274 el_set(el, EL_HIST, history, hl); 1275 1276 el_set(el, EL_PROMPT, prompt); 1277 el_set(el, EL_EDITOR, "emacs"); 1278 el_set(el, EL_TERMINAL, NULL); 1279 el_set(el, EL_SIGNAL, 1); 1280 el_source(el, NULL); 1281 } 1282 #endif /* USE_LIBEDIT */ 1283 1284 conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); 1285 if (conn == NULL) 1286 fatal("Couldn't initialise connection to server"); 1287 1288 pwd = do_realpath(conn, "."); 1289 if (pwd == NULL) 1290 fatal("Need cwd"); 1291 1292 if (file1 != NULL) { 1293 dir = xstrdup(file1); 1294 dir = make_absolute(dir, pwd); 1295 1296 if (remote_is_dir(conn, dir) && file2 == NULL) { 1297 printf("Changing to: %s\n", dir); 1298 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 1299 if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) { 1300 xfree(dir); 1301 xfree(pwd); 1302 xfree(conn); 1303 return (-1); 1304 } 1305 } else { 1306 if (file2 == NULL) 1307 snprintf(cmd, sizeof cmd, "get %s", dir); 1308 else 1309 snprintf(cmd, sizeof cmd, "get %s %s", dir, 1310 file2); 1311 1312 err = parse_dispatch_command(conn, cmd, &pwd, 1); 1313 xfree(dir); 1314 xfree(pwd); 1315 xfree(conn); 1316 return (err); 1317 } 1318 xfree(dir); 1319 } 1320 1321 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF) 1322 setvbuf(stdout, NULL, _IOLBF, 0); 1323 setvbuf(infile, NULL, _IOLBF, 0); 1324 #else 1325 setlinebuf(stdout); 1326 setlinebuf(infile); 1327 #endif 1328 1329 interactive = !batchmode && isatty(STDIN_FILENO); 1330 err = 0; 1331 for (;;) { 1332 char *cp; 1333 1334 signal(SIGINT, SIG_IGN); 1335 1336 if (el == NULL) { 1337 if (interactive) 1338 printf("sftp> "); 1339 if (fgets(cmd, sizeof(cmd), infile) == NULL) { 1340 if (interactive) 1341 printf("\n"); 1342 break; 1343 } 1344 if (!interactive) { /* Echo command */ 1345 printf("sftp> %s", cmd); 1346 if (strlen(cmd) > 0 && 1347 cmd[strlen(cmd) - 1] != '\n') 1348 printf("\n"); 1349 } 1350 } else { 1351 #ifdef USE_LIBEDIT 1352 const char *line; 1353 int count = 0; 1354 1355 if ((line = el_gets(el, &count)) == NULL || count <= 0) { 1356 printf("\n"); 1357 break; 1358 } 1359 history(hl, &hev, H_ENTER, line); 1360 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { 1361 fprintf(stderr, "Error: input line too long\n"); 1362 continue; 1363 } 1364 #endif /* USE_LIBEDIT */ 1365 } 1366 1367 cp = strrchr(cmd, '\n'); 1368 if (cp) 1369 *cp = '\0'; 1370 1371 /* Handle user interrupts gracefully during commands */ 1372 interrupted = 0; 1373 signal(SIGINT, cmd_interrupt); 1374 1375 err = parse_dispatch_command(conn, cmd, &pwd, batchmode); 1376 if (err != 0) 1377 break; 1378 } 1379 xfree(pwd); 1380 xfree(conn); 1381 1382 #ifdef USE_LIBEDIT 1383 if (el != NULL) 1384 el_end(el); 1385 #endif /* USE_LIBEDIT */ 1386 1387 /* err == 1 signifies normal "quit" exit */ 1388 return (err >= 0 ? 0 : -1); 1389 } 1390 1391 static void 1392 connect_to_server(char *path, char **args, int *in, int *out) 1393 { 1394 int c_in, c_out; 1395 1396 #ifdef USE_PIPES 1397 int pin[2], pout[2]; 1398 1399 if ((pipe(pin) == -1) || (pipe(pout) == -1)) 1400 fatal("pipe: %s", strerror(errno)); 1401 *in = pin[0]; 1402 *out = pout[1]; 1403 c_in = pout[0]; 1404 c_out = pin[1]; 1405 #else /* USE_PIPES */ 1406 int inout[2]; 1407 1408 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 1409 fatal("socketpair: %s", strerror(errno)); 1410 *in = *out = inout[0]; 1411 c_in = c_out = inout[1]; 1412 #endif /* USE_PIPES */ 1413 1414 if ((sshpid = fork()) == -1) 1415 fatal("fork: %s", strerror(errno)); 1416 else if (sshpid == 0) { 1417 if ((dup2(c_in, STDIN_FILENO) == -1) || 1418 (dup2(c_out, STDOUT_FILENO) == -1)) { 1419 fprintf(stderr, "dup2: %s\n", strerror(errno)); 1420 _exit(1); 1421 } 1422 close(*in); 1423 close(*out); 1424 close(c_in); 1425 close(c_out); 1426 1427 /* 1428 * The underlying ssh is in the same process group, so we must 1429 * ignore SIGINT if we want to gracefully abort commands, 1430 * otherwise the signal will make it to the ssh process and 1431 * kill it too 1432 */ 1433 signal(SIGINT, SIG_IGN); 1434 execvp(path, args); 1435 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 1436 _exit(1); 1437 } 1438 1439 signal(SIGTERM, killchild); 1440 signal(SIGINT, killchild); 1441 signal(SIGHUP, killchild); 1442 close(c_in); 1443 close(c_out); 1444 } 1445 1446 static void 1447 usage(void) 1448 { 1449 extern char *__progname; 1450 1451 fprintf(stderr, 1452 "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" 1453 " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" 1454 " [-S program] [-s subsystem | sftp_server] host\n" 1455 " %s [[user@]host[:file [file]]]\n" 1456 " %s [[user@]host[:dir[/]]]\n" 1457 " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); 1458 exit(1); 1459 } 1460 1461 int 1462 main(int argc, char **argv) 1463 { 1464 int in, out, ch, err; 1465 char *host, *userhost, *cp, *file2 = NULL; 1466 int debug_level = 0, sshver = 2; 1467 char *file1 = NULL, *sftp_server = NULL; 1468 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 1469 LogLevel ll = SYSLOG_LEVEL_INFO; 1470 arglist args; 1471 extern int optind; 1472 extern char *optarg; 1473 1474 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1475 sanitise_stdfd(); 1476 1477 __progname = ssh_get_progname(argv[0]); 1478 memset(&args, '\0', sizeof(args)); 1479 args.list = NULL; 1480 addargs(&args, "%s", ssh_program); 1481 addargs(&args, "-oForwardX11 no"); 1482 addargs(&args, "-oForwardAgent no"); 1483 addargs(&args, "-oPermitLocalCommand no"); 1484 addargs(&args, "-oClearAllForwardings yes"); 1485 1486 ll = SYSLOG_LEVEL_INFO; 1487 infile = stdin; 1488 1489 while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { 1490 switch (ch) { 1491 case 'C': 1492 addargs(&args, "-C"); 1493 break; 1494 case 'v': 1495 if (debug_level < 3) { 1496 addargs(&args, "-v"); 1497 ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 1498 } 1499 debug_level++; 1500 break; 1501 case 'F': 1502 case 'o': 1503 addargs(&args, "-%c%s", ch, optarg); 1504 break; 1505 case '1': 1506 sshver = 1; 1507 if (sftp_server == NULL) 1508 sftp_server = _PATH_SFTP_SERVER; 1509 break; 1510 case 's': 1511 sftp_server = optarg; 1512 break; 1513 case 'S': 1514 ssh_program = optarg; 1515 replacearg(&args, 0, "%s", ssh_program); 1516 break; 1517 case 'b': 1518 if (batchmode) 1519 fatal("Batch file already specified."); 1520 1521 /* Allow "-" as stdin */ 1522 if (strcmp(optarg, "-") != 0 && 1523 (infile = fopen(optarg, "r")) == NULL) 1524 fatal("%s (%s).", strerror(errno), optarg); 1525 showprogress = 0; 1526 batchmode = 1; 1527 addargs(&args, "-obatchmode yes"); 1528 break; 1529 case 'P': 1530 sftp_direct = optarg; 1531 break; 1532 case 'B': 1533 copy_buffer_len = strtol(optarg, &cp, 10); 1534 if (copy_buffer_len == 0 || *cp != '\0') 1535 fatal("Invalid buffer size \"%s\"", optarg); 1536 break; 1537 case 'R': 1538 num_requests = strtol(optarg, &cp, 10); 1539 if (num_requests == 0 || *cp != '\0') 1540 fatal("Invalid number of requests \"%s\"", 1541 optarg); 1542 break; 1543 case 'h': 1544 default: 1545 usage(); 1546 } 1547 } 1548 1549 if (!isatty(STDERR_FILENO)) 1550 showprogress = 0; 1551 1552 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 1553 1554 if (sftp_direct == NULL) { 1555 if (optind == argc || argc > (optind + 2)) 1556 usage(); 1557 1558 userhost = xstrdup(argv[optind]); 1559 file2 = argv[optind+1]; 1560 1561 if ((host = strrchr(userhost, '@')) == NULL) 1562 host = userhost; 1563 else { 1564 *host++ = '\0'; 1565 if (!userhost[0]) { 1566 fprintf(stderr, "Missing username\n"); 1567 usage(); 1568 } 1569 addargs(&args, "-l%s",userhost); 1570 } 1571 1572 if ((cp = colon(host)) != NULL) { 1573 *cp++ = '\0'; 1574 file1 = cp; 1575 } 1576 1577 host = cleanhostname(host); 1578 if (!*host) { 1579 fprintf(stderr, "Missing hostname\n"); 1580 usage(); 1581 } 1582 1583 addargs(&args, "-oProtocol %d", sshver); 1584 1585 /* no subsystem if the server-spec contains a '/' */ 1586 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 1587 addargs(&args, "-s"); 1588 1589 addargs(&args, "%s", host); 1590 addargs(&args, "%s", (sftp_server != NULL ? 1591 sftp_server : "sftp")); 1592 1593 if (!batchmode) 1594 fprintf(stderr, "Connecting to %s...\n", host); 1595 connect_to_server(ssh_program, args.list, &in, &out); 1596 } else { 1597 args.list = NULL; 1598 addargs(&args, "sftp-server"); 1599 1600 if (!batchmode) 1601 fprintf(stderr, "Attaching to %s...\n", sftp_direct); 1602 connect_to_server(sftp_direct, args.list, &in, &out); 1603 } 1604 freeargs(&args); 1605 1606 err = interactive_loop(in, out, file1, file2); 1607 1608 #if !defined(USE_PIPES) 1609 shutdown(in, SHUT_RDWR); 1610 shutdown(out, SHUT_RDWR); 1611 #endif 1612 1613 close(in); 1614 close(out); 1615 if (batchmode) 1616 fclose(infile); 1617 1618 while (waitpid(sshpid, NULL, 0) == -1) 1619 if (errno != EINTR) 1620 fatal("Couldn't wait for ssh process: %s", 1621 strerror(errno)); 1622 1623 exit(err == 0 ? 0 : 1); 1624 } 1625