[virtcon] Make virtcon host a fidl service for sessions.

(Second attempt, previous try at:
I09e66a7c5bc91fb41b6bfb99510b5be1aa609d57

reverted before due to race condition due to async fidl
call).

the virtcon (virtual-console) binary used to provide new sessions
through a convoluted path through device coordinator. This will make it
expose a fidl service directly for clients to request a new session.

This removes device coordinator from the operational loop and will make
it easy for the virtual-console binary to stand on it's own once we have
something else to start it.

It is a little messy due to the virtcon binary using the "port" library
heavily and fidl being built around the async_loop.

Test:
* Booted the following 3 times and verified that all three virtcons
appear and are interactive:
  * vim2
  * eve
  * qemu
  * qemu w/ KVM
* Tested qemu with virtcon.disable=true
* Tested qemu with zircon-only build (zircon/scripts/run-zircon-x64 -b
-g -k).

ZX-3403 # Get virtcon out of the way.

Change-Id: I708dc16a162e87838498afa5a38c4a5a0d9a3cf2
diff --git a/garnet/bin/appmgr/BUILD.gn b/garnet/bin/appmgr/BUILD.gn
index 286989b..6e1955c 100644
--- a/garnet/bin/appmgr/BUILD.gn
+++ b/garnet/bin/appmgr/BUILD.gn
@@ -60,6 +60,7 @@
     "//zircon/public/fidl/fuchsia-inspect",
     "//zircon/public/fidl/fuchsia-process",
     "//zircon/public/fidl/fuchsia-scheduler",
+    "//zircon/public/fidl/fuchsia-virtualconsole",
     "//zircon/public/lib/async-loop-cpp",
     "//zircon/public/lib/fit",
     "//zircon/public/lib/fs",
diff --git a/garnet/bin/appmgr/component_controller_unittest.cc b/garnet/bin/appmgr/component_controller_unittest.cc
index 6e4f550..7c103dc 100644
--- a/garnet/bin/appmgr/component_controller_unittest.cc
+++ b/garnet/bin/appmgr/component_controller_unittest.cc
@@ -9,6 +9,7 @@
 #include <fuchsia/device/manager/cpp/fidl.h>
 #include <fuchsia/kernel/cpp/fidl.h>
 #include <fuchsia/scheduler/cpp/fidl.h>
+#include <fuchsia/virtualconsole/cpp/fidl.h>
 #include <lib/fdio/spawn.h>
 #include <zircon/syscalls/object.h>
 
@@ -66,14 +67,15 @@
 std::vector<std::string> GetDefaultNamespaceServiceEntries() {
   return std::vector<std::string>{
       ".",
+      Namespace::Launcher::Name_,
       fuchsia::device::manager::Administrator::Name_,
       fuchsia::device::manager::DebugDumper::Name_,
       fuchsia::kernel::DebugBroker::Name_,
-      fuchsia::sys::Environment::Name_,
-      Namespace::Launcher::Name_,
       fuchsia::process::Launcher::Name_,
       fuchsia::process::Resolver::Name_,
       fuchsia::scheduler::ProfileProvider::Name_,
+      fuchsia::sys::Environment::Name_,
+      fuchsia::virtualconsole::SessionManager::Name_,
   };
 }
 
diff --git a/garnet/bin/appmgr/integration_tests/hub_integration_test.cc b/garnet/bin/appmgr/integration_tests/hub_integration_test.cc
index f2d143e..50b0586 100644
--- a/garnet/bin/appmgr/integration_tests/hub_integration_test.cc
+++ b/garnet/bin/appmgr/integration_tests/hub_integration_test.cc
@@ -95,7 +95,8 @@
         "fuchsia.scheduler.ProfileProvider",
         "fuchsia.sys.Environment",
         "fuchsia.sys.Launcher",
-        "fuchsia.sys.Loader"};
+        "fuchsia.sys.Loader",
+        "fuchsia.virtualconsole.SessionManager"};
     sysmgr::Config config;
     ASSERT_TRUE(config.ParseFromDirectory("/system/data/sysmgr"));
     // The following path is deprecated, and because config-data is component
diff --git a/garnet/bin/appmgr/namespace.cc b/garnet/bin/appmgr/namespace.cc
index 8138b88..929a300e 100644
--- a/garnet/bin/appmgr/namespace.cc
+++ b/garnet/bin/appmgr/namespace.cc
@@ -8,6 +8,7 @@
 #include <fuchsia/kernel/cpp/fidl.h>
 #include <fuchsia/process/cpp/fidl.h>
 #include <fuchsia/scheduler/cpp/fidl.h>
+#include <fuchsia/virtualconsole/cpp/fidl.h>
 #include <lib/async/default.h>
 #include <lib/fdio/directory.h>
 #include <lib/fdio/fd.h>
@@ -92,6 +93,14 @@
                 std::move(channel)));
         return ZX_OK;
       })));
+  services_->AddService(
+      fuchsia::virtualconsole::SessionManager::Name_,
+      fbl::AdoptRef(new fs::Service([this](zx::channel channel) {
+        realm_->environment_services()->Connect(
+            fidl::InterfaceRequest<fuchsia::virtualconsole::SessionManager>(
+                std::move(channel)));
+        return ZX_OK;
+      })));
 
   if (additional_services) {
     auto& names = additional_services->names;
diff --git a/zircon/system/core/devmgr/devcoordinator/main.cpp b/zircon/system/core/devmgr/devcoordinator/main.cpp
index 8de8b54..45e8f5d 100644
--- a/zircon/system/core/devmgr/devcoordinator/main.cpp
+++ b/zircon/system/core/devmgr/devcoordinator/main.cpp
@@ -74,6 +74,10 @@
     zx::channel svchost_outgoing;
 
     zx::channel fs_root;
+
+    // Used to bind the svchost to the virtual-console binary to provide fidl
+    // services.
+    zx::channel virtcon_fidl;
 } g_handles;
 
 // Wait for the requested file.  Its parent directory must exist.
@@ -461,6 +465,13 @@
         }
     }
 
+    zx::channel virtcon_client;
+    status = zx::channel::create(0, &virtcon_client, &g_handles.virtcon_fidl);
+    if (status != ZX_OK) {
+        printf("Unable to create virtcon channel.\n");
+        return status;
+    }
+
     // svchost needs to hold this to talk to zx_kerneldebug but doesn't need any rights.
     // TODO(ZX-971): when zx_debug_send_command syscall is descoped, update this too.
     zx::resource root_resource_copy;
@@ -501,6 +512,11 @@
 
     // Add a handle to allow svchost to proxy services to fshost.
     launchpad_add_handle(lp, fshost_client.release(), PA_HND(PA_USER0, 4));
+    if (!coordinator->boot_args().GetBool("virtcon.disable", false)) {
+        // Add handle to channel to allow svchost to proxy fidl services to
+        // virtcon.
+        launchpad_add_handle(lp, virtcon_client.release(), PA_HND(PA_USER0, 5));
+    }
 
     // Give svchost access to /dev/class/sysmem, to enable svchost to forward sysmem service
     // requests to the sysmem driver.  Create a namespace containing /dev/class/sysmem.
@@ -698,18 +714,13 @@
         zx_handle_t handles[2];
         uint32_t types[2];
 
-        zx::channel virtcon_client, virtcon_server;
-        zx_status_t status = zx::channel::create(0, &virtcon_client, &virtcon_server);
-        if (status == ZX_OK) {
-            coordinator->set_virtcon_channel(std::move(virtcon_client));
-            handles[handle_count] = virtcon_server.release();
-            types[handle_count] = PA_HND(PA_USER0, 0);
-            ++handle_count;
-        }
+        handles[handle_count] = g_handles.virtcon_fidl.release();
+        types[handle_count] = PA_HND(PA_USER0, 0);
+        ++handle_count;
 
         zx::debuglog debuglog;
-        status = zx::debuglog::create(coordinator->root_resource(),
-                                      ZX_LOG_FLAG_READABLE, &debuglog);
+        zx_status_t status = zx::debuglog::create(coordinator->root_resource(),
+                                                  ZX_LOG_FLAG_READABLE, &debuglog);
         if (status == ZX_OK) {
             handles[handle_count] = debuglog.release();
             types[handle_count] = PA_HND(PA_USER0, 1);
diff --git a/zircon/system/core/svchost/BUILD.gn b/zircon/system/core/svchost/BUILD.gn
index 91df160..cb9f914 100644
--- a/zircon/system/core/svchost/BUILD.gn
+++ b/zircon/system/core/svchost/BUILD.gn
@@ -19,6 +19,7 @@
     "$zx/system/fidl/fuchsia-process:c",
     "$zx/system/fidl/fuchsia-scheduler:c",
     "$zx/system/fidl/fuchsia-sysmem:c",
+    "$zx/system/fidl/fuchsia-virtualconsole:c",
     "$zx/system/ulib/async-loop:async-loop-cpp",
     "$zx/system/ulib/fbl",
     "$zx/system/ulib/fdio",
diff --git a/zircon/system/core/svchost/svchost.cpp b/zircon/system/core/svchost/svchost.cpp
index e4e4a26..c0cc5a9 100644
--- a/zircon/system/core/svchost/svchost.cpp
+++ b/zircon/system/core/svchost/svchost.cpp
@@ -9,6 +9,7 @@
 #include <fs/remote-dir.h>
 #include <fuchsia/device/manager/c/fidl.h>
 #include <fuchsia/fshost/c/fidl.h>
+#include <fuchsia/virtualconsole/c/fidl.h>
 #include <fuchsia/net/c/fidl.h>
 #include <lib/async-loop/cpp/loop.h>
 #include <lib/fdio/fd.h>
@@ -200,7 +201,7 @@
     }
 }
 
-void publish_proxy_service(const fbl::RefPtr<fs::PseudoDir>& dir,
+void publish_remote_service(const fbl::RefPtr<fs::PseudoDir>& dir,
                            const char* name, zx::unowned_channel forwarding_channel) {
     fbl::String path = fbl::StringPrintf("public/%s", name);
     dir->AddEntry(name, fbl::MakeRefCounted<fs::Service>(
@@ -209,6 +210,18 @@
             }));
 }
 
+//TODO(edcoyne): remove this and make virtcon talk virtual filesystems too.
+void publish_proxy_service(const fbl::RefPtr<fs::PseudoDir>& dir,
+                           const char* name, zx::unowned_channel forwarding_channel) {
+    dir->AddEntry(name, fbl::MakeRefCounted<fs::Service>(
+            [name, forwarding_channel = std::move(forwarding_channel)](zx::channel request) {
+                const auto request_handle = request.release();
+                return forwarding_channel->write(0, name, static_cast<uint32_t>(strlen(name)),
+                                                 &request_handle, 1);
+            }));
+}
+
+
 int main(int argc, char** argv) {
     bool require_system = false;
     if (argc > 1) {
@@ -223,6 +236,7 @@
     root_resource = zx_take_startup_handle(PA_HND(PA_USER0, 2));
     zx::channel devmgr_proxy_channel = zx::channel(zx_take_startup_handle(PA_HND(PA_USER0, 3)));
     zx::channel fshost_svc = zx::channel(zx_take_startup_handle(PA_HND(PA_USER0, 4)));
+    zx::channel virtcon_proxy_channel = zx::channel(zx_take_startup_handle(PA_HND(PA_USER0, 5)));
 
     zx_status_t status = outgoing.ServeFromStartupInfo();
     if (status != ZX_OK) {
@@ -279,13 +293,19 @@
     publish_services(outgoing.public_dir(), deprecated_services, zx::unowned_channel(appmgr_svc));
     publish_services(outgoing.public_dir(), fshost_services, zx::unowned_channel(fshost_svc));
 
-    publish_proxy_service(outgoing.public_dir(),
+    publish_remote_service(outgoing.public_dir(),
                           fuchsia_device_manager_DebugDumper_Name,
                           zx::unowned_channel(devmgr_proxy_channel));
-    publish_proxy_service(outgoing.public_dir(),
+    publish_remote_service(outgoing.public_dir(),
                           fuchsia_device_manager_Administrator_Name,
                           zx::unowned_channel(devmgr_proxy_channel));
 
+    if (virtcon_proxy_channel.is_valid()) {
+        publish_proxy_service(outgoing.public_dir(),
+                              fuchsia_virtualconsole_SessionManager_Name,
+                              zx::unowned_channel(virtcon_proxy_channel));
+    }
+
     start_crashsvc(zx::job(root_job),
         require_system? appmgr_svc : ZX_HANDLE_INVALID);
 
diff --git a/zircon/system/core/virtcon/BUILD.gn b/zircon/system/core/virtcon/BUILD.gn
index 48eab4c..b221a98 100644
--- a/zircon/system/core/virtcon/BUILD.gn
+++ b/zircon/system/core/virtcon/BUILD.gn
@@ -27,9 +27,11 @@
     "$zx/system/fidl/fuchsia-hardware-input:c",
     "$zx/system/fidl/fuchsia-hardware-pty:c",
     "$zx/system/fidl/fuchsia-io:c",
+    "$zx/system/fidl/fuchsia-virtualconsole:c",
     "$zx/system/ulib/fbl",
     "$zx/system/ulib/fdio",
     "$zx/system/ulib/fidl",
+    "$zx/system/ulib/fs",
     "$zx/system/ulib/fzl",
     "$zx/system/ulib/gfx",
     "$zx/system/ulib/gfx-font-data",
diff --git a/zircon/system/core/virtcon/main.cpp b/zircon/system/core/virtcon/main.cpp
index 8f5310b..a5e897d 100644
--- a/zircon/system/core/virtcon/main.cpp
+++ b/zircon/system/core/virtcon/main.cpp
@@ -9,16 +9,20 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <memory>
 
 #include <fbl/algorithm.h>
+#include <fbl/string_piece.h>
 #include <fbl/unique_fd.h>
+#include <fs/handler.h>
 #include <fuchsia/hardware/pty/c/fidl.h>
 #include <fuchsia/io/c/fidl.h>
-#include <lib/fdio/io.h>
-#include <lib/fdio/spawn.h>
+#include <fuchsia/virtualconsole/c/fidl.h>
+#include <lib/fdio/directory.h>
 #include <lib/fdio/fd.h>
 #include <lib/fdio/fdio.h>
-#include <lib/fdio/directory.h>
+#include <lib/fdio/io.h>
+#include <lib/fdio/spawn.h>
 #include <lib/fdio/watcher.h>
 #include <lib/fzl/fdio.h>
 #include <lib/zx/channel.h>
@@ -156,7 +160,7 @@
     return ZX_ERR_STOP;
 }
 
-static zx_status_t session_create(vc_t** out, int* out_fd, bool make_active, bool special) {
+static zx_status_t remote_session_create(vc_t** out, zx::channel session, bool make_active, bool special) {
     // The ptmx device can start later than these threads
     int retry = 30;
     int raw_fd;
@@ -173,14 +177,9 @@
         return ZX_ERR_INTERNAL;
     }
 
-    zx::channel device_channel, client_channel;
-    zx_status_t status = zx::channel::create(0, &device_channel, &client_channel);
-    if (status != ZX_OK) {
-        return status;
-    }
-
+    zx_status_t status;
     zx_status_t fidl_status = fuchsia_hardware_pty_DeviceOpenClient(
-        fdio_unsafe_borrow_channel(io), 0, device_channel.release(), &status);
+        fdio_unsafe_borrow_channel(io), 0, session.release(), &status);
     fdio_unsafe_release(io);
     if (fidl_status != ZX_OK) {
         return fidl_status;
@@ -189,13 +188,6 @@
         return status;
     }
 
-    int raw_client_fd;
-    status = fdio_fd_create(client_channel.release(), &raw_client_fd);
-    if (status != ZX_OK) {
-        return status;
-    }
-    fbl::unique_fd client_fd(raw_client_fd);
-
     vc_t* vc;
     if (vc_create(&vc, special)) {
         return ZX_ERR_INTERNAL;
@@ -225,13 +217,35 @@
     vc->fh.func = session_io_cb;
 
     *out = vc;
+    return ZX_OK;
+}
+
+static zx_status_t session_create(vc_t** out, int* out_fd, bool make_active, bool special) {
+    zx::channel device_channel, client_channel;
+    zx_status_t status = zx::channel::create(0, &device_channel, &client_channel);
+    if (status != ZX_OK) {
+        return status;
+    }
+
+    status = remote_session_create(out, std::move(device_channel), make_active, special);
+    if (status != ZX_OK) {
+        return status;
+    }
+
+    int raw_client_fd;
+    status = fdio_fd_create(client_channel.release(), &raw_client_fd);
+    if (status != ZX_OK) {
+        return status;
+    }
+    fbl::unique_fd client_fd(raw_client_fd);
+
     *out_fd = client_fd.release();
     return ZX_OK;
 }
 
 static void start_shell(bool make_active, const char* cmd) {
-    vc_t* vc;
-    int fd;
+    vc_t* vc = nullptr;
+    int fd = 0;
 
     if (session_create(&vc, &fd, make_active, cmd != NULL) < 0) {
         return;
@@ -246,36 +260,82 @@
     }
 }
 
-static zx_status_t new_vc_cb(port_handler_t* ph, zx_signals_t signals, uint32_t evt) {
-    zx::channel h;
-    uint32_t dcount, hcount;
-    if (zx_channel_read(ph->handle, 0, NULL, h.reset_and_get_address(), 0, 1, &dcount, &hcount) < 0) {
-        return ZX_OK;
-    }
-    if (hcount != 1) {
-        return ZX_OK;
-    }
+static zx_status_t new_vc_cb(void*, zx_handle_t session, fidl_txn_t* txn) {
+    zx::channel session_channel(session);
 
-    vc_t* vc;
-    int fd;
-    if (session_create(&vc, &fd, true, false) < 0) {
-        return ZX_OK;
-    }
-
-    zx_handle_t handle = ZX_HANDLE_INVALID;
-    uint32_t type = PA_HND(PA_FD, FDIO_FLAG_USE_FOR_STDIO);
-    zx_status_t status = fdio_fd_transfer(fd, &handle);
-    if (status != ZX_OK) {
-        session_destroy(vc);
-        return ZX_OK;
-    }
-    status = h.write(0, &type, static_cast<uint32_t>(sizeof(type)), &handle, 1);
-    if (status != ZX_OK) {
-        session_destroy(vc);
+    vc_t* vc = nullptr;
+    if (remote_session_create(&vc, std::move(session_channel), true, false) < 0) {
         return ZX_OK;
     }
 
     port_wait(&port, &vc->fh.ph);
+
+    return fuchsia_virtualconsole_SessionManagerCreateSession_reply(txn, ZX_OK);
+}
+
+static zx_status_t fidl_message_cb(port_handler_t* ph, zx_signals_t signals, uint32_t evt) {
+    if ((signals & ZX_CHANNEL_PEER_CLOSED) &&
+        !(signals & ZX_CHANNEL_READABLE)) {
+        zx_handle_close(ph->handle);
+        delete ph;
+        return ZX_ERR_STOP;
+    }
+
+    auto status = fs::ReadMessage(ph->handle, [](fidl_msg_t* message, fs::FidlConnection* txn) {
+        static constexpr fuchsia_virtualconsole_SessionManager_ops_t kOps {
+            .CreateSession = new_vc_cb,
+        };
+
+        return fuchsia_virtualconsole_SessionManager_dispatch(nullptr,
+                                                              reinterpret_cast<fidl_txn_t*>(txn),
+                                                              message,
+                                                              &kOps);
+    });
+
+    if (status != ZX_OK) {
+        printf("Failed to dispatch fidl message from client: %s\n", zx_status_get_string(status));
+        zx_handle_close(ph->handle);
+        delete ph;
+        return ZX_ERR_STOP;
+    }
+
+    return ZX_OK;
+}
+
+static zx_status_t fidl_connection_cb(port_handler_t* ph, zx_signals_t signals, uint32_t evt) {
+    constexpr size_t kBufferSize = 256;
+    char buffer[kBufferSize];
+
+    uint32_t bytes_read, handles_read;
+    zx_handle_t client_raw;
+    auto status = zx_channel_read(ph->handle, 0,
+                                  buffer, &client_raw,
+                                  kBufferSize, 1,
+                                  &bytes_read, &handles_read);
+    if (status != ZX_OK) {
+        printf("Failed to read from channel: %s\n", zx_status_get_string(status));
+        return ZX_OK;
+    }
+
+    if (handles_read < 1) {
+        printf("Fidl connection with no channel.\n");
+        return ZX_OK;
+    }
+    zx::channel client(client_raw);
+
+    if (fbl::StringPiece(fuchsia_virtualconsole_SessionManager_Name) ==
+        fbl::StringPiece(buffer, bytes_read)) {
+        auto handler = std::unique_ptr<port_handler_t>(new port_handler_t {
+            .handle = client.release(),
+            .waitfor = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
+            .func = fidl_message_cb,
+        });
+
+        port_wait(&port, handler.release());
+    } else {
+        printf("Unsupported fidl interface: %.*s\n", bytes_read, buffer);
+    }
+
     return ZX_OK;
 }
 
@@ -442,7 +502,7 @@
     log_ph.waitfor = ZX_LOG_READABLE;
 
     if ((new_vc_ph.handle = zx_take_startup_handle(PA_HND(PA_USER0, 0))) != ZX_HANDLE_INVALID) {
-        new_vc_ph.func = new_vc_cb;
+        new_vc_ph.func = fidl_connection_cb;
         new_vc_ph.waitfor = ZX_CHANNEL_READABLE;
         port_wait(&port, &new_vc_ph);
     }
diff --git a/zircon/system/fidl/fuchsia-virtualconsole/BUILD.gn b/zircon/system/fidl/fuchsia-virtualconsole/BUILD.gn
new file mode 100644
index 0000000..7cfa778
--- /dev/null
+++ b/zircon/system/fidl/fuchsia-virtualconsole/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("$zx/public/gn/fidl.gni")
+
+fidl_library("fuchsia-virtualconsole") {
+  sources = [
+    "session-manager.fidl",
+  ]
+  public_deps = [
+    "$zx/system/fidl/fuchsia-hardware-pty",
+  ]
+}
diff --git a/zircon/system/fidl/fuchsia-virtualconsole/session-manager.fidl b/zircon/system/fidl/fuchsia-virtualconsole/session-manager.fidl
new file mode 100644
index 0000000..7605381
--- /dev/null
+++ b/zircon/system/fidl/fuchsia-virtualconsole/session-manager.fidl
@@ -0,0 +1,17 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library fuchsia.virtualconsole;
+
+using zx;
+using fuchsia.hardware.pty;
+
+/// Manages virtual console sessions.
+[Discoverable, Layout = "Simple"]
+protocol SessionManager {
+    /// Create a new virtual console session.
+    // TODO(ZX-3407): Remove response status and debug why this doesn't work async when we switch
+    // to new pty impl.
+    CreateSession(request<fuchsia.hardware.pty.Device> session) -> (zx.status status);
+};
diff --git a/zircon/system/uapp/run-vc/BUILD.gn b/zircon/system/uapp/run-vc/BUILD.gn
index 7de523b..00f553ae 100644
--- a/zircon/system/uapp/run-vc/BUILD.gn
+++ b/zircon/system/uapp/run-vc/BUILD.gn
@@ -7,7 +7,7 @@
     "main.c",
   ]
   deps = [
-    "$zx/system/fidl/fuchsia-device-manager:c",
+    "$zx/system/fidl/fuchsia-virtualconsole:c",
     "$zx/system/ulib/fdio",
     "$zx/system/ulib/zircon",
   ]
diff --git a/zircon/system/uapp/run-vc/main.c b/zircon/system/uapp/run-vc/main.c
index 782e568..3829096 100644
--- a/zircon/system/uapp/run-vc/main.c
+++ b/zircon/system/uapp/run-vc/main.c
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include <fcntl.h>
-#include <fuchsia/device/manager/c/fidl.h>
+#include <fuchsia/virtualconsole/c/fidl.h>
 #include <lib/fdio/io.h>
 #include <lib/fdio/spawn.h>
 #include <lib/fdio/fd.h>
@@ -22,76 +22,54 @@
 #include <zircon/syscalls.h>
 #include <zircon/types.h>
 
-static zx_status_t dmctl_watch_func(int dirfd, int event, const char* fn, void* cookie) {
-    if (event != WATCH_EVENT_ADD_FILE) {
-        return ZX_OK;
+static zx_status_t connect_to_service(const char* service, zx_handle_t* channel) {
+    zx_handle_t channel_local, channel_remote;
+    zx_status_t status = zx_channel_create(0, &channel_local, &channel_remote);
+    if (status != ZX_OK) {
+        fprintf(stderr, "run-vc: failed to create channel: %s\n", zx_status_get_string(status));
+        return false;
     }
-    if (!strcmp(fn, "dmctl")) {
-        return ZX_ERR_STOP;
-    }
-    return ZX_OK;
-};
 
-static zx_status_t open_dmctl(int* fd) {
-    int dirfd = open("/dev/misc", O_RDONLY);
-    if (dirfd < 0) {
-        return ZX_ERR_IO;
+
+    status = fdio_service_connect(service, channel_remote);
+    if (status != ZX_OK) {
+        zx_handle_close(channel_local);
+        fprintf(stderr, "run-vc: failed to connect to service: %s\n",
+                zx_status_get_string(status));
+        return false;
     }
-    zx_status_t status = fdio_watch_directory(dirfd, dmctl_watch_func, ZX_TIME_INFINITE, NULL);
-    if (status != ZX_ERR_STOP) {
-        if (status == ZX_OK) {
-            status = ZX_ERR_BAD_STATE;
-        }
-        printf("failed to watch /dev/misc: %s\n", zx_status_get_string(status));
-        close(dirfd);
-        return status;
-    }
-    *fd = openat(dirfd, "dmctl", O_RDWR);
-    close(dirfd);
-    if (*fd < 0) {
-        return ZX_ERR_IO;
-    }
-    return ZX_OK;
+
+    *channel = channel_local;
+    return true;
+
 }
 
 int main(int argc, const char** argv) {
-    int fd;
-    zx_status_t status = open_dmctl(&fd);
-    if (status != ZX_OK) {
-        fprintf(stderr, "failed to open dmctl: %s\n", zx_status_get_string(status));
-        return -1;
-    }
-    zx_handle_t dmctl;
-    status = fdio_get_service_handle(fd, &dmctl);
-    if (status != ZX_OK) {
-        fprintf(stderr, "error %s converting fd to handle\n", zx_status_get_string(status));
+    zx_handle_t session_manager = ZX_HANDLE_INVALID;
+    if (!connect_to_service("/svc/fuchsia.virtualconsole.SessionManager", &session_manager)) {
         return -1;
     }
 
-    zx_handle_t h0, h1;
-    if (zx_channel_create(0, &h0, &h1) < 0) {
+
+    zx_handle_t session, session_remote;
+    if (zx_channel_create(0, &session, &session_remote) != ZX_OK) {
         return -1;
     }
 
-    if (fuchsia_device_manager_ExternalControllerOpenVirtcon(dmctl, h1) < 0) {
+    zx_status_t remote_status = ZX_OK;
+    zx_status_t status = fuchsia_virtualconsole_SessionManagerCreateSession(session_manager,
+                                                                            session_remote,
+                                                                            &remote_status);
+    if (status != ZX_OK || remote_status != ZX_OK) {
+        fprintf(stderr, "run-vc: failed to create session: local: %s remote: %s\n",
+                zx_status_get_string(status),
+                zx_status_get_string(remote_status));
         return -1;
     }
-    h1 = ZX_HANDLE_INVALID;
-    zx_handle_close(dmctl);
-
-    zx_object_wait_one(h0, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
-                       ZX_TIME_INFINITE, NULL);
-
-    uint32_t type = 0;
-    zx_handle_t handle = ZX_HANDLE_INVALID;
-    uint32_t dcount = 0, hcount = 0;
-    if (zx_channel_read(h0, 0, &type, &handle, sizeof(type), 1, &dcount, &hcount) != ZX_OK) {
+    if (session == ZX_HANDLE_INVALID) {
+        fprintf(stderr, "run-vc: Received invalid handle from session manager!\n");
         return -1;
     }
-    if (dcount / sizeof(uint32_t) != hcount) {
-        return -1;
-    }
-    zx_handle_close(h0);
 
     // start shell if no arguments
     if (argc == 1) {
@@ -108,20 +86,20 @@
     }
 
     uint32_t flags = FDIO_SPAWN_CLONE_ALL & ~FDIO_SPAWN_CLONE_STDIO;
+    uint32_t type = PA_HND(PA_FD, FDIO_FLAG_USE_FOR_STDIO);
 
     fdio_spawn_action_t actions[2] = {
         {.action = FDIO_SPAWN_ACTION_SET_NAME, .name = {.data = pname}},
-        {.action = FDIO_SPAWN_ACTION_ADD_HANDLE, .h = {.id = type, .handle = handle}},
+        {.action = FDIO_SPAWN_ACTION_ADD_HANDLE, .h = {.id = type, .handle = session}},
     };
 
     char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
     status = fdio_spawn_etc(ZX_HANDLE_INVALID, flags, argv[0], argv,
-                            NULL, 1 + hcount, actions, NULL, err_msg);
+                            NULL, countof(actions), actions, NULL, err_msg);
     if (status != ZX_OK) {
         fprintf(stderr, "error %d (%s) launching: %s\n", status,
                 zx_status_get_string(status), err_msg);
         return -1;
     }
-
     return 0;
 }