1 /* $OpenBSD: sftp.c,v 1.125 2010/06/18 00:58:39 djm Exp $ */ 2 /* 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "includes.h" 19 20 #include <sys/types.h> 21 #include <sys/ioctl.h> 22 #ifdef HAVE_SYS_STAT_H 23 # include <sys/stat.h> 24 #endif 25 #include <sys/param.h> 26 #include <sys/socket.h> 27 #include <sys/wait.h> 28 #ifdef HAVE_SYS_STATVFS_H 29 #include <sys/statvfs.h> 30 #endif 31 32 #include <ctype.h> 33 #include <errno.h> 34 35 #ifdef HAVE_PATHS_H 36 # include <paths.h> 37 #endif 38 #ifdef HAVE_LIBGEN_H 39 #include <libgen.h> 40 #endif 41 #ifdef USE_LIBEDIT 42 #include <histedit.h> 43 #else 44 typedef void EditLine; 45 #endif 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 #ifdef HAVE_UTIL_H 54 # include <util.h> 55 #endif 56 57 #ifdef HAVE_LIBUTIL_H 58 # include <libutil.h> 59 #endif 60 61 #include "xmalloc.h" 62 #include "log.h" 63 #include "pathnames.h" 64 #include "misc.h" 65 66 #include "sftp.h" 67 #include "buffer.h" 68 #include "sftp-common.h" 69 #include "sftp-client.h" 70 71 #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */ 72 #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */ 73 74 /* File to read commands from */ 75 FILE* infile; 76 77 /* Are we in batchfile mode? */ 78 int batchmode = 0; 79 80 /* PID of ssh transport process */ 81 static pid_t sshpid = -1; 82 83 /* This is set to 0 if the progressmeter is not desired. */ 84 int showprogress = 1; 85 86 /* When this option is set, we always recursively download/upload directories */ 87 int global_rflag = 0; 88 89 /* When this option is set, the file transfers will always preserve times */ 90 int global_pflag = 0; 91 92 /* SIGINT received during command processing */ 93 volatile sig_atomic_t interrupted = 0; 94 95 /* I wish qsort() took a separate ctx for the comparison function...*/ 96 int sort_flag; 97 98 /* Context used for commandline completion */ 99 struct complete_ctx { 100 struct sftp_conn *conn; 101 char **remote_pathp; 102 }; 103 104 int remote_glob(struct sftp_conn *, const char *, int, 105 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 106 107 extern char *__progname; 108 109 /* Separators for interactive commands */ 110 #define WHITESPACE " \t\r\n" 111 112 /* ls flags */ 113 #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */ 114 #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */ 115 #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */ 116 #define LS_NAME_SORT 0x0008 /* Sort by name (default) */ 117 #define LS_TIME_SORT 0x0010 /* Sort by mtime */ 118 #define LS_SIZE_SORT 0x0020 /* Sort by file size */ 119 #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */ 120 #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */ 121 #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */ 122 123 #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS) 124 #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) 125 126 /* Commands for interactive mode */ 127 #define I_CHDIR 1 128 #define I_CHGRP 2 129 #define I_CHMOD 3 130 #define I_CHOWN 4 131 #define I_DF 24 132 #define I_GET 5 133 #define I_HELP 6 134 #define I_LCHDIR 7 135 #define I_LLS 8 136 #define I_LMKDIR 9 137 #define I_LPWD 10 138 #define I_LS 11 139 #define I_LUMASK 12 140 #define I_MKDIR 13 141 #define I_PUT 14 142 #define I_PWD 15 143 #define I_QUIT 16 144 #define I_RENAME 17 145 #define I_RM 18 146 #define I_RMDIR 19 147 #define I_SHELL 20 148 #define I_SYMLINK 21 149 #define I_VERSION 22 150 #define I_PROGRESS 23 151 152 struct CMD { 153 const char *c; 154 const int n; 155 const int t; 156 }; 157 158 /* Type of completion */ 159 #define NOARGS 0 160 #define REMOTE 1 161 #define LOCAL 2 162 163 static const struct CMD cmds[] = { 164 { "bye", I_QUIT, NOARGS }, 165 { "cd", I_CHDIR, REMOTE }, 166 { "chdir", I_CHDIR, REMOTE }, 167 { "chgrp", I_CHGRP, REMOTE }, 168 { "chmod", I_CHMOD, REMOTE }, 169 { "chown", I_CHOWN, REMOTE }, 170 { "df", I_DF, REMOTE }, 171 { "dir", I_LS, REMOTE }, 172 { "exit", I_QUIT, NOARGS }, 173 { "get", I_GET, REMOTE }, 174 { "help", I_HELP, NOARGS }, 175 { "lcd", I_LCHDIR, LOCAL }, 176 { "lchdir", I_LCHDIR, LOCAL }, 177 { "lls", I_LLS, LOCAL }, 178 { "lmkdir", I_LMKDIR, LOCAL }, 179 { "ln", I_SYMLINK, REMOTE }, 180 { "lpwd", I_LPWD, LOCAL }, 181 { "ls", I_LS, REMOTE }, 182 { "lumask", I_LUMASK, NOARGS }, 183 { "mkdir", I_MKDIR, REMOTE }, 184 { "mget", I_GET, REMOTE }, 185 { "mput", I_PUT, LOCAL }, 186 { "progress", I_PROGRESS, NOARGS }, 187 { "put", I_PUT, LOCAL }, 188 { "pwd", I_PWD, REMOTE }, 189 { "quit", I_QUIT, NOARGS }, 190 { "rename", I_RENAME, REMOTE }, 191 { "rm", I_RM, REMOTE }, 192 { "rmdir", I_RMDIR, REMOTE }, 193 { "symlink", I_SYMLINK, REMOTE }, 194 { "version", I_VERSION, NOARGS }, 195 { "!", I_SHELL, NOARGS }, 196 { "?", I_HELP, NOARGS }, 197 { NULL, -1, -1 } 198 }; 199 200 int interactive_loop(struct sftp_conn *, char *file1, char *file2); 201 202 /* ARGSUSED */ 203 static void 204 killchild(int signo) 205 { 206 if (sshpid > 1) { 207 kill(sshpid, SIGTERM); 208 waitpid(sshpid, NULL, 0); 209 } 210 211 _exit(1); 212 } 213 214 /* ARGSUSED */ 215 static void 216 cmd_interrupt(int signo) 217 { 218 const char msg[] = "\rInterrupt \n"; 219 int olderrno = errno; 220 221 write(STDERR_FILENO, msg, sizeof(msg) - 1); 222 interrupted = 1; 223 errno = olderrno; 224 } 225 226 static void 227 help(void) 228 { 229 printf("Available commands:\n" 230 "bye Quit sftp\n" 231 "cd path Change remote directory to 'path'\n" 232 "chgrp grp path Change group of file 'path' to 'grp'\n" 233 "chmod mode path Change permissions of file 'path' to 'mode'\n" 234 "chown own path Change owner of file 'path' to 'own'\n" 235 "df [-hi] [path] Display statistics for current directory or\n" 236 " filesystem containing 'path'\n" 237 "exit Quit sftp\n" 238 "get [-Ppr] remote [local] Download file\n" 239 "help Display this help text\n" 240 "lcd path Change local directory to 'path'\n" 241 "lls [ls-options [path]] Display local directory listing\n" 242 "lmkdir path Create local directory\n" 243 "ln oldpath newpath Symlink remote file\n" 244 "lpwd Print local working directory\n" 245 "ls [-1afhlnrSt] [path] Display remote directory listing\n" 246 "lumask umask Set local umask to 'umask'\n" 247 "mkdir path Create remote directory\n" 248 "progress Toggle display of progress meter\n" 249 "put [-Ppr] local [remote] Upload file\n" 250 "pwd Display remote working directory\n" 251 "quit Quit sftp\n" 252 "rename oldpath newpath Rename remote file\n" 253 "rm path Delete remote file\n" 254 "rmdir path Remove remote directory\n" 255 "symlink oldpath newpath Symlink remote file\n" 256 "version Show SFTP version\n" 257 "!command Execute 'command' in local shell\n" 258 "! Escape to local shell\n" 259 "? Synonym for help\n"); 260 } 261 262 static void 263 local_do_shell(const char *args) 264 { 265 int status; 266 char *shell; 267 pid_t pid; 268 269 if (!*args) 270 args = NULL; 271 272 if ((shell = getenv("SHELL")) == NULL) 273 shell = _PATH_BSHELL; 274 275 if ((pid = fork()) == -1) 276 fatal("Couldn't fork: %s", strerror(errno)); 277 278 if (pid == 0) { 279 /* XXX: child has pipe fds to ssh subproc open - issue? */ 280 if (args) { 281 debug3("Executing %s -c \"%s\"", shell, args); 282 execl(shell, shell, "-c", args, (char *)NULL); 283 } else { 284 debug3("Executing %s", shell); 285 execl(shell, shell, (char *)NULL); 286 } 287 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, 288 strerror(errno)); 289 _exit(1); 290 } 291 while (waitpid(pid, &status, 0) == -1) 292 if (errno != EINTR) 293 fatal("Couldn't wait for child: %s", strerror(errno)); 294 if (!WIFEXITED(status)) 295 error("Shell exited abnormally"); 296 else if (WEXITSTATUS(status)) 297 error("Shell exited with status %d", WEXITSTATUS(status)); 298 } 299 300 static void 301 local_do_ls(const char *args) 302 { 303 if (!args || !*args) 304 local_do_shell(_PATH_LS); 305 else { 306 int len = strlen(_PATH_LS " ") + strlen(args) + 1; 307 char *buf = xmalloc(len); 308 309 /* XXX: quoting - rip quoting code from ftp? */ 310 snprintf(buf, len, _PATH_LS " %s", args); 311 local_do_shell(buf); 312 xfree(buf); 313 } 314 } 315 316 /* Strip one path (usually the pwd) from the start of another */ 317 static char * 318 path_strip(char *path, char *strip) 319 { 320 size_t len; 321 322 if (strip == NULL) 323 return (xstrdup(path)); 324 325 len = strlen(strip); 326 if (strncmp(path, strip, len) == 0) { 327 if (strip[len - 1] != '/' && path[len] == '/') 328 len++; 329 return (xstrdup(path + len)); 330 } 331 332 return (xstrdup(path)); 333 } 334 335 static char * 336 make_absolute(char *p, char *pwd) 337 { 338 char *abs_str; 339 340 /* Derelativise */ 341 if (p && p[0] != '/') { 342 abs_str = path_append(pwd, p); 343 xfree(p); 344 return(abs_str); 345 } else 346 return(p); 347 } 348 349 static int 350 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag, 351 int *rflag) 352 { 353 extern int opterr, optind, optopt, optreset; 354 int ch; 355 356 optind = optreset = 1; 357 opterr = 0; 358 359 *rflag = *pflag = 0; 360 while ((ch = getopt(argc, argv, "PpRr")) != -1) { 361 switch (ch) { 362 case 'p': 363 case 'P': 364 *pflag = 1; 365 break; 366 case 'r': 367 case 'R': 368 *rflag = 1; 369 break; 370 default: 371 error("%s: Invalid flag -%c", cmd, optopt); 372 return -1; 373 } 374 } 375 376 return optind; 377 } 378 379 static int 380 parse_ls_flags(char **argv, int argc, int *lflag) 381 { 382 extern int opterr, optind, optopt, optreset; 383 int ch; 384 385 optind = optreset = 1; 386 opterr = 0; 387 388 *lflag = LS_NAME_SORT; 389 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) { 390 switch (ch) { 391 case '1': 392 *lflag &= ~VIEW_FLAGS; 393 *lflag |= LS_SHORT_VIEW; 394 break; 395 case 'S': 396 *lflag &= ~SORT_FLAGS; 397 *lflag |= LS_SIZE_SORT; 398 break; 399 case 'a': 400 *lflag |= LS_SHOW_ALL; 401 break; 402 case 'f': 403 *lflag &= ~SORT_FLAGS; 404 break; 405 case 'h': 406 *lflag |= LS_SI_UNITS; 407 break; 408 case 'l': 409 *lflag &= ~LS_SHORT_VIEW; 410 *lflag |= LS_LONG_VIEW; 411 break; 412 case 'n': 413 *lflag &= ~LS_SHORT_VIEW; 414 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; 415 break; 416 case 'r': 417 *lflag |= LS_REVERSE_SORT; 418 break; 419 case 't': 420 *lflag &= ~SORT_FLAGS; 421 *lflag |= LS_TIME_SORT; 422 break; 423 default: 424 error("ls: Invalid flag -%c", optopt); 425 return -1; 426 } 427 } 428 429 return optind; 430 } 431 432 static int 433 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag) 434 { 435 extern int opterr, optind, optopt, optreset; 436 int ch; 437 438 optind = optreset = 1; 439 opterr = 0; 440 441 *hflag = *iflag = 0; 442 while ((ch = getopt(argc, argv, "hi")) != -1) { 443 switch (ch) { 444 case 'h': 445 *hflag = 1; 446 break; 447 case 'i': 448 *iflag = 1; 449 break; 450 default: 451 error("%s: Invalid flag -%c", cmd, optopt); 452 return -1; 453 } 454 } 455 456 return optind; 457 } 458 459 static int 460 is_dir(char *path) 461 { 462 struct stat sb; 463 464 /* XXX: report errors? */ 465 if (stat(path, &sb) == -1) 466 return(0); 467 468 return(S_ISDIR(sb.st_mode)); 469 } 470 471 static int 472 remote_is_dir(struct sftp_conn *conn, char *path) 473 { 474 Attrib *a; 475 476 /* XXX: report errors? */ 477 if ((a = do_stat(conn, path, 1)) == NULL) 478 return(0); 479 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) 480 return(0); 481 return(S_ISDIR(a->perm)); 482 } 483 484 /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ 485 static int 486 pathname_is_dir(char *pathname) 487 { 488 size_t l = strlen(pathname); 489 490 return l > 0 && pathname[l - 1] == '/'; 491 } 492 493 static int 494 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, 495 int pflag, int rflag) 496 { 497 char *abs_src = NULL; 498 char *abs_dst = NULL; 499 glob_t g; 500 char *filename, *tmp=NULL; 501 int i, err = 0; 502 503 abs_src = xstrdup(src); 504 abs_src = make_absolute(abs_src, pwd); 505 memset(&g, 0, sizeof(g)); 506 507 debug3("Looking up %s", abs_src); 508 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) { 509 error("File \"%s\" not found.", abs_src); 510 err = -1; 511 goto out; 512 } 513 514 /* 515 * If multiple matches then dst must be a directory or 516 * unspecified. 517 */ 518 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) { 519 error("Multiple source paths, but destination " 520 "\"%s\" is not a directory", dst); 521 err = -1; 522 goto out; 523 } 524 525 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 526 tmp = xstrdup(g.gl_pathv[i]); 527 if ((filename = basename(tmp)) == NULL) { 528 error("basename %s: %s", tmp, strerror(errno)); 529 xfree(tmp); 530 err = -1; 531 goto out; 532 } 533 534 if (g.gl_matchc == 1 && dst) { 535 if (is_dir(dst)) { 536 abs_dst = path_append(dst, filename); 537 } else { 538 abs_dst = xstrdup(dst); 539 } 540 } else if (dst) { 541 abs_dst = path_append(dst, filename); 542 } else { 543 abs_dst = xstrdup(filename); 544 } 545 xfree(tmp); 546 547 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 548 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 549 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, 550 pflag || global_pflag, 1) == -1) 551 err = -1; 552 } else { 553 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, 554 pflag || global_pflag) == -1) 555 err = -1; 556 } 557 xfree(abs_dst); 558 abs_dst = NULL; 559 } 560 561 out: 562 xfree(abs_src); 563 globfree(&g); 564 return(err); 565 } 566 567 static int 568 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, 569 int pflag, int rflag) 570 { 571 char *tmp_dst = NULL; 572 char *abs_dst = NULL; 573 char *tmp = NULL, *filename = NULL; 574 glob_t g; 575 int err = 0; 576 int i, dst_is_dir = 1; 577 struct stat sb; 578 579 if (dst) { 580 tmp_dst = xstrdup(dst); 581 tmp_dst = make_absolute(tmp_dst, pwd); 582 } 583 584 memset(&g, 0, sizeof(g)); 585 debug3("Looking up %s", src); 586 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) { 587 error("File \"%s\" not found.", src); 588 err = -1; 589 goto out; 590 } 591 592 /* If we aren't fetching to pwd then stash this status for later */ 593 if (tmp_dst != NULL) 594 dst_is_dir = remote_is_dir(conn, tmp_dst); 595 596 /* If multiple matches, dst may be directory or unspecified */ 597 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) { 598 error("Multiple paths match, but destination " 599 "\"%s\" is not a directory", tmp_dst); 600 err = -1; 601 goto out; 602 } 603 604 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 605 if (stat(g.gl_pathv[i], &sb) == -1) { 606 err = -1; 607 error("stat %s: %s", g.gl_pathv[i], strerror(errno)); 608 continue; 609 } 610 611 tmp = xstrdup(g.gl_pathv[i]); 612 if ((filename = basename(tmp)) == NULL) { 613 error("basename %s: %s", tmp, strerror(errno)); 614 xfree(tmp); 615 err = -1; 616 goto out; 617 } 618 619 if (g.gl_matchc == 1 && tmp_dst) { 620 /* If directory specified, append filename */ 621 if (dst_is_dir) 622 abs_dst = path_append(tmp_dst, filename); 623 else 624 abs_dst = xstrdup(tmp_dst); 625 } else if (tmp_dst) { 626 abs_dst = path_append(tmp_dst, filename); 627 } else { 628 abs_dst = make_absolute(xstrdup(filename), pwd); 629 } 630 xfree(tmp); 631 632 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 633 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 634 if (upload_dir(conn, g.gl_pathv[i], abs_dst, 635 pflag || global_pflag, 1) == -1) 636 err = -1; 637 } else { 638 if (do_upload(conn, g.gl_pathv[i], abs_dst, 639 pflag || global_pflag) == -1) 640 err = -1; 641 } 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|LS_SI_UNITS)) { 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 (lflag & LS_SI_UNITS)); 733 printf("%s\n", lname); 734 xfree(lname); 735 } else 736 printf("%s\n", d[n]->longname); 737 } else { 738 printf("%-*s", colspace, fname); 739 if (c >= columns) { 740 printf("\n"); 741 c = 1; 742 } else 743 c++; 744 } 745 746 xfree(fname); 747 } 748 749 if (!(lflag & LS_LONG_VIEW) && (c != 1)) 750 printf("\n"); 751 752 free_sftp_dirents(d); 753 return (0); 754 } 755 756 /* sftp ls.1 replacement which handles path globs */ 757 static int 758 do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, 759 int lflag) 760 { 761 glob_t g; 762 u_int i, c = 1, colspace = 0, columns = 1; 763 Attrib *a = NULL; 764 765 memset(&g, 0, sizeof(g)); 766 767 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, 768 NULL, &g) || (g.gl_pathc && !g.gl_matchc)) { 769 if (g.gl_pathc) 770 globfree(&g); 771 error("Can't ls: \"%s\" not found", path); 772 return (-1); 773 } 774 775 if (interrupted) 776 goto out; 777 778 /* 779 * If the glob returns a single match and it is a directory, 780 * then just list its contents. 781 */ 782 if (g.gl_matchc == 1) { 783 if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) { 784 globfree(&g); 785 return (-1); 786 } 787 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 788 S_ISDIR(a->perm)) { 789 int err; 790 791 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); 792 globfree(&g); 793 return (err); 794 } 795 } 796 797 if (!(lflag & LS_SHORT_VIEW)) { 798 u_int m = 0, width = 80; 799 struct winsize ws; 800 801 /* Count entries for sort and find longest filename */ 802 for (i = 0; g.gl_pathv[i]; i++) 803 m = MAX(m, strlen(g.gl_pathv[i])); 804 805 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 806 width = ws.ws_col; 807 808 columns = width / (m + 2); 809 columns = MAX(columns, 1); 810 colspace = width / columns; 811 } 812 813 for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) { 814 char *fname; 815 816 fname = path_strip(g.gl_pathv[i], strip_path); 817 818 if (lflag & LS_LONG_VIEW) { 819 char *lname; 820 struct stat sb; 821 822 /* 823 * XXX: this is slow - 1 roundtrip per path 824 * A solution to this is to fork glob() and 825 * build a sftp specific version which keeps the 826 * attribs (which currently get thrown away) 827 * that the server returns as well as the filenames. 828 */ 829 memset(&sb, 0, sizeof(sb)); 830 if (a == NULL) 831 a = do_lstat(conn, g.gl_pathv[i], 1); 832 if (a != NULL) 833 attrib_to_stat(a, &sb); 834 lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS)); 835 printf("%s\n", lname); 836 xfree(lname); 837 } else { 838 printf("%-*s", colspace, fname); 839 if (c >= columns) { 840 printf("\n"); 841 c = 1; 842 } else 843 c++; 844 } 845 xfree(fname); 846 } 847 848 if (!(lflag & LS_LONG_VIEW) && (c != 1)) 849 printf("\n"); 850 851 out: 852 if (g.gl_pathc) 853 globfree(&g); 854 855 return (0); 856 } 857 858 static int 859 do_df(struct sftp_conn *conn, char *path, int hflag, int iflag) 860 { 861 struct sftp_statvfs st; 862 char s_used[FMT_SCALED_STRSIZE]; 863 char s_avail[FMT_SCALED_STRSIZE]; 864 char s_root[FMT_SCALED_STRSIZE]; 865 char s_total[FMT_SCALED_STRSIZE]; 866 unsigned long long ffree; 867 868 if (do_statvfs(conn, path, &st, 1) == -1) 869 return -1; 870 if (iflag) { 871 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0; 872 printf(" Inodes Used Avail " 873 "(root) %%Capacity\n"); 874 printf("%11llu %11llu %11llu %11llu %3llu%%\n", 875 (unsigned long long)st.f_files, 876 (unsigned long long)(st.f_files - st.f_ffree), 877 (unsigned long long)st.f_favail, 878 (unsigned long long)st.f_ffree, ffree); 879 } else if (hflag) { 880 strlcpy(s_used, "error", sizeof(s_used)); 881 strlcpy(s_avail, "error", sizeof(s_avail)); 882 strlcpy(s_root, "error", sizeof(s_root)); 883 strlcpy(s_total, "error", sizeof(s_total)); 884 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used); 885 fmt_scaled(st.f_bavail * st.f_frsize, s_avail); 886 fmt_scaled(st.f_bfree * st.f_frsize, s_root); 887 fmt_scaled(st.f_blocks * st.f_frsize, s_total); 888 printf(" Size Used Avail (root) %%Capacity\n"); 889 printf("%7sB %7sB %7sB %7sB %3llu%%\n", 890 s_total, s_used, s_avail, s_root, 891 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / 892 st.f_blocks)); 893 } else { 894 printf(" Size Used Avail " 895 "(root) %%Capacity\n"); 896 printf("%12llu %12llu %12llu %12llu %3llu%%\n", 897 (unsigned long long)(st.f_frsize * st.f_blocks / 1024), 898 (unsigned long long)(st.f_frsize * 899 (st.f_blocks - st.f_bfree) / 1024), 900 (unsigned long long)(st.f_frsize * st.f_bavail / 1024), 901 (unsigned long long)(st.f_frsize * st.f_bfree / 1024), 902 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / 903 st.f_blocks)); 904 } 905 return 0; 906 } 907 908 /* 909 * Undo escaping of glob sequences in place. Used to undo extra escaping 910 * applied in makeargv() when the string is destined for a function that 911 * does not glob it. 912 */ 913 static void 914 undo_glob_escape(char *s) 915 { 916 size_t i, j; 917 918 for (i = j = 0;;) { 919 if (s[i] == '\0') { 920 s[j] = '\0'; 921 return; 922 } 923 if (s[i] != '\\') { 924 s[j++] = s[i++]; 925 continue; 926 } 927 /* s[i] == '\\' */ 928 ++i; 929 switch (s[i]) { 930 case '?': 931 case '[': 932 case '*': 933 case '\\': 934 s[j++] = s[i++]; 935 break; 936 case '\0': 937 s[j++] = '\\'; 938 s[j] = '\0'; 939 return; 940 default: 941 s[j++] = '\\'; 942 s[j++] = s[i++]; 943 break; 944 } 945 } 946 } 947 948 /* 949 * Split a string into an argument vector using sh(1)-style quoting, 950 * comment and escaping rules, but with some tweaks to handle glob(3) 951 * wildcards. 952 * The "sloppy" flag allows for recovery from missing terminating quote, for 953 * use in parsing incomplete commandlines during tab autocompletion. 954 * 955 * Returns NULL on error or a NULL-terminated array of arguments. 956 * 957 * If "lastquote" is not NULL, the quoting character used for the last 958 * argument is placed in *lastquote ("\0", "'" or "\""). 959 * 960 * If "terminated" is not NULL, *terminated will be set to 1 when the 961 * last argument's quote has been properly terminated or 0 otherwise. 962 * This parameter is only of use if "sloppy" is set. 963 */ 964 #define MAXARGS 128 965 #define MAXARGLEN 8192 966 static char ** 967 makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, 968 u_int *terminated) 969 { 970 int argc, quot; 971 size_t i, j; 972 static char argvs[MAXARGLEN]; 973 static char *argv[MAXARGS + 1]; 974 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q; 975 976 *argcp = argc = 0; 977 if (strlen(arg) > sizeof(argvs) - 1) { 978 args_too_longs: 979 error("string too long"); 980 return NULL; 981 } 982 if (terminated != NULL) 983 *terminated = 1; 984 if (lastquote != NULL) 985 *lastquote = '\0'; 986 state = MA_START; 987 i = j = 0; 988 for (;;) { 989 if (isspace(arg[i])) { 990 if (state == MA_UNQUOTED) { 991 /* Terminate current argument */ 992 argvs[j++] = '\0'; 993 argc++; 994 state = MA_START; 995 } else if (state != MA_START) 996 argvs[j++] = arg[i]; 997 } else if (arg[i] == '"' || arg[i] == '\'') { 998 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE; 999 if (state == MA_START) { 1000 argv[argc] = argvs + j; 1001 state = q; 1002 if (lastquote != NULL) 1003 *lastquote = arg[i]; 1004 } else if (state == MA_UNQUOTED) 1005 state = q; 1006 else if (state == q) 1007 state = MA_UNQUOTED; 1008 else 1009 argvs[j++] = arg[i]; 1010 } else if (arg[i] == '\\') { 1011 if (state == MA_SQUOTE || state == MA_DQUOTE) { 1012 quot = state == MA_SQUOTE ? '\'' : '"'; 1013 /* Unescape quote we are in */ 1014 /* XXX support \n and friends? */ 1015 if (arg[i + 1] == quot) { 1016 i++; 1017 argvs[j++] = arg[i]; 1018 } else if (arg[i + 1] == '?' || 1019 arg[i + 1] == '[' || arg[i + 1] == '*') { 1020 /* 1021 * Special case for sftp: append 1022 * double-escaped glob sequence - 1023 * glob will undo one level of 1024 * escaping. NB. string can grow here. 1025 */ 1026 if (j >= sizeof(argvs) - 5) 1027 goto args_too_longs; 1028 argvs[j++] = '\\'; 1029 argvs[j++] = arg[i++]; 1030 argvs[j++] = '\\'; 1031 argvs[j++] = arg[i]; 1032 } else { 1033 argvs[j++] = arg[i++]; 1034 argvs[j++] = arg[i]; 1035 } 1036 } else { 1037 if (state == MA_START) { 1038 argv[argc] = argvs + j; 1039 state = MA_UNQUOTED; 1040 if (lastquote != NULL) 1041 *lastquote = '\0'; 1042 } 1043 if (arg[i + 1] == '?' || arg[i + 1] == '[' || 1044 arg[i + 1] == '*' || arg[i + 1] == '\\') { 1045 /* 1046 * Special case for sftp: append 1047 * escaped glob sequence - 1048 * glob will undo one level of 1049 * escaping. 1050 */ 1051 argvs[j++] = arg[i++]; 1052 argvs[j++] = arg[i]; 1053 } else { 1054 /* Unescape everything */ 1055 /* XXX support \n and friends? */ 1056 i++; 1057 argvs[j++] = arg[i]; 1058 } 1059 } 1060 } else if (arg[i] == '#') { 1061 if (state == MA_SQUOTE || state == MA_DQUOTE) 1062 argvs[j++] = arg[i]; 1063 else 1064 goto string_done; 1065 } else if (arg[i] == '\0') { 1066 if (state == MA_SQUOTE || state == MA_DQUOTE) { 1067 if (sloppy) { 1068 state = MA_UNQUOTED; 1069 if (terminated != NULL) 1070 *terminated = 0; 1071 goto string_done; 1072 } 1073 error("Unterminated quoted argument"); 1074 return NULL; 1075 } 1076 string_done: 1077 if (state == MA_UNQUOTED) { 1078 argvs[j++] = '\0'; 1079 argc++; 1080 } 1081 break; 1082 } else { 1083 if (state == MA_START) { 1084 argv[argc] = argvs + j; 1085 state = MA_UNQUOTED; 1086 if (lastquote != NULL) 1087 *lastquote = '\0'; 1088 } 1089 if ((state == MA_SQUOTE || state == MA_DQUOTE) && 1090 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { 1091 /* 1092 * Special case for sftp: escape quoted 1093 * glob(3) wildcards. NB. string can grow 1094 * here. 1095 */ 1096 if (j >= sizeof(argvs) - 3) 1097 goto args_too_longs; 1098 argvs[j++] = '\\'; 1099 argvs[j++] = arg[i]; 1100 } else 1101 argvs[j++] = arg[i]; 1102 } 1103 i++; 1104 } 1105 *argcp = argc; 1106 return argv; 1107 } 1108 1109 static int 1110 parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, 1111 int *hflag, unsigned long *n_arg, char **path1, char **path2) 1112 { 1113 const char *cmd, *cp = *cpp; 1114 char *cp2, **argv; 1115 int base = 0; 1116 long l; 1117 int i, cmdnum, optidx, argc; 1118 1119 /* Skip leading whitespace */ 1120 cp = cp + strspn(cp, WHITESPACE); 1121 1122 /* Check for leading '-' (disable error processing) */ 1123 *iflag = 0; 1124 if (*cp == '-') { 1125 *iflag = 1; 1126 cp++; 1127 cp = cp + strspn(cp, WHITESPACE); 1128 } 1129 1130 /* Ignore blank lines and lines which begin with comment '#' char */ 1131 if (*cp == '\0' || *cp == '#') 1132 return (0); 1133 1134 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) 1135 return -1; 1136 1137 /* Figure out which command we have */ 1138 for (i = 0; cmds[i].c != NULL; i++) { 1139 if (strcasecmp(cmds[i].c, argv[0]) == 0) 1140 break; 1141 } 1142 cmdnum = cmds[i].n; 1143 cmd = cmds[i].c; 1144 1145 /* Special case */ 1146 if (*cp == '!') { 1147 cp++; 1148 cmdnum = I_SHELL; 1149 } else if (cmdnum == -1) { 1150 error("Invalid command."); 1151 return -1; 1152 } 1153 1154 /* Get arguments and parse flags */ 1155 *lflag = *pflag = *rflag = *hflag = *n_arg = 0; 1156 *path1 = *path2 = NULL; 1157 optidx = 1; 1158 switch (cmdnum) { 1159 case I_GET: 1160 case I_PUT: 1161 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1) 1162 return -1; 1163 /* Get first pathname (mandatory) */ 1164 if (argc - optidx < 1) { 1165 error("You must specify at least one path after a " 1166 "%s command.", cmd); 1167 return -1; 1168 } 1169 *path1 = xstrdup(argv[optidx]); 1170 /* Get second pathname (optional) */ 1171 if (argc - optidx > 1) { 1172 *path2 = xstrdup(argv[optidx + 1]); 1173 /* Destination is not globbed */ 1174 undo_glob_escape(*path2); 1175 } 1176 break; 1177 case I_RENAME: 1178 case I_SYMLINK: 1179 if (argc - optidx < 2) { 1180 error("You must specify two paths after a %s " 1181 "command.", cmd); 1182 return -1; 1183 } 1184 *path1 = xstrdup(argv[optidx]); 1185 *path2 = xstrdup(argv[optidx + 1]); 1186 /* Paths are not globbed */ 1187 undo_glob_escape(*path1); 1188 undo_glob_escape(*path2); 1189 break; 1190 case I_RM: 1191 case I_MKDIR: 1192 case I_RMDIR: 1193 case I_CHDIR: 1194 case I_LCHDIR: 1195 case I_LMKDIR: 1196 /* Get pathname (mandatory) */ 1197 if (argc - optidx < 1) { 1198 error("You must specify a path after a %s command.", 1199 cmd); 1200 return -1; 1201 } 1202 *path1 = xstrdup(argv[optidx]); 1203 /* Only "rm" globs */ 1204 if (cmdnum != I_RM) 1205 undo_glob_escape(*path1); 1206 break; 1207 case I_DF: 1208 if ((optidx = parse_df_flags(cmd, argv, argc, hflag, 1209 iflag)) == -1) 1210 return -1; 1211 /* Default to current directory if no path specified */ 1212 if (argc - optidx < 1) 1213 *path1 = NULL; 1214 else { 1215 *path1 = xstrdup(argv[optidx]); 1216 undo_glob_escape(*path1); 1217 } 1218 break; 1219 case I_LS: 1220 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1) 1221 return(-1); 1222 /* Path is optional */ 1223 if (argc - optidx > 0) 1224 *path1 = xstrdup(argv[optidx]); 1225 break; 1226 case I_LLS: 1227 /* Skip ls command and following whitespace */ 1228 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE); 1229 case I_SHELL: 1230 /* Uses the rest of the line */ 1231 break; 1232 case I_LUMASK: 1233 case I_CHMOD: 1234 base = 8; 1235 case I_CHOWN: 1236 case I_CHGRP: 1237 /* Get numeric arg (mandatory) */ 1238 if (argc - optidx < 1) 1239 goto need_num_arg; 1240 errno = 0; 1241 l = strtol(argv[optidx], &cp2, base); 1242 if (cp2 == argv[optidx] || *cp2 != '\0' || 1243 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) || 1244 l < 0) { 1245 need_num_arg: 1246 error("You must supply a numeric argument " 1247 "to the %s command.", cmd); 1248 return -1; 1249 } 1250 *n_arg = l; 1251 if (cmdnum == I_LUMASK) 1252 break; 1253 /* Get pathname (mandatory) */ 1254 if (argc - optidx < 2) { 1255 error("You must specify a path after a %s command.", 1256 cmd); 1257 return -1; 1258 } 1259 *path1 = xstrdup(argv[optidx + 1]); 1260 break; 1261 case I_QUIT: 1262 case I_PWD: 1263 case I_LPWD: 1264 case I_HELP: 1265 case I_VERSION: 1266 case I_PROGRESS: 1267 break; 1268 default: 1269 fatal("Command not implemented"); 1270 } 1271 1272 *cpp = cp; 1273 return(cmdnum); 1274 } 1275 1276 static int 1277 parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 1278 int err_abort) 1279 { 1280 char *path1, *path2, *tmp; 1281 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; 1282 unsigned long n_arg = 0; 1283 Attrib a, *aa; 1284 char path_buf[MAXPATHLEN]; 1285 int err = 0; 1286 glob_t g; 1287 1288 path1 = path2 = NULL; 1289 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg, 1290 &path1, &path2); 1291 1292 if (iflag != 0) 1293 err_abort = 0; 1294 1295 memset(&g, 0, sizeof(g)); 1296 1297 /* Perform command */ 1298 switch (cmdnum) { 1299 case 0: 1300 /* Blank line */ 1301 break; 1302 case -1: 1303 /* Unrecognized command */ 1304 err = -1; 1305 break; 1306 case I_GET: 1307 err = process_get(conn, path1, path2, *pwd, pflag, rflag); 1308 break; 1309 case I_PUT: 1310 err = process_put(conn, path1, path2, *pwd, pflag, rflag); 1311 break; 1312 case I_RENAME: 1313 path1 = make_absolute(path1, *pwd); 1314 path2 = make_absolute(path2, *pwd); 1315 err = do_rename(conn, path1, path2); 1316 break; 1317 case I_SYMLINK: 1318 path2 = make_absolute(path2, *pwd); 1319 err = do_symlink(conn, path1, path2); 1320 break; 1321 case I_RM: 1322 path1 = make_absolute(path1, *pwd); 1323 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1324 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1325 printf("Removing %s\n", g.gl_pathv[i]); 1326 err = do_rm(conn, g.gl_pathv[i]); 1327 if (err != 0 && err_abort) 1328 break; 1329 } 1330 break; 1331 case I_MKDIR: 1332 path1 = make_absolute(path1, *pwd); 1333 attrib_clear(&a); 1334 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1335 a.perm = 0777; 1336 err = do_mkdir(conn, path1, &a, 1); 1337 break; 1338 case I_RMDIR: 1339 path1 = make_absolute(path1, *pwd); 1340 err = do_rmdir(conn, path1); 1341 break; 1342 case I_CHDIR: 1343 path1 = make_absolute(path1, *pwd); 1344 if ((tmp = do_realpath(conn, path1)) == NULL) { 1345 err = 1; 1346 break; 1347 } 1348 if ((aa = do_stat(conn, tmp, 0)) == NULL) { 1349 xfree(tmp); 1350 err = 1; 1351 break; 1352 } 1353 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { 1354 error("Can't change directory: Can't check target"); 1355 xfree(tmp); 1356 err = 1; 1357 break; 1358 } 1359 if (!S_ISDIR(aa->perm)) { 1360 error("Can't change directory: \"%s\" is not " 1361 "a directory", tmp); 1362 xfree(tmp); 1363 err = 1; 1364 break; 1365 } 1366 xfree(*pwd); 1367 *pwd = tmp; 1368 break; 1369 case I_LS: 1370 if (!path1) { 1371 do_ls_dir(conn, *pwd, *pwd, lflag); 1372 break; 1373 } 1374 1375 /* Strip pwd off beginning of non-absolute paths */ 1376 tmp = NULL; 1377 if (*path1 != '/') 1378 tmp = *pwd; 1379 1380 path1 = make_absolute(path1, *pwd); 1381 err = do_globbed_ls(conn, path1, tmp, lflag); 1382 break; 1383 case I_DF: 1384 /* Default to current directory if no path specified */ 1385 if (path1 == NULL) 1386 path1 = xstrdup(*pwd); 1387 path1 = make_absolute(path1, *pwd); 1388 err = do_df(conn, path1, hflag, iflag); 1389 break; 1390 case I_LCHDIR: 1391 if (chdir(path1) == -1) { 1392 error("Couldn't change local directory to " 1393 "\"%s\": %s", path1, strerror(errno)); 1394 err = 1; 1395 } 1396 break; 1397 case I_LMKDIR: 1398 if (mkdir(path1, 0777) == -1) { 1399 error("Couldn't create local directory " 1400 "\"%s\": %s", path1, strerror(errno)); 1401 err = 1; 1402 } 1403 break; 1404 case I_LLS: 1405 local_do_ls(cmd); 1406 break; 1407 case I_SHELL: 1408 local_do_shell(cmd); 1409 break; 1410 case I_LUMASK: 1411 umask(n_arg); 1412 printf("Local umask: %03lo\n", n_arg); 1413 break; 1414 case I_CHMOD: 1415 path1 = make_absolute(path1, *pwd); 1416 attrib_clear(&a); 1417 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1418 a.perm = n_arg; 1419 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1420 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1421 printf("Changing mode on %s\n", g.gl_pathv[i]); 1422 err = do_setstat(conn, g.gl_pathv[i], &a); 1423 if (err != 0 && err_abort) 1424 break; 1425 } 1426 break; 1427 case I_CHOWN: 1428 case I_CHGRP: 1429 path1 = make_absolute(path1, *pwd); 1430 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1431 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1432 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1433 if (err_abort) { 1434 err = -1; 1435 break; 1436 } else 1437 continue; 1438 } 1439 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1440 error("Can't get current ownership of " 1441 "remote file \"%s\"", g.gl_pathv[i]); 1442 if (err_abort) { 1443 err = -1; 1444 break; 1445 } else 1446 continue; 1447 } 1448 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1449 if (cmdnum == I_CHOWN) { 1450 printf("Changing owner on %s\n", g.gl_pathv[i]); 1451 aa->uid = n_arg; 1452 } else { 1453 printf("Changing group on %s\n", g.gl_pathv[i]); 1454 aa->gid = n_arg; 1455 } 1456 err = do_setstat(conn, g.gl_pathv[i], aa); 1457 if (err != 0 && err_abort) 1458 break; 1459 } 1460 break; 1461 case I_PWD: 1462 printf("Remote working directory: %s\n", *pwd); 1463 break; 1464 case I_LPWD: 1465 if (!getcwd(path_buf, sizeof(path_buf))) { 1466 error("Couldn't get local cwd: %s", strerror(errno)); 1467 err = -1; 1468 break; 1469 } 1470 printf("Local working directory: %s\n", path_buf); 1471 break; 1472 case I_QUIT: 1473 /* Processed below */ 1474 break; 1475 case I_HELP: 1476 help(); 1477 break; 1478 case I_VERSION: 1479 printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1480 break; 1481 case I_PROGRESS: 1482 showprogress = !showprogress; 1483 if (showprogress) 1484 printf("Progress meter enabled\n"); 1485 else 1486 printf("Progress meter disabled\n"); 1487 break; 1488 default: 1489 fatal("%d is not implemented", cmdnum); 1490 } 1491 1492 if (g.gl_pathc) 1493 globfree(&g); 1494 if (path1) 1495 xfree(path1); 1496 if (path2) 1497 xfree(path2); 1498 1499 /* If an unignored error occurs in batch mode we should abort. */ 1500 if (err_abort && err != 0) 1501 return (-1); 1502 else if (cmdnum == I_QUIT) 1503 return (1); 1504 1505 return (0); 1506 } 1507 1508 #ifdef USE_LIBEDIT 1509 static char * 1510 prompt(EditLine *el) 1511 { 1512 return ("sftp> "); 1513 } 1514 1515 /* Display entries in 'list' after skipping the first 'len' chars */ 1516 static void 1517 complete_display(char **list, u_int len) 1518 { 1519 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen; 1520 struct winsize ws; 1521 char *tmp; 1522 1523 /* Count entries for sort and find longest */ 1524 for (y = 0; list[y]; y++) 1525 m = MAX(m, strlen(list[y])); 1526 1527 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 1528 width = ws.ws_col; 1529 1530 m = m > len ? m - len : 0; 1531 columns = width / (m + 2); 1532 columns = MAX(columns, 1); 1533 colspace = width / columns; 1534 colspace = MIN(colspace, width); 1535 1536 printf("\n"); 1537 m = 1; 1538 for (y = 0; list[y]; y++) { 1539 llen = strlen(list[y]); 1540 tmp = llen > len ? list[y] + len : ""; 1541 printf("%-*s", colspace, tmp); 1542 if (m >= columns) { 1543 printf("\n"); 1544 m = 1; 1545 } else 1546 m++; 1547 } 1548 printf("\n"); 1549 } 1550 1551 /* 1552 * Given a "list" of words that begin with a common prefix of "word", 1553 * attempt to find an autocompletion to extends "word" by the next 1554 * characters common to all entries in "list". 1555 */ 1556 static char * 1557 complete_ambiguous(const char *word, char **list, size_t count) 1558 { 1559 if (word == NULL) 1560 return NULL; 1561 1562 if (count > 0) { 1563 u_int y, matchlen = strlen(list[0]); 1564 1565 /* Find length of common stem */ 1566 for (y = 1; list[y]; y++) { 1567 u_int x; 1568 1569 for (x = 0; x < matchlen; x++) 1570 if (list[0][x] != list[y][x]) 1571 break; 1572 1573 matchlen = x; 1574 } 1575 1576 if (matchlen > strlen(word)) { 1577 char *tmp = xstrdup(list[0]); 1578 1579 tmp[matchlen] = '\0'; 1580 return tmp; 1581 } 1582 } 1583 1584 return xstrdup(word); 1585 } 1586 1587 /* Autocomplete a sftp command */ 1588 static int 1589 complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, 1590 int terminated) 1591 { 1592 u_int y, count = 0, cmdlen, tmplen; 1593 char *tmp, **list, argterm[3]; 1594 const LineInfo *lf; 1595 1596 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *)); 1597 1598 /* No command specified: display all available commands */ 1599 if (cmd == NULL) { 1600 for (y = 0; cmds[y].c; y++) 1601 list[count++] = xstrdup(cmds[y].c); 1602 1603 list[count] = NULL; 1604 complete_display(list, 0); 1605 1606 for (y = 0; list[y] != NULL; y++) 1607 xfree(list[y]); 1608 xfree(list); 1609 return count; 1610 } 1611 1612 /* Prepare subset of commands that start with "cmd" */ 1613 cmdlen = strlen(cmd); 1614 for (y = 0; cmds[y].c; y++) { 1615 if (!strncasecmp(cmd, cmds[y].c, cmdlen)) 1616 list[count++] = xstrdup(cmds[y].c); 1617 } 1618 list[count] = NULL; 1619 1620 if (count == 0) 1621 return 0; 1622 1623 /* Complete ambigious command */ 1624 tmp = complete_ambiguous(cmd, list, count); 1625 if (count > 1) 1626 complete_display(list, 0); 1627 1628 for (y = 0; list[y]; y++) 1629 xfree(list[y]); 1630 xfree(list); 1631 1632 if (tmp != NULL) { 1633 tmplen = strlen(tmp); 1634 cmdlen = strlen(cmd); 1635 /* If cmd may be extended then do so */ 1636 if (tmplen > cmdlen) 1637 if (el_insertstr(el, tmp + cmdlen) == -1) 1638 fatal("el_insertstr failed."); 1639 lf = el_line(el); 1640 /* Terminate argument cleanly */ 1641 if (count == 1) { 1642 y = 0; 1643 if (!terminated) 1644 argterm[y++] = quote; 1645 if (lastarg || *(lf->cursor) != ' ') 1646 argterm[y++] = ' '; 1647 argterm[y] = '\0'; 1648 if (y > 0 && el_insertstr(el, argterm) == -1) 1649 fatal("el_insertstr failed."); 1650 } 1651 xfree(tmp); 1652 } 1653 1654 return count; 1655 } 1656 1657 /* 1658 * Determine whether a particular sftp command's arguments (if any) 1659 * represent local or remote files. 1660 */ 1661 static int 1662 complete_is_remote(char *cmd) { 1663 int i; 1664 1665 if (cmd == NULL) 1666 return -1; 1667 1668 for (i = 0; cmds[i].c; i++) { 1669 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) 1670 return cmds[i].t; 1671 } 1672 1673 return -1; 1674 } 1675 1676 /* Autocomplete a filename "file" */ 1677 static int 1678 complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, 1679 char *file, int remote, int lastarg, char quote, int terminated) 1680 { 1681 glob_t g; 1682 char *tmp, *tmp2, ins[3]; 1683 u_int i, hadglob, pwdlen, len, tmplen, filelen; 1684 const LineInfo *lf; 1685 1686 /* Glob from "file" location */ 1687 if (file == NULL) 1688 tmp = xstrdup("*"); 1689 else 1690 xasprintf(&tmp, "%s*", file); 1691 1692 memset(&g, 0, sizeof(g)); 1693 if (remote != LOCAL) { 1694 tmp = make_absolute(tmp, remote_path); 1695 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); 1696 } else 1697 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); 1698 1699 /* Determine length of pwd so we can trim completion display */ 1700 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) { 1701 /* Terminate counting on first unescaped glob metacharacter */ 1702 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') { 1703 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0') 1704 hadglob = 1; 1705 break; 1706 } 1707 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0') 1708 tmplen++; 1709 if (tmp[tmplen] == '/') 1710 pwdlen = tmplen + 1; /* track last seen '/' */ 1711 } 1712 xfree(tmp); 1713 1714 if (g.gl_matchc == 0) 1715 goto out; 1716 1717 if (g.gl_matchc > 1) 1718 complete_display(g.gl_pathv, pwdlen); 1719 1720 tmp = NULL; 1721 /* Don't try to extend globs */ 1722 if (file == NULL || hadglob) 1723 goto out; 1724 1725 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); 1726 tmp = path_strip(tmp2, remote_path); 1727 xfree(tmp2); 1728 1729 if (tmp == NULL) 1730 goto out; 1731 1732 tmplen = strlen(tmp); 1733 filelen = strlen(file); 1734 1735 if (tmplen > filelen) { 1736 tmp2 = tmp + filelen; 1737 len = strlen(tmp2); 1738 /* quote argument on way out */ 1739 for (i = 0; i < len; i++) { 1740 ins[0] = '\\'; 1741 ins[1] = tmp2[i]; 1742 ins[2] = '\0'; 1743 switch (tmp2[i]) { 1744 case '\'': 1745 case '"': 1746 case '\\': 1747 case '\t': 1748 case ' ': 1749 if (quote == '\0' || tmp2[i] == quote) { 1750 if (el_insertstr(el, ins) == -1) 1751 fatal("el_insertstr " 1752 "failed."); 1753 break; 1754 } 1755 /* FALLTHROUGH */ 1756 default: 1757 if (el_insertstr(el, ins + 1) == -1) 1758 fatal("el_insertstr failed."); 1759 break; 1760 } 1761 } 1762 } 1763 1764 lf = el_line(el); 1765 if (g.gl_matchc == 1) { 1766 i = 0; 1767 if (!terminated) 1768 ins[i++] = quote; 1769 if (*(lf->cursor - 1) != '/' && 1770 (lastarg || *(lf->cursor) != ' ')) 1771 ins[i++] = ' '; 1772 ins[i] = '\0'; 1773 if (i > 0 && el_insertstr(el, ins) == -1) 1774 fatal("el_insertstr failed."); 1775 } 1776 xfree(tmp); 1777 1778 out: 1779 globfree(&g); 1780 return g.gl_matchc; 1781 } 1782 1783 /* tab-completion hook function, called via libedit */ 1784 static unsigned char 1785 complete(EditLine *el, int ch) 1786 { 1787 char **argv, *line, quote; 1788 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR; 1789 const LineInfo *lf; 1790 struct complete_ctx *complete_ctx; 1791 1792 lf = el_line(el); 1793 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0) 1794 fatal("%s: el_get failed", __func__); 1795 1796 /* Figure out which argument the cursor points to */ 1797 cursor = lf->cursor - lf->buffer; 1798 line = (char *)xmalloc(cursor + 1); 1799 memcpy(line, lf->buffer, cursor); 1800 line[cursor] = '\0'; 1801 argv = makeargv(line, &carg, 1, "e, &terminated); 1802 xfree(line); 1803 1804 /* Get all the arguments on the line */ 1805 len = lf->lastchar - lf->buffer; 1806 line = (char *)xmalloc(len + 1); 1807 memcpy(line, lf->buffer, len); 1808 line[len] = '\0'; 1809 argv = makeargv(line, &argc, 1, NULL, NULL); 1810 1811 /* Ensure cursor is at EOL or a argument boundary */ 1812 if (line[cursor] != ' ' && line[cursor] != '\0' && 1813 line[cursor] != '\n') { 1814 xfree(line); 1815 return ret; 1816 } 1817 1818 if (carg == 0) { 1819 /* Show all available commands */ 1820 complete_cmd_parse(el, NULL, argc == carg, '\0', 1); 1821 ret = CC_REDISPLAY; 1822 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') { 1823 /* Handle the command parsing */ 1824 if (complete_cmd_parse(el, argv[0], argc == carg, 1825 quote, terminated) != 0) 1826 ret = CC_REDISPLAY; 1827 } else if (carg >= 1) { 1828 /* Handle file parsing */ 1829 int remote = complete_is_remote(argv[0]); 1830 char *filematch = NULL; 1831 1832 if (carg > 1 && line[cursor-1] != ' ') 1833 filematch = argv[carg - 1]; 1834 1835 if (remote != 0 && 1836 complete_match(el, complete_ctx->conn, 1837 *complete_ctx->remote_pathp, filematch, 1838 remote, carg == argc, quote, terminated) != 0) 1839 ret = CC_REDISPLAY; 1840 } 1841 1842 xfree(line); 1843 return ret; 1844 } 1845 #endif /* USE_LIBEDIT */ 1846 1847 int 1848 interactive_loop(struct sftp_conn *conn, char *file1, char *file2) 1849 { 1850 char *remote_path; 1851 char *dir = NULL; 1852 char cmd[2048]; 1853 int err, interactive; 1854 EditLine *el = NULL; 1855 #ifdef USE_LIBEDIT 1856 History *hl = NULL; 1857 HistEvent hev; 1858 extern char *__progname; 1859 struct complete_ctx complete_ctx; 1860 1861 if (!batchmode && isatty(STDIN_FILENO)) { 1862 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) 1863 fatal("Couldn't initialise editline"); 1864 if ((hl = history_init()) == NULL) 1865 fatal("Couldn't initialise editline history"); 1866 history(hl, &hev, H_SETSIZE, 100); 1867 el_set(el, EL_HIST, history, hl); 1868 1869 el_set(el, EL_PROMPT, prompt); 1870 el_set(el, EL_EDITOR, "emacs"); 1871 el_set(el, EL_TERMINAL, NULL); 1872 el_set(el, EL_SIGNAL, 1); 1873 el_source(el, NULL); 1874 1875 /* Tab Completion */ 1876 el_set(el, EL_ADDFN, "ftp-complete", 1877 "Context senstive argument completion", complete); 1878 complete_ctx.conn = conn; 1879 complete_ctx.remote_pathp = &remote_path; 1880 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); 1881 el_set(el, EL_BIND, "^I", "ftp-complete", NULL); 1882 } 1883 #endif /* USE_LIBEDIT */ 1884 1885 remote_path = do_realpath(conn, "."); 1886 if (remote_path == NULL) 1887 fatal("Need cwd"); 1888 1889 if (file1 != NULL) { 1890 dir = xstrdup(file1); 1891 dir = make_absolute(dir, remote_path); 1892 1893 if (remote_is_dir(conn, dir) && file2 == NULL) { 1894 printf("Changing to: %s\n", dir); 1895 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 1896 if (parse_dispatch_command(conn, cmd, 1897 &remote_path, 1) != 0) { 1898 xfree(dir); 1899 xfree(remote_path); 1900 xfree(conn); 1901 return (-1); 1902 } 1903 } else { 1904 if (file2 == NULL) 1905 snprintf(cmd, sizeof cmd, "get %s", dir); 1906 else 1907 snprintf(cmd, sizeof cmd, "get %s %s", dir, 1908 file2); 1909 1910 err = parse_dispatch_command(conn, cmd, 1911 &remote_path, 1); 1912 xfree(dir); 1913 xfree(remote_path); 1914 xfree(conn); 1915 return (err); 1916 } 1917 xfree(dir); 1918 } 1919 1920 #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF) 1921 setvbuf(stdout, NULL, _IOLBF, 0); 1922 setvbuf(infile, NULL, _IOLBF, 0); 1923 #else 1924 setlinebuf(stdout); 1925 setlinebuf(infile); 1926 #endif 1927 1928 interactive = !batchmode && isatty(STDIN_FILENO); 1929 err = 0; 1930 for (;;) { 1931 char *cp; 1932 1933 signal(SIGINT, SIG_IGN); 1934 1935 if (el == NULL) { 1936 if (interactive) 1937 printf("sftp> "); 1938 if (fgets(cmd, sizeof(cmd), infile) == NULL) { 1939 if (interactive) 1940 printf("\n"); 1941 break; 1942 } 1943 if (!interactive) { /* Echo command */ 1944 printf("sftp> %s", cmd); 1945 if (strlen(cmd) > 0 && 1946 cmd[strlen(cmd) - 1] != '\n') 1947 printf("\n"); 1948 } 1949 } else { 1950 #ifdef USE_LIBEDIT 1951 const char *line; 1952 int count = 0; 1953 1954 if ((line = el_gets(el, &count)) == NULL || 1955 count <= 0) { 1956 printf("\n"); 1957 break; 1958 } 1959 history(hl, &hev, H_ENTER, line); 1960 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { 1961 fprintf(stderr, "Error: input line too long\n"); 1962 continue; 1963 } 1964 #endif /* USE_LIBEDIT */ 1965 } 1966 1967 cp = strrchr(cmd, '\n'); 1968 if (cp) 1969 *cp = '\0'; 1970 1971 /* Handle user interrupts gracefully during commands */ 1972 interrupted = 0; 1973 signal(SIGINT, cmd_interrupt); 1974 1975 err = parse_dispatch_command(conn, cmd, &remote_path, 1976 batchmode); 1977 if (err != 0) 1978 break; 1979 } 1980 xfree(remote_path); 1981 xfree(conn); 1982 1983 #ifdef USE_LIBEDIT 1984 if (el != NULL) 1985 el_end(el); 1986 #endif /* USE_LIBEDIT */ 1987 1988 /* err == 1 signifies normal "quit" exit */ 1989 return (err >= 0 ? 0 : -1); 1990 } 1991 1992 static void 1993 connect_to_server(char *path, char **args, int *in, int *out) 1994 { 1995 int c_in, c_out; 1996 1997 #ifdef USE_PIPES 1998 int pin[2], pout[2]; 1999 2000 if ((pipe(pin) == -1) || (pipe(pout) == -1)) 2001 fatal("pipe: %s", strerror(errno)); 2002 *in = pin[0]; 2003 *out = pout[1]; 2004 c_in = pout[0]; 2005 c_out = pin[1]; 2006 #else /* USE_PIPES */ 2007 int inout[2]; 2008 2009 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 2010 fatal("socketpair: %s", strerror(errno)); 2011 *in = *out = inout[0]; 2012 c_in = c_out = inout[1]; 2013 #endif /* USE_PIPES */ 2014 2015 if ((sshpid = fork()) == -1) 2016 fatal("fork: %s", strerror(errno)); 2017 else if (sshpid == 0) { 2018 if ((dup2(c_in, STDIN_FILENO) == -1) || 2019 (dup2(c_out, STDOUT_FILENO) == -1)) { 2020 fprintf(stderr, "dup2: %s\n", strerror(errno)); 2021 _exit(1); 2022 } 2023 close(*in); 2024 close(*out); 2025 close(c_in); 2026 close(c_out); 2027 2028 /* 2029 * The underlying ssh is in the same process group, so we must 2030 * ignore SIGINT if we want to gracefully abort commands, 2031 * otherwise the signal will make it to the ssh process and 2032 * kill it too. Contrawise, since sftp sends SIGTERMs to the 2033 * underlying ssh, it must *not* ignore that signal. 2034 */ 2035 signal(SIGINT, SIG_IGN); 2036 signal(SIGTERM, SIG_DFL); 2037 execvp(path, args); 2038 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 2039 _exit(1); 2040 } 2041 2042 signal(SIGTERM, killchild); 2043 signal(SIGINT, killchild); 2044 signal(SIGHUP, killchild); 2045 close(c_in); 2046 close(c_out); 2047 } 2048 2049 static void 2050 usage(void) 2051 { 2052 extern char *__progname; 2053 2054 fprintf(stderr, 2055 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" 2056 " [-D sftp_server_path] [-F ssh_config] " 2057 "[-i identity_file]\n" 2058 " [-o ssh_option] [-P port] [-R num_requests] " 2059 "[-S program]\n" 2060 " [-s subsystem | sftp_server] host\n" 2061 " %s [user@]host[:file ...]\n" 2062 " %s [user@]host[:dir[/]]\n" 2063 " %s -b batchfile [user@]host\n", 2064 __progname, __progname, __progname, __progname); 2065 exit(1); 2066 } 2067 2068 int 2069 main(int argc, char **argv) 2070 { 2071 int in, out, ch, err; 2072 char *host = NULL, *userhost, *cp, *file2 = NULL; 2073 int debug_level = 0, sshver = 2; 2074 char *file1 = NULL, *sftp_server = NULL; 2075 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 2076 LogLevel ll = SYSLOG_LEVEL_INFO; 2077 arglist args; 2078 extern int optind; 2079 extern char *optarg; 2080 struct sftp_conn *conn; 2081 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; 2082 size_t num_requests = DEFAULT_NUM_REQUESTS; 2083 2084 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 2085 sanitise_stdfd(); 2086 2087 __progname = ssh_get_progname(argv[0]); 2088 memset(&args, '\0', sizeof(args)); 2089 args.list = NULL; 2090 addargs(&args, "%s", ssh_program); 2091 addargs(&args, "-oForwardX11 no"); 2092 addargs(&args, "-oForwardAgent no"); 2093 addargs(&args, "-oPermitLocalCommand no"); 2094 addargs(&args, "-oClearAllForwardings yes"); 2095 2096 ll = SYSLOG_LEVEL_INFO; 2097 infile = stdin; 2098 2099 while ((ch = getopt(argc, argv, 2100 "1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { 2101 switch (ch) { 2102 /* Passed through to ssh(1) */ 2103 case '4': 2104 case '6': 2105 case 'C': 2106 addargs(&args, "-%c", ch); 2107 break; 2108 /* Passed through to ssh(1) with argument */ 2109 case 'F': 2110 case 'c': 2111 case 'i': 2112 case 'o': 2113 addargs(&args, "-%c", ch); 2114 addargs(&args, "%s", optarg); 2115 break; 2116 case 'q': 2117 showprogress = 0; 2118 addargs(&args, "-%c", ch); 2119 break; 2120 case 'P': 2121 addargs(&args, "-oPort %s", optarg); 2122 break; 2123 case 'v': 2124 if (debug_level < 3) { 2125 addargs(&args, "-v"); 2126 ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 2127 } 2128 debug_level++; 2129 break; 2130 case '1': 2131 sshver = 1; 2132 if (sftp_server == NULL) 2133 sftp_server = _PATH_SFTP_SERVER; 2134 break; 2135 case '2': 2136 sshver = 2; 2137 break; 2138 case 'B': 2139 copy_buffer_len = strtol(optarg, &cp, 10); 2140 if (copy_buffer_len == 0 || *cp != '\0') 2141 fatal("Invalid buffer size \"%s\"", optarg); 2142 break; 2143 case 'b': 2144 if (batchmode) 2145 fatal("Batch file already specified."); 2146 2147 /* Allow "-" as stdin */ 2148 if (strcmp(optarg, "-") != 0 && 2149 (infile = fopen(optarg, "r")) == NULL) 2150 fatal("%s (%s).", strerror(errno), optarg); 2151 showprogress = 0; 2152 batchmode = 1; 2153 addargs(&args, "-obatchmode yes"); 2154 break; 2155 case 'p': 2156 global_pflag = 1; 2157 break; 2158 case 'D': 2159 sftp_direct = optarg; 2160 break; 2161 case 'r': 2162 global_rflag = 1; 2163 break; 2164 case 'R': 2165 num_requests = strtol(optarg, &cp, 10); 2166 if (num_requests == 0 || *cp != '\0') 2167 fatal("Invalid number of requests \"%s\"", 2168 optarg); 2169 break; 2170 case 's': 2171 sftp_server = optarg; 2172 break; 2173 case 'S': 2174 ssh_program = optarg; 2175 replacearg(&args, 0, "%s", ssh_program); 2176 break; 2177 case 'h': 2178 default: 2179 usage(); 2180 } 2181 } 2182 2183 if (!isatty(STDERR_FILENO)) 2184 showprogress = 0; 2185 2186 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 2187 2188 if (sftp_direct == NULL) { 2189 if (optind == argc || argc > (optind + 2)) 2190 usage(); 2191 2192 userhost = xstrdup(argv[optind]); 2193 file2 = argv[optind+1]; 2194 2195 if ((host = strrchr(userhost, '@')) == NULL) 2196 host = userhost; 2197 else { 2198 *host++ = '\0'; 2199 if (!userhost[0]) { 2200 fprintf(stderr, "Missing username\n"); 2201 usage(); 2202 } 2203 addargs(&args, "-l"); 2204 addargs(&args, "%s", userhost); 2205 } 2206 2207 if ((cp = colon(host)) != NULL) { 2208 *cp++ = '\0'; 2209 file1 = cp; 2210 } 2211 2212 host = cleanhostname(host); 2213 if (!*host) { 2214 fprintf(stderr, "Missing hostname\n"); 2215 usage(); 2216 } 2217 2218 addargs(&args, "-oProtocol %d", sshver); 2219 2220 /* no subsystem if the server-spec contains a '/' */ 2221 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 2222 addargs(&args, "-s"); 2223 2224 addargs(&args, "--"); 2225 addargs(&args, "%s", host); 2226 addargs(&args, "%s", (sftp_server != NULL ? 2227 sftp_server : "sftp")); 2228 2229 connect_to_server(ssh_program, args.list, &in, &out); 2230 } else { 2231 args.list = NULL; 2232 addargs(&args, "sftp-server"); 2233 2234 connect_to_server(sftp_direct, args.list, &in, &out); 2235 } 2236 freeargs(&args); 2237 2238 conn = do_init(in, out, copy_buffer_len, num_requests); 2239 if (conn == NULL) 2240 fatal("Couldn't initialise connection to server"); 2241 2242 if (!batchmode) { 2243 if (sftp_direct == NULL) 2244 fprintf(stderr, "Connected to %s.\n", host); 2245 else 2246 fprintf(stderr, "Attached to %s.\n", sftp_direct); 2247 } 2248 2249 err = interactive_loop(conn, file1, file2); 2250 2251 #if !defined(USE_PIPES) 2252 shutdown(in, SHUT_RDWR); 2253 shutdown(out, SHUT_RDWR); 2254 #endif 2255 2256 close(in); 2257 close(out); 2258 if (batchmode) 2259 fclose(infile); 2260 2261 while (waitpid(sshpid, NULL, 0) == -1) 2262 if (errno != EINTR) 2263 fatal("Couldn't wait for ssh process: %s", 2264 strerror(errno)); 2265 2266 exit(err == 0 ? 0 : 1); 2267 } 2268