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