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