11e8db6e2SBrian Feldman /* 2efcad6b7SDag-Erling Smørgrav * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 31e8db6e2SBrian Feldman * 4efcad6b7SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 5efcad6b7SDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 6efcad6b7SDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 71e8db6e2SBrian Feldman * 8efcad6b7SDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9efcad6b7SDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10efcad6b7SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11efcad6b7SDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12efcad6b7SDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13efcad6b7SDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14efcad6b7SDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 151e8db6e2SBrian Feldman */ 161e8db6e2SBrian Feldman 171e8db6e2SBrian Feldman #include "includes.h" 181e8db6e2SBrian Feldman 19021d409fSDag-Erling Smørgrav RCSID("$OpenBSD: sftp.c,v 1.70 2006/01/31 10:19:02 djm Exp $"); 205e8dbd04SDag-Erling Smørgrav 215e8dbd04SDag-Erling Smørgrav #ifdef USE_LIBEDIT 225e8dbd04SDag-Erling Smørgrav #include <histedit.h> 235e8dbd04SDag-Erling Smørgrav #else 245e8dbd04SDag-Erling Smørgrav typedef void EditLine; 255e8dbd04SDag-Erling Smørgrav #endif 261e8db6e2SBrian Feldman 271e8db6e2SBrian Feldman #include "buffer.h" 281e8db6e2SBrian Feldman #include "xmalloc.h" 291e8db6e2SBrian Feldman #include "log.h" 301e8db6e2SBrian Feldman #include "pathnames.h" 31ae1f160dSDag-Erling Smørgrav #include "misc.h" 321e8db6e2SBrian Feldman 331e8db6e2SBrian Feldman #include "sftp.h" 341e8db6e2SBrian Feldman #include "sftp-common.h" 351e8db6e2SBrian Feldman #include "sftp-client.h" 36efcad6b7SDag-Erling Smørgrav 37efcad6b7SDag-Erling Smørgrav /* File to read commands from */ 38efcad6b7SDag-Erling Smørgrav FILE* infile; 39efcad6b7SDag-Erling Smørgrav 40efcad6b7SDag-Erling Smørgrav /* Are we in batchfile mode? */ 41efcad6b7SDag-Erling Smørgrav int batchmode = 0; 42efcad6b7SDag-Erling Smørgrav 43efcad6b7SDag-Erling Smørgrav /* Size of buffer used when copying files */ 44efcad6b7SDag-Erling Smørgrav size_t copy_buffer_len = 32768; 45efcad6b7SDag-Erling Smørgrav 46efcad6b7SDag-Erling Smørgrav /* Number of concurrent outstanding requests */ 47efcad6b7SDag-Erling Smørgrav size_t num_requests = 16; 48efcad6b7SDag-Erling Smørgrav 49efcad6b7SDag-Erling Smørgrav /* PID of ssh transport process */ 50efcad6b7SDag-Erling Smørgrav static pid_t sshpid = -1; 51efcad6b7SDag-Erling Smørgrav 52efcad6b7SDag-Erling Smørgrav /* This is set to 0 if the progressmeter is not desired. */ 5352028650SDag-Erling Smørgrav int showprogress = 1; 54efcad6b7SDag-Erling Smørgrav 55d74d50a8SDag-Erling Smørgrav /* SIGINT received during command processing */ 56d74d50a8SDag-Erling Smørgrav volatile sig_atomic_t interrupted = 0; 57d74d50a8SDag-Erling Smørgrav 58d74d50a8SDag-Erling Smørgrav /* I wish qsort() took a separate ctx for the comparison function...*/ 59d74d50a8SDag-Erling Smørgrav int sort_flag; 60d74d50a8SDag-Erling Smørgrav 61efcad6b7SDag-Erling Smørgrav int remote_glob(struct sftp_conn *, const char *, int, 62efcad6b7SDag-Erling Smørgrav int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 631e8db6e2SBrian Feldman 6483d2307dSDag-Erling Smørgrav extern char *__progname; 6583d2307dSDag-Erling Smørgrav 66efcad6b7SDag-Erling Smørgrav /* Separators for interactive commands */ 67efcad6b7SDag-Erling Smørgrav #define WHITESPACE " \t\r\n" 681e8db6e2SBrian Feldman 69d74d50a8SDag-Erling Smørgrav /* ls flags */ 70d74d50a8SDag-Erling Smørgrav #define LS_LONG_VIEW 0x01 /* Full view ala ls -l */ 71d74d50a8SDag-Erling Smørgrav #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */ 72d74d50a8SDag-Erling Smørgrav #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */ 73d74d50a8SDag-Erling Smørgrav #define LS_NAME_SORT 0x08 /* Sort by name (default) */ 74d74d50a8SDag-Erling Smørgrav #define LS_TIME_SORT 0x10 /* Sort by mtime */ 75d74d50a8SDag-Erling Smørgrav #define LS_SIZE_SORT 0x20 /* Sort by file size */ 76d74d50a8SDag-Erling Smørgrav #define LS_REVERSE_SORT 0x40 /* Reverse sort order */ 77d74d50a8SDag-Erling Smørgrav #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */ 78d74d50a8SDag-Erling Smørgrav 79d74d50a8SDag-Erling Smørgrav #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW) 80d74d50a8SDag-Erling Smørgrav #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) 81efcad6b7SDag-Erling Smørgrav 82efcad6b7SDag-Erling Smørgrav /* Commands for interactive mode */ 83efcad6b7SDag-Erling Smørgrav #define I_CHDIR 1 84efcad6b7SDag-Erling Smørgrav #define I_CHGRP 2 85efcad6b7SDag-Erling Smørgrav #define I_CHMOD 3 86efcad6b7SDag-Erling Smørgrav #define I_CHOWN 4 87efcad6b7SDag-Erling Smørgrav #define I_GET 5 88efcad6b7SDag-Erling Smørgrav #define I_HELP 6 89efcad6b7SDag-Erling Smørgrav #define I_LCHDIR 7 90efcad6b7SDag-Erling Smørgrav #define I_LLS 8 91efcad6b7SDag-Erling Smørgrav #define I_LMKDIR 9 92efcad6b7SDag-Erling Smørgrav #define I_LPWD 10 93efcad6b7SDag-Erling Smørgrav #define I_LS 11 94efcad6b7SDag-Erling Smørgrav #define I_LUMASK 12 95efcad6b7SDag-Erling Smørgrav #define I_MKDIR 13 96efcad6b7SDag-Erling Smørgrav #define I_PUT 14 97efcad6b7SDag-Erling Smørgrav #define I_PWD 15 98efcad6b7SDag-Erling Smørgrav #define I_QUIT 16 99efcad6b7SDag-Erling Smørgrav #define I_RENAME 17 100efcad6b7SDag-Erling Smørgrav #define I_RM 18 101efcad6b7SDag-Erling Smørgrav #define I_RMDIR 19 102efcad6b7SDag-Erling Smørgrav #define I_SHELL 20 103efcad6b7SDag-Erling Smørgrav #define I_SYMLINK 21 104efcad6b7SDag-Erling Smørgrav #define I_VERSION 22 105efcad6b7SDag-Erling Smørgrav #define I_PROGRESS 23 106efcad6b7SDag-Erling Smørgrav 107efcad6b7SDag-Erling Smørgrav struct CMD { 108efcad6b7SDag-Erling Smørgrav const char *c; 109efcad6b7SDag-Erling Smørgrav const int n; 110efcad6b7SDag-Erling Smørgrav }; 111efcad6b7SDag-Erling Smørgrav 112efcad6b7SDag-Erling Smørgrav static const struct CMD cmds[] = { 113efcad6b7SDag-Erling Smørgrav { "bye", I_QUIT }, 114efcad6b7SDag-Erling Smørgrav { "cd", I_CHDIR }, 115efcad6b7SDag-Erling Smørgrav { "chdir", I_CHDIR }, 116efcad6b7SDag-Erling Smørgrav { "chgrp", I_CHGRP }, 117efcad6b7SDag-Erling Smørgrav { "chmod", I_CHMOD }, 118efcad6b7SDag-Erling Smørgrav { "chown", I_CHOWN }, 119efcad6b7SDag-Erling Smørgrav { "dir", I_LS }, 120efcad6b7SDag-Erling Smørgrav { "exit", I_QUIT }, 121efcad6b7SDag-Erling Smørgrav { "get", I_GET }, 122efcad6b7SDag-Erling Smørgrav { "mget", I_GET }, 123efcad6b7SDag-Erling Smørgrav { "help", I_HELP }, 124efcad6b7SDag-Erling Smørgrav { "lcd", I_LCHDIR }, 125efcad6b7SDag-Erling Smørgrav { "lchdir", I_LCHDIR }, 126efcad6b7SDag-Erling Smørgrav { "lls", I_LLS }, 127efcad6b7SDag-Erling Smørgrav { "lmkdir", I_LMKDIR }, 128efcad6b7SDag-Erling Smørgrav { "ln", I_SYMLINK }, 129efcad6b7SDag-Erling Smørgrav { "lpwd", I_LPWD }, 130efcad6b7SDag-Erling Smørgrav { "ls", I_LS }, 131efcad6b7SDag-Erling Smørgrav { "lumask", I_LUMASK }, 132efcad6b7SDag-Erling Smørgrav { "mkdir", I_MKDIR }, 133efcad6b7SDag-Erling Smørgrav { "progress", I_PROGRESS }, 134efcad6b7SDag-Erling Smørgrav { "put", I_PUT }, 135efcad6b7SDag-Erling Smørgrav { "mput", I_PUT }, 136efcad6b7SDag-Erling Smørgrav { "pwd", I_PWD }, 137efcad6b7SDag-Erling Smørgrav { "quit", I_QUIT }, 138efcad6b7SDag-Erling Smørgrav { "rename", I_RENAME }, 139efcad6b7SDag-Erling Smørgrav { "rm", I_RM }, 140efcad6b7SDag-Erling Smørgrav { "rmdir", I_RMDIR }, 141efcad6b7SDag-Erling Smørgrav { "symlink", I_SYMLINK }, 142efcad6b7SDag-Erling Smørgrav { "version", I_VERSION }, 143efcad6b7SDag-Erling Smørgrav { "!", I_SHELL }, 144efcad6b7SDag-Erling Smørgrav { "?", I_HELP }, 145efcad6b7SDag-Erling Smørgrav { NULL, -1} 146efcad6b7SDag-Erling Smørgrav }; 147efcad6b7SDag-Erling Smørgrav 148efcad6b7SDag-Erling Smørgrav int interactive_loop(int fd_in, int fd_out, char *file1, char *file2); 149efcad6b7SDag-Erling Smørgrav 150efcad6b7SDag-Erling Smørgrav static void 151d74d50a8SDag-Erling Smørgrav killchild(int signo) 152d74d50a8SDag-Erling Smørgrav { 1535e8dbd04SDag-Erling Smørgrav if (sshpid > 1) { 154d74d50a8SDag-Erling Smørgrav kill(sshpid, SIGTERM); 1555e8dbd04SDag-Erling Smørgrav waitpid(sshpid, NULL, 0); 1565e8dbd04SDag-Erling Smørgrav } 157d74d50a8SDag-Erling Smørgrav 158d74d50a8SDag-Erling Smørgrav _exit(1); 159d74d50a8SDag-Erling Smørgrav } 160d74d50a8SDag-Erling Smørgrav 161d74d50a8SDag-Erling Smørgrav static void 162d74d50a8SDag-Erling Smørgrav cmd_interrupt(int signo) 163d74d50a8SDag-Erling Smørgrav { 164d74d50a8SDag-Erling Smørgrav const char msg[] = "\rInterrupt \n"; 1655e8dbd04SDag-Erling Smørgrav int olderrno = errno; 166d74d50a8SDag-Erling Smørgrav 167d74d50a8SDag-Erling Smørgrav write(STDERR_FILENO, msg, sizeof(msg) - 1); 168d74d50a8SDag-Erling Smørgrav interrupted = 1; 1695e8dbd04SDag-Erling Smørgrav errno = olderrno; 170d74d50a8SDag-Erling Smørgrav } 171d74d50a8SDag-Erling Smørgrav 172d74d50a8SDag-Erling Smørgrav static void 173efcad6b7SDag-Erling Smørgrav help(void) 174efcad6b7SDag-Erling Smørgrav { 175efcad6b7SDag-Erling Smørgrav printf("Available commands:\n"); 176efcad6b7SDag-Erling Smørgrav printf("cd path Change remote directory to 'path'\n"); 177efcad6b7SDag-Erling Smørgrav printf("lcd path Change local directory to 'path'\n"); 178efcad6b7SDag-Erling Smørgrav printf("chgrp grp path Change group of file 'path' to 'grp'\n"); 179efcad6b7SDag-Erling Smørgrav printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); 180efcad6b7SDag-Erling Smørgrav printf("chown own path Change owner of file 'path' to 'own'\n"); 181efcad6b7SDag-Erling Smørgrav printf("help Display this help text\n"); 182efcad6b7SDag-Erling Smørgrav printf("get remote-path [local-path] Download file\n"); 183efcad6b7SDag-Erling Smørgrav printf("lls [ls-options [path]] Display local directory listing\n"); 184efcad6b7SDag-Erling Smørgrav printf("ln oldpath newpath Symlink remote file\n"); 185efcad6b7SDag-Erling Smørgrav printf("lmkdir path Create local directory\n"); 186efcad6b7SDag-Erling Smørgrav printf("lpwd Print local working directory\n"); 187efcad6b7SDag-Erling Smørgrav printf("ls [path] Display remote directory listing\n"); 188efcad6b7SDag-Erling Smørgrav printf("lumask umask Set local umask to 'umask'\n"); 189efcad6b7SDag-Erling Smørgrav printf("mkdir path Create remote directory\n"); 190efcad6b7SDag-Erling Smørgrav printf("progress Toggle display of progress meter\n"); 191efcad6b7SDag-Erling Smørgrav printf("put local-path [remote-path] Upload file\n"); 192efcad6b7SDag-Erling Smørgrav printf("pwd Display remote working directory\n"); 193efcad6b7SDag-Erling Smørgrav printf("exit Quit sftp\n"); 194efcad6b7SDag-Erling Smørgrav printf("quit Quit sftp\n"); 195efcad6b7SDag-Erling Smørgrav printf("rename oldpath newpath Rename remote file\n"); 196efcad6b7SDag-Erling Smørgrav printf("rmdir path Remove remote directory\n"); 197efcad6b7SDag-Erling Smørgrav printf("rm path Delete remote file\n"); 198efcad6b7SDag-Erling Smørgrav printf("symlink oldpath newpath Symlink remote file\n"); 199efcad6b7SDag-Erling Smørgrav printf("version Show SFTP version\n"); 200efcad6b7SDag-Erling Smørgrav printf("!command Execute 'command' in local shell\n"); 201efcad6b7SDag-Erling Smørgrav printf("! Escape to local shell\n"); 202efcad6b7SDag-Erling Smørgrav printf("? Synonym for help\n"); 203efcad6b7SDag-Erling Smørgrav } 204efcad6b7SDag-Erling Smørgrav 205efcad6b7SDag-Erling Smørgrav static void 206efcad6b7SDag-Erling Smørgrav local_do_shell(const char *args) 207efcad6b7SDag-Erling Smørgrav { 208efcad6b7SDag-Erling Smørgrav int status; 209efcad6b7SDag-Erling Smørgrav char *shell; 210efcad6b7SDag-Erling Smørgrav pid_t pid; 211efcad6b7SDag-Erling Smørgrav 212efcad6b7SDag-Erling Smørgrav if (!*args) 213efcad6b7SDag-Erling Smørgrav args = NULL; 214efcad6b7SDag-Erling Smørgrav 215efcad6b7SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL) 216efcad6b7SDag-Erling Smørgrav shell = _PATH_BSHELL; 217efcad6b7SDag-Erling Smørgrav 218efcad6b7SDag-Erling Smørgrav if ((pid = fork()) == -1) 219efcad6b7SDag-Erling Smørgrav fatal("Couldn't fork: %s", strerror(errno)); 220efcad6b7SDag-Erling Smørgrav 221efcad6b7SDag-Erling Smørgrav if (pid == 0) { 222efcad6b7SDag-Erling Smørgrav /* XXX: child has pipe fds to ssh subproc open - issue? */ 223efcad6b7SDag-Erling Smørgrav if (args) { 224efcad6b7SDag-Erling Smørgrav debug3("Executing %s -c \"%s\"", shell, args); 225efcad6b7SDag-Erling Smørgrav execl(shell, shell, "-c", args, (char *)NULL); 226efcad6b7SDag-Erling Smørgrav } else { 227efcad6b7SDag-Erling Smørgrav debug3("Executing %s", shell); 228efcad6b7SDag-Erling Smørgrav execl(shell, shell, (char *)NULL); 229efcad6b7SDag-Erling Smørgrav } 230efcad6b7SDag-Erling Smørgrav fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, 231efcad6b7SDag-Erling Smørgrav strerror(errno)); 232efcad6b7SDag-Erling Smørgrav _exit(1); 233efcad6b7SDag-Erling Smørgrav } 234efcad6b7SDag-Erling Smørgrav while (waitpid(pid, &status, 0) == -1) 235efcad6b7SDag-Erling Smørgrav if (errno != EINTR) 236efcad6b7SDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 237efcad6b7SDag-Erling Smørgrav if (!WIFEXITED(status)) 238efcad6b7SDag-Erling Smørgrav error("Shell exited abormally"); 239efcad6b7SDag-Erling Smørgrav else if (WEXITSTATUS(status)) 240efcad6b7SDag-Erling Smørgrav error("Shell exited with status %d", WEXITSTATUS(status)); 241efcad6b7SDag-Erling Smørgrav } 242efcad6b7SDag-Erling Smørgrav 243efcad6b7SDag-Erling Smørgrav static void 244efcad6b7SDag-Erling Smørgrav local_do_ls(const char *args) 245efcad6b7SDag-Erling Smørgrav { 246efcad6b7SDag-Erling Smørgrav if (!args || !*args) 247efcad6b7SDag-Erling Smørgrav local_do_shell(_PATH_LS); 248efcad6b7SDag-Erling Smørgrav else { 249efcad6b7SDag-Erling Smørgrav int len = strlen(_PATH_LS " ") + strlen(args) + 1; 250efcad6b7SDag-Erling Smørgrav char *buf = xmalloc(len); 251efcad6b7SDag-Erling Smørgrav 252efcad6b7SDag-Erling Smørgrav /* XXX: quoting - rip quoting code from ftp? */ 253efcad6b7SDag-Erling Smørgrav snprintf(buf, len, _PATH_LS " %s", args); 254efcad6b7SDag-Erling Smørgrav local_do_shell(buf); 255efcad6b7SDag-Erling Smørgrav xfree(buf); 256efcad6b7SDag-Erling Smørgrav } 257efcad6b7SDag-Erling Smørgrav } 258efcad6b7SDag-Erling Smørgrav 259efcad6b7SDag-Erling Smørgrav /* Strip one path (usually the pwd) from the start of another */ 260efcad6b7SDag-Erling Smørgrav static char * 261efcad6b7SDag-Erling Smørgrav path_strip(char *path, char *strip) 262efcad6b7SDag-Erling Smørgrav { 263efcad6b7SDag-Erling Smørgrav size_t len; 264efcad6b7SDag-Erling Smørgrav 265efcad6b7SDag-Erling Smørgrav if (strip == NULL) 266efcad6b7SDag-Erling Smørgrav return (xstrdup(path)); 267efcad6b7SDag-Erling Smørgrav 268efcad6b7SDag-Erling Smørgrav len = strlen(strip); 2695e8dbd04SDag-Erling Smørgrav if (strncmp(path, strip, len) == 0) { 270efcad6b7SDag-Erling Smørgrav if (strip[len - 1] != '/' && path[len] == '/') 271efcad6b7SDag-Erling Smørgrav len++; 272efcad6b7SDag-Erling Smørgrav return (xstrdup(path + len)); 273efcad6b7SDag-Erling Smørgrav } 274efcad6b7SDag-Erling Smørgrav 275efcad6b7SDag-Erling Smørgrav return (xstrdup(path)); 276efcad6b7SDag-Erling Smørgrav } 277efcad6b7SDag-Erling Smørgrav 278efcad6b7SDag-Erling Smørgrav static char * 279efcad6b7SDag-Erling Smørgrav path_append(char *p1, char *p2) 280efcad6b7SDag-Erling Smørgrav { 281efcad6b7SDag-Erling Smørgrav char *ret; 282efcad6b7SDag-Erling Smørgrav int len = strlen(p1) + strlen(p2) + 2; 283efcad6b7SDag-Erling Smørgrav 284efcad6b7SDag-Erling Smørgrav ret = xmalloc(len); 285efcad6b7SDag-Erling Smørgrav strlcpy(ret, p1, len); 286efcad6b7SDag-Erling Smørgrav if (p1[strlen(p1) - 1] != '/') 287efcad6b7SDag-Erling Smørgrav strlcat(ret, "/", len); 288efcad6b7SDag-Erling Smørgrav strlcat(ret, p2, len); 289efcad6b7SDag-Erling Smørgrav 290efcad6b7SDag-Erling Smørgrav return(ret); 291efcad6b7SDag-Erling Smørgrav } 292efcad6b7SDag-Erling Smørgrav 293efcad6b7SDag-Erling Smørgrav static char * 294efcad6b7SDag-Erling Smørgrav make_absolute(char *p, char *pwd) 295efcad6b7SDag-Erling Smørgrav { 296d74d50a8SDag-Erling Smørgrav char *abs_str; 297efcad6b7SDag-Erling Smørgrav 298efcad6b7SDag-Erling Smørgrav /* Derelativise */ 299efcad6b7SDag-Erling Smørgrav if (p && p[0] != '/') { 300d74d50a8SDag-Erling Smørgrav abs_str = path_append(pwd, p); 301efcad6b7SDag-Erling Smørgrav xfree(p); 302d74d50a8SDag-Erling Smørgrav return(abs_str); 303efcad6b7SDag-Erling Smørgrav } else 304efcad6b7SDag-Erling Smørgrav return(p); 305efcad6b7SDag-Erling Smørgrav } 306efcad6b7SDag-Erling Smørgrav 307efcad6b7SDag-Erling Smørgrav static int 308efcad6b7SDag-Erling Smørgrav infer_path(const char *p, char **ifp) 309efcad6b7SDag-Erling Smørgrav { 310efcad6b7SDag-Erling Smørgrav char *cp; 311efcad6b7SDag-Erling Smørgrav 312efcad6b7SDag-Erling Smørgrav cp = strrchr(p, '/'); 313efcad6b7SDag-Erling Smørgrav if (cp == NULL) { 314efcad6b7SDag-Erling Smørgrav *ifp = xstrdup(p); 315efcad6b7SDag-Erling Smørgrav return(0); 316efcad6b7SDag-Erling Smørgrav } 317efcad6b7SDag-Erling Smørgrav 318efcad6b7SDag-Erling Smørgrav if (!cp[1]) { 319efcad6b7SDag-Erling Smørgrav error("Invalid path"); 320efcad6b7SDag-Erling Smørgrav return(-1); 321efcad6b7SDag-Erling Smørgrav } 322efcad6b7SDag-Erling Smørgrav 323efcad6b7SDag-Erling Smørgrav *ifp = xstrdup(cp + 1); 324efcad6b7SDag-Erling Smørgrav return(0); 325efcad6b7SDag-Erling Smørgrav } 326efcad6b7SDag-Erling Smørgrav 327efcad6b7SDag-Erling Smørgrav static int 328efcad6b7SDag-Erling Smørgrav parse_getput_flags(const char **cpp, int *pflag) 329efcad6b7SDag-Erling Smørgrav { 330efcad6b7SDag-Erling Smørgrav const char *cp = *cpp; 331efcad6b7SDag-Erling Smørgrav 332efcad6b7SDag-Erling Smørgrav /* Check for flags */ 333efcad6b7SDag-Erling Smørgrav if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { 334efcad6b7SDag-Erling Smørgrav switch (cp[1]) { 335efcad6b7SDag-Erling Smørgrav case 'p': 336efcad6b7SDag-Erling Smørgrav case 'P': 337efcad6b7SDag-Erling Smørgrav *pflag = 1; 338efcad6b7SDag-Erling Smørgrav break; 339efcad6b7SDag-Erling Smørgrav default: 340efcad6b7SDag-Erling Smørgrav error("Invalid flag -%c", cp[1]); 341efcad6b7SDag-Erling Smørgrav return(-1); 342efcad6b7SDag-Erling Smørgrav } 343efcad6b7SDag-Erling Smørgrav cp += 2; 344efcad6b7SDag-Erling Smørgrav *cpp = cp + strspn(cp, WHITESPACE); 345efcad6b7SDag-Erling Smørgrav } 346efcad6b7SDag-Erling Smørgrav 347efcad6b7SDag-Erling Smørgrav return(0); 348efcad6b7SDag-Erling Smørgrav } 349efcad6b7SDag-Erling Smørgrav 350efcad6b7SDag-Erling Smørgrav static int 351efcad6b7SDag-Erling Smørgrav parse_ls_flags(const char **cpp, int *lflag) 352efcad6b7SDag-Erling Smørgrav { 353efcad6b7SDag-Erling Smørgrav const char *cp = *cpp; 354efcad6b7SDag-Erling Smørgrav 355d74d50a8SDag-Erling Smørgrav /* Defaults */ 356d74d50a8SDag-Erling Smørgrav *lflag = LS_NAME_SORT; 357d74d50a8SDag-Erling Smørgrav 358efcad6b7SDag-Erling Smørgrav /* Check for flags */ 359efcad6b7SDag-Erling Smørgrav if (cp++[0] == '-') { 360efcad6b7SDag-Erling Smørgrav for (; strchr(WHITESPACE, *cp) == NULL; cp++) { 361efcad6b7SDag-Erling Smørgrav switch (*cp) { 362efcad6b7SDag-Erling Smørgrav case 'l': 363d74d50a8SDag-Erling Smørgrav *lflag &= ~VIEW_FLAGS; 364d74d50a8SDag-Erling Smørgrav *lflag |= LS_LONG_VIEW; 365efcad6b7SDag-Erling Smørgrav break; 366efcad6b7SDag-Erling Smørgrav case '1': 367d74d50a8SDag-Erling Smørgrav *lflag &= ~VIEW_FLAGS; 368d74d50a8SDag-Erling Smørgrav *lflag |= LS_SHORT_VIEW; 369d74d50a8SDag-Erling Smørgrav break; 370d74d50a8SDag-Erling Smørgrav case 'n': 371d74d50a8SDag-Erling Smørgrav *lflag &= ~VIEW_FLAGS; 372d74d50a8SDag-Erling Smørgrav *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; 373d74d50a8SDag-Erling Smørgrav break; 374d74d50a8SDag-Erling Smørgrav case 'S': 375d74d50a8SDag-Erling Smørgrav *lflag &= ~SORT_FLAGS; 376d74d50a8SDag-Erling Smørgrav *lflag |= LS_SIZE_SORT; 377d74d50a8SDag-Erling Smørgrav break; 378d74d50a8SDag-Erling Smørgrav case 't': 379d74d50a8SDag-Erling Smørgrav *lflag &= ~SORT_FLAGS; 380d74d50a8SDag-Erling Smørgrav *lflag |= LS_TIME_SORT; 381d74d50a8SDag-Erling Smørgrav break; 382d74d50a8SDag-Erling Smørgrav case 'r': 383d74d50a8SDag-Erling Smørgrav *lflag |= LS_REVERSE_SORT; 384d74d50a8SDag-Erling Smørgrav break; 385d74d50a8SDag-Erling Smørgrav case 'f': 386d74d50a8SDag-Erling Smørgrav *lflag &= ~SORT_FLAGS; 387d74d50a8SDag-Erling Smørgrav break; 388d74d50a8SDag-Erling Smørgrav case 'a': 389d74d50a8SDag-Erling Smørgrav *lflag |= LS_SHOW_ALL; 390efcad6b7SDag-Erling Smørgrav break; 391efcad6b7SDag-Erling Smørgrav default: 392efcad6b7SDag-Erling Smørgrav error("Invalid flag -%c", *cp); 393efcad6b7SDag-Erling Smørgrav return(-1); 394efcad6b7SDag-Erling Smørgrav } 395efcad6b7SDag-Erling Smørgrav } 396efcad6b7SDag-Erling Smørgrav *cpp = cp + strspn(cp, WHITESPACE); 397efcad6b7SDag-Erling Smørgrav } 398efcad6b7SDag-Erling Smørgrav 399efcad6b7SDag-Erling Smørgrav return(0); 400efcad6b7SDag-Erling Smørgrav } 401efcad6b7SDag-Erling Smørgrav 402efcad6b7SDag-Erling Smørgrav static int 403efcad6b7SDag-Erling Smørgrav get_pathname(const char **cpp, char **path) 404efcad6b7SDag-Erling Smørgrav { 405efcad6b7SDag-Erling Smørgrav const char *cp = *cpp, *end; 406efcad6b7SDag-Erling Smørgrav char quot; 407043840dfSDag-Erling Smørgrav u_int i, j; 408efcad6b7SDag-Erling Smørgrav 409efcad6b7SDag-Erling Smørgrav cp += strspn(cp, WHITESPACE); 410efcad6b7SDag-Erling Smørgrav if (!*cp) { 411efcad6b7SDag-Erling Smørgrav *cpp = cp; 412efcad6b7SDag-Erling Smørgrav *path = NULL; 413efcad6b7SDag-Erling Smørgrav return (0); 414efcad6b7SDag-Erling Smørgrav } 415efcad6b7SDag-Erling Smørgrav 416efcad6b7SDag-Erling Smørgrav *path = xmalloc(strlen(cp) + 1); 417efcad6b7SDag-Erling Smørgrav 418efcad6b7SDag-Erling Smørgrav /* Check for quoted filenames */ 419efcad6b7SDag-Erling Smørgrav if (*cp == '\"' || *cp == '\'') { 420efcad6b7SDag-Erling Smørgrav quot = *cp++; 421efcad6b7SDag-Erling Smørgrav 422efcad6b7SDag-Erling Smørgrav /* Search for terminating quote, unescape some chars */ 423efcad6b7SDag-Erling Smørgrav for (i = j = 0; i <= strlen(cp); i++) { 424efcad6b7SDag-Erling Smørgrav if (cp[i] == quot) { /* Found quote */ 425efcad6b7SDag-Erling Smørgrav i++; 426efcad6b7SDag-Erling Smørgrav (*path)[j] = '\0'; 427efcad6b7SDag-Erling Smørgrav break; 428efcad6b7SDag-Erling Smørgrav } 429efcad6b7SDag-Erling Smørgrav if (cp[i] == '\0') { /* End of string */ 430efcad6b7SDag-Erling Smørgrav error("Unterminated quote"); 431efcad6b7SDag-Erling Smørgrav goto fail; 432efcad6b7SDag-Erling Smørgrav } 433efcad6b7SDag-Erling Smørgrav if (cp[i] == '\\') { /* Escaped characters */ 434efcad6b7SDag-Erling Smørgrav i++; 435efcad6b7SDag-Erling Smørgrav if (cp[i] != '\'' && cp[i] != '\"' && 436efcad6b7SDag-Erling Smørgrav cp[i] != '\\') { 437d74d50a8SDag-Erling Smørgrav error("Bad escaped character '\\%c'", 438efcad6b7SDag-Erling Smørgrav cp[i]); 439efcad6b7SDag-Erling Smørgrav goto fail; 440efcad6b7SDag-Erling Smørgrav } 441efcad6b7SDag-Erling Smørgrav } 442efcad6b7SDag-Erling Smørgrav (*path)[j++] = cp[i]; 443efcad6b7SDag-Erling Smørgrav } 444efcad6b7SDag-Erling Smørgrav 445efcad6b7SDag-Erling Smørgrav if (j == 0) { 446efcad6b7SDag-Erling Smørgrav error("Empty quotes"); 447efcad6b7SDag-Erling Smørgrav goto fail; 448efcad6b7SDag-Erling Smørgrav } 449efcad6b7SDag-Erling Smørgrav *cpp = cp + i + strspn(cp + i, WHITESPACE); 450efcad6b7SDag-Erling Smørgrav } else { 451efcad6b7SDag-Erling Smørgrav /* Read to end of filename */ 452efcad6b7SDag-Erling Smørgrav end = strpbrk(cp, WHITESPACE); 453efcad6b7SDag-Erling Smørgrav if (end == NULL) 454efcad6b7SDag-Erling Smørgrav end = strchr(cp, '\0'); 455efcad6b7SDag-Erling Smørgrav *cpp = end + strspn(end, WHITESPACE); 456efcad6b7SDag-Erling Smørgrav 457efcad6b7SDag-Erling Smørgrav memcpy(*path, cp, end - cp); 458efcad6b7SDag-Erling Smørgrav (*path)[end - cp] = '\0'; 459efcad6b7SDag-Erling Smørgrav } 460efcad6b7SDag-Erling Smørgrav return (0); 461efcad6b7SDag-Erling Smørgrav 462efcad6b7SDag-Erling Smørgrav fail: 463efcad6b7SDag-Erling Smørgrav xfree(*path); 464efcad6b7SDag-Erling Smørgrav *path = NULL; 465efcad6b7SDag-Erling Smørgrav return (-1); 466efcad6b7SDag-Erling Smørgrav } 467efcad6b7SDag-Erling Smørgrav 468efcad6b7SDag-Erling Smørgrav static int 469efcad6b7SDag-Erling Smørgrav is_dir(char *path) 470efcad6b7SDag-Erling Smørgrav { 471efcad6b7SDag-Erling Smørgrav struct stat sb; 472efcad6b7SDag-Erling Smørgrav 473efcad6b7SDag-Erling Smørgrav /* XXX: report errors? */ 474efcad6b7SDag-Erling Smørgrav if (stat(path, &sb) == -1) 475efcad6b7SDag-Erling Smørgrav return(0); 476efcad6b7SDag-Erling Smørgrav 477efcad6b7SDag-Erling Smørgrav return(sb.st_mode & S_IFDIR); 478efcad6b7SDag-Erling Smørgrav } 479efcad6b7SDag-Erling Smørgrav 480efcad6b7SDag-Erling Smørgrav static int 481efcad6b7SDag-Erling Smørgrav is_reg(char *path) 482efcad6b7SDag-Erling Smørgrav { 483efcad6b7SDag-Erling Smørgrav struct stat sb; 484efcad6b7SDag-Erling Smørgrav 485efcad6b7SDag-Erling Smørgrav if (stat(path, &sb) == -1) 486efcad6b7SDag-Erling Smørgrav fatal("stat %s: %s", path, strerror(errno)); 487efcad6b7SDag-Erling Smørgrav 488efcad6b7SDag-Erling Smørgrav return(S_ISREG(sb.st_mode)); 489efcad6b7SDag-Erling Smørgrav } 490efcad6b7SDag-Erling Smørgrav 491efcad6b7SDag-Erling Smørgrav static int 492efcad6b7SDag-Erling Smørgrav remote_is_dir(struct sftp_conn *conn, char *path) 493efcad6b7SDag-Erling Smørgrav { 494efcad6b7SDag-Erling Smørgrav Attrib *a; 495efcad6b7SDag-Erling Smørgrav 496efcad6b7SDag-Erling Smørgrav /* XXX: report errors? */ 497efcad6b7SDag-Erling Smørgrav if ((a = do_stat(conn, path, 1)) == NULL) 498efcad6b7SDag-Erling Smørgrav return(0); 499efcad6b7SDag-Erling Smørgrav if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) 500efcad6b7SDag-Erling Smørgrav return(0); 501efcad6b7SDag-Erling Smørgrav return(a->perm & S_IFDIR); 502efcad6b7SDag-Erling Smørgrav } 503efcad6b7SDag-Erling Smørgrav 504efcad6b7SDag-Erling Smørgrav static int 505efcad6b7SDag-Erling Smørgrav process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 506efcad6b7SDag-Erling Smørgrav { 507efcad6b7SDag-Erling Smørgrav char *abs_src = NULL; 508efcad6b7SDag-Erling Smørgrav char *abs_dst = NULL; 509efcad6b7SDag-Erling Smørgrav char *tmp; 510efcad6b7SDag-Erling Smørgrav glob_t g; 511efcad6b7SDag-Erling Smørgrav int err = 0; 512efcad6b7SDag-Erling Smørgrav int i; 513efcad6b7SDag-Erling Smørgrav 514efcad6b7SDag-Erling Smørgrav abs_src = xstrdup(src); 515efcad6b7SDag-Erling Smørgrav abs_src = make_absolute(abs_src, pwd); 516efcad6b7SDag-Erling Smørgrav 517efcad6b7SDag-Erling Smørgrav memset(&g, 0, sizeof(g)); 518efcad6b7SDag-Erling Smørgrav debug3("Looking up %s", abs_src); 519efcad6b7SDag-Erling Smørgrav if (remote_glob(conn, abs_src, 0, NULL, &g)) { 520efcad6b7SDag-Erling Smørgrav error("File \"%s\" not found.", abs_src); 521efcad6b7SDag-Erling Smørgrav err = -1; 522efcad6b7SDag-Erling Smørgrav goto out; 523efcad6b7SDag-Erling Smørgrav } 524efcad6b7SDag-Erling Smørgrav 525efcad6b7SDag-Erling Smørgrav /* If multiple matches, dst must be a directory or unspecified */ 526efcad6b7SDag-Erling Smørgrav if (g.gl_matchc > 1 && dst && !is_dir(dst)) { 527efcad6b7SDag-Erling Smørgrav error("Multiple files match, but \"%s\" is not a directory", 528efcad6b7SDag-Erling Smørgrav dst); 529efcad6b7SDag-Erling Smørgrav err = -1; 530efcad6b7SDag-Erling Smørgrav goto out; 531efcad6b7SDag-Erling Smørgrav } 532efcad6b7SDag-Erling Smørgrav 533d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 534efcad6b7SDag-Erling Smørgrav if (infer_path(g.gl_pathv[i], &tmp)) { 535efcad6b7SDag-Erling Smørgrav err = -1; 536efcad6b7SDag-Erling Smørgrav goto out; 537efcad6b7SDag-Erling Smørgrav } 538efcad6b7SDag-Erling Smørgrav 539efcad6b7SDag-Erling Smørgrav if (g.gl_matchc == 1 && dst) { 540efcad6b7SDag-Erling Smørgrav /* If directory specified, append filename */ 541efcad6b7SDag-Erling Smørgrav if (is_dir(dst)) { 542efcad6b7SDag-Erling Smørgrav if (infer_path(g.gl_pathv[0], &tmp)) { 543efcad6b7SDag-Erling Smørgrav err = 1; 544efcad6b7SDag-Erling Smørgrav goto out; 545efcad6b7SDag-Erling Smørgrav } 546efcad6b7SDag-Erling Smørgrav abs_dst = path_append(dst, tmp); 547efcad6b7SDag-Erling Smørgrav xfree(tmp); 548efcad6b7SDag-Erling Smørgrav } else 549efcad6b7SDag-Erling Smørgrav abs_dst = xstrdup(dst); 550efcad6b7SDag-Erling Smørgrav } else if (dst) { 551efcad6b7SDag-Erling Smørgrav abs_dst = path_append(dst, tmp); 552efcad6b7SDag-Erling Smørgrav xfree(tmp); 553efcad6b7SDag-Erling Smørgrav } else 554efcad6b7SDag-Erling Smørgrav abs_dst = tmp; 555efcad6b7SDag-Erling Smørgrav 556efcad6b7SDag-Erling Smørgrav printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 557efcad6b7SDag-Erling Smørgrav if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 558efcad6b7SDag-Erling Smørgrav err = -1; 559efcad6b7SDag-Erling Smørgrav xfree(abs_dst); 560efcad6b7SDag-Erling Smørgrav abs_dst = NULL; 561efcad6b7SDag-Erling Smørgrav } 562efcad6b7SDag-Erling Smørgrav 563efcad6b7SDag-Erling Smørgrav out: 564efcad6b7SDag-Erling Smørgrav xfree(abs_src); 565efcad6b7SDag-Erling Smørgrav if (abs_dst) 566efcad6b7SDag-Erling Smørgrav xfree(abs_dst); 567efcad6b7SDag-Erling Smørgrav globfree(&g); 568efcad6b7SDag-Erling Smørgrav return(err); 569efcad6b7SDag-Erling Smørgrav } 570efcad6b7SDag-Erling Smørgrav 571efcad6b7SDag-Erling Smørgrav static int 572efcad6b7SDag-Erling Smørgrav process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 573efcad6b7SDag-Erling Smørgrav { 574efcad6b7SDag-Erling Smørgrav char *tmp_dst = NULL; 575efcad6b7SDag-Erling Smørgrav char *abs_dst = NULL; 576efcad6b7SDag-Erling Smørgrav char *tmp; 577efcad6b7SDag-Erling Smørgrav glob_t g; 578efcad6b7SDag-Erling Smørgrav int err = 0; 579efcad6b7SDag-Erling Smørgrav int i; 580efcad6b7SDag-Erling Smørgrav 581efcad6b7SDag-Erling Smørgrav if (dst) { 582efcad6b7SDag-Erling Smørgrav tmp_dst = xstrdup(dst); 583efcad6b7SDag-Erling Smørgrav tmp_dst = make_absolute(tmp_dst, pwd); 584efcad6b7SDag-Erling Smørgrav } 585efcad6b7SDag-Erling Smørgrav 586efcad6b7SDag-Erling Smørgrav memset(&g, 0, sizeof(g)); 587efcad6b7SDag-Erling Smørgrav debug3("Looking up %s", src); 588efcad6b7SDag-Erling Smørgrav if (glob(src, 0, NULL, &g)) { 589efcad6b7SDag-Erling Smørgrav error("File \"%s\" not found.", src); 590efcad6b7SDag-Erling Smørgrav err = -1; 591efcad6b7SDag-Erling Smørgrav goto out; 592efcad6b7SDag-Erling Smørgrav } 593efcad6b7SDag-Erling Smørgrav 594efcad6b7SDag-Erling Smørgrav /* If multiple matches, dst may be directory or unspecified */ 595efcad6b7SDag-Erling Smørgrav if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { 596efcad6b7SDag-Erling Smørgrav error("Multiple files match, but \"%s\" is not a directory", 597efcad6b7SDag-Erling Smørgrav tmp_dst); 598efcad6b7SDag-Erling Smørgrav err = -1; 599efcad6b7SDag-Erling Smørgrav goto out; 600efcad6b7SDag-Erling Smørgrav } 601efcad6b7SDag-Erling Smørgrav 602d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 603efcad6b7SDag-Erling Smørgrav if (!is_reg(g.gl_pathv[i])) { 604efcad6b7SDag-Erling Smørgrav error("skipping non-regular file %s", 605efcad6b7SDag-Erling Smørgrav g.gl_pathv[i]); 606efcad6b7SDag-Erling Smørgrav continue; 607efcad6b7SDag-Erling Smørgrav } 608efcad6b7SDag-Erling Smørgrav if (infer_path(g.gl_pathv[i], &tmp)) { 609efcad6b7SDag-Erling Smørgrav err = -1; 610efcad6b7SDag-Erling Smørgrav goto out; 611efcad6b7SDag-Erling Smørgrav } 612efcad6b7SDag-Erling Smørgrav 613efcad6b7SDag-Erling Smørgrav if (g.gl_matchc == 1 && tmp_dst) { 614efcad6b7SDag-Erling Smørgrav /* If directory specified, append filename */ 615efcad6b7SDag-Erling Smørgrav if (remote_is_dir(conn, tmp_dst)) { 616efcad6b7SDag-Erling Smørgrav if (infer_path(g.gl_pathv[0], &tmp)) { 617efcad6b7SDag-Erling Smørgrav err = 1; 618efcad6b7SDag-Erling Smørgrav goto out; 619efcad6b7SDag-Erling Smørgrav } 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 = xstrdup(tmp_dst); 624efcad6b7SDag-Erling Smørgrav 625efcad6b7SDag-Erling Smørgrav } else if (tmp_dst) { 626efcad6b7SDag-Erling Smørgrav abs_dst = path_append(tmp_dst, tmp); 627efcad6b7SDag-Erling Smørgrav xfree(tmp); 628efcad6b7SDag-Erling Smørgrav } else 629efcad6b7SDag-Erling Smørgrav abs_dst = make_absolute(tmp, pwd); 630efcad6b7SDag-Erling Smørgrav 631efcad6b7SDag-Erling Smørgrav printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 632efcad6b7SDag-Erling Smørgrav if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 633efcad6b7SDag-Erling Smørgrav err = -1; 634efcad6b7SDag-Erling Smørgrav } 635efcad6b7SDag-Erling Smørgrav 636efcad6b7SDag-Erling Smørgrav out: 637efcad6b7SDag-Erling Smørgrav if (abs_dst) 638efcad6b7SDag-Erling Smørgrav xfree(abs_dst); 639efcad6b7SDag-Erling Smørgrav if (tmp_dst) 640efcad6b7SDag-Erling Smørgrav xfree(tmp_dst); 641efcad6b7SDag-Erling Smørgrav globfree(&g); 642efcad6b7SDag-Erling Smørgrav return(err); 643efcad6b7SDag-Erling Smørgrav } 644efcad6b7SDag-Erling Smørgrav 645efcad6b7SDag-Erling Smørgrav static int 646efcad6b7SDag-Erling Smørgrav sdirent_comp(const void *aa, const void *bb) 647efcad6b7SDag-Erling Smørgrav { 648efcad6b7SDag-Erling Smørgrav SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; 649efcad6b7SDag-Erling Smørgrav SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; 650d74d50a8SDag-Erling Smørgrav int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; 651efcad6b7SDag-Erling Smørgrav 652d74d50a8SDag-Erling Smørgrav #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) 653d74d50a8SDag-Erling Smørgrav if (sort_flag & LS_NAME_SORT) 654d74d50a8SDag-Erling Smørgrav return (rmul * strcmp(a->filename, b->filename)); 655d74d50a8SDag-Erling Smørgrav else if (sort_flag & LS_TIME_SORT) 656d74d50a8SDag-Erling Smørgrav return (rmul * NCMP(a->a.mtime, b->a.mtime)); 657d74d50a8SDag-Erling Smørgrav else if (sort_flag & LS_SIZE_SORT) 658d74d50a8SDag-Erling Smørgrav return (rmul * NCMP(a->a.size, b->a.size)); 659d74d50a8SDag-Erling Smørgrav 660d74d50a8SDag-Erling Smørgrav fatal("Unknown ls sort type"); 661efcad6b7SDag-Erling Smørgrav } 662efcad6b7SDag-Erling Smørgrav 663efcad6b7SDag-Erling Smørgrav /* sftp ls.1 replacement for directories */ 664efcad6b7SDag-Erling Smørgrav static int 665efcad6b7SDag-Erling Smørgrav do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) 666efcad6b7SDag-Erling Smørgrav { 667043840dfSDag-Erling Smørgrav int n; 668043840dfSDag-Erling Smørgrav u_int c = 1, colspace = 0, columns = 1; 669efcad6b7SDag-Erling Smørgrav SFTP_DIRENT **d; 670efcad6b7SDag-Erling Smørgrav 671efcad6b7SDag-Erling Smørgrav if ((n = do_readdir(conn, path, &d)) != 0) 672efcad6b7SDag-Erling Smørgrav return (n); 673efcad6b7SDag-Erling Smørgrav 674d74d50a8SDag-Erling Smørgrav if (!(lflag & LS_SHORT_VIEW)) { 675043840dfSDag-Erling Smørgrav u_int m = 0, width = 80; 676efcad6b7SDag-Erling Smørgrav struct winsize ws; 677efcad6b7SDag-Erling Smørgrav char *tmp; 678efcad6b7SDag-Erling Smørgrav 679efcad6b7SDag-Erling Smørgrav /* Count entries for sort and find longest filename */ 680d74d50a8SDag-Erling Smørgrav for (n = 0; d[n] != NULL; n++) { 681d74d50a8SDag-Erling Smørgrav if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL)) 682efcad6b7SDag-Erling Smørgrav m = MAX(m, strlen(d[n]->filename)); 683d74d50a8SDag-Erling Smørgrav } 684efcad6b7SDag-Erling Smørgrav 685efcad6b7SDag-Erling Smørgrav /* Add any subpath that also needs to be counted */ 686efcad6b7SDag-Erling Smørgrav tmp = path_strip(path, strip_path); 687efcad6b7SDag-Erling Smørgrav m += strlen(tmp); 688efcad6b7SDag-Erling Smørgrav xfree(tmp); 689efcad6b7SDag-Erling Smørgrav 690efcad6b7SDag-Erling Smørgrav if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 691efcad6b7SDag-Erling Smørgrav width = ws.ws_col; 692efcad6b7SDag-Erling Smørgrav 693efcad6b7SDag-Erling Smørgrav columns = width / (m + 2); 694efcad6b7SDag-Erling Smørgrav columns = MAX(columns, 1); 695efcad6b7SDag-Erling Smørgrav colspace = width / columns; 696efcad6b7SDag-Erling Smørgrav colspace = MIN(colspace, width); 697efcad6b7SDag-Erling Smørgrav } 698efcad6b7SDag-Erling Smørgrav 699d74d50a8SDag-Erling Smørgrav if (lflag & SORT_FLAGS) { 700021d409fSDag-Erling Smørgrav for (n = 0; d[n] != NULL; n++) 701021d409fSDag-Erling Smørgrav ; /* count entries */ 702d74d50a8SDag-Erling Smørgrav sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); 703efcad6b7SDag-Erling Smørgrav qsort(d, n, sizeof(*d), sdirent_comp); 704d74d50a8SDag-Erling Smørgrav } 705efcad6b7SDag-Erling Smørgrav 706d74d50a8SDag-Erling Smørgrav for (n = 0; d[n] != NULL && !interrupted; n++) { 707efcad6b7SDag-Erling Smørgrav char *tmp, *fname; 708efcad6b7SDag-Erling Smørgrav 709d74d50a8SDag-Erling Smørgrav if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL)) 710d74d50a8SDag-Erling Smørgrav continue; 711d74d50a8SDag-Erling Smørgrav 712efcad6b7SDag-Erling Smørgrav tmp = path_append(path, d[n]->filename); 713efcad6b7SDag-Erling Smørgrav fname = path_strip(tmp, strip_path); 714efcad6b7SDag-Erling Smørgrav xfree(tmp); 715efcad6b7SDag-Erling Smørgrav 716d74d50a8SDag-Erling Smørgrav if (lflag & LS_LONG_VIEW) { 717d74d50a8SDag-Erling Smørgrav if (lflag & LS_NUMERIC_VIEW) { 718efcad6b7SDag-Erling Smørgrav char *lname; 719efcad6b7SDag-Erling Smørgrav struct stat sb; 720efcad6b7SDag-Erling Smørgrav 721efcad6b7SDag-Erling Smørgrav memset(&sb, 0, sizeof(sb)); 722efcad6b7SDag-Erling Smørgrav attrib_to_stat(&d[n]->a, &sb); 723efcad6b7SDag-Erling Smørgrav lname = ls_file(fname, &sb, 1); 724efcad6b7SDag-Erling Smørgrav printf("%s\n", lname); 725efcad6b7SDag-Erling Smørgrav xfree(lname); 726d74d50a8SDag-Erling Smørgrav } else 727d74d50a8SDag-Erling Smørgrav printf("%s\n", d[n]->longname); 728efcad6b7SDag-Erling Smørgrav } else { 729efcad6b7SDag-Erling Smørgrav printf("%-*s", colspace, fname); 730efcad6b7SDag-Erling Smørgrav if (c >= columns) { 731efcad6b7SDag-Erling Smørgrav printf("\n"); 732efcad6b7SDag-Erling Smørgrav c = 1; 733efcad6b7SDag-Erling Smørgrav } else 734efcad6b7SDag-Erling Smørgrav c++; 735efcad6b7SDag-Erling Smørgrav } 736efcad6b7SDag-Erling Smørgrav 737efcad6b7SDag-Erling Smørgrav xfree(fname); 738efcad6b7SDag-Erling Smørgrav } 739efcad6b7SDag-Erling Smørgrav 740d74d50a8SDag-Erling Smørgrav if (!(lflag & LS_LONG_VIEW) && (c != 1)) 741efcad6b7SDag-Erling Smørgrav printf("\n"); 742efcad6b7SDag-Erling Smørgrav 743efcad6b7SDag-Erling Smørgrav free_sftp_dirents(d); 744efcad6b7SDag-Erling Smørgrav return (0); 745efcad6b7SDag-Erling Smørgrav } 746efcad6b7SDag-Erling Smørgrav 747efcad6b7SDag-Erling Smørgrav /* sftp ls.1 replacement which handles path globs */ 748efcad6b7SDag-Erling Smørgrav static int 749efcad6b7SDag-Erling Smørgrav do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, 750efcad6b7SDag-Erling Smørgrav int lflag) 751efcad6b7SDag-Erling Smørgrav { 752efcad6b7SDag-Erling Smørgrav glob_t g; 753043840dfSDag-Erling Smørgrav u_int i, c = 1, colspace = 0, columns = 1; 7545e8dbd04SDag-Erling Smørgrav Attrib *a = NULL; 755efcad6b7SDag-Erling Smørgrav 756efcad6b7SDag-Erling Smørgrav memset(&g, 0, sizeof(g)); 757efcad6b7SDag-Erling Smørgrav 758efcad6b7SDag-Erling Smørgrav if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, 7595e8dbd04SDag-Erling Smørgrav NULL, &g) || (g.gl_pathc && !g.gl_matchc)) { 7605e8dbd04SDag-Erling Smørgrav if (g.gl_pathc) 7615e8dbd04SDag-Erling Smørgrav globfree(&g); 762efcad6b7SDag-Erling Smørgrav error("Can't ls: \"%s\" not found", path); 763efcad6b7SDag-Erling Smørgrav return (-1); 764efcad6b7SDag-Erling Smørgrav } 765efcad6b7SDag-Erling Smørgrav 766d74d50a8SDag-Erling Smørgrav if (interrupted) 767d74d50a8SDag-Erling Smørgrav goto out; 768d74d50a8SDag-Erling Smørgrav 769efcad6b7SDag-Erling Smørgrav /* 7705e8dbd04SDag-Erling Smørgrav * If the glob returns a single match and it is a directory, 7715e8dbd04SDag-Erling Smørgrav * then just list its contents. 772efcad6b7SDag-Erling Smørgrav */ 7735e8dbd04SDag-Erling Smørgrav if (g.gl_matchc == 1) { 7745e8dbd04SDag-Erling Smørgrav if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) { 775efcad6b7SDag-Erling Smørgrav globfree(&g); 776efcad6b7SDag-Erling Smørgrav return (-1); 777efcad6b7SDag-Erling Smørgrav } 778efcad6b7SDag-Erling Smørgrav if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 779efcad6b7SDag-Erling Smørgrav S_ISDIR(a->perm)) { 7805e8dbd04SDag-Erling Smørgrav int err; 7815e8dbd04SDag-Erling Smørgrav 7825e8dbd04SDag-Erling Smørgrav err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); 783efcad6b7SDag-Erling Smørgrav globfree(&g); 7845e8dbd04SDag-Erling Smørgrav return (err); 785efcad6b7SDag-Erling Smørgrav } 786efcad6b7SDag-Erling Smørgrav } 787efcad6b7SDag-Erling Smørgrav 788d74d50a8SDag-Erling Smørgrav if (!(lflag & LS_SHORT_VIEW)) { 789043840dfSDag-Erling Smørgrav u_int m = 0, width = 80; 790efcad6b7SDag-Erling Smørgrav struct winsize ws; 791efcad6b7SDag-Erling Smørgrav 792efcad6b7SDag-Erling Smørgrav /* Count entries for sort and find longest filename */ 793efcad6b7SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i]; i++) 794efcad6b7SDag-Erling Smørgrav m = MAX(m, strlen(g.gl_pathv[i])); 795efcad6b7SDag-Erling Smørgrav 796efcad6b7SDag-Erling Smørgrav if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 797efcad6b7SDag-Erling Smørgrav width = ws.ws_col; 798efcad6b7SDag-Erling Smørgrav 799efcad6b7SDag-Erling Smørgrav columns = width / (m + 2); 800efcad6b7SDag-Erling Smørgrav columns = MAX(columns, 1); 801efcad6b7SDag-Erling Smørgrav colspace = width / columns; 802efcad6b7SDag-Erling Smørgrav } 803efcad6b7SDag-Erling Smørgrav 8045e8dbd04SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) { 805efcad6b7SDag-Erling Smørgrav char *fname; 806efcad6b7SDag-Erling Smørgrav 807efcad6b7SDag-Erling Smørgrav fname = path_strip(g.gl_pathv[i], strip_path); 808efcad6b7SDag-Erling Smørgrav 809d74d50a8SDag-Erling Smørgrav if (lflag & LS_LONG_VIEW) { 810efcad6b7SDag-Erling Smørgrav char *lname; 811efcad6b7SDag-Erling Smørgrav struct stat sb; 812efcad6b7SDag-Erling Smørgrav 813efcad6b7SDag-Erling Smørgrav /* 814efcad6b7SDag-Erling Smørgrav * XXX: this is slow - 1 roundtrip per path 815efcad6b7SDag-Erling Smørgrav * A solution to this is to fork glob() and 816efcad6b7SDag-Erling Smørgrav * build a sftp specific version which keeps the 817efcad6b7SDag-Erling Smørgrav * attribs (which currently get thrown away) 818efcad6b7SDag-Erling Smørgrav * that the server returns as well as the filenames. 819efcad6b7SDag-Erling Smørgrav */ 820efcad6b7SDag-Erling Smørgrav memset(&sb, 0, sizeof(sb)); 8215e8dbd04SDag-Erling Smørgrav if (a == NULL) 822efcad6b7SDag-Erling Smørgrav a = do_lstat(conn, g.gl_pathv[i], 1); 823efcad6b7SDag-Erling Smørgrav if (a != NULL) 824efcad6b7SDag-Erling Smørgrav attrib_to_stat(a, &sb); 825efcad6b7SDag-Erling Smørgrav lname = ls_file(fname, &sb, 1); 826efcad6b7SDag-Erling Smørgrav printf("%s\n", lname); 827efcad6b7SDag-Erling Smørgrav xfree(lname); 828efcad6b7SDag-Erling Smørgrav } else { 829efcad6b7SDag-Erling Smørgrav printf("%-*s", colspace, fname); 830efcad6b7SDag-Erling Smørgrav if (c >= columns) { 831efcad6b7SDag-Erling Smørgrav printf("\n"); 832efcad6b7SDag-Erling Smørgrav c = 1; 833efcad6b7SDag-Erling Smørgrav } else 834efcad6b7SDag-Erling Smørgrav c++; 835efcad6b7SDag-Erling Smørgrav } 836efcad6b7SDag-Erling Smørgrav xfree(fname); 837efcad6b7SDag-Erling Smørgrav } 838efcad6b7SDag-Erling Smørgrav 839d74d50a8SDag-Erling Smørgrav if (!(lflag & LS_LONG_VIEW) && (c != 1)) 840efcad6b7SDag-Erling Smørgrav printf("\n"); 841efcad6b7SDag-Erling Smørgrav 842d74d50a8SDag-Erling Smørgrav out: 843efcad6b7SDag-Erling Smørgrav if (g.gl_pathc) 844efcad6b7SDag-Erling Smørgrav globfree(&g); 845efcad6b7SDag-Erling Smørgrav 846efcad6b7SDag-Erling Smørgrav return (0); 847efcad6b7SDag-Erling Smørgrav } 848efcad6b7SDag-Erling Smørgrav 849efcad6b7SDag-Erling Smørgrav static int 850efcad6b7SDag-Erling Smørgrav parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, 851efcad6b7SDag-Erling Smørgrav unsigned long *n_arg, char **path1, char **path2) 852efcad6b7SDag-Erling Smørgrav { 853efcad6b7SDag-Erling Smørgrav const char *cmd, *cp = *cpp; 854efcad6b7SDag-Erling Smørgrav char *cp2; 855efcad6b7SDag-Erling Smørgrav int base = 0; 856efcad6b7SDag-Erling Smørgrav long l; 857efcad6b7SDag-Erling Smørgrav int i, cmdnum; 858efcad6b7SDag-Erling Smørgrav 859efcad6b7SDag-Erling Smørgrav /* Skip leading whitespace */ 860efcad6b7SDag-Erling Smørgrav cp = cp + strspn(cp, WHITESPACE); 861efcad6b7SDag-Erling Smørgrav 862efcad6b7SDag-Erling Smørgrav /* Ignore blank lines and lines which begin with comment '#' char */ 863efcad6b7SDag-Erling Smørgrav if (*cp == '\0' || *cp == '#') 864efcad6b7SDag-Erling Smørgrav return (0); 865efcad6b7SDag-Erling Smørgrav 866efcad6b7SDag-Erling Smørgrav /* Check for leading '-' (disable error processing) */ 867efcad6b7SDag-Erling Smørgrav *iflag = 0; 868efcad6b7SDag-Erling Smørgrav if (*cp == '-') { 869efcad6b7SDag-Erling Smørgrav *iflag = 1; 870efcad6b7SDag-Erling Smørgrav cp++; 871efcad6b7SDag-Erling Smørgrav } 872efcad6b7SDag-Erling Smørgrav 873efcad6b7SDag-Erling Smørgrav /* Figure out which command we have */ 874efcad6b7SDag-Erling Smørgrav for (i = 0; cmds[i].c; i++) { 875efcad6b7SDag-Erling Smørgrav int cmdlen = strlen(cmds[i].c); 876efcad6b7SDag-Erling Smørgrav 877efcad6b7SDag-Erling Smørgrav /* Check for command followed by whitespace */ 878efcad6b7SDag-Erling Smørgrav if (!strncasecmp(cp, cmds[i].c, cmdlen) && 879efcad6b7SDag-Erling Smørgrav strchr(WHITESPACE, cp[cmdlen])) { 880efcad6b7SDag-Erling Smørgrav cp += cmdlen; 881efcad6b7SDag-Erling Smørgrav cp = cp + strspn(cp, WHITESPACE); 882efcad6b7SDag-Erling Smørgrav break; 883efcad6b7SDag-Erling Smørgrav } 884efcad6b7SDag-Erling Smørgrav } 885efcad6b7SDag-Erling Smørgrav cmdnum = cmds[i].n; 886efcad6b7SDag-Erling Smørgrav cmd = cmds[i].c; 887efcad6b7SDag-Erling Smørgrav 888efcad6b7SDag-Erling Smørgrav /* Special case */ 889efcad6b7SDag-Erling Smørgrav if (*cp == '!') { 890efcad6b7SDag-Erling Smørgrav cp++; 891efcad6b7SDag-Erling Smørgrav cmdnum = I_SHELL; 892efcad6b7SDag-Erling Smørgrav } else if (cmdnum == -1) { 893efcad6b7SDag-Erling Smørgrav error("Invalid command."); 894efcad6b7SDag-Erling Smørgrav return (-1); 895efcad6b7SDag-Erling Smørgrav } 896efcad6b7SDag-Erling Smørgrav 897efcad6b7SDag-Erling Smørgrav /* Get arguments and parse flags */ 898efcad6b7SDag-Erling Smørgrav *lflag = *pflag = *n_arg = 0; 899efcad6b7SDag-Erling Smørgrav *path1 = *path2 = NULL; 900efcad6b7SDag-Erling Smørgrav switch (cmdnum) { 901efcad6b7SDag-Erling Smørgrav case I_GET: 902efcad6b7SDag-Erling Smørgrav case I_PUT: 903efcad6b7SDag-Erling Smørgrav if (parse_getput_flags(&cp, pflag)) 904efcad6b7SDag-Erling Smørgrav return(-1); 905efcad6b7SDag-Erling Smørgrav /* Get first pathname (mandatory) */ 906efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 907efcad6b7SDag-Erling Smørgrav return(-1); 908efcad6b7SDag-Erling Smørgrav if (*path1 == NULL) { 909efcad6b7SDag-Erling Smørgrav error("You must specify at least one path after a " 910efcad6b7SDag-Erling Smørgrav "%s command.", cmd); 911efcad6b7SDag-Erling Smørgrav return(-1); 912efcad6b7SDag-Erling Smørgrav } 913efcad6b7SDag-Erling Smørgrav /* Try to get second pathname (optional) */ 914efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path2)) 915efcad6b7SDag-Erling Smørgrav return(-1); 916efcad6b7SDag-Erling Smørgrav break; 917efcad6b7SDag-Erling Smørgrav case I_RENAME: 918efcad6b7SDag-Erling Smørgrav case I_SYMLINK: 919efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 920efcad6b7SDag-Erling Smørgrav return(-1); 921efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path2)) 922efcad6b7SDag-Erling Smørgrav return(-1); 923efcad6b7SDag-Erling Smørgrav if (!*path1 || !*path2) { 924efcad6b7SDag-Erling Smørgrav error("You must specify two paths after a %s " 925efcad6b7SDag-Erling Smørgrav "command.", cmd); 926efcad6b7SDag-Erling Smørgrav return(-1); 927efcad6b7SDag-Erling Smørgrav } 928efcad6b7SDag-Erling Smørgrav break; 929efcad6b7SDag-Erling Smørgrav case I_RM: 930efcad6b7SDag-Erling Smørgrav case I_MKDIR: 931efcad6b7SDag-Erling Smørgrav case I_RMDIR: 932efcad6b7SDag-Erling Smørgrav case I_CHDIR: 933efcad6b7SDag-Erling Smørgrav case I_LCHDIR: 934efcad6b7SDag-Erling Smørgrav case I_LMKDIR: 935efcad6b7SDag-Erling Smørgrav /* Get pathname (mandatory) */ 936efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 937efcad6b7SDag-Erling Smørgrav return(-1); 938efcad6b7SDag-Erling Smørgrav if (*path1 == NULL) { 939efcad6b7SDag-Erling Smørgrav error("You must specify a path after a %s command.", 940efcad6b7SDag-Erling Smørgrav cmd); 941efcad6b7SDag-Erling Smørgrav return(-1); 942efcad6b7SDag-Erling Smørgrav } 943efcad6b7SDag-Erling Smørgrav break; 944efcad6b7SDag-Erling Smørgrav case I_LS: 945efcad6b7SDag-Erling Smørgrav if (parse_ls_flags(&cp, lflag)) 946efcad6b7SDag-Erling Smørgrav return(-1); 947efcad6b7SDag-Erling Smørgrav /* Path is optional */ 948efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 949efcad6b7SDag-Erling Smørgrav return(-1); 950efcad6b7SDag-Erling Smørgrav break; 951efcad6b7SDag-Erling Smørgrav case I_LLS: 952efcad6b7SDag-Erling Smørgrav case I_SHELL: 953efcad6b7SDag-Erling Smørgrav /* Uses the rest of the line */ 954efcad6b7SDag-Erling Smørgrav break; 955efcad6b7SDag-Erling Smørgrav case I_LUMASK: 956efcad6b7SDag-Erling Smørgrav base = 8; 957efcad6b7SDag-Erling Smørgrav case I_CHMOD: 958efcad6b7SDag-Erling Smørgrav base = 8; 959efcad6b7SDag-Erling Smørgrav case I_CHOWN: 960efcad6b7SDag-Erling Smørgrav case I_CHGRP: 961efcad6b7SDag-Erling Smørgrav /* Get numeric arg (mandatory) */ 962efcad6b7SDag-Erling Smørgrav l = strtol(cp, &cp2, base); 963efcad6b7SDag-Erling Smørgrav if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) && 964efcad6b7SDag-Erling Smørgrav errno == ERANGE) || l < 0) { 965efcad6b7SDag-Erling Smørgrav error("You must supply a numeric argument " 966efcad6b7SDag-Erling Smørgrav "to the %s command.", cmd); 967efcad6b7SDag-Erling Smørgrav return(-1); 968efcad6b7SDag-Erling Smørgrav } 969efcad6b7SDag-Erling Smørgrav cp = cp2; 970efcad6b7SDag-Erling Smørgrav *n_arg = l; 971efcad6b7SDag-Erling Smørgrav if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp)) 972efcad6b7SDag-Erling Smørgrav break; 973efcad6b7SDag-Erling Smørgrav if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) { 974efcad6b7SDag-Erling Smørgrav error("You must supply a numeric argument " 975efcad6b7SDag-Erling Smørgrav "to the %s command.", cmd); 976efcad6b7SDag-Erling Smørgrav return(-1); 977efcad6b7SDag-Erling Smørgrav } 978efcad6b7SDag-Erling Smørgrav cp += strspn(cp, WHITESPACE); 979efcad6b7SDag-Erling Smørgrav 980efcad6b7SDag-Erling Smørgrav /* Get pathname (mandatory) */ 981efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 982efcad6b7SDag-Erling Smørgrav return(-1); 983efcad6b7SDag-Erling Smørgrav if (*path1 == NULL) { 984efcad6b7SDag-Erling Smørgrav error("You must specify a path after a %s command.", 985efcad6b7SDag-Erling Smørgrav cmd); 986efcad6b7SDag-Erling Smørgrav return(-1); 987efcad6b7SDag-Erling Smørgrav } 988efcad6b7SDag-Erling Smørgrav break; 989efcad6b7SDag-Erling Smørgrav case I_QUIT: 990efcad6b7SDag-Erling Smørgrav case I_PWD: 991efcad6b7SDag-Erling Smørgrav case I_LPWD: 992efcad6b7SDag-Erling Smørgrav case I_HELP: 993efcad6b7SDag-Erling Smørgrav case I_VERSION: 994efcad6b7SDag-Erling Smørgrav case I_PROGRESS: 995efcad6b7SDag-Erling Smørgrav break; 996efcad6b7SDag-Erling Smørgrav default: 997efcad6b7SDag-Erling Smørgrav fatal("Command not implemented"); 998efcad6b7SDag-Erling Smørgrav } 999efcad6b7SDag-Erling Smørgrav 1000efcad6b7SDag-Erling Smørgrav *cpp = cp; 1001efcad6b7SDag-Erling Smørgrav return(cmdnum); 1002efcad6b7SDag-Erling Smørgrav } 1003efcad6b7SDag-Erling Smørgrav 1004efcad6b7SDag-Erling Smørgrav static int 1005efcad6b7SDag-Erling Smørgrav parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 1006efcad6b7SDag-Erling Smørgrav int err_abort) 1007efcad6b7SDag-Erling Smørgrav { 1008efcad6b7SDag-Erling Smørgrav char *path1, *path2, *tmp; 1009efcad6b7SDag-Erling Smørgrav int pflag, lflag, iflag, cmdnum, i; 1010efcad6b7SDag-Erling Smørgrav unsigned long n_arg; 1011efcad6b7SDag-Erling Smørgrav Attrib a, *aa; 1012efcad6b7SDag-Erling Smørgrav char path_buf[MAXPATHLEN]; 1013efcad6b7SDag-Erling Smørgrav int err = 0; 1014efcad6b7SDag-Erling Smørgrav glob_t g; 1015efcad6b7SDag-Erling Smørgrav 1016efcad6b7SDag-Erling Smørgrav path1 = path2 = NULL; 1017efcad6b7SDag-Erling Smørgrav cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg, 1018efcad6b7SDag-Erling Smørgrav &path1, &path2); 1019efcad6b7SDag-Erling Smørgrav 1020efcad6b7SDag-Erling Smørgrav if (iflag != 0) 1021efcad6b7SDag-Erling Smørgrav err_abort = 0; 1022efcad6b7SDag-Erling Smørgrav 1023efcad6b7SDag-Erling Smørgrav memset(&g, 0, sizeof(g)); 1024efcad6b7SDag-Erling Smørgrav 1025efcad6b7SDag-Erling Smørgrav /* Perform command */ 1026efcad6b7SDag-Erling Smørgrav switch (cmdnum) { 1027efcad6b7SDag-Erling Smørgrav case 0: 1028efcad6b7SDag-Erling Smørgrav /* Blank line */ 1029efcad6b7SDag-Erling Smørgrav break; 1030efcad6b7SDag-Erling Smørgrav case -1: 1031efcad6b7SDag-Erling Smørgrav /* Unrecognized command */ 1032efcad6b7SDag-Erling Smørgrav err = -1; 1033efcad6b7SDag-Erling Smørgrav break; 1034efcad6b7SDag-Erling Smørgrav case I_GET: 1035efcad6b7SDag-Erling Smørgrav err = process_get(conn, path1, path2, *pwd, pflag); 1036efcad6b7SDag-Erling Smørgrav break; 1037efcad6b7SDag-Erling Smørgrav case I_PUT: 1038efcad6b7SDag-Erling Smørgrav err = process_put(conn, path1, path2, *pwd, pflag); 1039efcad6b7SDag-Erling Smørgrav break; 1040efcad6b7SDag-Erling Smørgrav case I_RENAME: 1041efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1042efcad6b7SDag-Erling Smørgrav path2 = make_absolute(path2, *pwd); 1043efcad6b7SDag-Erling Smørgrav err = do_rename(conn, path1, path2); 1044efcad6b7SDag-Erling Smørgrav break; 1045efcad6b7SDag-Erling Smørgrav case I_SYMLINK: 1046efcad6b7SDag-Erling Smørgrav path2 = make_absolute(path2, *pwd); 1047efcad6b7SDag-Erling Smørgrav err = do_symlink(conn, path1, path2); 1048efcad6b7SDag-Erling Smørgrav break; 1049efcad6b7SDag-Erling Smørgrav case I_RM: 1050efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1051efcad6b7SDag-Erling Smørgrav remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1052d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1053efcad6b7SDag-Erling Smørgrav printf("Removing %s\n", g.gl_pathv[i]); 1054efcad6b7SDag-Erling Smørgrav err = do_rm(conn, g.gl_pathv[i]); 1055efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1056efcad6b7SDag-Erling Smørgrav break; 1057efcad6b7SDag-Erling Smørgrav } 1058efcad6b7SDag-Erling Smørgrav break; 1059efcad6b7SDag-Erling Smørgrav case I_MKDIR: 1060efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1061efcad6b7SDag-Erling Smørgrav attrib_clear(&a); 1062efcad6b7SDag-Erling Smørgrav a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1063efcad6b7SDag-Erling Smørgrav a.perm = 0777; 1064efcad6b7SDag-Erling Smørgrav err = do_mkdir(conn, path1, &a); 1065efcad6b7SDag-Erling Smørgrav break; 1066efcad6b7SDag-Erling Smørgrav case I_RMDIR: 1067efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1068efcad6b7SDag-Erling Smørgrav err = do_rmdir(conn, path1); 1069efcad6b7SDag-Erling Smørgrav break; 1070efcad6b7SDag-Erling Smørgrav case I_CHDIR: 1071efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1072efcad6b7SDag-Erling Smørgrav if ((tmp = do_realpath(conn, path1)) == NULL) { 1073efcad6b7SDag-Erling Smørgrav err = 1; 1074efcad6b7SDag-Erling Smørgrav break; 1075efcad6b7SDag-Erling Smørgrav } 1076efcad6b7SDag-Erling Smørgrav if ((aa = do_stat(conn, tmp, 0)) == NULL) { 1077efcad6b7SDag-Erling Smørgrav xfree(tmp); 1078efcad6b7SDag-Erling Smørgrav err = 1; 1079efcad6b7SDag-Erling Smørgrav break; 1080efcad6b7SDag-Erling Smørgrav } 1081efcad6b7SDag-Erling Smørgrav if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { 1082efcad6b7SDag-Erling Smørgrav error("Can't change directory: Can't check target"); 1083efcad6b7SDag-Erling Smørgrav xfree(tmp); 1084efcad6b7SDag-Erling Smørgrav err = 1; 1085efcad6b7SDag-Erling Smørgrav break; 1086efcad6b7SDag-Erling Smørgrav } 1087efcad6b7SDag-Erling Smørgrav if (!S_ISDIR(aa->perm)) { 1088efcad6b7SDag-Erling Smørgrav error("Can't change directory: \"%s\" is not " 1089efcad6b7SDag-Erling Smørgrav "a directory", tmp); 1090efcad6b7SDag-Erling Smørgrav xfree(tmp); 1091efcad6b7SDag-Erling Smørgrav err = 1; 1092efcad6b7SDag-Erling Smørgrav break; 1093efcad6b7SDag-Erling Smørgrav } 1094efcad6b7SDag-Erling Smørgrav xfree(*pwd); 1095efcad6b7SDag-Erling Smørgrav *pwd = tmp; 1096efcad6b7SDag-Erling Smørgrav break; 1097efcad6b7SDag-Erling Smørgrav case I_LS: 1098efcad6b7SDag-Erling Smørgrav if (!path1) { 1099efcad6b7SDag-Erling Smørgrav do_globbed_ls(conn, *pwd, *pwd, lflag); 1100efcad6b7SDag-Erling Smørgrav break; 1101efcad6b7SDag-Erling Smørgrav } 1102efcad6b7SDag-Erling Smørgrav 1103efcad6b7SDag-Erling Smørgrav /* Strip pwd off beginning of non-absolute paths */ 1104efcad6b7SDag-Erling Smørgrav tmp = NULL; 1105efcad6b7SDag-Erling Smørgrav if (*path1 != '/') 1106efcad6b7SDag-Erling Smørgrav tmp = *pwd; 1107efcad6b7SDag-Erling Smørgrav 1108efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1109efcad6b7SDag-Erling Smørgrav err = do_globbed_ls(conn, path1, tmp, lflag); 1110efcad6b7SDag-Erling Smørgrav break; 1111efcad6b7SDag-Erling Smørgrav case I_LCHDIR: 1112efcad6b7SDag-Erling Smørgrav if (chdir(path1) == -1) { 1113efcad6b7SDag-Erling Smørgrav error("Couldn't change local directory to " 1114efcad6b7SDag-Erling Smørgrav "\"%s\": %s", path1, strerror(errno)); 1115efcad6b7SDag-Erling Smørgrav err = 1; 1116efcad6b7SDag-Erling Smørgrav } 1117efcad6b7SDag-Erling Smørgrav break; 1118efcad6b7SDag-Erling Smørgrav case I_LMKDIR: 1119efcad6b7SDag-Erling Smørgrav if (mkdir(path1, 0777) == -1) { 1120efcad6b7SDag-Erling Smørgrav error("Couldn't create local directory " 1121efcad6b7SDag-Erling Smørgrav "\"%s\": %s", path1, strerror(errno)); 1122efcad6b7SDag-Erling Smørgrav err = 1; 1123efcad6b7SDag-Erling Smørgrav } 1124efcad6b7SDag-Erling Smørgrav break; 1125efcad6b7SDag-Erling Smørgrav case I_LLS: 1126efcad6b7SDag-Erling Smørgrav local_do_ls(cmd); 1127efcad6b7SDag-Erling Smørgrav break; 1128efcad6b7SDag-Erling Smørgrav case I_SHELL: 1129efcad6b7SDag-Erling Smørgrav local_do_shell(cmd); 1130efcad6b7SDag-Erling Smørgrav break; 1131efcad6b7SDag-Erling Smørgrav case I_LUMASK: 1132efcad6b7SDag-Erling Smørgrav umask(n_arg); 1133efcad6b7SDag-Erling Smørgrav printf("Local umask: %03lo\n", n_arg); 1134efcad6b7SDag-Erling Smørgrav break; 1135efcad6b7SDag-Erling Smørgrav case I_CHMOD: 1136efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1137efcad6b7SDag-Erling Smørgrav attrib_clear(&a); 1138efcad6b7SDag-Erling Smørgrav a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1139efcad6b7SDag-Erling Smørgrav a.perm = n_arg; 1140efcad6b7SDag-Erling Smørgrav remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1141d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1142efcad6b7SDag-Erling Smørgrav printf("Changing mode on %s\n", g.gl_pathv[i]); 1143efcad6b7SDag-Erling Smørgrav err = do_setstat(conn, g.gl_pathv[i], &a); 1144efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1145efcad6b7SDag-Erling Smørgrav break; 1146efcad6b7SDag-Erling Smørgrav } 1147efcad6b7SDag-Erling Smørgrav break; 1148efcad6b7SDag-Erling Smørgrav case I_CHOWN: 1149efcad6b7SDag-Erling Smørgrav case I_CHGRP: 1150efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1151efcad6b7SDag-Erling Smørgrav remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1152d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1153efcad6b7SDag-Erling Smørgrav if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1154efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1155efcad6b7SDag-Erling Smørgrav break; 1156efcad6b7SDag-Erling Smørgrav else 1157efcad6b7SDag-Erling Smørgrav continue; 1158efcad6b7SDag-Erling Smørgrav } 1159efcad6b7SDag-Erling Smørgrav if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1160efcad6b7SDag-Erling Smørgrav error("Can't get current ownership of " 1161efcad6b7SDag-Erling Smørgrav "remote file \"%s\"", g.gl_pathv[i]); 1162efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1163efcad6b7SDag-Erling Smørgrav break; 1164efcad6b7SDag-Erling Smørgrav else 1165efcad6b7SDag-Erling Smørgrav continue; 1166efcad6b7SDag-Erling Smørgrav } 1167efcad6b7SDag-Erling Smørgrav aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1168efcad6b7SDag-Erling Smørgrav if (cmdnum == I_CHOWN) { 1169efcad6b7SDag-Erling Smørgrav printf("Changing owner on %s\n", g.gl_pathv[i]); 1170efcad6b7SDag-Erling Smørgrav aa->uid = n_arg; 1171efcad6b7SDag-Erling Smørgrav } else { 1172efcad6b7SDag-Erling Smørgrav printf("Changing group on %s\n", g.gl_pathv[i]); 1173efcad6b7SDag-Erling Smørgrav aa->gid = n_arg; 1174efcad6b7SDag-Erling Smørgrav } 1175efcad6b7SDag-Erling Smørgrav err = do_setstat(conn, g.gl_pathv[i], aa); 1176efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1177efcad6b7SDag-Erling Smørgrav break; 1178efcad6b7SDag-Erling Smørgrav } 1179efcad6b7SDag-Erling Smørgrav break; 1180efcad6b7SDag-Erling Smørgrav case I_PWD: 1181efcad6b7SDag-Erling Smørgrav printf("Remote working directory: %s\n", *pwd); 1182efcad6b7SDag-Erling Smørgrav break; 1183efcad6b7SDag-Erling Smørgrav case I_LPWD: 1184efcad6b7SDag-Erling Smørgrav if (!getcwd(path_buf, sizeof(path_buf))) { 1185efcad6b7SDag-Erling Smørgrav error("Couldn't get local cwd: %s", strerror(errno)); 1186efcad6b7SDag-Erling Smørgrav err = -1; 1187efcad6b7SDag-Erling Smørgrav break; 1188efcad6b7SDag-Erling Smørgrav } 1189efcad6b7SDag-Erling Smørgrav printf("Local working directory: %s\n", path_buf); 1190efcad6b7SDag-Erling Smørgrav break; 1191efcad6b7SDag-Erling Smørgrav case I_QUIT: 1192efcad6b7SDag-Erling Smørgrav /* Processed below */ 1193efcad6b7SDag-Erling Smørgrav break; 1194efcad6b7SDag-Erling Smørgrav case I_HELP: 1195efcad6b7SDag-Erling Smørgrav help(); 1196efcad6b7SDag-Erling Smørgrav break; 1197efcad6b7SDag-Erling Smørgrav case I_VERSION: 1198efcad6b7SDag-Erling Smørgrav printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1199efcad6b7SDag-Erling Smørgrav break; 1200efcad6b7SDag-Erling Smørgrav case I_PROGRESS: 1201efcad6b7SDag-Erling Smørgrav showprogress = !showprogress; 1202efcad6b7SDag-Erling Smørgrav if (showprogress) 1203efcad6b7SDag-Erling Smørgrav printf("Progress meter enabled\n"); 1204efcad6b7SDag-Erling Smørgrav else 1205efcad6b7SDag-Erling Smørgrav printf("Progress meter disabled\n"); 1206efcad6b7SDag-Erling Smørgrav break; 1207efcad6b7SDag-Erling Smørgrav default: 1208efcad6b7SDag-Erling Smørgrav fatal("%d is not implemented", cmdnum); 1209efcad6b7SDag-Erling Smørgrav } 1210efcad6b7SDag-Erling Smørgrav 1211efcad6b7SDag-Erling Smørgrav if (g.gl_pathc) 1212efcad6b7SDag-Erling Smørgrav globfree(&g); 1213efcad6b7SDag-Erling Smørgrav if (path1) 1214efcad6b7SDag-Erling Smørgrav xfree(path1); 1215efcad6b7SDag-Erling Smørgrav if (path2) 1216efcad6b7SDag-Erling Smørgrav xfree(path2); 1217efcad6b7SDag-Erling Smørgrav 1218efcad6b7SDag-Erling Smørgrav /* If an unignored error occurs in batch mode we should abort. */ 1219efcad6b7SDag-Erling Smørgrav if (err_abort && err != 0) 1220efcad6b7SDag-Erling Smørgrav return (-1); 1221efcad6b7SDag-Erling Smørgrav else if (cmdnum == I_QUIT) 1222efcad6b7SDag-Erling Smørgrav return (1); 1223efcad6b7SDag-Erling Smørgrav 1224efcad6b7SDag-Erling Smørgrav return (0); 1225efcad6b7SDag-Erling Smørgrav } 1226efcad6b7SDag-Erling Smørgrav 12275e8dbd04SDag-Erling Smørgrav #ifdef USE_LIBEDIT 12285e8dbd04SDag-Erling Smørgrav static char * 12295e8dbd04SDag-Erling Smørgrav prompt(EditLine *el) 12305e8dbd04SDag-Erling Smørgrav { 12315e8dbd04SDag-Erling Smørgrav return ("sftp> "); 12325e8dbd04SDag-Erling Smørgrav } 12335e8dbd04SDag-Erling Smørgrav #endif 12345e8dbd04SDag-Erling Smørgrav 1235efcad6b7SDag-Erling Smørgrav int 1236efcad6b7SDag-Erling Smørgrav interactive_loop(int fd_in, int fd_out, char *file1, char *file2) 1237efcad6b7SDag-Erling Smørgrav { 1238efcad6b7SDag-Erling Smørgrav char *pwd; 1239efcad6b7SDag-Erling Smørgrav char *dir = NULL; 1240efcad6b7SDag-Erling Smørgrav char cmd[2048]; 1241efcad6b7SDag-Erling Smørgrav struct sftp_conn *conn; 1242043840dfSDag-Erling Smørgrav int err, interactive; 12435e8dbd04SDag-Erling Smørgrav EditLine *el = NULL; 12445e8dbd04SDag-Erling Smørgrav #ifdef USE_LIBEDIT 12455e8dbd04SDag-Erling Smørgrav History *hl = NULL; 12465e8dbd04SDag-Erling Smørgrav HistEvent hev; 12475e8dbd04SDag-Erling Smørgrav extern char *__progname; 12485e8dbd04SDag-Erling Smørgrav 12495e8dbd04SDag-Erling Smørgrav if (!batchmode && isatty(STDIN_FILENO)) { 12505e8dbd04SDag-Erling Smørgrav if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) 12515e8dbd04SDag-Erling Smørgrav fatal("Couldn't initialise editline"); 12525e8dbd04SDag-Erling Smørgrav if ((hl = history_init()) == NULL) 12535e8dbd04SDag-Erling Smørgrav fatal("Couldn't initialise editline history"); 12545e8dbd04SDag-Erling Smørgrav history(hl, &hev, H_SETSIZE, 100); 12555e8dbd04SDag-Erling Smørgrav el_set(el, EL_HIST, history, hl); 12565e8dbd04SDag-Erling Smørgrav 12575e8dbd04SDag-Erling Smørgrav el_set(el, EL_PROMPT, prompt); 12585e8dbd04SDag-Erling Smørgrav el_set(el, EL_EDITOR, "emacs"); 12595e8dbd04SDag-Erling Smørgrav el_set(el, EL_TERMINAL, NULL); 12605e8dbd04SDag-Erling Smørgrav el_set(el, EL_SIGNAL, 1); 12615e8dbd04SDag-Erling Smørgrav el_source(el, NULL); 12625e8dbd04SDag-Erling Smørgrav } 12635e8dbd04SDag-Erling Smørgrav #endif /* USE_LIBEDIT */ 1264efcad6b7SDag-Erling Smørgrav 1265efcad6b7SDag-Erling Smørgrav conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); 1266efcad6b7SDag-Erling Smørgrav if (conn == NULL) 1267efcad6b7SDag-Erling Smørgrav fatal("Couldn't initialise connection to server"); 1268efcad6b7SDag-Erling Smørgrav 1269efcad6b7SDag-Erling Smørgrav pwd = do_realpath(conn, "."); 1270efcad6b7SDag-Erling Smørgrav if (pwd == NULL) 1271efcad6b7SDag-Erling Smørgrav fatal("Need cwd"); 1272efcad6b7SDag-Erling Smørgrav 1273efcad6b7SDag-Erling Smørgrav if (file1 != NULL) { 1274efcad6b7SDag-Erling Smørgrav dir = xstrdup(file1); 1275efcad6b7SDag-Erling Smørgrav dir = make_absolute(dir, pwd); 1276efcad6b7SDag-Erling Smørgrav 1277efcad6b7SDag-Erling Smørgrav if (remote_is_dir(conn, dir) && file2 == NULL) { 1278efcad6b7SDag-Erling Smørgrav printf("Changing to: %s\n", dir); 1279efcad6b7SDag-Erling Smørgrav snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 12805e8dbd04SDag-Erling Smørgrav if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) { 12815e8dbd04SDag-Erling Smørgrav xfree(dir); 12825e8dbd04SDag-Erling Smørgrav xfree(pwd); 1283efcad6b7SDag-Erling Smørgrav return (-1); 12845e8dbd04SDag-Erling Smørgrav } 1285efcad6b7SDag-Erling Smørgrav } else { 1286efcad6b7SDag-Erling Smørgrav if (file2 == NULL) 1287efcad6b7SDag-Erling Smørgrav snprintf(cmd, sizeof cmd, "get %s", dir); 1288efcad6b7SDag-Erling Smørgrav else 1289efcad6b7SDag-Erling Smørgrav snprintf(cmd, sizeof cmd, "get %s %s", dir, 1290efcad6b7SDag-Erling Smørgrav file2); 1291efcad6b7SDag-Erling Smørgrav 1292efcad6b7SDag-Erling Smørgrav err = parse_dispatch_command(conn, cmd, &pwd, 1); 1293efcad6b7SDag-Erling Smørgrav xfree(dir); 1294efcad6b7SDag-Erling Smørgrav xfree(pwd); 1295efcad6b7SDag-Erling Smørgrav return (err); 1296efcad6b7SDag-Erling Smørgrav } 1297efcad6b7SDag-Erling Smørgrav xfree(dir); 1298efcad6b7SDag-Erling Smørgrav } 1299efcad6b7SDag-Erling Smørgrav 1300043840dfSDag-Erling Smørgrav #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF) 1301efcad6b7SDag-Erling Smørgrav setvbuf(stdout, NULL, _IOLBF, 0); 1302efcad6b7SDag-Erling Smørgrav setvbuf(infile, NULL, _IOLBF, 0); 1303efcad6b7SDag-Erling Smørgrav #else 1304efcad6b7SDag-Erling Smørgrav setlinebuf(stdout); 1305efcad6b7SDag-Erling Smørgrav setlinebuf(infile); 1306efcad6b7SDag-Erling Smørgrav #endif 1307efcad6b7SDag-Erling Smørgrav 1308043840dfSDag-Erling Smørgrav interactive = !batchmode && isatty(STDIN_FILENO); 1309efcad6b7SDag-Erling Smørgrav err = 0; 1310efcad6b7SDag-Erling Smørgrav for (;;) { 1311efcad6b7SDag-Erling Smørgrav char *cp; 1312efcad6b7SDag-Erling Smørgrav 1313d74d50a8SDag-Erling Smørgrav signal(SIGINT, SIG_IGN); 1314d74d50a8SDag-Erling Smørgrav 13155e8dbd04SDag-Erling Smørgrav if (el == NULL) { 1316043840dfSDag-Erling Smørgrav if (interactive) 1317efcad6b7SDag-Erling Smørgrav printf("sftp> "); 1318efcad6b7SDag-Erling Smørgrav if (fgets(cmd, sizeof(cmd), infile) == NULL) { 1319043840dfSDag-Erling Smørgrav if (interactive) 1320efcad6b7SDag-Erling Smørgrav printf("\n"); 1321efcad6b7SDag-Erling Smørgrav break; 1322efcad6b7SDag-Erling Smørgrav } 1323043840dfSDag-Erling Smørgrav if (!interactive) { /* Echo command */ 1324043840dfSDag-Erling Smørgrav printf("sftp> %s", cmd); 1325043840dfSDag-Erling Smørgrav if (strlen(cmd) > 0 && 1326043840dfSDag-Erling Smørgrav cmd[strlen(cmd) - 1] != '\n') 1327043840dfSDag-Erling Smørgrav printf("\n"); 1328043840dfSDag-Erling Smørgrav } 13295e8dbd04SDag-Erling Smørgrav } else { 13305e8dbd04SDag-Erling Smørgrav #ifdef USE_LIBEDIT 13315e8dbd04SDag-Erling Smørgrav const char *line; 13325e8dbd04SDag-Erling Smørgrav int count = 0; 13335e8dbd04SDag-Erling Smørgrav 1334043840dfSDag-Erling Smørgrav if ((line = el_gets(el, &count)) == NULL || count <= 0) { 1335043840dfSDag-Erling Smørgrav printf("\n"); 13365e8dbd04SDag-Erling Smørgrav break; 1337043840dfSDag-Erling Smørgrav } 13385e8dbd04SDag-Erling Smørgrav history(hl, &hev, H_ENTER, line); 13395e8dbd04SDag-Erling Smørgrav if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { 13405e8dbd04SDag-Erling Smørgrav fprintf(stderr, "Error: input line too long\n"); 13415e8dbd04SDag-Erling Smørgrav continue; 13425e8dbd04SDag-Erling Smørgrav } 13435e8dbd04SDag-Erling Smørgrav #endif /* USE_LIBEDIT */ 13445e8dbd04SDag-Erling Smørgrav } 1345efcad6b7SDag-Erling Smørgrav 1346efcad6b7SDag-Erling Smørgrav cp = strrchr(cmd, '\n'); 1347efcad6b7SDag-Erling Smørgrav if (cp) 1348efcad6b7SDag-Erling Smørgrav *cp = '\0'; 1349efcad6b7SDag-Erling Smørgrav 1350d74d50a8SDag-Erling Smørgrav /* Handle user interrupts gracefully during commands */ 1351d74d50a8SDag-Erling Smørgrav interrupted = 0; 1352d74d50a8SDag-Erling Smørgrav signal(SIGINT, cmd_interrupt); 1353d74d50a8SDag-Erling Smørgrav 1354efcad6b7SDag-Erling Smørgrav err = parse_dispatch_command(conn, cmd, &pwd, batchmode); 1355efcad6b7SDag-Erling Smørgrav if (err != 0) 1356efcad6b7SDag-Erling Smørgrav break; 1357efcad6b7SDag-Erling Smørgrav } 1358efcad6b7SDag-Erling Smørgrav xfree(pwd); 1359efcad6b7SDag-Erling Smørgrav 1360043840dfSDag-Erling Smørgrav #ifdef USE_LIBEDIT 1361043840dfSDag-Erling Smørgrav if (el != NULL) 1362043840dfSDag-Erling Smørgrav el_end(el); 1363043840dfSDag-Erling Smørgrav #endif /* USE_LIBEDIT */ 1364043840dfSDag-Erling Smørgrav 1365efcad6b7SDag-Erling Smørgrav /* err == 1 signifies normal "quit" exit */ 1366efcad6b7SDag-Erling Smørgrav return (err >= 0 ? 0 : -1); 1367efcad6b7SDag-Erling Smørgrav } 1368d0c8c0bcSDag-Erling Smørgrav 1369ae1f160dSDag-Erling Smørgrav static void 1370d95e11bfSDag-Erling Smørgrav connect_to_server(char *path, char **args, int *in, int *out) 13711e8db6e2SBrian Feldman { 13721e8db6e2SBrian Feldman int c_in, c_out; 1373ee21a45fSDag-Erling Smørgrav 13741e8db6e2SBrian Feldman #ifdef USE_PIPES 13751e8db6e2SBrian Feldman int pin[2], pout[2]; 1376ee21a45fSDag-Erling Smørgrav 13771e8db6e2SBrian Feldman if ((pipe(pin) == -1) || (pipe(pout) == -1)) 13781e8db6e2SBrian Feldman fatal("pipe: %s", strerror(errno)); 13791e8db6e2SBrian Feldman *in = pin[0]; 13801e8db6e2SBrian Feldman *out = pout[1]; 13811e8db6e2SBrian Feldman c_in = pout[0]; 13821e8db6e2SBrian Feldman c_out = pin[1]; 13831e8db6e2SBrian Feldman #else /* USE_PIPES */ 13841e8db6e2SBrian Feldman int inout[2]; 1385ee21a45fSDag-Erling Smørgrav 13861e8db6e2SBrian Feldman if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 13871e8db6e2SBrian Feldman fatal("socketpair: %s", strerror(errno)); 13881e8db6e2SBrian Feldman *in = *out = inout[0]; 13891e8db6e2SBrian Feldman c_in = c_out = inout[1]; 13901e8db6e2SBrian Feldman #endif /* USE_PIPES */ 13911e8db6e2SBrian Feldman 1392d95e11bfSDag-Erling Smørgrav if ((sshpid = fork()) == -1) 13931e8db6e2SBrian Feldman fatal("fork: %s", strerror(errno)); 1394d95e11bfSDag-Erling Smørgrav else if (sshpid == 0) { 13951e8db6e2SBrian Feldman if ((dup2(c_in, STDIN_FILENO) == -1) || 13961e8db6e2SBrian Feldman (dup2(c_out, STDOUT_FILENO) == -1)) { 13971e8db6e2SBrian Feldman fprintf(stderr, "dup2: %s\n", strerror(errno)); 1398d74d50a8SDag-Erling Smørgrav _exit(1); 13991e8db6e2SBrian Feldman } 14001e8db6e2SBrian Feldman close(*in); 14011e8db6e2SBrian Feldman close(*out); 14021e8db6e2SBrian Feldman close(c_in); 14031e8db6e2SBrian Feldman close(c_out); 1404d74d50a8SDag-Erling Smørgrav 1405d74d50a8SDag-Erling Smørgrav /* 1406d74d50a8SDag-Erling Smørgrav * The underlying ssh is in the same process group, so we must 1407d74d50a8SDag-Erling Smørgrav * ignore SIGINT if we want to gracefully abort commands, 1408d74d50a8SDag-Erling Smørgrav * otherwise the signal will make it to the ssh process and 1409d74d50a8SDag-Erling Smørgrav * kill it too 1410d74d50a8SDag-Erling Smørgrav */ 1411d74d50a8SDag-Erling Smørgrav signal(SIGINT, SIG_IGN); 1412d74d50a8SDag-Erling Smørgrav execvp(path, args); 1413ae1f160dSDag-Erling Smørgrav fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 1414d74d50a8SDag-Erling Smørgrav _exit(1); 14151e8db6e2SBrian Feldman } 14161e8db6e2SBrian Feldman 1417d95e11bfSDag-Erling Smørgrav signal(SIGTERM, killchild); 1418d95e11bfSDag-Erling Smørgrav signal(SIGINT, killchild); 1419d95e11bfSDag-Erling Smørgrav signal(SIGHUP, killchild); 14201e8db6e2SBrian Feldman close(c_in); 14211e8db6e2SBrian Feldman close(c_out); 14221e8db6e2SBrian Feldman } 14231e8db6e2SBrian Feldman 1424ae1f160dSDag-Erling Smørgrav static void 14251e8db6e2SBrian Feldman usage(void) 14261e8db6e2SBrian Feldman { 1427ae1f160dSDag-Erling Smørgrav extern char *__progname; 1428ae1f160dSDag-Erling Smørgrav 1429ae1f160dSDag-Erling Smørgrav fprintf(stderr, 1430efcad6b7SDag-Erling Smørgrav "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" 1431efcad6b7SDag-Erling Smørgrav " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" 1432efcad6b7SDag-Erling Smørgrav " [-S program] [-s subsystem | sftp_server] host\n" 1433efcad6b7SDag-Erling Smørgrav " %s [[user@]host[:file [file]]]\n" 1434efcad6b7SDag-Erling Smørgrav " %s [[user@]host[:dir[/]]]\n" 1435efcad6b7SDag-Erling Smørgrav " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); 14361e8db6e2SBrian Feldman exit(1); 14371e8db6e2SBrian Feldman } 14381e8db6e2SBrian Feldman 14391e8db6e2SBrian Feldman int 14401e8db6e2SBrian Feldman main(int argc, char **argv) 14411e8db6e2SBrian Feldman { 1442d0c8c0bcSDag-Erling Smørgrav int in, out, ch, err; 1443d74d50a8SDag-Erling Smørgrav char *host, *userhost, *cp, *file2 = NULL; 1444ae1f160dSDag-Erling Smørgrav int debug_level = 0, sshver = 2; 1445ae1f160dSDag-Erling Smørgrav char *file1 = NULL, *sftp_server = NULL; 1446ae1f160dSDag-Erling Smørgrav char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 1447ae1f160dSDag-Erling Smørgrav LogLevel ll = SYSLOG_LEVEL_INFO; 1448ae1f160dSDag-Erling Smørgrav arglist args; 14491e8db6e2SBrian Feldman extern int optind; 14501e8db6e2SBrian Feldman extern char *optarg; 14511e8db6e2SBrian Feldman 1452021d409fSDag-Erling Smørgrav /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1453021d409fSDag-Erling Smørgrav sanitise_stdfd(); 1454021d409fSDag-Erling Smørgrav 1455d95e11bfSDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 1456021d409fSDag-Erling Smørgrav memset(&args, '\0', sizeof(args)); 1457ae1f160dSDag-Erling Smørgrav args.list = NULL; 1458021d409fSDag-Erling Smørgrav addargs(&args, ssh_program); 1459ae1f160dSDag-Erling Smørgrav addargs(&args, "-oForwardX11 no"); 1460ae1f160dSDag-Erling Smørgrav addargs(&args, "-oForwardAgent no"); 1461021d409fSDag-Erling Smørgrav addargs(&args, "-oPermitLocalCommand no"); 1462ae1f160dSDag-Erling Smørgrav addargs(&args, "-oClearAllForwardings yes"); 1463efcad6b7SDag-Erling Smørgrav 1464ae1f160dSDag-Erling Smørgrav ll = SYSLOG_LEVEL_INFO; 1465efcad6b7SDag-Erling Smørgrav infile = stdin; 14661e8db6e2SBrian Feldman 1467ae1f160dSDag-Erling Smørgrav while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { 14681e8db6e2SBrian Feldman switch (ch) { 14691e8db6e2SBrian Feldman case 'C': 1470ae1f160dSDag-Erling Smørgrav addargs(&args, "-C"); 14711e8db6e2SBrian Feldman break; 14721e8db6e2SBrian Feldman case 'v': 1473ae1f160dSDag-Erling Smørgrav if (debug_level < 3) { 1474ae1f160dSDag-Erling Smørgrav addargs(&args, "-v"); 1475ae1f160dSDag-Erling Smørgrav ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 1476ae1f160dSDag-Erling Smørgrav } 1477ae1f160dSDag-Erling Smørgrav debug_level++; 14781e8db6e2SBrian Feldman break; 1479ae1f160dSDag-Erling Smørgrav case 'F': 14801e8db6e2SBrian Feldman case 'o': 1481ae1f160dSDag-Erling Smørgrav addargs(&args, "-%c%s", ch, optarg); 14821e8db6e2SBrian Feldman break; 14831e8db6e2SBrian Feldman case '1': 1484ae1f160dSDag-Erling Smørgrav sshver = 1; 14851e8db6e2SBrian Feldman if (sftp_server == NULL) 14861e8db6e2SBrian Feldman sftp_server = _PATH_SFTP_SERVER; 14871e8db6e2SBrian Feldman break; 14881e8db6e2SBrian Feldman case 's': 14891e8db6e2SBrian Feldman sftp_server = optarg; 14901e8db6e2SBrian Feldman break; 14911e8db6e2SBrian Feldman case 'S': 14921e8db6e2SBrian Feldman ssh_program = optarg; 1493021d409fSDag-Erling Smørgrav replacearg(&args, 0, "%s", ssh_program); 14941e8db6e2SBrian Feldman break; 14951e8db6e2SBrian Feldman case 'b': 1496efcad6b7SDag-Erling Smørgrav if (batchmode) 1497efcad6b7SDag-Erling Smørgrav fatal("Batch file already specified."); 1498efcad6b7SDag-Erling Smørgrav 1499efcad6b7SDag-Erling Smørgrav /* Allow "-" as stdin */ 1500efcad6b7SDag-Erling Smørgrav if (strcmp(optarg, "-") != 0 && 1501efcad6b7SDag-Erling Smørgrav (infile = fopen(optarg, "r")) == NULL) 15021e8db6e2SBrian Feldman fatal("%s (%s).", strerror(errno), optarg); 1503d0c8c0bcSDag-Erling Smørgrav showprogress = 0; 1504efcad6b7SDag-Erling Smørgrav batchmode = 1; 15055e8dbd04SDag-Erling Smørgrav addargs(&args, "-obatchmode yes"); 15061e8db6e2SBrian Feldman break; 1507ae1f160dSDag-Erling Smørgrav case 'P': 1508ae1f160dSDag-Erling Smørgrav sftp_direct = optarg; 1509ae1f160dSDag-Erling Smørgrav break; 1510ae1f160dSDag-Erling Smørgrav case 'B': 1511ae1f160dSDag-Erling Smørgrav copy_buffer_len = strtol(optarg, &cp, 10); 1512ae1f160dSDag-Erling Smørgrav if (copy_buffer_len == 0 || *cp != '\0') 1513ae1f160dSDag-Erling Smørgrav fatal("Invalid buffer size \"%s\"", optarg); 1514ae1f160dSDag-Erling Smørgrav break; 1515ae1f160dSDag-Erling Smørgrav case 'R': 1516ae1f160dSDag-Erling Smørgrav num_requests = strtol(optarg, &cp, 10); 1517ae1f160dSDag-Erling Smørgrav if (num_requests == 0 || *cp != '\0') 1518ae1f160dSDag-Erling Smørgrav fatal("Invalid number of requests \"%s\"", 1519ae1f160dSDag-Erling Smørgrav optarg); 1520ae1f160dSDag-Erling Smørgrav break; 15211e8db6e2SBrian Feldman case 'h': 15221e8db6e2SBrian Feldman default: 15231e8db6e2SBrian Feldman usage(); 15241e8db6e2SBrian Feldman } 15251e8db6e2SBrian Feldman } 15261e8db6e2SBrian Feldman 152752028650SDag-Erling Smørgrav if (!isatty(STDERR_FILENO)) 152852028650SDag-Erling Smørgrav showprogress = 0; 152952028650SDag-Erling Smørgrav 1530545d5ecaSDag-Erling Smørgrav log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 1531545d5ecaSDag-Erling Smørgrav 1532ae1f160dSDag-Erling Smørgrav if (sftp_direct == NULL) { 15331e8db6e2SBrian Feldman if (optind == argc || argc > (optind + 2)) 15341e8db6e2SBrian Feldman usage(); 15351e8db6e2SBrian Feldman 15361e8db6e2SBrian Feldman userhost = xstrdup(argv[optind]); 15371e8db6e2SBrian Feldman file2 = argv[optind+1]; 15381e8db6e2SBrian Feldman 1539d0c8c0bcSDag-Erling Smørgrav if ((host = strrchr(userhost, '@')) == NULL) 15401e8db6e2SBrian Feldman host = userhost; 15411e8db6e2SBrian Feldman else { 15421e8db6e2SBrian Feldman *host++ = '\0'; 15431e8db6e2SBrian Feldman if (!userhost[0]) { 15441e8db6e2SBrian Feldman fprintf(stderr, "Missing username\n"); 15451e8db6e2SBrian Feldman usage(); 15461e8db6e2SBrian Feldman } 1547ae1f160dSDag-Erling Smørgrav addargs(&args, "-l%s",userhost); 15481e8db6e2SBrian Feldman } 15491e8db6e2SBrian Feldman 1550efcad6b7SDag-Erling Smørgrav if ((cp = colon(host)) != NULL) { 1551efcad6b7SDag-Erling Smørgrav *cp++ = '\0'; 1552efcad6b7SDag-Erling Smørgrav file1 = cp; 1553efcad6b7SDag-Erling Smørgrav } 1554efcad6b7SDag-Erling Smørgrav 15551e8db6e2SBrian Feldman host = cleanhostname(host); 15561e8db6e2SBrian Feldman if (!*host) { 15571e8db6e2SBrian Feldman fprintf(stderr, "Missing hostname\n"); 15581e8db6e2SBrian Feldman usage(); 15591e8db6e2SBrian Feldman } 15601e8db6e2SBrian Feldman 1561ae1f160dSDag-Erling Smørgrav addargs(&args, "-oProtocol %d", sshver); 15621e8db6e2SBrian Feldman 1563ae1f160dSDag-Erling Smørgrav /* no subsystem if the server-spec contains a '/' */ 1564ae1f160dSDag-Erling Smørgrav if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 1565ae1f160dSDag-Erling Smørgrav addargs(&args, "-s"); 1566ae1f160dSDag-Erling Smørgrav 1567ae1f160dSDag-Erling Smørgrav addargs(&args, "%s", host); 1568ae1f160dSDag-Erling Smørgrav addargs(&args, "%s", (sftp_server != NULL ? 1569ae1f160dSDag-Erling Smørgrav sftp_server : "sftp")); 15701e8db6e2SBrian Feldman 1571efcad6b7SDag-Erling Smørgrav if (!batchmode) 15721e8db6e2SBrian Feldman fprintf(stderr, "Connecting to %s...\n", host); 1573d95e11bfSDag-Erling Smørgrav connect_to_server(ssh_program, args.list, &in, &out); 1574ae1f160dSDag-Erling Smørgrav } else { 1575ae1f160dSDag-Erling Smørgrav args.list = NULL; 1576ae1f160dSDag-Erling Smørgrav addargs(&args, "sftp-server"); 15771e8db6e2SBrian Feldman 1578efcad6b7SDag-Erling Smørgrav if (!batchmode) 1579ae1f160dSDag-Erling Smørgrav fprintf(stderr, "Attaching to %s...\n", sftp_direct); 1580d95e11bfSDag-Erling Smørgrav connect_to_server(sftp_direct, args.list, &in, &out); 1581ae1f160dSDag-Erling Smørgrav } 1582021d409fSDag-Erling Smørgrav freeargs(&args); 15831e8db6e2SBrian Feldman 1584d0c8c0bcSDag-Erling Smørgrav err = interactive_loop(in, out, file1, file2); 15851e8db6e2SBrian Feldman 158683d2307dSDag-Erling Smørgrav #if !defined(USE_PIPES) 158783d2307dSDag-Erling Smørgrav shutdown(in, SHUT_RDWR); 158883d2307dSDag-Erling Smørgrav shutdown(out, SHUT_RDWR); 158983d2307dSDag-Erling Smørgrav #endif 159083d2307dSDag-Erling Smørgrav 15911e8db6e2SBrian Feldman close(in); 15921e8db6e2SBrian Feldman close(out); 1593efcad6b7SDag-Erling Smørgrav if (batchmode) 15941e8db6e2SBrian Feldman fclose(infile); 15951e8db6e2SBrian Feldman 1596545d5ecaSDag-Erling Smørgrav while (waitpid(sshpid, NULL, 0) == -1) 1597545d5ecaSDag-Erling Smørgrav if (errno != EINTR) 1598545d5ecaSDag-Erling Smørgrav fatal("Couldn't wait for ssh process: %s", 1599545d5ecaSDag-Erling Smørgrav strerror(errno)); 16001e8db6e2SBrian Feldman 1601d0c8c0bcSDag-Erling Smørgrav exit(err == 0 ? 0 : 1); 16021e8db6e2SBrian Feldman } 1603