sftp.c revision 181111
1181111Sdes/* $OpenBSD: sftp.c,v 1.103 2008/07/13 22:16:03 djm Exp $ */ 276259Sgreen/* 3126274Sdes * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 476259Sgreen * 5126274Sdes * Permission to use, copy, modify, and distribute this software for any 6126274Sdes * purpose with or without fee is hereby granted, provided that the above 7126274Sdes * copyright notice and this permission notice appear in all copies. 876259Sgreen * 9126274Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10126274Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11126274Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12126274Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13126274Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14126274Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15126274Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1676259Sgreen */ 1776259Sgreen 1876259Sgreen#include "includes.h" 1976259Sgreen 20162852Sdes#include <sys/types.h> 21162852Sdes#include <sys/ioctl.h> 22162852Sdes#ifdef HAVE_SYS_STAT_H 23162852Sdes# include <sys/stat.h> 24162852Sdes#endif 25162852Sdes#include <sys/param.h> 26162852Sdes#include <sys/socket.h> 27162852Sdes#include <sys/wait.h> 28181111Sdes#ifdef HAVE_SYS_STATVFS_H 29181111Sdes#include <sys/statvfs.h> 30181111Sdes#endif 3176259Sgreen 32181111Sdes#include <ctype.h> 33162852Sdes#include <errno.h> 34162852Sdes 35162852Sdes#ifdef HAVE_PATHS_H 36162852Sdes# include <paths.h> 37162852Sdes#endif 38146998Sdes#ifdef USE_LIBEDIT 39146998Sdes#include <histedit.h> 40146998Sdes#else 41146998Sdestypedef void EditLine; 42146998Sdes#endif 43162852Sdes#include <signal.h> 44162852Sdes#include <stdlib.h> 45162852Sdes#include <stdio.h> 46162852Sdes#include <string.h> 47162852Sdes#include <unistd.h> 48162852Sdes#include <stdarg.h> 49146998Sdes 50181111Sdes#ifdef HAVE_UTIL_H 51181111Sdes# include <util.h> 52181111Sdes#endif 53181111Sdes 54181111Sdes#ifdef HAVE_LIBUTIL_H 55181111Sdes# include <libutil.h> 56181111Sdes#endif 57181111Sdes 5876259Sgreen#include "xmalloc.h" 5976259Sgreen#include "log.h" 6076259Sgreen#include "pathnames.h" 6192555Sdes#include "misc.h" 6276259Sgreen 6376259Sgreen#include "sftp.h" 64162852Sdes#include "buffer.h" 6576259Sgreen#include "sftp-common.h" 6676259Sgreen#include "sftp-client.h" 6776259Sgreen 68126274Sdes/* File to read commands from */ 69126274SdesFILE* infile; 70126274Sdes 71126274Sdes/* Are we in batchfile mode? */ 72126274Sdesint batchmode = 0; 73126274Sdes 74126274Sdes/* Size of buffer used when copying files */ 75126274Sdessize_t copy_buffer_len = 32768; 76126274Sdes 77126274Sdes/* Number of concurrent outstanding requests */ 78181111Sdessize_t num_requests = 64; 79126274Sdes 80126274Sdes/* PID of ssh transport process */ 81126274Sdesstatic pid_t sshpid = -1; 82126274Sdes 83126274Sdes/* This is set to 0 if the progressmeter is not desired. */ 84128456Sdesint showprogress = 1; 85126274Sdes 86137015Sdes/* SIGINT received during command processing */ 87137015Sdesvolatile sig_atomic_t interrupted = 0; 88137015Sdes 89137015Sdes/* I wish qsort() took a separate ctx for the comparison function...*/ 90137015Sdesint sort_flag; 91137015Sdes 92126274Sdesint remote_glob(struct sftp_conn *, const char *, int, 93126274Sdes int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 94126274Sdes 9598937Sdesextern char *__progname; 9698937Sdes 97126274Sdes/* Separators for interactive commands */ 98126274Sdes#define WHITESPACE " \t\r\n" 9976259Sgreen 100137015Sdes/* ls flags */ 101137015Sdes#define LS_LONG_VIEW 0x01 /* Full view ala ls -l */ 102137015Sdes#define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */ 103137015Sdes#define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */ 104137015Sdes#define LS_NAME_SORT 0x08 /* Sort by name (default) */ 105137015Sdes#define LS_TIME_SORT 0x10 /* Sort by mtime */ 106137015Sdes#define LS_SIZE_SORT 0x20 /* Sort by file size */ 107137015Sdes#define LS_REVERSE_SORT 0x40 /* Reverse sort order */ 108137015Sdes#define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */ 109113908Sdes 110137015Sdes#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW) 111137015Sdes#define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) 112137015Sdes 113126274Sdes/* Commands for interactive mode */ 114126274Sdes#define I_CHDIR 1 115126274Sdes#define I_CHGRP 2 116126274Sdes#define I_CHMOD 3 117126274Sdes#define I_CHOWN 4 118181111Sdes#define I_DF 24 119126274Sdes#define I_GET 5 120126274Sdes#define I_HELP 6 121126274Sdes#define I_LCHDIR 7 122126274Sdes#define I_LLS 8 123126274Sdes#define I_LMKDIR 9 124126274Sdes#define I_LPWD 10 125126274Sdes#define I_LS 11 126126274Sdes#define I_LUMASK 12 127126274Sdes#define I_MKDIR 13 128126274Sdes#define I_PUT 14 129126274Sdes#define I_PWD 15 130126274Sdes#define I_QUIT 16 131126274Sdes#define I_RENAME 17 132126274Sdes#define I_RM 18 133126274Sdes#define I_RMDIR 19 134126274Sdes#define I_SHELL 20 135126274Sdes#define I_SYMLINK 21 136126274Sdes#define I_VERSION 22 137126274Sdes#define I_PROGRESS 23 138126274Sdes 139126274Sdesstruct CMD { 140126274Sdes const char *c; 141126274Sdes const int n; 142126274Sdes}; 143126274Sdes 144126274Sdesstatic const struct CMD cmds[] = { 145126274Sdes { "bye", I_QUIT }, 146126274Sdes { "cd", I_CHDIR }, 147126274Sdes { "chdir", I_CHDIR }, 148126274Sdes { "chgrp", I_CHGRP }, 149126274Sdes { "chmod", I_CHMOD }, 150126274Sdes { "chown", I_CHOWN }, 151181111Sdes { "df", I_DF }, 152126274Sdes { "dir", I_LS }, 153126274Sdes { "exit", I_QUIT }, 154126274Sdes { "get", I_GET }, 155126274Sdes { "mget", I_GET }, 156126274Sdes { "help", I_HELP }, 157126274Sdes { "lcd", I_LCHDIR }, 158126274Sdes { "lchdir", I_LCHDIR }, 159126274Sdes { "lls", I_LLS }, 160126274Sdes { "lmkdir", I_LMKDIR }, 161126274Sdes { "ln", I_SYMLINK }, 162126274Sdes { "lpwd", I_LPWD }, 163126274Sdes { "ls", I_LS }, 164126274Sdes { "lumask", I_LUMASK }, 165126274Sdes { "mkdir", I_MKDIR }, 166126274Sdes { "progress", I_PROGRESS }, 167126274Sdes { "put", I_PUT }, 168126274Sdes { "mput", I_PUT }, 169126274Sdes { "pwd", I_PWD }, 170126274Sdes { "quit", I_QUIT }, 171126274Sdes { "rename", I_RENAME }, 172126274Sdes { "rm", I_RM }, 173126274Sdes { "rmdir", I_RMDIR }, 174126274Sdes { "symlink", I_SYMLINK }, 175126274Sdes { "version", I_VERSION }, 176126274Sdes { "!", I_SHELL }, 177126274Sdes { "?", I_HELP }, 178126274Sdes { NULL, -1} 179126274Sdes}; 180126274Sdes 181126274Sdesint interactive_loop(int fd_in, int fd_out, char *file1, char *file2); 182126274Sdes 183181111Sdes/* ARGSUSED */ 18492555Sdesstatic void 185137015Sdeskillchild(int signo) 186137015Sdes{ 187146998Sdes if (sshpid > 1) { 188137015Sdes kill(sshpid, SIGTERM); 189146998Sdes waitpid(sshpid, NULL, 0); 190146998Sdes } 191137015Sdes 192137015Sdes _exit(1); 193137015Sdes} 194137015Sdes 195181111Sdes/* ARGSUSED */ 196137015Sdesstatic void 197137015Sdescmd_interrupt(int signo) 198137015Sdes{ 199137015Sdes const char msg[] = "\rInterrupt \n"; 200146998Sdes int olderrno = errno; 201137015Sdes 202137015Sdes write(STDERR_FILENO, msg, sizeof(msg) - 1); 203137015Sdes interrupted = 1; 204146998Sdes errno = olderrno; 205137015Sdes} 206137015Sdes 207137015Sdesstatic void 208126274Sdeshelp(void) 209126274Sdes{ 210126274Sdes printf("Available commands:\n"); 211126274Sdes printf("cd path Change remote directory to 'path'\n"); 212126274Sdes printf("lcd path Change local directory to 'path'\n"); 213126274Sdes printf("chgrp grp path Change group of file 'path' to 'grp'\n"); 214126274Sdes printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); 215126274Sdes printf("chown own path Change owner of file 'path' to 'own'\n"); 216181111Sdes printf("df [path] Display statistics for current directory or\n"); 217181111Sdes printf(" filesystem containing 'path'\n"); 218126274Sdes printf("help Display this help text\n"); 219126274Sdes printf("get remote-path [local-path] Download file\n"); 220126274Sdes printf("lls [ls-options [path]] Display local directory listing\n"); 221126274Sdes printf("ln oldpath newpath Symlink remote file\n"); 222126274Sdes printf("lmkdir path Create local directory\n"); 223126274Sdes printf("lpwd Print local working directory\n"); 224126274Sdes printf("ls [path] Display remote directory listing\n"); 225126274Sdes printf("lumask umask Set local umask to 'umask'\n"); 226126274Sdes printf("mkdir path Create remote directory\n"); 227126274Sdes printf("progress Toggle display of progress meter\n"); 228126274Sdes printf("put local-path [remote-path] Upload file\n"); 229126274Sdes printf("pwd Display remote working directory\n"); 230126274Sdes printf("exit Quit sftp\n"); 231126274Sdes printf("quit Quit sftp\n"); 232126274Sdes printf("rename oldpath newpath Rename remote file\n"); 233126274Sdes printf("rmdir path Remove remote directory\n"); 234126274Sdes printf("rm path Delete remote file\n"); 235126274Sdes printf("symlink oldpath newpath Symlink remote file\n"); 236126274Sdes printf("version Show SFTP version\n"); 237126274Sdes printf("!command Execute 'command' in local shell\n"); 238126274Sdes printf("! Escape to local shell\n"); 239126274Sdes printf("? Synonym for help\n"); 240126274Sdes} 241126274Sdes 242126274Sdesstatic void 243126274Sdeslocal_do_shell(const char *args) 244126274Sdes{ 245126274Sdes int status; 246126274Sdes char *shell; 247126274Sdes pid_t pid; 248126274Sdes 249126274Sdes if (!*args) 250126274Sdes args = NULL; 251126274Sdes 252126274Sdes if ((shell = getenv("SHELL")) == NULL) 253126274Sdes shell = _PATH_BSHELL; 254126274Sdes 255126274Sdes if ((pid = fork()) == -1) 256126274Sdes fatal("Couldn't fork: %s", strerror(errno)); 257126274Sdes 258126274Sdes if (pid == 0) { 259126274Sdes /* XXX: child has pipe fds to ssh subproc open - issue? */ 260126274Sdes if (args) { 261126274Sdes debug3("Executing %s -c \"%s\"", shell, args); 262126274Sdes execl(shell, shell, "-c", args, (char *)NULL); 263126274Sdes } else { 264126274Sdes debug3("Executing %s", shell); 265126274Sdes execl(shell, shell, (char *)NULL); 266126274Sdes } 267126274Sdes fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, 268126274Sdes strerror(errno)); 269126274Sdes _exit(1); 270126274Sdes } 271126274Sdes while (waitpid(pid, &status, 0) == -1) 272126274Sdes if (errno != EINTR) 273126274Sdes fatal("Couldn't wait for child: %s", strerror(errno)); 274126274Sdes if (!WIFEXITED(status)) 275162852Sdes error("Shell exited abnormally"); 276126274Sdes else if (WEXITSTATUS(status)) 277126274Sdes error("Shell exited with status %d", WEXITSTATUS(status)); 278126274Sdes} 279126274Sdes 280126274Sdesstatic void 281126274Sdeslocal_do_ls(const char *args) 282126274Sdes{ 283126274Sdes if (!args || !*args) 284126274Sdes local_do_shell(_PATH_LS); 285126274Sdes else { 286126274Sdes int len = strlen(_PATH_LS " ") + strlen(args) + 1; 287126274Sdes char *buf = xmalloc(len); 288126274Sdes 289126274Sdes /* XXX: quoting - rip quoting code from ftp? */ 290126274Sdes snprintf(buf, len, _PATH_LS " %s", args); 291126274Sdes local_do_shell(buf); 292126274Sdes xfree(buf); 293126274Sdes } 294126274Sdes} 295126274Sdes 296126274Sdes/* Strip one path (usually the pwd) from the start of another */ 297126274Sdesstatic char * 298126274Sdespath_strip(char *path, char *strip) 299126274Sdes{ 300126274Sdes size_t len; 301126274Sdes 302126274Sdes if (strip == NULL) 303126274Sdes return (xstrdup(path)); 304126274Sdes 305126274Sdes len = strlen(strip); 306146998Sdes if (strncmp(path, strip, len) == 0) { 307126274Sdes if (strip[len - 1] != '/' && path[len] == '/') 308126274Sdes len++; 309126274Sdes return (xstrdup(path + len)); 310126274Sdes } 311126274Sdes 312126274Sdes return (xstrdup(path)); 313126274Sdes} 314126274Sdes 315126274Sdesstatic char * 316126274Sdespath_append(char *p1, char *p2) 317126274Sdes{ 318126274Sdes char *ret; 319181111Sdes size_t len = strlen(p1) + strlen(p2) + 2; 320126274Sdes 321126274Sdes ret = xmalloc(len); 322126274Sdes strlcpy(ret, p1, len); 323181111Sdes if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') 324126274Sdes strlcat(ret, "/", len); 325126274Sdes strlcat(ret, p2, len); 326126274Sdes 327126274Sdes return(ret); 328126274Sdes} 329126274Sdes 330126274Sdesstatic char * 331126274Sdesmake_absolute(char *p, char *pwd) 332126274Sdes{ 333137015Sdes char *abs_str; 334126274Sdes 335126274Sdes /* Derelativise */ 336126274Sdes if (p && p[0] != '/') { 337137015Sdes abs_str = path_append(pwd, p); 338126274Sdes xfree(p); 339137015Sdes return(abs_str); 340126274Sdes } else 341126274Sdes return(p); 342126274Sdes} 343126274Sdes 344126274Sdesstatic int 345126274Sdesinfer_path(const char *p, char **ifp) 346126274Sdes{ 347126274Sdes char *cp; 348126274Sdes 349126274Sdes cp = strrchr(p, '/'); 350126274Sdes if (cp == NULL) { 351126274Sdes *ifp = xstrdup(p); 352126274Sdes return(0); 353126274Sdes } 354126274Sdes 355126274Sdes if (!cp[1]) { 356126274Sdes error("Invalid path"); 357126274Sdes return(-1); 358126274Sdes } 359126274Sdes 360126274Sdes *ifp = xstrdup(cp + 1); 361126274Sdes return(0); 362126274Sdes} 363126274Sdes 364126274Sdesstatic int 365181111Sdesparse_getput_flags(const char *cmd, char **argv, int argc, int *pflag) 366126274Sdes{ 367181111Sdes extern int opterr, optind, optopt, optreset; 368181111Sdes int ch; 369126274Sdes 370181111Sdes optind = optreset = 1; 371181111Sdes opterr = 0; 372181111Sdes 373181111Sdes *pflag = 0; 374181111Sdes while ((ch = getopt(argc, argv, "Pp")) != -1) { 375181111Sdes switch (ch) { 376126274Sdes case 'p': 377126274Sdes case 'P': 378126274Sdes *pflag = 1; 379126274Sdes break; 380126274Sdes default: 381181111Sdes error("%s: Invalid flag -%c", cmd, optopt); 382181111Sdes return -1; 383126274Sdes } 384126274Sdes } 385126274Sdes 386181111Sdes return optind; 387126274Sdes} 388126274Sdes 389126274Sdesstatic int 390181111Sdesparse_ls_flags(char **argv, int argc, int *lflag) 391126274Sdes{ 392181111Sdes extern int opterr, optind, optopt, optreset; 393181111Sdes int ch; 394126274Sdes 395181111Sdes optind = optreset = 1; 396181111Sdes opterr = 0; 397181111Sdes 398137015Sdes *lflag = LS_NAME_SORT; 399181111Sdes while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) { 400181111Sdes switch (ch) { 401181111Sdes case '1': 402181111Sdes *lflag &= ~VIEW_FLAGS; 403181111Sdes *lflag |= LS_SHORT_VIEW; 404181111Sdes break; 405181111Sdes case 'S': 406181111Sdes *lflag &= ~SORT_FLAGS; 407181111Sdes *lflag |= LS_SIZE_SORT; 408181111Sdes break; 409181111Sdes case 'a': 410181111Sdes *lflag |= LS_SHOW_ALL; 411181111Sdes break; 412181111Sdes case 'f': 413181111Sdes *lflag &= ~SORT_FLAGS; 414181111Sdes break; 415181111Sdes case 'l': 416181111Sdes *lflag &= ~VIEW_FLAGS; 417181111Sdes *lflag |= LS_LONG_VIEW; 418181111Sdes break; 419181111Sdes case 'n': 420181111Sdes *lflag &= ~VIEW_FLAGS; 421181111Sdes *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; 422181111Sdes break; 423181111Sdes case 'r': 424181111Sdes *lflag |= LS_REVERSE_SORT; 425181111Sdes break; 426181111Sdes case 't': 427181111Sdes *lflag &= ~SORT_FLAGS; 428181111Sdes *lflag |= LS_TIME_SORT; 429181111Sdes break; 430181111Sdes default: 431181111Sdes error("ls: Invalid flag -%c", optopt); 432181111Sdes return -1; 433126274Sdes } 434126274Sdes } 435126274Sdes 436181111Sdes return optind; 437126274Sdes} 438126274Sdes 439126274Sdesstatic int 440181111Sdesparse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag) 441126274Sdes{ 442181111Sdes extern int opterr, optind, optopt, optreset; 443181111Sdes int ch; 444126274Sdes 445181111Sdes optind = optreset = 1; 446181111Sdes opterr = 0; 447126274Sdes 448181111Sdes *hflag = *iflag = 0; 449181111Sdes while ((ch = getopt(argc, argv, "hi")) != -1) { 450181111Sdes switch (ch) { 451181111Sdes case 'h': 452181111Sdes *hflag = 1; 453181111Sdes break; 454181111Sdes case 'i': 455181111Sdes *iflag = 1; 456181111Sdes break; 457181111Sdes default: 458181111Sdes error("%s: Invalid flag -%c", cmd, optopt); 459181111Sdes return -1; 460126274Sdes } 461126274Sdes } 462126274Sdes 463181111Sdes return optind; 464126274Sdes} 465126274Sdes 466126274Sdesstatic int 467126274Sdesis_dir(char *path) 468126274Sdes{ 469126274Sdes struct stat sb; 470126274Sdes 471126274Sdes /* XXX: report errors? */ 472126274Sdes if (stat(path, &sb) == -1) 473126274Sdes return(0); 474126274Sdes 475162852Sdes return(S_ISDIR(sb.st_mode)); 476126274Sdes} 477126274Sdes 478126274Sdesstatic int 479126274Sdesremote_is_dir(struct sftp_conn *conn, char *path) 480126274Sdes{ 481126274Sdes Attrib *a; 482126274Sdes 483126274Sdes /* XXX: report errors? */ 484126274Sdes if ((a = do_stat(conn, path, 1)) == NULL) 485126274Sdes return(0); 486126274Sdes if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) 487126274Sdes return(0); 488162852Sdes return(S_ISDIR(a->perm)); 489126274Sdes} 490126274Sdes 491126274Sdesstatic int 492126274Sdesprocess_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 493126274Sdes{ 494126274Sdes char *abs_src = NULL; 495126274Sdes char *abs_dst = NULL; 496126274Sdes char *tmp; 497126274Sdes glob_t g; 498126274Sdes int err = 0; 499126274Sdes int i; 500126274Sdes 501126274Sdes abs_src = xstrdup(src); 502126274Sdes abs_src = make_absolute(abs_src, pwd); 503126274Sdes 504126274Sdes memset(&g, 0, sizeof(g)); 505126274Sdes debug3("Looking up %s", abs_src); 506126274Sdes if (remote_glob(conn, abs_src, 0, NULL, &g)) { 507126274Sdes error("File \"%s\" not found.", abs_src); 508126274Sdes err = -1; 509126274Sdes goto out; 510126274Sdes } 511126274Sdes 512126274Sdes /* If multiple matches, dst must be a directory or unspecified */ 513126274Sdes if (g.gl_matchc > 1 && dst && !is_dir(dst)) { 514126274Sdes error("Multiple files match, but \"%s\" is not a directory", 515126274Sdes dst); 516126274Sdes err = -1; 517126274Sdes goto out; 518126274Sdes } 519126274Sdes 520137015Sdes for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 521126274Sdes if (infer_path(g.gl_pathv[i], &tmp)) { 522126274Sdes err = -1; 523126274Sdes goto out; 524126274Sdes } 525126274Sdes 526126274Sdes if (g.gl_matchc == 1 && dst) { 527126274Sdes /* If directory specified, append filename */ 528162852Sdes xfree(tmp); 529126274Sdes if (is_dir(dst)) { 530126274Sdes if (infer_path(g.gl_pathv[0], &tmp)) { 531126274Sdes err = 1; 532126274Sdes goto out; 533126274Sdes } 534126274Sdes abs_dst = path_append(dst, tmp); 535126274Sdes xfree(tmp); 536126274Sdes } else 537126274Sdes abs_dst = xstrdup(dst); 538126274Sdes } else if (dst) { 539126274Sdes abs_dst = path_append(dst, tmp); 540126274Sdes xfree(tmp); 541126274Sdes } else 542126274Sdes abs_dst = tmp; 543126274Sdes 544126274Sdes printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 545126274Sdes if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 546126274Sdes err = -1; 547126274Sdes xfree(abs_dst); 548126274Sdes abs_dst = NULL; 549126274Sdes } 550126274Sdes 551126274Sdesout: 552126274Sdes xfree(abs_src); 553126274Sdes globfree(&g); 554126274Sdes return(err); 555126274Sdes} 556126274Sdes 557126274Sdesstatic int 558126274Sdesprocess_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 559126274Sdes{ 560126274Sdes char *tmp_dst = NULL; 561126274Sdes char *abs_dst = NULL; 562126274Sdes char *tmp; 563126274Sdes glob_t g; 564126274Sdes int err = 0; 565126274Sdes int i; 566181111Sdes struct stat sb; 567126274Sdes 568126274Sdes if (dst) { 569126274Sdes tmp_dst = xstrdup(dst); 570126274Sdes tmp_dst = make_absolute(tmp_dst, pwd); 571126274Sdes } 572126274Sdes 573126274Sdes memset(&g, 0, sizeof(g)); 574126274Sdes debug3("Looking up %s", src); 575181111Sdes if (glob(src, GLOB_NOCHECK, NULL, &g)) { 576126274Sdes error("File \"%s\" not found.", src); 577126274Sdes err = -1; 578126274Sdes goto out; 579126274Sdes } 580126274Sdes 581126274Sdes /* If multiple matches, dst may be directory or unspecified */ 582126274Sdes if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { 583126274Sdes error("Multiple files match, but \"%s\" is not a directory", 584126274Sdes tmp_dst); 585126274Sdes err = -1; 586126274Sdes goto out; 587126274Sdes } 588126274Sdes 589137015Sdes for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 590181111Sdes if (stat(g.gl_pathv[i], &sb) == -1) { 591181111Sdes err = -1; 592181111Sdes error("stat %s: %s", g.gl_pathv[i], strerror(errno)); 593181111Sdes continue; 594181111Sdes } 595181111Sdes 596181111Sdes if (!S_ISREG(sb.st_mode)) { 597126274Sdes error("skipping non-regular file %s", 598126274Sdes g.gl_pathv[i]); 599126274Sdes continue; 600126274Sdes } 601126274Sdes if (infer_path(g.gl_pathv[i], &tmp)) { 602126274Sdes err = -1; 603126274Sdes goto out; 604126274Sdes } 605126274Sdes 606126274Sdes if (g.gl_matchc == 1 && tmp_dst) { 607126274Sdes /* If directory specified, append filename */ 608126274Sdes if (remote_is_dir(conn, tmp_dst)) { 609126274Sdes if (infer_path(g.gl_pathv[0], &tmp)) { 610126274Sdes err = 1; 611126274Sdes goto out; 612126274Sdes } 613126274Sdes abs_dst = path_append(tmp_dst, tmp); 614126274Sdes xfree(tmp); 615126274Sdes } else 616126274Sdes abs_dst = xstrdup(tmp_dst); 617126274Sdes 618126274Sdes } else if (tmp_dst) { 619126274Sdes abs_dst = path_append(tmp_dst, tmp); 620126274Sdes xfree(tmp); 621126274Sdes } else 622126274Sdes abs_dst = make_absolute(tmp, pwd); 623126274Sdes 624126274Sdes printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 625126274Sdes if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 626126274Sdes err = -1; 627126274Sdes } 628126274Sdes 629126274Sdesout: 630126274Sdes if (abs_dst) 631126274Sdes xfree(abs_dst); 632126274Sdes if (tmp_dst) 633126274Sdes xfree(tmp_dst); 634126274Sdes globfree(&g); 635126274Sdes return(err); 636126274Sdes} 637126274Sdes 638126274Sdesstatic int 639126274Sdessdirent_comp(const void *aa, const void *bb) 640126274Sdes{ 641126274Sdes SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; 642126274Sdes SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; 643137015Sdes int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; 644126274Sdes 645137015Sdes#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) 646137015Sdes if (sort_flag & LS_NAME_SORT) 647137015Sdes return (rmul * strcmp(a->filename, b->filename)); 648137015Sdes else if (sort_flag & LS_TIME_SORT) 649137015Sdes return (rmul * NCMP(a->a.mtime, b->a.mtime)); 650137015Sdes else if (sort_flag & LS_SIZE_SORT) 651137015Sdes return (rmul * NCMP(a->a.size, b->a.size)); 652137015Sdes 653137015Sdes fatal("Unknown ls sort type"); 654126274Sdes} 655126274Sdes 656126274Sdes/* sftp ls.1 replacement for directories */ 657126274Sdesstatic int 658126274Sdesdo_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) 659126274Sdes{ 660149749Sdes int n; 661149749Sdes u_int c = 1, colspace = 0, columns = 1; 662126274Sdes SFTP_DIRENT **d; 663126274Sdes 664126274Sdes if ((n = do_readdir(conn, path, &d)) != 0) 665126274Sdes return (n); 666126274Sdes 667137015Sdes if (!(lflag & LS_SHORT_VIEW)) { 668149749Sdes u_int m = 0, width = 80; 669126274Sdes struct winsize ws; 670126274Sdes char *tmp; 671126274Sdes 672126274Sdes /* Count entries for sort and find longest filename */ 673137015Sdes for (n = 0; d[n] != NULL; n++) { 674137015Sdes if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL)) 675137015Sdes m = MAX(m, strlen(d[n]->filename)); 676137015Sdes } 677126274Sdes 678126274Sdes /* Add any subpath that also needs to be counted */ 679126274Sdes tmp = path_strip(path, strip_path); 680126274Sdes m += strlen(tmp); 681126274Sdes xfree(tmp); 682126274Sdes 683126274Sdes if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 684126274Sdes width = ws.ws_col; 685126274Sdes 686126274Sdes columns = width / (m + 2); 687126274Sdes columns = MAX(columns, 1); 688126274Sdes colspace = width / columns; 689126274Sdes colspace = MIN(colspace, width); 690126274Sdes } 691126274Sdes 692137015Sdes if (lflag & SORT_FLAGS) { 693157016Sdes for (n = 0; d[n] != NULL; n++) 694157016Sdes ; /* count entries */ 695137015Sdes sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); 696137015Sdes qsort(d, n, sizeof(*d), sdirent_comp); 697137015Sdes } 698126274Sdes 699137015Sdes for (n = 0; d[n] != NULL && !interrupted; n++) { 700126274Sdes char *tmp, *fname; 701126274Sdes 702137015Sdes if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL)) 703137015Sdes continue; 704137015Sdes 705126274Sdes tmp = path_append(path, d[n]->filename); 706126274Sdes fname = path_strip(tmp, strip_path); 707126274Sdes xfree(tmp); 708126274Sdes 709137015Sdes if (lflag & LS_LONG_VIEW) { 710137015Sdes if (lflag & LS_NUMERIC_VIEW) { 711137015Sdes char *lname; 712137015Sdes struct stat sb; 713126274Sdes 714137015Sdes memset(&sb, 0, sizeof(sb)); 715137015Sdes attrib_to_stat(&d[n]->a, &sb); 716137015Sdes lname = ls_file(fname, &sb, 1); 717137015Sdes printf("%s\n", lname); 718137015Sdes xfree(lname); 719137015Sdes } else 720137015Sdes printf("%s\n", d[n]->longname); 721126274Sdes } else { 722126274Sdes printf("%-*s", colspace, fname); 723126274Sdes if (c >= columns) { 724126274Sdes printf("\n"); 725126274Sdes c = 1; 726126274Sdes } else 727126274Sdes c++; 728126274Sdes } 729126274Sdes 730126274Sdes xfree(fname); 731126274Sdes } 732126274Sdes 733137015Sdes if (!(lflag & LS_LONG_VIEW) && (c != 1)) 734126274Sdes printf("\n"); 735126274Sdes 736126274Sdes free_sftp_dirents(d); 737126274Sdes return (0); 738126274Sdes} 739126274Sdes 740126274Sdes/* sftp ls.1 replacement which handles path globs */ 741126274Sdesstatic int 742126274Sdesdo_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, 743126274Sdes int lflag) 744126274Sdes{ 745126274Sdes glob_t g; 746149749Sdes u_int i, c = 1, colspace = 0, columns = 1; 747146998Sdes Attrib *a = NULL; 748126274Sdes 749126274Sdes memset(&g, 0, sizeof(g)); 750126274Sdes 751126274Sdes if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, 752146998Sdes NULL, &g) || (g.gl_pathc && !g.gl_matchc)) { 753146998Sdes if (g.gl_pathc) 754146998Sdes globfree(&g); 755126274Sdes error("Can't ls: \"%s\" not found", path); 756126274Sdes return (-1); 757126274Sdes } 758126274Sdes 759137015Sdes if (interrupted) 760137015Sdes goto out; 761137015Sdes 762126274Sdes /* 763146998Sdes * If the glob returns a single match and it is a directory, 764146998Sdes * then just list its contents. 765126274Sdes */ 766146998Sdes if (g.gl_matchc == 1) { 767146998Sdes if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) { 768126274Sdes globfree(&g); 769126274Sdes return (-1); 770126274Sdes } 771126274Sdes if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 772126274Sdes S_ISDIR(a->perm)) { 773146998Sdes int err; 774146998Sdes 775146998Sdes err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); 776126274Sdes globfree(&g); 777146998Sdes return (err); 778126274Sdes } 779126274Sdes } 780126274Sdes 781137015Sdes if (!(lflag & LS_SHORT_VIEW)) { 782149749Sdes u_int m = 0, width = 80; 783126274Sdes struct winsize ws; 784126274Sdes 785126274Sdes /* Count entries for sort and find longest filename */ 786126274Sdes for (i = 0; g.gl_pathv[i]; i++) 787126274Sdes m = MAX(m, strlen(g.gl_pathv[i])); 788126274Sdes 789126274Sdes if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 790126274Sdes width = ws.ws_col; 791126274Sdes 792126274Sdes columns = width / (m + 2); 793126274Sdes columns = MAX(columns, 1); 794126274Sdes colspace = width / columns; 795126274Sdes } 796126274Sdes 797146998Sdes for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) { 798126274Sdes char *fname; 799126274Sdes 800126274Sdes fname = path_strip(g.gl_pathv[i], strip_path); 801126274Sdes 802137015Sdes if (lflag & LS_LONG_VIEW) { 803126274Sdes char *lname; 804126274Sdes struct stat sb; 805126274Sdes 806126274Sdes /* 807126274Sdes * XXX: this is slow - 1 roundtrip per path 808126274Sdes * A solution to this is to fork glob() and 809126274Sdes * build a sftp specific version which keeps the 810126274Sdes * attribs (which currently get thrown away) 811126274Sdes * that the server returns as well as the filenames. 812126274Sdes */ 813126274Sdes memset(&sb, 0, sizeof(sb)); 814146998Sdes if (a == NULL) 815146998Sdes a = do_lstat(conn, g.gl_pathv[i], 1); 816126274Sdes if (a != NULL) 817126274Sdes attrib_to_stat(a, &sb); 818126274Sdes lname = ls_file(fname, &sb, 1); 819126274Sdes printf("%s\n", lname); 820126274Sdes xfree(lname); 821126274Sdes } else { 822126274Sdes printf("%-*s", colspace, fname); 823126274Sdes if (c >= columns) { 824126274Sdes printf("\n"); 825126274Sdes c = 1; 826126274Sdes } else 827126274Sdes c++; 828126274Sdes } 829126274Sdes xfree(fname); 830126274Sdes } 831126274Sdes 832137015Sdes if (!(lflag & LS_LONG_VIEW) && (c != 1)) 833126274Sdes printf("\n"); 834126274Sdes 835137015Sdes out: 836126274Sdes if (g.gl_pathc) 837126274Sdes globfree(&g); 838126274Sdes 839126274Sdes return (0); 840126274Sdes} 841126274Sdes 842126274Sdesstatic int 843181111Sdesdo_df(struct sftp_conn *conn, char *path, int hflag, int iflag) 844181111Sdes{ 845181111Sdes struct sftp_statvfs st; 846181111Sdes char s_used[FMT_SCALED_STRSIZE]; 847181111Sdes char s_avail[FMT_SCALED_STRSIZE]; 848181111Sdes char s_root[FMT_SCALED_STRSIZE]; 849181111Sdes char s_total[FMT_SCALED_STRSIZE]; 850181111Sdes 851181111Sdes if (do_statvfs(conn, path, &st, 1) == -1) 852181111Sdes return -1; 853181111Sdes if (iflag) { 854181111Sdes printf(" Inodes Used Avail " 855181111Sdes "(root) %%Capacity\n"); 856181111Sdes printf("%11llu %11llu %11llu %11llu %3llu%%\n", 857181111Sdes (unsigned long long)st.f_files, 858181111Sdes (unsigned long long)(st.f_files - st.f_ffree), 859181111Sdes (unsigned long long)st.f_favail, 860181111Sdes (unsigned long long)st.f_ffree, 861181111Sdes (unsigned long long)(100 * (st.f_files - st.f_ffree) / 862181111Sdes st.f_files)); 863181111Sdes } else if (hflag) { 864181111Sdes strlcpy(s_used, "error", sizeof(s_used)); 865181111Sdes strlcpy(s_avail, "error", sizeof(s_avail)); 866181111Sdes strlcpy(s_root, "error", sizeof(s_root)); 867181111Sdes strlcpy(s_total, "error", sizeof(s_total)); 868181111Sdes fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used); 869181111Sdes fmt_scaled(st.f_bavail * st.f_frsize, s_avail); 870181111Sdes fmt_scaled(st.f_bfree * st.f_frsize, s_root); 871181111Sdes fmt_scaled(st.f_blocks * st.f_frsize, s_total); 872181111Sdes printf(" Size Used Avail (root) %%Capacity\n"); 873181111Sdes printf("%7sB %7sB %7sB %7sB %3llu%%\n", 874181111Sdes s_total, s_used, s_avail, s_root, 875181111Sdes (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / 876181111Sdes st.f_blocks)); 877181111Sdes } else { 878181111Sdes printf(" Size Used Avail " 879181111Sdes "(root) %%Capacity\n"); 880181111Sdes printf("%12llu %12llu %12llu %12llu %3llu%%\n", 881181111Sdes (unsigned long long)(st.f_frsize * st.f_blocks / 1024), 882181111Sdes (unsigned long long)(st.f_frsize * 883181111Sdes (st.f_blocks - st.f_bfree) / 1024), 884181111Sdes (unsigned long long)(st.f_frsize * st.f_bavail / 1024), 885181111Sdes (unsigned long long)(st.f_frsize * st.f_bfree / 1024), 886181111Sdes (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / 887181111Sdes st.f_blocks)); 888181111Sdes } 889181111Sdes return 0; 890181111Sdes} 891181111Sdes 892181111Sdes/* 893181111Sdes * Undo escaping of glob sequences in place. Used to undo extra escaping 894181111Sdes * applied in makeargv() when the string is destined for a function that 895181111Sdes * does not glob it. 896181111Sdes */ 897181111Sdesstatic void 898181111Sdesundo_glob_escape(char *s) 899181111Sdes{ 900181111Sdes size_t i, j; 901181111Sdes 902181111Sdes for (i = j = 0;;) { 903181111Sdes if (s[i] == '\0') { 904181111Sdes s[j] = '\0'; 905181111Sdes return; 906181111Sdes } 907181111Sdes if (s[i] != '\\') { 908181111Sdes s[j++] = s[i++]; 909181111Sdes continue; 910181111Sdes } 911181111Sdes /* s[i] == '\\' */ 912181111Sdes ++i; 913181111Sdes switch (s[i]) { 914181111Sdes case '?': 915181111Sdes case '[': 916181111Sdes case '*': 917181111Sdes case '\\': 918181111Sdes s[j++] = s[i++]; 919181111Sdes break; 920181111Sdes case '\0': 921181111Sdes s[j++] = '\\'; 922181111Sdes s[j] = '\0'; 923181111Sdes return; 924181111Sdes default: 925181111Sdes s[j++] = '\\'; 926181111Sdes s[j++] = s[i++]; 927181111Sdes break; 928181111Sdes } 929181111Sdes } 930181111Sdes} 931181111Sdes 932181111Sdes/* 933181111Sdes * Split a string into an argument vector using sh(1)-style quoting, 934181111Sdes * comment and escaping rules, but with some tweaks to handle glob(3) 935181111Sdes * wildcards. 936181111Sdes * Returns NULL on error or a NULL-terminated array of arguments. 937181111Sdes */ 938181111Sdes#define MAXARGS 128 939181111Sdes#define MAXARGLEN 8192 940181111Sdesstatic char ** 941181111Sdesmakeargv(const char *arg, int *argcp) 942181111Sdes{ 943181111Sdes int argc, quot; 944181111Sdes size_t i, j; 945181111Sdes static char argvs[MAXARGLEN]; 946181111Sdes static char *argv[MAXARGS + 1]; 947181111Sdes enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q; 948181111Sdes 949181111Sdes *argcp = argc = 0; 950181111Sdes if (strlen(arg) > sizeof(argvs) - 1) { 951181111Sdes args_too_longs: 952181111Sdes error("string too long"); 953181111Sdes return NULL; 954181111Sdes } 955181111Sdes state = MA_START; 956181111Sdes i = j = 0; 957181111Sdes for (;;) { 958181111Sdes if (isspace(arg[i])) { 959181111Sdes if (state == MA_UNQUOTED) { 960181111Sdes /* Terminate current argument */ 961181111Sdes argvs[j++] = '\0'; 962181111Sdes argc++; 963181111Sdes state = MA_START; 964181111Sdes } else if (state != MA_START) 965181111Sdes argvs[j++] = arg[i]; 966181111Sdes } else if (arg[i] == '"' || arg[i] == '\'') { 967181111Sdes q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE; 968181111Sdes if (state == MA_START) { 969181111Sdes argv[argc] = argvs + j; 970181111Sdes state = q; 971181111Sdes } else if (state == MA_UNQUOTED) 972181111Sdes state = q; 973181111Sdes else if (state == q) 974181111Sdes state = MA_UNQUOTED; 975181111Sdes else 976181111Sdes argvs[j++] = arg[i]; 977181111Sdes } else if (arg[i] == '\\') { 978181111Sdes if (state == MA_SQUOTE || state == MA_DQUOTE) { 979181111Sdes quot = state == MA_SQUOTE ? '\'' : '"'; 980181111Sdes /* Unescape quote we are in */ 981181111Sdes /* XXX support \n and friends? */ 982181111Sdes if (arg[i + 1] == quot) { 983181111Sdes i++; 984181111Sdes argvs[j++] = arg[i]; 985181111Sdes } else if (arg[i + 1] == '?' || 986181111Sdes arg[i + 1] == '[' || arg[i + 1] == '*') { 987181111Sdes /* 988181111Sdes * Special case for sftp: append 989181111Sdes * double-escaped glob sequence - 990181111Sdes * glob will undo one level of 991181111Sdes * escaping. NB. string can grow here. 992181111Sdes */ 993181111Sdes if (j >= sizeof(argvs) - 5) 994181111Sdes goto args_too_longs; 995181111Sdes argvs[j++] = '\\'; 996181111Sdes argvs[j++] = arg[i++]; 997181111Sdes argvs[j++] = '\\'; 998181111Sdes argvs[j++] = arg[i]; 999181111Sdes } else { 1000181111Sdes argvs[j++] = arg[i++]; 1001181111Sdes argvs[j++] = arg[i]; 1002181111Sdes } 1003181111Sdes } else { 1004181111Sdes if (state == MA_START) { 1005181111Sdes argv[argc] = argvs + j; 1006181111Sdes state = MA_UNQUOTED; 1007181111Sdes } 1008181111Sdes if (arg[i + 1] == '?' || arg[i + 1] == '[' || 1009181111Sdes arg[i + 1] == '*' || arg[i + 1] == '\\') { 1010181111Sdes /* 1011181111Sdes * Special case for sftp: append 1012181111Sdes * escaped glob sequence - 1013181111Sdes * glob will undo one level of 1014181111Sdes * escaping. 1015181111Sdes */ 1016181111Sdes argvs[j++] = arg[i++]; 1017181111Sdes argvs[j++] = arg[i]; 1018181111Sdes } else { 1019181111Sdes /* Unescape everything */ 1020181111Sdes /* XXX support \n and friends? */ 1021181111Sdes i++; 1022181111Sdes argvs[j++] = arg[i]; 1023181111Sdes } 1024181111Sdes } 1025181111Sdes } else if (arg[i] == '#') { 1026181111Sdes if (state == MA_SQUOTE || state == MA_DQUOTE) 1027181111Sdes argvs[j++] = arg[i]; 1028181111Sdes else 1029181111Sdes goto string_done; 1030181111Sdes } else if (arg[i] == '\0') { 1031181111Sdes if (state == MA_SQUOTE || state == MA_DQUOTE) { 1032181111Sdes error("Unterminated quoted argument"); 1033181111Sdes return NULL; 1034181111Sdes } 1035181111Sdes string_done: 1036181111Sdes if (state == MA_UNQUOTED) { 1037181111Sdes argvs[j++] = '\0'; 1038181111Sdes argc++; 1039181111Sdes } 1040181111Sdes break; 1041181111Sdes } else { 1042181111Sdes if (state == MA_START) { 1043181111Sdes argv[argc] = argvs + j; 1044181111Sdes state = MA_UNQUOTED; 1045181111Sdes } 1046181111Sdes if ((state == MA_SQUOTE || state == MA_DQUOTE) && 1047181111Sdes (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { 1048181111Sdes /* 1049181111Sdes * Special case for sftp: escape quoted 1050181111Sdes * glob(3) wildcards. NB. string can grow 1051181111Sdes * here. 1052181111Sdes */ 1053181111Sdes if (j >= sizeof(argvs) - 3) 1054181111Sdes goto args_too_longs; 1055181111Sdes argvs[j++] = '\\'; 1056181111Sdes argvs[j++] = arg[i]; 1057181111Sdes } else 1058181111Sdes argvs[j++] = arg[i]; 1059181111Sdes } 1060181111Sdes i++; 1061181111Sdes } 1062181111Sdes *argcp = argc; 1063181111Sdes return argv; 1064181111Sdes} 1065181111Sdes 1066181111Sdesstatic int 1067181111Sdesparse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, 1068126274Sdes unsigned long *n_arg, char **path1, char **path2) 1069126274Sdes{ 1070126274Sdes const char *cmd, *cp = *cpp; 1071181111Sdes char *cp2, **argv; 1072126274Sdes int base = 0; 1073126274Sdes long l; 1074181111Sdes int i, cmdnum, optidx, argc; 1075126274Sdes 1076126274Sdes /* Skip leading whitespace */ 1077126274Sdes cp = cp + strspn(cp, WHITESPACE); 1078126274Sdes 1079126274Sdes /* Ignore blank lines and lines which begin with comment '#' char */ 1080126274Sdes if (*cp == '\0' || *cp == '#') 1081126274Sdes return (0); 1082126274Sdes 1083126274Sdes /* Check for leading '-' (disable error processing) */ 1084126274Sdes *iflag = 0; 1085126274Sdes if (*cp == '-') { 1086126274Sdes *iflag = 1; 1087126274Sdes cp++; 1088126274Sdes } 1089126274Sdes 1090181111Sdes if ((argv = makeargv(cp, &argc)) == NULL) 1091181111Sdes return -1; 1092181111Sdes 1093126274Sdes /* Figure out which command we have */ 1094181111Sdes for (i = 0; cmds[i].c != NULL; i++) { 1095181111Sdes if (strcasecmp(cmds[i].c, argv[0]) == 0) 1096126274Sdes break; 1097126274Sdes } 1098126274Sdes cmdnum = cmds[i].n; 1099126274Sdes cmd = cmds[i].c; 1100126274Sdes 1101126274Sdes /* Special case */ 1102126274Sdes if (*cp == '!') { 1103126274Sdes cp++; 1104126274Sdes cmdnum = I_SHELL; 1105126274Sdes } else if (cmdnum == -1) { 1106126274Sdes error("Invalid command."); 1107181111Sdes return -1; 1108126274Sdes } 1109126274Sdes 1110126274Sdes /* Get arguments and parse flags */ 1111181111Sdes *lflag = *pflag = *hflag = *n_arg = 0; 1112126274Sdes *path1 = *path2 = NULL; 1113181111Sdes optidx = 1; 1114126274Sdes switch (cmdnum) { 1115126274Sdes case I_GET: 1116126274Sdes case I_PUT: 1117181111Sdes if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1) 1118181111Sdes return -1; 1119126274Sdes /* Get first pathname (mandatory) */ 1120181111Sdes if (argc - optidx < 1) { 1121126274Sdes error("You must specify at least one path after a " 1122126274Sdes "%s command.", cmd); 1123181111Sdes return -1; 1124126274Sdes } 1125181111Sdes *path1 = xstrdup(argv[optidx]); 1126181111Sdes /* Get second pathname (optional) */ 1127181111Sdes if (argc - optidx > 1) { 1128181111Sdes *path2 = xstrdup(argv[optidx + 1]); 1129181111Sdes /* Destination is not globbed */ 1130181111Sdes undo_glob_escape(*path2); 1131181111Sdes } 1132126274Sdes break; 1133126274Sdes case I_RENAME: 1134126274Sdes case I_SYMLINK: 1135181111Sdes if (argc - optidx < 2) { 1136126274Sdes error("You must specify two paths after a %s " 1137126274Sdes "command.", cmd); 1138181111Sdes return -1; 1139126274Sdes } 1140181111Sdes *path1 = xstrdup(argv[optidx]); 1141181111Sdes *path2 = xstrdup(argv[optidx + 1]); 1142181111Sdes /* Paths are not globbed */ 1143181111Sdes undo_glob_escape(*path1); 1144181111Sdes undo_glob_escape(*path2); 1145126274Sdes break; 1146126274Sdes case I_RM: 1147126274Sdes case I_MKDIR: 1148126274Sdes case I_RMDIR: 1149126274Sdes case I_CHDIR: 1150126274Sdes case I_LCHDIR: 1151126274Sdes case I_LMKDIR: 1152126274Sdes /* Get pathname (mandatory) */ 1153181111Sdes if (argc - optidx < 1) { 1154126274Sdes error("You must specify a path after a %s command.", 1155126274Sdes cmd); 1156181111Sdes return -1; 1157126274Sdes } 1158181111Sdes *path1 = xstrdup(argv[optidx]); 1159181111Sdes /* Only "rm" globs */ 1160181111Sdes if (cmdnum != I_RM) 1161181111Sdes undo_glob_escape(*path1); 1162126274Sdes break; 1163181111Sdes case I_DF: 1164181111Sdes if ((optidx = parse_df_flags(cmd, argv, argc, hflag, 1165181111Sdes iflag)) == -1) 1166181111Sdes return -1; 1167181111Sdes /* Default to current directory if no path specified */ 1168181111Sdes if (argc - optidx < 1) 1169181111Sdes *path1 = NULL; 1170181111Sdes else { 1171181111Sdes *path1 = xstrdup(argv[optidx]); 1172181111Sdes undo_glob_escape(*path1); 1173181111Sdes } 1174181111Sdes break; 1175126274Sdes case I_LS: 1176181111Sdes if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1) 1177126274Sdes return(-1); 1178126274Sdes /* Path is optional */ 1179181111Sdes if (argc - optidx > 0) 1180181111Sdes *path1 = xstrdup(argv[optidx]); 1181126274Sdes break; 1182126274Sdes case I_LLS: 1183181111Sdes /* Skip ls command and following whitespace */ 1184181111Sdes cp = cp + strlen(cmd) + strspn(cp, WHITESPACE); 1185126274Sdes case I_SHELL: 1186126274Sdes /* Uses the rest of the line */ 1187126274Sdes break; 1188126274Sdes case I_LUMASK: 1189126274Sdes case I_CHMOD: 1190126274Sdes base = 8; 1191126274Sdes case I_CHOWN: 1192126274Sdes case I_CHGRP: 1193126274Sdes /* Get numeric arg (mandatory) */ 1194181111Sdes if (argc - optidx < 1) 1195181111Sdes goto need_num_arg; 1196164146Sdes errno = 0; 1197181111Sdes l = strtol(argv[optidx], &cp2, base); 1198181111Sdes if (cp2 == argv[optidx] || *cp2 != '\0' || 1199181111Sdes ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) || 1200181111Sdes l < 0) { 1201181111Sdes need_num_arg: 1202126274Sdes error("You must supply a numeric argument " 1203126274Sdes "to the %s command.", cmd); 1204181111Sdes return -1; 1205126274Sdes } 1206126274Sdes *n_arg = l; 1207181111Sdes if (cmdnum == I_LUMASK) 1208126274Sdes break; 1209126274Sdes /* Get pathname (mandatory) */ 1210181111Sdes if (argc - optidx < 2) { 1211126274Sdes error("You must specify a path after a %s command.", 1212126274Sdes cmd); 1213181111Sdes return -1; 1214126274Sdes } 1215181111Sdes *path1 = xstrdup(argv[optidx + 1]); 1216126274Sdes break; 1217126274Sdes case I_QUIT: 1218126274Sdes case I_PWD: 1219126274Sdes case I_LPWD: 1220126274Sdes case I_HELP: 1221126274Sdes case I_VERSION: 1222126274Sdes case I_PROGRESS: 1223126274Sdes break; 1224126274Sdes default: 1225126274Sdes fatal("Command not implemented"); 1226126274Sdes } 1227126274Sdes 1228126274Sdes *cpp = cp; 1229126274Sdes return(cmdnum); 1230126274Sdes} 1231126274Sdes 1232126274Sdesstatic int 1233126274Sdesparse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 1234126274Sdes int err_abort) 1235126274Sdes{ 1236126274Sdes char *path1, *path2, *tmp; 1237181111Sdes int pflag, lflag, iflag, hflag, cmdnum, i; 1238126274Sdes unsigned long n_arg; 1239126274Sdes Attrib a, *aa; 1240126274Sdes char path_buf[MAXPATHLEN]; 1241126274Sdes int err = 0; 1242126274Sdes glob_t g; 1243126274Sdes 1244126274Sdes path1 = path2 = NULL; 1245181111Sdes cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg, 1246126274Sdes &path1, &path2); 1247126274Sdes 1248126274Sdes if (iflag != 0) 1249126274Sdes err_abort = 0; 1250126274Sdes 1251126274Sdes memset(&g, 0, sizeof(g)); 1252126274Sdes 1253126274Sdes /* Perform command */ 1254126274Sdes switch (cmdnum) { 1255126274Sdes case 0: 1256126274Sdes /* Blank line */ 1257126274Sdes break; 1258126274Sdes case -1: 1259126274Sdes /* Unrecognized command */ 1260126274Sdes err = -1; 1261126274Sdes break; 1262126274Sdes case I_GET: 1263126274Sdes err = process_get(conn, path1, path2, *pwd, pflag); 1264126274Sdes break; 1265126274Sdes case I_PUT: 1266126274Sdes err = process_put(conn, path1, path2, *pwd, pflag); 1267126274Sdes break; 1268126274Sdes case I_RENAME: 1269126274Sdes path1 = make_absolute(path1, *pwd); 1270126274Sdes path2 = make_absolute(path2, *pwd); 1271126274Sdes err = do_rename(conn, path1, path2); 1272126274Sdes break; 1273126274Sdes case I_SYMLINK: 1274126274Sdes path2 = make_absolute(path2, *pwd); 1275126274Sdes err = do_symlink(conn, path1, path2); 1276126274Sdes break; 1277126274Sdes case I_RM: 1278126274Sdes path1 = make_absolute(path1, *pwd); 1279126274Sdes remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1280137015Sdes for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1281126274Sdes printf("Removing %s\n", g.gl_pathv[i]); 1282126274Sdes err = do_rm(conn, g.gl_pathv[i]); 1283126274Sdes if (err != 0 && err_abort) 1284126274Sdes break; 1285126274Sdes } 1286126274Sdes break; 1287126274Sdes case I_MKDIR: 1288126274Sdes path1 = make_absolute(path1, *pwd); 1289126274Sdes attrib_clear(&a); 1290126274Sdes a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1291126274Sdes a.perm = 0777; 1292126274Sdes err = do_mkdir(conn, path1, &a); 1293126274Sdes break; 1294126274Sdes case I_RMDIR: 1295126274Sdes path1 = make_absolute(path1, *pwd); 1296126274Sdes err = do_rmdir(conn, path1); 1297126274Sdes break; 1298126274Sdes case I_CHDIR: 1299126274Sdes path1 = make_absolute(path1, *pwd); 1300126274Sdes if ((tmp = do_realpath(conn, path1)) == NULL) { 1301126274Sdes err = 1; 1302126274Sdes break; 1303126274Sdes } 1304126274Sdes if ((aa = do_stat(conn, tmp, 0)) == NULL) { 1305126274Sdes xfree(tmp); 1306126274Sdes err = 1; 1307126274Sdes break; 1308126274Sdes } 1309126274Sdes if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { 1310126274Sdes error("Can't change directory: Can't check target"); 1311126274Sdes xfree(tmp); 1312126274Sdes err = 1; 1313126274Sdes break; 1314126274Sdes } 1315126274Sdes if (!S_ISDIR(aa->perm)) { 1316126274Sdes error("Can't change directory: \"%s\" is not " 1317126274Sdes "a directory", tmp); 1318126274Sdes xfree(tmp); 1319126274Sdes err = 1; 1320126274Sdes break; 1321126274Sdes } 1322126274Sdes xfree(*pwd); 1323126274Sdes *pwd = tmp; 1324126274Sdes break; 1325126274Sdes case I_LS: 1326126274Sdes if (!path1) { 1327126274Sdes do_globbed_ls(conn, *pwd, *pwd, lflag); 1328126274Sdes break; 1329126274Sdes } 1330126274Sdes 1331126274Sdes /* Strip pwd off beginning of non-absolute paths */ 1332126274Sdes tmp = NULL; 1333126274Sdes if (*path1 != '/') 1334126274Sdes tmp = *pwd; 1335126274Sdes 1336126274Sdes path1 = make_absolute(path1, *pwd); 1337126274Sdes err = do_globbed_ls(conn, path1, tmp, lflag); 1338126274Sdes break; 1339181111Sdes case I_DF: 1340181111Sdes /* Default to current directory if no path specified */ 1341181111Sdes if (path1 == NULL) 1342181111Sdes path1 = xstrdup(*pwd); 1343181111Sdes path1 = make_absolute(path1, *pwd); 1344181111Sdes err = do_df(conn, path1, hflag, iflag); 1345181111Sdes break; 1346126274Sdes case I_LCHDIR: 1347126274Sdes if (chdir(path1) == -1) { 1348126274Sdes error("Couldn't change local directory to " 1349126274Sdes "\"%s\": %s", path1, strerror(errno)); 1350126274Sdes err = 1; 1351126274Sdes } 1352126274Sdes break; 1353126274Sdes case I_LMKDIR: 1354126274Sdes if (mkdir(path1, 0777) == -1) { 1355126274Sdes error("Couldn't create local directory " 1356126274Sdes "\"%s\": %s", path1, strerror(errno)); 1357126274Sdes err = 1; 1358126274Sdes } 1359126274Sdes break; 1360126274Sdes case I_LLS: 1361126274Sdes local_do_ls(cmd); 1362126274Sdes break; 1363126274Sdes case I_SHELL: 1364126274Sdes local_do_shell(cmd); 1365126274Sdes break; 1366126274Sdes case I_LUMASK: 1367126274Sdes umask(n_arg); 1368126274Sdes printf("Local umask: %03lo\n", n_arg); 1369126274Sdes break; 1370126274Sdes case I_CHMOD: 1371126274Sdes path1 = make_absolute(path1, *pwd); 1372126274Sdes attrib_clear(&a); 1373126274Sdes a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1374126274Sdes a.perm = n_arg; 1375126274Sdes remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1376137015Sdes for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1377126274Sdes printf("Changing mode on %s\n", g.gl_pathv[i]); 1378126274Sdes err = do_setstat(conn, g.gl_pathv[i], &a); 1379126274Sdes if (err != 0 && err_abort) 1380126274Sdes break; 1381126274Sdes } 1382126274Sdes break; 1383126274Sdes case I_CHOWN: 1384126274Sdes case I_CHGRP: 1385126274Sdes path1 = make_absolute(path1, *pwd); 1386126274Sdes remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1387137015Sdes for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1388126274Sdes if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1389126274Sdes if (err != 0 && err_abort) 1390126274Sdes break; 1391126274Sdes else 1392126274Sdes continue; 1393126274Sdes } 1394126274Sdes if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1395126274Sdes error("Can't get current ownership of " 1396126274Sdes "remote file \"%s\"", g.gl_pathv[i]); 1397126274Sdes if (err != 0 && err_abort) 1398126274Sdes break; 1399126274Sdes else 1400126274Sdes continue; 1401126274Sdes } 1402126274Sdes aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1403126274Sdes if (cmdnum == I_CHOWN) { 1404126274Sdes printf("Changing owner on %s\n", g.gl_pathv[i]); 1405126274Sdes aa->uid = n_arg; 1406126274Sdes } else { 1407126274Sdes printf("Changing group on %s\n", g.gl_pathv[i]); 1408126274Sdes aa->gid = n_arg; 1409126274Sdes } 1410126274Sdes err = do_setstat(conn, g.gl_pathv[i], aa); 1411126274Sdes if (err != 0 && err_abort) 1412126274Sdes break; 1413126274Sdes } 1414126274Sdes break; 1415126274Sdes case I_PWD: 1416126274Sdes printf("Remote working directory: %s\n", *pwd); 1417126274Sdes break; 1418126274Sdes case I_LPWD: 1419126274Sdes if (!getcwd(path_buf, sizeof(path_buf))) { 1420126274Sdes error("Couldn't get local cwd: %s", strerror(errno)); 1421126274Sdes err = -1; 1422126274Sdes break; 1423126274Sdes } 1424126274Sdes printf("Local working directory: %s\n", path_buf); 1425126274Sdes break; 1426126274Sdes case I_QUIT: 1427126274Sdes /* Processed below */ 1428126274Sdes break; 1429126274Sdes case I_HELP: 1430126274Sdes help(); 1431126274Sdes break; 1432126274Sdes case I_VERSION: 1433126274Sdes printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1434126274Sdes break; 1435126274Sdes case I_PROGRESS: 1436126274Sdes showprogress = !showprogress; 1437126274Sdes if (showprogress) 1438126274Sdes printf("Progress meter enabled\n"); 1439126274Sdes else 1440126274Sdes printf("Progress meter disabled\n"); 1441126274Sdes break; 1442126274Sdes default: 1443126274Sdes fatal("%d is not implemented", cmdnum); 1444126274Sdes } 1445126274Sdes 1446126274Sdes if (g.gl_pathc) 1447126274Sdes globfree(&g); 1448126274Sdes if (path1) 1449126274Sdes xfree(path1); 1450126274Sdes if (path2) 1451126274Sdes xfree(path2); 1452126274Sdes 1453126274Sdes /* If an unignored error occurs in batch mode we should abort. */ 1454126274Sdes if (err_abort && err != 0) 1455126274Sdes return (-1); 1456126274Sdes else if (cmdnum == I_QUIT) 1457126274Sdes return (1); 1458126274Sdes 1459126274Sdes return (0); 1460126274Sdes} 1461126274Sdes 1462146998Sdes#ifdef USE_LIBEDIT 1463146998Sdesstatic char * 1464146998Sdesprompt(EditLine *el) 1465146998Sdes{ 1466146998Sdes return ("sftp> "); 1467146998Sdes} 1468146998Sdes#endif 1469146998Sdes 1470126274Sdesint 1471126274Sdesinteractive_loop(int fd_in, int fd_out, char *file1, char *file2) 1472126274Sdes{ 1473126274Sdes char *pwd; 1474126274Sdes char *dir = NULL; 1475126274Sdes char cmd[2048]; 1476126274Sdes struct sftp_conn *conn; 1477149749Sdes int err, interactive; 1478146998Sdes EditLine *el = NULL; 1479146998Sdes#ifdef USE_LIBEDIT 1480146998Sdes History *hl = NULL; 1481146998Sdes HistEvent hev; 1482146998Sdes extern char *__progname; 1483126274Sdes 1484146998Sdes if (!batchmode && isatty(STDIN_FILENO)) { 1485146998Sdes if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) 1486146998Sdes fatal("Couldn't initialise editline"); 1487146998Sdes if ((hl = history_init()) == NULL) 1488146998Sdes fatal("Couldn't initialise editline history"); 1489146998Sdes history(hl, &hev, H_SETSIZE, 100); 1490146998Sdes el_set(el, EL_HIST, history, hl); 1491146998Sdes 1492146998Sdes el_set(el, EL_PROMPT, prompt); 1493146998Sdes el_set(el, EL_EDITOR, "emacs"); 1494146998Sdes el_set(el, EL_TERMINAL, NULL); 1495146998Sdes el_set(el, EL_SIGNAL, 1); 1496146998Sdes el_source(el, NULL); 1497146998Sdes } 1498146998Sdes#endif /* USE_LIBEDIT */ 1499146998Sdes 1500126274Sdes conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); 1501126274Sdes if (conn == NULL) 1502126274Sdes fatal("Couldn't initialise connection to server"); 1503126274Sdes 1504126274Sdes pwd = do_realpath(conn, "."); 1505126274Sdes if (pwd == NULL) 1506126274Sdes fatal("Need cwd"); 1507126274Sdes 1508126274Sdes if (file1 != NULL) { 1509126274Sdes dir = xstrdup(file1); 1510126274Sdes dir = make_absolute(dir, pwd); 1511126274Sdes 1512126274Sdes if (remote_is_dir(conn, dir) && file2 == NULL) { 1513126274Sdes printf("Changing to: %s\n", dir); 1514126274Sdes snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 1515146998Sdes if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) { 1516146998Sdes xfree(dir); 1517146998Sdes xfree(pwd); 1518162852Sdes xfree(conn); 1519126274Sdes return (-1); 1520146998Sdes } 1521126274Sdes } else { 1522126274Sdes if (file2 == NULL) 1523126274Sdes snprintf(cmd, sizeof cmd, "get %s", dir); 1524126274Sdes else 1525126274Sdes snprintf(cmd, sizeof cmd, "get %s %s", dir, 1526126274Sdes file2); 1527126274Sdes 1528126274Sdes err = parse_dispatch_command(conn, cmd, &pwd, 1); 1529126274Sdes xfree(dir); 1530126274Sdes xfree(pwd); 1531162852Sdes xfree(conn); 1532126274Sdes return (err); 1533126274Sdes } 1534126274Sdes xfree(dir); 1535126274Sdes } 1536126274Sdes 1537149749Sdes#if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF) 1538126274Sdes setvbuf(stdout, NULL, _IOLBF, 0); 1539126274Sdes setvbuf(infile, NULL, _IOLBF, 0); 1540126274Sdes#else 1541149749Sdes setlinebuf(stdout); 1542149749Sdes setlinebuf(infile); 1543126274Sdes#endif 1544126274Sdes 1545149749Sdes interactive = !batchmode && isatty(STDIN_FILENO); 1546126274Sdes err = 0; 1547126274Sdes for (;;) { 1548126274Sdes char *cp; 1549126274Sdes 1550137015Sdes signal(SIGINT, SIG_IGN); 1551137015Sdes 1552146998Sdes if (el == NULL) { 1553149749Sdes if (interactive) 1554149749Sdes printf("sftp> "); 1555146998Sdes if (fgets(cmd, sizeof(cmd), infile) == NULL) { 1556149749Sdes if (interactive) 1557149749Sdes printf("\n"); 1558146998Sdes break; 1559146998Sdes } 1560149749Sdes if (!interactive) { /* Echo command */ 1561149749Sdes printf("sftp> %s", cmd); 1562149749Sdes if (strlen(cmd) > 0 && 1563149749Sdes cmd[strlen(cmd) - 1] != '\n') 1564149749Sdes printf("\n"); 1565149749Sdes } 1566146998Sdes } else { 1567146998Sdes#ifdef USE_LIBEDIT 1568146998Sdes const char *line; 1569146998Sdes int count = 0; 1570126274Sdes 1571149749Sdes if ((line = el_gets(el, &count)) == NULL || count <= 0) { 1572149749Sdes printf("\n"); 1573149749Sdes break; 1574149749Sdes } 1575146998Sdes history(hl, &hev, H_ENTER, line); 1576146998Sdes if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { 1577146998Sdes fprintf(stderr, "Error: input line too long\n"); 1578146998Sdes continue; 1579146998Sdes } 1580146998Sdes#endif /* USE_LIBEDIT */ 1581126274Sdes } 1582126274Sdes 1583126274Sdes cp = strrchr(cmd, '\n'); 1584126274Sdes if (cp) 1585126274Sdes *cp = '\0'; 1586126274Sdes 1587137015Sdes /* Handle user interrupts gracefully during commands */ 1588137015Sdes interrupted = 0; 1589137015Sdes signal(SIGINT, cmd_interrupt); 1590137015Sdes 1591126274Sdes err = parse_dispatch_command(conn, cmd, &pwd, batchmode); 1592126274Sdes if (err != 0) 1593126274Sdes break; 1594126274Sdes } 1595126274Sdes xfree(pwd); 1596162852Sdes xfree(conn); 1597126274Sdes 1598149749Sdes#ifdef USE_LIBEDIT 1599149749Sdes if (el != NULL) 1600149749Sdes el_end(el); 1601149749Sdes#endif /* USE_LIBEDIT */ 1602149749Sdes 1603126274Sdes /* err == 1 signifies normal "quit" exit */ 1604126274Sdes return (err >= 0 ? 0 : -1); 1605126274Sdes} 1606126274Sdes 1607126274Sdesstatic void 1608124208Sdesconnect_to_server(char *path, char **args, int *in, int *out) 1609124208Sdes{ 161076259Sgreen int c_in, c_out; 161199060Sdes 161276259Sgreen#ifdef USE_PIPES 161376259Sgreen int pin[2], pout[2]; 161499060Sdes 161576259Sgreen if ((pipe(pin) == -1) || (pipe(pout) == -1)) 161676259Sgreen fatal("pipe: %s", strerror(errno)); 161776259Sgreen *in = pin[0]; 161876259Sgreen *out = pout[1]; 161976259Sgreen c_in = pout[0]; 162076259Sgreen c_out = pin[1]; 162176259Sgreen#else /* USE_PIPES */ 162276259Sgreen int inout[2]; 162399060Sdes 162476259Sgreen if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 162576259Sgreen fatal("socketpair: %s", strerror(errno)); 162676259Sgreen *in = *out = inout[0]; 162776259Sgreen c_in = c_out = inout[1]; 162876259Sgreen#endif /* USE_PIPES */ 162976259Sgreen 1630124208Sdes if ((sshpid = fork()) == -1) 163176259Sgreen fatal("fork: %s", strerror(errno)); 1632124208Sdes else if (sshpid == 0) { 163376259Sgreen if ((dup2(c_in, STDIN_FILENO) == -1) || 163476259Sgreen (dup2(c_out, STDOUT_FILENO) == -1)) { 163576259Sgreen fprintf(stderr, "dup2: %s\n", strerror(errno)); 1636137015Sdes _exit(1); 163776259Sgreen } 163876259Sgreen close(*in); 163976259Sgreen close(*out); 164076259Sgreen close(c_in); 164176259Sgreen close(c_out); 1642137015Sdes 1643137015Sdes /* 1644137015Sdes * The underlying ssh is in the same process group, so we must 1645137015Sdes * ignore SIGINT if we want to gracefully abort commands, 1646137015Sdes * otherwise the signal will make it to the ssh process and 1647137015Sdes * kill it too 1648137015Sdes */ 1649137015Sdes signal(SIGINT, SIG_IGN); 1650137015Sdes execvp(path, args); 165192555Sdes fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 1652137015Sdes _exit(1); 165376259Sgreen } 165476259Sgreen 1655124208Sdes signal(SIGTERM, killchild); 1656124208Sdes signal(SIGINT, killchild); 1657124208Sdes signal(SIGHUP, killchild); 165876259Sgreen close(c_in); 165976259Sgreen close(c_out); 166076259Sgreen} 166176259Sgreen 166292555Sdesstatic void 166376259Sgreenusage(void) 166476259Sgreen{ 166592555Sdes extern char *__progname; 166698675Sdes 166792555Sdes fprintf(stderr, 1668126274Sdes "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" 1669126274Sdes " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" 1670126274Sdes " [-S program] [-s subsystem | sftp_server] host\n" 1671126274Sdes " %s [[user@]host[:file [file]]]\n" 1672126274Sdes " %s [[user@]host[:dir[/]]]\n" 1673126274Sdes " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); 167476259Sgreen exit(1); 167576259Sgreen} 167676259Sgreen 167776259Sgreenint 167876259Sgreenmain(int argc, char **argv) 167976259Sgreen{ 1680113908Sdes int in, out, ch, err; 1681137015Sdes char *host, *userhost, *cp, *file2 = NULL; 168292555Sdes int debug_level = 0, sshver = 2; 168392555Sdes char *file1 = NULL, *sftp_server = NULL; 168492555Sdes char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 168592555Sdes LogLevel ll = SYSLOG_LEVEL_INFO; 168692555Sdes arglist args; 168776259Sgreen extern int optind; 168876259Sgreen extern char *optarg; 168976259Sgreen 1690157016Sdes /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1691157016Sdes sanitise_stdfd(); 1692157016Sdes 1693124208Sdes __progname = ssh_get_progname(argv[0]); 1694157016Sdes memset(&args, '\0', sizeof(args)); 169592555Sdes args.list = NULL; 1696162852Sdes addargs(&args, "%s", ssh_program); 169792555Sdes addargs(&args, "-oForwardX11 no"); 169892555Sdes addargs(&args, "-oForwardAgent no"); 1699157016Sdes addargs(&args, "-oPermitLocalCommand no"); 170092555Sdes addargs(&args, "-oClearAllForwardings yes"); 1701126274Sdes 170292555Sdes ll = SYSLOG_LEVEL_INFO; 1703126274Sdes infile = stdin; 170476259Sgreen 170592555Sdes while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { 170676259Sgreen switch (ch) { 170776259Sgreen case 'C': 170892555Sdes addargs(&args, "-C"); 170976259Sgreen break; 171076259Sgreen case 'v': 171192555Sdes if (debug_level < 3) { 171292555Sdes addargs(&args, "-v"); 171392555Sdes ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 171492555Sdes } 171592555Sdes debug_level++; 171676259Sgreen break; 171792555Sdes case 'F': 171876259Sgreen case 'o': 171992555Sdes addargs(&args, "-%c%s", ch, optarg); 172076259Sgreen break; 172176259Sgreen case '1': 172292555Sdes sshver = 1; 172376259Sgreen if (sftp_server == NULL) 172476259Sgreen sftp_server = _PATH_SFTP_SERVER; 172576259Sgreen break; 172676259Sgreen case 's': 172776259Sgreen sftp_server = optarg; 172876259Sgreen break; 172976259Sgreen case 'S': 173076259Sgreen ssh_program = optarg; 1731157016Sdes replacearg(&args, 0, "%s", ssh_program); 173276259Sgreen break; 173376259Sgreen case 'b': 1734126274Sdes if (batchmode) 1735126274Sdes fatal("Batch file already specified."); 1736126274Sdes 1737126274Sdes /* Allow "-" as stdin */ 1738137015Sdes if (strcmp(optarg, "-") != 0 && 1739149749Sdes (infile = fopen(optarg, "r")) == NULL) 1740126274Sdes fatal("%s (%s).", strerror(errno), optarg); 1741113908Sdes showprogress = 0; 1742126274Sdes batchmode = 1; 1743146998Sdes addargs(&args, "-obatchmode yes"); 174476259Sgreen break; 174592555Sdes case 'P': 174692555Sdes sftp_direct = optarg; 174792555Sdes break; 174892555Sdes case 'B': 174992555Sdes copy_buffer_len = strtol(optarg, &cp, 10); 175092555Sdes if (copy_buffer_len == 0 || *cp != '\0') 175192555Sdes fatal("Invalid buffer size \"%s\"", optarg); 175292555Sdes break; 175392555Sdes case 'R': 175492555Sdes num_requests = strtol(optarg, &cp, 10); 175592555Sdes if (num_requests == 0 || *cp != '\0') 175698675Sdes fatal("Invalid number of requests \"%s\"", 175792555Sdes optarg); 175892555Sdes break; 175976259Sgreen case 'h': 176076259Sgreen default: 176176259Sgreen usage(); 176276259Sgreen } 176376259Sgreen } 176476259Sgreen 1765128456Sdes if (!isatty(STDERR_FILENO)) 1766128456Sdes showprogress = 0; 1767128456Sdes 176898675Sdes log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 176998675Sdes 177092555Sdes if (sftp_direct == NULL) { 177192555Sdes if (optind == argc || argc > (optind + 2)) 177292555Sdes usage(); 177376259Sgreen 177492555Sdes userhost = xstrdup(argv[optind]); 177592555Sdes file2 = argv[optind+1]; 177676259Sgreen 1777113908Sdes if ((host = strrchr(userhost, '@')) == NULL) 177892555Sdes host = userhost; 177992555Sdes else { 178092555Sdes *host++ = '\0'; 178192555Sdes if (!userhost[0]) { 178292555Sdes fprintf(stderr, "Missing username\n"); 178392555Sdes usage(); 178492555Sdes } 1785181111Sdes addargs(&args, "-l%s", userhost); 178692555Sdes } 178792555Sdes 1788126274Sdes if ((cp = colon(host)) != NULL) { 1789126274Sdes *cp++ = '\0'; 1790126274Sdes file1 = cp; 1791126274Sdes } 1792126274Sdes 179392555Sdes host = cleanhostname(host); 179492555Sdes if (!*host) { 179592555Sdes fprintf(stderr, "Missing hostname\n"); 179676259Sgreen usage(); 179776259Sgreen } 179876259Sgreen 179992555Sdes addargs(&args, "-oProtocol %d", sshver); 180076259Sgreen 180192555Sdes /* no subsystem if the server-spec contains a '/' */ 180292555Sdes if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 180392555Sdes addargs(&args, "-s"); 180476259Sgreen 180592555Sdes addargs(&args, "%s", host); 180698675Sdes addargs(&args, "%s", (sftp_server != NULL ? 180792555Sdes sftp_server : "sftp")); 180876259Sgreen 1809126274Sdes if (!batchmode) 1810126274Sdes fprintf(stderr, "Connecting to %s...\n", host); 1811124208Sdes connect_to_server(ssh_program, args.list, &in, &out); 181292555Sdes } else { 181392555Sdes args.list = NULL; 181492555Sdes addargs(&args, "sftp-server"); 181576259Sgreen 1816126274Sdes if (!batchmode) 1817126274Sdes fprintf(stderr, "Attaching to %s...\n", sftp_direct); 1818124208Sdes connect_to_server(sftp_direct, args.list, &in, &out); 181992555Sdes } 1820157016Sdes freeargs(&args); 182176259Sgreen 1822113908Sdes err = interactive_loop(in, out, file1, file2); 182376259Sgreen 182498937Sdes#if !defined(USE_PIPES) 1825149749Sdes shutdown(in, SHUT_RDWR); 1826149749Sdes shutdown(out, SHUT_RDWR); 182798937Sdes#endif 182898937Sdes 182976259Sgreen close(in); 183076259Sgreen close(out); 1831126274Sdes if (batchmode) 183276259Sgreen fclose(infile); 183376259Sgreen 183498675Sdes while (waitpid(sshpid, NULL, 0) == -1) 183598675Sdes if (errno != EINTR) 183698675Sdes fatal("Couldn't wait for ssh process: %s", 183798675Sdes strerror(errno)); 183876259Sgreen 1839113908Sdes exit(err == 0 ? 0 : 1); 184076259Sgreen} 1841