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