sftp.c revision 248619
11573Srgrimes/* $OpenBSD: sftp.c,v 1.142 2013/02/08 00:41:12 djm Exp $ */ 21573Srgrimes/* $FreeBSD: head/crypto/openssh/sftp.c 248619 2013-03-22 17:55:38Z des $ */ 31573Srgrimes/* 41573Srgrimes * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 51573Srgrimes * 61573Srgrimes * Permission to use, copy, modify, and distribute this software for any 71573Srgrimes * purpose with or without fee is hereby granted, provided that the above 81573Srgrimes * copyright notice and this permission notice appear in all copies. 91573Srgrimes * 101573Srgrimes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 111573Srgrimes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 121573Srgrimes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 131573Srgrimes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 141573Srgrimes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 151573Srgrimes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 161573Srgrimes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 171573Srgrimes */ 181573Srgrimes 191573Srgrimes#include "includes.h" 201573Srgrimes 211573Srgrimes#include <sys/types.h> 221573Srgrimes#include <sys/ioctl.h> 231573Srgrimes#ifdef HAVE_SYS_STAT_H 241573Srgrimes# include <sys/stat.h> 251573Srgrimes#endif 261573Srgrimes#include <sys/param.h> 271573Srgrimes#include <sys/socket.h> 281573Srgrimes#include <sys/wait.h> 291573Srgrimes#ifdef HAVE_SYS_STATVFS_H 301573Srgrimes#include <sys/statvfs.h> 311573Srgrimes#endif 321573Srgrimes 3390045Sobrien#include <ctype.h> 3490045Sobrien#include <errno.h> 351573Srgrimes 3671579Sdeischen#ifdef HAVE_PATHS_H 371573Srgrimes# include <paths.h> 381573Srgrimes#endif 391573Srgrimes#ifdef HAVE_LIBGEN_H 401573Srgrimes#include <libgen.h> 41111618Snectar#endif 421573Srgrimes#ifdef USE_LIBEDIT 431573Srgrimes#include <histedit.h> 441573Srgrimes#else 451573Srgrimestypedef void EditLine; 461573Srgrimes#endif 471573Srgrimes#include <signal.h> 4871579Sdeischen#include <stdlib.h> 491573Srgrimes#include <stdio.h> 50292623Semaste#include <string.h> 51292623Semaste#include <unistd.h> 5228947Speter#include <stdarg.h> 53280219Sandrew 5436919Speter#ifdef HAVE_UTIL_H 5517141Sjkh# include <util.h> 5628947Speter#endif 5776224Sobrien 5876224Sobrien#include "xmalloc.h" 5928947Speter#include "log.h" 6028947Speter#include "pathnames.h" 6190045Sobrien#include "misc.h" 6290045Sobrien 6390045Sobrien#include "sftp.h" 64288028Srodrigc#include "buffer.h" 6528947Speter#include "sftp-common.h" 661573Srgrimes#include "sftp-client.h" 67287793Srodrigc 681573Srgrimes#define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */ 691573Srgrimes#define DEFAULT_NUM_REQUESTS 256 /* # concurrent outstanding requests */ 701573Srgrimes 71241046Sjilles/* File to read commands from */ 721573SrgrimesFILE* infile; 731573Srgrimes 741573Srgrimes/* Are we in batchfile mode? */ 7556698Sjasoneint batchmode = 0; 761573Srgrimes 771573Srgrimes/* PID of ssh transport process */ 781573Srgrimesstatic pid_t sshpid = -1; 7928947Speter 8090045Sobrien/* This is set to 0 if the progressmeter is not desired. */ 8128947Speterint showprogress = 1; 8228947Speter 8328947Speter/* When this option is set, we always recursively download/upload directories */ 8428947Speterint global_rflag = 0; 8528947Speter 8628947Speter/* When this option is set, the file transfers will always preserve times */ 8728947Speterint global_pflag = 0; 8828947Speter 891573Srgrimes/* SIGINT received during command processing */ 901573Srgrimesvolatile sig_atomic_t interrupted = 0; 91287793Srodrigc 921573Srgrimes/* I wish qsort() took a separate ctx for the comparison function...*/ 93287793Srodrigcint sort_flag; 94287793Srodrigc 9528947Speter/* Context used for commandline completion */ 9628947Speterstruct complete_ctx { 9728947Speter struct sftp_conn *conn; 9828947Speter char **remote_pathp; 9928947Speter}; 10028947Speter 10128947Speterint remote_glob(struct sftp_conn *, const char *, int, 10228947Speter int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 10328947Speter 10428947Speterextern char *__progname; 10528947Speter 10628947Speter/* Separators for interactive commands */ 10728947Speter#define WHITESPACE " \t\r\n" 108287793Srodrigc 10928947Speter/* ls flags */ 11090045Sobrien#define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */ 11190045Sobrien#define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */ 11290045Sobrien#define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */ 11390045Sobrien#define LS_NAME_SORT 0x0008 /* Sort by name (default) */ 11490045Sobrien#define LS_TIME_SORT 0x0010 /* Sort by mtime */ 1151794Scsgr#define LS_SIZE_SORT 0x0020 /* Sort by file size */ 1161573Srgrimes#define LS_REVERSE_SORT 0x0040 /* Reverse sort order */ 1171573Srgrimes#define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */ 1181794Scsgr#define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */ 11971579Sdeischen 1201573Srgrimes#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS) 1211573Srgrimes#define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) 1221573Srgrimes 1231794Scsgr/* Commands for interactive mode */ 1241573Srgrimes#define I_CHDIR 1 1251573Srgrimes#define I_CHGRP 2 1261573Srgrimes#define I_CHMOD 3 1271794Scsgr#define I_CHOWN 4 1281573Srgrimes#define I_DF 24 1291794Scsgr#define I_GET 5 1301794Scsgr#define I_HELP 6 1311794Scsgr#define I_LCHDIR 7 1321794Scsgr#define I_LINK 25 1331794Scsgr#define I_LLS 8 1341794Scsgr#define I_LMKDIR 9 1358870Srgrimes#define I_LPWD 10 1368870Srgrimes#define I_LS 11 1371794Scsgr#define I_LUMASK 12 1381573Srgrimes#define I_MKDIR 13 13921786Salex#define I_PUT 14 14021786Salex#define I_PWD 15 1411573Srgrimes#define I_QUIT 16 1421794Scsgr#define I_RENAME 17 1431794Scsgr#define I_RM 18 1441794Scsgr#define I_RMDIR 19 1451794Scsgr#define I_SHELL 20 1461794Scsgr#define I_SYMLINK 21 1471794Scsgr#define I_VERSION 22 1481794Scsgr#define I_PROGRESS 23 1491794Scsgr 1501794Scsgrstruct CMD { 1511794Scsgr const char *c; 1521794Scsgr const int n; 1531794Scsgr const int t; 1541794Scsgr}; 1551794Scsgr 1561794Scsgr/* Type of completion */ 1571573Srgrimes#define NOARGS 0 1581573Srgrimes#define REMOTE 1 1591573Srgrimes#define LOCAL 2 1601573Srgrimes 1611573Srgrimesstatic const struct CMD cmds[] = { 1621573Srgrimes { "bye", I_QUIT, NOARGS }, 1631573Srgrimes { "cd", I_CHDIR, REMOTE }, 1641573Srgrimes { "chdir", I_CHDIR, REMOTE }, 1651573Srgrimes { "chgrp", I_CHGRP, REMOTE }, 1661573Srgrimes { "chmod", I_CHMOD, REMOTE }, 1671573Srgrimes { "chown", I_CHOWN, REMOTE }, 1681573Srgrimes { "df", I_DF, REMOTE }, 1691573Srgrimes { "dir", I_LS, REMOTE }, 1701573Srgrimes { "exit", I_QUIT, NOARGS }, 1711573Srgrimes { "get", I_GET, REMOTE }, 1721573Srgrimes { "help", I_HELP, NOARGS }, 1731573Srgrimes { "lcd", I_LCHDIR, LOCAL }, 1741573Srgrimes { "lchdir", I_LCHDIR, LOCAL }, 1751573Srgrimes { "lls", I_LLS, LOCAL }, 1761573Srgrimes { "lmkdir", I_LMKDIR, LOCAL }, 17790045Sobrien { "ln", I_LINK, REMOTE }, 1781573Srgrimes { "lpwd", I_LPWD, LOCAL }, 1791794Scsgr { "ls", I_LS, REMOTE }, 1801794Scsgr { "lumask", I_LUMASK, NOARGS }, 1811794Scsgr { "mkdir", I_MKDIR, REMOTE }, 1821794Scsgr { "mget", I_GET, REMOTE }, 1831794Scsgr { "mput", I_PUT, LOCAL }, 1841573Srgrimes { "progress", I_PROGRESS, NOARGS }, 1851573Srgrimes { "put", I_PUT, LOCAL }, 1861794Scsgr { "pwd", I_PWD, REMOTE }, 1871794Scsgr { "quit", I_QUIT, NOARGS }, 1881794Scsgr { "rename", I_RENAME, REMOTE }, 1891794Scsgr { "rm", I_RM, REMOTE }, 1901573Srgrimes { "rmdir", I_RMDIR, REMOTE }, 1911573Srgrimes { "symlink", I_SYMLINK, REMOTE }, 1921573Srgrimes { "version", I_VERSION, NOARGS }, 1931794Scsgr { "!", I_SHELL, NOARGS }, 1941573Srgrimes { "?", I_HELP, NOARGS }, 1951794Scsgr { NULL, -1, -1 } 1961573Srgrimes}; 1971573Srgrimes 19828947Speterint interactive_loop(struct sftp_conn *, char *file1, char *file2); 19928947Speter 20028947Speter/* ARGSUSED */ 20190045Sobrienstatic void 20238391Sdfrkillchild(int signo) 20328947Speter{ 20428947Speter if (sshpid > 1) { 20528947Speter kill(sshpid, SIGTERM); 20628947Speter waitpid(sshpid, NULL, 0); 207108533Sschweikh } 208108533Sschweikh 20928947Speter _exit(1); 21028947Speter} 211200150Sed 21228947Speter/* ARGSUSED */ 21390045Sobrienstatic void 21428947Spetercmd_interrupt(int signo) 21528947Speter{ 21628947Speter const char msg[] = "\rInterrupt \n"; 21740688Sjdp int olderrno = errno; 21828947Speter 21928947Speter write(STDERR_FILENO, msg, sizeof(msg) - 1); 22028947Speter interrupted = 1; 22128947Speter errno = olderrno; 22228947Speter} 22328947Speter 22428947Speterstatic void 22528947Speterhelp(void) 22628947Speter{ 22728947Speter printf("Available commands:\n" 22828947Speter "bye Quit sftp\n" 22928947Speter "cd path Change remote directory to 'path'\n" 23028947Speter "chgrp grp path Change group of file 'path' to 'grp'\n" 23128947Speter "chmod mode path Change permissions of file 'path' to 'mode'\n" 23228947Speter "chown own path Change owner of file 'path' to 'own'\n" 23328947Speter "df [-hi] [path] Display statistics for current directory or\n" 234287793Srodrigc " filesystem containing 'path'\n" 23528947Speter "exit Quit sftp\n" 23690045Sobrien "get [-Ppr] remote [local] Download file\n" 23790045Sobrien "help Display this help text\n" 238153504Smarcel "lcd path Change local directory to 'path'\n" 239153504Smarcel "lls [ls-options [path]] Display local directory listing\n" 24040688Sjdp "lmkdir path Create local directory\n" 24140688Sjdp "ln [-s] oldpath newpath Link remote file (-s for symlink)\n" 24238391Sdfr "lpwd Print local working directory\n" 24338391Sdfr "ls [-1afhlnrSt] [path] Display remote directory listing\n" 24438391Sdfr "lumask umask Set local umask to 'umask'\n" 24540688Sjdp "mkdir path Create remote directory\n" 24638391Sdfr "progress Toggle display of progress meter\n" 247153504Smarcel "put [-Ppr] local [remote] Upload file\n" 24840688Sjdp "pwd Display remote working directory\n" 24928947Speter "quit Quit sftp\n" 25028947Speter "rename oldpath newpath Rename remote file\n" 25128947Speter "rm path Delete remote file\n" 25228947Speter "rmdir path Remove remote directory\n" 25356698Sjasone "symlink oldpath newpath Symlink remote file\n" 25428947Speter "version Show SFTP version\n" 25571579Sdeischen "!command Execute 'command' in local shell\n" 25628947Speter "! Escape to local shell\n" 25728947Speter "? Synonym for help\n"); 25828947Speter} 25928947Speter 26028947Speterstatic void 26128947Speterlocal_do_shell(const char *args) 26228947Speter{ 26328947Speter int status; 26428947Speter char *shell; 26528947Speter pid_t pid; 26628947Speter 26728947Speter if (!*args) 268271723Sbdrewery args = NULL; 26940688Sjdp 27040688Sjdp if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 27128947Speter shell = _PATH_BSHELL; 27240688Sjdp 27328947Speter if ((pid = fork()) == -1) 27428947Speter fatal("Couldn't fork: %s", strerror(errno)); 27528947Speter 27628947Speter if (pid == 0) { 27728947Speter /* XXX: child has pipe fds to ssh subproc open - issue? */ 27828947Speter if (args) { 27928947Speter debug3("Executing %s -c \"%s\"", shell, args); 28028947Speter execl(shell, shell, "-c", args, (char *)NULL); 28128947Speter } else { 28228947Speter debug3("Executing %s", shell); 28328947Speter execl(shell, shell, (char *)NULL); 28428947Speter } 28528947Speter fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, 28628947Speter strerror(errno)); 28728947Speter _exit(1); 28828947Speter } 28928947Speter while (waitpid(pid, &status, 0) == -1) 29028947Speter if (errno != EINTR) 29128947Speter fatal("Couldn't wait for child: %s", strerror(errno)); 29228947Speter if (!WIFEXITED(status)) 29340688Sjdp error("Shell exited abnormally"); 29428947Speter else if (WEXITSTATUS(status)) 29528947Speter error("Shell exited with status %d", WEXITSTATUS(status)); 29628947Speter} 29728947Speter 29828947Speterstatic void 29928947Speterlocal_do_ls(const char *args) 30028947Speter{ 301271723Sbdrewery if (!args || !*args) 30240688Sjdp local_do_shell(_PATH_LS); 30340688Sjdp else { 30440688Sjdp int len = strlen(_PATH_LS " ") + strlen(args) + 1; 30540688Sjdp char *buf = xmalloc(len); 30628947Speter 30728947Speter /* XXX: quoting - rip quoting code from ftp? */ 30828947Speter snprintf(buf, len, _PATH_LS " %s", args); 30928947Speter local_do_shell(buf); 31028947Speter xfree(buf); 31128947Speter } 31228947Speter} 31328947Speter 31428947Speter/* Strip one path (usually the pwd) from the start of another */ 31528947Speterstatic char * 31628947Speterpath_strip(char *path, char *strip) 31728947Speter{ 31828947Speter size_t len; 31928947Speter 32028947Speter if (strip == NULL) 32128947Speter return (xstrdup(path)); 32228947Speter 32328947Speter len = strlen(strip); 32428947Speter if (strncmp(path, strip, len) == 0) { 32528947Speter if (strip[len - 1] != '/' && path[len] == '/') 32628947Speter len++; 32728947Speter return (xstrdup(path + len)); 32828947Speter } 32928947Speter 33028947Speter return (xstrdup(path)); 33128947Speter} 33228947Speter 33328947Speterstatic char * 33428947Spetermake_absolute(char *p, char *pwd) 33540688Sjdp{ 33628947Speter char *abs_str; 33756698Sjasone 33828947Speter /* Derelativise */ 33928947Speter if (p && p[0] != '/') { 34040688Sjdp abs_str = path_append(pwd, p); 34140688Sjdp xfree(p); 34240688Sjdp return(abs_str); 34328947Speter } else 34440688Sjdp return(p); 34540688Sjdp} 34628947Speter 34728947Speterstatic int 34828947Speterparse_getput_flags(const char *cmd, char **argv, int argc, int *pflag, 34940688Sjdp int *rflag) 35040688Sjdp{ 35140688Sjdp extern int opterr, optind, optopt, optreset; 35240688Sjdp int ch; 35328947Speter 35428947Speter optind = optreset = 1; 35528947Speter opterr = 0; 35628947Speter 35728947Speter *rflag = *pflag = 0; 35828947Speter while ((ch = getopt(argc, argv, "PpRr")) != -1) { 35928947Speter switch (ch) { 36040688Sjdp case 'p': 36140688Sjdp case 'P': 36240688Sjdp *pflag = 1; 36340688Sjdp break; 36440688Sjdp case 'r': 36540688Sjdp case 'R': 36628947Speter *rflag = 1; 36728947Speter break; 36840688Sjdp default: 36940688Sjdp error("%s: Invalid flag -%c", cmd, optopt); 37040688Sjdp return -1; 37140688Sjdp } 37240688Sjdp } 37340688Sjdp 374287793Srodrigc return optind; 37540688Sjdp} 37640688Sjdp 37740688Sjdpstatic int 37840688Sjdpparse_link_flags(const char *cmd, char **argv, int argc, int *sflag) 37940688Sjdp{ 38040688Sjdp extern int opterr, optind, optopt, optreset; 38140688Sjdp int ch; 38240688Sjdp 38340688Sjdp optind = optreset = 1; 38440688Sjdp opterr = 0; 38540688Sjdp 38640688Sjdp *sflag = 0; 38740688Sjdp while ((ch = getopt(argc, argv, "s")) != -1) { 38840688Sjdp switch (ch) { 38940688Sjdp case 's': 39040688Sjdp *sflag = 1; 39140688Sjdp break; 39240688Sjdp default: 39340688Sjdp error("%s: Invalid flag -%c", cmd, optopt); 39440688Sjdp return -1; 39540688Sjdp } 39640688Sjdp } 39740688Sjdp 39840688Sjdp return optind; 39940688Sjdp} 40040688Sjdp 40140688Sjdpstatic int 40240688Sjdpparse_ls_flags(char **argv, int argc, int *lflag) 40340688Sjdp{ 40428947Speter extern int opterr, optind, optopt, optreset; 405 int ch; 406 407 optind = optreset = 1; 408 opterr = 0; 409 410 *lflag = LS_NAME_SORT; 411 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) { 412 switch (ch) { 413 case '1': 414 *lflag &= ~VIEW_FLAGS; 415 *lflag |= LS_SHORT_VIEW; 416 break; 417 case 'S': 418 *lflag &= ~SORT_FLAGS; 419 *lflag |= LS_SIZE_SORT; 420 break; 421 case 'a': 422 *lflag |= LS_SHOW_ALL; 423 break; 424 case 'f': 425 *lflag &= ~SORT_FLAGS; 426 break; 427 case 'h': 428 *lflag |= LS_SI_UNITS; 429 break; 430 case 'l': 431 *lflag &= ~LS_SHORT_VIEW; 432 *lflag |= LS_LONG_VIEW; 433 break; 434 case 'n': 435 *lflag &= ~LS_SHORT_VIEW; 436 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; 437 break; 438 case 'r': 439 *lflag |= LS_REVERSE_SORT; 440 break; 441 case 't': 442 *lflag &= ~SORT_FLAGS; 443 *lflag |= LS_TIME_SORT; 444 break; 445 default: 446 error("ls: Invalid flag -%c", optopt); 447 return -1; 448 } 449 } 450 451 return optind; 452} 453 454static int 455parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag) 456{ 457 extern int opterr, optind, optopt, optreset; 458 int ch; 459 460 optind = optreset = 1; 461 opterr = 0; 462 463 *hflag = *iflag = 0; 464 while ((ch = getopt(argc, argv, "hi")) != -1) { 465 switch (ch) { 466 case 'h': 467 *hflag = 1; 468 break; 469 case 'i': 470 *iflag = 1; 471 break; 472 default: 473 error("%s: Invalid flag -%c", cmd, optopt); 474 return -1; 475 } 476 } 477 478 return optind; 479} 480 481static int 482is_dir(char *path) 483{ 484 struct stat sb; 485 486 /* XXX: report errors? */ 487 if (stat(path, &sb) == -1) 488 return(0); 489 490 return(S_ISDIR(sb.st_mode)); 491} 492 493static int 494remote_is_dir(struct sftp_conn *conn, char *path) 495{ 496 Attrib *a; 497 498 /* XXX: report errors? */ 499 if ((a = do_stat(conn, path, 1)) == NULL) 500 return(0); 501 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) 502 return(0); 503 return(S_ISDIR(a->perm)); 504} 505 506/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ 507static int 508pathname_is_dir(char *pathname) 509{ 510 size_t l = strlen(pathname); 511 512 return l > 0 && pathname[l - 1] == '/'; 513} 514 515static int 516process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, 517 int pflag, int rflag) 518{ 519 char *abs_src = NULL; 520 char *abs_dst = NULL; 521 glob_t g; 522 char *filename, *tmp=NULL; 523 int i, err = 0; 524 525 abs_src = xstrdup(src); 526 abs_src = make_absolute(abs_src, pwd); 527 memset(&g, 0, sizeof(g)); 528 529 debug3("Looking up %s", abs_src); 530 if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) { 531 error("File \"%s\" not found.", abs_src); 532 err = -1; 533 goto out; 534 } 535 536 /* 537 * If multiple matches then dst must be a directory or 538 * unspecified. 539 */ 540 if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) { 541 error("Multiple source paths, but destination " 542 "\"%s\" is not a directory", dst); 543 err = -1; 544 goto out; 545 } 546 547 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 548 tmp = xstrdup(g.gl_pathv[i]); 549 if ((filename = basename(tmp)) == NULL) { 550 error("basename %s: %s", tmp, strerror(errno)); 551 xfree(tmp); 552 err = -1; 553 goto out; 554 } 555 556 if (g.gl_matchc == 1 && dst) { 557 if (is_dir(dst)) { 558 abs_dst = path_append(dst, filename); 559 } else { 560 abs_dst = xstrdup(dst); 561 } 562 } else if (dst) { 563 abs_dst = path_append(dst, filename); 564 } else { 565 abs_dst = xstrdup(filename); 566 } 567 xfree(tmp); 568 569 printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 570 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 571 if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, 572 pflag || global_pflag, 1) == -1) 573 err = -1; 574 } else { 575 if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, 576 pflag || global_pflag) == -1) 577 err = -1; 578 } 579 xfree(abs_dst); 580 abs_dst = NULL; 581 } 582 583out: 584 xfree(abs_src); 585 globfree(&g); 586 return(err); 587} 588 589static int 590process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, 591 int pflag, int rflag) 592{ 593 char *tmp_dst = NULL; 594 char *abs_dst = NULL; 595 char *tmp = NULL, *filename = NULL; 596 glob_t g; 597 int err = 0; 598 int i, dst_is_dir = 1; 599 struct stat sb; 600 601 if (dst) { 602 tmp_dst = xstrdup(dst); 603 tmp_dst = make_absolute(tmp_dst, pwd); 604 } 605 606 memset(&g, 0, sizeof(g)); 607 debug3("Looking up %s", src); 608 if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) { 609 error("File \"%s\" not found.", src); 610 err = -1; 611 goto out; 612 } 613 614 /* If we aren't fetching to pwd then stash this status for later */ 615 if (tmp_dst != NULL) 616 dst_is_dir = remote_is_dir(conn, tmp_dst); 617 618 /* If multiple matches, dst may be directory or unspecified */ 619 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) { 620 error("Multiple paths match, but destination " 621 "\"%s\" is not a directory", tmp_dst); 622 err = -1; 623 goto out; 624 } 625 626 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 627 if (stat(g.gl_pathv[i], &sb) == -1) { 628 err = -1; 629 error("stat %s: %s", g.gl_pathv[i], strerror(errno)); 630 continue; 631 } 632 633 tmp = xstrdup(g.gl_pathv[i]); 634 if ((filename = basename(tmp)) == NULL) { 635 error("basename %s: %s", tmp, strerror(errno)); 636 xfree(tmp); 637 err = -1; 638 goto out; 639 } 640 641 if (g.gl_matchc == 1 && tmp_dst) { 642 /* If directory specified, append filename */ 643 if (dst_is_dir) 644 abs_dst = path_append(tmp_dst, filename); 645 else 646 abs_dst = xstrdup(tmp_dst); 647 } else if (tmp_dst) { 648 abs_dst = path_append(tmp_dst, filename); 649 } else { 650 abs_dst = make_absolute(xstrdup(filename), pwd); 651 } 652 xfree(tmp); 653 654 printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 655 if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { 656 if (upload_dir(conn, g.gl_pathv[i], abs_dst, 657 pflag || global_pflag, 1) == -1) 658 err = -1; 659 } else { 660 if (do_upload(conn, g.gl_pathv[i], abs_dst, 661 pflag || global_pflag) == -1) 662 err = -1; 663 } 664 } 665 666out: 667 if (abs_dst) 668 xfree(abs_dst); 669 if (tmp_dst) 670 xfree(tmp_dst); 671 globfree(&g); 672 return(err); 673} 674 675static int 676sdirent_comp(const void *aa, const void *bb) 677{ 678 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; 679 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; 680 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; 681 682#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) 683 if (sort_flag & LS_NAME_SORT) 684 return (rmul * strcmp(a->filename, b->filename)); 685 else if (sort_flag & LS_TIME_SORT) 686 return (rmul * NCMP(a->a.mtime, b->a.mtime)); 687 else if (sort_flag & LS_SIZE_SORT) 688 return (rmul * NCMP(a->a.size, b->a.size)); 689 690 fatal("Unknown ls sort type"); 691} 692 693/* sftp ls.1 replacement for directories */ 694static int 695do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) 696{ 697 int n; 698 u_int c = 1, colspace = 0, columns = 1; 699 SFTP_DIRENT **d; 700 701 if ((n = do_readdir(conn, path, &d)) != 0) 702 return (n); 703 704 if (!(lflag & LS_SHORT_VIEW)) { 705 u_int m = 0, width = 80; 706 struct winsize ws; 707 char *tmp; 708 709 /* Count entries for sort and find longest filename */ 710 for (n = 0; d[n] != NULL; n++) { 711 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL)) 712 m = MAX(m, strlen(d[n]->filename)); 713 } 714 715 /* Add any subpath that also needs to be counted */ 716 tmp = path_strip(path, strip_path); 717 m += strlen(tmp); 718 xfree(tmp); 719 720 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 721 width = ws.ws_col; 722 723 columns = width / (m + 2); 724 columns = MAX(columns, 1); 725 colspace = width / columns; 726 colspace = MIN(colspace, width); 727 } 728 729 if (lflag & SORT_FLAGS) { 730 for (n = 0; d[n] != NULL; n++) 731 ; /* count entries */ 732 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); 733 qsort(d, n, sizeof(*d), sdirent_comp); 734 } 735 736 for (n = 0; d[n] != NULL && !interrupted; n++) { 737 char *tmp, *fname; 738 739 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL)) 740 continue; 741 742 tmp = path_append(path, d[n]->filename); 743 fname = path_strip(tmp, strip_path); 744 xfree(tmp); 745 746 if (lflag & LS_LONG_VIEW) { 747 if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) { 748 char *lname; 749 struct stat sb; 750 751 memset(&sb, 0, sizeof(sb)); 752 attrib_to_stat(&d[n]->a, &sb); 753 lname = ls_file(fname, &sb, 1, 754 (lflag & LS_SI_UNITS)); 755 printf("%s\n", lname); 756 xfree(lname); 757 } else 758 printf("%s\n", d[n]->longname); 759 } else { 760 printf("%-*s", colspace, fname); 761 if (c >= columns) { 762 printf("\n"); 763 c = 1; 764 } else 765 c++; 766 } 767 768 xfree(fname); 769 } 770 771 if (!(lflag & LS_LONG_VIEW) && (c != 1)) 772 printf("\n"); 773 774 free_sftp_dirents(d); 775 return (0); 776} 777 778/* sftp ls.1 replacement which handles path globs */ 779static int 780do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, 781 int lflag) 782{ 783 char *fname, *lname; 784 glob_t g; 785 int err; 786 struct winsize ws; 787 u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80; 788 789 memset(&g, 0, sizeof(g)); 790 791 if (remote_glob(conn, path, 792 GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT, 793 NULL, &g) || 794 (g.gl_pathc && !g.gl_matchc)) { 795 if (g.gl_pathc) 796 globfree(&g); 797 error("Can't ls: \"%s\" not found", path); 798 return -1; 799 } 800 801 if (interrupted) 802 goto out; 803 804 /* 805 * If the glob returns a single match and it is a directory, 806 * then just list its contents. 807 */ 808 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL && 809 S_ISDIR(g.gl_statv[0]->st_mode)) { 810 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); 811 globfree(&g); 812 return err; 813 } 814 815 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 816 width = ws.ws_col; 817 818 if (!(lflag & LS_SHORT_VIEW)) { 819 /* Count entries for sort and find longest filename */ 820 for (i = 0; g.gl_pathv[i]; i++) 821 m = MAX(m, strlen(g.gl_pathv[i])); 822 823 columns = width / (m + 2); 824 columns = MAX(columns, 1); 825 colspace = width / columns; 826 } 827 828 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 829 fname = path_strip(g.gl_pathv[i], strip_path); 830 if (lflag & LS_LONG_VIEW) { 831 if (g.gl_statv[i] == NULL) { 832 error("no stat information for %s", fname); 833 continue; 834 } 835 lname = ls_file(fname, g.gl_statv[i], 1, 836 (lflag & LS_SI_UNITS)); 837 printf("%s\n", lname); 838 xfree(lname); 839 } else { 840 printf("%-*s", colspace, fname); 841 if (c >= columns) { 842 printf("\n"); 843 c = 1; 844 } else 845 c++; 846 } 847 xfree(fname); 848 } 849 850 if (!(lflag & LS_LONG_VIEW) && (c != 1)) 851 printf("\n"); 852 853 out: 854 if (g.gl_pathc) 855 globfree(&g); 856 857 return 0; 858} 859 860static int 861do_df(struct sftp_conn *conn, char *path, int hflag, int iflag) 862{ 863 struct sftp_statvfs st; 864 char s_used[FMT_SCALED_STRSIZE]; 865 char s_avail[FMT_SCALED_STRSIZE]; 866 char s_root[FMT_SCALED_STRSIZE]; 867 char s_total[FMT_SCALED_STRSIZE]; 868 unsigned long long ffree; 869 870 if (do_statvfs(conn, path, &st, 1) == -1) 871 return -1; 872 if (iflag) { 873 ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0; 874 printf(" Inodes Used Avail " 875 "(root) %%Capacity\n"); 876 printf("%11llu %11llu %11llu %11llu %3llu%%\n", 877 (unsigned long long)st.f_files, 878 (unsigned long long)(st.f_files - st.f_ffree), 879 (unsigned long long)st.f_favail, 880 (unsigned long long)st.f_ffree, ffree); 881 } else if (hflag) { 882 strlcpy(s_used, "error", sizeof(s_used)); 883 strlcpy(s_avail, "error", sizeof(s_avail)); 884 strlcpy(s_root, "error", sizeof(s_root)); 885 strlcpy(s_total, "error", sizeof(s_total)); 886 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used); 887 fmt_scaled(st.f_bavail * st.f_frsize, s_avail); 888 fmt_scaled(st.f_bfree * st.f_frsize, s_root); 889 fmt_scaled(st.f_blocks * st.f_frsize, s_total); 890 printf(" Size Used Avail (root) %%Capacity\n"); 891 printf("%7sB %7sB %7sB %7sB %3llu%%\n", 892 s_total, s_used, s_avail, s_root, 893 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / 894 st.f_blocks)); 895 } else { 896 printf(" Size Used Avail " 897 "(root) %%Capacity\n"); 898 printf("%12llu %12llu %12llu %12llu %3llu%%\n", 899 (unsigned long long)(st.f_frsize * st.f_blocks / 1024), 900 (unsigned long long)(st.f_frsize * 901 (st.f_blocks - st.f_bfree) / 1024), 902 (unsigned long long)(st.f_frsize * st.f_bavail / 1024), 903 (unsigned long long)(st.f_frsize * st.f_bfree / 1024), 904 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / 905 st.f_blocks)); 906 } 907 return 0; 908} 909 910/* 911 * Undo escaping of glob sequences in place. Used to undo extra escaping 912 * applied in makeargv() when the string is destined for a function that 913 * does not glob it. 914 */ 915static void 916undo_glob_escape(char *s) 917{ 918 size_t i, j; 919 920 for (i = j = 0;;) { 921 if (s[i] == '\0') { 922 s[j] = '\0'; 923 return; 924 } 925 if (s[i] != '\\') { 926 s[j++] = s[i++]; 927 continue; 928 } 929 /* s[i] == '\\' */ 930 ++i; 931 switch (s[i]) { 932 case '?': 933 case '[': 934 case '*': 935 case '\\': 936 s[j++] = s[i++]; 937 break; 938 case '\0': 939 s[j++] = '\\'; 940 s[j] = '\0'; 941 return; 942 default: 943 s[j++] = '\\'; 944 s[j++] = s[i++]; 945 break; 946 } 947 } 948} 949 950/* 951 * Split a string into an argument vector using sh(1)-style quoting, 952 * comment and escaping rules, but with some tweaks to handle glob(3) 953 * wildcards. 954 * The "sloppy" flag allows for recovery from missing terminating quote, for 955 * use in parsing incomplete commandlines during tab autocompletion. 956 * 957 * Returns NULL on error or a NULL-terminated array of arguments. 958 * 959 * If "lastquote" is not NULL, the quoting character used for the last 960 * argument is placed in *lastquote ("\0", "'" or "\""). 961 * 962 * If "terminated" is not NULL, *terminated will be set to 1 when the 963 * last argument's quote has been properly terminated or 0 otherwise. 964 * This parameter is only of use if "sloppy" is set. 965 */ 966#define MAXARGS 128 967#define MAXARGLEN 8192 968static char ** 969makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, 970 u_int *terminated) 971{ 972 int argc, quot; 973 size_t i, j; 974 static char argvs[MAXARGLEN]; 975 static char *argv[MAXARGS + 1]; 976 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q; 977 978 *argcp = argc = 0; 979 if (strlen(arg) > sizeof(argvs) - 1) { 980 args_too_longs: 981 error("string too long"); 982 return NULL; 983 } 984 if (terminated != NULL) 985 *terminated = 1; 986 if (lastquote != NULL) 987 *lastquote = '\0'; 988 state = MA_START; 989 i = j = 0; 990 for (;;) { 991 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){ 992 error("Too many arguments."); 993 return NULL; 994 } 995 if (isspace(arg[i])) { 996 if (state == MA_UNQUOTED) { 997 /* Terminate current argument */ 998 argvs[j++] = '\0'; 999 argc++; 1000 state = MA_START; 1001 } else if (state != MA_START) 1002 argvs[j++] = arg[i]; 1003 } else if (arg[i] == '"' || arg[i] == '\'') { 1004 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE; 1005 if (state == MA_START) { 1006 argv[argc] = argvs + j; 1007 state = q; 1008 if (lastquote != NULL) 1009 *lastquote = arg[i]; 1010 } else if (state == MA_UNQUOTED) 1011 state = q; 1012 else if (state == q) 1013 state = MA_UNQUOTED; 1014 else 1015 argvs[j++] = arg[i]; 1016 } else if (arg[i] == '\\') { 1017 if (state == MA_SQUOTE || state == MA_DQUOTE) { 1018 quot = state == MA_SQUOTE ? '\'' : '"'; 1019 /* Unescape quote we are in */ 1020 /* XXX support \n and friends? */ 1021 if (arg[i + 1] == quot) { 1022 i++; 1023 argvs[j++] = arg[i]; 1024 } else if (arg[i + 1] == '?' || 1025 arg[i + 1] == '[' || arg[i + 1] == '*') { 1026 /* 1027 * Special case for sftp: append 1028 * double-escaped glob sequence - 1029 * glob will undo one level of 1030 * escaping. NB. string can grow here. 1031 */ 1032 if (j >= sizeof(argvs) - 5) 1033 goto args_too_longs; 1034 argvs[j++] = '\\'; 1035 argvs[j++] = arg[i++]; 1036 argvs[j++] = '\\'; 1037 argvs[j++] = arg[i]; 1038 } else { 1039 argvs[j++] = arg[i++]; 1040 argvs[j++] = arg[i]; 1041 } 1042 } else { 1043 if (state == MA_START) { 1044 argv[argc] = argvs + j; 1045 state = MA_UNQUOTED; 1046 if (lastquote != NULL) 1047 *lastquote = '\0'; 1048 } 1049 if (arg[i + 1] == '?' || arg[i + 1] == '[' || 1050 arg[i + 1] == '*' || arg[i + 1] == '\\') { 1051 /* 1052 * Special case for sftp: append 1053 * escaped glob sequence - 1054 * glob will undo one level of 1055 * escaping. 1056 */ 1057 argvs[j++] = arg[i++]; 1058 argvs[j++] = arg[i]; 1059 } else { 1060 /* Unescape everything */ 1061 /* XXX support \n and friends? */ 1062 i++; 1063 argvs[j++] = arg[i]; 1064 } 1065 } 1066 } else if (arg[i] == '#') { 1067 if (state == MA_SQUOTE || state == MA_DQUOTE) 1068 argvs[j++] = arg[i]; 1069 else 1070 goto string_done; 1071 } else if (arg[i] == '\0') { 1072 if (state == MA_SQUOTE || state == MA_DQUOTE) { 1073 if (sloppy) { 1074 state = MA_UNQUOTED; 1075 if (terminated != NULL) 1076 *terminated = 0; 1077 goto string_done; 1078 } 1079 error("Unterminated quoted argument"); 1080 return NULL; 1081 } 1082 string_done: 1083 if (state == MA_UNQUOTED) { 1084 argvs[j++] = '\0'; 1085 argc++; 1086 } 1087 break; 1088 } else { 1089 if (state == MA_START) { 1090 argv[argc] = argvs + j; 1091 state = MA_UNQUOTED; 1092 if (lastquote != NULL) 1093 *lastquote = '\0'; 1094 } 1095 if ((state == MA_SQUOTE || state == MA_DQUOTE) && 1096 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { 1097 /* 1098 * Special case for sftp: escape quoted 1099 * glob(3) wildcards. NB. string can grow 1100 * here. 1101 */ 1102 if (j >= sizeof(argvs) - 3) 1103 goto args_too_longs; 1104 argvs[j++] = '\\'; 1105 argvs[j++] = arg[i]; 1106 } else 1107 argvs[j++] = arg[i]; 1108 } 1109 i++; 1110 } 1111 *argcp = argc; 1112 return argv; 1113} 1114 1115static int 1116parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, 1117 int *hflag, int *sflag, unsigned long *n_arg, char **path1, char **path2) 1118{ 1119 const char *cmd, *cp = *cpp; 1120 char *cp2, **argv; 1121 int base = 0; 1122 long l; 1123 int i, cmdnum, optidx, argc; 1124 1125 /* Skip leading whitespace */ 1126 cp = cp + strspn(cp, WHITESPACE); 1127 1128 /* Check for leading '-' (disable error processing) */ 1129 *iflag = 0; 1130 if (*cp == '-') { 1131 *iflag = 1; 1132 cp++; 1133 cp = cp + strspn(cp, WHITESPACE); 1134 } 1135 1136 /* Ignore blank lines and lines which begin with comment '#' char */ 1137 if (*cp == '\0' || *cp == '#') 1138 return (0); 1139 1140 if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) 1141 return -1; 1142 1143 /* Figure out which command we have */ 1144 for (i = 0; cmds[i].c != NULL; i++) { 1145 if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0) 1146 break; 1147 } 1148 cmdnum = cmds[i].n; 1149 cmd = cmds[i].c; 1150 1151 /* Special case */ 1152 if (*cp == '!') { 1153 cp++; 1154 cmdnum = I_SHELL; 1155 } else if (cmdnum == -1) { 1156 error("Invalid command."); 1157 return -1; 1158 } 1159 1160 /* Get arguments and parse flags */ 1161 *lflag = *pflag = *rflag = *hflag = *n_arg = 0; 1162 *path1 = *path2 = NULL; 1163 optidx = 1; 1164 switch (cmdnum) { 1165 case I_GET: 1166 case I_PUT: 1167 if ((optidx = parse_getput_flags(cmd, argv, argc, 1168 pflag, rflag)) == -1) 1169 return -1; 1170 /* Get first pathname (mandatory) */ 1171 if (argc - optidx < 1) { 1172 error("You must specify at least one path after a " 1173 "%s command.", cmd); 1174 return -1; 1175 } 1176 *path1 = xstrdup(argv[optidx]); 1177 /* Get second pathname (optional) */ 1178 if (argc - optidx > 1) { 1179 *path2 = xstrdup(argv[optidx + 1]); 1180 /* Destination is not globbed */ 1181 undo_glob_escape(*path2); 1182 } 1183 break; 1184 case I_LINK: 1185 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1) 1186 return -1; 1187 case I_SYMLINK: 1188 case I_RENAME: 1189 if (argc - optidx < 2) { 1190 error("You must specify two paths after a %s " 1191 "command.", cmd); 1192 return -1; 1193 } 1194 *path1 = xstrdup(argv[optidx]); 1195 *path2 = xstrdup(argv[optidx + 1]); 1196 /* Paths are not globbed */ 1197 undo_glob_escape(*path1); 1198 undo_glob_escape(*path2); 1199 break; 1200 case I_RM: 1201 case I_MKDIR: 1202 case I_RMDIR: 1203 case I_CHDIR: 1204 case I_LCHDIR: 1205 case I_LMKDIR: 1206 /* Get pathname (mandatory) */ 1207 if (argc - optidx < 1) { 1208 error("You must specify a path after a %s command.", 1209 cmd); 1210 return -1; 1211 } 1212 *path1 = xstrdup(argv[optidx]); 1213 /* Only "rm" globs */ 1214 if (cmdnum != I_RM) 1215 undo_glob_escape(*path1); 1216 break; 1217 case I_DF: 1218 if ((optidx = parse_df_flags(cmd, argv, argc, hflag, 1219 iflag)) == -1) 1220 return -1; 1221 /* Default to current directory if no path specified */ 1222 if (argc - optidx < 1) 1223 *path1 = NULL; 1224 else { 1225 *path1 = xstrdup(argv[optidx]); 1226 undo_glob_escape(*path1); 1227 } 1228 break; 1229 case I_LS: 1230 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1) 1231 return(-1); 1232 /* Path is optional */ 1233 if (argc - optidx > 0) 1234 *path1 = xstrdup(argv[optidx]); 1235 break; 1236 case I_LLS: 1237 /* Skip ls command and following whitespace */ 1238 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE); 1239 case I_SHELL: 1240 /* Uses the rest of the line */ 1241 break; 1242 case I_LUMASK: 1243 case I_CHMOD: 1244 base = 8; 1245 case I_CHOWN: 1246 case I_CHGRP: 1247 /* Get numeric arg (mandatory) */ 1248 if (argc - optidx < 1) 1249 goto need_num_arg; 1250 errno = 0; 1251 l = strtol(argv[optidx], &cp2, base); 1252 if (cp2 == argv[optidx] || *cp2 != '\0' || 1253 ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) || 1254 l < 0) { 1255 need_num_arg: 1256 error("You must supply a numeric argument " 1257 "to the %s command.", cmd); 1258 return -1; 1259 } 1260 *n_arg = l; 1261 if (cmdnum == I_LUMASK) 1262 break; 1263 /* Get pathname (mandatory) */ 1264 if (argc - optidx < 2) { 1265 error("You must specify a path after a %s command.", 1266 cmd); 1267 return -1; 1268 } 1269 *path1 = xstrdup(argv[optidx + 1]); 1270 break; 1271 case I_QUIT: 1272 case I_PWD: 1273 case I_LPWD: 1274 case I_HELP: 1275 case I_VERSION: 1276 case I_PROGRESS: 1277 break; 1278 default: 1279 fatal("Command not implemented"); 1280 } 1281 1282 *cpp = cp; 1283 return(cmdnum); 1284} 1285 1286static int 1287parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 1288 int err_abort) 1289{ 1290 char *path1, *path2, *tmp; 1291 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0; 1292 int cmdnum, i; 1293 unsigned long n_arg = 0; 1294 Attrib a, *aa; 1295 char path_buf[MAXPATHLEN]; 1296 int err = 0; 1297 glob_t g; 1298 1299 path1 = path2 = NULL; 1300 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, 1301 &sflag, &n_arg, &path1, &path2); 1302 1303 if (iflag != 0) 1304 err_abort = 0; 1305 1306 memset(&g, 0, sizeof(g)); 1307 1308 /* Perform command */ 1309 switch (cmdnum) { 1310 case 0: 1311 /* Blank line */ 1312 break; 1313 case -1: 1314 /* Unrecognized command */ 1315 err = -1; 1316 break; 1317 case I_GET: 1318 err = process_get(conn, path1, path2, *pwd, pflag, rflag); 1319 break; 1320 case I_PUT: 1321 err = process_put(conn, path1, path2, *pwd, pflag, rflag); 1322 break; 1323 case I_RENAME: 1324 path1 = make_absolute(path1, *pwd); 1325 path2 = make_absolute(path2, *pwd); 1326 err = do_rename(conn, path1, path2); 1327 break; 1328 case I_SYMLINK: 1329 sflag = 1; 1330 case I_LINK: 1331 path1 = make_absolute(path1, *pwd); 1332 path2 = make_absolute(path2, *pwd); 1333 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2); 1334 break; 1335 case I_RM: 1336 path1 = make_absolute(path1, *pwd); 1337 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1338 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1339 printf("Removing %s\n", g.gl_pathv[i]); 1340 err = do_rm(conn, g.gl_pathv[i]); 1341 if (err != 0 && err_abort) 1342 break; 1343 } 1344 break; 1345 case I_MKDIR: 1346 path1 = make_absolute(path1, *pwd); 1347 attrib_clear(&a); 1348 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1349 a.perm = 0777; 1350 err = do_mkdir(conn, path1, &a, 1); 1351 break; 1352 case I_RMDIR: 1353 path1 = make_absolute(path1, *pwd); 1354 err = do_rmdir(conn, path1); 1355 break; 1356 case I_CHDIR: 1357 path1 = make_absolute(path1, *pwd); 1358 if ((tmp = do_realpath(conn, path1)) == NULL) { 1359 err = 1; 1360 break; 1361 } 1362 if ((aa = do_stat(conn, tmp, 0)) == NULL) { 1363 xfree(tmp); 1364 err = 1; 1365 break; 1366 } 1367 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { 1368 error("Can't change directory: Can't check target"); 1369 xfree(tmp); 1370 err = 1; 1371 break; 1372 } 1373 if (!S_ISDIR(aa->perm)) { 1374 error("Can't change directory: \"%s\" is not " 1375 "a directory", tmp); 1376 xfree(tmp); 1377 err = 1; 1378 break; 1379 } 1380 xfree(*pwd); 1381 *pwd = tmp; 1382 break; 1383 case I_LS: 1384 if (!path1) { 1385 do_ls_dir(conn, *pwd, *pwd, lflag); 1386 break; 1387 } 1388 1389 /* Strip pwd off beginning of non-absolute paths */ 1390 tmp = NULL; 1391 if (*path1 != '/') 1392 tmp = *pwd; 1393 1394 path1 = make_absolute(path1, *pwd); 1395 err = do_globbed_ls(conn, path1, tmp, lflag); 1396 break; 1397 case I_DF: 1398 /* Default to current directory if no path specified */ 1399 if (path1 == NULL) 1400 path1 = xstrdup(*pwd); 1401 path1 = make_absolute(path1, *pwd); 1402 err = do_df(conn, path1, hflag, iflag); 1403 break; 1404 case I_LCHDIR: 1405 if (chdir(path1) == -1) { 1406 error("Couldn't change local directory to " 1407 "\"%s\": %s", path1, strerror(errno)); 1408 err = 1; 1409 } 1410 break; 1411 case I_LMKDIR: 1412 if (mkdir(path1, 0777) == -1) { 1413 error("Couldn't create local directory " 1414 "\"%s\": %s", path1, strerror(errno)); 1415 err = 1; 1416 } 1417 break; 1418 case I_LLS: 1419 local_do_ls(cmd); 1420 break; 1421 case I_SHELL: 1422 local_do_shell(cmd); 1423 break; 1424 case I_LUMASK: 1425 umask(n_arg); 1426 printf("Local umask: %03lo\n", n_arg); 1427 break; 1428 case I_CHMOD: 1429 path1 = make_absolute(path1, *pwd); 1430 attrib_clear(&a); 1431 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1432 a.perm = n_arg; 1433 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1434 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1435 printf("Changing mode on %s\n", g.gl_pathv[i]); 1436 err = do_setstat(conn, g.gl_pathv[i], &a); 1437 if (err != 0 && err_abort) 1438 break; 1439 } 1440 break; 1441 case I_CHOWN: 1442 case I_CHGRP: 1443 path1 = make_absolute(path1, *pwd); 1444 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1445 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1446 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1447 if (err_abort) { 1448 err = -1; 1449 break; 1450 } else 1451 continue; 1452 } 1453 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1454 error("Can't get current ownership of " 1455 "remote file \"%s\"", g.gl_pathv[i]); 1456 if (err_abort) { 1457 err = -1; 1458 break; 1459 } else 1460 continue; 1461 } 1462 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1463 if (cmdnum == I_CHOWN) { 1464 printf("Changing owner on %s\n", g.gl_pathv[i]); 1465 aa->uid = n_arg; 1466 } else { 1467 printf("Changing group on %s\n", g.gl_pathv[i]); 1468 aa->gid = n_arg; 1469 } 1470 err = do_setstat(conn, g.gl_pathv[i], aa); 1471 if (err != 0 && err_abort) 1472 break; 1473 } 1474 break; 1475 case I_PWD: 1476 printf("Remote working directory: %s\n", *pwd); 1477 break; 1478 case I_LPWD: 1479 if (!getcwd(path_buf, sizeof(path_buf))) { 1480 error("Couldn't get local cwd: %s", strerror(errno)); 1481 err = -1; 1482 break; 1483 } 1484 printf("Local working directory: %s\n", path_buf); 1485 break; 1486 case I_QUIT: 1487 /* Processed below */ 1488 break; 1489 case I_HELP: 1490 help(); 1491 break; 1492 case I_VERSION: 1493 printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1494 break; 1495 case I_PROGRESS: 1496 showprogress = !showprogress; 1497 if (showprogress) 1498 printf("Progress meter enabled\n"); 1499 else 1500 printf("Progress meter disabled\n"); 1501 break; 1502 default: 1503 fatal("%d is not implemented", cmdnum); 1504 } 1505 1506 if (g.gl_pathc) 1507 globfree(&g); 1508 if (path1) 1509 xfree(path1); 1510 if (path2) 1511 xfree(path2); 1512 1513 /* If an unignored error occurs in batch mode we should abort. */ 1514 if (err_abort && err != 0) 1515 return (-1); 1516 else if (cmdnum == I_QUIT) 1517 return (1); 1518 1519 return (0); 1520} 1521 1522#ifdef USE_LIBEDIT 1523static char * 1524prompt(EditLine *el) 1525{ 1526 return ("sftp> "); 1527} 1528 1529/* Display entries in 'list' after skipping the first 'len' chars */ 1530static void 1531complete_display(char **list, u_int len) 1532{ 1533 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen; 1534 struct winsize ws; 1535 char *tmp; 1536 1537 /* Count entries for sort and find longest */ 1538 for (y = 0; list[y]; y++) 1539 m = MAX(m, strlen(list[y])); 1540 1541 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 1542 width = ws.ws_col; 1543 1544 m = m > len ? m - len : 0; 1545 columns = width / (m + 2); 1546 columns = MAX(columns, 1); 1547 colspace = width / columns; 1548 colspace = MIN(colspace, width); 1549 1550 printf("\n"); 1551 m = 1; 1552 for (y = 0; list[y]; y++) { 1553 llen = strlen(list[y]); 1554 tmp = llen > len ? list[y] + len : ""; 1555 printf("%-*s", colspace, tmp); 1556 if (m >= columns) { 1557 printf("\n"); 1558 m = 1; 1559 } else 1560 m++; 1561 } 1562 printf("\n"); 1563} 1564 1565/* 1566 * Given a "list" of words that begin with a common prefix of "word", 1567 * attempt to find an autocompletion to extends "word" by the next 1568 * characters common to all entries in "list". 1569 */ 1570static char * 1571complete_ambiguous(const char *word, char **list, size_t count) 1572{ 1573 if (word == NULL) 1574 return NULL; 1575 1576 if (count > 0) { 1577 u_int y, matchlen = strlen(list[0]); 1578 1579 /* Find length of common stem */ 1580 for (y = 1; list[y]; y++) { 1581 u_int x; 1582 1583 for (x = 0; x < matchlen; x++) 1584 if (list[0][x] != list[y][x]) 1585 break; 1586 1587 matchlen = x; 1588 } 1589 1590 if (matchlen > strlen(word)) { 1591 char *tmp = xstrdup(list[0]); 1592 1593 tmp[matchlen] = '\0'; 1594 return tmp; 1595 } 1596 } 1597 1598 return xstrdup(word); 1599} 1600 1601/* Autocomplete a sftp command */ 1602static int 1603complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, 1604 int terminated) 1605{ 1606 u_int y, count = 0, cmdlen, tmplen; 1607 char *tmp, **list, argterm[3]; 1608 const LineInfo *lf; 1609 1610 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *)); 1611 1612 /* No command specified: display all available commands */ 1613 if (cmd == NULL) { 1614 for (y = 0; cmds[y].c; y++) 1615 list[count++] = xstrdup(cmds[y].c); 1616 1617 list[count] = NULL; 1618 complete_display(list, 0); 1619 1620 for (y = 0; list[y] != NULL; y++) 1621 xfree(list[y]); 1622 xfree(list); 1623 return count; 1624 } 1625 1626 /* Prepare subset of commands that start with "cmd" */ 1627 cmdlen = strlen(cmd); 1628 for (y = 0; cmds[y].c; y++) { 1629 if (!strncasecmp(cmd, cmds[y].c, cmdlen)) 1630 list[count++] = xstrdup(cmds[y].c); 1631 } 1632 list[count] = NULL; 1633 1634 if (count == 0) { 1635 xfree(list); 1636 return 0; 1637 } 1638 1639 /* Complete ambigious command */ 1640 tmp = complete_ambiguous(cmd, list, count); 1641 if (count > 1) 1642 complete_display(list, 0); 1643 1644 for (y = 0; list[y]; y++) 1645 xfree(list[y]); 1646 xfree(list); 1647 1648 if (tmp != NULL) { 1649 tmplen = strlen(tmp); 1650 cmdlen = strlen(cmd); 1651 /* If cmd may be extended then do so */ 1652 if (tmplen > cmdlen) 1653 if (el_insertstr(el, tmp + cmdlen) == -1) 1654 fatal("el_insertstr failed."); 1655 lf = el_line(el); 1656 /* Terminate argument cleanly */ 1657 if (count == 1) { 1658 y = 0; 1659 if (!terminated) 1660 argterm[y++] = quote; 1661 if (lastarg || *(lf->cursor) != ' ') 1662 argterm[y++] = ' '; 1663 argterm[y] = '\0'; 1664 if (y > 0 && el_insertstr(el, argterm) == -1) 1665 fatal("el_insertstr failed."); 1666 } 1667 xfree(tmp); 1668 } 1669 1670 return count; 1671} 1672 1673/* 1674 * Determine whether a particular sftp command's arguments (if any) 1675 * represent local or remote files. 1676 */ 1677static int 1678complete_is_remote(char *cmd) { 1679 int i; 1680 1681 if (cmd == NULL) 1682 return -1; 1683 1684 for (i = 0; cmds[i].c; i++) { 1685 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) 1686 return cmds[i].t; 1687 } 1688 1689 return -1; 1690} 1691 1692/* Autocomplete a filename "file" */ 1693static int 1694complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, 1695 char *file, int remote, int lastarg, char quote, int terminated) 1696{ 1697 glob_t g; 1698 char *tmp, *tmp2, ins[3]; 1699 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs; 1700 const LineInfo *lf; 1701 1702 /* Glob from "file" location */ 1703 if (file == NULL) 1704 tmp = xstrdup("*"); 1705 else 1706 xasprintf(&tmp, "%s*", file); 1707 1708 /* Check if the path is absolute. */ 1709 isabs = tmp[0] == '/'; 1710 1711 memset(&g, 0, sizeof(g)); 1712 if (remote != LOCAL) { 1713 tmp = make_absolute(tmp, remote_path); 1714 remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); 1715 } else 1716 glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); 1717 1718 /* Determine length of pwd so we can trim completion display */ 1719 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) { 1720 /* Terminate counting on first unescaped glob metacharacter */ 1721 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') { 1722 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0') 1723 hadglob = 1; 1724 break; 1725 } 1726 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0') 1727 tmplen++; 1728 if (tmp[tmplen] == '/') 1729 pwdlen = tmplen + 1; /* track last seen '/' */ 1730 } 1731 xfree(tmp); 1732 1733 if (g.gl_matchc == 0) 1734 goto out; 1735 1736 if (g.gl_matchc > 1) 1737 complete_display(g.gl_pathv, pwdlen); 1738 1739 tmp = NULL; 1740 /* Don't try to extend globs */ 1741 if (file == NULL || hadglob) 1742 goto out; 1743 1744 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); 1745 tmp = path_strip(tmp2, isabs ? NULL : remote_path); 1746 xfree(tmp2); 1747 1748 if (tmp == NULL) 1749 goto out; 1750 1751 tmplen = strlen(tmp); 1752 filelen = strlen(file); 1753 1754 /* Count the number of escaped characters in the input string. */ 1755 cesc = isesc = 0; 1756 for (i = 0; i < filelen; i++) { 1757 if (!isesc && file[i] == '\\' && i + 1 < filelen){ 1758 isesc = 1; 1759 cesc++; 1760 } else 1761 isesc = 0; 1762 } 1763 1764 if (tmplen > (filelen - cesc)) { 1765 tmp2 = tmp + filelen - cesc; 1766 len = strlen(tmp2); 1767 /* quote argument on way out */ 1768 for (i = 0; i < len; i++) { 1769 ins[0] = '\\'; 1770 ins[1] = tmp2[i]; 1771 ins[2] = '\0'; 1772 switch (tmp2[i]) { 1773 case '\'': 1774 case '"': 1775 case '\\': 1776 case '\t': 1777 case '[': 1778 case ' ': 1779 case '#': 1780 case '*': 1781 if (quote == '\0' || tmp2[i] == quote) { 1782 if (el_insertstr(el, ins) == -1) 1783 fatal("el_insertstr " 1784 "failed."); 1785 break; 1786 } 1787 /* FALLTHROUGH */ 1788 default: 1789 if (el_insertstr(el, ins + 1) == -1) 1790 fatal("el_insertstr failed."); 1791 break; 1792 } 1793 } 1794 } 1795 1796 lf = el_line(el); 1797 if (g.gl_matchc == 1) { 1798 i = 0; 1799 if (!terminated) 1800 ins[i++] = quote; 1801 if (*(lf->cursor - 1) != '/' && 1802 (lastarg || *(lf->cursor) != ' ')) 1803 ins[i++] = ' '; 1804 ins[i] = '\0'; 1805 if (i > 0 && el_insertstr(el, ins) == -1) 1806 fatal("el_insertstr failed."); 1807 } 1808 xfree(tmp); 1809 1810 out: 1811 globfree(&g); 1812 return g.gl_matchc; 1813} 1814 1815/* tab-completion hook function, called via libedit */ 1816static unsigned char 1817complete(EditLine *el, int ch) 1818{ 1819 char **argv, *line, quote; 1820 u_int argc, carg, cursor, len, terminated, ret = CC_ERROR; 1821 const LineInfo *lf; 1822 struct complete_ctx *complete_ctx; 1823 1824 lf = el_line(el); 1825 if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0) 1826 fatal("%s: el_get failed", __func__); 1827 1828 /* Figure out which argument the cursor points to */ 1829 cursor = lf->cursor - lf->buffer; 1830 line = (char *)xmalloc(cursor + 1); 1831 memcpy(line, lf->buffer, cursor); 1832 line[cursor] = '\0'; 1833 argv = makeargv(line, &carg, 1, "e, &terminated); 1834 xfree(line); 1835 1836 /* Get all the arguments on the line */ 1837 len = lf->lastchar - lf->buffer; 1838 line = (char *)xmalloc(len + 1); 1839 memcpy(line, lf->buffer, len); 1840 line[len] = '\0'; 1841 argv = makeargv(line, &argc, 1, NULL, NULL); 1842 1843 /* Ensure cursor is at EOL or a argument boundary */ 1844 if (line[cursor] != ' ' && line[cursor] != '\0' && 1845 line[cursor] != '\n') { 1846 xfree(line); 1847 return ret; 1848 } 1849 1850 if (carg == 0) { 1851 /* Show all available commands */ 1852 complete_cmd_parse(el, NULL, argc == carg, '\0', 1); 1853 ret = CC_REDISPLAY; 1854 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') { 1855 /* Handle the command parsing */ 1856 if (complete_cmd_parse(el, argv[0], argc == carg, 1857 quote, terminated) != 0) 1858 ret = CC_REDISPLAY; 1859 } else if (carg >= 1) { 1860 /* Handle file parsing */ 1861 int remote = complete_is_remote(argv[0]); 1862 char *filematch = NULL; 1863 1864 if (carg > 1 && line[cursor-1] != ' ') 1865 filematch = argv[carg - 1]; 1866 1867 if (remote != 0 && 1868 complete_match(el, complete_ctx->conn, 1869 *complete_ctx->remote_pathp, filematch, 1870 remote, carg == argc, quote, terminated) != 0) 1871 ret = CC_REDISPLAY; 1872 } 1873 1874 xfree(line); 1875 return ret; 1876} 1877#endif /* USE_LIBEDIT */ 1878 1879int 1880interactive_loop(struct sftp_conn *conn, char *file1, char *file2) 1881{ 1882 char *remote_path; 1883 char *dir = NULL; 1884 char cmd[2048]; 1885 int err, interactive; 1886 EditLine *el = NULL; 1887#ifdef USE_LIBEDIT 1888 History *hl = NULL; 1889 HistEvent hev; 1890 extern char *__progname; 1891 struct complete_ctx complete_ctx; 1892 1893 if (!batchmode && isatty(STDIN_FILENO)) { 1894 if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) 1895 fatal("Couldn't initialise editline"); 1896 if ((hl = history_init()) == NULL) 1897 fatal("Couldn't initialise editline history"); 1898 history(hl, &hev, H_SETSIZE, 100); 1899 el_set(el, EL_HIST, history, hl); 1900 1901 el_set(el, EL_PROMPT, prompt); 1902 el_set(el, EL_EDITOR, "emacs"); 1903 el_set(el, EL_TERMINAL, NULL); 1904 el_set(el, EL_SIGNAL, 1); 1905 el_source(el, NULL); 1906 1907 /* Tab Completion */ 1908 el_set(el, EL_ADDFN, "ftp-complete", 1909 "Context sensitive argument completion", complete); 1910 complete_ctx.conn = conn; 1911 complete_ctx.remote_pathp = &remote_path; 1912 el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); 1913 el_set(el, EL_BIND, "^I", "ftp-complete", NULL); 1914 } 1915#endif /* USE_LIBEDIT */ 1916 1917 remote_path = do_realpath(conn, "."); 1918 if (remote_path == NULL) 1919 fatal("Need cwd"); 1920 1921 if (file1 != NULL) { 1922 dir = xstrdup(file1); 1923 dir = make_absolute(dir, remote_path); 1924 1925 if (remote_is_dir(conn, dir) && file2 == NULL) { 1926 printf("Changing to: %s\n", dir); 1927 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 1928 if (parse_dispatch_command(conn, cmd, 1929 &remote_path, 1) != 0) { 1930 xfree(dir); 1931 xfree(remote_path); 1932 xfree(conn); 1933 return (-1); 1934 } 1935 } else { 1936 /* XXX this is wrong wrt quoting */ 1937 if (file2 == NULL) 1938 snprintf(cmd, sizeof cmd, "get %s", dir); 1939 else 1940 snprintf(cmd, sizeof cmd, "get %s %s", dir, 1941 file2); 1942 1943 err = parse_dispatch_command(conn, cmd, 1944 &remote_path, 1); 1945 xfree(dir); 1946 xfree(remote_path); 1947 xfree(conn); 1948 return (err); 1949 } 1950 xfree(dir); 1951 } 1952 1953 setlinebuf(stdout); 1954 setlinebuf(infile); 1955 1956 interactive = !batchmode && isatty(STDIN_FILENO); 1957 err = 0; 1958 for (;;) { 1959 char *cp; 1960 1961 signal(SIGINT, SIG_IGN); 1962 1963 if (el == NULL) { 1964 if (interactive) 1965 printf("sftp> "); 1966 if (fgets(cmd, sizeof(cmd), infile) == NULL) { 1967 if (interactive) 1968 printf("\n"); 1969 break; 1970 } 1971 if (!interactive) { /* Echo command */ 1972 printf("sftp> %s", cmd); 1973 if (strlen(cmd) > 0 && 1974 cmd[strlen(cmd) - 1] != '\n') 1975 printf("\n"); 1976 } 1977 } else { 1978#ifdef USE_LIBEDIT 1979 const char *line; 1980 int count = 0; 1981 1982 if ((line = el_gets(el, &count)) == NULL || 1983 count <= 0) { 1984 printf("\n"); 1985 break; 1986 } 1987 history(hl, &hev, H_ENTER, line); 1988 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { 1989 fprintf(stderr, "Error: input line too long\n"); 1990 continue; 1991 } 1992#endif /* USE_LIBEDIT */ 1993 } 1994 1995 cp = strrchr(cmd, '\n'); 1996 if (cp) 1997 *cp = '\0'; 1998 1999 /* Handle user interrupts gracefully during commands */ 2000 interrupted = 0; 2001 signal(SIGINT, cmd_interrupt); 2002 2003 err = parse_dispatch_command(conn, cmd, &remote_path, 2004 batchmode); 2005 if (err != 0) 2006 break; 2007 } 2008 xfree(remote_path); 2009 xfree(conn); 2010 2011#ifdef USE_LIBEDIT 2012 if (el != NULL) 2013 el_end(el); 2014#endif /* USE_LIBEDIT */ 2015 2016 /* err == 1 signifies normal "quit" exit */ 2017 return (err >= 0 ? 0 : -1); 2018} 2019 2020static void 2021connect_to_server(char *path, char **args, int *in, int *out) 2022{ 2023 int c_in, c_out; 2024 2025#ifdef USE_PIPES 2026 int pin[2], pout[2]; 2027 2028 if ((pipe(pin) == -1) || (pipe(pout) == -1)) 2029 fatal("pipe: %s", strerror(errno)); 2030 *in = pin[0]; 2031 *out = pout[1]; 2032 c_in = pout[0]; 2033 c_out = pin[1]; 2034#else /* USE_PIPES */ 2035 int inout[2]; 2036 2037 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 2038 fatal("socketpair: %s", strerror(errno)); 2039 *in = *out = inout[0]; 2040 c_in = c_out = inout[1]; 2041#endif /* USE_PIPES */ 2042 2043 if ((sshpid = fork()) == -1) 2044 fatal("fork: %s", strerror(errno)); 2045 else if (sshpid == 0) { 2046 if ((dup2(c_in, STDIN_FILENO) == -1) || 2047 (dup2(c_out, STDOUT_FILENO) == -1)) { 2048 fprintf(stderr, "dup2: %s\n", strerror(errno)); 2049 _exit(1); 2050 } 2051 close(*in); 2052 close(*out); 2053 close(c_in); 2054 close(c_out); 2055 2056 /* 2057 * The underlying ssh is in the same process group, so we must 2058 * ignore SIGINT if we want to gracefully abort commands, 2059 * otherwise the signal will make it to the ssh process and 2060 * kill it too. Contrawise, since sftp sends SIGTERMs to the 2061 * underlying ssh, it must *not* ignore that signal. 2062 */ 2063 signal(SIGINT, SIG_IGN); 2064 signal(SIGTERM, SIG_DFL); 2065 execvp(path, args); 2066 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 2067 _exit(1); 2068 } 2069 2070 signal(SIGTERM, killchild); 2071 signal(SIGINT, killchild); 2072 signal(SIGHUP, killchild); 2073 close(c_in); 2074 close(c_out); 2075} 2076 2077static void 2078usage(void) 2079{ 2080 extern char *__progname; 2081 2082 fprintf(stderr, 2083 "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" 2084 " [-D sftp_server_path] [-F ssh_config] " 2085 "[-i identity_file] [-l limit]\n" 2086 " [-o ssh_option] [-P port] [-R num_requests] " 2087 "[-S program]\n" 2088 " [-s subsystem | sftp_server] host\n" 2089 " %s [user@]host[:file ...]\n" 2090 " %s [user@]host[:dir[/]]\n" 2091 " %s -b batchfile [user@]host\n", 2092 __progname, __progname, __progname, __progname); 2093 exit(1); 2094} 2095 2096int 2097main(int argc, char **argv) 2098{ 2099 int in, out, ch, err; 2100 char *host = NULL, *userhost, *cp, *file2 = NULL; 2101 int debug_level = 0, sshver = 2; 2102 char *file1 = NULL, *sftp_server = NULL; 2103 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 2104 const char *errstr; 2105 LogLevel ll = SYSLOG_LEVEL_INFO; 2106 arglist args; 2107 extern int optind; 2108 extern char *optarg; 2109 struct sftp_conn *conn; 2110 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; 2111 size_t num_requests = DEFAULT_NUM_REQUESTS; 2112 long long limit_kbps = 0; 2113 2114 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 2115 sanitise_stdfd(); 2116 2117 __progname = ssh_get_progname(argv[0]); 2118 memset(&args, '\0', sizeof(args)); 2119 args.list = NULL; 2120 addargs(&args, "%s", ssh_program); 2121 addargs(&args, "-oForwardX11 no"); 2122 addargs(&args, "-oForwardAgent no"); 2123 addargs(&args, "-oPermitLocalCommand no"); 2124 addargs(&args, "-oClearAllForwardings yes"); 2125 2126 ll = SYSLOG_LEVEL_INFO; 2127 infile = stdin; 2128 2129 while ((ch = getopt(argc, argv, 2130 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) { 2131 switch (ch) { 2132 /* Passed through to ssh(1) */ 2133 case '4': 2134 case '6': 2135 case 'C': 2136 addargs(&args, "-%c", ch); 2137 break; 2138 /* Passed through to ssh(1) with argument */ 2139 case 'F': 2140 case 'c': 2141 case 'i': 2142 case 'o': 2143 addargs(&args, "-%c", ch); 2144 addargs(&args, "%s", optarg); 2145 break; 2146 case 'q': 2147 showprogress = 0; 2148 addargs(&args, "-%c", ch); 2149 break; 2150 case 'P': 2151 addargs(&args, "-oPort %s", optarg); 2152 break; 2153 case 'v': 2154 if (debug_level < 3) { 2155 addargs(&args, "-v"); 2156 ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 2157 } 2158 debug_level++; 2159 break; 2160 case '1': 2161 sshver = 1; 2162 if (sftp_server == NULL) 2163 sftp_server = _PATH_SFTP_SERVER; 2164 break; 2165 case '2': 2166 sshver = 2; 2167 break; 2168 case 'B': 2169 copy_buffer_len = strtol(optarg, &cp, 10); 2170 if (copy_buffer_len == 0 || *cp != '\0') 2171 fatal("Invalid buffer size \"%s\"", optarg); 2172 break; 2173 case 'b': 2174 if (batchmode) 2175 fatal("Batch file already specified."); 2176 2177 /* Allow "-" as stdin */ 2178 if (strcmp(optarg, "-") != 0 && 2179 (infile = fopen(optarg, "r")) == NULL) 2180 fatal("%s (%s).", strerror(errno), optarg); 2181 showprogress = 0; 2182 batchmode = 1; 2183 addargs(&args, "-obatchmode yes"); 2184 break; 2185 case 'p': 2186 global_pflag = 1; 2187 break; 2188 case 'D': 2189 sftp_direct = optarg; 2190 break; 2191 case 'l': 2192 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, 2193 &errstr); 2194 if (errstr != NULL) 2195 usage(); 2196 limit_kbps *= 1024; /* kbps */ 2197 break; 2198 case 'r': 2199 global_rflag = 1; 2200 break; 2201 case 'R': 2202 num_requests = strtol(optarg, &cp, 10); 2203 if (num_requests == 0 || *cp != '\0') 2204 fatal("Invalid number of requests \"%s\"", 2205 optarg); 2206 break; 2207 case 's': 2208 sftp_server = optarg; 2209 break; 2210 case 'S': 2211 ssh_program = optarg; 2212 replacearg(&args, 0, "%s", ssh_program); 2213 break; 2214 case 'h': 2215 default: 2216 usage(); 2217 } 2218 } 2219 2220 if (!isatty(STDERR_FILENO)) 2221 showprogress = 0; 2222 2223 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 2224 2225 if (sftp_direct == NULL) { 2226 if (optind == argc || argc > (optind + 2)) 2227 usage(); 2228 2229 userhost = xstrdup(argv[optind]); 2230 file2 = argv[optind+1]; 2231 2232 if ((host = strrchr(userhost, '@')) == NULL) 2233 host = userhost; 2234 else { 2235 *host++ = '\0'; 2236 if (!userhost[0]) { 2237 fprintf(stderr, "Missing username\n"); 2238 usage(); 2239 } 2240 addargs(&args, "-l"); 2241 addargs(&args, "%s", userhost); 2242 } 2243 2244 if ((cp = colon(host)) != NULL) { 2245 *cp++ = '\0'; 2246 file1 = cp; 2247 } 2248 2249 host = cleanhostname(host); 2250 if (!*host) { 2251 fprintf(stderr, "Missing hostname\n"); 2252 usage(); 2253 } 2254 2255 addargs(&args, "-oProtocol %d", sshver); 2256 2257 /* no subsystem if the server-spec contains a '/' */ 2258 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 2259 addargs(&args, "-s"); 2260 2261 addargs(&args, "--"); 2262 addargs(&args, "%s", host); 2263 addargs(&args, "%s", (sftp_server != NULL ? 2264 sftp_server : "sftp")); 2265 2266 connect_to_server(ssh_program, args.list, &in, &out); 2267 } else { 2268 args.list = NULL; 2269 addargs(&args, "sftp-server"); 2270 2271 connect_to_server(sftp_direct, args.list, &in, &out); 2272 } 2273 freeargs(&args); 2274 2275 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps); 2276 if (conn == NULL) 2277 fatal("Couldn't initialise connection to server"); 2278 2279 if (!batchmode) { 2280 if (sftp_direct == NULL) 2281 fprintf(stderr, "Connected to %s.\n", host); 2282 else 2283 fprintf(stderr, "Attached to %s.\n", sftp_direct); 2284 } 2285 2286 err = interactive_loop(conn, file1, file2); 2287 2288#if !defined(USE_PIPES) 2289 shutdown(in, SHUT_RDWR); 2290 shutdown(out, SHUT_RDWR); 2291#endif 2292 2293 close(in); 2294 close(out); 2295 if (batchmode) 2296 fclose(infile); 2297 2298 while (waitpid(sshpid, NULL, 0) == -1) 2299 if (errno != EINTR) 2300 fatal("Couldn't wait for ssh process: %s", 2301 strerror(errno)); 2302 2303 exit(err == 0 ? 0 : 1); 2304} 2305