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