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 19d74d50a8SDag-Erling Smørgrav RCSID("$OpenBSD: sftp.c,v 1.56 2004/07/11 17:48:47 deraadt Exp $"); 201e8db6e2SBrian Feldman 211e8db6e2SBrian Feldman #include "buffer.h" 221e8db6e2SBrian Feldman #include "xmalloc.h" 231e8db6e2SBrian Feldman #include "log.h" 241e8db6e2SBrian Feldman #include "pathnames.h" 25ae1f160dSDag-Erling Smørgrav #include "misc.h" 261e8db6e2SBrian Feldman 271e8db6e2SBrian Feldman #include "sftp.h" 281e8db6e2SBrian Feldman #include "sftp-common.h" 291e8db6e2SBrian Feldman #include "sftp-client.h" 30efcad6b7SDag-Erling Smørgrav 31efcad6b7SDag-Erling Smørgrav /* File to read commands from */ 32efcad6b7SDag-Erling Smørgrav FILE* infile; 33efcad6b7SDag-Erling Smørgrav 34efcad6b7SDag-Erling Smørgrav /* Are we in batchfile mode? */ 35efcad6b7SDag-Erling Smørgrav int batchmode = 0; 36efcad6b7SDag-Erling Smørgrav 37efcad6b7SDag-Erling Smørgrav /* Size of buffer used when copying files */ 38efcad6b7SDag-Erling Smørgrav size_t copy_buffer_len = 32768; 39efcad6b7SDag-Erling Smørgrav 40efcad6b7SDag-Erling Smørgrav /* Number of concurrent outstanding requests */ 41efcad6b7SDag-Erling Smørgrav size_t num_requests = 16; 42efcad6b7SDag-Erling Smørgrav 43efcad6b7SDag-Erling Smørgrav /* PID of ssh transport process */ 44efcad6b7SDag-Erling Smørgrav static pid_t sshpid = -1; 45efcad6b7SDag-Erling Smørgrav 46efcad6b7SDag-Erling Smørgrav /* This is set to 0 if the progressmeter is not desired. */ 4752028650SDag-Erling Smørgrav int showprogress = 1; 48efcad6b7SDag-Erling Smørgrav 49d74d50a8SDag-Erling Smørgrav /* SIGINT received during command processing */ 50d74d50a8SDag-Erling Smørgrav volatile sig_atomic_t interrupted = 0; 51d74d50a8SDag-Erling Smørgrav 52d74d50a8SDag-Erling Smørgrav /* I wish qsort() took a separate ctx for the comparison function...*/ 53d74d50a8SDag-Erling Smørgrav int sort_flag; 54d74d50a8SDag-Erling Smørgrav 55efcad6b7SDag-Erling Smørgrav int remote_glob(struct sftp_conn *, const char *, int, 56efcad6b7SDag-Erling Smørgrav int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 571e8db6e2SBrian Feldman 5883d2307dSDag-Erling Smørgrav extern char *__progname; 5983d2307dSDag-Erling Smørgrav 60efcad6b7SDag-Erling Smørgrav /* Separators for interactive commands */ 61efcad6b7SDag-Erling Smørgrav #define WHITESPACE " \t\r\n" 621e8db6e2SBrian Feldman 63d74d50a8SDag-Erling Smørgrav /* ls flags */ 64d74d50a8SDag-Erling Smørgrav #define LS_LONG_VIEW 0x01 /* Full view ala ls -l */ 65d74d50a8SDag-Erling Smørgrav #define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */ 66d74d50a8SDag-Erling Smørgrav #define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */ 67d74d50a8SDag-Erling Smørgrav #define LS_NAME_SORT 0x08 /* Sort by name (default) */ 68d74d50a8SDag-Erling Smørgrav #define LS_TIME_SORT 0x10 /* Sort by mtime */ 69d74d50a8SDag-Erling Smørgrav #define LS_SIZE_SORT 0x20 /* Sort by file size */ 70d74d50a8SDag-Erling Smørgrav #define LS_REVERSE_SORT 0x40 /* Reverse sort order */ 71d74d50a8SDag-Erling Smørgrav #define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */ 72d74d50a8SDag-Erling Smørgrav 73d74d50a8SDag-Erling Smørgrav #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW) 74d74d50a8SDag-Erling Smørgrav #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) 75efcad6b7SDag-Erling Smørgrav 76efcad6b7SDag-Erling Smørgrav /* Commands for interactive mode */ 77efcad6b7SDag-Erling Smørgrav #define I_CHDIR 1 78efcad6b7SDag-Erling Smørgrav #define I_CHGRP 2 79efcad6b7SDag-Erling Smørgrav #define I_CHMOD 3 80efcad6b7SDag-Erling Smørgrav #define I_CHOWN 4 81efcad6b7SDag-Erling Smørgrav #define I_GET 5 82efcad6b7SDag-Erling Smørgrav #define I_HELP 6 83efcad6b7SDag-Erling Smørgrav #define I_LCHDIR 7 84efcad6b7SDag-Erling Smørgrav #define I_LLS 8 85efcad6b7SDag-Erling Smørgrav #define I_LMKDIR 9 86efcad6b7SDag-Erling Smørgrav #define I_LPWD 10 87efcad6b7SDag-Erling Smørgrav #define I_LS 11 88efcad6b7SDag-Erling Smørgrav #define I_LUMASK 12 89efcad6b7SDag-Erling Smørgrav #define I_MKDIR 13 90efcad6b7SDag-Erling Smørgrav #define I_PUT 14 91efcad6b7SDag-Erling Smørgrav #define I_PWD 15 92efcad6b7SDag-Erling Smørgrav #define I_QUIT 16 93efcad6b7SDag-Erling Smørgrav #define I_RENAME 17 94efcad6b7SDag-Erling Smørgrav #define I_RM 18 95efcad6b7SDag-Erling Smørgrav #define I_RMDIR 19 96efcad6b7SDag-Erling Smørgrav #define I_SHELL 20 97efcad6b7SDag-Erling Smørgrav #define I_SYMLINK 21 98efcad6b7SDag-Erling Smørgrav #define I_VERSION 22 99efcad6b7SDag-Erling Smørgrav #define I_PROGRESS 23 100efcad6b7SDag-Erling Smørgrav 101efcad6b7SDag-Erling Smørgrav struct CMD { 102efcad6b7SDag-Erling Smørgrav const char *c; 103efcad6b7SDag-Erling Smørgrav const int n; 104efcad6b7SDag-Erling Smørgrav }; 105efcad6b7SDag-Erling Smørgrav 106efcad6b7SDag-Erling Smørgrav static const struct CMD cmds[] = { 107efcad6b7SDag-Erling Smørgrav { "bye", I_QUIT }, 108efcad6b7SDag-Erling Smørgrav { "cd", I_CHDIR }, 109efcad6b7SDag-Erling Smørgrav { "chdir", I_CHDIR }, 110efcad6b7SDag-Erling Smørgrav { "chgrp", I_CHGRP }, 111efcad6b7SDag-Erling Smørgrav { "chmod", I_CHMOD }, 112efcad6b7SDag-Erling Smørgrav { "chown", I_CHOWN }, 113efcad6b7SDag-Erling Smørgrav { "dir", I_LS }, 114efcad6b7SDag-Erling Smørgrav { "exit", I_QUIT }, 115efcad6b7SDag-Erling Smørgrav { "get", I_GET }, 116efcad6b7SDag-Erling Smørgrav { "mget", I_GET }, 117efcad6b7SDag-Erling Smørgrav { "help", I_HELP }, 118efcad6b7SDag-Erling Smørgrav { "lcd", I_LCHDIR }, 119efcad6b7SDag-Erling Smørgrav { "lchdir", I_LCHDIR }, 120efcad6b7SDag-Erling Smørgrav { "lls", I_LLS }, 121efcad6b7SDag-Erling Smørgrav { "lmkdir", I_LMKDIR }, 122efcad6b7SDag-Erling Smørgrav { "ln", I_SYMLINK }, 123efcad6b7SDag-Erling Smørgrav { "lpwd", I_LPWD }, 124efcad6b7SDag-Erling Smørgrav { "ls", I_LS }, 125efcad6b7SDag-Erling Smørgrav { "lumask", I_LUMASK }, 126efcad6b7SDag-Erling Smørgrav { "mkdir", I_MKDIR }, 127efcad6b7SDag-Erling Smørgrav { "progress", I_PROGRESS }, 128efcad6b7SDag-Erling Smørgrav { "put", I_PUT }, 129efcad6b7SDag-Erling Smørgrav { "mput", I_PUT }, 130efcad6b7SDag-Erling Smørgrav { "pwd", I_PWD }, 131efcad6b7SDag-Erling Smørgrav { "quit", I_QUIT }, 132efcad6b7SDag-Erling Smørgrav { "rename", I_RENAME }, 133efcad6b7SDag-Erling Smørgrav { "rm", I_RM }, 134efcad6b7SDag-Erling Smørgrav { "rmdir", I_RMDIR }, 135efcad6b7SDag-Erling Smørgrav { "symlink", I_SYMLINK }, 136efcad6b7SDag-Erling Smørgrav { "version", I_VERSION }, 137efcad6b7SDag-Erling Smørgrav { "!", I_SHELL }, 138efcad6b7SDag-Erling Smørgrav { "?", I_HELP }, 139efcad6b7SDag-Erling Smørgrav { NULL, -1} 140efcad6b7SDag-Erling Smørgrav }; 141efcad6b7SDag-Erling Smørgrav 142efcad6b7SDag-Erling Smørgrav int interactive_loop(int fd_in, int fd_out, char *file1, char *file2); 143efcad6b7SDag-Erling Smørgrav 144efcad6b7SDag-Erling Smørgrav static void 145d74d50a8SDag-Erling Smørgrav killchild(int signo) 146d74d50a8SDag-Erling Smørgrav { 147d74d50a8SDag-Erling Smørgrav if (sshpid > 1) 148d74d50a8SDag-Erling Smørgrav kill(sshpid, SIGTERM); 149d74d50a8SDag-Erling Smørgrav 150d74d50a8SDag-Erling Smørgrav _exit(1); 151d74d50a8SDag-Erling Smørgrav } 152d74d50a8SDag-Erling Smørgrav 153d74d50a8SDag-Erling Smørgrav static void 154d74d50a8SDag-Erling Smørgrav cmd_interrupt(int signo) 155d74d50a8SDag-Erling Smørgrav { 156d74d50a8SDag-Erling Smørgrav const char msg[] = "\rInterrupt \n"; 157d74d50a8SDag-Erling Smørgrav 158d74d50a8SDag-Erling Smørgrav write(STDERR_FILENO, msg, sizeof(msg) - 1); 159d74d50a8SDag-Erling Smørgrav interrupted = 1; 160d74d50a8SDag-Erling Smørgrav } 161d74d50a8SDag-Erling Smørgrav 162d74d50a8SDag-Erling Smørgrav static void 163efcad6b7SDag-Erling Smørgrav help(void) 164efcad6b7SDag-Erling Smørgrav { 165efcad6b7SDag-Erling Smørgrav printf("Available commands:\n"); 166efcad6b7SDag-Erling Smørgrav printf("cd path Change remote directory to 'path'\n"); 167efcad6b7SDag-Erling Smørgrav printf("lcd path Change local directory to 'path'\n"); 168efcad6b7SDag-Erling Smørgrav printf("chgrp grp path Change group of file 'path' to 'grp'\n"); 169efcad6b7SDag-Erling Smørgrav printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); 170efcad6b7SDag-Erling Smørgrav printf("chown own path Change owner of file 'path' to 'own'\n"); 171efcad6b7SDag-Erling Smørgrav printf("help Display this help text\n"); 172efcad6b7SDag-Erling Smørgrav printf("get remote-path [local-path] Download file\n"); 173efcad6b7SDag-Erling Smørgrav printf("lls [ls-options [path]] Display local directory listing\n"); 174efcad6b7SDag-Erling Smørgrav printf("ln oldpath newpath Symlink remote file\n"); 175efcad6b7SDag-Erling Smørgrav printf("lmkdir path Create local directory\n"); 176efcad6b7SDag-Erling Smørgrav printf("lpwd Print local working directory\n"); 177efcad6b7SDag-Erling Smørgrav printf("ls [path] Display remote directory listing\n"); 178efcad6b7SDag-Erling Smørgrav printf("lumask umask Set local umask to 'umask'\n"); 179efcad6b7SDag-Erling Smørgrav printf("mkdir path Create remote directory\n"); 180efcad6b7SDag-Erling Smørgrav printf("progress Toggle display of progress meter\n"); 181efcad6b7SDag-Erling Smørgrav printf("put local-path [remote-path] Upload file\n"); 182efcad6b7SDag-Erling Smørgrav printf("pwd Display remote working directory\n"); 183efcad6b7SDag-Erling Smørgrav printf("exit Quit sftp\n"); 184efcad6b7SDag-Erling Smørgrav printf("quit Quit sftp\n"); 185efcad6b7SDag-Erling Smørgrav printf("rename oldpath newpath Rename remote file\n"); 186efcad6b7SDag-Erling Smørgrav printf("rmdir path Remove remote directory\n"); 187efcad6b7SDag-Erling Smørgrav printf("rm path Delete remote file\n"); 188efcad6b7SDag-Erling Smørgrav printf("symlink oldpath newpath Symlink remote file\n"); 189efcad6b7SDag-Erling Smørgrav printf("version Show SFTP version\n"); 190efcad6b7SDag-Erling Smørgrav printf("!command Execute 'command' in local shell\n"); 191efcad6b7SDag-Erling Smørgrav printf("! Escape to local shell\n"); 192efcad6b7SDag-Erling Smørgrav printf("? Synonym for help\n"); 193efcad6b7SDag-Erling Smørgrav } 194efcad6b7SDag-Erling Smørgrav 195efcad6b7SDag-Erling Smørgrav static void 196efcad6b7SDag-Erling Smørgrav local_do_shell(const char *args) 197efcad6b7SDag-Erling Smørgrav { 198efcad6b7SDag-Erling Smørgrav int status; 199efcad6b7SDag-Erling Smørgrav char *shell; 200efcad6b7SDag-Erling Smørgrav pid_t pid; 201efcad6b7SDag-Erling Smørgrav 202efcad6b7SDag-Erling Smørgrav if (!*args) 203efcad6b7SDag-Erling Smørgrav args = NULL; 204efcad6b7SDag-Erling Smørgrav 205efcad6b7SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL) 206efcad6b7SDag-Erling Smørgrav shell = _PATH_BSHELL; 207efcad6b7SDag-Erling Smørgrav 208efcad6b7SDag-Erling Smørgrav if ((pid = fork()) == -1) 209efcad6b7SDag-Erling Smørgrav fatal("Couldn't fork: %s", strerror(errno)); 210efcad6b7SDag-Erling Smørgrav 211efcad6b7SDag-Erling Smørgrav if (pid == 0) { 212efcad6b7SDag-Erling Smørgrav /* XXX: child has pipe fds to ssh subproc open - issue? */ 213efcad6b7SDag-Erling Smørgrav if (args) { 214efcad6b7SDag-Erling Smørgrav debug3("Executing %s -c \"%s\"", shell, args); 215efcad6b7SDag-Erling Smørgrav execl(shell, shell, "-c", args, (char *)NULL); 216efcad6b7SDag-Erling Smørgrav } else { 217efcad6b7SDag-Erling Smørgrav debug3("Executing %s", shell); 218efcad6b7SDag-Erling Smørgrav execl(shell, shell, (char *)NULL); 219efcad6b7SDag-Erling Smørgrav } 220efcad6b7SDag-Erling Smørgrav fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, 221efcad6b7SDag-Erling Smørgrav strerror(errno)); 222efcad6b7SDag-Erling Smørgrav _exit(1); 223efcad6b7SDag-Erling Smørgrav } 224efcad6b7SDag-Erling Smørgrav while (waitpid(pid, &status, 0) == -1) 225efcad6b7SDag-Erling Smørgrav if (errno != EINTR) 226efcad6b7SDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 227efcad6b7SDag-Erling Smørgrav if (!WIFEXITED(status)) 228efcad6b7SDag-Erling Smørgrav error("Shell exited abormally"); 229efcad6b7SDag-Erling Smørgrav else if (WEXITSTATUS(status)) 230efcad6b7SDag-Erling Smørgrav error("Shell exited with status %d", WEXITSTATUS(status)); 231efcad6b7SDag-Erling Smørgrav } 232efcad6b7SDag-Erling Smørgrav 233efcad6b7SDag-Erling Smørgrav static void 234efcad6b7SDag-Erling Smørgrav local_do_ls(const char *args) 235efcad6b7SDag-Erling Smørgrav { 236efcad6b7SDag-Erling Smørgrav if (!args || !*args) 237efcad6b7SDag-Erling Smørgrav local_do_shell(_PATH_LS); 238efcad6b7SDag-Erling Smørgrav else { 239efcad6b7SDag-Erling Smørgrav int len = strlen(_PATH_LS " ") + strlen(args) + 1; 240efcad6b7SDag-Erling Smørgrav char *buf = xmalloc(len); 241efcad6b7SDag-Erling Smørgrav 242efcad6b7SDag-Erling Smørgrav /* XXX: quoting - rip quoting code from ftp? */ 243efcad6b7SDag-Erling Smørgrav snprintf(buf, len, _PATH_LS " %s", args); 244efcad6b7SDag-Erling Smørgrav local_do_shell(buf); 245efcad6b7SDag-Erling Smørgrav xfree(buf); 246efcad6b7SDag-Erling Smørgrav } 247efcad6b7SDag-Erling Smørgrav } 248efcad6b7SDag-Erling Smørgrav 249efcad6b7SDag-Erling Smørgrav /* Strip one path (usually the pwd) from the start of another */ 250efcad6b7SDag-Erling Smørgrav static char * 251efcad6b7SDag-Erling Smørgrav path_strip(char *path, char *strip) 252efcad6b7SDag-Erling Smørgrav { 253efcad6b7SDag-Erling Smørgrav size_t len; 254efcad6b7SDag-Erling Smørgrav 255efcad6b7SDag-Erling Smørgrav if (strip == NULL) 256efcad6b7SDag-Erling Smørgrav return (xstrdup(path)); 257efcad6b7SDag-Erling Smørgrav 258efcad6b7SDag-Erling Smørgrav len = strlen(strip); 259efcad6b7SDag-Erling Smørgrav if (strip != NULL && strncmp(path, strip, len) == 0) { 260efcad6b7SDag-Erling Smørgrav if (strip[len - 1] != '/' && path[len] == '/') 261efcad6b7SDag-Erling Smørgrav len++; 262efcad6b7SDag-Erling Smørgrav return (xstrdup(path + len)); 263efcad6b7SDag-Erling Smørgrav } 264efcad6b7SDag-Erling Smørgrav 265efcad6b7SDag-Erling Smørgrav return (xstrdup(path)); 266efcad6b7SDag-Erling Smørgrav } 267efcad6b7SDag-Erling Smørgrav 268efcad6b7SDag-Erling Smørgrav static char * 269efcad6b7SDag-Erling Smørgrav path_append(char *p1, char *p2) 270efcad6b7SDag-Erling Smørgrav { 271efcad6b7SDag-Erling Smørgrav char *ret; 272efcad6b7SDag-Erling Smørgrav int len = strlen(p1) + strlen(p2) + 2; 273efcad6b7SDag-Erling Smørgrav 274efcad6b7SDag-Erling Smørgrav ret = xmalloc(len); 275efcad6b7SDag-Erling Smørgrav strlcpy(ret, p1, len); 276efcad6b7SDag-Erling Smørgrav if (p1[strlen(p1) - 1] != '/') 277efcad6b7SDag-Erling Smørgrav strlcat(ret, "/", len); 278efcad6b7SDag-Erling Smørgrav strlcat(ret, p2, len); 279efcad6b7SDag-Erling Smørgrav 280efcad6b7SDag-Erling Smørgrav return(ret); 281efcad6b7SDag-Erling Smørgrav } 282efcad6b7SDag-Erling Smørgrav 283efcad6b7SDag-Erling Smørgrav static char * 284efcad6b7SDag-Erling Smørgrav make_absolute(char *p, char *pwd) 285efcad6b7SDag-Erling Smørgrav { 286d74d50a8SDag-Erling Smørgrav char *abs_str; 287efcad6b7SDag-Erling Smørgrav 288efcad6b7SDag-Erling Smørgrav /* Derelativise */ 289efcad6b7SDag-Erling Smørgrav if (p && p[0] != '/') { 290d74d50a8SDag-Erling Smørgrav abs_str = path_append(pwd, p); 291efcad6b7SDag-Erling Smørgrav xfree(p); 292d74d50a8SDag-Erling Smørgrav return(abs_str); 293efcad6b7SDag-Erling Smørgrav } else 294efcad6b7SDag-Erling Smørgrav return(p); 295efcad6b7SDag-Erling Smørgrav } 296efcad6b7SDag-Erling Smørgrav 297efcad6b7SDag-Erling Smørgrav static int 298efcad6b7SDag-Erling Smørgrav infer_path(const char *p, char **ifp) 299efcad6b7SDag-Erling Smørgrav { 300efcad6b7SDag-Erling Smørgrav char *cp; 301efcad6b7SDag-Erling Smørgrav 302efcad6b7SDag-Erling Smørgrav cp = strrchr(p, '/'); 303efcad6b7SDag-Erling Smørgrav if (cp == NULL) { 304efcad6b7SDag-Erling Smørgrav *ifp = xstrdup(p); 305efcad6b7SDag-Erling Smørgrav return(0); 306efcad6b7SDag-Erling Smørgrav } 307efcad6b7SDag-Erling Smørgrav 308efcad6b7SDag-Erling Smørgrav if (!cp[1]) { 309efcad6b7SDag-Erling Smørgrav error("Invalid path"); 310efcad6b7SDag-Erling Smørgrav return(-1); 311efcad6b7SDag-Erling Smørgrav } 312efcad6b7SDag-Erling Smørgrav 313efcad6b7SDag-Erling Smørgrav *ifp = xstrdup(cp + 1); 314efcad6b7SDag-Erling Smørgrav return(0); 315efcad6b7SDag-Erling Smørgrav } 316efcad6b7SDag-Erling Smørgrav 317efcad6b7SDag-Erling Smørgrav static int 318efcad6b7SDag-Erling Smørgrav parse_getput_flags(const char **cpp, int *pflag) 319efcad6b7SDag-Erling Smørgrav { 320efcad6b7SDag-Erling Smørgrav const char *cp = *cpp; 321efcad6b7SDag-Erling Smørgrav 322efcad6b7SDag-Erling Smørgrav /* Check for flags */ 323efcad6b7SDag-Erling Smørgrav if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { 324efcad6b7SDag-Erling Smørgrav switch (cp[1]) { 325efcad6b7SDag-Erling Smørgrav case 'p': 326efcad6b7SDag-Erling Smørgrav case 'P': 327efcad6b7SDag-Erling Smørgrav *pflag = 1; 328efcad6b7SDag-Erling Smørgrav break; 329efcad6b7SDag-Erling Smørgrav default: 330efcad6b7SDag-Erling Smørgrav error("Invalid flag -%c", cp[1]); 331efcad6b7SDag-Erling Smørgrav return(-1); 332efcad6b7SDag-Erling Smørgrav } 333efcad6b7SDag-Erling Smørgrav cp += 2; 334efcad6b7SDag-Erling Smørgrav *cpp = cp + strspn(cp, WHITESPACE); 335efcad6b7SDag-Erling Smørgrav } 336efcad6b7SDag-Erling Smørgrav 337efcad6b7SDag-Erling Smørgrav return(0); 338efcad6b7SDag-Erling Smørgrav } 339efcad6b7SDag-Erling Smørgrav 340efcad6b7SDag-Erling Smørgrav static int 341efcad6b7SDag-Erling Smørgrav parse_ls_flags(const char **cpp, int *lflag) 342efcad6b7SDag-Erling Smørgrav { 343efcad6b7SDag-Erling Smørgrav const char *cp = *cpp; 344efcad6b7SDag-Erling Smørgrav 345d74d50a8SDag-Erling Smørgrav /* Defaults */ 346d74d50a8SDag-Erling Smørgrav *lflag = LS_NAME_SORT; 347d74d50a8SDag-Erling Smørgrav 348efcad6b7SDag-Erling Smørgrav /* Check for flags */ 349efcad6b7SDag-Erling Smørgrav if (cp++[0] == '-') { 350efcad6b7SDag-Erling Smørgrav for(; strchr(WHITESPACE, *cp) == NULL; cp++) { 351efcad6b7SDag-Erling Smørgrav switch (*cp) { 352efcad6b7SDag-Erling Smørgrav case 'l': 353d74d50a8SDag-Erling Smørgrav *lflag &= ~VIEW_FLAGS; 354d74d50a8SDag-Erling Smørgrav *lflag |= LS_LONG_VIEW; 355efcad6b7SDag-Erling Smørgrav break; 356efcad6b7SDag-Erling Smørgrav case '1': 357d74d50a8SDag-Erling Smørgrav *lflag &= ~VIEW_FLAGS; 358d74d50a8SDag-Erling Smørgrav *lflag |= LS_SHORT_VIEW; 359d74d50a8SDag-Erling Smørgrav break; 360d74d50a8SDag-Erling Smørgrav case 'n': 361d74d50a8SDag-Erling Smørgrav *lflag &= ~VIEW_FLAGS; 362d74d50a8SDag-Erling Smørgrav *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; 363d74d50a8SDag-Erling Smørgrav break; 364d74d50a8SDag-Erling Smørgrav case 'S': 365d74d50a8SDag-Erling Smørgrav *lflag &= ~SORT_FLAGS; 366d74d50a8SDag-Erling Smørgrav *lflag |= LS_SIZE_SORT; 367d74d50a8SDag-Erling Smørgrav break; 368d74d50a8SDag-Erling Smørgrav case 't': 369d74d50a8SDag-Erling Smørgrav *lflag &= ~SORT_FLAGS; 370d74d50a8SDag-Erling Smørgrav *lflag |= LS_TIME_SORT; 371d74d50a8SDag-Erling Smørgrav break; 372d74d50a8SDag-Erling Smørgrav case 'r': 373d74d50a8SDag-Erling Smørgrav *lflag |= LS_REVERSE_SORT; 374d74d50a8SDag-Erling Smørgrav break; 375d74d50a8SDag-Erling Smørgrav case 'f': 376d74d50a8SDag-Erling Smørgrav *lflag &= ~SORT_FLAGS; 377d74d50a8SDag-Erling Smørgrav break; 378d74d50a8SDag-Erling Smørgrav case 'a': 379d74d50a8SDag-Erling Smørgrav *lflag |= LS_SHOW_ALL; 380efcad6b7SDag-Erling Smørgrav break; 381efcad6b7SDag-Erling Smørgrav default: 382efcad6b7SDag-Erling Smørgrav error("Invalid flag -%c", *cp); 383efcad6b7SDag-Erling Smørgrav return(-1); 384efcad6b7SDag-Erling Smørgrav } 385efcad6b7SDag-Erling Smørgrav } 386efcad6b7SDag-Erling Smørgrav *cpp = cp + strspn(cp, WHITESPACE); 387efcad6b7SDag-Erling Smørgrav } 388efcad6b7SDag-Erling Smørgrav 389efcad6b7SDag-Erling Smørgrav return(0); 390efcad6b7SDag-Erling Smørgrav } 391efcad6b7SDag-Erling Smørgrav 392efcad6b7SDag-Erling Smørgrav static int 393efcad6b7SDag-Erling Smørgrav get_pathname(const char **cpp, char **path) 394efcad6b7SDag-Erling Smørgrav { 395efcad6b7SDag-Erling Smørgrav const char *cp = *cpp, *end; 396efcad6b7SDag-Erling Smørgrav char quot; 397efcad6b7SDag-Erling Smørgrav int i, j; 398efcad6b7SDag-Erling Smørgrav 399efcad6b7SDag-Erling Smørgrav cp += strspn(cp, WHITESPACE); 400efcad6b7SDag-Erling Smørgrav if (!*cp) { 401efcad6b7SDag-Erling Smørgrav *cpp = cp; 402efcad6b7SDag-Erling Smørgrav *path = NULL; 403efcad6b7SDag-Erling Smørgrav return (0); 404efcad6b7SDag-Erling Smørgrav } 405efcad6b7SDag-Erling Smørgrav 406efcad6b7SDag-Erling Smørgrav *path = xmalloc(strlen(cp) + 1); 407efcad6b7SDag-Erling Smørgrav 408efcad6b7SDag-Erling Smørgrav /* Check for quoted filenames */ 409efcad6b7SDag-Erling Smørgrav if (*cp == '\"' || *cp == '\'') { 410efcad6b7SDag-Erling Smørgrav quot = *cp++; 411efcad6b7SDag-Erling Smørgrav 412efcad6b7SDag-Erling Smørgrav /* Search for terminating quote, unescape some chars */ 413efcad6b7SDag-Erling Smørgrav for (i = j = 0; i <= strlen(cp); i++) { 414efcad6b7SDag-Erling Smørgrav if (cp[i] == quot) { /* Found quote */ 415efcad6b7SDag-Erling Smørgrav i++; 416efcad6b7SDag-Erling Smørgrav (*path)[j] = '\0'; 417efcad6b7SDag-Erling Smørgrav break; 418efcad6b7SDag-Erling Smørgrav } 419efcad6b7SDag-Erling Smørgrav if (cp[i] == '\0') { /* End of string */ 420efcad6b7SDag-Erling Smørgrav error("Unterminated quote"); 421efcad6b7SDag-Erling Smørgrav goto fail; 422efcad6b7SDag-Erling Smørgrav } 423efcad6b7SDag-Erling Smørgrav if (cp[i] == '\\') { /* Escaped characters */ 424efcad6b7SDag-Erling Smørgrav i++; 425efcad6b7SDag-Erling Smørgrav if (cp[i] != '\'' && cp[i] != '\"' && 426efcad6b7SDag-Erling Smørgrav cp[i] != '\\') { 427d74d50a8SDag-Erling Smørgrav error("Bad escaped character '\\%c'", 428efcad6b7SDag-Erling Smørgrav cp[i]); 429efcad6b7SDag-Erling Smørgrav goto fail; 430efcad6b7SDag-Erling Smørgrav } 431efcad6b7SDag-Erling Smørgrav } 432efcad6b7SDag-Erling Smørgrav (*path)[j++] = cp[i]; 433efcad6b7SDag-Erling Smørgrav } 434efcad6b7SDag-Erling Smørgrav 435efcad6b7SDag-Erling Smørgrav if (j == 0) { 436efcad6b7SDag-Erling Smørgrav error("Empty quotes"); 437efcad6b7SDag-Erling Smørgrav goto fail; 438efcad6b7SDag-Erling Smørgrav } 439efcad6b7SDag-Erling Smørgrav *cpp = cp + i + strspn(cp + i, WHITESPACE); 440efcad6b7SDag-Erling Smørgrav } else { 441efcad6b7SDag-Erling Smørgrav /* Read to end of filename */ 442efcad6b7SDag-Erling Smørgrav end = strpbrk(cp, WHITESPACE); 443efcad6b7SDag-Erling Smørgrav if (end == NULL) 444efcad6b7SDag-Erling Smørgrav end = strchr(cp, '\0'); 445efcad6b7SDag-Erling Smørgrav *cpp = end + strspn(end, WHITESPACE); 446efcad6b7SDag-Erling Smørgrav 447efcad6b7SDag-Erling Smørgrav memcpy(*path, cp, end - cp); 448efcad6b7SDag-Erling Smørgrav (*path)[end - cp] = '\0'; 449efcad6b7SDag-Erling Smørgrav } 450efcad6b7SDag-Erling Smørgrav return (0); 451efcad6b7SDag-Erling Smørgrav 452efcad6b7SDag-Erling Smørgrav fail: 453efcad6b7SDag-Erling Smørgrav xfree(*path); 454efcad6b7SDag-Erling Smørgrav *path = NULL; 455efcad6b7SDag-Erling Smørgrav return (-1); 456efcad6b7SDag-Erling Smørgrav } 457efcad6b7SDag-Erling Smørgrav 458efcad6b7SDag-Erling Smørgrav static int 459efcad6b7SDag-Erling Smørgrav is_dir(char *path) 460efcad6b7SDag-Erling Smørgrav { 461efcad6b7SDag-Erling Smørgrav struct stat sb; 462efcad6b7SDag-Erling Smørgrav 463efcad6b7SDag-Erling Smørgrav /* XXX: report errors? */ 464efcad6b7SDag-Erling Smørgrav if (stat(path, &sb) == -1) 465efcad6b7SDag-Erling Smørgrav return(0); 466efcad6b7SDag-Erling Smørgrav 467efcad6b7SDag-Erling Smørgrav return(sb.st_mode & S_IFDIR); 468efcad6b7SDag-Erling Smørgrav } 469efcad6b7SDag-Erling Smørgrav 470efcad6b7SDag-Erling Smørgrav static int 471efcad6b7SDag-Erling Smørgrav is_reg(char *path) 472efcad6b7SDag-Erling Smørgrav { 473efcad6b7SDag-Erling Smørgrav struct stat sb; 474efcad6b7SDag-Erling Smørgrav 475efcad6b7SDag-Erling Smørgrav if (stat(path, &sb) == -1) 476efcad6b7SDag-Erling Smørgrav fatal("stat %s: %s", path, strerror(errno)); 477efcad6b7SDag-Erling Smørgrav 478efcad6b7SDag-Erling Smørgrav return(S_ISREG(sb.st_mode)); 479efcad6b7SDag-Erling Smørgrav } 480efcad6b7SDag-Erling Smørgrav 481efcad6b7SDag-Erling Smørgrav static int 482efcad6b7SDag-Erling Smørgrav remote_is_dir(struct sftp_conn *conn, char *path) 483efcad6b7SDag-Erling Smørgrav { 484efcad6b7SDag-Erling Smørgrav Attrib *a; 485efcad6b7SDag-Erling Smørgrav 486efcad6b7SDag-Erling Smørgrav /* XXX: report errors? */ 487efcad6b7SDag-Erling Smørgrav if ((a = do_stat(conn, path, 1)) == NULL) 488efcad6b7SDag-Erling Smørgrav return(0); 489efcad6b7SDag-Erling Smørgrav if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) 490efcad6b7SDag-Erling Smørgrav return(0); 491efcad6b7SDag-Erling Smørgrav return(a->perm & S_IFDIR); 492efcad6b7SDag-Erling Smørgrav } 493efcad6b7SDag-Erling Smørgrav 494efcad6b7SDag-Erling Smørgrav static int 495efcad6b7SDag-Erling Smørgrav process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 496efcad6b7SDag-Erling Smørgrav { 497efcad6b7SDag-Erling Smørgrav char *abs_src = NULL; 498efcad6b7SDag-Erling Smørgrav char *abs_dst = NULL; 499efcad6b7SDag-Erling Smørgrav char *tmp; 500efcad6b7SDag-Erling Smørgrav glob_t g; 501efcad6b7SDag-Erling Smørgrav int err = 0; 502efcad6b7SDag-Erling Smørgrav int i; 503efcad6b7SDag-Erling Smørgrav 504efcad6b7SDag-Erling Smørgrav abs_src = xstrdup(src); 505efcad6b7SDag-Erling Smørgrav abs_src = make_absolute(abs_src, pwd); 506efcad6b7SDag-Erling Smørgrav 507efcad6b7SDag-Erling Smørgrav memset(&g, 0, sizeof(g)); 508efcad6b7SDag-Erling Smørgrav debug3("Looking up %s", abs_src); 509efcad6b7SDag-Erling Smørgrav if (remote_glob(conn, abs_src, 0, NULL, &g)) { 510efcad6b7SDag-Erling Smørgrav error("File \"%s\" not found.", abs_src); 511efcad6b7SDag-Erling Smørgrav err = -1; 512efcad6b7SDag-Erling Smørgrav goto out; 513efcad6b7SDag-Erling Smørgrav } 514efcad6b7SDag-Erling Smørgrav 515efcad6b7SDag-Erling Smørgrav /* If multiple matches, dst must be a directory or unspecified */ 516efcad6b7SDag-Erling Smørgrav if (g.gl_matchc > 1 && dst && !is_dir(dst)) { 517efcad6b7SDag-Erling Smørgrav error("Multiple files match, but \"%s\" is not a directory", 518efcad6b7SDag-Erling Smørgrav dst); 519efcad6b7SDag-Erling Smørgrav err = -1; 520efcad6b7SDag-Erling Smørgrav goto out; 521efcad6b7SDag-Erling Smørgrav } 522efcad6b7SDag-Erling Smørgrav 523d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 524efcad6b7SDag-Erling Smørgrav if (infer_path(g.gl_pathv[i], &tmp)) { 525efcad6b7SDag-Erling Smørgrav err = -1; 526efcad6b7SDag-Erling Smørgrav goto out; 527efcad6b7SDag-Erling Smørgrav } 528efcad6b7SDag-Erling Smørgrav 529efcad6b7SDag-Erling Smørgrav if (g.gl_matchc == 1 && dst) { 530efcad6b7SDag-Erling Smørgrav /* If directory specified, append filename */ 531efcad6b7SDag-Erling Smørgrav if (is_dir(dst)) { 532efcad6b7SDag-Erling Smørgrav if (infer_path(g.gl_pathv[0], &tmp)) { 533efcad6b7SDag-Erling Smørgrav err = 1; 534efcad6b7SDag-Erling Smørgrav goto out; 535efcad6b7SDag-Erling Smørgrav } 536efcad6b7SDag-Erling Smørgrav abs_dst = path_append(dst, tmp); 537efcad6b7SDag-Erling Smørgrav xfree(tmp); 538efcad6b7SDag-Erling Smørgrav } else 539efcad6b7SDag-Erling Smørgrav abs_dst = xstrdup(dst); 540efcad6b7SDag-Erling Smørgrav } else if (dst) { 541efcad6b7SDag-Erling Smørgrav abs_dst = path_append(dst, tmp); 542efcad6b7SDag-Erling Smørgrav xfree(tmp); 543efcad6b7SDag-Erling Smørgrav } else 544efcad6b7SDag-Erling Smørgrav abs_dst = tmp; 545efcad6b7SDag-Erling Smørgrav 546efcad6b7SDag-Erling Smørgrav printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 547efcad6b7SDag-Erling Smørgrav if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 548efcad6b7SDag-Erling Smørgrav err = -1; 549efcad6b7SDag-Erling Smørgrav xfree(abs_dst); 550efcad6b7SDag-Erling Smørgrav abs_dst = NULL; 551efcad6b7SDag-Erling Smørgrav } 552efcad6b7SDag-Erling Smørgrav 553efcad6b7SDag-Erling Smørgrav out: 554efcad6b7SDag-Erling Smørgrav xfree(abs_src); 555efcad6b7SDag-Erling Smørgrav if (abs_dst) 556efcad6b7SDag-Erling Smørgrav xfree(abs_dst); 557efcad6b7SDag-Erling Smørgrav globfree(&g); 558efcad6b7SDag-Erling Smørgrav return(err); 559efcad6b7SDag-Erling Smørgrav } 560efcad6b7SDag-Erling Smørgrav 561efcad6b7SDag-Erling Smørgrav static int 562efcad6b7SDag-Erling Smørgrav process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 563efcad6b7SDag-Erling Smørgrav { 564efcad6b7SDag-Erling Smørgrav char *tmp_dst = NULL; 565efcad6b7SDag-Erling Smørgrav char *abs_dst = NULL; 566efcad6b7SDag-Erling Smørgrav char *tmp; 567efcad6b7SDag-Erling Smørgrav glob_t g; 568efcad6b7SDag-Erling Smørgrav int err = 0; 569efcad6b7SDag-Erling Smørgrav int i; 570efcad6b7SDag-Erling Smørgrav 571efcad6b7SDag-Erling Smørgrav if (dst) { 572efcad6b7SDag-Erling Smørgrav tmp_dst = xstrdup(dst); 573efcad6b7SDag-Erling Smørgrav tmp_dst = make_absolute(tmp_dst, pwd); 574efcad6b7SDag-Erling Smørgrav } 575efcad6b7SDag-Erling Smørgrav 576efcad6b7SDag-Erling Smørgrav memset(&g, 0, sizeof(g)); 577efcad6b7SDag-Erling Smørgrav debug3("Looking up %s", src); 578efcad6b7SDag-Erling Smørgrav if (glob(src, 0, NULL, &g)) { 579efcad6b7SDag-Erling Smørgrav error("File \"%s\" not found.", src); 580efcad6b7SDag-Erling Smørgrav err = -1; 581efcad6b7SDag-Erling Smørgrav goto out; 582efcad6b7SDag-Erling Smørgrav } 583efcad6b7SDag-Erling Smørgrav 584efcad6b7SDag-Erling Smørgrav /* If multiple matches, dst may be directory or unspecified */ 585efcad6b7SDag-Erling Smørgrav if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { 586efcad6b7SDag-Erling Smørgrav error("Multiple files match, but \"%s\" is not a directory", 587efcad6b7SDag-Erling Smørgrav tmp_dst); 588efcad6b7SDag-Erling Smørgrav err = -1; 589efcad6b7SDag-Erling Smørgrav goto out; 590efcad6b7SDag-Erling Smørgrav } 591efcad6b7SDag-Erling Smørgrav 592d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 593efcad6b7SDag-Erling Smørgrav if (!is_reg(g.gl_pathv[i])) { 594efcad6b7SDag-Erling Smørgrav error("skipping non-regular file %s", 595efcad6b7SDag-Erling Smørgrav g.gl_pathv[i]); 596efcad6b7SDag-Erling Smørgrav continue; 597efcad6b7SDag-Erling Smørgrav } 598efcad6b7SDag-Erling Smørgrav if (infer_path(g.gl_pathv[i], &tmp)) { 599efcad6b7SDag-Erling Smørgrav err = -1; 600efcad6b7SDag-Erling Smørgrav goto out; 601efcad6b7SDag-Erling Smørgrav } 602efcad6b7SDag-Erling Smørgrav 603efcad6b7SDag-Erling Smørgrav if (g.gl_matchc == 1 && tmp_dst) { 604efcad6b7SDag-Erling Smørgrav /* If directory specified, append filename */ 605efcad6b7SDag-Erling Smørgrav if (remote_is_dir(conn, tmp_dst)) { 606efcad6b7SDag-Erling Smørgrav if (infer_path(g.gl_pathv[0], &tmp)) { 607efcad6b7SDag-Erling Smørgrav err = 1; 608efcad6b7SDag-Erling Smørgrav goto out; 609efcad6b7SDag-Erling Smørgrav } 610efcad6b7SDag-Erling Smørgrav abs_dst = path_append(tmp_dst, tmp); 611efcad6b7SDag-Erling Smørgrav xfree(tmp); 612efcad6b7SDag-Erling Smørgrav } else 613efcad6b7SDag-Erling Smørgrav abs_dst = xstrdup(tmp_dst); 614efcad6b7SDag-Erling Smørgrav 615efcad6b7SDag-Erling Smørgrav } else if (tmp_dst) { 616efcad6b7SDag-Erling Smørgrav abs_dst = path_append(tmp_dst, tmp); 617efcad6b7SDag-Erling Smørgrav xfree(tmp); 618efcad6b7SDag-Erling Smørgrav } else 619efcad6b7SDag-Erling Smørgrav abs_dst = make_absolute(tmp, pwd); 620efcad6b7SDag-Erling Smørgrav 621efcad6b7SDag-Erling Smørgrav printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 622efcad6b7SDag-Erling Smørgrav if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 623efcad6b7SDag-Erling Smørgrav err = -1; 624efcad6b7SDag-Erling Smørgrav } 625efcad6b7SDag-Erling Smørgrav 626efcad6b7SDag-Erling Smørgrav out: 627efcad6b7SDag-Erling Smørgrav if (abs_dst) 628efcad6b7SDag-Erling Smørgrav xfree(abs_dst); 629efcad6b7SDag-Erling Smørgrav if (tmp_dst) 630efcad6b7SDag-Erling Smørgrav xfree(tmp_dst); 631efcad6b7SDag-Erling Smørgrav globfree(&g); 632efcad6b7SDag-Erling Smørgrav return(err); 633efcad6b7SDag-Erling Smørgrav } 634efcad6b7SDag-Erling Smørgrav 635efcad6b7SDag-Erling Smørgrav static int 636efcad6b7SDag-Erling Smørgrav sdirent_comp(const void *aa, const void *bb) 637efcad6b7SDag-Erling Smørgrav { 638efcad6b7SDag-Erling Smørgrav SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; 639efcad6b7SDag-Erling Smørgrav SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; 640d74d50a8SDag-Erling Smørgrav int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; 641efcad6b7SDag-Erling Smørgrav 642d74d50a8SDag-Erling Smørgrav #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) 643d74d50a8SDag-Erling Smørgrav if (sort_flag & LS_NAME_SORT) 644d74d50a8SDag-Erling Smørgrav return (rmul * strcmp(a->filename, b->filename)); 645d74d50a8SDag-Erling Smørgrav else if (sort_flag & LS_TIME_SORT) 646d74d50a8SDag-Erling Smørgrav return (rmul * NCMP(a->a.mtime, b->a.mtime)); 647d74d50a8SDag-Erling Smørgrav else if (sort_flag & LS_SIZE_SORT) 648d74d50a8SDag-Erling Smørgrav return (rmul * NCMP(a->a.size, b->a.size)); 649d74d50a8SDag-Erling Smørgrav 650d74d50a8SDag-Erling Smørgrav fatal("Unknown ls sort type"); 651efcad6b7SDag-Erling Smørgrav } 652efcad6b7SDag-Erling Smørgrav 653efcad6b7SDag-Erling Smørgrav /* sftp ls.1 replacement for directories */ 654efcad6b7SDag-Erling Smørgrav static int 655efcad6b7SDag-Erling Smørgrav do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) 656efcad6b7SDag-Erling Smørgrav { 657efcad6b7SDag-Erling Smørgrav int n, c = 1, colspace = 0, columns = 1; 658efcad6b7SDag-Erling Smørgrav SFTP_DIRENT **d; 659efcad6b7SDag-Erling Smørgrav 660efcad6b7SDag-Erling Smørgrav if ((n = do_readdir(conn, path, &d)) != 0) 661efcad6b7SDag-Erling Smørgrav return (n); 662efcad6b7SDag-Erling Smørgrav 663d74d50a8SDag-Erling Smørgrav if (!(lflag & LS_SHORT_VIEW)) { 664efcad6b7SDag-Erling Smørgrav int m = 0, width = 80; 665efcad6b7SDag-Erling Smørgrav struct winsize ws; 666efcad6b7SDag-Erling Smørgrav char *tmp; 667efcad6b7SDag-Erling Smørgrav 668efcad6b7SDag-Erling Smørgrav /* Count entries for sort and find longest filename */ 669d74d50a8SDag-Erling Smørgrav for (n = 0; d[n] != NULL; n++) { 670d74d50a8SDag-Erling Smørgrav if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL)) 671efcad6b7SDag-Erling Smørgrav m = MAX(m, strlen(d[n]->filename)); 672d74d50a8SDag-Erling Smørgrav } 673efcad6b7SDag-Erling Smørgrav 674efcad6b7SDag-Erling Smørgrav /* Add any subpath that also needs to be counted */ 675efcad6b7SDag-Erling Smørgrav tmp = path_strip(path, strip_path); 676efcad6b7SDag-Erling Smørgrav m += strlen(tmp); 677efcad6b7SDag-Erling Smørgrav xfree(tmp); 678efcad6b7SDag-Erling Smørgrav 679efcad6b7SDag-Erling Smørgrav if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 680efcad6b7SDag-Erling Smørgrav width = ws.ws_col; 681efcad6b7SDag-Erling Smørgrav 682efcad6b7SDag-Erling Smørgrav columns = width / (m + 2); 683efcad6b7SDag-Erling Smørgrav columns = MAX(columns, 1); 684efcad6b7SDag-Erling Smørgrav colspace = width / columns; 685efcad6b7SDag-Erling Smørgrav colspace = MIN(colspace, width); 686efcad6b7SDag-Erling Smørgrav } 687efcad6b7SDag-Erling Smørgrav 688d74d50a8SDag-Erling Smørgrav if (lflag & SORT_FLAGS) { 689d74d50a8SDag-Erling Smørgrav sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); 690efcad6b7SDag-Erling Smørgrav qsort(d, n, sizeof(*d), sdirent_comp); 691d74d50a8SDag-Erling Smørgrav } 692efcad6b7SDag-Erling Smørgrav 693d74d50a8SDag-Erling Smørgrav for (n = 0; d[n] != NULL && !interrupted; n++) { 694efcad6b7SDag-Erling Smørgrav char *tmp, *fname; 695efcad6b7SDag-Erling Smørgrav 696d74d50a8SDag-Erling Smørgrav if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL)) 697d74d50a8SDag-Erling Smørgrav continue; 698d74d50a8SDag-Erling Smørgrav 699efcad6b7SDag-Erling Smørgrav tmp = path_append(path, d[n]->filename); 700efcad6b7SDag-Erling Smørgrav fname = path_strip(tmp, strip_path); 701efcad6b7SDag-Erling Smørgrav xfree(tmp); 702efcad6b7SDag-Erling Smørgrav 703d74d50a8SDag-Erling Smørgrav if (lflag & LS_LONG_VIEW) { 704d74d50a8SDag-Erling Smørgrav if (lflag & LS_NUMERIC_VIEW) { 705efcad6b7SDag-Erling Smørgrav char *lname; 706efcad6b7SDag-Erling Smørgrav struct stat sb; 707efcad6b7SDag-Erling Smørgrav 708efcad6b7SDag-Erling Smørgrav memset(&sb, 0, sizeof(sb)); 709efcad6b7SDag-Erling Smørgrav attrib_to_stat(&d[n]->a, &sb); 710efcad6b7SDag-Erling Smørgrav lname = ls_file(fname, &sb, 1); 711efcad6b7SDag-Erling Smørgrav printf("%s\n", lname); 712efcad6b7SDag-Erling Smørgrav xfree(lname); 713d74d50a8SDag-Erling Smørgrav } else 714d74d50a8SDag-Erling Smørgrav printf("%s\n", d[n]->longname); 715efcad6b7SDag-Erling Smørgrav } else { 716efcad6b7SDag-Erling Smørgrav printf("%-*s", colspace, fname); 717efcad6b7SDag-Erling Smørgrav if (c >= columns) { 718efcad6b7SDag-Erling Smørgrav printf("\n"); 719efcad6b7SDag-Erling Smørgrav c = 1; 720efcad6b7SDag-Erling Smørgrav } else 721efcad6b7SDag-Erling Smørgrav c++; 722efcad6b7SDag-Erling Smørgrav } 723efcad6b7SDag-Erling Smørgrav 724efcad6b7SDag-Erling Smørgrav xfree(fname); 725efcad6b7SDag-Erling Smørgrav } 726efcad6b7SDag-Erling Smørgrav 727d74d50a8SDag-Erling Smørgrav if (!(lflag & LS_LONG_VIEW) && (c != 1)) 728efcad6b7SDag-Erling Smørgrav printf("\n"); 729efcad6b7SDag-Erling Smørgrav 730efcad6b7SDag-Erling Smørgrav free_sftp_dirents(d); 731efcad6b7SDag-Erling Smørgrav return (0); 732efcad6b7SDag-Erling Smørgrav } 733efcad6b7SDag-Erling Smørgrav 734efcad6b7SDag-Erling Smørgrav /* sftp ls.1 replacement which handles path globs */ 735efcad6b7SDag-Erling Smørgrav static int 736efcad6b7SDag-Erling Smørgrav do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, 737efcad6b7SDag-Erling Smørgrav int lflag) 738efcad6b7SDag-Erling Smørgrav { 739efcad6b7SDag-Erling Smørgrav glob_t g; 740efcad6b7SDag-Erling Smørgrav int i, c = 1, colspace = 0, columns = 1; 741efcad6b7SDag-Erling Smørgrav Attrib *a; 742efcad6b7SDag-Erling Smørgrav 743efcad6b7SDag-Erling Smørgrav memset(&g, 0, sizeof(g)); 744efcad6b7SDag-Erling Smørgrav 745efcad6b7SDag-Erling Smørgrav if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, 746efcad6b7SDag-Erling Smørgrav NULL, &g)) { 747efcad6b7SDag-Erling Smørgrav error("Can't ls: \"%s\" not found", path); 748efcad6b7SDag-Erling Smørgrav return (-1); 749efcad6b7SDag-Erling Smørgrav } 750efcad6b7SDag-Erling Smørgrav 751d74d50a8SDag-Erling Smørgrav if (interrupted) 752d74d50a8SDag-Erling Smørgrav goto out; 753d74d50a8SDag-Erling Smørgrav 754efcad6b7SDag-Erling Smørgrav /* 755efcad6b7SDag-Erling Smørgrav * If the glob returns a single match, which is the same as the 756efcad6b7SDag-Erling Smørgrav * input glob, and it is a directory, then just list its contents 757efcad6b7SDag-Erling Smørgrav */ 758efcad6b7SDag-Erling Smørgrav if (g.gl_pathc == 1 && 759efcad6b7SDag-Erling Smørgrav strncmp(path, g.gl_pathv[0], strlen(g.gl_pathv[0]) - 1) == 0) { 760efcad6b7SDag-Erling Smørgrav if ((a = do_lstat(conn, path, 1)) == NULL) { 761efcad6b7SDag-Erling Smørgrav globfree(&g); 762efcad6b7SDag-Erling Smørgrav return (-1); 763efcad6b7SDag-Erling Smørgrav } 764efcad6b7SDag-Erling Smørgrav if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 765efcad6b7SDag-Erling Smørgrav S_ISDIR(a->perm)) { 766efcad6b7SDag-Erling Smørgrav globfree(&g); 767efcad6b7SDag-Erling Smørgrav return (do_ls_dir(conn, path, strip_path, lflag)); 768efcad6b7SDag-Erling Smørgrav } 769efcad6b7SDag-Erling Smørgrav } 770efcad6b7SDag-Erling Smørgrav 771d74d50a8SDag-Erling Smørgrav if (!(lflag & LS_SHORT_VIEW)) { 772efcad6b7SDag-Erling Smørgrav int m = 0, width = 80; 773efcad6b7SDag-Erling Smørgrav struct winsize ws; 774efcad6b7SDag-Erling Smørgrav 775efcad6b7SDag-Erling Smørgrav /* Count entries for sort and find longest filename */ 776efcad6b7SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i]; i++) 777efcad6b7SDag-Erling Smørgrav m = MAX(m, strlen(g.gl_pathv[i])); 778efcad6b7SDag-Erling Smørgrav 779efcad6b7SDag-Erling Smørgrav if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 780efcad6b7SDag-Erling Smørgrav width = ws.ws_col; 781efcad6b7SDag-Erling Smørgrav 782efcad6b7SDag-Erling Smørgrav columns = width / (m + 2); 783efcad6b7SDag-Erling Smørgrav columns = MAX(columns, 1); 784efcad6b7SDag-Erling Smørgrav colspace = width / columns; 785efcad6b7SDag-Erling Smørgrav } 786efcad6b7SDag-Erling Smørgrav 787d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 788efcad6b7SDag-Erling Smørgrav char *fname; 789efcad6b7SDag-Erling Smørgrav 790efcad6b7SDag-Erling Smørgrav fname = path_strip(g.gl_pathv[i], strip_path); 791efcad6b7SDag-Erling Smørgrav 792d74d50a8SDag-Erling Smørgrav if (lflag & LS_LONG_VIEW) { 793efcad6b7SDag-Erling Smørgrav char *lname; 794efcad6b7SDag-Erling Smørgrav struct stat sb; 795efcad6b7SDag-Erling Smørgrav 796efcad6b7SDag-Erling Smørgrav /* 797efcad6b7SDag-Erling Smørgrav * XXX: this is slow - 1 roundtrip per path 798efcad6b7SDag-Erling Smørgrav * A solution to this is to fork glob() and 799efcad6b7SDag-Erling Smørgrav * build a sftp specific version which keeps the 800efcad6b7SDag-Erling Smørgrav * attribs (which currently get thrown away) 801efcad6b7SDag-Erling Smørgrav * that the server returns as well as the filenames. 802efcad6b7SDag-Erling Smørgrav */ 803efcad6b7SDag-Erling Smørgrav memset(&sb, 0, sizeof(sb)); 804efcad6b7SDag-Erling Smørgrav a = do_lstat(conn, g.gl_pathv[i], 1); 805efcad6b7SDag-Erling Smørgrav if (a != NULL) 806efcad6b7SDag-Erling Smørgrav attrib_to_stat(a, &sb); 807efcad6b7SDag-Erling Smørgrav lname = ls_file(fname, &sb, 1); 808efcad6b7SDag-Erling Smørgrav printf("%s\n", lname); 809efcad6b7SDag-Erling Smørgrav xfree(lname); 810efcad6b7SDag-Erling Smørgrav } else { 811efcad6b7SDag-Erling Smørgrav printf("%-*s", colspace, fname); 812efcad6b7SDag-Erling Smørgrav if (c >= columns) { 813efcad6b7SDag-Erling Smørgrav printf("\n"); 814efcad6b7SDag-Erling Smørgrav c = 1; 815efcad6b7SDag-Erling Smørgrav } else 816efcad6b7SDag-Erling Smørgrav c++; 817efcad6b7SDag-Erling Smørgrav } 818efcad6b7SDag-Erling Smørgrav xfree(fname); 819efcad6b7SDag-Erling Smørgrav } 820efcad6b7SDag-Erling Smørgrav 821d74d50a8SDag-Erling Smørgrav if (!(lflag & LS_LONG_VIEW) && (c != 1)) 822efcad6b7SDag-Erling Smørgrav printf("\n"); 823efcad6b7SDag-Erling Smørgrav 824d74d50a8SDag-Erling Smørgrav out: 825efcad6b7SDag-Erling Smørgrav if (g.gl_pathc) 826efcad6b7SDag-Erling Smørgrav globfree(&g); 827efcad6b7SDag-Erling Smørgrav 828efcad6b7SDag-Erling Smørgrav return (0); 829efcad6b7SDag-Erling Smørgrav } 830efcad6b7SDag-Erling Smørgrav 831efcad6b7SDag-Erling Smørgrav static int 832efcad6b7SDag-Erling Smørgrav parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, 833efcad6b7SDag-Erling Smørgrav unsigned long *n_arg, char **path1, char **path2) 834efcad6b7SDag-Erling Smørgrav { 835efcad6b7SDag-Erling Smørgrav const char *cmd, *cp = *cpp; 836efcad6b7SDag-Erling Smørgrav char *cp2; 837efcad6b7SDag-Erling Smørgrav int base = 0; 838efcad6b7SDag-Erling Smørgrav long l; 839efcad6b7SDag-Erling Smørgrav int i, cmdnum; 840efcad6b7SDag-Erling Smørgrav 841efcad6b7SDag-Erling Smørgrav /* Skip leading whitespace */ 842efcad6b7SDag-Erling Smørgrav cp = cp + strspn(cp, WHITESPACE); 843efcad6b7SDag-Erling Smørgrav 844efcad6b7SDag-Erling Smørgrav /* Ignore blank lines and lines which begin with comment '#' char */ 845efcad6b7SDag-Erling Smørgrav if (*cp == '\0' || *cp == '#') 846efcad6b7SDag-Erling Smørgrav return (0); 847efcad6b7SDag-Erling Smørgrav 848efcad6b7SDag-Erling Smørgrav /* Check for leading '-' (disable error processing) */ 849efcad6b7SDag-Erling Smørgrav *iflag = 0; 850efcad6b7SDag-Erling Smørgrav if (*cp == '-') { 851efcad6b7SDag-Erling Smørgrav *iflag = 1; 852efcad6b7SDag-Erling Smørgrav cp++; 853efcad6b7SDag-Erling Smørgrav } 854efcad6b7SDag-Erling Smørgrav 855efcad6b7SDag-Erling Smørgrav /* Figure out which command we have */ 856efcad6b7SDag-Erling Smørgrav for (i = 0; cmds[i].c; i++) { 857efcad6b7SDag-Erling Smørgrav int cmdlen = strlen(cmds[i].c); 858efcad6b7SDag-Erling Smørgrav 859efcad6b7SDag-Erling Smørgrav /* Check for command followed by whitespace */ 860efcad6b7SDag-Erling Smørgrav if (!strncasecmp(cp, cmds[i].c, cmdlen) && 861efcad6b7SDag-Erling Smørgrav strchr(WHITESPACE, cp[cmdlen])) { 862efcad6b7SDag-Erling Smørgrav cp += cmdlen; 863efcad6b7SDag-Erling Smørgrav cp = cp + strspn(cp, WHITESPACE); 864efcad6b7SDag-Erling Smørgrav break; 865efcad6b7SDag-Erling Smørgrav } 866efcad6b7SDag-Erling Smørgrav } 867efcad6b7SDag-Erling Smørgrav cmdnum = cmds[i].n; 868efcad6b7SDag-Erling Smørgrav cmd = cmds[i].c; 869efcad6b7SDag-Erling Smørgrav 870efcad6b7SDag-Erling Smørgrav /* Special case */ 871efcad6b7SDag-Erling Smørgrav if (*cp == '!') { 872efcad6b7SDag-Erling Smørgrav cp++; 873efcad6b7SDag-Erling Smørgrav cmdnum = I_SHELL; 874efcad6b7SDag-Erling Smørgrav } else if (cmdnum == -1) { 875efcad6b7SDag-Erling Smørgrav error("Invalid command."); 876efcad6b7SDag-Erling Smørgrav return (-1); 877efcad6b7SDag-Erling Smørgrav } 878efcad6b7SDag-Erling Smørgrav 879efcad6b7SDag-Erling Smørgrav /* Get arguments and parse flags */ 880efcad6b7SDag-Erling Smørgrav *lflag = *pflag = *n_arg = 0; 881efcad6b7SDag-Erling Smørgrav *path1 = *path2 = NULL; 882efcad6b7SDag-Erling Smørgrav switch (cmdnum) { 883efcad6b7SDag-Erling Smørgrav case I_GET: 884efcad6b7SDag-Erling Smørgrav case I_PUT: 885efcad6b7SDag-Erling Smørgrav if (parse_getput_flags(&cp, pflag)) 886efcad6b7SDag-Erling Smørgrav return(-1); 887efcad6b7SDag-Erling Smørgrav /* Get first pathname (mandatory) */ 888efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 889efcad6b7SDag-Erling Smørgrav return(-1); 890efcad6b7SDag-Erling Smørgrav if (*path1 == NULL) { 891efcad6b7SDag-Erling Smørgrav error("You must specify at least one path after a " 892efcad6b7SDag-Erling Smørgrav "%s command.", cmd); 893efcad6b7SDag-Erling Smørgrav return(-1); 894efcad6b7SDag-Erling Smørgrav } 895efcad6b7SDag-Erling Smørgrav /* Try to get second pathname (optional) */ 896efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path2)) 897efcad6b7SDag-Erling Smørgrav return(-1); 898efcad6b7SDag-Erling Smørgrav break; 899efcad6b7SDag-Erling Smørgrav case I_RENAME: 900efcad6b7SDag-Erling Smørgrav case I_SYMLINK: 901efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 902efcad6b7SDag-Erling Smørgrav return(-1); 903efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path2)) 904efcad6b7SDag-Erling Smørgrav return(-1); 905efcad6b7SDag-Erling Smørgrav if (!*path1 || !*path2) { 906efcad6b7SDag-Erling Smørgrav error("You must specify two paths after a %s " 907efcad6b7SDag-Erling Smørgrav "command.", cmd); 908efcad6b7SDag-Erling Smørgrav return(-1); 909efcad6b7SDag-Erling Smørgrav } 910efcad6b7SDag-Erling Smørgrav break; 911efcad6b7SDag-Erling Smørgrav case I_RM: 912efcad6b7SDag-Erling Smørgrav case I_MKDIR: 913efcad6b7SDag-Erling Smørgrav case I_RMDIR: 914efcad6b7SDag-Erling Smørgrav case I_CHDIR: 915efcad6b7SDag-Erling Smørgrav case I_LCHDIR: 916efcad6b7SDag-Erling Smørgrav case I_LMKDIR: 917efcad6b7SDag-Erling Smørgrav /* Get pathname (mandatory) */ 918efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 919efcad6b7SDag-Erling Smørgrav return(-1); 920efcad6b7SDag-Erling Smørgrav if (*path1 == NULL) { 921efcad6b7SDag-Erling Smørgrav error("You must specify a path after a %s command.", 922efcad6b7SDag-Erling Smørgrav cmd); 923efcad6b7SDag-Erling Smørgrav return(-1); 924efcad6b7SDag-Erling Smørgrav } 925efcad6b7SDag-Erling Smørgrav break; 926efcad6b7SDag-Erling Smørgrav case I_LS: 927efcad6b7SDag-Erling Smørgrav if (parse_ls_flags(&cp, lflag)) 928efcad6b7SDag-Erling Smørgrav return(-1); 929efcad6b7SDag-Erling Smørgrav /* Path is optional */ 930efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 931efcad6b7SDag-Erling Smørgrav return(-1); 932efcad6b7SDag-Erling Smørgrav break; 933efcad6b7SDag-Erling Smørgrav case I_LLS: 934efcad6b7SDag-Erling Smørgrav case I_SHELL: 935efcad6b7SDag-Erling Smørgrav /* Uses the rest of the line */ 936efcad6b7SDag-Erling Smørgrav break; 937efcad6b7SDag-Erling Smørgrav case I_LUMASK: 938efcad6b7SDag-Erling Smørgrav base = 8; 939efcad6b7SDag-Erling Smørgrav case I_CHMOD: 940efcad6b7SDag-Erling Smørgrav base = 8; 941efcad6b7SDag-Erling Smørgrav case I_CHOWN: 942efcad6b7SDag-Erling Smørgrav case I_CHGRP: 943efcad6b7SDag-Erling Smørgrav /* Get numeric arg (mandatory) */ 944efcad6b7SDag-Erling Smørgrav l = strtol(cp, &cp2, base); 945efcad6b7SDag-Erling Smørgrav if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) && 946efcad6b7SDag-Erling Smørgrav errno == ERANGE) || l < 0) { 947efcad6b7SDag-Erling Smørgrav error("You must supply a numeric argument " 948efcad6b7SDag-Erling Smørgrav "to the %s command.", cmd); 949efcad6b7SDag-Erling Smørgrav return(-1); 950efcad6b7SDag-Erling Smørgrav } 951efcad6b7SDag-Erling Smørgrav cp = cp2; 952efcad6b7SDag-Erling Smørgrav *n_arg = l; 953efcad6b7SDag-Erling Smørgrav if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp)) 954efcad6b7SDag-Erling Smørgrav break; 955efcad6b7SDag-Erling Smørgrav if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) { 956efcad6b7SDag-Erling Smørgrav error("You must supply a numeric argument " 957efcad6b7SDag-Erling Smørgrav "to the %s command.", cmd); 958efcad6b7SDag-Erling Smørgrav return(-1); 959efcad6b7SDag-Erling Smørgrav } 960efcad6b7SDag-Erling Smørgrav cp += strspn(cp, WHITESPACE); 961efcad6b7SDag-Erling Smørgrav 962efcad6b7SDag-Erling Smørgrav /* Get pathname (mandatory) */ 963efcad6b7SDag-Erling Smørgrav if (get_pathname(&cp, path1)) 964efcad6b7SDag-Erling Smørgrav return(-1); 965efcad6b7SDag-Erling Smørgrav if (*path1 == NULL) { 966efcad6b7SDag-Erling Smørgrav error("You must specify a path after a %s command.", 967efcad6b7SDag-Erling Smørgrav cmd); 968efcad6b7SDag-Erling Smørgrav return(-1); 969efcad6b7SDag-Erling Smørgrav } 970efcad6b7SDag-Erling Smørgrav break; 971efcad6b7SDag-Erling Smørgrav case I_QUIT: 972efcad6b7SDag-Erling Smørgrav case I_PWD: 973efcad6b7SDag-Erling Smørgrav case I_LPWD: 974efcad6b7SDag-Erling Smørgrav case I_HELP: 975efcad6b7SDag-Erling Smørgrav case I_VERSION: 976efcad6b7SDag-Erling Smørgrav case I_PROGRESS: 977efcad6b7SDag-Erling Smørgrav break; 978efcad6b7SDag-Erling Smørgrav default: 979efcad6b7SDag-Erling Smørgrav fatal("Command not implemented"); 980efcad6b7SDag-Erling Smørgrav } 981efcad6b7SDag-Erling Smørgrav 982efcad6b7SDag-Erling Smørgrav *cpp = cp; 983efcad6b7SDag-Erling Smørgrav return(cmdnum); 984efcad6b7SDag-Erling Smørgrav } 985efcad6b7SDag-Erling Smørgrav 986efcad6b7SDag-Erling Smørgrav static int 987efcad6b7SDag-Erling Smørgrav parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 988efcad6b7SDag-Erling Smørgrav int err_abort) 989efcad6b7SDag-Erling Smørgrav { 990efcad6b7SDag-Erling Smørgrav char *path1, *path2, *tmp; 991efcad6b7SDag-Erling Smørgrav int pflag, lflag, iflag, cmdnum, i; 992efcad6b7SDag-Erling Smørgrav unsigned long n_arg; 993efcad6b7SDag-Erling Smørgrav Attrib a, *aa; 994efcad6b7SDag-Erling Smørgrav char path_buf[MAXPATHLEN]; 995efcad6b7SDag-Erling Smørgrav int err = 0; 996efcad6b7SDag-Erling Smørgrav glob_t g; 997efcad6b7SDag-Erling Smørgrav 998efcad6b7SDag-Erling Smørgrav path1 = path2 = NULL; 999efcad6b7SDag-Erling Smørgrav cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg, 1000efcad6b7SDag-Erling Smørgrav &path1, &path2); 1001efcad6b7SDag-Erling Smørgrav 1002efcad6b7SDag-Erling Smørgrav if (iflag != 0) 1003efcad6b7SDag-Erling Smørgrav err_abort = 0; 1004efcad6b7SDag-Erling Smørgrav 1005efcad6b7SDag-Erling Smørgrav memset(&g, 0, sizeof(g)); 1006efcad6b7SDag-Erling Smørgrav 1007efcad6b7SDag-Erling Smørgrav /* Perform command */ 1008efcad6b7SDag-Erling Smørgrav switch (cmdnum) { 1009efcad6b7SDag-Erling Smørgrav case 0: 1010efcad6b7SDag-Erling Smørgrav /* Blank line */ 1011efcad6b7SDag-Erling Smørgrav break; 1012efcad6b7SDag-Erling Smørgrav case -1: 1013efcad6b7SDag-Erling Smørgrav /* Unrecognized command */ 1014efcad6b7SDag-Erling Smørgrav err = -1; 1015efcad6b7SDag-Erling Smørgrav break; 1016efcad6b7SDag-Erling Smørgrav case I_GET: 1017efcad6b7SDag-Erling Smørgrav err = process_get(conn, path1, path2, *pwd, pflag); 1018efcad6b7SDag-Erling Smørgrav break; 1019efcad6b7SDag-Erling Smørgrav case I_PUT: 1020efcad6b7SDag-Erling Smørgrav err = process_put(conn, path1, path2, *pwd, pflag); 1021efcad6b7SDag-Erling Smørgrav break; 1022efcad6b7SDag-Erling Smørgrav case I_RENAME: 1023efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1024efcad6b7SDag-Erling Smørgrav path2 = make_absolute(path2, *pwd); 1025efcad6b7SDag-Erling Smørgrav err = do_rename(conn, path1, path2); 1026efcad6b7SDag-Erling Smørgrav break; 1027efcad6b7SDag-Erling Smørgrav case I_SYMLINK: 1028efcad6b7SDag-Erling Smørgrav path2 = make_absolute(path2, *pwd); 1029efcad6b7SDag-Erling Smørgrav err = do_symlink(conn, path1, path2); 1030efcad6b7SDag-Erling Smørgrav break; 1031efcad6b7SDag-Erling Smørgrav case I_RM: 1032efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1033efcad6b7SDag-Erling Smørgrav remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1034d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1035efcad6b7SDag-Erling Smørgrav printf("Removing %s\n", g.gl_pathv[i]); 1036efcad6b7SDag-Erling Smørgrav err = do_rm(conn, g.gl_pathv[i]); 1037efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1038efcad6b7SDag-Erling Smørgrav break; 1039efcad6b7SDag-Erling Smørgrav } 1040efcad6b7SDag-Erling Smørgrav break; 1041efcad6b7SDag-Erling Smørgrav case I_MKDIR: 1042efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1043efcad6b7SDag-Erling Smørgrav attrib_clear(&a); 1044efcad6b7SDag-Erling Smørgrav a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1045efcad6b7SDag-Erling Smørgrav a.perm = 0777; 1046efcad6b7SDag-Erling Smørgrav err = do_mkdir(conn, path1, &a); 1047efcad6b7SDag-Erling Smørgrav break; 1048efcad6b7SDag-Erling Smørgrav case I_RMDIR: 1049efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1050efcad6b7SDag-Erling Smørgrav err = do_rmdir(conn, path1); 1051efcad6b7SDag-Erling Smørgrav break; 1052efcad6b7SDag-Erling Smørgrav case I_CHDIR: 1053efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1054efcad6b7SDag-Erling Smørgrav if ((tmp = do_realpath(conn, path1)) == NULL) { 1055efcad6b7SDag-Erling Smørgrav err = 1; 1056efcad6b7SDag-Erling Smørgrav break; 1057efcad6b7SDag-Erling Smørgrav } 1058efcad6b7SDag-Erling Smørgrav if ((aa = do_stat(conn, tmp, 0)) == NULL) { 1059efcad6b7SDag-Erling Smørgrav xfree(tmp); 1060efcad6b7SDag-Erling Smørgrav err = 1; 1061efcad6b7SDag-Erling Smørgrav break; 1062efcad6b7SDag-Erling Smørgrav } 1063efcad6b7SDag-Erling Smørgrav if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { 1064efcad6b7SDag-Erling Smørgrav error("Can't change directory: Can't check target"); 1065efcad6b7SDag-Erling Smørgrav xfree(tmp); 1066efcad6b7SDag-Erling Smørgrav err = 1; 1067efcad6b7SDag-Erling Smørgrav break; 1068efcad6b7SDag-Erling Smørgrav } 1069efcad6b7SDag-Erling Smørgrav if (!S_ISDIR(aa->perm)) { 1070efcad6b7SDag-Erling Smørgrav error("Can't change directory: \"%s\" is not " 1071efcad6b7SDag-Erling Smørgrav "a directory", tmp); 1072efcad6b7SDag-Erling Smørgrav xfree(tmp); 1073efcad6b7SDag-Erling Smørgrav err = 1; 1074efcad6b7SDag-Erling Smørgrav break; 1075efcad6b7SDag-Erling Smørgrav } 1076efcad6b7SDag-Erling Smørgrav xfree(*pwd); 1077efcad6b7SDag-Erling Smørgrav *pwd = tmp; 1078efcad6b7SDag-Erling Smørgrav break; 1079efcad6b7SDag-Erling Smørgrav case I_LS: 1080efcad6b7SDag-Erling Smørgrav if (!path1) { 1081efcad6b7SDag-Erling Smørgrav do_globbed_ls(conn, *pwd, *pwd, lflag); 1082efcad6b7SDag-Erling Smørgrav break; 1083efcad6b7SDag-Erling Smørgrav } 1084efcad6b7SDag-Erling Smørgrav 1085efcad6b7SDag-Erling Smørgrav /* Strip pwd off beginning of non-absolute paths */ 1086efcad6b7SDag-Erling Smørgrav tmp = NULL; 1087efcad6b7SDag-Erling Smørgrav if (*path1 != '/') 1088efcad6b7SDag-Erling Smørgrav tmp = *pwd; 1089efcad6b7SDag-Erling Smørgrav 1090efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1091efcad6b7SDag-Erling Smørgrav err = do_globbed_ls(conn, path1, tmp, lflag); 1092efcad6b7SDag-Erling Smørgrav break; 1093efcad6b7SDag-Erling Smørgrav case I_LCHDIR: 1094efcad6b7SDag-Erling Smørgrav if (chdir(path1) == -1) { 1095efcad6b7SDag-Erling Smørgrav error("Couldn't change local directory to " 1096efcad6b7SDag-Erling Smørgrav "\"%s\": %s", path1, strerror(errno)); 1097efcad6b7SDag-Erling Smørgrav err = 1; 1098efcad6b7SDag-Erling Smørgrav } 1099efcad6b7SDag-Erling Smørgrav break; 1100efcad6b7SDag-Erling Smørgrav case I_LMKDIR: 1101efcad6b7SDag-Erling Smørgrav if (mkdir(path1, 0777) == -1) { 1102efcad6b7SDag-Erling Smørgrav error("Couldn't create local directory " 1103efcad6b7SDag-Erling Smørgrav "\"%s\": %s", path1, strerror(errno)); 1104efcad6b7SDag-Erling Smørgrav err = 1; 1105efcad6b7SDag-Erling Smørgrav } 1106efcad6b7SDag-Erling Smørgrav break; 1107efcad6b7SDag-Erling Smørgrav case I_LLS: 1108efcad6b7SDag-Erling Smørgrav local_do_ls(cmd); 1109efcad6b7SDag-Erling Smørgrav break; 1110efcad6b7SDag-Erling Smørgrav case I_SHELL: 1111efcad6b7SDag-Erling Smørgrav local_do_shell(cmd); 1112efcad6b7SDag-Erling Smørgrav break; 1113efcad6b7SDag-Erling Smørgrav case I_LUMASK: 1114efcad6b7SDag-Erling Smørgrav umask(n_arg); 1115efcad6b7SDag-Erling Smørgrav printf("Local umask: %03lo\n", n_arg); 1116efcad6b7SDag-Erling Smørgrav break; 1117efcad6b7SDag-Erling Smørgrav case I_CHMOD: 1118efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1119efcad6b7SDag-Erling Smørgrav attrib_clear(&a); 1120efcad6b7SDag-Erling Smørgrav a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1121efcad6b7SDag-Erling Smørgrav a.perm = n_arg; 1122efcad6b7SDag-Erling Smørgrav remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1123d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1124efcad6b7SDag-Erling Smørgrav printf("Changing mode on %s\n", g.gl_pathv[i]); 1125efcad6b7SDag-Erling Smørgrav err = do_setstat(conn, g.gl_pathv[i], &a); 1126efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1127efcad6b7SDag-Erling Smørgrav break; 1128efcad6b7SDag-Erling Smørgrav } 1129efcad6b7SDag-Erling Smørgrav break; 1130efcad6b7SDag-Erling Smørgrav case I_CHOWN: 1131efcad6b7SDag-Erling Smørgrav case I_CHGRP: 1132efcad6b7SDag-Erling Smørgrav path1 = make_absolute(path1, *pwd); 1133efcad6b7SDag-Erling Smørgrav remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1134d74d50a8SDag-Erling Smørgrav for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1135efcad6b7SDag-Erling Smørgrav if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1136efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1137efcad6b7SDag-Erling Smørgrav break; 1138efcad6b7SDag-Erling Smørgrav else 1139efcad6b7SDag-Erling Smørgrav continue; 1140efcad6b7SDag-Erling Smørgrav } 1141efcad6b7SDag-Erling Smørgrav if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1142efcad6b7SDag-Erling Smørgrav error("Can't get current ownership of " 1143efcad6b7SDag-Erling Smørgrav "remote file \"%s\"", g.gl_pathv[i]); 1144efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1145efcad6b7SDag-Erling Smørgrav break; 1146efcad6b7SDag-Erling Smørgrav else 1147efcad6b7SDag-Erling Smørgrav continue; 1148efcad6b7SDag-Erling Smørgrav } 1149efcad6b7SDag-Erling Smørgrav aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1150efcad6b7SDag-Erling Smørgrav if (cmdnum == I_CHOWN) { 1151efcad6b7SDag-Erling Smørgrav printf("Changing owner on %s\n", g.gl_pathv[i]); 1152efcad6b7SDag-Erling Smørgrav aa->uid = n_arg; 1153efcad6b7SDag-Erling Smørgrav } else { 1154efcad6b7SDag-Erling Smørgrav printf("Changing group on %s\n", g.gl_pathv[i]); 1155efcad6b7SDag-Erling Smørgrav aa->gid = n_arg; 1156efcad6b7SDag-Erling Smørgrav } 1157efcad6b7SDag-Erling Smørgrav err = do_setstat(conn, g.gl_pathv[i], aa); 1158efcad6b7SDag-Erling Smørgrav if (err != 0 && err_abort) 1159efcad6b7SDag-Erling Smørgrav break; 1160efcad6b7SDag-Erling Smørgrav } 1161efcad6b7SDag-Erling Smørgrav break; 1162efcad6b7SDag-Erling Smørgrav case I_PWD: 1163efcad6b7SDag-Erling Smørgrav printf("Remote working directory: %s\n", *pwd); 1164efcad6b7SDag-Erling Smørgrav break; 1165efcad6b7SDag-Erling Smørgrav case I_LPWD: 1166efcad6b7SDag-Erling Smørgrav if (!getcwd(path_buf, sizeof(path_buf))) { 1167efcad6b7SDag-Erling Smørgrav error("Couldn't get local cwd: %s", strerror(errno)); 1168efcad6b7SDag-Erling Smørgrav err = -1; 1169efcad6b7SDag-Erling Smørgrav break; 1170efcad6b7SDag-Erling Smørgrav } 1171efcad6b7SDag-Erling Smørgrav printf("Local working directory: %s\n", path_buf); 1172efcad6b7SDag-Erling Smørgrav break; 1173efcad6b7SDag-Erling Smørgrav case I_QUIT: 1174efcad6b7SDag-Erling Smørgrav /* Processed below */ 1175efcad6b7SDag-Erling Smørgrav break; 1176efcad6b7SDag-Erling Smørgrav case I_HELP: 1177efcad6b7SDag-Erling Smørgrav help(); 1178efcad6b7SDag-Erling Smørgrav break; 1179efcad6b7SDag-Erling Smørgrav case I_VERSION: 1180efcad6b7SDag-Erling Smørgrav printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1181efcad6b7SDag-Erling Smørgrav break; 1182efcad6b7SDag-Erling Smørgrav case I_PROGRESS: 1183efcad6b7SDag-Erling Smørgrav showprogress = !showprogress; 1184efcad6b7SDag-Erling Smørgrav if (showprogress) 1185efcad6b7SDag-Erling Smørgrav printf("Progress meter enabled\n"); 1186efcad6b7SDag-Erling Smørgrav else 1187efcad6b7SDag-Erling Smørgrav printf("Progress meter disabled\n"); 1188efcad6b7SDag-Erling Smørgrav break; 1189efcad6b7SDag-Erling Smørgrav default: 1190efcad6b7SDag-Erling Smørgrav fatal("%d is not implemented", cmdnum); 1191efcad6b7SDag-Erling Smørgrav } 1192efcad6b7SDag-Erling Smørgrav 1193efcad6b7SDag-Erling Smørgrav if (g.gl_pathc) 1194efcad6b7SDag-Erling Smørgrav globfree(&g); 1195efcad6b7SDag-Erling Smørgrav if (path1) 1196efcad6b7SDag-Erling Smørgrav xfree(path1); 1197efcad6b7SDag-Erling Smørgrav if (path2) 1198efcad6b7SDag-Erling Smørgrav xfree(path2); 1199efcad6b7SDag-Erling Smørgrav 1200efcad6b7SDag-Erling Smørgrav /* If an unignored error occurs in batch mode we should abort. */ 1201efcad6b7SDag-Erling Smørgrav if (err_abort && err != 0) 1202efcad6b7SDag-Erling Smørgrav return (-1); 1203efcad6b7SDag-Erling Smørgrav else if (cmdnum == I_QUIT) 1204efcad6b7SDag-Erling Smørgrav return (1); 1205efcad6b7SDag-Erling Smørgrav 1206efcad6b7SDag-Erling Smørgrav return (0); 1207efcad6b7SDag-Erling Smørgrav } 1208efcad6b7SDag-Erling Smørgrav 1209efcad6b7SDag-Erling Smørgrav int 1210efcad6b7SDag-Erling Smørgrav interactive_loop(int fd_in, int fd_out, char *file1, char *file2) 1211efcad6b7SDag-Erling Smørgrav { 1212efcad6b7SDag-Erling Smørgrav char *pwd; 1213efcad6b7SDag-Erling Smørgrav char *dir = NULL; 1214efcad6b7SDag-Erling Smørgrav char cmd[2048]; 1215efcad6b7SDag-Erling Smørgrav struct sftp_conn *conn; 1216efcad6b7SDag-Erling Smørgrav int err; 1217efcad6b7SDag-Erling Smørgrav 1218efcad6b7SDag-Erling Smørgrav conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); 1219efcad6b7SDag-Erling Smørgrav if (conn == NULL) 1220efcad6b7SDag-Erling Smørgrav fatal("Couldn't initialise connection to server"); 1221efcad6b7SDag-Erling Smørgrav 1222efcad6b7SDag-Erling Smørgrav pwd = do_realpath(conn, "."); 1223efcad6b7SDag-Erling Smørgrav if (pwd == NULL) 1224efcad6b7SDag-Erling Smørgrav fatal("Need cwd"); 1225efcad6b7SDag-Erling Smørgrav 1226efcad6b7SDag-Erling Smørgrav if (file1 != NULL) { 1227efcad6b7SDag-Erling Smørgrav dir = xstrdup(file1); 1228efcad6b7SDag-Erling Smørgrav dir = make_absolute(dir, pwd); 1229efcad6b7SDag-Erling Smørgrav 1230efcad6b7SDag-Erling Smørgrav if (remote_is_dir(conn, dir) && file2 == NULL) { 1231efcad6b7SDag-Erling Smørgrav printf("Changing to: %s\n", dir); 1232efcad6b7SDag-Erling Smørgrav snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 1233efcad6b7SDag-Erling Smørgrav if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) 1234efcad6b7SDag-Erling Smørgrav return (-1); 1235efcad6b7SDag-Erling Smørgrav } else { 1236efcad6b7SDag-Erling Smørgrav if (file2 == NULL) 1237efcad6b7SDag-Erling Smørgrav snprintf(cmd, sizeof cmd, "get %s", dir); 1238efcad6b7SDag-Erling Smørgrav else 1239efcad6b7SDag-Erling Smørgrav snprintf(cmd, sizeof cmd, "get %s %s", dir, 1240efcad6b7SDag-Erling Smørgrav file2); 1241efcad6b7SDag-Erling Smørgrav 1242efcad6b7SDag-Erling Smørgrav err = parse_dispatch_command(conn, cmd, &pwd, 1); 1243efcad6b7SDag-Erling Smørgrav xfree(dir); 1244efcad6b7SDag-Erling Smørgrav xfree(pwd); 1245efcad6b7SDag-Erling Smørgrav return (err); 1246efcad6b7SDag-Erling Smørgrav } 1247efcad6b7SDag-Erling Smørgrav xfree(dir); 1248efcad6b7SDag-Erling Smørgrav } 1249efcad6b7SDag-Erling Smørgrav 1250efcad6b7SDag-Erling Smørgrav #if HAVE_SETVBUF 1251efcad6b7SDag-Erling Smørgrav setvbuf(stdout, NULL, _IOLBF, 0); 1252efcad6b7SDag-Erling Smørgrav setvbuf(infile, NULL, _IOLBF, 0); 1253efcad6b7SDag-Erling Smørgrav #else 1254efcad6b7SDag-Erling Smørgrav setlinebuf(stdout); 1255efcad6b7SDag-Erling Smørgrav setlinebuf(infile); 1256efcad6b7SDag-Erling Smørgrav #endif 1257efcad6b7SDag-Erling Smørgrav 1258efcad6b7SDag-Erling Smørgrav err = 0; 1259efcad6b7SDag-Erling Smørgrav for (;;) { 1260efcad6b7SDag-Erling Smørgrav char *cp; 1261efcad6b7SDag-Erling Smørgrav 1262d74d50a8SDag-Erling Smørgrav signal(SIGINT, SIG_IGN); 1263d74d50a8SDag-Erling Smørgrav 1264efcad6b7SDag-Erling Smørgrav printf("sftp> "); 1265efcad6b7SDag-Erling Smørgrav 1266efcad6b7SDag-Erling Smørgrav /* XXX: use libedit */ 1267efcad6b7SDag-Erling Smørgrav if (fgets(cmd, sizeof(cmd), infile) == NULL) { 1268efcad6b7SDag-Erling Smørgrav printf("\n"); 1269efcad6b7SDag-Erling Smørgrav break; 1270efcad6b7SDag-Erling Smørgrav } 1271efcad6b7SDag-Erling Smørgrav 1272efcad6b7SDag-Erling Smørgrav if (batchmode) /* Echo command */ 1273efcad6b7SDag-Erling Smørgrav printf("%s", cmd); 1274efcad6b7SDag-Erling Smørgrav 1275efcad6b7SDag-Erling Smørgrav cp = strrchr(cmd, '\n'); 1276efcad6b7SDag-Erling Smørgrav if (cp) 1277efcad6b7SDag-Erling Smørgrav *cp = '\0'; 1278efcad6b7SDag-Erling Smørgrav 1279d74d50a8SDag-Erling Smørgrav /* Handle user interrupts gracefully during commands */ 1280d74d50a8SDag-Erling Smørgrav interrupted = 0; 1281d74d50a8SDag-Erling Smørgrav signal(SIGINT, cmd_interrupt); 1282d74d50a8SDag-Erling Smørgrav 1283efcad6b7SDag-Erling Smørgrav err = parse_dispatch_command(conn, cmd, &pwd, batchmode); 1284efcad6b7SDag-Erling Smørgrav if (err != 0) 1285efcad6b7SDag-Erling Smørgrav break; 1286efcad6b7SDag-Erling Smørgrav } 1287efcad6b7SDag-Erling Smørgrav xfree(pwd); 1288efcad6b7SDag-Erling Smørgrav 1289efcad6b7SDag-Erling Smørgrav /* err == 1 signifies normal "quit" exit */ 1290efcad6b7SDag-Erling Smørgrav return (err >= 0 ? 0 : -1); 1291efcad6b7SDag-Erling Smørgrav } 1292d0c8c0bcSDag-Erling Smørgrav 1293ae1f160dSDag-Erling Smørgrav static void 1294d95e11bfSDag-Erling Smørgrav connect_to_server(char *path, char **args, int *in, int *out) 12951e8db6e2SBrian Feldman { 12961e8db6e2SBrian Feldman int c_in, c_out; 1297ee21a45fSDag-Erling Smørgrav 12981e8db6e2SBrian Feldman #ifdef USE_PIPES 12991e8db6e2SBrian Feldman int pin[2], pout[2]; 1300ee21a45fSDag-Erling Smørgrav 13011e8db6e2SBrian Feldman if ((pipe(pin) == -1) || (pipe(pout) == -1)) 13021e8db6e2SBrian Feldman fatal("pipe: %s", strerror(errno)); 13031e8db6e2SBrian Feldman *in = pin[0]; 13041e8db6e2SBrian Feldman *out = pout[1]; 13051e8db6e2SBrian Feldman c_in = pout[0]; 13061e8db6e2SBrian Feldman c_out = pin[1]; 13071e8db6e2SBrian Feldman #else /* USE_PIPES */ 13081e8db6e2SBrian Feldman int inout[2]; 1309ee21a45fSDag-Erling Smørgrav 13101e8db6e2SBrian Feldman if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 13111e8db6e2SBrian Feldman fatal("socketpair: %s", strerror(errno)); 13121e8db6e2SBrian Feldman *in = *out = inout[0]; 13131e8db6e2SBrian Feldman c_in = c_out = inout[1]; 13141e8db6e2SBrian Feldman #endif /* USE_PIPES */ 13151e8db6e2SBrian Feldman 1316d95e11bfSDag-Erling Smørgrav if ((sshpid = fork()) == -1) 13171e8db6e2SBrian Feldman fatal("fork: %s", strerror(errno)); 1318d95e11bfSDag-Erling Smørgrav else if (sshpid == 0) { 13191e8db6e2SBrian Feldman if ((dup2(c_in, STDIN_FILENO) == -1) || 13201e8db6e2SBrian Feldman (dup2(c_out, STDOUT_FILENO) == -1)) { 13211e8db6e2SBrian Feldman fprintf(stderr, "dup2: %s\n", strerror(errno)); 1322d74d50a8SDag-Erling Smørgrav _exit(1); 13231e8db6e2SBrian Feldman } 13241e8db6e2SBrian Feldman close(*in); 13251e8db6e2SBrian Feldman close(*out); 13261e8db6e2SBrian Feldman close(c_in); 13271e8db6e2SBrian Feldman close(c_out); 1328d74d50a8SDag-Erling Smørgrav 1329d74d50a8SDag-Erling Smørgrav /* 1330d74d50a8SDag-Erling Smørgrav * The underlying ssh is in the same process group, so we must 1331d74d50a8SDag-Erling Smørgrav * ignore SIGINT if we want to gracefully abort commands, 1332d74d50a8SDag-Erling Smørgrav * otherwise the signal will make it to the ssh process and 1333d74d50a8SDag-Erling Smørgrav * kill it too 1334d74d50a8SDag-Erling Smørgrav */ 1335d74d50a8SDag-Erling Smørgrav signal(SIGINT, SIG_IGN); 1336d74d50a8SDag-Erling Smørgrav execvp(path, args); 1337ae1f160dSDag-Erling Smørgrav fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 1338d74d50a8SDag-Erling Smørgrav _exit(1); 13391e8db6e2SBrian Feldman } 13401e8db6e2SBrian Feldman 1341d95e11bfSDag-Erling Smørgrav signal(SIGTERM, killchild); 1342d95e11bfSDag-Erling Smørgrav signal(SIGINT, killchild); 1343d95e11bfSDag-Erling Smørgrav signal(SIGHUP, killchild); 13441e8db6e2SBrian Feldman close(c_in); 13451e8db6e2SBrian Feldman close(c_out); 13461e8db6e2SBrian Feldman } 13471e8db6e2SBrian Feldman 1348ae1f160dSDag-Erling Smørgrav static void 13491e8db6e2SBrian Feldman usage(void) 13501e8db6e2SBrian Feldman { 1351ae1f160dSDag-Erling Smørgrav extern char *__progname; 1352ae1f160dSDag-Erling Smørgrav 1353ae1f160dSDag-Erling Smørgrav fprintf(stderr, 1354efcad6b7SDag-Erling Smørgrav "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" 1355efcad6b7SDag-Erling Smørgrav " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" 1356efcad6b7SDag-Erling Smørgrav " [-S program] [-s subsystem | sftp_server] host\n" 1357efcad6b7SDag-Erling Smørgrav " %s [[user@]host[:file [file]]]\n" 1358efcad6b7SDag-Erling Smørgrav " %s [[user@]host[:dir[/]]]\n" 1359efcad6b7SDag-Erling Smørgrav " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); 13601e8db6e2SBrian Feldman exit(1); 13611e8db6e2SBrian Feldman } 13621e8db6e2SBrian Feldman 13631e8db6e2SBrian Feldman int 13641e8db6e2SBrian Feldman main(int argc, char **argv) 13651e8db6e2SBrian Feldman { 1366d0c8c0bcSDag-Erling Smørgrav int in, out, ch, err; 1367d74d50a8SDag-Erling Smørgrav char *host, *userhost, *cp, *file2 = NULL; 1368ae1f160dSDag-Erling Smørgrav int debug_level = 0, sshver = 2; 1369ae1f160dSDag-Erling Smørgrav char *file1 = NULL, *sftp_server = NULL; 1370ae1f160dSDag-Erling Smørgrav char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 1371ae1f160dSDag-Erling Smørgrav LogLevel ll = SYSLOG_LEVEL_INFO; 1372ae1f160dSDag-Erling Smørgrav arglist args; 13731e8db6e2SBrian Feldman extern int optind; 13741e8db6e2SBrian Feldman extern char *optarg; 13751e8db6e2SBrian Feldman 1376d95e11bfSDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 1377ae1f160dSDag-Erling Smørgrav args.list = NULL; 1378ae1f160dSDag-Erling Smørgrav addargs(&args, "ssh"); /* overwritten with ssh_program */ 1379ae1f160dSDag-Erling Smørgrav addargs(&args, "-oForwardX11 no"); 1380ae1f160dSDag-Erling Smørgrav addargs(&args, "-oForwardAgent no"); 1381ae1f160dSDag-Erling Smørgrav addargs(&args, "-oClearAllForwardings yes"); 1382efcad6b7SDag-Erling Smørgrav 1383ae1f160dSDag-Erling Smørgrav ll = SYSLOG_LEVEL_INFO; 1384efcad6b7SDag-Erling Smørgrav infile = stdin; 13851e8db6e2SBrian Feldman 1386ae1f160dSDag-Erling Smørgrav while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { 13871e8db6e2SBrian Feldman switch (ch) { 13881e8db6e2SBrian Feldman case 'C': 1389ae1f160dSDag-Erling Smørgrav addargs(&args, "-C"); 13901e8db6e2SBrian Feldman break; 13911e8db6e2SBrian Feldman case 'v': 1392ae1f160dSDag-Erling Smørgrav if (debug_level < 3) { 1393ae1f160dSDag-Erling Smørgrav addargs(&args, "-v"); 1394ae1f160dSDag-Erling Smørgrav ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 1395ae1f160dSDag-Erling Smørgrav } 1396ae1f160dSDag-Erling Smørgrav debug_level++; 13971e8db6e2SBrian Feldman break; 1398ae1f160dSDag-Erling Smørgrav case 'F': 13991e8db6e2SBrian Feldman case 'o': 1400ae1f160dSDag-Erling Smørgrav addargs(&args, "-%c%s", ch, optarg); 14011e8db6e2SBrian Feldman break; 14021e8db6e2SBrian Feldman case '1': 1403ae1f160dSDag-Erling Smørgrav sshver = 1; 14041e8db6e2SBrian Feldman if (sftp_server == NULL) 14051e8db6e2SBrian Feldman sftp_server = _PATH_SFTP_SERVER; 14061e8db6e2SBrian Feldman break; 14071e8db6e2SBrian Feldman case 's': 14081e8db6e2SBrian Feldman sftp_server = optarg; 14091e8db6e2SBrian Feldman break; 14101e8db6e2SBrian Feldman case 'S': 14111e8db6e2SBrian Feldman ssh_program = optarg; 14121e8db6e2SBrian Feldman break; 14131e8db6e2SBrian Feldman case 'b': 1414efcad6b7SDag-Erling Smørgrav if (batchmode) 1415efcad6b7SDag-Erling Smørgrav fatal("Batch file already specified."); 1416efcad6b7SDag-Erling Smørgrav 1417efcad6b7SDag-Erling Smørgrav /* Allow "-" as stdin */ 1418efcad6b7SDag-Erling Smørgrav if (strcmp(optarg, "-") != 0 && 1419efcad6b7SDag-Erling Smørgrav (infile = fopen(optarg, "r")) == NULL) 14201e8db6e2SBrian Feldman fatal("%s (%s).", strerror(errno), optarg); 1421d0c8c0bcSDag-Erling Smørgrav showprogress = 0; 1422efcad6b7SDag-Erling Smørgrav batchmode = 1; 14231e8db6e2SBrian Feldman break; 1424ae1f160dSDag-Erling Smørgrav case 'P': 1425ae1f160dSDag-Erling Smørgrav sftp_direct = optarg; 1426ae1f160dSDag-Erling Smørgrav break; 1427ae1f160dSDag-Erling Smørgrav case 'B': 1428ae1f160dSDag-Erling Smørgrav copy_buffer_len = strtol(optarg, &cp, 10); 1429ae1f160dSDag-Erling Smørgrav if (copy_buffer_len == 0 || *cp != '\0') 1430ae1f160dSDag-Erling Smørgrav fatal("Invalid buffer size \"%s\"", optarg); 1431ae1f160dSDag-Erling Smørgrav break; 1432ae1f160dSDag-Erling Smørgrav case 'R': 1433ae1f160dSDag-Erling Smørgrav num_requests = strtol(optarg, &cp, 10); 1434ae1f160dSDag-Erling Smørgrav if (num_requests == 0 || *cp != '\0') 1435ae1f160dSDag-Erling Smørgrav fatal("Invalid number of requests \"%s\"", 1436ae1f160dSDag-Erling Smørgrav optarg); 1437ae1f160dSDag-Erling Smørgrav break; 14381e8db6e2SBrian Feldman case 'h': 14391e8db6e2SBrian Feldman default: 14401e8db6e2SBrian Feldman usage(); 14411e8db6e2SBrian Feldman } 14421e8db6e2SBrian Feldman } 14431e8db6e2SBrian Feldman 144452028650SDag-Erling Smørgrav if (!isatty(STDERR_FILENO)) 144552028650SDag-Erling Smørgrav showprogress = 0; 144652028650SDag-Erling Smørgrav 1447545d5ecaSDag-Erling Smørgrav log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 1448545d5ecaSDag-Erling Smørgrav 1449ae1f160dSDag-Erling Smørgrav if (sftp_direct == NULL) { 14501e8db6e2SBrian Feldman if (optind == argc || argc > (optind + 2)) 14511e8db6e2SBrian Feldman usage(); 14521e8db6e2SBrian Feldman 14531e8db6e2SBrian Feldman userhost = xstrdup(argv[optind]); 14541e8db6e2SBrian Feldman file2 = argv[optind+1]; 14551e8db6e2SBrian Feldman 1456d0c8c0bcSDag-Erling Smørgrav if ((host = strrchr(userhost, '@')) == NULL) 14571e8db6e2SBrian Feldman host = userhost; 14581e8db6e2SBrian Feldman else { 14591e8db6e2SBrian Feldman *host++ = '\0'; 14601e8db6e2SBrian Feldman if (!userhost[0]) { 14611e8db6e2SBrian Feldman fprintf(stderr, "Missing username\n"); 14621e8db6e2SBrian Feldman usage(); 14631e8db6e2SBrian Feldman } 1464ae1f160dSDag-Erling Smørgrav addargs(&args, "-l%s",userhost); 14651e8db6e2SBrian Feldman } 14661e8db6e2SBrian Feldman 1467efcad6b7SDag-Erling Smørgrav if ((cp = colon(host)) != NULL) { 1468efcad6b7SDag-Erling Smørgrav *cp++ = '\0'; 1469efcad6b7SDag-Erling Smørgrav file1 = cp; 1470efcad6b7SDag-Erling Smørgrav } 1471efcad6b7SDag-Erling Smørgrav 14721e8db6e2SBrian Feldman host = cleanhostname(host); 14731e8db6e2SBrian Feldman if (!*host) { 14741e8db6e2SBrian Feldman fprintf(stderr, "Missing hostname\n"); 14751e8db6e2SBrian Feldman usage(); 14761e8db6e2SBrian Feldman } 14771e8db6e2SBrian Feldman 1478ae1f160dSDag-Erling Smørgrav addargs(&args, "-oProtocol %d", sshver); 14791e8db6e2SBrian Feldman 1480ae1f160dSDag-Erling Smørgrav /* no subsystem if the server-spec contains a '/' */ 1481ae1f160dSDag-Erling Smørgrav if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 1482ae1f160dSDag-Erling Smørgrav addargs(&args, "-s"); 1483ae1f160dSDag-Erling Smørgrav 1484ae1f160dSDag-Erling Smørgrav addargs(&args, "%s", host); 1485ae1f160dSDag-Erling Smørgrav addargs(&args, "%s", (sftp_server != NULL ? 1486ae1f160dSDag-Erling Smørgrav sftp_server : "sftp")); 1487ae1f160dSDag-Erling Smørgrav args.list[0] = ssh_program; 14881e8db6e2SBrian Feldman 1489efcad6b7SDag-Erling Smørgrav if (!batchmode) 14901e8db6e2SBrian Feldman fprintf(stderr, "Connecting to %s...\n", host); 1491d95e11bfSDag-Erling Smørgrav connect_to_server(ssh_program, args.list, &in, &out); 1492ae1f160dSDag-Erling Smørgrav } else { 1493ae1f160dSDag-Erling Smørgrav args.list = NULL; 1494ae1f160dSDag-Erling Smørgrav addargs(&args, "sftp-server"); 14951e8db6e2SBrian Feldman 1496efcad6b7SDag-Erling Smørgrav if (!batchmode) 1497ae1f160dSDag-Erling Smørgrav fprintf(stderr, "Attaching to %s...\n", sftp_direct); 1498d95e11bfSDag-Erling Smørgrav connect_to_server(sftp_direct, args.list, &in, &out); 1499ae1f160dSDag-Erling Smørgrav } 15001e8db6e2SBrian Feldman 1501d0c8c0bcSDag-Erling Smørgrav err = interactive_loop(in, out, file1, file2); 15021e8db6e2SBrian Feldman 150383d2307dSDag-Erling Smørgrav #if !defined(USE_PIPES) 150483d2307dSDag-Erling Smørgrav shutdown(in, SHUT_RDWR); 150583d2307dSDag-Erling Smørgrav shutdown(out, SHUT_RDWR); 150683d2307dSDag-Erling Smørgrav #endif 150783d2307dSDag-Erling Smørgrav 15081e8db6e2SBrian Feldman close(in); 15091e8db6e2SBrian Feldman close(out); 1510efcad6b7SDag-Erling Smørgrav if (batchmode) 15111e8db6e2SBrian Feldman fclose(infile); 15121e8db6e2SBrian Feldman 1513545d5ecaSDag-Erling Smørgrav while (waitpid(sshpid, NULL, 0) == -1) 1514545d5ecaSDag-Erling Smørgrav if (errno != EINTR) 1515545d5ecaSDag-Erling Smørgrav fatal("Couldn't wait for ssh process: %s", 1516545d5ecaSDag-Erling Smørgrav strerror(errno)); 15171e8db6e2SBrian Feldman 1518d0c8c0bcSDag-Erling Smørgrav exit(err == 0 ? 0 : 1); 15191e8db6e2SBrian Feldman } 1520