[dash-launcher] Split launch code into a sub-module

Change-Id: I5581f02d9b7603faf1d9e8420d549c144d0546ea
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/692713
Reviewed-by: Adam Perry <adamperry@google.com>
Commit-Queue: Xyan Bhatnagar <xbhatnag@google.com>
Reviewed-by: Shai Barack <shayba@google.com>
diff --git a/src/sys/tools/dash-launcher/BUILD.gn b/src/sys/tools/dash-launcher/BUILD.gn
index 3fd5fbe..34a6760 100644
--- a/src/sys/tools/dash-launcher/BUILD.gn
+++ b/src/sys/tools/dash-launcher/BUILD.gn
@@ -36,6 +36,7 @@
   ]
 
   sources = [
+    "src/launch.rs",
     "src/main.rs",
     "src/socket.rs",
   ]
diff --git a/src/sys/tools/dash-launcher/src/launch.rs b/src/sys/tools/dash-launcher/src/launch.rs
new file mode 100644
index 0000000..ee3577f
--- /dev/null
+++ b/src/sys/tools/dash-launcher/src/launch.rs
@@ -0,0 +1,421 @@
+// Copyright 2022 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.
+
+use crate::socket;
+use fidl::{
+    endpoints::{ClientEnd, Proxy},
+    HandleBased,
+};
+use fidl_fuchsia_component as fcomp;
+use fidl_fuchsia_dash::LauncherError;
+use fidl_fuchsia_hardware_pty as pty;
+use fidl_fuchsia_io as fio;
+use fidl_fuchsia_process::{HandleInfo, LaunchInfo, LauncherMarker, NameInfo};
+use fidl_fuchsia_sys2 as fsys;
+use fuchsia_async as fasync;
+use fuchsia_component::client::connect_to_protocol;
+use fuchsia_component::server::ServiceFs;
+use fuchsia_runtime::{HandleInfo as HandleId, HandleType};
+use fuchsia_zircon as zx;
+use futures::prelude::*;
+use moniker::{RelativeMoniker, RelativeMonikerBase};
+use std::sync::Arc;
+use tracing::*;
+
+pub async fn launch_with_socket(
+    moniker: &str,
+    socket: zx::Socket,
+) -> Result<zx::Process, LauncherError> {
+    let pty = socket::spawn_pty_forwarder(socket).await?;
+    launch_with_pty(moniker, pty).await
+}
+
+pub async fn launch_with_pty(
+    moniker: &str,
+    pty: ClientEnd<pty::DeviceMarker>,
+) -> Result<zx::Process, LauncherError> {
+    let (stdin, stdout, stderr) = split_pty_into_handles(pty)?;
+    launch_with_handles(moniker, stdin, stdout, stderr).await
+}
+
+pub async fn launch_with_handles(
+    moniker: &str,
+    stdin: zx::Handle,
+    stdout: zx::Handle,
+    stderr: zx::Handle,
+) -> Result<zx::Process, LauncherError> {
+    // Process moniker
+    let moniker = RelativeMoniker::parse(&moniker).map_err(|_| LauncherError::BadMoniker)?;
+    if !moniker.up_path().is_empty() {
+        return Err(LauncherError::BadMoniker);
+    }
+    let moniker = moniker.to_string();
+
+    let launcher =
+        connect_to_protocol::<LauncherMarker>().map_err(|_| LauncherError::ProcessLauncher)?;
+
+    let query =
+        connect_to_protocol::<fsys::RealmQueryMarker>().map_err(|_| LauncherError::RealmQuery)?;
+
+    let instance_scope = InstanceScope::new(&query, &moniker).await?;
+
+    // The dash-launcher can be asked to launch multiple dash processes, each of which can
+    // make their own process hierarchies. This will look better topologically if we make a
+    // child job for each dash process.
+    let job =
+        fuchsia_runtime::job_default().create_child_job().map_err(|_| LauncherError::Internal)?;
+
+    let mut ns = create_dash_namespace(instance_scope.ns)?;
+    let mut handles = create_dash_handles(&job, stdin, stdout, stderr, instance_scope.lib_dir)?;
+
+    // -s: force input from stdin
+    // -i: force interactive
+    let args = vec!["-i".as_bytes(), "-s".as_bytes()];
+
+    // Set PATH to generic command-line tools and package-specific command-line tools
+    let env = vec!["PATH=/bin:/ns/pkg/bin".as_bytes()];
+
+    let mut info = create_launch_info(&moniker, &job).await?;
+
+    // Spawn the dash process
+    launcher.add_names(&mut ns.iter_mut()).map_err(|_| LauncherError::ProcessLauncher)?;
+    launcher.add_handles(&mut handles.iter_mut()).map_err(|_| LauncherError::ProcessLauncher)?;
+    launcher.add_args(&mut args.into_iter()).map_err(|_| LauncherError::ProcessLauncher)?;
+    launcher.add_environs(&mut env.into_iter()).map_err(|_| LauncherError::ProcessLauncher)?;
+    let (status, process) =
+        launcher.launch(&mut info).await.map_err(|_| LauncherError::ProcessLauncher)?;
+    zx::Status::ok(status).map_err(|_| LauncherError::ProcessLauncher)?;
+    let process = process.ok_or(LauncherError::ProcessLauncher)?;
+
+    // The job should be terminated when the dash process dies
+    job.set_critical(zx::JobCriticalOptions::empty(), &process)
+        .map_err(|_| LauncherError::Internal)?;
+
+    Ok(process)
+}
+
+fn get_bin_from_launcher_namespace() -> Result<ClientEnd<fio::DirectoryMarker>, LauncherError> {
+    let (bin_dir, server) = fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
+    fuchsia_fs::node::connect_in_namespace(
+        "/pkg/bin",
+        fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE,
+        server.into_channel(),
+    )
+    .map_err(|_| LauncherError::Internal)?;
+
+    Ok(bin_dir)
+}
+
+async fn create_launch_info(moniker: &str, job: &zx::Job) -> Result<LaunchInfo, LauncherError> {
+    // Load `/pkg/bin/sh` as an executable VMO and pass it to the Launcher
+    let dash_file = fuchsia_fs::file::open_in_namespace(
+        "/pkg/bin/sh",
+        fio::OpenFlags::RIGHT_EXECUTABLE | fio::OpenFlags::RIGHT_READABLE,
+    )
+    .map_err(|_| LauncherError::DashBinary)?;
+
+    let executable = dash_file
+        .get_backing_memory(
+            fio::VmoFlags::READ | fio::VmoFlags::EXECUTE | fio::VmoFlags::PRIVATE_CLONE,
+        )
+        .await
+        .map_err(|_| LauncherError::DashBinary)?
+        .map_err(|_| LauncherError::DashBinary)?;
+
+    let job_dup =
+        job.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(|_| LauncherError::Internal)?;
+
+    // Set a name that's easy to find
+    // if moniker is `./core/foo`, process name is `sh-core-foo`
+    let mut process_name = moniker.replace('/', "-");
+    process_name.remove(0);
+    let process_name = format!("sh{}", process_name);
+
+    Ok(LaunchInfo { name: process_name, job: job_dup, executable })
+}
+
+fn serve_dash_svc_dir() -> Result<ClientEnd<fio::DirectoryMarker>, LauncherError> {
+    // Serve a directory that only provides fuchsia.process.Launcher to dash
+    let (svc_dir, server_end) =
+        fidl::endpoints::create_endpoints().map_err(|_| LauncherError::Internal)?;
+
+    let mut fs = ServiceFs::new();
+    fs.add_proxy_service::<LauncherMarker, ()>();
+    fs.serve_connection(server_end.into_channel()).map_err(|_| LauncherError::Internal)?;
+
+    fasync::Task::spawn(async move {
+        fs.collect::<()>().await;
+    })
+    .detach();
+
+    Ok(svc_dir)
+}
+
+fn create_dash_namespace(instance_ns: Vec<NameInfo>) -> Result<Vec<NameInfo>, LauncherError> {
+    let mut ns = instance_ns;
+
+    // Add the dash-launcher `/pkg/bin` to dash as `/bin`
+    let bin_dir = get_bin_from_launcher_namespace()?;
+    ns.push(NameInfo { path: "/bin".to_string(), directory: bin_dir });
+
+    // Add a custom `/svc` directory to dash
+    let svc_dir = serve_dash_svc_dir()?;
+    ns.push(NameInfo { path: "/svc".to_string(), directory: svc_dir });
+
+    Ok(ns)
+}
+
+fn split_pty_into_handles(
+    pty: ClientEnd<pty::DeviceMarker>,
+) -> Result<(zx::Handle, zx::Handle, zx::Handle), LauncherError> {
+    let pty = pty.into_proxy().map_err(|_| LauncherError::Pty)?;
+
+    // Split the PTY into 3 channels (stdin, stdout, stderr)
+    let (stdout, to_pty_stdout) =
+        fidl::endpoints::create_endpoints::<pty::DeviceMarker>().map_err(|_| LauncherError::Pty)?;
+    let (stderr, to_pty_stderr) =
+        fidl::endpoints::create_endpoints::<pty::DeviceMarker>().map_err(|_| LauncherError::Pty)?;
+    let to_pty_stdout = to_pty_stdout.into_channel().into();
+    let to_pty_stderr = to_pty_stderr.into_channel().into();
+
+    // Clone the PTY to also be used for stdout and stderr
+    pty.clone(fio::OpenFlags::CLONE_SAME_RIGHTS, to_pty_stdout).map_err(|_| LauncherError::Pty)?;
+    pty.clone(fio::OpenFlags::CLONE_SAME_RIGHTS, to_pty_stderr).map_err(|_| LauncherError::Pty)?;
+
+    let stdin = pty.into_channel().map_err(|_| LauncherError::Pty)?.into_zx_channel().into_handle();
+    let stdout = stdout.into_handle();
+    let stderr = stderr.into_handle();
+
+    Ok((stdin, stdout, stderr))
+}
+
+fn create_dash_handles(
+    job: &zx::Job,
+    stdin: zx::Handle,
+    stdout: zx::Handle,
+    stderr: zx::Handle,
+    lib_dir: Option<fio::DirectoryProxy>,
+) -> Result<Vec<HandleInfo>, LauncherError> {
+    let stdin_handle = HandleInfo {
+        handle: stdin.into_handle(),
+        id: HandleId::new(HandleType::FileDescriptor, 0).as_raw(),
+    };
+
+    let stdout_handle = HandleInfo {
+        handle: stdout.into_handle(),
+        id: HandleId::new(HandleType::FileDescriptor, 1).as_raw(),
+    };
+
+    let stderr_handle = HandleInfo {
+        handle: stderr.into_handle(),
+        id: HandleId::new(HandleType::FileDescriptor, 2).as_raw(),
+    };
+
+    let job_dup =
+        job.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(|_| LauncherError::Internal)?;
+    let job_handle = HandleInfo {
+        handle: zx::Handle::from(job_dup),
+        id: HandleId::new(HandleType::DefaultJob, 0).as_raw(),
+    };
+
+    let ldsvc = if let Some(lib_dir) = lib_dir {
+        let (ldsvc, server_end) = zx::Channel::create().map_err(|_| LauncherError::Internal)?;
+        library_loader::start(Arc::new(lib_dir), server_end);
+        ldsvc.into_handle()
+    } else {
+        // Use the default loader in the dash-launcher process
+        fuchsia_runtime::loader_svc().map_err(|_| LauncherError::Internal)?
+    };
+    let ldsvc_handle =
+        HandleInfo { handle: ldsvc, id: HandleId::new(HandleType::LdsvcLoader, 0).as_raw() };
+
+    let utc_clock = {
+        let utc_clock = fuchsia_runtime::duplicate_utc_clock_handle(zx::Rights::SAME_RIGHTS)
+            .map_err(|_| LauncherError::Internal)?;
+        utc_clock.into_handle()
+    };
+    let utc_clock_handle =
+        HandleInfo { handle: utc_clock, id: HandleId::new(HandleType::ClockUtc, 0).as_raw() };
+
+    Ok(vec![stdin_handle, stdout_handle, stderr_handle, job_handle, ldsvc_handle, utc_clock_handle])
+}
+
+struct InstanceScope {
+    lib_dir: Option<fio::DirectoryProxy>,
+    ns: Vec<NameInfo>,
+}
+
+impl InstanceScope {
+    async fn new(query: &fsys::RealmQueryProxy, moniker: &str) -> Result<Self, LauncherError> {
+        let (_, resolved) = query
+            .get_instance_info(moniker)
+            .await
+            .map_err(|_| LauncherError::RealmQuery)?
+            .map_err(|e| {
+                if e == fcomp::Error::InstanceNotFound {
+                    LauncherError::InstanceNotFound
+                } else {
+                    LauncherError::RealmQuery
+                }
+            })?;
+
+        let mut ns = vec![];
+        let mut lib_dir = None;
+
+        if let Some(resolved) = resolved {
+            if let Some(started) = resolved.started {
+                if let Some(out_dir) = started.out_dir {
+                    ns.push(NameInfo { path: "/out".to_string(), directory: out_dir });
+                }
+                if let Some(runtime_dir) = started.runtime_dir {
+                    ns.push(NameInfo { path: "/runtime".to_string(), directory: runtime_dir });
+                }
+            }
+            ns.push(NameInfo { path: "/ns".to_string(), directory: resolved.ns_dir });
+            ns.push(NameInfo { path: "/exposed".to_string(), directory: resolved.exposed_dir });
+
+            // If available, use the component's /pkg/lib dir to load libraries
+            if let Some(pkg_dir) = resolved.pkg_dir {
+                let pkg_dir = pkg_dir.into_proxy().map_err(|_| LauncherError::Internal)?;
+                if let Ok(dir) = fuchsia_fs::directory::open_directory(
+                    &pkg_dir,
+                    "lib",
+                    fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE,
+                )
+                .await
+                {
+                    info!("Using /pkg/lib dir of {} for loading libraries", moniker);
+                    lib_dir.replace(dir);
+                }
+            }
+        }
+        Ok(Self { ns, lib_dir })
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    fn serve_realm_query(
+        instance_info: fsys::InstanceInfo,
+        resolved: Option<Box<fsys::ResolvedState>>,
+    ) -> fsys::RealmQueryProxy {
+        let (proxy, server_end) =
+            fidl::endpoints::create_proxy::<fsys::RealmQueryMarker>().unwrap();
+        fasync::Task::spawn(async move {
+            let mut stream = server_end.into_stream().unwrap();
+            let fsys::RealmQueryRequest::GetInstanceInfo { moniker, responder } =
+                stream.next().await.unwrap().unwrap();
+            assert_eq!(moniker, ".");
+            responder.send(&mut Ok((instance_info, resolved))).unwrap();
+        })
+        .detach();
+        proxy
+    }
+
+    #[fuchsia::test]
+    async fn check_namespace_started() {
+        let instance_info = fsys::InstanceInfo {
+            moniker: ".".to_string(),
+            url: "test://foo.com#meta/bar.cm".to_string(),
+            instance_id: None,
+            state: fsys::InstanceState::Started,
+        };
+
+        let (exposed_dir, _exposed_dir_server) =
+            fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
+        let (ns_dir, _ns_dir_server) =
+            fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
+        let (out_dir, _ns_dir_server) =
+            fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
+        let (runtime_dir, _ns_dir_server) =
+            fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
+
+        let started = Some(Box::new(fsys::StartedState {
+            out_dir: Some(out_dir),
+            runtime_dir: Some(runtime_dir),
+            start_reason: "Debug".to_string(),
+        }));
+
+        let resolved = Some(Box::new(fsys::ResolvedState {
+            uses: vec![],
+            exposes: vec![],
+            config: None,
+            pkg_dir: None,
+            started,
+            exposed_dir,
+            ns_dir,
+        }));
+
+        let query = serve_realm_query(instance_info, resolved);
+
+        let instance_scope = InstanceScope::new(&query, ".").await.unwrap();
+        let ns = create_dash_namespace(instance_scope.ns).unwrap();
+
+        assert_eq!(ns.len(), 6);
+        let mut paths: Vec<String> = ns.into_iter().map(|e| e.path).collect();
+        paths.sort();
+
+        assert_eq!(paths, vec!["/bin", "/exposed", "/ns", "/out", "/runtime", "/svc"]);
+    }
+
+    #[fuchsia::test]
+    async fn check_namespace_resolved() {
+        let instance_info = fsys::InstanceInfo {
+            moniker: ".".to_string(),
+            url: "test://foo.com#meta/bar.cm".to_string(),
+            instance_id: None,
+            state: fsys::InstanceState::Resolved,
+        };
+
+        let (exposed_dir, _exposed_dir_server) =
+            fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
+        let (ns_dir, _ns_dir_server) =
+            fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
+
+        let resolved = Some(Box::new(fsys::ResolvedState {
+            uses: vec![],
+            exposes: vec![],
+            config: None,
+            pkg_dir: None,
+            started: None,
+            exposed_dir,
+            ns_dir,
+        }));
+
+        let query = serve_realm_query(instance_info, resolved);
+
+        let instance_scope = InstanceScope::new(&query, ".").await.unwrap();
+        let ns = create_dash_namespace(instance_scope.ns).unwrap();
+
+        assert_eq!(ns.len(), 4);
+        let mut paths: Vec<String> = ns.into_iter().map(|e| e.path).collect();
+        paths.sort();
+
+        assert_eq!(paths, vec!["/bin", "/exposed", "/ns", "/svc"]);
+    }
+
+    #[fuchsia::test]
+    async fn check_namespace_unresolved() {
+        let instance_info = fsys::InstanceInfo {
+            moniker: ".".to_string(),
+            url: "test://foo.com#meta/bar.cm".to_string(),
+            instance_id: None,
+            state: fsys::InstanceState::Unresolved,
+        };
+
+        let resolved = None;
+        let query = serve_realm_query(instance_info, resolved);
+
+        let instance_scope = InstanceScope::new(&query, ".").await.unwrap();
+        let ns = create_dash_namespace(instance_scope.ns).unwrap();
+
+        assert_eq!(ns.len(), 2);
+        let mut paths: Vec<String> = ns.into_iter().map(|e| e.path).collect();
+        paths.sort();
+
+        assert_eq!(paths, vec!["/bin", "/svc"]);
+    }
+}
diff --git a/src/sys/tools/dash-launcher/src/main.rs b/src/sys/tools/dash-launcher/src/main.rs
index 4a19949..b74a79d 100644
--- a/src/sys/tools/dash-launcher/src/main.rs
+++ b/src/sys/tools/dash-launcher/src/main.rs
@@ -3,27 +3,16 @@
 // found in the LICENSE file.
 
 use anyhow::{self, Context};
-use fidl::{
-    endpoints::{ClientEnd, Proxy},
-    HandleBased,
-};
-use fidl_fuchsia_component as fcomp;
-use fidl_fuchsia_dash::{LauncherError, LauncherRequest, LauncherRequestStream};
-use fidl_fuchsia_hardware_pty as pty;
-use fidl_fuchsia_io as fio;
-use fidl_fuchsia_process::{HandleInfo, LaunchInfo, LauncherMarker, NameInfo};
-use fidl_fuchsia_sys2 as fsys;
+use fidl_fuchsia_dash::{LauncherRequest, LauncherRequestStream};
 use fuchsia_async as fasync;
-use fuchsia_component::client::connect_to_protocol;
 use fuchsia_component::server::ServiceFs;
 use fuchsia_inspect::{component, health::Reporter};
-use fuchsia_runtime::{HandleInfo as HandleId, HandleType};
 use fuchsia_zircon as zx;
 use futures::prelude::*;
-use moniker::{RelativeMoniker, RelativeMonikerBase};
-use std::sync::Arc;
+use launch::{launch_with_pty, launch_with_socket};
 use tracing::*;
 
+mod launch;
 mod socket;
 
 enum IncomingRequest {
@@ -50,11 +39,17 @@
             while let Some(Ok(request)) = stream.next().await {
                 match request {
                     LauncherRequest::LaunchWithPty { moniker, pty, responder } => {
-                        let mut result = launch_with_pty(moniker, pty).await;
+                        let mut result = launch_with_pty(&moniker, pty).await.map(|p| {
+                            info!("Launched dash for instance {}", moniker);
+                            log_on_process_exit(p);
+                        });
                         let _ = responder.send(&mut result);
                     }
                     LauncherRequest::LaunchWithSocket { moniker, socket, responder } => {
-                        let mut result = launch_with_socket(moniker, socket).await;
+                        let mut result = launch_with_socket(&moniker, socket).await.map(|p| {
+                            info!("Launched dash for instance {}", moniker);
+                            log_on_process_exit(p);
+                        });
                         let _ = responder.send(&mut result);
                     }
                 }
@@ -65,64 +60,7 @@
     Ok(())
 }
 
-async fn launch_with_socket(moniker: String, socket: zx::Socket) -> Result<(), LauncherError> {
-    let pty = socket::spawn_pty_forwarder(socket).await?;
-    launch_with_pty(moniker, pty).await
-}
-
-async fn launch_with_pty(
-    moniker: String,
-    pty: ClientEnd<pty::DeviceMarker>,
-) -> Result<(), LauncherError> {
-    // Process moniker
-    let moniker = RelativeMoniker::parse(&moniker).map_err(|_| LauncherError::BadMoniker)?;
-    if !moniker.up_path().is_empty() {
-        return Err(LauncherError::BadMoniker);
-    }
-    let moniker = moniker.to_string();
-
-    let launcher =
-        connect_to_protocol::<LauncherMarker>().map_err(|_| LauncherError::ProcessLauncher)?;
-
-    let query =
-        connect_to_protocol::<fsys::RealmQueryMarker>().map_err(|_| LauncherError::RealmQuery)?;
-
-    let instance_scope = InstanceScope::new(&query, &moniker).await?;
-
-    // The dash-launcher can be asked to launch multiple dash processes, each of which can
-    // make their own process hierarchies. This will look better topologically if we make a
-    // child job for each dash process.
-    let job =
-        fuchsia_runtime::job_default().create_child_job().map_err(|_| LauncherError::Internal)?;
-
-    let mut ns = create_dash_namespace(instance_scope.ns)?;
-    let mut handles = create_dash_handles(&job, pty, instance_scope.lib_dir)?;
-
-    // -s: force input from stdin
-    // -i: force interactive
-    let args = vec!["-i".as_bytes(), "-s".as_bytes()];
-
-    // Set PATH to generic command-line tools and package-specific command-line tools
-    let env = vec!["PATH=/bin:/ns/pkg/bin".as_bytes()];
-
-    let mut info = create_launch_info(&moniker, &job).await?;
-
-    // Spawn the dash process
-    launcher.add_names(&mut ns.iter_mut()).map_err(|_| LauncherError::ProcessLauncher)?;
-    launcher.add_handles(&mut handles.iter_mut()).map_err(|_| LauncherError::ProcessLauncher)?;
-    launcher.add_args(&mut args.into_iter()).map_err(|_| LauncherError::ProcessLauncher)?;
-    launcher.add_environs(&mut env.into_iter()).map_err(|_| LauncherError::ProcessLauncher)?;
-    let (status, process) =
-        launcher.launch(&mut info).await.map_err(|_| LauncherError::ProcessLauncher)?;
-    zx::Status::ok(status).map_err(|_| LauncherError::ProcessLauncher)?;
-    let process = process.ok_or(LauncherError::ProcessLauncher)?;
-
-    // The job should be terminated when the dash process dies
-    job.set_critical(zx::JobCriticalOptions::empty(), &process)
-        .map_err(|_| LauncherError::Internal)?;
-
-    info!("Launched dash process in {}", moniker);
-
+fn log_on_process_exit(process: zx::Process) {
     fasync::Task::spawn(async move {
         let _ = fasync::OnSignals::new(&process, zx::Signals::PROCESS_TERMINATED).await;
         match process.info() {
@@ -135,320 +73,4 @@
         }
     })
     .detach();
-
-    Ok(())
-}
-
-pub fn get_bin_from_launcher_namespace() -> Result<ClientEnd<fio::DirectoryMarker>, LauncherError> {
-    let (bin_dir, server) = fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
-    fuchsia_fs::node::connect_in_namespace(
-        "/pkg/bin",
-        fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE,
-        server.into_channel(),
-    )
-    .map_err(|_| LauncherError::Internal)?;
-
-    Ok(bin_dir)
-}
-
-pub async fn create_launch_info(moniker: &str, job: &zx::Job) -> Result<LaunchInfo, LauncherError> {
-    // Load `/pkg/bin/sh` as an executable VMO and pass it to the Launcher
-    let dash_file = fuchsia_fs::file::open_in_namespace(
-        "/pkg/bin/sh",
-        fio::OpenFlags::RIGHT_EXECUTABLE | fio::OpenFlags::RIGHT_READABLE,
-    )
-    .map_err(|_| LauncherError::DashBinary)?;
-
-    let executable = dash_file
-        .get_backing_memory(
-            fio::VmoFlags::READ | fio::VmoFlags::EXECUTE | fio::VmoFlags::PRIVATE_CLONE,
-        )
-        .await
-        .map_err(|_| LauncherError::DashBinary)?
-        .map_err(|_| LauncherError::DashBinary)?;
-
-    let job_dup =
-        job.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(|_| LauncherError::Internal)?;
-
-    // Set a name that's easy to find
-    // if moniker is `./core/foo`, process name is `sh-core-foo`
-    let mut process_name = moniker.replace('/', "-");
-    process_name.remove(0);
-    let process_name = format!("sh{}", process_name);
-
-    Ok(LaunchInfo { name: process_name, job: job_dup, executable })
-}
-
-fn serve_dash_svc_dir() -> Result<ClientEnd<fio::DirectoryMarker>, LauncherError> {
-    // Serve a directory that only provides fuchsia.process.Launcher to dash
-    let (svc_dir, server_end) =
-        fidl::endpoints::create_endpoints().map_err(|_| LauncherError::Internal)?;
-
-    let mut fs = ServiceFs::new();
-    fs.add_proxy_service::<LauncherMarker, ()>();
-    fs.serve_connection(server_end.into_channel()).map_err(|_| LauncherError::Internal)?;
-
-    fasync::Task::spawn(async move {
-        fs.collect::<()>().await;
-    })
-    .detach();
-
-    Ok(svc_dir)
-}
-
-fn create_dash_namespace(instance_ns: Vec<NameInfo>) -> Result<Vec<NameInfo>, LauncherError> {
-    let mut ns = instance_ns;
-
-    // Add the dash-launcher `/pkg/bin` to dash as `/bin`
-    let bin_dir = get_bin_from_launcher_namespace()?;
-    ns.push(NameInfo { path: "/bin".to_string(), directory: bin_dir });
-
-    // Add a custom `/svc` directory to dash
-    let svc_dir = serve_dash_svc_dir()?;
-    ns.push(NameInfo { path: "/svc".to_string(), directory: svc_dir });
-
-    Ok(ns)
-}
-
-fn create_dash_handles(
-    job: &zx::Job,
-    pty: ClientEnd<pty::DeviceMarker>,
-    lib_dir: Option<fio::DirectoryProxy>,
-) -> Result<Vec<HandleInfo>, LauncherError> {
-    let pty = pty.into_proxy().map_err(|_| LauncherError::Pty)?;
-
-    // Split the PTY into 3 channels (stdin, stdout, stderr)
-    let (stdout, to_pty_stdout) =
-        fidl::endpoints::create_endpoints::<pty::DeviceMarker>().map_err(|_| LauncherError::Pty)?;
-    let (stderr, to_pty_stderr) =
-        fidl::endpoints::create_endpoints::<pty::DeviceMarker>().map_err(|_| LauncherError::Pty)?;
-    let to_pty_stdout = to_pty_stdout.into_channel().into();
-    let to_pty_stderr = to_pty_stderr.into_channel().into();
-
-    // Clone the PTY to also be used for stdout and stderr
-    pty.clone(fio::OpenFlags::CLONE_SAME_RIGHTS, to_pty_stdout).map_err(|_| LauncherError::Pty)?;
-    pty.clone(fio::OpenFlags::CLONE_SAME_RIGHTS, to_pty_stderr).map_err(|_| LauncherError::Pty)?;
-    let stdin = pty.into_channel().map_err(|_| LauncherError::Pty)?.into_zx_channel();
-
-    let stdin_handle = HandleInfo {
-        handle: stdin.into_handle(),
-        id: HandleId::new(HandleType::FileDescriptor, 0).as_raw(),
-    };
-
-    let stdout_handle = HandleInfo {
-        handle: stdout.into_handle(),
-        id: HandleId::new(HandleType::FileDescriptor, 1).as_raw(),
-    };
-
-    let stderr_handle = HandleInfo {
-        handle: stderr.into_handle(),
-        id: HandleId::new(HandleType::FileDescriptor, 2).as_raw(),
-    };
-
-    let job_dup =
-        job.duplicate_handle(zx::Rights::SAME_RIGHTS).map_err(|_| LauncherError::Internal)?;
-    let job_handle = HandleInfo {
-        handle: zx::Handle::from(job_dup),
-        id: HandleId::new(HandleType::DefaultJob, 0).as_raw(),
-    };
-
-    let ldsvc = if let Some(lib_dir) = lib_dir {
-        let (ldsvc, server_end) = zx::Channel::create().map_err(|_| LauncherError::Internal)?;
-        library_loader::start(Arc::new(lib_dir), server_end);
-        ldsvc.into_handle()
-    } else {
-        // Use the default loader in the dash-launcher process
-        fuchsia_runtime::loader_svc().map_err(|_| LauncherError::Internal)?
-    };
-    let ldsvc_handle =
-        HandleInfo { handle: ldsvc, id: HandleId::new(HandleType::LdsvcLoader, 0).as_raw() };
-
-    let utc_clock = {
-        let utc_clock = fuchsia_runtime::duplicate_utc_clock_handle(zx::Rights::SAME_RIGHTS)
-            .map_err(|_| LauncherError::Internal)?;
-        utc_clock.into_handle()
-    };
-    let utc_clock_handle =
-        HandleInfo { handle: utc_clock, id: HandleId::new(HandleType::ClockUtc, 0).as_raw() };
-
-    Ok(vec![stdin_handle, stdout_handle, stderr_handle, job_handle, ldsvc_handle, utc_clock_handle])
-}
-
-struct InstanceScope {
-    lib_dir: Option<fio::DirectoryProxy>,
-    ns: Vec<NameInfo>,
-}
-
-impl InstanceScope {
-    pub async fn new(query: &fsys::RealmQueryProxy, moniker: &str) -> Result<Self, LauncherError> {
-        let (_, resolved) = query
-            .get_instance_info(moniker)
-            .await
-            .map_err(|_| LauncherError::RealmQuery)?
-            .map_err(|e| {
-                if e == fcomp::Error::InstanceNotFound {
-                    LauncherError::InstanceNotFound
-                } else {
-                    LauncherError::RealmQuery
-                }
-            })?;
-
-        let mut ns = vec![];
-        let mut lib_dir = None;
-
-        if let Some(resolved) = resolved {
-            if let Some(started) = resolved.started {
-                if let Some(out_dir) = started.out_dir {
-                    ns.push(NameInfo { path: "/out".to_string(), directory: out_dir });
-                }
-                if let Some(runtime_dir) = started.runtime_dir {
-                    ns.push(NameInfo { path: "/runtime".to_string(), directory: runtime_dir });
-                }
-            }
-            ns.push(NameInfo { path: "/ns".to_string(), directory: resolved.ns_dir });
-            ns.push(NameInfo { path: "/exposed".to_string(), directory: resolved.exposed_dir });
-
-            // If available, use the component's /pkg/lib dir to load libraries
-            if let Some(pkg_dir) = resolved.pkg_dir {
-                let pkg_dir = pkg_dir.into_proxy().map_err(|_| LauncherError::Internal)?;
-                if let Ok(dir) = fuchsia_fs::directory::open_directory(
-                    &pkg_dir,
-                    "lib",
-                    fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE,
-                )
-                .await
-                {
-                    info!("Using /pkg/lib dir of {} for loading libraries", moniker);
-                    lib_dir.replace(dir);
-                }
-            }
-        }
-        Ok(Self { ns, lib_dir })
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    fn serve_realm_query(
-        instance_info: fsys::InstanceInfo,
-        resolved: Option<Box<fsys::ResolvedState>>,
-    ) -> fsys::RealmQueryProxy {
-        let (proxy, server_end) =
-            fidl::endpoints::create_proxy::<fsys::RealmQueryMarker>().unwrap();
-        fasync::Task::spawn(async move {
-            let mut stream = server_end.into_stream().unwrap();
-            let fsys::RealmQueryRequest::GetInstanceInfo { moniker, responder } =
-                stream.next().await.unwrap().unwrap();
-            assert_eq!(moniker, ".");
-            responder.send(&mut Ok((instance_info, resolved))).unwrap();
-        })
-        .detach();
-        proxy
-    }
-
-    #[fuchsia::test]
-    async fn check_namespace_started() {
-        let instance_info = fsys::InstanceInfo {
-            moniker: ".".to_string(),
-            url: "test://foo.com#meta/bar.cm".to_string(),
-            instance_id: None,
-            state: fsys::InstanceState::Started,
-        };
-
-        let (exposed_dir, _exposed_dir_server) =
-            fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
-        let (ns_dir, _ns_dir_server) =
-            fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
-        let (out_dir, _ns_dir_server) =
-            fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
-        let (runtime_dir, _ns_dir_server) =
-            fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
-
-        let started = Some(Box::new(fsys::StartedState {
-            out_dir: Some(out_dir),
-            runtime_dir: Some(runtime_dir),
-            start_reason: "Debug".to_string(),
-        }));
-
-        let resolved = Some(Box::new(fsys::ResolvedState {
-            uses: vec![],
-            exposes: vec![],
-            config: None,
-            pkg_dir: None,
-            started,
-            exposed_dir,
-            ns_dir,
-        }));
-
-        let query = serve_realm_query(instance_info, resolved);
-
-        let instance_scope = InstanceScope::new(&query, ".").await.unwrap();
-        let ns = create_dash_namespace(instance_scope.ns).unwrap();
-
-        assert_eq!(ns.len(), 6);
-        let mut paths: Vec<String> = ns.into_iter().map(|e| e.path).collect();
-        paths.sort();
-
-        assert_eq!(paths, vec!["/bin", "/exposed", "/ns", "/out", "/runtime", "/svc"]);
-    }
-
-    #[fuchsia::test]
-    async fn check_namespace_resolved() {
-        let instance_info = fsys::InstanceInfo {
-            moniker: ".".to_string(),
-            url: "test://foo.com#meta/bar.cm".to_string(),
-            instance_id: None,
-            state: fsys::InstanceState::Resolved,
-        };
-
-        let (exposed_dir, _exposed_dir_server) =
-            fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
-        let (ns_dir, _ns_dir_server) =
-            fidl::endpoints::create_endpoints::<fio::DirectoryMarker>().unwrap();
-
-        let resolved = Some(Box::new(fsys::ResolvedState {
-            uses: vec![],
-            exposes: vec![],
-            config: None,
-            pkg_dir: None,
-            started: None,
-            exposed_dir,
-            ns_dir,
-        }));
-
-        let query = serve_realm_query(instance_info, resolved);
-
-        let instance_scope = InstanceScope::new(&query, ".").await.unwrap();
-        let ns = create_dash_namespace(instance_scope.ns).unwrap();
-
-        assert_eq!(ns.len(), 4);
-        let mut paths: Vec<String> = ns.into_iter().map(|e| e.path).collect();
-        paths.sort();
-
-        assert_eq!(paths, vec!["/bin", "/exposed", "/ns", "/svc"]);
-    }
-
-    #[fuchsia::test]
-    async fn check_namespace_unresolved() {
-        let instance_info = fsys::InstanceInfo {
-            moniker: ".".to_string(),
-            url: "test://foo.com#meta/bar.cm".to_string(),
-            instance_id: None,
-            state: fsys::InstanceState::Unresolved,
-        };
-
-        let resolved = None;
-        let query = serve_realm_query(instance_info, resolved);
-
-        let instance_scope = InstanceScope::new(&query, ".").await.unwrap();
-        let ns = create_dash_namespace(instance_scope.ns).unwrap();
-
-        assert_eq!(ns.len(), 2);
-        let mut paths: Vec<String> = ns.into_iter().map(|e| e.path).collect();
-        paths.sort();
-
-        assert_eq!(paths, vec!["/bin", "/svc"]);
-    }
 }