blob: 16a49d6fc65d551c235b296ea4b4e5424bb486df [file] [log] [blame]
// 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::{device::Device, environment::Environment, matcher, service},
anyhow::{format_err, Error},
fs_management::format::DiskFormat,
futures::{channel::mpsc, lock::Mutex, StreamExt},
std::{collections::HashSet, sync::Arc},
};
pub struct Manager {
matcher: matcher::Matchers,
environment: Arc<Mutex<dyn Environment>>,
/// Holds a set of topological paths that have already been processed and
/// should be ignored when matching. When matched, the ignored paths are removed from the set.
/// (i.e. The device is ignored only once.)
matcher_lock: Arc<Mutex<HashSet<String>>>,
}
impl Manager {
pub fn new(
config: &fshost_config::Config,
ramdisk_path: Option<String>,
environment: Arc<Mutex<dyn Environment>>,
matcher_lock: Arc<Mutex<HashSet<String>>>,
) -> Self {
Manager { matcher: matcher::Matchers::new(config, ramdisk_path), environment, matcher_lock }
}
/// The main loop of fshost. Watch for new devices, match them against filesystems we expect,
/// and then launch them appropriately.
pub async fn device_handler(
&mut self,
device_stream: impl futures::Stream<Item = Box<dyn Device>>,
mut shutdown_rx: mpsc::Receiver<service::FshostShutdownResponder>,
) -> Result<service::FshostShutdownResponder, Error> {
let mut device_stream = Box::pin(device_stream).fuse();
let mut ignored_paths = HashSet::new();
loop {
// Wait for the next device to come in, or the shutdown signal to arrive.
let mut device = futures::select! {
responder = shutdown_rx.next() => {
let responder = responder
.ok_or_else(|| format_err!("shutdown signal stream ended unexpectedly"))?;
return Ok(responder);
},
maybe_device = device_stream.next() => {
if let Some(device) = maybe_device {
device
} else {
anyhow::bail!("block watcher returned none unexpectedly");
}
},
};
for path in (*self.matcher_lock.lock().await).drain() {
ignored_paths.insert(path);
}
let topological_path = device.topological_path().to_string();
if ignored_paths.remove(&topological_path) {
tracing::info!(
topological_path = topological_path.as_str(),
"Skipping explicitly ignored device."
);
continue;
}
let content_format = device.content_format().await.unwrap_or(DiskFormat::Unknown);
tracing::info!(
topological_path=topological_path.as_str(),
path = %device.path(),
?content_format,
"Matching device"
);
match self
.matcher
.match_device(device.as_mut(), &mut *self.environment.lock().await)
.await
{
Ok(true) => {}
// TODO(https://fxbug.dev/42069366): //src/tests/installer and //src/tests/femu look for
// "/dev/class/block/008 ignored"
Ok(false) => tracing::info!("{} ignored", device.path()),
Err(e) => {
tracing::error!(
path = %device.path(),
?e,
"Failed to match device",
);
}
}
}
}
pub async fn shutdown(self) -> Result<(), Error> {
self.environment.lock().await.shutdown().await
}
}