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