Use PTYs and support interactive SSH sessions (with bugs)

Change-Id: If8cc0905ee0e968fe01b6dff6e1be55b1a65e15e
diff --git a/fuchsia/fuchsia-compat.c b/fuchsia/fuchsia-compat.c
index e126723..5d5356a 100644
--- a/fuchsia/fuchsia-compat.c
+++ b/fuchsia/fuchsia-compat.c
@@ -118,8 +118,16 @@
   launchpad_set_args(lp, argc, argv);
   launchpad_clone(lp, LP_CLONE_MXIO_ROOT|LP_CLONE_MXIO_CWD);
   // TODO: set up environment
-  launchpad_transfer_fd(lp, in, STDIN_FILENO);
-  launchpad_transfer_fd(lp, out, STDOUT_FILENO);
+  if (in == out) {
+    launchpad_clone_fd(lp, in, STDIN_FILENO);
+  } else {
+    launchpad_transfer_fd(lp, in, STDIN_FILENO);
+  }
+  if (out == err) {
+    launchpad_clone_fd(lp, out, STDOUT_FILENO);
+  } else {
+    launchpad_transfer_fd(lp, out, STDOUT_FILENO);
+  }
   launchpad_transfer_fd(lp, err, STDERR_FILENO);
 
   mx_handle_t proc = 0;
diff --git a/fuchsia/sshd_config b/fuchsia/sshd_config
index d8b664b..878ad04 100644
--- a/fuchsia/sshd_config
+++ b/fuchsia/sshd_config
@@ -88,7 +88,7 @@
 #X11Forwarding no
 #X11DisplayOffset 10
 #X11UseLocalhost yes
-PermitTTY no
+#PermitTTY no
 #PrintMotd yes
 #PrintLastLog yes
 #TCPKeepAlive yes
diff --git a/session.c b/session.c
index 19b25f2..627f214 100644
--- a/session.c
+++ b/session.c
@@ -529,6 +529,10 @@
 		return -1;
 	}
 
+#ifdef __Fuchsia__
+        pid = fuchsia_launch_child(command, ttyfd, ttyfd, ttyfd);
+
+#else
 	/* Fork the child. */
 	switch ((pid = fork())) {
 	case -1:
@@ -588,6 +592,8 @@
 	cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
 #endif
 
+#endif  // __Fuchsia__
+
 	s->pid = pid;
 
 	/* Parent.  Close the slave side of the pseudo tty. */
diff --git a/sshpty.c b/sshpty.c
index fe2fb5a..a5f8f34 100644
--- a/sshpty.c
+++ b/sshpty.c
@@ -53,6 +53,10 @@
 # endif
 #endif
 
+#ifdef __Fuchsia__
+#include <magenta/device/pty.h>
+#endif  // __Fuchsia__
+
 /*
  * Allocates and opens a pty.  Returns 0 if no pty could be allocated, or
  * nonzero if a pty was successfully allocated.  On success, open file
@@ -63,6 +67,21 @@
 int
 pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
 {
+#ifdef __Fuchsia__
+        *ptyfd = open("/dev/misc/ptmx", O_RDWR | O_NONBLOCK);
+        if (*ptyfd < 0) {
+                error("open /dev/misc/ptmx: %s", strerror(errno));
+                return 0;
+        }
+        *ttyfd = openat(*ptyfd, "0", O_RDWR | O_NONBLOCK);
+        if (*ttyfd < 0) {
+                error("openat /dev/misc/ptmx 0: %s", strerror(errno));
+                return 0;
+        }
+        // ssh thinks it wants to be able to manipulate a PTY by filename but
+        // that isn't possible on Fuchsia.
+        strlcpy(namebuf, "/dev/not-a-file", namebuflen);
+#else
 	/* openpty(3) exists in OSF/1 and some other os'es */
 	char *name;
 	int i;
@@ -77,6 +96,7 @@
 		fatal("openpty returns device for which ttyname fails.");
 
 	strlcpy(namebuf, name, namebuflen);	/* possible truncation */
+#endif  // __Fuchsia__
 	return 1;
 }
 
@@ -176,6 +196,13 @@
 pty_change_window_size(int ptyfd, u_int row, u_int col,
 	u_int xpixel, u_int ypixel)
 {
+#ifdef __Fuchsia__
+        pty_window_size_t w;
+
+        w.width = col;
+        w.height = row;
+        ioctl_pty_set_window_size(ptyfd, &w);
+#else
 	struct winsize w;
 
 	/* may truncate u_int -> u_short */
@@ -184,11 +211,17 @@
 	w.ws_xpixel = xpixel;
 	w.ws_ypixel = ypixel;
 	(void) ioctl(ptyfd, TIOCSWINSZ, &w);
+#endif  // __Fuchsia__
 }
 
 void
 pty_setowner(struct passwd *pw, const char *tty)
 {
+#ifdef __Fuchsia__
+        // This is meaningless on Fuchsia.
+        return;
+#endif
+
 	struct group *grp;
 	gid_t gid;
 	mode_t mode;