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 <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 printf("cd path Change remote directory to 'path'\n"); 191 printf("lcd path Change local directory to 'path'\n"); 192 printf("chgrp grp path Change group of file 'path' to 'grp'\n"); 193 printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); 194 printf("chown own path Change owner of file 'path' to 'own'\n"); 195 printf("df [path] Display statistics for current directory or\n"); 196 printf(" filesystem containing 'path'\n"); 197 printf("help Display this help text\n"); 198 printf("get remote-path [local-path] Download file\n"); 199 printf("lls [ls-options [path]] Display local directory listing\n"); 200 printf("ln oldpath newpath Symlink remote file\n"); 201 printf("lmkdir path Create local directory\n"); 202 printf("lpwd Print local working directory\n"); 203 printf("ls [path] Display remote directory listing\n"); 204 printf("lumask umask Set local umask to 'umask'\n"); 205 printf("mkdir path Create remote directory\n"); 206 printf("progress Toggle display of progress meter\n"); 207 printf("put local-path [remote-path] Upload file\n"); 208 printf("pwd Display remote working directory\n"); 209 printf("exit Quit sftp\n"); 210 printf("quit Quit sftp\n"); 211 printf("rename oldpath newpath Rename remote file\n"); 212 printf("rmdir path Remove remote directory\n"); 213 printf("rm path Delete remote file\n"); 214 printf("symlink oldpath newpath Symlink remote file\n"); 215 printf("version Show SFTP version\n"); 216 printf("!command Execute 'command' in local shell\n"); 217 printf("! Escape to local shell\n"); 218 printf("? Synonym for help\n"); 219 } 220 221 static void 222 local_do_shell(const char *args) 223 { 224 int status; 225 char *shell; 226 pid_t pid; 227 228 if (!*args) 229 args = NULL; 230 231 if ((shell = getenv("SHELL")) == NULL) 232 shell = _PATH_BSHELL; 233 234 if ((pid = fork()) == -1) 235 fatal("Couldn't fork: %s", strerror(errno)); 236 237 if (pid == 0) { 238 /* XXX: child has pipe fds to ssh subproc open - issue? */ 239 if (args) { 240 debug3("Executing %s -c \"%s\"", shell, args); 241 execl(shell, shell, "-c", args, (char *)NULL); 242 } else { 243 debug3("Executing %s", shell); 244 execl(shell, shell, (char *)NULL); 245 } 246 fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, 247 strerror(errno)); 248 _exit(1); 249 } 250 while (waitpid(pid, &status, 0) == -1) 251 if (errno != EINTR) 252 fatal("Couldn't wait for child: %s", strerror(errno)); 253 if (!WIFEXITED(status)) 254 error("Shell exited abnormally"); 255 else if (WEXITSTATUS(status)) 256 error("Shell exited with status %d", WEXITSTATUS(status)); 257 } 258 259 static void 260 local_do_ls(const char *args) 261 { 262 if (!args || !*args) 263 local_do_shell(_PATH_LS); 264 else { 265 int len = strlen(_PATH_LS " ") + strlen(args) + 1; 266 char *buf = xmalloc(len); 267 268 /* XXX: quoting - rip quoting code from ftp? */ 269 snprintf(buf, len, _PATH_LS " %s", args); 270 local_do_shell(buf); 271 xfree(buf); 272 } 273 } 274 275 /* Strip one path (usually the pwd) from the start of another */ 276 static char * 277 path_strip(char *path, char *strip) 278 { 279 size_t len; 280 281 if (strip == NULL) 282 return (xstrdup(path)); 283 284 len = strlen(strip); 285 if (strncmp(path, strip, len) == 0) { 286 if (strip[len - 1] != '/' && path[len] == '/') 287 len++; 288 return (xstrdup(path + len)); 289 } 290 291 return (xstrdup(path)); 292 } 293 294 static char * 295 path_append(char *p1, char *p2) 296 { 297 char *ret; 298 size_t len = strlen(p1) + strlen(p2) + 2; 299 300 ret = xmalloc(len); 301 strlcpy(ret, p1, len); 302 if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') 303 strlcat(ret, "/", len); 304 strlcat(ret, p2, len); 305 306 return(ret); 307 } 308 309 static char * 310 make_absolute(char *p, char *pwd) 311 { 312 char *abs_str; 313 314 /* Derelativise */ 315 if (p && p[0] != '/') { 316 abs_str = path_append(pwd, p); 317 xfree(p); 318 return(abs_str); 319 } else 320 return(p); 321 } 322 323 static int 324 infer_path(const char *p, char **ifp) 325 { 326 char *cp; 327 328 cp = strrchr(p, '/'); 329 if (cp == NULL) { 330 *ifp = xstrdup(p); 331 return(0); 332 } 333 334 if (!cp[1]) { 335 error("Invalid path"); 336 return(-1); 337 } 338 339 *ifp = xstrdup(cp + 1); 340 return(0); 341 } 342 343 static int 344 parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag) 345 { 346 extern int opterr, optind, optopt, optreset; 347 int ch; 348 349 optind = optreset = 1; 350 opterr = 0; 351 352 *pflag = 0; 353 while ((ch = getopt(argc, argv, "Pp")) != -1) { 354 switch (ch) { 355 case 'p': 356 case 'P': 357 *pflag = 1; 358 break; 359 default: 360 error("%s: Invalid flag -%c", cmd, optopt); 361 return -1; 362 } 363 } 364 365 return optind; 366 } 367 368 static int 369 parse_ls_flags(char **argv, int argc, int *lflag) 370 { 371 extern int opterr, optind, optopt, optreset; 372 int ch; 373 374 optind = optreset = 1; 375 opterr = 0; 376 377 *lflag = LS_NAME_SORT; 378 while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) { 379 switch (ch) { 380 case '1': 381 *lflag &= ~VIEW_FLAGS; 382 *lflag |= LS_SHORT_VIEW; 383 break; 384 case 'S': 385 *lflag &= ~SORT_FLAGS; 386 *lflag |= LS_SIZE_SORT; 387 break; 388 case 'a': 389 *lflag |= LS_SHOW_ALL; 390 break; 391 case 'f': 392 *lflag &= ~SORT_FLAGS; 393 break; 394 case 'l': 395 *lflag &= ~VIEW_FLAGS; 396 *lflag |= LS_LONG_VIEW; 397 break; 398 case 'n': 399 *lflag &= ~VIEW_FLAGS; 400 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; 401 break; 402 case 'r': 403 *lflag |= LS_REVERSE_SORT; 404 break; 405 case 't': 406 *lflag &= ~SORT_FLAGS; 407 *lflag |= LS_TIME_SORT; 408 break; 409 default: 410 error("ls: Invalid flag -%c", optopt); 411 return -1; 412 } 413 } 414 415 return optind; 416 } 417 418 static int 419 parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag) 420 { 421 extern int opterr, optind, optopt, optreset; 422 int ch; 423 424 optind = optreset = 1; 425 opterr = 0; 426 427 *hflag = *iflag = 0; 428 while ((ch = getopt(argc, argv, "hi")) != -1) { 429 switch (ch) { 430 case 'h': 431 *hflag = 1; 432 break; 433 case 'i': 434 *iflag = 1; 435 break; 436 default: 437 error("%s: Invalid flag -%c", cmd, optopt); 438 return -1; 439 } 440 } 441 442 return optind; 443 } 444 445 static int 446 is_dir(char *path) 447 { 448 struct stat sb; 449 450 /* XXX: report errors? */ 451 if (stat(path, &sb) == -1) 452 return(0); 453 454 return(S_ISDIR(sb.st_mode)); 455 } 456 457 static int 458 remote_is_dir(struct sftp_conn *conn, char *path) 459 { 460 Attrib *a; 461 462 /* XXX: report errors? */ 463 if ((a = do_stat(conn, path, 1)) == NULL) 464 return(0); 465 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) 466 return(0); 467 return(S_ISDIR(a->perm)); 468 } 469 470 static int 471 process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 472 { 473 char *abs_src = NULL; 474 char *abs_dst = NULL; 475 char *tmp; 476 glob_t g; 477 int err = 0; 478 int i; 479 480 abs_src = xstrdup(src); 481 abs_src = make_absolute(abs_src, pwd); 482 483 memset(&g, 0, sizeof(g)); 484 debug3("Looking up %s", abs_src); 485 if (remote_glob(conn, abs_src, 0, NULL, &g)) { 486 error("File \"%s\" not found.", abs_src); 487 err = -1; 488 goto out; 489 } 490 491 /* If multiple matches, dst must be a directory or unspecified */ 492 if (g.gl_matchc > 1 && dst && !is_dir(dst)) { 493 error("Multiple files match, but \"%s\" is not a directory", 494 dst); 495 err = -1; 496 goto out; 497 } 498 499 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 500 if (infer_path(g.gl_pathv[i], &tmp)) { 501 err = -1; 502 goto out; 503 } 504 505 if (g.gl_matchc == 1 && dst) { 506 /* If directory specified, append filename */ 507 xfree(tmp); 508 if (is_dir(dst)) { 509 if (infer_path(g.gl_pathv[0], &tmp)) { 510 err = 1; 511 goto out; 512 } 513 abs_dst = path_append(dst, tmp); 514 xfree(tmp); 515 } else 516 abs_dst = xstrdup(dst); 517 } else if (dst) { 518 abs_dst = path_append(dst, tmp); 519 xfree(tmp); 520 } else 521 abs_dst = tmp; 522 523 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 524 if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 525 err = -1; 526 xfree(abs_dst); 527 abs_dst = NULL; 528 } 529 530 out: 531 xfree(abs_src); 532 globfree(&g); 533 return(err); 534 } 535 536 static int 537 process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 538 { 539 char *tmp_dst = NULL; 540 char *abs_dst = NULL; 541 char *tmp; 542 glob_t g; 543 int err = 0; 544 int i; 545 struct stat sb; 546 547 if (dst) { 548 tmp_dst = xstrdup(dst); 549 tmp_dst = make_absolute(tmp_dst, pwd); 550 } 551 552 memset(&g, 0, sizeof(g)); 553 debug3("Looking up %s", src); 554 if (glob(src, GLOB_NOCHECK, NULL, &g)) { 555 error("File \"%s\" not found.", src); 556 err = -1; 557 goto out; 558 } 559 560 /* If multiple matches, dst may be directory or unspecified */ 561 if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { 562 error("Multiple files match, but \"%s\" is not a directory", 563 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 if (!S_ISREG(sb.st_mode)) { 576 error("skipping non-regular file %s", 577 g.gl_pathv[i]); 578 continue; 579 } 580 if (infer_path(g.gl_pathv[i], &tmp)) { 581 err = -1; 582 goto out; 583 } 584 585 if (g.gl_matchc == 1 && tmp_dst) { 586 /* If directory specified, append filename */ 587 if (remote_is_dir(conn, tmp_dst)) { 588 if (infer_path(g.gl_pathv[0], &tmp)) { 589 err = 1; 590 goto out; 591 } 592 abs_dst = path_append(tmp_dst, tmp); 593 xfree(tmp); 594 } else 595 abs_dst = xstrdup(tmp_dst); 596 597 } else if (tmp_dst) { 598 abs_dst = path_append(tmp_dst, tmp); 599 xfree(tmp); 600 } else 601 abs_dst = make_absolute(tmp, pwd); 602 603 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 604 if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 605 err = -1; 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 *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 = *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)) == -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, lflag, iflag, hflag, cmdnum, i; 1217 unsigned long n_arg; 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, &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); 1243 break; 1244 case I_PUT: 1245 err = process_put(conn, path1, path2, *pwd, pflag); 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); 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 != 0 && err_abort) 1369 break; 1370 else 1371 continue; 1372 } 1373 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1374 error("Can't get current ownership of " 1375 "remote file \"%s\"", g.gl_pathv[i]); 1376 if (err != 0 && err_abort) 1377 break; 1378 else 1379 continue; 1380 } 1381 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1382 if (cmdnum == I_CHOWN) { 1383 printf("Changing owner on %s\n", g.gl_pathv[i]); 1384 aa->uid = n_arg; 1385 } else { 1386 printf("Changing group on %s\n", g.gl_pathv[i]); 1387 aa->gid = n_arg; 1388 } 1389 err = do_setstat(conn, g.gl_pathv[i], aa); 1390 if (err != 0 && err_abort) 1391 break; 1392 } 1393 break; 1394 case I_PWD: 1395 printf("Remote working directory: %s\n", *pwd); 1396 break; 1397 case I_LPWD: 1398 if (!getcwd(path_buf, sizeof(path_buf))) { 1399 error("Couldn't get local cwd: %s", strerror(errno)); 1400 err = -1; 1401 break; 1402 } 1403 printf("Local working directory: %s\n", path_buf); 1404 break; 1405 case I_QUIT: 1406 /* Processed below */ 1407 break; 1408 case I_HELP: 1409 help(); 1410 break; 1411 case I_VERSION: 1412 printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1413 break; 1414 case I_PROGRESS: 1415 showprogress = !showprogress; 1416 if (showprogress) 1417 printf("Progress meter enabled\n"); 1418 else 1419 printf("Progress meter disabled\n"); 1420 break; 1421 default: 1422 fatal("%d is not implemented", cmdnum); 1423 } 1424 1425 if (g.gl_pathc) 1426 globfree(&g); 1427 if (path1) 1428 xfree(path1); 1429 if (path2) 1430 xfree(path2); 1431 1432 /* If an unignored error occurs in batch mode we should abort. */ 1433 if (err_abort && err != 0) 1434 return (-1); 1435 else if (cmdnum == I_QUIT) 1436 return (1); 1437 1438 return (0); 1439 } 1440 1441 static char * 1442 prompt(EditLine *el) 1443 { 1444 return ("sftp> "); 1445 } 1446 1447 int 1448 interactive_loop(int fd_in, int fd_out, char *file1, char *file2) 1449 { 1450 char *pwd; 1451 char *dir = NULL; 1452 char cmd[2048]; 1453 struct sftp_conn *conn; 1454 int err, interactive; 1455 EditLine *el = NULL; 1456 History *hl = NULL; 1457 HistEvent hev; 1458 extern char *__progname; 1459 1460 if (!batchmode && isatty(STDIN_FILENO)) { 1461 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) 1462 fatal("Couldn't initialise editline"); 1463 if ((hl = history_init()) == NULL) 1464 fatal("Couldn't initialise editline history"); 1465 history(hl, &hev, H_SETSIZE, 100); 1466 el_set(el, EL_HIST, history, hl); 1467 1468 el_set(el, EL_PROMPT, prompt); 1469 el_set(el, EL_EDITOR, "emacs"); 1470 el_set(el, EL_TERMINAL, NULL); 1471 el_set(el, EL_SIGNAL, 1); 1472 el_source(el, NULL); 1473 } 1474 1475 conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); 1476 if (conn == NULL) 1477 fatal("Couldn't initialise connection to server"); 1478 1479 pwd = do_realpath(conn, "."); 1480 if (pwd == NULL) 1481 fatal("Need cwd"); 1482 1483 if (file1 != NULL) { 1484 dir = xstrdup(file1); 1485 dir = make_absolute(dir, pwd); 1486 1487 if (remote_is_dir(conn, dir) && file2 == NULL) { 1488 printf("Changing to: %s\n", dir); 1489 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 1490 if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) { 1491 xfree(dir); 1492 xfree(pwd); 1493 xfree(conn); 1494 return (-1); 1495 } 1496 } else { 1497 if (file2 == NULL) 1498 snprintf(cmd, sizeof cmd, "get %s", dir); 1499 else 1500 snprintf(cmd, sizeof cmd, "get %s %s", dir, 1501 file2); 1502 1503 err = parse_dispatch_command(conn, cmd, &pwd, 1); 1504 xfree(dir); 1505 xfree(pwd); 1506 xfree(conn); 1507 return (err); 1508 } 1509 xfree(dir); 1510 } 1511 1512 setvbuf(stdout, NULL, _IOLBF, 0); 1513 setvbuf(infile, NULL, _IOLBF, 0); 1514 1515 interactive = !batchmode && isatty(STDIN_FILENO); 1516 err = 0; 1517 for (;;) { 1518 char *cp; 1519 const char *line; 1520 int count = 0; 1521 1522 signal(SIGINT, SIG_IGN); 1523 1524 if (el == NULL) { 1525 if (interactive) 1526 printf("sftp> "); 1527 if (fgets(cmd, sizeof(cmd), infile) == NULL) { 1528 if (interactive) 1529 printf("\n"); 1530 break; 1531 } 1532 if (!interactive) { /* Echo command */ 1533 printf("sftp> %s", cmd); 1534 if (strlen(cmd) > 0 && 1535 cmd[strlen(cmd) - 1] != '\n') 1536 printf("\n"); 1537 } 1538 } else { 1539 if ((line = el_gets(el, &count)) == NULL || count <= 0) { 1540 printf("\n"); 1541 break; 1542 } 1543 history(hl, &hev, H_ENTER, line); 1544 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { 1545 fprintf(stderr, "Error: input line too long\n"); 1546 continue; 1547 } 1548 } 1549 1550 cp = strrchr(cmd, '\n'); 1551 if (cp) 1552 *cp = '\0'; 1553 1554 /* Handle user interrupts gracefully during commands */ 1555 interrupted = 0; 1556 signal(SIGINT, cmd_interrupt); 1557 1558 err = parse_dispatch_command(conn, cmd, &pwd, batchmode); 1559 if (err != 0) 1560 break; 1561 } 1562 xfree(pwd); 1563 xfree(conn); 1564 1565 if (el != NULL) 1566 el_end(el); 1567 1568 /* err == 1 signifies normal "quit" exit */ 1569 return (err >= 0 ? 0 : -1); 1570 } 1571 1572 static void 1573 connect_to_server(char *path, char **args, int *in, int *out) 1574 { 1575 int c_in, c_out; 1576 1577 int inout[2]; 1578 1579 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 1580 fatal("socketpair: %s", strerror(errno)); 1581 *in = *out = inout[0]; 1582 c_in = c_out = inout[1]; 1583 1584 if ((sshpid = fork()) == -1) 1585 fatal("fork: %s", strerror(errno)); 1586 else if (sshpid == 0) { 1587 if ((dup2(c_in, STDIN_FILENO) == -1) || 1588 (dup2(c_out, STDOUT_FILENO) == -1)) { 1589 fprintf(stderr, "dup2: %s\n", strerror(errno)); 1590 _exit(1); 1591 } 1592 close(*in); 1593 close(*out); 1594 close(c_in); 1595 close(c_out); 1596 1597 /* 1598 * The underlying ssh is in the same process group, so we must 1599 * ignore SIGINT if we want to gracefully abort commands, 1600 * otherwise the signal will make it to the ssh process and 1601 * kill it too 1602 */ 1603 signal(SIGINT, SIG_IGN); 1604 execvp(path, args); 1605 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 1606 _exit(1); 1607 } 1608 1609 signal(SIGTERM, killchild); 1610 signal(SIGINT, killchild); 1611 signal(SIGHUP, killchild); 1612 close(c_in); 1613 close(c_out); 1614 } 1615 1616 static void 1617 usage(void) 1618 { 1619 extern char *__progname; 1620 1621 fprintf(stderr, 1622 "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" 1623 " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" 1624 " [-S program] [-s subsystem | sftp_server] host\n" 1625 " %s [[user@]host[:file [file]]]\n" 1626 " %s [[user@]host[:dir[/]]]\n" 1627 " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); 1628 exit(1); 1629 } 1630 1631 int 1632 main(int argc, char **argv) 1633 { 1634 int in, out, ch, err; 1635 char *host, *userhost, *cp, *file2 = NULL; 1636 int debug_level = 0, sshver = 2; 1637 char *file1 = NULL, *sftp_server = NULL; 1638 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 1639 LogLevel ll = SYSLOG_LEVEL_INFO; 1640 arglist args; 1641 extern int optind; 1642 extern char *optarg; 1643 1644 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1645 sanitise_stdfd(); 1646 1647 memset(&args, '\0', sizeof(args)); 1648 args.list = NULL; 1649 addargs(&args, "%s", ssh_program); 1650 addargs(&args, "-oForwardX11 no"); 1651 addargs(&args, "-oForwardAgent no"); 1652 addargs(&args, "-oPermitLocalCommand no"); 1653 addargs(&args, "-oClearAllForwardings yes"); 1654 1655 ll = SYSLOG_LEVEL_INFO; 1656 infile = stdin; 1657 1658 while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { 1659 switch (ch) { 1660 case 'C': 1661 addargs(&args, "-C"); 1662 break; 1663 case 'v': 1664 if (debug_level < 3) { 1665 addargs(&args, "-v"); 1666 ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 1667 } 1668 debug_level++; 1669 break; 1670 case 'F': 1671 case 'o': 1672 addargs(&args, "-%c%s", ch, optarg); 1673 break; 1674 case '1': 1675 sshver = 1; 1676 if (sftp_server == NULL) 1677 sftp_server = _PATH_SFTP_SERVER; 1678 break; 1679 case 's': 1680 sftp_server = optarg; 1681 break; 1682 case 'S': 1683 ssh_program = optarg; 1684 replacearg(&args, 0, "%s", ssh_program); 1685 break; 1686 case 'b': 1687 if (batchmode) 1688 fatal("Batch file already specified."); 1689 1690 /* Allow "-" as stdin */ 1691 if (strcmp(optarg, "-") != 0 && 1692 (infile = fopen(optarg, "r")) == NULL) 1693 fatal("%s (%s).", strerror(errno), optarg); 1694 showprogress = 0; 1695 batchmode = 1; 1696 addargs(&args, "-obatchmode yes"); 1697 break; 1698 case 'P': 1699 sftp_direct = optarg; 1700 break; 1701 case 'B': 1702 copy_buffer_len = strtol(optarg, &cp, 10); 1703 if (copy_buffer_len == 0 || *cp != '\0') 1704 fatal("Invalid buffer size \"%s\"", optarg); 1705 break; 1706 case 'R': 1707 num_requests = strtol(optarg, &cp, 10); 1708 if (num_requests == 0 || *cp != '\0') 1709 fatal("Invalid number of requests \"%s\"", 1710 optarg); 1711 break; 1712 case 'h': 1713 default: 1714 usage(); 1715 } 1716 } 1717 1718 if (!isatty(STDERR_FILENO)) 1719 showprogress = 0; 1720 1721 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 1722 1723 if (sftp_direct == NULL) { 1724 if (optind == argc || argc > (optind + 2)) 1725 usage(); 1726 1727 userhost = xstrdup(argv[optind]); 1728 file2 = argv[optind+1]; 1729 1730 if ((host = strrchr(userhost, '@')) == NULL) 1731 host = userhost; 1732 else { 1733 *host++ = '\0'; 1734 if (!userhost[0]) { 1735 fprintf(stderr, "Missing username\n"); 1736 usage(); 1737 } 1738 addargs(&args, "-l%s", userhost); 1739 } 1740 1741 if ((cp = colon(host)) != NULL) { 1742 *cp++ = '\0'; 1743 file1 = cp; 1744 } 1745 1746 host = cleanhostname(host); 1747 if (!*host) { 1748 fprintf(stderr, "Missing hostname\n"); 1749 usage(); 1750 } 1751 1752 addargs(&args, "-oProtocol %d", sshver); 1753 1754 /* no subsystem if the server-spec contains a '/' */ 1755 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 1756 addargs(&args, "-s"); 1757 1758 addargs(&args, "%s", host); 1759 addargs(&args, "%s", (sftp_server != NULL ? 1760 sftp_server : "sftp")); 1761 1762 if (!batchmode) 1763 fprintf(stderr, "Connecting to %s...\n", host); 1764 connect_to_server(ssh_program, args.list, &in, &out); 1765 } else { 1766 args.list = NULL; 1767 addargs(&args, "sftp-server"); 1768 1769 if (!batchmode) 1770 fprintf(stderr, "Attaching to %s...\n", sftp_direct); 1771 connect_to_server(sftp_direct, args.list, &in, &out); 1772 } 1773 freeargs(&args); 1774 1775 err = interactive_loop(in, out, file1, file2); 1776 1777 close(in); 1778 close(out); 1779 if (batchmode) 1780 fclose(infile); 1781 1782 while (waitpid(sshpid, NULL, 0) == -1) 1783 if (errno != EINTR) 1784 fatal("Couldn't wait for ssh process: %s", 1785 strerror(errno)); 1786 1787 exit(err == 0 ? 0 : 1); 1788 } 1789