microsoft/openvmm

Public

mirrored fromhttps://github.com/microsoft/openvmmAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
6531c77717c0bab81bc1fec26aa386d69cbe507a

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

support/inspect/src/defer.rs

275lines · modecode

1// Copyright (C) Microsoft Corporation. All rights reserved.
2
3//! Support for deferring inspection requests.
4
5use super::InspectMut;
6use super::InternalNode;
7use super::Request;
8use super::RequestRoot;
9use super::Response;
10use super::SensitivityLevel;
11use super::UpdateRequest;
12use super::Value;
13use alloc::borrow::ToOwned;
14use alloc::string::String;
15use mesh::MeshPayload;
16
17impl Request<'_> {
18 /// Defers the inspection request, producing a value that can be sent to
19 /// another thread or context to continue the inspection asynchronously.
20 pub fn defer(self) -> Deferred {
21 let (send, recv) = mesh::oneshot();
22 *self.node = InternalNode::Deferred(recv);
23 Deferred {
24 path: self.path.to_owned(),
25 value: self.value.map(|x| x.to_owned()),
26 depth: self.depth,
27 node: send,
28 sensitivity: self.sensitivity,
29 }
30 }
31}
32
33impl UpdateRequest<'_> {
34 /// Defers this update request, returning an object that can be sent across
35 /// threads or processes and then used to report the update result at a
36 /// later time.
37 pub fn defer(self) -> DeferredUpdate {
38 let (send, recv) = mesh::oneshot();
39 *self.node = InternalNode::Deferred(recv);
40 DeferredUpdate {
41 value: self.value.to_owned(),
42 node: send,
43 }
44 }
45}
46
47/// A deferred inspection, which can provide inspection results asynchronously
48/// from a call to [`inspect`](crate::Inspect::inspect).
49#[derive(Debug, MeshPayload)]
50pub struct Deferred {
51 path: String,
52 value: Option<String>,
53 depth: usize,
54 node: mesh::OneshotSender<InternalNode>,
55 sensitivity: SensitivityLevel,
56}
57
58impl Deferred {
59 /// Inspect an object as part of a deferred inspection.
60 pub fn inspect(self, mut obj: impl InspectMut) {
61 let mut root = self.root();
62 obj.inspect_mut(root.request());
63 let node = root.node;
64 self.node.send(node);
65 }
66
67 /// Responds to the deferred inspection, calling `f` with a [`Response`].
68 pub fn respond<F: FnOnce(&mut Response<'_>)>(self, f: F) {
69 let mut root = self.root();
70 f(&mut root.request().respond());
71 let node = root.node;
72 self.node.send(node);
73 }
74
75 /// Responds to the deferred request with a value.
76 pub fn value(self, value: Value) {
77 let mut root = self.root();
78 root.request().value(value);
79 let node = root.node;
80 self.node.send(node);
81 }
82
83 /// Returns an object used for handling an update request.
84 ///
85 /// If this is not an update request, returns `Err(self)`.
86 pub fn update(self) -> Result<DeferredUpdate, Self> {
87 if self.value.is_some() && self.path.is_empty() {
88 Ok(DeferredUpdate {
89 value: self.value.unwrap(),
90 node: self.node,
91 })
92 } else {
93 Err(self)
94 }
95 }
96
97 fn root(&self) -> RequestRoot<'_> {
98 RequestRoot::new(
99 &self.path,
100 self.depth,
101 self.value.as_deref(),
102 self.sensitivity,
103 )
104 }
105
106 /// Removes this node from the inspection output.
107 pub fn ignore(self) {
108 self.node.send(InternalNode::Ignored);
109 }
110
111 /// Gets the request information for sending to a remote node via a non-mesh
112 /// communication mechanism, for use with [`complete_external`][].
113 ///
114 /// You don't need this if you are communicating with the remote object via
115 /// mesh. In that case, just send this object to the remote object over a
116 /// mesh channel, and then use [`respond`][] or similar methods to
117 /// handle the request.
118 ///
119 /// Use this when the remote object is across some other communication
120 /// boundary, such as gRPC. In that case, you will be responsible for using
121 /// [`inspect`][] on the remote node to handle the request, and to serializing
122 /// and deserializing the [`Node`][] structure across the communications
123 /// boundary.
124 ///
125 /// [`inspect`]: crate::inspect
126 /// [`Node`]: crate::Node
127 /// [`respond`]: Self::respond
128 /// [`complete_external`]: Self::complete_external
129 #[cfg(feature = "initiate")]
130 pub fn external_request(&self) -> ExternalRequest<'_> {
131 ExternalRequest {
132 path: &self.path,
133 sensitivity: self.sensitivity,
134 request_type: match &self.value {
135 None => ExternalRequestType::Inspect { depth: self.depth },
136 Some(value) => ExternalRequestType::Update { value },
137 },
138 }
139 }
140
141 /// Complete the request with a [`Node`][] and a [`SensitivityLevel`].
142 ///
143 /// See [`external_request`][] for details on how to use this.
144 ///
145 /// [`Node`]: crate::Node
146 /// [`external_request`]: Self::external_request
147 #[cfg(feature = "initiate")]
148 pub fn complete_external(self, node: super::Node, sensitivity: SensitivityLevel) {
149 // If the returned sensitivity level is not allowed for this request, drop it.
150 if sensitivity > self.sensitivity {
151 return;
152 }
153 if let Some(node) = InternalNode::from_node(node, self.sensitivity) {
154 // Add the prefixed path back on as a sequence of directory nodes. This
155 // is necessary so that they can be skipped in post-processing.
156 let node =
157 self.path
158 .split('/')
159 .filter(|s| !s.is_empty())
160 .rev()
161 .fold(node, |node, name| {
162 InternalNode::DirResolved(alloc::vec![crate::InternalEntry {
163 name: name.to_owned(),
164 node,
165 sensitivity: Some(sensitivity),
166 }])
167 });
168
169 self.node.send(node);
170 }
171 }
172
173 /// Gets the sensitivity level for this request.
174 pub fn sensitivity(&self) -> SensitivityLevel {
175 self.sensitivity
176 }
177}
178
179impl InternalNode {
180 #[cfg(feature = "initiate")]
181 pub(crate) fn from_node(
182 value: crate::Node,
183 request_sensitivity: SensitivityLevel,
184 ) -> Option<Self> {
185 use crate::Error;
186 use crate::InternalError;
187 use crate::Node;
188
189 let node = match value {
190 Node::Unevaluated => Self::Unevaluated,
191 Node::Failed(err) => Self::Failed(match err {
192 Error::NotFound => return None,
193 Error::Unresolved => InternalError::Unresolved,
194 Error::Mesh(err) => InternalError::Mesh(err),
195 Error::Immutable => InternalError::Immutable,
196 Error::Update(err) => InternalError::Update(err),
197 Error::NotADirectory => InternalError::NotADirectory,
198 Error::Internal => return None,
199 }),
200 Node::Value(v) => Self::Value(v),
201 Node::Dir(children) => Self::DirResolved(
202 children
203 .into_iter()
204 .filter_map(|e| {
205 // If the returned sensitivity level is not allowed for this request, drop it.
206 if e.sensitivity.unwrap_or_default() > request_sensitivity {
207 return None;
208 }
209 InternalNode::from_node(e.node, request_sensitivity).map(|v| {
210 crate::InternalEntry {
211 name: e.name,
212 node: v,
213 sensitivity: e.sensitivity,
214 }
215 })
216 })
217 .collect(),
218 ),
219 };
220 Some(node)
221 }
222}
223
224/// Return value from [`Deferred::external_request`], specifying parameters for
225/// a remote inspection.
226#[cfg(feature = "initiate")]
227pub struct ExternalRequest<'a> {
228 /// The remaining path of the request.
229 pub path: &'a str,
230 /// The request type and associated data.
231 pub request_type: ExternalRequestType<'a>,
232 /// The sensitivity level of the request.
233 pub sensitivity: SensitivityLevel,
234}
235
236/// The request type associated with [`ExternalRequest`].
237#[cfg(feature = "initiate")]
238pub enum ExternalRequestType<'a> {
239 /// An inspection request.
240 Inspect {
241 /// The depth to which to recurse.
242 depth: usize,
243 },
244 /// An update request.
245 Update {
246 /// The value to update to.
247 value: &'a str,
248 },
249}
250
251/// A deferred inspection, which can provide inspection results asynchronously
252/// from a call to [`inspect`](crate::Inspect::inspect).
253#[derive(Debug, MeshPayload)]
254pub struct DeferredUpdate {
255 value: String,
256 node: mesh::OneshotSender<InternalNode>,
257}
258
259impl DeferredUpdate {
260 /// Gets the requested new value.
261 pub fn new_value(&self) -> &str {
262 &self.value
263 }
264
265 /// Report that the update succeeded, with a new value of `value`.
266 pub fn succeed(self, value: Value) {
267 self.node.send(InternalNode::Value(value));
268 }
269
270 /// Report that the update failed, with the reason in `err`.
271 #[cfg(feature = "std")]
272 pub fn fail<E: Into<alloc::boxed::Box<dyn std::error::Error + Send + Sync>>>(self, err: E) {
273 self.node.send(InternalNode::failed(err.into()));
274 }
275}
276