blob: 11335aae8081dc669fb3c3d8d8e6e0536d3859c4 [file] [log] [blame]
// Copyright 2019 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.
//! Online Insertion and Removal worker.
use {
anyhow::{format_err, Context as _},
fidl::endpoints::Proxy,
fidl_fuchsia_hardware_ethernet_ext::is_physical,
futures::stream::{Stream, StreamExt as _, TryStreamExt as _},
io_util::{open_directory_in_namespace, OPEN_RIGHT_READABLE},
network_manager_core::oir::OIRInfo,
std::fs,
std::os::unix::io::AsRawFd,
std::path,
};
/// A node that represents the directory it is in.
///
/// `/dir` and `/dir/.` point to the same directory.
const THIS_DIRECTORY: &str = ".";
fn ethdir_path() -> std::path::PathBuf {
let options: crate::Opt = argh::from_env();
path::Path::new(&options.dev_path).join("class/ethernet")
}
async fn device_found(filename: &std::path::PathBuf) -> Result<Option<OIRInfo>, anyhow::Error> {
let file_path = ethdir_path().join(filename);
let device = fs::File::open(&file_path)
.with_context(|| format!("could not open {}", file_path.display()))?;
let topological_path = fdio::device_get_topo_path(&device)
.with_context(|| format!("fdio::device_get_topo_path({})", file_path.display()))?;
let device_fd = device.as_raw_fd();
let mut client = 0;
// Safe because we're passing a valid fd.
fuchsia_zircon::Status::ok(unsafe {
fdio::fdio_sys::fdio_get_service_handle(device_fd, &mut client)
})
.with_context(|| {
format!("fuchsia_zircon::sys::fdio_get_service_handle({})", file_path.display())
})?;
// Safe because we checked the return status above.
let client = fuchsia_zircon::Channel::from(unsafe { fuchsia_zircon::Handle::from_raw(client) });
let ethernet_device =
fidl::endpoints::ClientEnd::<fidl_fuchsia_hardware_ethernet::DeviceMarker>::new(client)
.into_proxy()?;
if let Ok(device_info) = ethernet_device.get_info().await {
let device_info: fidl_fuchsia_hardware_ethernet_ext::EthernetInfo = device_info.into();
let device_channel = ethernet_device
.into_channel()
.map_err(|fidl_fuchsia_hardware_ethernet::DeviceProxy { .. }| {
anyhow::anyhow!("failed to convert device proxy into channel")
})?
.into_zx_channel();
info!("Device found: topo_path {} info {:?}", topological_path, device_info);
if is_physical(device_info.features) {
return Ok(Some(OIRInfo {
action: network_manager_core::oir::Action::ADD,
file_path: file_path.to_str().unwrap().to_string(),
topological_path,
device_information: Some(device_info),
device_channel: Some(device_channel),
}));
} else {
info!("device not physical {:?}", device_info);
}
}
Ok(None)
}
fn device_removed(topological_path: &std::path::PathBuf) -> OIRInfo {
info!("removing device topo path {:?}", topological_path);
OIRInfo {
action: network_manager_core::oir::Action::REMOVE,
topological_path: topological_path.to_str().unwrap().to_string(),
file_path: "".to_string(),
device_information: None,
device_channel: None,
}
}
/// Returns a `Stream` that yields `OIRInfo`s in response to events from the
/// device filesystem.
pub(super) async fn new_stream(
) -> Result<impl Stream<Item = Result<OIRInfo, anyhow::Error>>, anyhow::Error> {
let path = ethdir_path();
let path_as_str = path.to_str().ok_or_else(|| {
format_err!(
"path requested for watch is non-utf8 and our non-blocking directory apis require utf8 paths: {:?}.",
path
)
})?;
debug!("device path: {}", path_as_str);
let dir_proxy = open_directory_in_namespace(path_as_str, OPEN_RIGHT_READABLE)?;
let watcher = fuchsia_vfs_watcher::Watcher::new(dir_proxy)
.await
.with_context(|| format!("could not watch {:?}", path))?;
Ok(watcher
.map(|r| r.context("getting next VFS event from"))
.try_filter_map(|fuchsia_vfs_watcher::WatchMessage { event, filename }| async move {
info!("received {:?} event for filename: {:?}", event, filename);
if filename == path::PathBuf::from(THIS_DIRECTORY) {
debug!("skipping filename = {}", filename.display());
return Ok(None);
}
match event {
fuchsia_vfs_watcher::WatchEvent::ADD_FILE
| fuchsia_vfs_watcher::WatchEvent::EXISTING => Ok(device_found(&filename).await?),
fuchsia_vfs_watcher::WatchEvent::IDLE => Ok(None),
fuchsia_vfs_watcher::WatchEvent::REMOVE_FILE => Ok(Some(device_removed(&filename))),
event => {
warn!("received unknown event {:?} for filename: {:?}", event, filename);
Ok(None)
}
}
})
.map(move |r| r.with_context(|| format!("watching {:?}", path))))
}