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