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