[devmgr] Reorganise main.cpp

Arrange all functions within a namespace together, and reduce the number
of functions that are defined within the devmgr.h header.

This makes it easier to understand which namespace a function is in, and
to reduce the API surface of the main.cpp file.

Test: Booted up Fuchsia on a NUC.
Change-Id: Ibe8a94cf9d221354e9047e115a430cdd65b7235d
diff --git a/system/core/devmgr/devmgr/coordinator.cpp b/system/core/devmgr/devmgr/coordinator.cpp
index 71f06e9..8280b48 100644
--- a/system/core/devmgr/devmgr/coordinator.cpp
+++ b/system/core/devmgr/devmgr/coordinator.cpp
@@ -62,8 +62,6 @@
 
 namespace devmgr {
 
-extern zx_handle_t virtcon_open;
-
 uint32_t log_flags = LOG_ERROR | LOG_INFO;
 
 bool dc_asan_drivers = false;
diff --git a/system/core/devmgr/devmgr/coordinator.h b/system/core/devmgr/devmgr/coordinator.h
index a61aaef..15e44de 100644
--- a/system/core/devmgr/devmgr/coordinator.h
+++ b/system/core/devmgr/devmgr/coordinator.h
@@ -535,6 +535,7 @@
 };
 
 void coordinator_setup(Coordinator* coordinator, DevmgrArgs args);
+void devmgr_set_bootdata(zx::unowned_vmo vmo);
 
 using DriverLoadCallback = fit::function<void(Driver* driver, const char* version)>;
 
diff --git a/system/core/devmgr/devmgr/devmgr.h b/system/core/devmgr/devmgr/devmgr.h
index c61526f..5f53e41 100644
--- a/system/core/devmgr/devmgr/devmgr.h
+++ b/system/core/devmgr/devmgr/devmgr.h
@@ -21,31 +21,24 @@
 struct Device;
 struct Devnode;
 
+extern zx_handle_t virtcon_open;
+
+// Initializes a devfs directory from |device|.
 void devfs_init(Device* device, async_dispatcher_t* dispatcher);
+
+// Watches the devfs directory |dn|, and sends events to |watcher|.
 zx_status_t devfs_watch(Devnode* dn, zx::channel h, uint32_t mask);
 
-void devmgr_svc_init(bool require_system);
-void devmgr_vfs_init();
-void devmgr_set_bootdata(zx::unowned_vmo vmo);
-
-zx_status_t devmgr_load_file(const char* path, zx::vmo* out_vmo, uint32_t* out_size);
-zx_status_t devmgr_launch_load(void* ctx, launchpad_t* lp, const char* file);
-
-bool secondary_bootfs_ready();
-
-void fshost_start();
-
-zx::job get_sysinfo_job_root();
-
-void devmgr_disable_appmgr_services();
-
 // Borrows the channel connected to the root of devfs.
 zx::unowned_channel devfs_root_borrow();
 
 // Clones the channel connected to the root of devfs.
 zx::channel devfs_root_clone();
 
+// Tells VFS to exit by shutting down the fshost.
 void devmgr_vfs_exit();
+
 zx_handle_t get_root_resource();
+zx::job get_sysinfo_job_root();
 
 } // namespace devmgr
diff --git a/system/core/devmgr/devmgr/main.cpp b/system/core/devmgr/devmgr/main.cpp
index ffee178..9120bb7 100644
--- a/system/core/devmgr/devmgr/main.cpp
+++ b/system/core/devmgr/devmgr/main.cpp
@@ -107,15 +107,37 @@
     return status;
 }
 
+zx_status_t devmgr_launch_load(void* ctx, launchpad_t* lp, const char* file) {
+    return launchpad_load_from_file(lp, file);
+}
+
 void do_autorun(const char* name, const char* env) {
     const char* cmd = getenv(env);
     if (cmd != nullptr) {
-        devmgr::devmgr_launch_cmdline(env, g_handles.svc_job, name,
-                                      &devmgr::devmgr_launch_load, nullptr, cmd,
-                                      nullptr, nullptr, 0, nullptr, FS_ALL);
+        devmgr::devmgr_launch_cmdline(env, g_handles.svc_job, name, &devmgr_launch_load, nullptr,
+                                      cmd, nullptr, nullptr, 0, nullptr, FS_ALL);
     }
 }
 
+// Get the root resource from the startup handle.  Not receiving the startup
+// handle is logged, but not fatal.  In test environments, it would not be
+// present.
+void fetch_root_resource() {
+    // Read the root resource out of its channel
+    zx::channel root_resource_channel(
+        zx_take_startup_handle(DEVMGR_LAUNCHER_ROOT_RESOURCE_CHANNEL_HND));
+    if (!root_resource_channel.is_valid()) {
+        printf("devmgr: did not receive root resource channel\n");
+        return;
+    }
+    uint32_t actual_handles = 0;
+    zx_status_t status = root_resource_channel.read(0, nullptr, 0, nullptr,
+                                                    g_handles.root_resource.reset_and_get_address(),
+                                                    1, &actual_handles);
+    ZX_ASSERT_MSG(status == ZX_OK && actual_handles == 1,
+                  "devmgr: did not receive root resource: %s\n", zx_status_get_string(status));
+}
+
 int fuchsia_starter(void* arg) {
     auto coordinator = static_cast<devmgr::Coordinator*>(arg);
     bool appmgr_started = false;
@@ -166,11 +188,9 @@
                 appmgr_ids[appmgr_hnd_count] = PA_DIRECTORY_REQUEST;
                 appmgr_hnd_count++;
             }
-            devmgr::devmgr_launch(g_handles.fuchsia_job, "appmgr",
-                                  &devmgr::devmgr_launch_load, nullptr,
-                                  fbl::count_of(argv_appmgr), argv_appmgr, nullptr, -1,
-                                  appmgr_hnds, appmgr_ids, appmgr_hnd_count,
-                                  nullptr, FS_FOR_APPMGR);
+            devmgr::devmgr_launch(g_handles.fuchsia_job, "appmgr", &devmgr_launch_load, nullptr,
+                                  fbl::count_of(argv_appmgr), argv_appmgr, nullptr, -1, appmgr_hnds,
+                                  appmgr_ids, appmgr_hnd_count, nullptr, FS_FOR_APPMGR);
             appmgr_started = true;
         }
         if (!autorun_started) {
@@ -216,8 +236,7 @@
     }
 
     const char* argv_sh[] = {"/boot/bin/sh"};
-    devmgr::devmgr_launch(g_handles.svc_job, "sh:console",
-                          &devmgr::devmgr_launch_load, nullptr,
+    devmgr::devmgr_launch(g_handles.svc_job, "sh:console", &devmgr_launch_load, nullptr,
                           fbl::count_of(argv_sh), argv_sh, envp, fd.release(), nullptr, nullptr, 0,
                           nullptr, FS_ALL);
     return 0;
@@ -239,7 +258,7 @@
     launchpad_t* lp;
     launchpad_create(job_copy.get(), name, &lp);
 
-    status = devmgr::devmgr_launch_load(nullptr, lp, argv[0]);
+    status = launchpad_load_from_file(lp, argv[0]);
     if (status != ZX_OK) {
         launchpad_abort(lp, status, "cannot load file");
     }
@@ -319,23 +338,204 @@
     return ZX_OK;
 }
 
-} // namespace
+zx_status_t svchost_start(bool require_system) {
+    printf("devmgr: svc init\n");
 
-namespace devmgr {
+    zx::channel dir_request;
+    zx::debuglog logger;
+    zx::channel appmgr_svc_req;
+    zx::channel appmgr_svc;
 
-zx_handle_t virtcon_open;
+    zx_status_t status = zx::channel::create(0, &dir_request, &g_handles.svchost_outgoing);
+    if (status != ZX_OK) {
+        return status;
+    }
 
-zx_handle_t get_root_resource() {
-    return g_handles.root_resource.get();
+    status = zx::debuglog::create(zx::resource(), 0, &logger);
+    if (status != ZX_OK) {
+        return status;
+    }
+
+    status = zx::channel::create(0, &appmgr_svc_req, &appmgr_svc);
+    if (status != ZX_OK) {
+        return status;
+    }
+
+    status = fdio_service_connect_at(g_handles.appmgr_client.get(), "svc", appmgr_svc_req.release());
+    if (status != ZX_OK) {
+        return status;
+    }
+
+    const char* name = "svchost";
+    const char* argv[2] = {
+        "/boot/bin/svchost",
+        require_system ? "--require-system" : nullptr,
+    };
+    int argc = require_system ? 2 : 1;
+
+    zx::job svc_job_copy;
+    status = g_handles.svc_job.duplicate(
+        ZX_RIGHTS_BASIC | ZX_RIGHT_MANAGE_JOB | ZX_RIGHT_MANAGE_PROCESS, &svc_job_copy);
+    if (status != ZX_OK) {
+        return status;
+    }
+
+    zx::job root_job_copy;
+    status = g_handles.root_job->duplicate(
+        ZX_RIGHTS_BASIC | ZX_RIGHTS_IO | ZX_RIGHTS_PROPERTY | ZX_RIGHT_ENUMERATE, &root_job_copy);
+    if (status != ZX_OK) {
+        return status;
+    }
+
+    launchpad_t* lp = nullptr;
+    launchpad_create(svc_job_copy.get(), name, &lp);
+    launchpad_load_from_file(lp, argv[0]);
+    launchpad_set_args(lp, argc, argv);
+    launchpad_add_handle(lp, dir_request.release(), PA_DIRECTORY_REQUEST);
+    launchpad_add_handle(lp, logger.release(), PA_HND(PA_FDIO_LOGGER, FDIO_FLAG_USE_FOR_STDIO));
+
+    // Remove once svchost hosts the tracelink service itself.
+    launchpad_add_handle(lp, appmgr_svc.release(), PA_HND(PA_USER0, 0));
+
+    // Give svchost a restricted root job handle. svchost is already a privileged system service
+    // as it controls system-wide process launching. With the root job it can consolidate a few
+    // services such as crashsvc and the profile service.
+    launchpad_add_handle(lp, root_job_copy.release(), PA_HND(PA_USER0, 1));
+
+    const char* errmsg = nullptr;
+    if ((status = launchpad_go(lp, nullptr, &errmsg)) < 0) {
+        printf("devmgr: launchpad %s (%s) failed: %s: %d\n",
+               argv[0], name, errmsg, status);
+    } else {
+        printf("devmgr: launch %s (%s) OK\n", argv[0], name);
+    }
+    return ZX_OK;
 }
 
-zx::job get_sysinfo_job_root() {
-    zx::job h;
-    //TODO: limit to enumerate rights
-    if (g_handles.root_job->duplicate(ZX_RIGHT_SAME_RIGHTS, &h) < 0) {
-        return zx::job();
+void fshost_start() {
+    // assemble handles to pass down to fshost
+    zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
+    uint32_t types[fbl::count_of(handles)];
+    size_t n = 0;
+    zx_handle_t ldsvc;
+
+    // pass / and /svc handles to fsboot
+    if (zx_channel_create(0, g_handles.fs_root.reset_and_get_address(), &handles[0]) == ZX_OK) {
+        types[n++] = PA_HND(PA_USER0, 0);
+    }
+    if ((handles[n] = devmgr::fs_clone("svc").release()) != ZX_HANDLE_INVALID) {
+        types[n++] = PA_HND(PA_USER0, 2);
+    }
+    if (zx_channel_create(0, &ldsvc, &handles[n]) == ZX_OK) {
+        types[n++] = PA_HND(PA_USER0, 3);
     } else {
-        return h;
+        ldsvc = ZX_HANDLE_INVALID;
+    }
+
+    // pass fuchsia start event to fshost
+    zx::event fshost_event_duplicate;
+    if (g_handles.fshost_event.duplicate(ZX_RIGHT_SAME_RIGHTS, &fshost_event_duplicate) == ZX_OK) {
+        handles[n] = fshost_event_duplicate.release();
+        types[n++] = PA_HND(PA_USER1, 0);
+    }
+
+    // pass bootdata VMOs to fshost
+    for (uint32_t m = 0; n < fbl::count_of(handles); m++) {
+        uint32_t type = PA_HND(PA_VMO_BOOTDATA, m);
+        if ((handles[n] = zx_take_startup_handle(type)) != ZX_HANDLE_INVALID) {
+            devmgr::devmgr_set_bootdata(zx::unowned_vmo(handles[n]));
+            types[n++] = type;
+        } else {
+            break;
+        }
+    }
+
+    // pass VDSO VMOS to fshost
+    for (uint32_t m = 0; n < fbl::count_of(handles); m++) {
+        uint32_t type = PA_HND(PA_VMO_VDSO, m);
+        if (m == 0) {
+            // By this point, launchpad has already moved PA_HND(PA_VMO_VDSO, 0) into a static.
+            handles[n] = ZX_HANDLE_INVALID;
+            launchpad_get_vdso_vmo(&handles[n]);
+        } else {
+            handles[n] = zx_take_startup_handle(type);
+        }
+
+        if (handles[n] != ZX_HANDLE_INVALID) {
+            types[n++] = type;
+        } else {
+            break;
+        }
+    }
+
+    // pass KERNEL FILE VMOS to fsboot
+    for (uint32_t m = 0; n < fbl::count_of(handles); m++) {
+        uint32_t type = PA_HND(PA_VMO_KERNEL_FILE, m);
+        if ((handles[n] = zx_take_startup_handle(type)) != ZX_HANDLE_INVALID) {
+            types[n++] = type;
+        } else {
+            break;
+        }
+    }
+
+    const char* argv[] = {"/boot/bin/fshost", "--netboot"};
+    int argc = (devmgr::getenv_bool("netsvc.netboot", false) ||
+                devmgr::getenv_bool("zircon.system.disable-automount", false))
+                   ? 2
+                   : 1;
+
+    // Pass zircon.system.* options to the fshost as environment variables
+    const char* envp[16];
+    unsigned envc = 0;
+    char** e = environ;
+    while (*e && (envc < fbl::count_of(envp))) {
+        if (!strncmp(*e, "zircon.system", strlen("zircon.system"))) {
+            envp[envc++] = *e;
+        }
+        e++;
+    }
+    envp[envc] = nullptr;
+
+    devmgr::devmgr_launch(g_handles.svc_job, "fshost", &devmgr_launch_load, nullptr, argc, argv,
+                          envp, -1, handles, types, n, nullptr, FS_BOOT | FS_DEV);
+
+    // switch to system loader service provided by fshost
+    zx_handle_close(dl_set_loader_service(ldsvc));
+}
+
+zx::channel bootfs_root_clone() {
+    zx::channel boot, boot_remote;
+    zx_status_t status = zx::channel::create(0, &boot, &boot_remote);
+    if (status != ZX_OK) {
+        return zx::channel();
+    }
+
+    fdio_ns_t* ns;
+    status = fdio_ns_get_installed(&ns);
+    ZX_ASSERT(status == ZX_OK);
+    status = fdio_ns_connect(ns, "/boot", ZX_FS_RIGHT_READABLE, boot_remote.release());
+    if (status != ZX_OK) {
+        return zx::channel();
+    }
+    return boot;
+}
+
+void devmgr_vfs_init() {
+    printf("devmgr: vfs init\n");
+
+    fdio_ns_t* ns;
+    zx_status_t r;
+    r = fdio_ns_get_installed(&ns);
+    ZX_ASSERT_MSG(r == ZX_OK, "devmgr: cannot get namespace: %s\n", zx_status_get_string(r));
+    r = fdio_ns_bind(ns, "/dev", devmgr::fs_clone("dev").release());
+    ZX_ASSERT_MSG(r == ZX_OK, "devmgr: cannot bind /dev to namespace: %s\n",
+                  zx_status_get_string(r));
+
+    // Start fshost before binding /system, since it publishes it.
+    fshost_start();
+
+    if ((r = fdio_ns_bind(ns, "/system", devmgr::fs_clone("system").release())) != ZX_OK) {
+        printf("devmgr: cannot bind /system to namespace: %d\n", r);
     }
 }
 
@@ -356,17 +556,17 @@
     char vcmd[64];
     bool netboot = false;
     bool vruncmd = false;
-    if (!getenv_bool("netsvc.disable", false)) {
+    if (!devmgr::getenv_bool("netsvc.disable", false)) {
         const char* args[] = {"/boot/bin/netsvc", nullptr, nullptr, nullptr, nullptr, nullptr};
         int argc = 1;
 
-        if (getenv_bool("netsvc.netboot", false)) {
+        if (devmgr::getenv_bool("netsvc.netboot", false)) {
             args[argc++] = "--netboot";
             netboot = true;
             vruncmd = true;
         }
 
-        if (getenv_bool("netsvc.advertise", true)) {
+        if (devmgr::getenv_bool("netsvc.advertise", true)) {
             args[argc++] = "--advertise";
         }
 
@@ -382,9 +582,8 @@
         }
 
         zx::process proc;
-        if (devmgr_launch(g_handles.svc_job, "netsvc",
-                          &devmgr_launch_load, nullptr, argc, args,
-                          nullptr, -1, nullptr, nullptr, 0, &proc, FS_ALL) == ZX_OK) {
+        if (devmgr::devmgr_launch(g_handles.svc_job, "netsvc", &devmgr_launch_load, nullptr, argc,
+                                  args, nullptr, -1, nullptr, nullptr, 0, &proc, FS_ALL) == ZX_OK) {
             if (vruncmd) {
                 zx_info_handle_basic_t info = {};
                 proc.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
@@ -398,7 +597,7 @@
     }
 
     auto coordinator = static_cast<devmgr::Coordinator*>(arg);
-    if (!getenv_bool("virtcon.disable", false)) {
+    if (!devmgr::getenv_bool("virtcon.disable", false)) {
         // pass virtcon.* options along
         const char* envp[16];
         unsigned envc = 0;
@@ -417,7 +616,7 @@
         uint32_t types[2];
         zx_handle_t handles[2] = {ZX_HANDLE_INVALID, ZX_HANDLE_INVALID};
 
-        if (zx_channel_create(0, &handles[0], &virtcon_open) == ZX_OK) {
+        if (zx_channel_create(0, &handles[0], &devmgr::virtcon_open) == ZX_OK) {
             types[handle_count++] = PA_HND(PA_USER0, 0);
         }
 
@@ -430,15 +629,15 @@
         }
 
         const char* args[] = {"/boot/bin/virtual-console", "--shells", num_shells, "--run", vcmd};
-        devmgr_launch(g_handles.svc_job, "virtual-console", &devmgr_launch_load, nullptr,
-                      vruncmd ? 5 : 3, args, envp, -1, handles, types, handle_count, nullptr,
-                      FS_ALL);
+        devmgr::devmgr_launch(g_handles.svc_job, "virtual-console", &devmgr_launch_load, nullptr,
+                              vruncmd ? 5 : 3, args, envp, -1, handles, types, handle_count,
+                              nullptr, FS_ALL);
     }
 
     const char* epoch = getenv("devmgr.epoch");
     if (epoch) {
         zx_time_t offset = ZX_SEC(atoi(epoch));
-        zx_clock_adjust(get_root_resource(), ZX_CLOCK_UTC, offset);
+        zx_clock_adjust(devmgr::get_root_resource(), ZX_CLOCK_UTC, offset);
     }
 
     do_autorun("autorun:boot", "zircon.autorun.boot");
@@ -452,29 +651,6 @@
     return 0;
 }
 
-// Get the root resource from the startup handle.  Not receiving the startup
-// handle is logged, but not fatal.  In test environments, it would not be
-// present.
-void fetch_root_resource() {
-    // Read the root resource out of its channel
-    zx::channel root_resource_channel(
-        zx_take_startup_handle(DEVMGR_LAUNCHER_ROOT_RESOURCE_CHANNEL_HND));
-    if (!root_resource_channel.is_valid()) {
-        printf("devmgr: did not receive root resource channel\n");
-        return;
-    }
-    uint32_t actual_handles = 0;
-    zx_status_t status = root_resource_channel.read(0, nullptr, 0, nullptr,
-                                                    g_handles.root_resource.reset_and_get_address(),
-                                                    1, &actual_handles);
-    ZX_ASSERT_MSG(status == ZX_OK && actual_handles == 1,
-                  "devmgr: did not receive root resource: %s\n", zx_status_get_string(status));
-}
-
-} // namespace devmgr
-
-namespace {
-
 void ParseArgs(int argc, char** argv, devmgr::DevmgrArgs* out) {
     enum {
         kDriverSearchPath,
@@ -554,192 +730,22 @@
 
 } // namespace
 
-int main(int argc, char** argv) {
-    printf("devmgr: main()\n");
-
-
-    devmgr::DevmgrArgs args;
-    ParseArgs(argc, argv, &args);
-
-    devmgr::fetch_root_resource();
-    g_handles.root_job = zx::job::default_job();
-
-    zx::job devhost_job;
-    zx_status_t status = CreateDevhostJob(*g_handles.root_job, &devhost_job);
-    if (status != ZX_OK) {
-        printf("unable to create devhost job\n");
-        return 1;
-    }
-
-    async::Loop loop(&kAsyncLoopConfigAttachToThread);
-    bool require_system = devmgr::getenv_bool("devmgr.require-system", false);
-
-    devmgr::Coordinator coordinator(std::move(devhost_job), loop.dispatcher(), require_system);
-    devmgr::devfs_init(&coordinator.root_device(), loop.dispatcher());
-
-    // Check if whatever launched devmgr gave a channel to be connected to /dev.
-    // This is for use in tests to let the test environment see devfs.
-    zx::channel devfs_client(zx_take_startup_handle(DEVMGR_LAUNCHER_DEVFS_ROOT_HND));
-    if (devfs_client.is_valid()) {
-        fdio_service_clone_to(devmgr::devfs_root_borrow()->get(), devfs_client.release());
-    }
-
-    g_handles.root_job->set_property(ZX_PROP_NAME, "root", 4);
-
-    status = zx::job::create(*g_handles.root_job, 0u, &g_handles.svc_job);
-    if (status != ZX_OK) {
-        printf("unable to create service job\n");
-        return 1;
-    }
-    g_handles.svc_job.set_property(ZX_PROP_NAME, "zircon-services", 16);
-
-    if (fuchsia_create_job() != ZX_OK) {
-        return 1;
-    }
-
-    zx::channel::create(0, &g_handles.appmgr_client, &g_handles.appmgr_server);
-    zx::event::create(0, &g_handles.fshost_event);
-
-    char** e = environ;
-    while (*e) {
-        printf("cmdline: %s\n", *e++);
-    }
-
-    devmgr::devmgr_svc_init(require_system);
-    devmgr::devmgr_vfs_init();
-
-    // if we're not a full fuchsia build, no point to set up appmgr services
-    // which will just cause things attempting to access it to block until
-    // we give up on the appmgr 10s later
-    if (!require_system) {
-        devmgr::devmgr_disable_appmgr_services();
-    }
-
-    thrd_t t;
-    if ((thrd_create_with_name(&t, pwrbtn_monitor_starter, nullptr,
-                               "pwrbtn-monitor-starter")) == thrd_success) {
-        thrd_detach(t);
-    }
-
-    start_console_shell();
-
-    if ((thrd_create_with_name(&t, devmgr::service_starter, &coordinator, "service-starter")) ==
-        thrd_success) {
-        thrd_detach(t);
-    }
-
-    fbl::unique_ptr<devmgr::DevhostLoaderService> loader_service;
-    if (devmgr::getenv_bool("devmgr.devhost.strict-linking", false)) {
-        status = devmgr::DevhostLoaderService::Create(loop.dispatcher(), &loader_service);
-        if (status != ZX_OK) {
-            return 1;
-        }
-        coordinator.set_loader_service(loader_service.get());
-    }
-
-    coordinator_setup(&coordinator, std::move(args));
-
-    status = loop.Run();
-    fprintf(stderr, "devmgr: coordinator exited unexpectedly: %d\n", status);
-    return status == ZX_OK ? 0 : 1;
-}
-
 namespace devmgr {
 
-void fshost_start() {
-    // assemble handles to pass down to fshost
-    zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
-    uint32_t types[fbl::count_of(handles)];
-    size_t n = 0;
-    zx_handle_t ldsvc;
+zx_handle_t virtcon_open;
 
-    // pass / and /svc handles to fsboot
-    if (zx_channel_create(0, g_handles.fs_root.reset_and_get_address(), &handles[0]) == ZX_OK) {
-        types[n++] = PA_HND(PA_USER0, 0);
-    }
-    if ((handles[n] = fs_clone("svc").release()) != ZX_HANDLE_INVALID) {
-        types[n++] = PA_HND(PA_USER0, 2);
-    }
-    if (zx_channel_create(0, &ldsvc, &handles[n]) == ZX_OK) {
-        types[n++] = PA_HND(PA_USER0, 3);
-    } else {
-        ldsvc = ZX_HANDLE_INVALID;
-    }
-
-    // pass fuchsia start event to fshost
-    zx::event fshost_event_duplicate;
-    if (g_handles.fshost_event.duplicate(ZX_RIGHT_SAME_RIGHTS, &fshost_event_duplicate) == ZX_OK) {
-        handles[n] = fshost_event_duplicate.release();
-        types[n++] = PA_HND(PA_USER1, 0);
-    }
-
-    // pass bootdata VMOs to fshost
-    for (uint32_t m = 0; n < fbl::count_of(handles); m++) {
-        uint32_t type = PA_HND(PA_VMO_BOOTDATA, m);
-        if ((handles[n] = zx_take_startup_handle(type)) != ZX_HANDLE_INVALID) {
-            devmgr_set_bootdata(zx::unowned_vmo(handles[n]));
-            types[n++] = type;
-        } else {
-            break;
-        }
-    }
-
-    // pass VDSO VMOS to fshost
-    for (uint32_t m = 0; n < fbl::count_of(handles); m++) {
-        uint32_t type = PA_HND(PA_VMO_VDSO, m);
-        if (m == 0) {
-            // By this point, launchpad has already moved PA_HND(PA_VMO_VDSO, 0) into a static.
-            handles[n] = ZX_HANDLE_INVALID;
-            launchpad_get_vdso_vmo(&handles[n]);
-        } else {
-            handles[n] = zx_take_startup_handle(type);
-        }
-
-        if (handles[n] != ZX_HANDLE_INVALID) {
-            types[n++] = type;
-        } else {
-            break;
-        }
-    }
-
-    // pass KERNEL FILE VMOS to fsboot
-    for (uint32_t m = 0; n < fbl::count_of(handles); m++) {
-        uint32_t type = PA_HND(PA_VMO_KERNEL_FILE, m);
-        if ((handles[n] = zx_take_startup_handle(type)) != ZX_HANDLE_INVALID) {
-            types[n++] = type;
-        } else {
-            break;
-        }
-    }
-
-    const char* argv[] = {"/boot/bin/fshost", "--netboot"};
-    int argc = (getenv_bool("netsvc.netboot", false) ||
-                getenv_bool("zircon.system.disable-automount", false))
-                   ? 2
-                   : 1;
-
-    // Pass zircon.system.* options to the fshost as environment variables
-    const char* envp[16];
-    unsigned envc = 0;
-    char** e = environ;
-    while (*e && (envc < fbl::count_of(envp))) {
-        if (!strncmp(*e, "zircon.system", strlen("zircon.system"))) {
-            envp[envc++] = *e;
-        }
-        e++;
-    }
-    envp[envc] = nullptr;
-
-    devmgr_launch(g_handles.svc_job, "fshost",
-                  &devmgr_launch_load, nullptr, argc, argv, envp, -1,
-                  handles, types, n, nullptr, FS_BOOT | FS_DEV);
-
-    // switch to system loader service provided by fshost
-    zx_handle_close(dl_set_loader_service(ldsvc));
+zx_handle_t get_root_resource() {
+    return g_handles.root_resource.get();
 }
 
-zx_status_t devmgr_launch_load(void* ctx, launchpad_t* lp, const char* file) {
-    return launchpad_load_from_file(lp, file);
+zx::job get_sysinfo_job_root() {
+    zx::job h;
+    //TODO: limit to enumerate rights
+    if (g_handles.root_job->duplicate(ZX_RIGHT_SAME_RIGHTS, &h) < 0) {
+        return zx::job();
+    } else {
+        return h;
+    }
 }
 
 void devmgr_vfs_exit() {
@@ -754,23 +760,6 @@
     }
 }
 
-zx::channel bootfs_root_clone() {
-    zx::channel boot, boot_remote;
-    zx_status_t status = zx::channel::create(0, &boot, &boot_remote);
-    if (status != ZX_OK) {
-        return zx::channel();
-    }
-
-    fdio_ns_t* ns;
-    status = fdio_ns_get_installed(&ns);
-    ZX_ASSERT(status == ZX_OK);
-    status = fdio_ns_connect(ns, "/boot", ZX_FS_RIGHT_READABLE, boot_remote.release());
-    if (status != ZX_OK) {
-        return zx::channel();
-    }
-    return boot;
-}
-
 zx::channel fs_clone(const char* path) {
     if (!strcmp(path, "dev")) {
         return devfs_root_clone();
@@ -801,104 +790,98 @@
     return h0;
 }
 
-void devmgr_vfs_init() {
-    printf("devmgr: vfs init\n");
-
-    fdio_ns_t* ns;
-    zx_status_t r;
-    r = fdio_ns_get_installed(&ns);
-    ZX_ASSERT_MSG(r == ZX_OK, "devmgr: cannot get namespace: %s\n", zx_status_get_string(r));
-    r = fdio_ns_bind(ns, "/dev", fs_clone("dev").release());
-    ZX_ASSERT_MSG(r == ZX_OK, "devmgr: cannot bind /dev to namespace: %s\n",
-                  zx_status_get_string(r));
-
-    // Start fshost before binding /system, since it publishes it.
-    fshost_start();
-
-    if ((r = fdio_ns_bind(ns, "/system", fs_clone("system").release())) != ZX_OK) {
-        printf("devmgr: cannot bind /system to namespace: %d\n", r);
-    }
-}
-
-zx_status_t svchost_start(bool require_system) {
-    zx::channel dir_request;
-    zx::debuglog logger;
-    zx::channel appmgr_svc_req;
-    zx::channel appmgr_svc;
-
-    zx_status_t status = zx::channel::create(0, &dir_request, &g_handles.svchost_outgoing);
-    if (status != ZX_OK) {
-        return status;
-    }
-
-    status = zx::debuglog::create(zx::resource(), 0, &logger);
-    if (status != ZX_OK) {
-        return status;
-    }
-
-    status = zx::channel::create(0, &appmgr_svc_req, &appmgr_svc);
-    if (status != ZX_OK) {
-        return status;
-    }
-
-    status = fdio_service_connect_at(g_handles.appmgr_client.get(), "svc", appmgr_svc_req.release());
-    if (status != ZX_OK) {
-        return status;
-    }
-
-    const char* name = "svchost";
-    const char* argv[2] = {
-        "/boot/bin/svchost",
-        require_system ? "--require-system" : nullptr,
-    };
-    int argc = require_system ? 2 : 1;
-
-    zx::job svc_job_copy;
-    status = g_handles.svc_job.duplicate(
-        ZX_RIGHTS_BASIC | ZX_RIGHT_MANAGE_JOB | ZX_RIGHT_MANAGE_PROCESS, &svc_job_copy);
-    if (status != ZX_OK) {
-        return status;
-    }
-
-    zx::job root_job_copy;
-    status = g_handles.root_job->duplicate(
-        ZX_RIGHTS_BASIC | ZX_RIGHTS_IO | ZX_RIGHTS_PROPERTY | ZX_RIGHT_ENUMERATE, &root_job_copy);
-    if (status != ZX_OK) {
-        return status;
-    }
-
-    launchpad_t* lp = nullptr;
-    launchpad_create(svc_job_copy.get(), name, &lp);
-    launchpad_load_from_file(lp, argv[0]);
-    launchpad_set_args(lp, argc, argv);
-    launchpad_add_handle(lp, dir_request.release(), PA_DIRECTORY_REQUEST);
-    launchpad_add_handle(lp, logger.release(), PA_HND(PA_FDIO_LOGGER, FDIO_FLAG_USE_FOR_STDIO));
-
-    // Remove once svchost hosts the tracelink service itself.
-    launchpad_add_handle(lp, appmgr_svc.release(), PA_HND(PA_USER0, 0));
-
-    // Give svchost a restricted root job handle. svchost is already a privileged system service
-    // as it controls system-wide process launching. With the root job it can consolidate a few
-    // services such as crashsvc and the profile service.
-    launchpad_add_handle(lp, root_job_copy.release(), PA_HND(PA_USER0, 1));
-
-    const char* errmsg = nullptr;
-    if ((status = launchpad_go(lp, nullptr, &errmsg)) < 0) {
-        printf("devmgr: launchpad %s (%s) failed: %s: %d\n",
-               argv[0], name, errmsg, status);
-    } else {
-        printf("devmgr: launch %s (%s) OK\n", argv[0], name);
-    }
-    return ZX_OK;
-}
-
-void devmgr_svc_init(bool require_system) {
-    printf("devmgr: svc init\n");
-
-    zx_status_t status = svchost_start(require_system);
-    if (status != ZX_OK) {
-        printf("devmgr_svc_init failed %s\n", zx_status_get_string(status));
-    }
-}
-
 } // namespace devmgr
+
+int main(int argc, char** argv) {
+    printf("devmgr: main()\n");
+
+    devmgr::DevmgrArgs args;
+    ParseArgs(argc, argv, &args);
+
+    fetch_root_resource();
+    g_handles.root_job = zx::job::default_job();
+
+    zx::job devhost_job;
+    zx_status_t status = CreateDevhostJob(*g_handles.root_job, &devhost_job);
+    if (status != ZX_OK) {
+        printf("unable to create devhost job\n");
+        return 1;
+    }
+
+    async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
+    bool require_system = devmgr::getenv_bool("devmgr.require-system", false);
+
+    devmgr::Coordinator coordinator(std::move(devhost_job), loop.dispatcher(), require_system);
+    devmgr::devfs_init(&coordinator.root_device(), loop.dispatcher());
+
+    // Check if whatever launched devmgr gave a channel to be connected to /dev.
+    // This is for use in tests to let the test environment see devfs.
+    zx::channel devfs_client(zx_take_startup_handle(DEVMGR_LAUNCHER_DEVFS_ROOT_HND));
+    if (devfs_client.is_valid()) {
+        fdio_service_clone_to(devmgr::devfs_root_borrow()->get(), devfs_client.release());
+    }
+
+    g_handles.root_job->set_property(ZX_PROP_NAME, "root", 4);
+
+    status = zx::job::create(*g_handles.root_job, 0u, &g_handles.svc_job);
+    if (status != ZX_OK) {
+        fprintf(stderr, "devmgr: unable to create service job: %d\n", status);
+        return 1;
+    }
+    g_handles.svc_job.set_property(ZX_PROP_NAME, "zircon-services", 16);
+
+    status = fuchsia_create_job();
+    if (status != ZX_OK) {
+        return 1;
+    }
+
+    zx::channel::create(0, &g_handles.appmgr_client, &g_handles.appmgr_server);
+    zx::event::create(0, &g_handles.fshost_event);
+
+    char** e = environ;
+    while (*e) {
+        printf("cmdline: %s\n", *e++);
+    }
+
+    status = svchost_start(require_system);
+    if (status != ZX_OK) {
+        fprintf(stderr, "devmgr: failed to start svchost: %d", status);
+        return 1;
+    }
+
+    devmgr_vfs_init();
+
+    // If this is not a full Fuchsia build, do not setup appmgr services, as
+    // this will delay startup.
+    if (!require_system) {
+        devmgr::devmgr_disable_appmgr_services();
+    }
+
+    thrd_t t;
+    if ((thrd_create_with_name(&t, pwrbtn_monitor_starter, nullptr, "pwrbtn-monitor-starter")) ==
+        thrd_success) {
+        thrd_detach(t);
+    }
+
+    start_console_shell();
+
+    if ((thrd_create_with_name(&t, service_starter, &coordinator, "service-starter")) ==
+        thrd_success) {
+        thrd_detach(t);
+    }
+
+    fbl::unique_ptr<devmgr::DevhostLoaderService> loader_service;
+    if (devmgr::getenv_bool("devmgr.devhost.strict-linking", false)) {
+        status = devmgr::DevhostLoaderService::Create(loop.dispatcher(), &loader_service);
+        if (status != ZX_OK) {
+            return 1;
+        }
+        coordinator.set_loader_service(loader_service.get());
+    }
+
+    coordinator_setup(&coordinator, std::move(args));
+
+    status = loop.Run();
+    fprintf(stderr, "devmgr: coordinator exited unexpectedly: %d\n", status);
+    return status == ZX_OK ? 0 : 1;
+}
diff --git a/system/core/devmgr/shared/fdio.h b/system/core/devmgr/shared/fdio.h
index 3d785ee..cb1232b 100644
--- a/system/core/devmgr/shared/fdio.h
+++ b/system/core/devmgr/shared/fdio.h
@@ -51,6 +51,8 @@
     const zx_handle_t* handles, const uint32_t* types, size_t hcount,
     zx::process* proc_out, uint32_t flags);
 
+void devmgr_disable_appmgr_services();
+
 // The variable to set on the kernel command line to enable ld.so tracing
 // of the processes we launch.
 #define LDSO_TRACE_CMDLINE "ldso.trace"