Fake enough of signal() and waitpid() to clean up children.

Change-Id: Idfdf268419b0b92c60d1e398bbfb0cda0cfa3036
diff --git a/fuchsia/README.md b/fuchsia/README.md
index aa1a6d7..06d8476 100644
--- a/fuchsia/README.md
+++ b/fuchsia/README.md
@@ -16,9 +16,10 @@
 % netcp ~/.ssh/id_rsa.pub :/.ssh/authorized_keys
 ```
 
-Run `sshd` with `-d` for debug mode and `-r` to disable reexecing:
+Under `listen` run `sshd -ire` where `i` is for inetd mode, `r` to disable
+reexecing and `e` to print logs to stderr:
 ```
-$ sshd -dr
+$ listen 22 /system/bin/sshd -ire
 ```
 ### scp
 
diff --git a/fuchsia/fuchsia-compat.c b/fuchsia/fuchsia-compat.c
index efabdaa..e126723 100644
--- a/fuchsia/fuchsia-compat.c
+++ b/fuchsia/fuchsia-compat.c
@@ -3,12 +3,16 @@
 // found in the LICENSE file.
 
 #include <launchpad/launchpad.h>
+#include <magenta/syscalls.h>
+#include <pthread.h>
 #include <pwd.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
-#include <unistd.h>
+
+#include "openbsd-compat/bsd-misc.h"
 
 int chroot(const char *path) { return -1; }
 
@@ -43,7 +47,60 @@
 
 #define ARGV_MAX 256
 
-mx_handle_t fuchsia_launch_child(const char *command, int in, int out, int err) {
+typedef struct {
+  enum {
+    UNUSED, RUNNING, STOPPED
+  } state;
+  mx_handle_t handle;
+  int exit_code;
+} Child;
+
+#define BASE_PID 2
+#define NUM_CHILDREN 256
+static Child children[NUM_CHILDREN];
+
+static Child* get_child(pid_t pid) {
+  assert(pid - BASE_PID < NUM_CHILDREN);
+  assert(pid >= BASE_PID);
+  return &children[pid - BASE_PID];
+}
+
+static pid_t get_unused_pid() {
+  for (int i=0; i<NUM_CHILDREN; i++) {
+    if (children[i].state == UNUSED) {
+      return i+BASE_PID;
+    }
+  }
+  fprintf(stderr, "Can't allocate new pid.\n");
+  exit(1);
+}
+
+static volatile mysig_t sigchld_handler = SIG_IGN;
+
+static void* wait_thread_func(void* voidp) {
+  Child* child = voidp;
+
+  mx_signals_t observed;
+  mx_object_wait_one(child->handle, MX_PROCESS_SIGNALED, MX_TIME_INFINITE, &observed);
+
+  mx_info_process_t info;
+  size_t actual;
+  mx_object_get_info(child->handle, MX_INFO_PROCESS, &info, sizeof(info), &actual, NULL);
+
+  child->state = STOPPED;
+  child->exit_code = info.return_code;
+
+  mysig_t handler = sigchld_handler;
+  if (handler == SIG_IGN || handler == SIG_DFL) {
+    // Don't call a handler
+  } else {
+    handler(SIGCHLD);
+  }
+
+  return NULL;
+}
+
+pid_t fuchsia_launch_child(const char *command, int in, int out, int err) {
   const char *argv[ARGV_MAX];
   int argc = 1;
   argv[0] = "/boot/bin/sh";
@@ -70,8 +127,57 @@
 
   mx_status_t status = launchpad_go(lp, &proc, &errmsg);
   if (status < 0) {
-    printf("error from launchpad_go: %s\n", errmsg);
+    fprintf(stderr, "error from launchpad_go: %s\n", errmsg);
+    exit(1);
   }
 
-  return proc;
+  pid_t pid = get_unused_pid();
+  Child* child = get_child(pid);
+  child->state = RUNNING;
+  child->handle = proc;
+
+  pthread_t wait_thread;
+  if (pthread_create(&wait_thread, NULL, wait_thread_func, (void*)child) != 0) {
+    fprintf(stderr, "Failed to create process waiter thread: %s\n", strerror(errno));
+  }
+
+  return pid;
+}
+
+mysig_t mysignal(int signum, mysig_t handler) {
+  if (signum == SIGCHLD) {
+    sigchld_handler = handler;
+  }
+  // Ignore all non-SIGCHLD requests
+  return handler;
+}
+
+pid_t waitpid(pid_t pid, int* status, int options) {
+  if (pid == -1 || pid == 0) {
+    // Find an exited process.
+    for (pid = BASE_PID; pid < BASE_PID + NUM_CHILDREN; pid++) {
+      if (get_child(pid)->state == STOPPED) {
+        return waitpid(pid, status, options);
+      }
+    }
+    if (options & WNOHANG) {
+      return 0;
+    } else {
+      fprintf(stderr, "No child pids waiting for wait.\n");
+      exit(1);
+    }
+  }
+
+  Child* child = get_child(pid);
+  if (child->state != STOPPED) {
+    fprintf(stderr, "Child with pid %d isn't stopped.\n", pid);
+    exit(1);
+  }
+
+  if (status) {
+    *status = child->exit_code;
+  }
+  child->state = UNUSED;
+
+  return pid;
 }
diff --git a/fuchsia/fuchsia-compat.h b/fuchsia/fuchsia-compat.h
index a04794d..89a15fa 100644
--- a/fuchsia/fuchsia-compat.h
+++ b/fuchsia/fuchsia-compat.h
@@ -6,7 +6,7 @@
 #include <magenta/types.h>
 
 int chroot(const char *path);
-mx_handle_t fuchsia_launch_child(const char *command, int in, int out, int err);
+int fuchsia_launch_child(const char *command, int in, int out, int err);
 
 #define CUSTOM_SYS_AUTH_PASSWD
 
diff --git a/fuchsia/sshd_config b/fuchsia/sshd_config
index 5b94765..5bf9825 100644
--- a/fuchsia/sshd_config
+++ b/fuchsia/sshd_config
@@ -88,7 +88,7 @@
 #X11Forwarding no
 #X11DisplayOffset 10
 #X11UseLocalhost yes
-#PermitTTY yes
+PermitTTY no
 #PrintMotd yes
 #PrintLastLog yes
 #TCPKeepAlive yes
diff --git a/openbsd-compat/bsd-misc.c b/openbsd-compat/bsd-misc.c
index 6f3bc8f..5a175cc 100644
--- a/openbsd-compat/bsd-misc.c
+++ b/openbsd-compat/bsd-misc.c
@@ -211,6 +211,7 @@
 }
 #endif /* HAVE_TCSENDBREAK */
 
+#ifndef __Fuchsia__
 mysig_t
 mysignal(int sig, mysig_t act)
 {
@@ -237,6 +238,7 @@
 	return (signal(sig, act));
 #endif
 }
+#endif
 
 #ifndef HAVE_STRDUP
 char *
diff --git a/session.c b/session.c
index c51ede8..19b25f2 100644
--- a/session.c
+++ b/session.c
@@ -344,8 +344,8 @@
 	session_proctitle(s);
 
 #ifdef __Fuchsia__
-        mx_handle_t process_handle = fuchsia_launch_child(command, pin[0], pout[1], perr[1]);
-        if (process_handle == 0) {
+        pid = fuchsia_launch_child(command, pin[0], pout[1], perr[1]);
+        if (pid <= 0) {
 #ifdef USE_PIPES
 		close(pin[0]);
 		close(pin[1]);
@@ -361,8 +361,6 @@
 #endif
 		return -1;
         }
-        // Force an mx_handle_t into a pid_t.
-        pid = (pid_t)process_handle;
 #else
 	/* Fork the child. */
 	switch ((pid = fork())) {
@@ -454,8 +452,10 @@
 	cygwin_set_impersonation_token(INVALID_HANDLE_VALUE);
 #endif
 
-	s->pid = pid;
 #endif  // __Fuchsia__
+
+	s->pid = pid;
+
 	/* Set interactive/non-interactive mode. */
 	packet_set_interactive(s->display != NULL,
 	    options.ip_qos_interactive, options.ip_qos_bulk);