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