Skip to main content

fuchsia_fuzzctl_fdomain/
manager.rs

1// Copyright 2022 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
5use anyhow::{Context as _, Result, anyhow, bail};
6use flex_client::fidl::ProtocolMarker;
7use flex_client::{self, ProxyHasDomain};
8use flex_fuchsia_fuzzer::{self as fuzz};
9use url::Url;
10use zx_status as zx;
11
12/// Represents the FIDL connection from the `ffx fuzz` plugin to the `fuzz-manager` component on a
13/// target device.
14pub struct Manager {
15    proxy: fuzz::ManagerProxy,
16}
17
18impl Manager {
19    /// Creates a new `Manager`.
20    ///
21    /// The created object maintains a FIDL `proxy` to the `fuzz-manager` component on a target
22    /// device. Any output produced by this object will be written using the given `writer`.
23    pub fn new(proxy: fuzz::ManagerProxy) -> Self {
24        Self { proxy }
25    }
26
27    /// Requests that the `fuzz-manager` connect to a fuzzer instance.
28    ///
29    /// This will create and connect a `fuchsia.fuzzer.Controller` to the fuzzer on the target
30    /// device given by the `url`. Any artifacts produced by the fuzzer will be saved to the
31    /// `artifact_dir`, and outputs such as logs can optionally be saved to the `output_dir`.
32    ///
33    /// Returns an object representing the connected fuzzer, or an error.
34    pub async fn connect(&self, url: &Url) -> Result<fuzz::ControllerProxy> {
35        let dc = self.proxy.domain();
36        let (proxy, server_end) = dc.create_proxy::<fuzz::ControllerMarker>();
37        let result = self
38            .proxy
39            .connect(url.as_str(), server_end)
40            .await
41            .map_err(|e| anyhow!("{}/Connect: {}", fuzz::ManagerMarker::DEBUG_NAME, e))?;
42        if let Err(e) = result {
43            return Err(anyhow!(
44                "{}/Connect returned ZX_ERR_{}",
45                fuzz::ManagerMarker::DEBUG_NAME,
46                zx::Status::err_from_raw(e)
47            ));
48        }
49        Ok(proxy)
50    }
51
52    /// Returns a socket that provides the given type of fuzzer output.
53    pub async fn get_output(
54        &self,
55        url: &Url,
56        output: fuzz::TestOutput,
57    ) -> Result<flex_client::Socket> {
58        let dc = self.proxy.domain();
59        let (rx, tx) = dc.create_stream_socket();
60        let result = self
61            .proxy
62            .get_output(url.as_str(), output, tx)
63            .await
64            .context("failed to get output")?;
65        if let Err(e) = result {
66            bail!(
67                "fuchsia.fuzzer/Manager.GetOutput returned ZX_ERR_{}",
68                zx::Status::err_from_raw(e)
69            );
70        }
71        Ok(rx)
72    }
73
74    /// Requests that the `fuzz-manager` stop a running fuzzer instance.
75    ///
76    /// As a result of this call, the fuzzer component will cease an ongoing workflow and exit.
77    ///
78    /// Returns whether a fuzzer was stopped.
79    pub async fn stop(&self, url: &Url) -> Result<bool> {
80        let result = self.proxy.stop(url.as_str()).await.context(fidl_name("Stop"))?;
81        match result {
82            Ok(()) => Ok(true),
83            Err(e) if e == zx::Status::NOT_FOUND.into_raw() => Ok(false),
84            Err(e) => {
85                bail!("fuchsia.fuzzer/Manager.Stop returned ZX_ERR_{}", zx::Status::err_from_raw(e))
86            }
87        }
88    }
89}
90
91fn fidl_name(method: &str) -> String {
92    format!("{}/{}", fuzz::ManagerMarker::DEBUG_NAME, method)
93}
94
95#[cfg(test)]
96mod tests {
97    use super::Manager;
98    use anyhow::Result;
99    use flex_fuchsia_fuzzer as fuzz;
100    use fuchsia_fuzzctl_test::{TEST_URL, Test, create_task, serve_manager};
101    use url::Url;
102
103    #[fuchsia::test]
104    async fn test_connect() -> Result<()> {
105        let test = Test::try_new()?;
106        let (proxy, server_end) = test.create_proxy::<fuzz::ManagerMarker>();
107        let _task = create_task(serve_manager(server_end, test.clone()), test.writer());
108        let manager = Manager::new(proxy);
109
110        let url = Url::parse(TEST_URL)?;
111        manager.connect(&url).await?;
112
113        let actual = test.url().borrow().as_ref().map(|url| url.to_string());
114        let expected = Some(url.to_string());
115        assert_eq!(actual, expected);
116        Ok(())
117    }
118
119    #[fuchsia::test]
120    async fn test_stop() -> Result<()> {
121        let test = Test::try_new()?;
122        let (proxy, server_end) = test.create_proxy::<fuzz::ManagerMarker>();
123        let _task = create_task(serve_manager(server_end, test.clone()), test.writer());
124        let manager = Manager::new(proxy);
125
126        let url = Url::parse(TEST_URL)?;
127        manager.stop(&url).await?;
128
129        let actual = test.url().borrow().as_ref().map(|url| url.to_string());
130        let expected = Some(url.to_string());
131        assert_eq!(actual, expected);
132        Ok(())
133    }
134}