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