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::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!("fuchsia.fuzzer/Manager.GetOutput returned ZX_ERR_{}", zx::Status::from_raw(e));
67        }
68        Ok(rx)
69    }
70
71    /// Requests that the `fuzz-manager` stop a running fuzzer instance.
72    ///
73    /// As a result of this call, the fuzzer component will cease an ongoing workflow and exit.
74    ///
75    /// Returns whether a fuzzer was stopped.
76    pub async fn stop(&self, url: &Url) -> Result<bool> {
77        let result = self.proxy.stop(url.as_str()).await.context(fidl_name("Stop"))?;
78        match result {
79            Ok(()) => Ok(true),
80            Err(e) if e == zx::Status::NOT_FOUND.into_raw() => Ok(false),
81            Err(e) => {
82                bail!("fuchsia.fuzzer/Manager.Stop returned ZX_ERR_{}", zx::Status::from_raw(e))
83            }
84        }
85    }
86}
87
88fn fidl_name(method: &str) -> String {
89    format!("{}/{}", fuzz::ManagerMarker::DEBUG_NAME, method)
90}
91
92#[cfg(test)]
93mod tests {
94    use super::Manager;
95    use anyhow::Result;
96    use flex_fuchsia_fuzzer as fuzz;
97    use fuchsia_fuzzctl_test::{TEST_URL, Test, create_task, serve_manager};
98    use url::Url;
99
100    #[fuchsia::test]
101    async fn test_connect() -> Result<()> {
102        let test = Test::try_new()?;
103        let (proxy, server_end) = test.create_proxy::<fuzz::ManagerMarker>();
104        let _task = create_task(serve_manager(server_end, test.clone()), test.writer());
105        let manager = Manager::new(proxy);
106
107        let url = Url::parse(TEST_URL)?;
108        manager.connect(&url).await?;
109
110        let actual = test.url().borrow().as_ref().map(|url| url.to_string());
111        let expected = Some(url.to_string());
112        assert_eq!(actual, expected);
113        Ok(())
114    }
115
116    #[fuchsia::test]
117    async fn test_stop() -> Result<()> {
118        let test = Test::try_new()?;
119        let (proxy, server_end) = test.create_proxy::<fuzz::ManagerMarker>();
120        let _task = create_task(serve_manager(server_end, test.clone()), test.writer());
121        let manager = Manager::new(proxy);
122
123        let url = Url::parse(TEST_URL)?;
124        manager.stop(&url).await?;
125
126        let actual = test.url().borrow().as_ref().map(|url| url.to_string());
127        let expected = Some(url.to_string());
128        assert_eq!(actual, expected);
129        Ok(())
130    }
131}