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