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