sftp.c revision 128456
14Srgrimes/* 24Srgrimes * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3295Sjtc * 44Srgrimes * Permission to use, copy, modify, and distribute this software for any 5295Sjtc * purpose with or without fee is hereby granted, provided that the above 6295Sjtc * copyright notice and this permission notice appear in all copies. 750471Speter * 84Srgrimes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9468Sjtc * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062926Sse * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1192979Swollman * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1292979Swollman * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1392979Swollman * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1492979Swollman * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1592979Swollman */ 1692979Swollman 1792979Swollman#include "includes.h" 184Srgrimes 194SrgrimesRCSID("$OpenBSD: sftp.c,v 1.45 2004/03/03 09:31:20 djm Exp $"); 204Srgrimes 2162926Sse#include "buffer.h" 2292979Swollman#include "xmalloc.h" 2331Salm#include "log.h" 2492979Swollman#include "pathnames.h" 2592979Swollman#include "misc.h" 2692979Swollman 2792979Swollman#include "sftp.h" 2892979Swollman#include "sftp-common.h" 2992979Swollman#include "sftp-client.h" 304Srgrimes 3112378Sjoerg/* File to read commands from */ 324SrgrimesFILE* infile; 3331Salm 344Srgrimes/* Are we in batchfile mode? */ 354Srgrimesint batchmode = 0; 364Srgrimes 374Srgrimes/* Size of buffer used when copying files */ 3892979Swollmansize_t copy_buffer_len = 32768; 394Srgrimes 4031Salm/* Number of concurrent outstanding requests */ 414Srgrimessize_t num_requests = 16; 424Srgrimes 434Srgrimes/* PID of ssh transport process */ 4492979Swollmanstatic pid_t sshpid = -1; 4592979Swollman 4692979Swollman/* This is set to 0 if the progressmeter is not desired. */ 4792979Swollmanint showprogress = 1; 4890109Simp 4990109Simpint remote_glob(struct sftp_conn *, const char *, int, 5090109Simp int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 5192979Swollman 5290109Simp#ifdef HAVE___PROGNAME 5390109Simpextern char *__progname; 5490109Simp#else 5590109Simpchar *__progname; 5690109Simp#endif 5790109Simp 5890109Simp/* Separators for interactive commands */ 5990109Simp#define WHITESPACE " \t\r\n" 6090109Simp 6190109Simp/* Define what type of ls view (0 - multi-column) */ 6290109Simp#define LONG_VIEW 1 /* Full view ala ls -l */ 6390109Simp#define SHORT_VIEW 2 /* Single row view ala ls -1 */ 6490109Simp 6590109Simp/* Commands for interactive mode */ 6690109Simp#define I_CHDIR 1 6792979Swollman#define I_CHGRP 2 6890109Simp#define I_CHMOD 3 6990109Simp#define I_CHOWN 4 7090109Simp#define I_GET 5 7190109Simp#define I_HELP 6 7277244Skris#define I_LCHDIR 7 734Srgrimes#define I_LLS 8 744Srgrimes#define I_LMKDIR 9 754Srgrimes#define I_LPWD 10 764Srgrimes#define I_LS 11 774Srgrimes#define I_LUMASK 12 784Srgrimes#define I_MKDIR 13 794Srgrimes#define I_PUT 14 804Srgrimes#define I_PWD 15 814Srgrimes#define I_QUIT 16 824Srgrimes#define I_RENAME 17 834Srgrimes#define I_RM 18 844Srgrimes#define I_RMDIR 19 854Srgrimes#define I_SHELL 20 864Srgrimes#define I_SYMLINK 21 874Srgrimes#define I_VERSION 22 884Srgrimes#define I_PROGRESS 23 894Srgrimes 904Srgrimesstruct CMD { 914Srgrimes const char *c; 924Srgrimes const int n; 934Srgrimes}; 944Srgrimes 954Srgrimesstatic const struct CMD cmds[] = { 964Srgrimes { "bye", I_QUIT }, 974Srgrimes { "cd", I_CHDIR }, 984Srgrimes { "chdir", I_CHDIR }, 994Srgrimes { "chgrp", I_CHGRP }, 1004Srgrimes { "chmod", I_CHMOD }, 1014Srgrimes { "chown", I_CHOWN }, 1024Srgrimes { "dir", I_LS }, 1034Srgrimes { "exit", I_QUIT }, 1044Srgrimes { "get", I_GET }, 1054Srgrimes { "mget", I_GET }, 1064Srgrimes { "help", I_HELP }, 1074Srgrimes { "lcd", I_LCHDIR }, 1084Srgrimes { "lchdir", I_LCHDIR }, 1094Srgrimes { "lls", I_LLS }, 1104Srgrimes { "lmkdir", I_LMKDIR }, 1114Srgrimes { "ln", I_SYMLINK }, 1124Srgrimes { "lpwd", I_LPWD }, 1134Srgrimes { "ls", I_LS }, 1144Srgrimes { "lumask", I_LUMASK }, 1154Srgrimes { "mkdir", I_MKDIR }, 1164Srgrimes { "progress", I_PROGRESS }, 11792979Swollman { "put", I_PUT }, 1184Srgrimes { "mput", I_PUT }, 1194Srgrimes { "pwd", I_PWD }, 1204Srgrimes { "quit", I_QUIT }, 1214Srgrimes { "rename", I_RENAME }, 1224Srgrimes { "rm", I_RM }, 12392979Swollman { "rmdir", I_RMDIR }, 1244Srgrimes { "symlink", I_SYMLINK }, 1254Srgrimes { "version", I_VERSION }, 1264Srgrimes { "!", I_SHELL }, 1274Srgrimes { "?", I_HELP }, 1284Srgrimes { NULL, -1} 1294Srgrimes}; 1304Srgrimes 1314Srgrimesint interactive_loop(int fd_in, int fd_out, char *file1, char *file2); 13290109Simp 1334Srgrimesstatic void 1344Srgrimeshelp(void) 13592979Swollman{ 1364Srgrimes printf("Available commands:\n"); 1374Srgrimes printf("cd path Change remote directory to 'path'\n"); 1384Srgrimes printf("lcd path Change local directory to 'path'\n"); 13992979Swollman printf("chgrp grp path Change group of file 'path' to 'grp'\n"); 1404Srgrimes printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); 1414Srgrimes printf("chown own path Change owner of file 'path' to 'own'\n"); 14292979Swollman printf("help Display this help text\n"); 14392979Swollman printf("get remote-path [local-path] Download file\n"); 14492979Swollman printf("lls [ls-options [path]] Display local directory listing\n"); 14592979Swollman printf("ln oldpath newpath Symlink remote file\n"); 14692979Swollman printf("lmkdir path Create local directory\n"); 14792979Swollman printf("lpwd Print local working directory\n"); 14892979Swollman printf("ls [path] Display remote directory listing\n"); 14992979Swollman printf("lumask umask Set local umask to 'umask'\n"); 15092979Swollman printf("mkdir path Create remote directory\n"); 15192979Swollman printf("progress Toggle display of progress meter\n"); 15292979Swollman printf("put local-path [remote-path] Upload file\n"); 15392979Swollman printf("pwd Display remote working directory\n"); 15492979Swollman printf("exit Quit sftp\n"); 15592979Swollman printf("quit Quit sftp\n"); 15692979Swollman printf("rename oldpath newpath Rename remote file\n"); 15792979Swollman printf("rmdir path Remove remote directory\n"); 1589909Sjoerg printf("rm path Delete remote file\n"); 15992979Swollman printf("symlink oldpath newpath Symlink remote file\n"); 16092979Swollman printf("version Show SFTP version\n"); 16192979Swollman printf("!command Execute 'command' in local shell\n"); 16212378Sjoerg printf("! Escape to local shell\n"); 1639909Sjoerg printf("? Synonym for help\n"); 1644Srgrimes} 1654Srgrimes 1664Srgrimesstatic void 1674Srgrimeslocal_do_shell(const char *args) 1684Srgrimes{ 16990109Simp int status; 1704Srgrimes char *shell; 17112378Sjoerg pid_t pid; 1724Srgrimes 1734Srgrimes if (!*args) 1744Srgrimes args = NULL; 1754Srgrimes 17692979Swollman if ((shell = getenv("SHELL")) == NULL) 17790109Simp shell = _PATH_BSHELL; 1784Srgrimes 17992979Swollman if ((pid = fork()) == -1) 1804Srgrimes fatal("Couldn't fork: %s", strerror(errno)); 1814Srgrimes 1824Srgrimes if (pid == 0) { 1834Srgrimes /* XXX: child has pipe fds to ssh subproc open - issue? */ 18412378Sjoerg if (args) { 18512378Sjoerg debug3("Executing %s -c \"%s\"", shell, args); 1864Srgrimes execl(shell, shell, "-c", args, (char *)NULL); 18712378Sjoerg } else { 18863755Sse debug3("Executing %s", shell); 18992979Swollman execl(shell, shell, (char *)NULL); 19092979Swollman } 19192979Swollman fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, 19292979Swollman strerror(errno)); 1934Srgrimes _exit(1); 19412378Sjoerg } 1954Srgrimes while (waitpid(pid, &status, 0) == -1) 1964Srgrimes if (errno != EINTR) 1974Srgrimes fatal("Couldn't wait for child: %s", strerror(errno)); 1984Srgrimes if (!WIFEXITED(status)) 1994Srgrimes error("Shell exited abormally"); 20090109Simp else if (WEXITSTATUS(status)) 2014Srgrimes error("Shell exited with status %d", WEXITSTATUS(status)); 2024Srgrimes} 2034Srgrimes 20412378Sjoergstatic void 2054Srgrimeslocal_do_ls(const char *args) 2064Srgrimes{ 20792979Swollman if (!args || !*args) 20892979Swollman local_do_shell(_PATH_LS); 20992979Swollman else { 21092979Swollman int len = strlen(_PATH_LS " ") + strlen(args) + 1; 21192979Swollman char *buf = xmalloc(len); 21292979Swollman 21392979Swollman /* XXX: quoting - rip quoting code from ftp? */ 21492979Swollman snprintf(buf, len, _PATH_LS " %s", args); 21592979Swollman local_do_shell(buf); 2164Srgrimes xfree(buf); 21792979Swollman } 2184Srgrimes} 2194Srgrimes 2204Srgrimes/* Strip one path (usually the pwd) from the start of another */ 2214Srgrimesstatic char * 2224Srgrimespath_strip(char *path, char *strip) 2234Srgrimes{ 22490109Simp size_t len; 2254Srgrimes 22612378Sjoerg if (strip == NULL) 2274Srgrimes return (xstrdup(path)); 2284Srgrimes 2294Srgrimes len = strlen(strip); 2304Srgrimes if (strip != NULL && strncmp(path, strip, len) == 0) { 2314Srgrimes if (strip[len - 1] != '/' && path[len] == '/') 23290109Simp len++; 2334Srgrimes return (xstrdup(path + len)); 2344Srgrimes } 2354Srgrimes 2364Srgrimes return (xstrdup(path)); 2374Srgrimes} 2384Srgrimes 2394Srgrimesstatic char * 2404Srgrimespath_append(char *p1, char *p2) 2414Srgrimes{ 2424Srgrimes char *ret; 2434Srgrimes int len = strlen(p1) + strlen(p2) + 2; 2444Srgrimes 2454Srgrimes ret = xmalloc(len); 2464Srgrimes strlcpy(ret, p1, len); 2474Srgrimes if (p1[strlen(p1) - 1] != '/') 2484Srgrimes strlcat(ret, "/", len); 2494Srgrimes strlcat(ret, p2, len); 2504Srgrimes 2514Srgrimes return(ret); 2524Srgrimes} 2534Srgrimes 2544Srgrimesstatic char * 2554Srgrimesmake_absolute(char *p, char *pwd) 2564Srgrimes{ 25790109Simp char *abs; 2584Srgrimes 2594Srgrimes /* Derelativise */ 2604Srgrimes if (p && p[0] != '/') { 2614Srgrimes abs = path_append(pwd, p); 262181Sconklin xfree(p); 2634Srgrimes return(abs); 2644Srgrimes } else 2654Srgrimes return(p); 2664Srgrimes} 26712378Sjoerg 26892979Swollmanstatic int 2694Srgrimesinfer_path(const char *p, char **ifp) 27092979Swollman{ 27192979Swollman char *cp; 272468Sjtc 27392979Swollman cp = strrchr(p, '/'); 27492979Swollman if (cp == NULL) { 27592979Swollman *ifp = xstrdup(p); 27692979Swollman return(0); 27792979Swollman } 27892979Swollman 279468Sjtc if (!cp[1]) { 28092979Swollman error("Invalid path"); 2814Srgrimes return(-1); 28292979Swollman } 2834Srgrimes 2844Srgrimes *ifp = xstrdup(cp + 1); 28592979Swollman return(0); 2864Srgrimes} 28792979Swollman 2884Srgrimesstatic int 28992979Swollmanparse_getput_flags(const char **cpp, int *pflag) 2904Srgrimes{ 2914Srgrimes const char *cp = *cpp; 2924Srgrimes 29390109Simp /* Check for flags */ 2944Srgrimes if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { 29592979Swollman switch (cp[1]) { 2964Srgrimes case 'p': 2974Srgrimes case 'P': 2984Srgrimes *pflag = 1; 2994Srgrimes break; 30090109Simp default: 3014Srgrimes error("Invalid flag -%c", cp[1]); 3024Srgrimes return(-1); 3034Srgrimes } 3044Srgrimes cp += 2; 3054Srgrimes *cpp = cp + strspn(cp, WHITESPACE); 3064Srgrimes } 3074Srgrimes 3084Srgrimes return(0); 3094Srgrimes} 3104Srgrimes 3114Srgrimesstatic int 31290109Simpparse_ls_flags(const char **cpp, int *lflag) 3134Srgrimes{ 3144Srgrimes const char *cp = *cpp; 3154Srgrimes 3164Srgrimes /* Check for flags */ 31792979Swollman if (cp++[0] == '-') { 3184Srgrimes for(; strchr(WHITESPACE, *cp) == NULL; cp++) { 3194Srgrimes switch (*cp) { 3204Srgrimes case 'l': 3214Srgrimes *lflag = LONG_VIEW; 3224Srgrimes break; 3234Srgrimes case '1': 3244Srgrimes *lflag = SHORT_VIEW; 32590109Simp break; 3264Srgrimes default: 3274Srgrimes error("Invalid flag -%c", *cp); 3284Srgrimes return(-1); 3294Srgrimes } 3304Srgrimes } 3314Srgrimes *cpp = cp + strspn(cp, WHITESPACE); 33292979Swollman } 3334Srgrimes 33412378Sjoerg return(0); 33512378Sjoerg} 33692979Swollman 3374Srgrimesstatic int 3384Srgrimesget_pathname(const char **cpp, char **path) 3394Srgrimes{ 3404Srgrimes const char *cp = *cpp, *end; 3414Srgrimes char quot; 3424Srgrimes int i, j; 3434Srgrimes 3444Srgrimes cp += strspn(cp, WHITESPACE); 34590109Simp if (!*cp) { 3464Srgrimes *cpp = cp; 3474Srgrimes *path = NULL; 3484Srgrimes return (0); 3494Srgrimes } 3504Srgrimes 3514Srgrimes *path = xmalloc(strlen(cp) + 1); 35292979Swollman 3534Srgrimes /* Check for quoted filenames */ 35412378Sjoerg if (*cp == '\"' || *cp == '\'') { 35512378Sjoerg quot = *cp++; 35692979Swollman 3574Srgrimes /* Search for terminating quote, unescape some chars */ 3584Srgrimes for (i = j = 0; i <= strlen(cp); i++) { 3594Srgrimes if (cp[i] == quot) { /* Found quote */ 3604Srgrimes i++; 3614Srgrimes (*path)[j] = '\0'; 3624Srgrimes break; 3634Srgrimes } 3644Srgrimes if (cp[i] == '\0') { /* End of string */ 36590109Simp error("Unterminated quote"); 3664Srgrimes goto fail; 3674Srgrimes } 3684Srgrimes if (cp[i] == '\\') { /* Escaped characters */ 3694Srgrimes i++; 3704Srgrimes if (cp[i] != '\'' && cp[i] != '\"' && 3714Srgrimes cp[i] != '\\') { 37292979Swollman error("Bad escaped character '\%c'", 3734Srgrimes cp[i]); 37412378Sjoerg goto fail; 37512378Sjoerg } 37692979Swollman } 3774Srgrimes (*path)[j++] = cp[i]; 3784Srgrimes } 3794Srgrimes 3804Srgrimes if (j == 0) { 3814Srgrimes error("Empty quotes"); 3824Srgrimes goto fail; 3834Srgrimes } 3844Srgrimes *cpp = cp + i + strspn(cp + i, WHITESPACE); 38590109Simp } else { 3864Srgrimes /* Read to end of filename */ 3874Srgrimes end = strpbrk(cp, WHITESPACE); 3884Srgrimes if (end == NULL) 3894Srgrimes end = strchr(cp, '\0'); 3904Srgrimes *cpp = end + strspn(end, WHITESPACE); 3914Srgrimes 39292979Swollman memcpy(*path, cp, end - cp); 3934Srgrimes (*path)[end - cp] = '\0'; 39412378Sjoerg } 39512378Sjoerg return (0); 39692979Swollman 3974Srgrimes fail: 3984Srgrimes xfree(*path); 3994Srgrimes *path = NULL; 4004Srgrimes return (-1); 4014Srgrimes} 4024Srgrimes 4034Srgrimesstatic int 4044Srgrimesis_dir(char *path) 40590109Simp{ 4064Srgrimes struct stat sb; 4074Srgrimes 4084Srgrimes /* XXX: report errors? */ 4094Srgrimes if (stat(path, &sb) == -1) 4104Srgrimes return(0); 4114Srgrimes 41292979Swollman return(sb.st_mode & S_IFDIR); 4134Srgrimes} 41412378Sjoerg 41512378Sjoergstatic int 41692979Swollmanis_reg(char *path) 4174Srgrimes{ 4184Srgrimes struct stat sb; 4194Srgrimes 4204Srgrimes if (stat(path, &sb) == -1) 4214Srgrimes fatal("stat %s: %s", path, strerror(errno)); 4224Srgrimes 4234Srgrimes return(S_ISREG(sb.st_mode)); 4244Srgrimes} 42590109Simp 4264Srgrimesstatic int 4274Srgrimesremote_is_dir(struct sftp_conn *conn, char *path) 4284Srgrimes{ 4294Srgrimes Attrib *a; 4304Srgrimes 4314Srgrimes /* XXX: report errors? */ 43292979Swollman if ((a = do_stat(conn, path, 1)) == NULL) 4334Srgrimes return(0); 43412378Sjoerg if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) 43512378Sjoerg return(0); 43692979Swollman return(a->perm & S_IFDIR); 4374Srgrimes} 4384Srgrimes 4394Srgrimesstatic int 4404Srgrimesprocess_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 4414Srgrimes{ 4424Srgrimes char *abs_src = NULL; 4434Srgrimes char *abs_dst = NULL; 44463755Sse char *tmp; 44592979Swollman glob_t g; 44663755Sse int err = 0; 44763755Sse int i; 44863755Sse 44963755Sse abs_src = xstrdup(src); 45063755Sse abs_src = make_absolute(abs_src, pwd); 45163755Sse 45263755Sse memset(&g, 0, sizeof(g)); 45363755Sse debug3("Looking up %s", abs_src); 45463755Sse if (remote_glob(conn, abs_src, 0, NULL, &g)) { 45563755Sse error("File \"%s\" not found.", abs_src); 45663755Sse err = -1; 4574Srgrimes goto out; 45890109Simp } 4594Srgrimes 4604Srgrimes /* If multiple matches, dst must be a directory or unspecified */ 4614Srgrimes if (g.gl_matchc > 1 && dst && !is_dir(dst)) { 4624Srgrimes error("Multiple files match, but \"%s\" is not a directory", 46392979Swollman dst); 4644Srgrimes err = -1; 4654Srgrimes goto out; 46692979Swollman } 46763755Sse 46892979Swollman for (i = 0; g.gl_pathv[i]; i++) { 46963755Sse if (infer_path(g.gl_pathv[i], &tmp)) { 4704Srgrimes err = -1; 4714Srgrimes goto out; 4724Srgrimes } 4734Srgrimes 47463755Sse if (g.gl_matchc == 1 && dst) { 47563755Sse /* If directory specified, append filename */ 47692979Swollman if (is_dir(dst)) { 47763755Sse if (infer_path(g.gl_pathv[0], &tmp)) { 47892979Swollman err = 1; 47992979Swollman goto out; 48063755Sse } 48163755Sse abs_dst = path_append(dst, tmp); 48263755Sse xfree(tmp); 48363755Sse } else 48463755Sse abs_dst = xstrdup(dst); 48592979Swollman } else if (dst) { 48663755Sse abs_dst = path_append(dst, tmp); 48763755Sse xfree(tmp); 48863755Sse } else 4894Srgrimes abs_dst = tmp; 49090109Simp 4914Srgrimes printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); 4924Srgrimes if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 4934Srgrimes err = -1; 4944Srgrimes xfree(abs_dst); 49592979Swollman abs_dst = NULL; 4964Srgrimes } 4974Srgrimes 49892979Swollmanout: 49963755Sse xfree(abs_src); 50092979Swollman if (abs_dst) 50163755Sse xfree(abs_dst); 5024Srgrimes globfree(&g); 5034Srgrimes return(err); 5044Srgrimes} 5054Srgrimes 50663755Ssestatic int 50763755Sseprocess_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) 50892979Swollman{ 50963755Sse char *tmp_dst = NULL; 51063755Sse char *abs_dst = NULL; 51163755Sse char *tmp; 51263755Sse glob_t g; 51363755Sse int err = 0; 51463755Sse int i; 51563755Sse 51663755Sse if (dst) { 51763755Sse tmp_dst = xstrdup(dst); 51863755Sse tmp_dst = make_absolute(tmp_dst, pwd); 5194Srgrimes } 52090109Simp 5214Srgrimes memset(&g, 0, sizeof(g)); 5224Srgrimes debug3("Looking up %s", src); 5234Srgrimes if (glob(src, 0, NULL, &g)) { 5244Srgrimes error("File \"%s\" not found.", src); 52592979Swollman err = -1; 5264Srgrimes goto out; 5274Srgrimes } 52892979Swollman 52963755Sse /* If multiple matches, dst may be directory or unspecified */ 53092979Swollman if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { 53163755Sse error("Multiple files match, but \"%s\" is not a directory", 5324Srgrimes tmp_dst); 5334Srgrimes err = -1; 5344Srgrimes goto out; 5354Srgrimes } 53663755Sse 53763755Sse for (i = 0; g.gl_pathv[i]; i++) { 53892979Swollman if (!is_reg(g.gl_pathv[i])) { 53963755Sse error("skipping non-regular file %s", 54063755Sse g.gl_pathv[i]); 54192979Swollman continue; 54292979Swollman } 54363755Sse if (infer_path(g.gl_pathv[i], &tmp)) { 54463755Sse err = -1; 54563755Sse goto out; 54663755Sse } 54763755Sse 5484Srgrimes if (g.gl_matchc == 1 && tmp_dst) { 54990109Simp /* If directory specified, append filename */ 5504Srgrimes if (remote_is_dir(conn, tmp_dst)) { 5514Srgrimes if (infer_path(g.gl_pathv[0], &tmp)) { 5524Srgrimes err = 1; 5534Srgrimes goto out; 55492979Swollman } 5554Srgrimes abs_dst = path_append(tmp_dst, tmp); 5564Srgrimes xfree(tmp); 5574Srgrimes } else 55892979Swollman abs_dst = xstrdup(tmp_dst); 5594Srgrimes 5604Srgrimes } else if (tmp_dst) { 56192979Swollman abs_dst = path_append(tmp_dst, tmp); 56277244Skris xfree(tmp); 56392979Swollman } else 56463755Sse abs_dst = make_absolute(tmp, pwd); 5654Srgrimes 5664Srgrimes printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); 5674Srgrimes if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) 5684Srgrimes err = -1; 5694Srgrimes } 5704Srgrimes 57190109Simpout: 5724Srgrimes if (abs_dst) 5734Srgrimes xfree(abs_dst); 5744Srgrimes if (tmp_dst) 5754Srgrimes xfree(tmp_dst); 57692979Swollman globfree(&g); 5774Srgrimes return(err); 5784Srgrimes} 5794Srgrimes 58092979Swollmanstatic int 5814Srgrimessdirent_comp(const void *aa, const void *bb) 5824Srgrimes{ 58392979Swollman SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; 58463755Sse SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; 5854Srgrimes 5864Srgrimes return (strcmp(a->filename, b->filename)); 5874Srgrimes} 5884Srgrimes 5894Srgrimes/* sftp ls.1 replacement for directories */ 5904Srgrimesstatic int 59190109Simpdo_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) 5924Srgrimes{ 59331Salm int n, c = 1, colspace = 0, columns = 1; 594468Sjtc SFTP_DIRENT **d; 59531Salm 59631Salm if ((n = do_readdir(conn, path, &d)) != 0) 597181Sconklin return (n); 5984Srgrimes 599181Sconklin if (!(lflag & SHORT_VIEW)) { 600181Sconklin int m = 0, width = 80; 601181Sconklin struct winsize ws; 602181Sconklin char *tmp; 60331Salm 604539Sjtc /* Count entries for sort and find longest filename */ 60531Salm for (n = 0; d[n] != NULL; n++) 60692979Swollman m = MAX(m, strlen(d[n]->filename)); 6074Srgrimes 6084Srgrimes /* Add any subpath that also needs to be counted */ 60931Salm tmp = path_strip(path, strip_path); 610539Sjtc m += strlen(tmp); 61177244Skris xfree(tmp); 61231Salm 613295Sjtc if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 61431Salm width = ws.ws_col; 61531Salm 6164Srgrimes columns = width / (m + 2); 61792979Swollman columns = MAX(columns, 1); 6184Srgrimes colspace = width / columns; 6194Srgrimes colspace = MIN(colspace, width); 620295Sjtc } 62192979Swollman 622295Sjtc qsort(d, n, sizeof(*d), sdirent_comp); 623295Sjtc 624295Sjtc for (n = 0; d[n] != NULL; n++) { 6254Srgrimes char *tmp, *fname; 6264Srgrimes 62731Salm tmp = path_append(path, d[n]->filename); 62831Salm fname = path_strip(tmp, strip_path); 62931Salm xfree(tmp); 63031Salm 63131Salm if (lflag & LONG_VIEW) { 63231Salm char *lname; 6334Srgrimes struct stat sb; 634 635 memset(&sb, 0, sizeof(sb)); 636 attrib_to_stat(&d[n]->a, &sb); 637 lname = ls_file(fname, &sb, 1); 638 printf("%s\n", lname); 639 xfree(lname); 640 } else { 641 printf("%-*s", colspace, fname); 642 if (c >= columns) { 643 printf("\n"); 644 c = 1; 645 } else 646 c++; 647 } 648 649 xfree(fname); 650 } 651 652 if (!(lflag & LONG_VIEW) && (c != 1)) 653 printf("\n"); 654 655 free_sftp_dirents(d); 656 return (0); 657} 658 659/* sftp ls.1 replacement which handles path globs */ 660static int 661do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, 662 int lflag) 663{ 664 glob_t g; 665 int i, c = 1, colspace = 0, columns = 1; 666 Attrib *a; 667 668 memset(&g, 0, sizeof(g)); 669 670 if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, 671 NULL, &g)) { 672 error("Can't ls: \"%s\" not found", path); 673 return (-1); 674 } 675 676 /* 677 * If the glob returns a single match, which is the same as the 678 * input glob, and it is a directory, then just list its contents 679 */ 680 if (g.gl_pathc == 1 && 681 strncmp(path, g.gl_pathv[0], strlen(g.gl_pathv[0]) - 1) == 0) { 682 if ((a = do_lstat(conn, path, 1)) == NULL) { 683 globfree(&g); 684 return (-1); 685 } 686 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 687 S_ISDIR(a->perm)) { 688 globfree(&g); 689 return (do_ls_dir(conn, path, strip_path, lflag)); 690 } 691 } 692 693 if (!(lflag & SHORT_VIEW)) { 694 int m = 0, width = 80; 695 struct winsize ws; 696 697 /* Count entries for sort and find longest filename */ 698 for (i = 0; g.gl_pathv[i]; i++) 699 m = MAX(m, strlen(g.gl_pathv[i])); 700 701 if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) 702 width = ws.ws_col; 703 704 columns = width / (m + 2); 705 columns = MAX(columns, 1); 706 colspace = width / columns; 707 } 708 709 for (i = 0; g.gl_pathv[i]; i++) { 710 char *fname; 711 712 fname = path_strip(g.gl_pathv[i], strip_path); 713 714 if (lflag & LONG_VIEW) { 715 char *lname; 716 struct stat sb; 717 718 /* 719 * XXX: this is slow - 1 roundtrip per path 720 * A solution to this is to fork glob() and 721 * build a sftp specific version which keeps the 722 * attribs (which currently get thrown away) 723 * that the server returns as well as the filenames. 724 */ 725 memset(&sb, 0, sizeof(sb)); 726 a = do_lstat(conn, g.gl_pathv[i], 1); 727 if (a != NULL) 728 attrib_to_stat(a, &sb); 729 lname = ls_file(fname, &sb, 1); 730 printf("%s\n", lname); 731 xfree(lname); 732 } else { 733 printf("%-*s", colspace, fname); 734 if (c >= columns) { 735 printf("\n"); 736 c = 1; 737 } else 738 c++; 739 } 740 xfree(fname); 741 } 742 743 if (!(lflag & LONG_VIEW) && (c != 1)) 744 printf("\n"); 745 746 if (g.gl_pathc) 747 globfree(&g); 748 749 return (0); 750} 751 752static int 753parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, 754 unsigned long *n_arg, char **path1, char **path2) 755{ 756 const char *cmd, *cp = *cpp; 757 char *cp2; 758 int base = 0; 759 long l; 760 int i, cmdnum; 761 762 /* Skip leading whitespace */ 763 cp = cp + strspn(cp, WHITESPACE); 764 765 /* Ignore blank lines and lines which begin with comment '#' char */ 766 if (*cp == '\0' || *cp == '#') 767 return (0); 768 769 /* Check for leading '-' (disable error processing) */ 770 *iflag = 0; 771 if (*cp == '-') { 772 *iflag = 1; 773 cp++; 774 } 775 776 /* Figure out which command we have */ 777 for (i = 0; cmds[i].c; i++) { 778 int cmdlen = strlen(cmds[i].c); 779 780 /* Check for command followed by whitespace */ 781 if (!strncasecmp(cp, cmds[i].c, cmdlen) && 782 strchr(WHITESPACE, cp[cmdlen])) { 783 cp += cmdlen; 784 cp = cp + strspn(cp, WHITESPACE); 785 break; 786 } 787 } 788 cmdnum = cmds[i].n; 789 cmd = cmds[i].c; 790 791 /* Special case */ 792 if (*cp == '!') { 793 cp++; 794 cmdnum = I_SHELL; 795 } else if (cmdnum == -1) { 796 error("Invalid command."); 797 return (-1); 798 } 799 800 /* Get arguments and parse flags */ 801 *lflag = *pflag = *n_arg = 0; 802 *path1 = *path2 = NULL; 803 switch (cmdnum) { 804 case I_GET: 805 case I_PUT: 806 if (parse_getput_flags(&cp, pflag)) 807 return(-1); 808 /* Get first pathname (mandatory) */ 809 if (get_pathname(&cp, path1)) 810 return(-1); 811 if (*path1 == NULL) { 812 error("You must specify at least one path after a " 813 "%s command.", cmd); 814 return(-1); 815 } 816 /* Try to get second pathname (optional) */ 817 if (get_pathname(&cp, path2)) 818 return(-1); 819 break; 820 case I_RENAME: 821 case I_SYMLINK: 822 if (get_pathname(&cp, path1)) 823 return(-1); 824 if (get_pathname(&cp, path2)) 825 return(-1); 826 if (!*path1 || !*path2) { 827 error("You must specify two paths after a %s " 828 "command.", cmd); 829 return(-1); 830 } 831 break; 832 case I_RM: 833 case I_MKDIR: 834 case I_RMDIR: 835 case I_CHDIR: 836 case I_LCHDIR: 837 case I_LMKDIR: 838 /* Get pathname (mandatory) */ 839 if (get_pathname(&cp, path1)) 840 return(-1); 841 if (*path1 == NULL) { 842 error("You must specify a path after a %s command.", 843 cmd); 844 return(-1); 845 } 846 break; 847 case I_LS: 848 if (parse_ls_flags(&cp, lflag)) 849 return(-1); 850 /* Path is optional */ 851 if (get_pathname(&cp, path1)) 852 return(-1); 853 break; 854 case I_LLS: 855 case I_SHELL: 856 /* Uses the rest of the line */ 857 break; 858 case I_LUMASK: 859 base = 8; 860 case I_CHMOD: 861 base = 8; 862 case I_CHOWN: 863 case I_CHGRP: 864 /* Get numeric arg (mandatory) */ 865 l = strtol(cp, &cp2, base); 866 if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) && 867 errno == ERANGE) || l < 0) { 868 error("You must supply a numeric argument " 869 "to the %s command.", cmd); 870 return(-1); 871 } 872 cp = cp2; 873 *n_arg = l; 874 if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp)) 875 break; 876 if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) { 877 error("You must supply a numeric argument " 878 "to the %s command.", cmd); 879 return(-1); 880 } 881 cp += strspn(cp, WHITESPACE); 882 883 /* Get pathname (mandatory) */ 884 if (get_pathname(&cp, path1)) 885 return(-1); 886 if (*path1 == NULL) { 887 error("You must specify a path after a %s command.", 888 cmd); 889 return(-1); 890 } 891 break; 892 case I_QUIT: 893 case I_PWD: 894 case I_LPWD: 895 case I_HELP: 896 case I_VERSION: 897 case I_PROGRESS: 898 break; 899 default: 900 fatal("Command not implemented"); 901 } 902 903 *cpp = cp; 904 return(cmdnum); 905} 906 907static int 908parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 909 int err_abort) 910{ 911 char *path1, *path2, *tmp; 912 int pflag, lflag, iflag, cmdnum, i; 913 unsigned long n_arg; 914 Attrib a, *aa; 915 char path_buf[MAXPATHLEN]; 916 int err = 0; 917 glob_t g; 918 919 path1 = path2 = NULL; 920 cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg, 921 &path1, &path2); 922 923 if (iflag != 0) 924 err_abort = 0; 925 926 memset(&g, 0, sizeof(g)); 927 928 /* Perform command */ 929 switch (cmdnum) { 930 case 0: 931 /* Blank line */ 932 break; 933 case -1: 934 /* Unrecognized command */ 935 err = -1; 936 break; 937 case I_GET: 938 err = process_get(conn, path1, path2, *pwd, pflag); 939 break; 940 case I_PUT: 941 err = process_put(conn, path1, path2, *pwd, pflag); 942 break; 943 case I_RENAME: 944 path1 = make_absolute(path1, *pwd); 945 path2 = make_absolute(path2, *pwd); 946 err = do_rename(conn, path1, path2); 947 break; 948 case I_SYMLINK: 949 path2 = make_absolute(path2, *pwd); 950 err = do_symlink(conn, path1, path2); 951 break; 952 case I_RM: 953 path1 = make_absolute(path1, *pwd); 954 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 955 for (i = 0; g.gl_pathv[i]; i++) { 956 printf("Removing %s\n", g.gl_pathv[i]); 957 err = do_rm(conn, g.gl_pathv[i]); 958 if (err != 0 && err_abort) 959 break; 960 } 961 break; 962 case I_MKDIR: 963 path1 = make_absolute(path1, *pwd); 964 attrib_clear(&a); 965 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 966 a.perm = 0777; 967 err = do_mkdir(conn, path1, &a); 968 break; 969 case I_RMDIR: 970 path1 = make_absolute(path1, *pwd); 971 err = do_rmdir(conn, path1); 972 break; 973 case I_CHDIR: 974 path1 = make_absolute(path1, *pwd); 975 if ((tmp = do_realpath(conn, path1)) == NULL) { 976 err = 1; 977 break; 978 } 979 if ((aa = do_stat(conn, tmp, 0)) == NULL) { 980 xfree(tmp); 981 err = 1; 982 break; 983 } 984 if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { 985 error("Can't change directory: Can't check target"); 986 xfree(tmp); 987 err = 1; 988 break; 989 } 990 if (!S_ISDIR(aa->perm)) { 991 error("Can't change directory: \"%s\" is not " 992 "a directory", tmp); 993 xfree(tmp); 994 err = 1; 995 break; 996 } 997 xfree(*pwd); 998 *pwd = tmp; 999 break; 1000 case I_LS: 1001 if (!path1) { 1002 do_globbed_ls(conn, *pwd, *pwd, lflag); 1003 break; 1004 } 1005 1006 /* Strip pwd off beginning of non-absolute paths */ 1007 tmp = NULL; 1008 if (*path1 != '/') 1009 tmp = *pwd; 1010 1011 path1 = make_absolute(path1, *pwd); 1012 err = do_globbed_ls(conn, path1, tmp, lflag); 1013 break; 1014 case I_LCHDIR: 1015 if (chdir(path1) == -1) { 1016 error("Couldn't change local directory to " 1017 "\"%s\": %s", path1, strerror(errno)); 1018 err = 1; 1019 } 1020 break; 1021 case I_LMKDIR: 1022 if (mkdir(path1, 0777) == -1) { 1023 error("Couldn't create local directory " 1024 "\"%s\": %s", path1, strerror(errno)); 1025 err = 1; 1026 } 1027 break; 1028 case I_LLS: 1029 local_do_ls(cmd); 1030 break; 1031 case I_SHELL: 1032 local_do_shell(cmd); 1033 break; 1034 case I_LUMASK: 1035 umask(n_arg); 1036 printf("Local umask: %03lo\n", n_arg); 1037 break; 1038 case I_CHMOD: 1039 path1 = make_absolute(path1, *pwd); 1040 attrib_clear(&a); 1041 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1042 a.perm = n_arg; 1043 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1044 for (i = 0; g.gl_pathv[i]; i++) { 1045 printf("Changing mode on %s\n", g.gl_pathv[i]); 1046 err = do_setstat(conn, g.gl_pathv[i], &a); 1047 if (err != 0 && err_abort) 1048 break; 1049 } 1050 break; 1051 case I_CHOWN: 1052 case I_CHGRP: 1053 path1 = make_absolute(path1, *pwd); 1054 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1055 for (i = 0; g.gl_pathv[i]; i++) { 1056 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1057 if (err != 0 && err_abort) 1058 break; 1059 else 1060 continue; 1061 } 1062 if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { 1063 error("Can't get current ownership of " 1064 "remote file \"%s\"", g.gl_pathv[i]); 1065 if (err != 0 && err_abort) 1066 break; 1067 else 1068 continue; 1069 } 1070 aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; 1071 if (cmdnum == I_CHOWN) { 1072 printf("Changing owner on %s\n", g.gl_pathv[i]); 1073 aa->uid = n_arg; 1074 } else { 1075 printf("Changing group on %s\n", g.gl_pathv[i]); 1076 aa->gid = n_arg; 1077 } 1078 err = do_setstat(conn, g.gl_pathv[i], aa); 1079 if (err != 0 && err_abort) 1080 break; 1081 } 1082 break; 1083 case I_PWD: 1084 printf("Remote working directory: %s\n", *pwd); 1085 break; 1086 case I_LPWD: 1087 if (!getcwd(path_buf, sizeof(path_buf))) { 1088 error("Couldn't get local cwd: %s", strerror(errno)); 1089 err = -1; 1090 break; 1091 } 1092 printf("Local working directory: %s\n", path_buf); 1093 break; 1094 case I_QUIT: 1095 /* Processed below */ 1096 break; 1097 case I_HELP: 1098 help(); 1099 break; 1100 case I_VERSION: 1101 printf("SFTP protocol version %u\n", sftp_proto_version(conn)); 1102 break; 1103 case I_PROGRESS: 1104 showprogress = !showprogress; 1105 if (showprogress) 1106 printf("Progress meter enabled\n"); 1107 else 1108 printf("Progress meter disabled\n"); 1109 break; 1110 default: 1111 fatal("%d is not implemented", cmdnum); 1112 } 1113 1114 if (g.gl_pathc) 1115 globfree(&g); 1116 if (path1) 1117 xfree(path1); 1118 if (path2) 1119 xfree(path2); 1120 1121 /* If an unignored error occurs in batch mode we should abort. */ 1122 if (err_abort && err != 0) 1123 return (-1); 1124 else if (cmdnum == I_QUIT) 1125 return (1); 1126 1127 return (0); 1128} 1129 1130int 1131interactive_loop(int fd_in, int fd_out, char *file1, char *file2) 1132{ 1133 char *pwd; 1134 char *dir = NULL; 1135 char cmd[2048]; 1136 struct sftp_conn *conn; 1137 int err; 1138 1139 conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); 1140 if (conn == NULL) 1141 fatal("Couldn't initialise connection to server"); 1142 1143 pwd = do_realpath(conn, "."); 1144 if (pwd == NULL) 1145 fatal("Need cwd"); 1146 1147 if (file1 != NULL) { 1148 dir = xstrdup(file1); 1149 dir = make_absolute(dir, pwd); 1150 1151 if (remote_is_dir(conn, dir) && file2 == NULL) { 1152 printf("Changing to: %s\n", dir); 1153 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); 1154 if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) 1155 return (-1); 1156 } else { 1157 if (file2 == NULL) 1158 snprintf(cmd, sizeof cmd, "get %s", dir); 1159 else 1160 snprintf(cmd, sizeof cmd, "get %s %s", dir, 1161 file2); 1162 1163 err = parse_dispatch_command(conn, cmd, &pwd, 1); 1164 xfree(dir); 1165 xfree(pwd); 1166 return (err); 1167 } 1168 xfree(dir); 1169 } 1170 1171#if HAVE_SETVBUF 1172 setvbuf(stdout, NULL, _IOLBF, 0); 1173 setvbuf(infile, NULL, _IOLBF, 0); 1174#else 1175 setlinebuf(stdout); 1176 setlinebuf(infile); 1177#endif 1178 1179 err = 0; 1180 for (;;) { 1181 char *cp; 1182 1183 printf("sftp> "); 1184 1185 /* XXX: use libedit */ 1186 if (fgets(cmd, sizeof(cmd), infile) == NULL) { 1187 printf("\n"); 1188 break; 1189 } 1190 1191 if (batchmode) /* Echo command */ 1192 printf("%s", cmd); 1193 1194 cp = strrchr(cmd, '\n'); 1195 if (cp) 1196 *cp = '\0'; 1197 1198 err = parse_dispatch_command(conn, cmd, &pwd, batchmode); 1199 if (err != 0) 1200 break; 1201 } 1202 xfree(pwd); 1203 1204 /* err == 1 signifies normal "quit" exit */ 1205 return (err >= 0 ? 0 : -1); 1206} 1207 1208static void 1209killchild(int signo) 1210{ 1211 if (sshpid > 1) 1212 kill(sshpid, signo); 1213 1214 _exit(1); 1215} 1216 1217static void 1218connect_to_server(char *path, char **args, int *in, int *out) 1219{ 1220 int c_in, c_out; 1221 1222#ifdef USE_PIPES 1223 int pin[2], pout[2]; 1224 1225 if ((pipe(pin) == -1) || (pipe(pout) == -1)) 1226 fatal("pipe: %s", strerror(errno)); 1227 *in = pin[0]; 1228 *out = pout[1]; 1229 c_in = pout[0]; 1230 c_out = pin[1]; 1231#else /* USE_PIPES */ 1232 int inout[2]; 1233 1234 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 1235 fatal("socketpair: %s", strerror(errno)); 1236 *in = *out = inout[0]; 1237 c_in = c_out = inout[1]; 1238#endif /* USE_PIPES */ 1239 1240 if ((sshpid = fork()) == -1) 1241 fatal("fork: %s", strerror(errno)); 1242 else if (sshpid == 0) { 1243 if ((dup2(c_in, STDIN_FILENO) == -1) || 1244 (dup2(c_out, STDOUT_FILENO) == -1)) { 1245 fprintf(stderr, "dup2: %s\n", strerror(errno)); 1246 exit(1); 1247 } 1248 close(*in); 1249 close(*out); 1250 close(c_in); 1251 close(c_out); 1252 execv(path, args); 1253 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 1254 exit(1); 1255 } 1256 1257 signal(SIGTERM, killchild); 1258 signal(SIGINT, killchild); 1259 signal(SIGHUP, killchild); 1260 close(c_in); 1261 close(c_out); 1262} 1263 1264static void 1265usage(void) 1266{ 1267 extern char *__progname; 1268 1269 fprintf(stderr, 1270 "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" 1271 " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" 1272 " [-S program] [-s subsystem | sftp_server] host\n" 1273 " %s [[user@]host[:file [file]]]\n" 1274 " %s [[user@]host[:dir[/]]]\n" 1275 " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); 1276 exit(1); 1277} 1278 1279int 1280main(int argc, char **argv) 1281{ 1282 int in, out, ch, err; 1283 char *host, *userhost, *cp, *file2; 1284 int debug_level = 0, sshver = 2; 1285 char *file1 = NULL, *sftp_server = NULL; 1286 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 1287 LogLevel ll = SYSLOG_LEVEL_INFO; 1288 arglist args; 1289 extern int optind; 1290 extern char *optarg; 1291 1292 __progname = ssh_get_progname(argv[0]); 1293 args.list = NULL; 1294 addargs(&args, "ssh"); /* overwritten with ssh_program */ 1295 addargs(&args, "-oForwardX11 no"); 1296 addargs(&args, "-oForwardAgent no"); 1297 addargs(&args, "-oClearAllForwardings yes"); 1298 1299 ll = SYSLOG_LEVEL_INFO; 1300 infile = stdin; 1301 1302 while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { 1303 switch (ch) { 1304 case 'C': 1305 addargs(&args, "-C"); 1306 break; 1307 case 'v': 1308 if (debug_level < 3) { 1309 addargs(&args, "-v"); 1310 ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 1311 } 1312 debug_level++; 1313 break; 1314 case 'F': 1315 case 'o': 1316 addargs(&args, "-%c%s", ch, optarg); 1317 break; 1318 case '1': 1319 sshver = 1; 1320 if (sftp_server == NULL) 1321 sftp_server = _PATH_SFTP_SERVER; 1322 break; 1323 case 's': 1324 sftp_server = optarg; 1325 break; 1326 case 'S': 1327 ssh_program = optarg; 1328 break; 1329 case 'b': 1330 if (batchmode) 1331 fatal("Batch file already specified."); 1332 1333 /* Allow "-" as stdin */ 1334 if (strcmp(optarg, "-") != 0 && 1335 (infile = fopen(optarg, "r")) == NULL) 1336 fatal("%s (%s).", strerror(errno), optarg); 1337 showprogress = 0; 1338 batchmode = 1; 1339 break; 1340 case 'P': 1341 sftp_direct = optarg; 1342 break; 1343 case 'B': 1344 copy_buffer_len = strtol(optarg, &cp, 10); 1345 if (copy_buffer_len == 0 || *cp != '\0') 1346 fatal("Invalid buffer size \"%s\"", optarg); 1347 break; 1348 case 'R': 1349 num_requests = strtol(optarg, &cp, 10); 1350 if (num_requests == 0 || *cp != '\0') 1351 fatal("Invalid number of requests \"%s\"", 1352 optarg); 1353 break; 1354 case 'h': 1355 default: 1356 usage(); 1357 } 1358 } 1359 1360 if (!isatty(STDERR_FILENO)) 1361 showprogress = 0; 1362 1363 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 1364 1365 if (sftp_direct == NULL) { 1366 if (optind == argc || argc > (optind + 2)) 1367 usage(); 1368 1369 userhost = xstrdup(argv[optind]); 1370 file2 = argv[optind+1]; 1371 1372 if ((host = strrchr(userhost, '@')) == NULL) 1373 host = userhost; 1374 else { 1375 *host++ = '\0'; 1376 if (!userhost[0]) { 1377 fprintf(stderr, "Missing username\n"); 1378 usage(); 1379 } 1380 addargs(&args, "-l%s",userhost); 1381 } 1382 1383 if ((cp = colon(host)) != NULL) { 1384 *cp++ = '\0'; 1385 file1 = cp; 1386 } 1387 1388 host = cleanhostname(host); 1389 if (!*host) { 1390 fprintf(stderr, "Missing hostname\n"); 1391 usage(); 1392 } 1393 1394 addargs(&args, "-oProtocol %d", sshver); 1395 1396 /* no subsystem if the server-spec contains a '/' */ 1397 if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 1398 addargs(&args, "-s"); 1399 1400 addargs(&args, "%s", host); 1401 addargs(&args, "%s", (sftp_server != NULL ? 1402 sftp_server : "sftp")); 1403 args.list[0] = ssh_program; 1404 1405 if (!batchmode) 1406 fprintf(stderr, "Connecting to %s...\n", host); 1407 connect_to_server(ssh_program, args.list, &in, &out); 1408 } else { 1409 args.list = NULL; 1410 addargs(&args, "sftp-server"); 1411 1412 if (!batchmode) 1413 fprintf(stderr, "Attaching to %s...\n", sftp_direct); 1414 connect_to_server(sftp_direct, args.list, &in, &out); 1415 } 1416 1417 err = interactive_loop(in, out, file1, file2); 1418 1419#if !defined(USE_PIPES) 1420 shutdown(in, SHUT_RDWR); 1421 shutdown(out, SHUT_RDWR); 1422#endif 1423 1424 close(in); 1425 close(out); 1426 if (batchmode) 1427 fclose(infile); 1428 1429 while (waitpid(sshpid, NULL, 0) == -1) 1430 if (errno != EINTR) 1431 fatal("Couldn't wait for ssh process: %s", 1432 strerror(errno)); 1433 1434 exit(err == 0 ? 0 : 1); 1435} 1436