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