component_debug/
dirs.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Convenience functions for accessing directories of a component instance
6//! and opening protocols that exist in them.
7
8use flex_client::fidl::ProtocolMarker;
9use flex_client::ProxyHasDomain;
10use moniker::Moniker;
11use thiserror::Error;
12use {flex_fuchsia_io as fio, flex_fuchsia_sys2 as fsys};
13
14/// Errors that can be returned from opening a component instance directory.
15#[derive(Debug, Error)]
16pub enum OpenError {
17    #[error("instance {0} could not be found")]
18    InstanceNotFound(Moniker),
19    #[error("opening {0} requires {1} to be resolved")]
20    InstanceNotResolved(OpenDirType, Moniker),
21    #[error("opening {0} requires {1} to be running")]
22    InstanceNotRunning(OpenDirType, Moniker),
23    #[error("component manager's open request on the directory returned a FIDL error")]
24    OpenDirectoryFidlError,
25    #[error("{0} does not have a {1}")]
26    NoSuchDir(Moniker, OpenDirType),
27    #[error("component manager could not parse moniker: {0}")]
28    BadMoniker(Moniker),
29    #[error("component manager could not parse dir type: {0}")]
30    BadDirType(OpenDirType),
31    #[error("component manager could not parse path: {0}")]
32    BadPath(String),
33    #[error("component manager responded with an unknown error code")]
34    UnknownError,
35    #[error(transparent)]
36    Fidl(#[from] fidl::Error),
37}
38
39/// The directories of a component instance that can be opened.
40#[derive(Clone, Debug)]
41pub enum OpenDirType {
42    /// Served by the component's program. Rights unknown.
43    Outgoing,
44    /// Served by the component's runner. Rights unknown.
45    Runtime,
46    /// Served by the component's resolver. Rights unknown.
47    Package,
48    /// Served by component manager. Directory has RW rights.
49    Exposed,
50    /// Served by component manager. Directory has RW rights.
51    Namespace,
52}
53
54impl std::fmt::Display for OpenDirType {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        match self {
57            Self::Outgoing => write!(f, "outgoing directory"),
58            Self::Runtime => write!(f, "runtime directory"),
59            Self::Package => write!(f, "package directory"),
60            Self::Exposed => write!(f, "exposed directory"),
61            Self::Namespace => write!(f, "namespace directory"),
62        }
63    }
64}
65
66impl Into<fsys::OpenDirType> for OpenDirType {
67    fn into(self) -> fsys::OpenDirType {
68        match self {
69            Self::Outgoing => fsys::OpenDirType::OutgoingDir,
70            Self::Runtime => fsys::OpenDirType::RuntimeDir,
71            Self::Package => fsys::OpenDirType::PackageDir,
72            Self::Exposed => fsys::OpenDirType::ExposedDir,
73            Self::Namespace => fsys::OpenDirType::NamespaceDir,
74        }
75    }
76}
77
78impl From<fsys::OpenDirType> for OpenDirType {
79    fn from(fidl_type: fsys::OpenDirType) -> Self {
80        match fidl_type {
81            fsys::OpenDirType::OutgoingDir => Self::Outgoing,
82            fsys::OpenDirType::RuntimeDir => Self::Runtime,
83            fsys::OpenDirType::PackageDir => Self::Package,
84            fsys::OpenDirType::ExposedDir => Self::Exposed,
85            fsys::OpenDirType::NamespaceDir => Self::Namespace,
86            fsys::OpenDirTypeUnknown!() => panic!("This should not be constructed"),
87        }
88    }
89}
90
91/// Opens a protocol in a component instance directory, assuming it is located at the root.
92pub async fn connect_to_instance_protocol<P: ProtocolMarker>(
93    moniker: &Moniker,
94    dir_type: OpenDirType,
95    realm: &fsys::RealmQueryProxy,
96) -> Result<P::Proxy, OpenError> {
97    connect_to_instance_protocol_at_path::<P>(moniker, dir_type, P::DEBUG_NAME, realm).await
98}
99
100/// Opens a protocol in a component instance directory at the given |path|.
101pub async fn connect_to_instance_protocol_at_path<P: ProtocolMarker>(
102    moniker: &Moniker,
103    dir_type: OpenDirType,
104    path: &str,
105    realm: &fsys::RealmQueryProxy,
106) -> Result<P::Proxy, OpenError> {
107    let dir_client = open_instance_directory(moniker, dir_type, realm).await?;
108    let (proxy, server_end) = realm.domain().create_proxy::<P>();
109    dir_client
110        .open(path, fio::Flags::PROTOCOL_SERVICE, &Default::default(), server_end.into_channel())
111        .map_err(|_| OpenError::OpenDirectoryFidlError)?;
112    Ok(proxy)
113}
114
115/// Opens the specified directory type in a component instance identified by `moniker`.
116pub async fn open_instance_directory(
117    moniker: &Moniker,
118    dir_type: OpenDirType,
119    realm: &fsys::RealmQueryProxy,
120) -> Result<fio::DirectoryProxy, OpenError> {
121    let moniker_str = moniker.to_string();
122    let (dir_client, dir_server) = realm.domain().create_proxy::<fio::DirectoryMarker>();
123    realm
124        .open_directory(&moniker_str, dir_type.clone().into(), dir_server)
125        .await
126        .map_err(|e| OpenError::Fidl(e))?
127        .map_err(|e| match e {
128            fsys::OpenError::InstanceNotFound => OpenError::InstanceNotFound(moniker.clone()),
129            fsys::OpenError::InstanceNotResolved => {
130                OpenError::InstanceNotResolved(dir_type, moniker.clone())
131            }
132            fsys::OpenError::InstanceNotRunning => {
133                OpenError::InstanceNotRunning(dir_type, moniker.clone())
134            }
135            fsys::OpenError::NoSuchDir => OpenError::NoSuchDir(moniker.clone(), dir_type),
136            fsys::OpenError::BadDirType => OpenError::BadDirType(dir_type),
137            fsys::OpenError::BadMoniker => OpenError::BadMoniker(moniker.clone()),
138            fsys::OpenError::FidlError => OpenError::OpenDirectoryFidlError,
139            _ => OpenError::UnknownError,
140        })?;
141    Ok(dir_client)
142}