allow device matching by udev properties

This commit is contained in:
Mikaël Cluseau
2025-11-10 19:15:22 +01:00
parent 148aa0cc44
commit ac9d7e8d9d
9 changed files with 518 additions and 393 deletions

View File

@ -1,4 +1,4 @@
use eyre::{Result, format_err};
use eyre::{format_err, Result};
use log::{error, info, warn};
use std::collections::BTreeSet as Set;
use std::process::Stdio;
@ -6,7 +6,7 @@ use tokio::io::AsyncWriteExt;
use tokio::process::Command;
use tokio::sync::Mutex;
use super::{USED_DEVS, retry_or_ignore};
use super::{retry_or_ignore, USED_DEVS};
use crate::blockdev::{is_uninitialized, uninitialize};
use crate::fs::walk_dir;
use crate::input;
@ -29,7 +29,7 @@ pub async fn setup(devs: &[CryptDev]) {
let all_devs = walk_dir("/dev").await;
for dev in devs {
let mut mappings = find_dev(dev, &all_devs);
let mut mappings = find_dev(dev, &all_devs).await?;
mappings.retain(|(_, dev_path)| !used_devs.contains(dev_path));
if mappings.is_empty() && !dev.optional() && !done.contains(&dev.name) {
@ -134,18 +134,39 @@ async fn cryptsetup<const N: usize>(pw: &str, args: [&str; N]) -> Result<bool> {
Ok(child.wait().await?.success())
}
fn find_dev(dev: &CryptDev, all_devs: &[String]) -> Vec<(String, String)> {
async fn find_dev(dev: &CryptDev, all_devs: &[String]) -> Result<Vec<(String, String)>> {
let dev_name = &dev.name;
match dev.filter {
DevFilter::Dev(ref path) => (all_devs.iter())
Ok(match dev.filter() {
DevFilter::None => vec![],
DevFilter::Dev(path) => (all_devs.iter())
.filter(|dev_path| dev_path == &path)
.map(|dev_path| (dev.name.clone(), dev_path.clone()))
.collect(),
DevFilter::Prefix(ref prefix) => (all_devs.iter())
DevFilter::Prefix(prefix) => (all_devs.iter())
.filter_map(|path| {
let suffix = path.strip_prefix(prefix)?;
Some((format!("{dev_name}{suffix}"), path.clone()))
})
.collect(),
}
DevFilter::Udev(filter) => {
use crate::udev;
let devs = udev::all().await?;
let filter: udev::Filter = filter.clone().into();
(devs.iter())
.filter(|dev| dev.subsystem() == Some("block") && filter.matches(dev))
.filter_map(|dev| {
let path = dev.property("DEVNAME")?.to_string();
let mut name = dev_name.replace("${name}", dev.name()?);
for (p, v) in dev.properties() {
name = name.replace(&format!("${{{p}}}"), v);
}
Some((name, path))
})
.collect()
}
})
}

View File

@ -3,10 +3,10 @@ use log::{info, warn};
use std::collections::BTreeSet as Set;
use tokio::process::Command;
use super::{Result, format_err, retry_or_ignore};
use super::{format_err, retry_or_ignore, Result};
use crate::{
udev,
utils::{NameAliases, select_n_by_regex},
utils::{select_n_by_regex, select_n_by_udev, NameAliases},
};
use dkl::bootstrap::{Config, Network};
@ -26,16 +26,13 @@ pub async fn setup(cfg: &Config) {
async fn setup_network(net: &Network, assigned: &mut Set<String>) -> Result<()> {
info!("setting up network {}", net.name);
let netdevs = get_interfaces()?
let netdevs = (get_interfaces().await?)
.filter(|dev| !assigned.contains(dev.name()))
.collect::<Vec<_>>();
for dev in &netdevs {
info!(
"- available network device: {}, aliases [{}]",
dev.name(),
dev.aliases().join(", ")
);
let names = [dev.name()].into_iter().chain(dev.aliases()).join(", ");
info!("- available network device: {}", names);
}
let mut cmd = Command::new("ash");
@ -47,8 +44,19 @@ async fn setup_network(net: &Network, assigned: &mut Set<String>) -> Result<()>
for iface in &net.interfaces {
let var = &iface.var;
let netdevs = netdevs.iter().filter(|na| !assigned.contains(na.name()));
let if_names = select_n_by_regex(iface.n, &iface.regexps, netdevs);
let if_names = if let Some(ref udev_filter) = iface.udev {
select_n_by_udev(
iface.n,
"net",
"INTERFACE",
&udev_filter.clone().into(),
&assigned,
)
.await?
} else {
let netdevs = netdevs.iter().filter(|na| !assigned.contains(na.name()));
select_n_by_regex(iface.n, &iface.regexps, netdevs)
};
if if_names.is_empty() {
return Err(format_err!("- no interface match for {var:?}"));
@ -71,24 +79,20 @@ async fn setup_network(net: &Network, assigned: &mut Set<String>) -> Result<()>
Ok(())
}
fn get_interfaces() -> Result<impl Iterator<Item = NameAliases>> {
Ok(udev::get_devices("net")?.into_iter().map(|dev| {
let mut na = NameAliases::new(dev.sysname().to_string());
async fn get_interfaces() -> Result<impl Iterator<Item = NameAliases>> {
let nas: Vec<_> = (udev::all().await?.of_subsystem("net"))
.filter_map(|dev| {
let name = dev.property("INTERFACE")?;
let mut na = NameAliases::new(name.to_string());
for (property, value) in dev.properties() {
if [
"INTERFACE",
"ID_NET_NAME",
"ID_NET_NAME_PATH",
"ID_NET_NAME_MAC",
"ID_NET_NAME_SLOT",
]
.contains(&property)
{
na.push(value.to_string());
for (p, v) in dev.properties() {
if p.starts_with("ID_NET_NAME") {
na.push(v.to_string());
}
}
}
na
}))
Some(na)
})
.collect();
Ok(nas.into_iter())
}