1344 lines
44 KiB
Diff
1344 lines
44 KiB
Diff
From ac13301c681cd4a4b50e4874b8f6c1f7b81e9f13 Mon Sep 17 00:00:00 2001
|
|
From: huyubiao <huyubiao@huawei.com>
|
|
Date: Fri, 3 Nov 2023 13:54:22 +0800
|
|
Subject: [PATCH 031/103] feature(devmaster): Add subcommands for devctl
|
|
trigger
|
|
|
|
---
|
|
exts/devmaster/src/bin/devctl/main.rs | 107 +++-
|
|
.../src/bin/devctl/subcmds/devctl_info.rs | 5 +-
|
|
.../src/bin/devctl/subcmds/devctl_trigger.rs | 482 ++++++++++++++++--
|
|
.../src/bin/devctl/subcmds/devctl_utils.rs | 15 +-
|
|
exts/devmaster/src/bin/devctl/subcmds/mod.rs | 2 +
|
|
exts/random_seed/src/random_seed.rs | 38 +-
|
|
libs/basic/Cargo.toml | 2 +
|
|
libs/basic/src/io_util.rs | 52 ++
|
|
libs/basic/src/lib.rs | 2 +
|
|
libs/basic/src/random_util.rs | 70 +++
|
|
libs/basic/src/uuid.rs | 29 +-
|
|
libs/device/src/device.rs | 47 +-
|
|
libs/device/src/device_enumerator.rs | 151 ++++--
|
|
13 files changed, 851 insertions(+), 151 deletions(-)
|
|
create mode 100644 libs/basic/src/random_util.rs
|
|
|
|
diff --git a/exts/devmaster/src/bin/devctl/main.rs b/exts/devmaster/src/bin/devctl/main.rs
|
|
index 56d6d3b4..17645ee6 100644
|
|
--- a/exts/devmaster/src/bin/devctl/main.rs
|
|
+++ b/exts/devmaster/src/bin/devctl/main.rs
|
|
@@ -26,9 +26,8 @@ use subcmds::devctl_hwdb::subcommand_hwdb;
|
|
use subcmds::devctl_info::InfoArgs;
|
|
use subcmds::devctl_monitor::subcommand_monitor;
|
|
use subcmds::devctl_test_builtin::subcommand_test_builtin;
|
|
-use subcmds::devctl_trigger::subcommand_trigger;
|
|
-
|
|
-type Result<T> = std::result::Result<T, nix::Error>;
|
|
+use subcmds::devctl_trigger::TriggerArgs;
|
|
+use subcmds::Result;
|
|
|
|
/// parse program arguments
|
|
#[derive(Parser, Debug)]
|
|
@@ -101,24 +100,73 @@ enum SubCmd {
|
|
#[clap(display_order = 4)]
|
|
Trigger {
|
|
/// the kind of device action to trigger
|
|
- #[clap(short, long)]
|
|
+ #[clap(short('c'), long)]
|
|
action: Option<String>,
|
|
|
|
- /// the enumerator type, can be devices (default) or subsystems
|
|
- #[clap(short, long)]
|
|
+ /// Type of events to trigger
|
|
+ #[clap(short, long, possible_values(&["devices", "subsystems", "all"]), help(
|
|
+ "Query device information:\n\
|
|
+ devices sysfs devices (default)\n\
|
|
+ subsystems sysfs subsystems and drivers\n\
|
|
+ all sysfs devices, subsystems, and drivers\n")
|
|
+ )]
|
|
r#type: Option<String>,
|
|
|
|
- /// print searched devices by enumerator
|
|
+ /// Print searched devices by enumerator
|
|
#[clap(short, long)]
|
|
verbose: bool,
|
|
|
|
+ /// Do not actually trigger the device events
|
|
+ #[clap(short('n'), long)]
|
|
+ dry_run: bool,
|
|
+
|
|
+ /// Trigger devices from a matching subsystem
|
|
+ #[clap(short('s'), long)]
|
|
+ subsystem_match: Option<Vec<String>>,
|
|
+
|
|
+ /// Exclude devices from a matching subsystem
|
|
+ #[clap(short('S'), long)]
|
|
+ subsystem_nomatch: Option<Vec<String>>,
|
|
+
|
|
+ /// Trigger devices with a matching attribute
|
|
+ #[clap(short('a'), long)]
|
|
+ attr_match: Option<Vec<String>>,
|
|
+
|
|
+ /// Exclude devices with a matching attribute
|
|
+ #[clap(short('A'), long)]
|
|
+ attr_nomatch: Option<Vec<String>>,
|
|
+
|
|
+ /// Trigger devices with a matching property
|
|
+ #[clap(short('p'), long)]
|
|
+ property_match: Option<Vec<String>>,
|
|
+
|
|
+ /// Trigger devices with a matching tag
|
|
+ #[clap(short('g'), long)]
|
|
+ tag_match: Option<Vec<String>>,
|
|
+
|
|
+ /// Trigger devices with this /sys path
|
|
+ #[clap(short('y'), long)]
|
|
+ sysname_match: Option<Vec<String>>,
|
|
+
|
|
+ /// Trigger devices with this /dev name
|
|
+ #[clap(long)]
|
|
+ name_match: Option<Vec<String>>,
|
|
+
|
|
+ /// Trigger devices with this /sys path
|
|
+ #[clap(short('b'), long)]
|
|
+ parent_match: Option<Vec<String>>,
|
|
+
|
|
+ /// Wait for the triggered events to complete
|
|
+ #[clap(short('w'), long)]
|
|
+ settle: bool,
|
|
+
|
|
+ /// Print synthetic uevent UUID
|
|
+ #[clap(long)]
|
|
+ uuid: bool,
|
|
+
|
|
/// the devices to be triggered
|
|
#[clap(required = false)]
|
|
devices: Vec<String>,
|
|
-
|
|
- /// do not actually trigger the device events
|
|
- #[clap(short('n'), long)]
|
|
- dry_run: bool,
|
|
},
|
|
|
|
/// Test builtin command on a device
|
|
@@ -197,7 +245,7 @@ fn main() -> Result<()> {
|
|
root,
|
|
devices,
|
|
)
|
|
- .subcommand_info()
|
|
+ .subcommand()
|
|
}
|
|
SubCmd::Monitor {} => subcommand_monitor(),
|
|
SubCmd::Kill {} => subcommand_kill(),
|
|
@@ -205,9 +253,40 @@ fn main() -> Result<()> {
|
|
action,
|
|
r#type,
|
|
verbose,
|
|
- devices,
|
|
dry_run,
|
|
- } => subcommand_trigger(devices, r#type, verbose, action, dry_run),
|
|
+ subsystem_match,
|
|
+ subsystem_nomatch,
|
|
+ attr_match,
|
|
+ attr_nomatch,
|
|
+ property_match,
|
|
+ tag_match,
|
|
+ sysname_match,
|
|
+ name_match,
|
|
+ parent_match,
|
|
+ settle,
|
|
+ uuid,
|
|
+ devices,
|
|
+ } => {
|
|
+ return TriggerArgs::new(
|
|
+ action,
|
|
+ r#type,
|
|
+ verbose,
|
|
+ dry_run,
|
|
+ subsystem_match,
|
|
+ subsystem_nomatch,
|
|
+ attr_match,
|
|
+ attr_nomatch,
|
|
+ property_match,
|
|
+ tag_match,
|
|
+ sysname_match,
|
|
+ name_match,
|
|
+ parent_match,
|
|
+ settle,
|
|
+ uuid,
|
|
+ devices,
|
|
+ )
|
|
+ .subcommand()
|
|
+ }
|
|
SubCmd::TestBuiltin {
|
|
action,
|
|
builtin,
|
|
diff --git a/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs b/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs
|
|
index 0fa76751..e18c3898 100644
|
|
--- a/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs
|
|
+++ b/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs
|
|
@@ -13,6 +13,7 @@
|
|
//! subcommand for devctl trigger
|
|
|
|
use crate::subcmds::devctl_utils;
|
|
+use crate::Result;
|
|
use basic::fd_util::{dot_or_dot_dot, xopendirat};
|
|
use device::{device_enumerator::DeviceEnumerator, Device};
|
|
use nix::dir::Dir;
|
|
@@ -25,8 +26,6 @@ use std::os::unix::fs::MetadataExt;
|
|
use std::os::unix::io::AsRawFd;
|
|
use std::path::Path;
|
|
|
|
-type Result<T> = std::result::Result<T, nix::Error>;
|
|
-
|
|
#[derive(Debug)]
|
|
enum QueryType {
|
|
Name,
|
|
@@ -94,7 +93,7 @@ impl InfoArgs {
|
|
}
|
|
|
|
/// subcommand for hwdb a fake device action, then the kernel will report an uevent
|
|
- pub fn subcommand_info(&self) -> Result<()> {
|
|
+ pub fn subcommand(&self) -> Result<()> {
|
|
let mut devs = Vec::new();
|
|
|
|
let mut arg_export = false;
|
|
diff --git a/exts/devmaster/src/bin/devctl/subcmds/devctl_trigger.rs b/exts/devmaster/src/bin/devctl/subcmds/devctl_trigger.rs
|
|
index cd425a8b..76a1712b 100644
|
|
--- a/exts/devmaster/src/bin/devctl/subcmds/devctl_trigger.rs
|
|
+++ b/exts/devmaster/src/bin/devctl/subcmds/devctl_trigger.rs
|
|
@@ -12,82 +12,482 @@
|
|
|
|
//! subcommand for devctl trigger
|
|
//!
|
|
-use std::{cell::RefCell, rc::Rc};
|
|
|
|
+use crate::subcmds::devctl_utils::find_device;
|
|
+use crate::Result;
|
|
+
|
|
+use device::device_monitor::{DeviceMonitor, MonitorNetlinkGroup};
|
|
use device::{
|
|
device_enumerator::{DeviceEnumerationType, DeviceEnumerator},
|
|
- Device, DeviceAction,
|
|
+ DeviceAction,
|
|
};
|
|
+use event::{EventState, EventType, Events, Source};
|
|
+use std::os::unix::io::RawFd;
|
|
+use std::path::Path;
|
|
+use std::{cell::RefCell, collections::HashSet, rc::Rc};
|
|
|
|
-/// subcommand for trigger a fake device action, then the kernel will report an uevent
|
|
-pub fn subcommand_trigger(
|
|
- devices: Vec<String>,
|
|
+#[derive(Debug)]
|
|
+pub struct TriggerArgs {
|
|
+ action: Option<String>,
|
|
r#type: Option<String>,
|
|
verbose: bool,
|
|
- action: Option<String>,
|
|
dry_run: bool,
|
|
-) {
|
|
- let mut devlist = vec![];
|
|
+ subsystem_match: Option<Vec<String>>,
|
|
+ subsystem_nomatch: Option<Vec<String>>,
|
|
+ attr_match: Option<Vec<String>>,
|
|
+ attr_nomatch: Option<Vec<String>>,
|
|
+ property_match: Option<Vec<String>>,
|
|
+ tag_match: Option<Vec<String>>,
|
|
+ sysname_match: Option<Vec<String>>,
|
|
+ name_match: Option<Vec<String>>,
|
|
+ parent_match: Option<Vec<String>>,
|
|
+ settle: bool,
|
|
+ uuid: bool,
|
|
+ devices: Vec<String>,
|
|
+}
|
|
+
|
|
+impl TriggerArgs {
|
|
+ #[allow(clippy::too_many_arguments)]
|
|
+ pub fn new(
|
|
+ action: Option<String>,
|
|
+ r#type: Option<String>,
|
|
+ verbose: bool,
|
|
+ dry_run: bool,
|
|
+ subsystem_match: Option<Vec<String>>,
|
|
+ subsystem_nomatch: Option<Vec<String>>,
|
|
+ attr_match: Option<Vec<String>>,
|
|
+ attr_nomatch: Option<Vec<String>>,
|
|
+ property_match: Option<Vec<String>>,
|
|
+ tag_match: Option<Vec<String>>,
|
|
+ sysname_match: Option<Vec<String>>,
|
|
+ name_match: Option<Vec<String>>,
|
|
+ parent_match: Option<Vec<String>>,
|
|
+ settle: bool,
|
|
+ uuid: bool,
|
|
+ devices: Vec<String>,
|
|
+ ) -> Self {
|
|
+ TriggerArgs {
|
|
+ action,
|
|
+ r#type,
|
|
+ verbose,
|
|
+ dry_run,
|
|
+ subsystem_match,
|
|
+ subsystem_nomatch,
|
|
+ attr_match,
|
|
+ attr_nomatch,
|
|
+ property_match,
|
|
+ tag_match,
|
|
+ sysname_match,
|
|
+ name_match,
|
|
+ parent_match,
|
|
+ settle,
|
|
+ uuid,
|
|
+ devices,
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /// subcommand for trigger a fake device action, then the kernel will report an uevent
|
|
+ pub fn subcommand(&self) -> Result<()> {
|
|
+ // if no device is declared, enumerate all devices or subsystems and drivers under /sys/
|
|
+ let mut enumerator = DeviceEnumerator::new();
|
|
+ if let Err(err) = enumerator.allow_uninitialized() {
|
|
+ return Err(err.get_errno());
|
|
+ }
|
|
+
|
|
+ let action = match &self.action {
|
|
+ Some(a) => a.parse::<DeviceAction>().unwrap(),
|
|
+ None => DeviceAction::Change,
|
|
+ };
|
|
+
|
|
+ if let Some(subsystems) = &self.subsystem_match {
|
|
+ for subsystem in subsystems {
|
|
+ if let Err(e) = enumerator.add_match_subsystem(subsystem, true) {
|
|
+ log::error!("Failed to add subsystem match {:?}", subsystem);
|
|
+ return Err(e.get_errno());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if let Some(subsystems) = &self.subsystem_nomatch {
|
|
+ for subsystem in subsystems {
|
|
+ if let Err(e) = enumerator.add_match_subsystem(subsystem, false) {
|
|
+ log::error!("Failed to add negative subsystem match {:?}", subsystem);
|
|
+ return Err(e.get_errno());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if let Some(attrs) = &self.attr_match {
|
|
+ for attr in attrs {
|
|
+ let (key, val) = keyval(attr);
|
|
+ if let Err(e) = enumerator.add_match_sysattr(&key, &val, true) {
|
|
+ log::error!("Failed to add sysattr match {:?}={:?}", key, val);
|
|
+ return Err(e.get_errno());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if let Some(attrs) = &self.attr_nomatch {
|
|
+ for attr in attrs {
|
|
+ let (key, val) = keyval(attr);
|
|
+ if let Err(e) = enumerator.add_match_sysattr(&key, &val, false) {
|
|
+ log::error!("Failed to add negative sysattr match {:?}={:?}", key, val);
|
|
+ return Err(e.get_errno());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if let Some(properties) = &self.property_match {
|
|
+ for property in properties {
|
|
+ let (key, val) = keyval(property);
|
|
+ if let Err(e) = enumerator.add_match_property(&key, &val) {
|
|
+ log::error!("Failed to add property match {:?}={:?}", key, val);
|
|
+ return Err(e.get_errno());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if let Some(tags) = &self.tag_match {
|
|
+ for tag in tags {
|
|
+ if let Err(e) = enumerator.add_match_tag(tag) {
|
|
+ log::error!("Failed to add tag match {:?}", tag);
|
|
+ return Err(e.get_errno());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if let Some(sysnames) = &self.sysname_match {
|
|
+ for sysname in sysnames {
|
|
+ if let Err(e) = enumerator.add_match_sysname(sysname, true) {
|
|
+ log::error!("Failed to add sysname match {:?}", sysname);
|
|
+ return Err(e.get_errno());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if let Some(names) = &self.name_match {
|
|
+ for name in names {
|
|
+ let dev = match find_device(name, "/dev") {
|
|
+ Ok(dev) => dev,
|
|
+ Err(e) => {
|
|
+ log::error!("Failed to open the device {:?}", name);
|
|
+ return Err(e);
|
|
+ }
|
|
+ };
|
|
+
|
|
+ if let Err(e) = enumerator.add_match_parent_incremental(&dev) {
|
|
+ log::error!("Failed to add parent match {:?} err:{:?}", name, e);
|
|
+ return Err(e.get_errno());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
|
|
- let action = match action {
|
|
- Some(a) => a.parse::<DeviceAction>().unwrap(),
|
|
- None => DeviceAction::Change,
|
|
- };
|
|
+ if let Some(parents) = &self.parent_match {
|
|
+ for parent in parents {
|
|
+ let dev = match find_device(parent, "/sys") {
|
|
+ Ok(dev) => dev,
|
|
+ Err(e) => {
|
|
+ log::error!("Failed to open the device {:?}", parent);
|
|
+ return Err(e);
|
|
+ }
|
|
+ };
|
|
|
|
- // if no device is declared, enumerate all devices or subsystems and drivers under /sys/
|
|
- if devices.is_empty() {
|
|
- let etype = match r#type {
|
|
+ if let Err(e) = enumerator.add_match_parent_incremental(&dev) {
|
|
+ log::error!("Failed to add parent match {:?} err:{:?}", parent, e);
|
|
+ return Err(e.get_errno());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for dev in &self.devices {
|
|
+ let device = match find_device(dev, "") {
|
|
+ Ok(d) => d,
|
|
+ Err(e) => {
|
|
+ log::error!("Failed to open the device: {:?} err:{:?}", dev, e);
|
|
+ return Err(e);
|
|
+ }
|
|
+ };
|
|
+ if let Err(err) = enumerator.add_match_parent_incremental(&device) {
|
|
+ return Err(err.get_errno());
|
|
+ }
|
|
+ }
|
|
+
|
|
+ let etype = match &self.r#type {
|
|
Some(t) => {
|
|
if t == "devices" {
|
|
DeviceEnumerationType::Devices
|
|
} else if t == "subsystems" {
|
|
DeviceEnumerationType::Subsystems
|
|
+ } else if t == "all" {
|
|
+ DeviceEnumerationType::All
|
|
} else {
|
|
log::error!("invalid events type{}", t);
|
|
- return;
|
|
+ return Err(nix::Error::EINVAL);
|
|
}
|
|
}
|
|
None => DeviceEnumerationType::Devices,
|
|
};
|
|
|
|
- let mut enumerator = DeviceEnumerator::new();
|
|
enumerator.set_enumerator_type(etype);
|
|
- for device in enumerator.iter() {
|
|
- devlist.push(device);
|
|
+
|
|
+ let settle_path_or_ids = self.exec_list(&mut enumerator, action)?;
|
|
+
|
|
+ let events = Events::new().unwrap();
|
|
+ if self.settle {
|
|
+ let monitor = Rc::new(TriggerMonitor::new(
|
|
+ DeviceMonitor::new(MonitorNetlinkGroup::Userspace, None),
|
|
+ settle_path_or_ids.clone(),
|
|
+ self.verbose,
|
|
+ self.uuid,
|
|
+ ));
|
|
+ events.add_source(monitor.clone()).unwrap();
|
|
+ events.set_enabled(monitor, EventState::On).unwrap();
|
|
}
|
|
- } else {
|
|
- for d in devices {
|
|
- match Device::from_path(&d) {
|
|
- Ok(dev) => {
|
|
- devlist.push(Rc::new(RefCell::new(dev)));
|
|
- }
|
|
+
|
|
+ if !settle_path_or_ids.is_empty() {
|
|
+ if let Err(e) = events.rloop() {
|
|
+ log::error!("Event loop failed err:{:?}", e);
|
|
+ return Err(nix::Error::EINVAL);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ Ok(())
|
|
+ }
|
|
+
|
|
+ fn exec_list(
|
|
+ &self,
|
|
+ enumerator: &mut DeviceEnumerator,
|
|
+ action: DeviceAction,
|
|
+ ) -> Result<HashSet<String>> {
|
|
+ let mut uuid_supported = -1;
|
|
+ let mut uuids = HashSet::new();
|
|
+ let mut ret = Ok(HashSet::<String>::new());
|
|
+ for device in enumerator.iter() {
|
|
+ let syspath = match device.borrow().get_syspath() {
|
|
+ Ok(syspath) => syspath,
|
|
+ Err(_) => continue,
|
|
+ };
|
|
+ if self.verbose {
|
|
+ println!("{}", syspath);
|
|
+ }
|
|
+ if self.dry_run {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ let id = match device
|
|
+ .borrow()
|
|
+ .trigger_with_uuid(action, (self.uuid || self.settle) && uuid_supported != 0)
|
|
+ {
|
|
+ Ok(id) => id,
|
|
Err(e) => {
|
|
- eprintln!("Invalid device path '{}': {}", d, e);
|
|
+ if e.get_errno() == nix::errno::Errno::EINVAL
|
|
+ && !self.uuid
|
|
+ && self.settle
|
|
+ && uuid_supported < 0
|
|
+ {
|
|
+ /* If we specified a UUID because of the settling logic, and we got EINVAL this might
|
|
+ * be caused by an old kernel which doesn't know the UUID logic (pre-4.13). Let's try
|
|
+ * if it works without the UUID logic then. */
|
|
+ if let Err(e) = device.borrow().trigger(action) {
|
|
+ if e.get_errno() != nix::Error::EINVAL {
|
|
+ /* dropping the uuid stuff changed the return code,
|
|
+ * hence don't bother next time */
|
|
+ uuid_supported = 0;
|
|
+ }
|
|
+ }
|
|
+ None
|
|
+ } else {
|
|
+ if ![nix::Error::ENOENT, nix::Error::ENODEV].contains(&e.get_errno()) {
|
|
+ eprintln!("Failed to trigger {:?}: {:?}", syspath, e);
|
|
+ if ret.is_ok() {
|
|
+ ret = Err(e.get_errno());
|
|
+ }
|
|
+ } else {
|
|
+ println!("Ignore to trigger {:?}: {:?}", syspath, e);
|
|
+ }
|
|
+
|
|
+ if [nix::Error::EACCES, nix::Error::EROFS].contains(&e.get_errno()) {
|
|
+ /* Inovoked by unprivileged user, or read only filesystem. Return earlier. */
|
|
+ return Err(e.get_errno());
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+
|
|
+ if uuid_supported < 0 {
|
|
+ uuid_supported = 1;
|
|
+ }
|
|
+
|
|
+ /* If the user asked for it, write event UUID to stdout */
|
|
+ if self.uuid {
|
|
+ if let Some(uuid) = &id {
|
|
+ println!("{}", uuid.to_string());
|
|
}
|
|
}
|
|
+
|
|
+ if self.settle {
|
|
+ if uuid_supported != 0 {
|
|
+ if let Some(uuid) = id {
|
|
+ uuids.insert(uuid.to_string());
|
|
+ }
|
|
+ } else {
|
|
+ uuids.insert(syspath);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if let Err(err) = ret {
|
|
+ return Err(err);
|
|
}
|
|
+
|
|
+ Ok(uuids)
|
|
}
|
|
+}
|
|
|
|
- for d in devlist {
|
|
- if !dry_run {
|
|
- if let Err(e) = d.borrow().trigger(action) {
|
|
- if ![nix::Error::ENOENT, nix::Error::ENODEV].contains(&e.get_errno()) {
|
|
- eprintln!(
|
|
- "Failed to trigger '{}': {}",
|
|
- d.borrow().get_syspath().unwrap_or_default(),
|
|
- e
|
|
+fn keyval(buf: &str) -> (String, String) {
|
|
+ let mut key = buf.to_string();
|
|
+ let mut val = String::new();
|
|
+
|
|
+ if let Some(pos) = buf.rfind('=') {
|
|
+ let (left, right) = buf.split_at(pos);
|
|
+ let right = &right[1..];
|
|
+ key = left.to_string();
|
|
+ val = right.to_string();
|
|
+ }
|
|
+
|
|
+ (key, val)
|
|
+}
|
|
+
|
|
+/// trigger monitor
|
|
+#[derive(Debug)]
|
|
+struct TriggerMonitor {
|
|
+ device_monitor: DeviceMonitor,
|
|
+ settle_path_or_ids: RefCell<HashSet<String>>,
|
|
+ verbose: bool,
|
|
+ uuid: bool,
|
|
+}
|
|
+
|
|
+/// public methods
|
|
+impl TriggerMonitor {
|
|
+ /// create a monitor instance for monitoring trigger
|
|
+ pub fn new(
|
|
+ device_monitor: DeviceMonitor,
|
|
+ settle_path_or_ids: HashSet<String>,
|
|
+ verbose: bool,
|
|
+ uuid: bool,
|
|
+ ) -> TriggerMonitor {
|
|
+ TriggerMonitor {
|
|
+ device_monitor,
|
|
+ settle_path_or_ids: RefCell::new(settle_path_or_ids),
|
|
+ verbose,
|
|
+ uuid,
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+impl Source for TriggerMonitor {
|
|
+ /// socket fd
|
|
+ fn fd(&self) -> RawFd {
|
|
+ self.device_monitor.fd()
|
|
+ }
|
|
+
|
|
+ /// event type
|
|
+ fn event_type(&self) -> EventType {
|
|
+ EventType::Io
|
|
+ }
|
|
+
|
|
+ /// epoll type
|
|
+ fn epoll_event(&self) -> u32 {
|
|
+ (libc::EPOLLIN) as u32
|
|
+ }
|
|
+
|
|
+ /// priority of event source
|
|
+ fn priority(&self) -> i8 {
|
|
+ 0i8
|
|
+ }
|
|
+
|
|
+ /// receive device from socket and remove path or uuid from settle_path_or_ids
|
|
+ fn dispatch(&self, event: &Events) -> i32 {
|
|
+ let device = match self.device_monitor.receive_device() {
|
|
+ Ok(device) => device,
|
|
+ Err(_) => {
|
|
+ return 0;
|
|
+ }
|
|
+ };
|
|
+
|
|
+ let syspath = match device.get_syspath() {
|
|
+ Ok(syspath) => syspath,
|
|
+ Err(e) => {
|
|
+ log::error!("Failed to get syspath of device event, ignoring:{:?}", e);
|
|
+ return 0;
|
|
+ }
|
|
+ };
|
|
+
|
|
+ let id = device.get_trigger_uuid();
|
|
+ match &id {
|
|
+ Ok(Some(id)) => {
|
|
+ if !self.settle_path_or_ids.borrow_mut().remove(&id.to_string()) {
|
|
+ log::debug!(
|
|
+ "Got uevent not matching expected UUID, ignoring. {:?}",
|
|
+ device.get_syspath()
|
|
);
|
|
- } else {
|
|
- println!(
|
|
- "Ignore to trigger '{}': {}",
|
|
- d.borrow().get_syspath().unwrap_or_default(),
|
|
- e
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ _ => {
|
|
+ let mut saved = self.settle_path_or_ids.borrow_mut().remove(&syspath);
|
|
+ if !saved {
|
|
+ /* When the device is renamed, the new name is broadcast, and the old name is saved
|
|
+ * in INTERFACE_OLD.
|
|
+ *
|
|
+ * TODO: remove support for INTERFACE_OLD when kernel baseline is bumped to 4.13 or
|
|
+ * higher.
|
|
+ */
|
|
+ if let Ok(old_sysname) = device.get_property_value("INTERFACE_OLD") {
|
|
+ let dir = match Path::new(&syspath).parent() {
|
|
+ Some(dir) => dir.to_str().unwrap(),
|
|
+ None => {
|
|
+ log::error!(
|
|
+ "Failed to extract directory from {:?}, ignoring",
|
|
+ syspath
|
|
+ );
|
|
+ return 0;
|
|
+ }
|
|
+ };
|
|
+ let old_syspath = dir.to_string() + "/" + &old_sysname;
|
|
+ saved = self.settle_path_or_ids.borrow_mut().remove(&old_syspath);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if !saved {
|
|
+ log::debug!(
|
|
+ "Got uevent for unexpected device, ignoring. {:?}",
|
|
+ device.get_syspath()
|
|
);
|
|
+ return 0;
|
|
}
|
|
}
|
|
}
|
|
- if verbose {
|
|
- println!("{}", d.borrow().get_syspath().unwrap_or_default());
|
|
+
|
|
+ if self.verbose {
|
|
+ println!("settle {}", syspath);
|
|
}
|
|
+
|
|
+ if self.uuid {
|
|
+ println!("settle {}", id.unwrap().unwrap().to_string());
|
|
+ }
|
|
+
|
|
+ if self.settle_path_or_ids.borrow().is_empty() {
|
|
+ event.set_exit();
|
|
+ }
|
|
+
|
|
+ 0
|
|
+ }
|
|
+
|
|
+ /// token of event source
|
|
+ fn token(&self) -> u64 {
|
|
+ let data: u64 = unsafe { std::mem::transmute(self) };
|
|
+ data
|
|
}
|
|
}
|
|
diff --git a/exts/devmaster/src/bin/devctl/subcmds/devctl_utils.rs b/exts/devmaster/src/bin/devctl/subcmds/devctl_utils.rs
|
|
index 63b7a98e..ecaab3a2 100644
|
|
--- a/exts/devmaster/src/bin/devctl/subcmds/devctl_utils.rs
|
|
+++ b/exts/devmaster/src/bin/devctl/subcmds/devctl_utils.rs
|
|
@@ -1,4 +1,5 @@
|
|
use device::Device;
|
|
+use std::path::PathBuf;
|
|
|
|
type Result<T> = std::result::Result<T, nix::Error>;
|
|
|
|
@@ -11,14 +12,22 @@ pub fn find_device(id: &str, prefix: &str) -> Result<Device> {
|
|
return Ok(device);
|
|
}
|
|
|
|
- if !prefix.is_empty() && !id.starts_with(prefix) {
|
|
- let path = prefix.to_string() + id;
|
|
+ let mut path = PathBuf::from(id);
|
|
|
|
- if let Ok(device) = Device::from_path(&path) {
|
|
+ if !prefix.is_empty() && !id.starts_with(prefix) {
|
|
+ path = PathBuf::from(prefix.to_string() + "/" + id)
|
|
+ .canonicalize()
|
|
+ .unwrap();
|
|
+ if let Ok(device) = Device::from_path(path.to_str().unwrap()) {
|
|
return Ok(device);
|
|
}
|
|
}
|
|
|
|
+ /* if a path is provided, then it cannot be a unit name. Let's return earlier. */
|
|
+ if path.to_str().unwrap().contains('/') {
|
|
+ return Err(nix::Error::ENODEV);
|
|
+ }
|
|
+
|
|
/* Check if the argument looks like a device unit name. */
|
|
find_device_from_unit(id)
|
|
}
|
|
diff --git a/exts/devmaster/src/bin/devctl/subcmds/mod.rs b/exts/devmaster/src/bin/devctl/subcmds/mod.rs
|
|
index 3d79a88d..e4289028 100644
|
|
--- a/exts/devmaster/src/bin/devctl/subcmds/mod.rs
|
|
+++ b/exts/devmaster/src/bin/devctl/subcmds/mod.rs
|
|
@@ -19,3 +19,5 @@ pub(crate) mod devctl_monitor;
|
|
pub(crate) mod devctl_test_builtin;
|
|
pub(crate) mod devctl_trigger;
|
|
pub(self) mod devctl_utils;
|
|
+
|
|
+pub(crate) type Result<T> = std::result::Result<T, nix::Error>;
|
|
diff --git a/exts/random_seed/src/random_seed.rs b/exts/random_seed/src/random_seed.rs
|
|
index 009be2ec..74846a6f 100644
|
|
--- a/exts/random_seed/src/random_seed.rs
|
|
+++ b/exts/random_seed/src/random_seed.rs
|
|
@@ -10,6 +10,7 @@
|
|
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
// See the Mulan PSL v2 for more details.
|
|
|
|
+use basic::io_util::loop_read;
|
|
use nix::ioctl_write_ptr;
|
|
use std::alloc::{alloc, dealloc, Layout};
|
|
use std::ffi::CString;
|
|
@@ -17,7 +18,7 @@ use std::path::{Path, PathBuf};
|
|
use std::{env, mem, str};
|
|
use std::{
|
|
fs::{self, read_link, File},
|
|
- io::{self, Read, Seek, Write},
|
|
+ io::{self, Seek, Write},
|
|
os::unix::prelude::AsRawFd,
|
|
};
|
|
|
|
@@ -173,26 +174,6 @@ fn fsync_full(file: &mut File) -> bool {
|
|
true
|
|
}
|
|
|
|
-fn loop_read(file: &mut File, buf: &mut [u8]) -> Result<usize, ()> {
|
|
- let size = buf.len();
|
|
- let mut pos = 0;
|
|
- while pos < size {
|
|
- let read_size = match file.read(&mut buf[pos..]) {
|
|
- Ok(size) => size,
|
|
- Err(err) => {
|
|
- println!("{}", err);
|
|
- return Err(());
|
|
- }
|
|
- };
|
|
-
|
|
- pos += read_size;
|
|
- if read_size == 0 {
|
|
- return Ok(pos);
|
|
- }
|
|
- }
|
|
- Ok(pos)
|
|
-}
|
|
-
|
|
fn sd_id128_from_string(buf: &[u8]) -> Result<[u8; 16], ()> {
|
|
let mut bytes = [0; 16];
|
|
|
|
@@ -603,27 +584,12 @@ pub fn run(arg: &str) -> Result<(), String> {
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
- use std::io::Seek;
|
|
-
|
|
use super::*;
|
|
|
|
fn is_root() -> bool {
|
|
let uid = unsafe { libc::geteuid() };
|
|
uid == 0
|
|
}
|
|
- #[test]
|
|
- fn loop_read_test() {
|
|
- let mut file = fs::OpenOptions::new()
|
|
- .read(true)
|
|
- .open("/etc/machine-id")
|
|
- .unwrap();
|
|
- let mut buf = [0; 38];
|
|
- assert_eq!(33, loop_read(&mut file, &mut buf).unwrap());
|
|
- file.rewind().unwrap();
|
|
- let mut buf = [0; 20];
|
|
- assert_eq!(20, loop_read(&mut file, &mut buf).unwrap());
|
|
- }
|
|
-
|
|
#[test]
|
|
fn sd_id128_from_string_test() {
|
|
let buf = b"e57446f87c3f4f978a7eca30ff7197d3";
|
|
diff --git a/libs/basic/Cargo.toml b/libs/basic/Cargo.toml
|
|
index e18db5c7..ebcb7801 100644
|
|
--- a/libs/basic/Cargo.toml
|
|
+++ b/libs/basic/Cargo.toml
|
|
@@ -63,6 +63,7 @@ full = [
|
|
"strbuf",
|
|
"argv",
|
|
"exec_util",
|
|
+ "random",
|
|
]
|
|
|
|
capability = []
|
|
@@ -111,3 +112,4 @@ murmurhash2 = []
|
|
strbuf = []
|
|
argv = []
|
|
exec_util = []
|
|
+random = []
|
|
diff --git a/libs/basic/src/io_util.rs b/libs/basic/src/io_util.rs
|
|
index b90d3a9b..d443976f 100644
|
|
--- a/libs/basic/src/io_util.rs
|
|
+++ b/libs/basic/src/io_util.rs
|
|
@@ -17,6 +17,8 @@ use nix::{
|
|
poll::{self, PollFd, PollFlags},
|
|
sys::{signal::SigSet, time::TimeSpec},
|
|
};
|
|
+use std::fs::File;
|
|
+use std::io::Read;
|
|
use std::os::unix::prelude::RawFd;
|
|
|
|
fn ppoll_timeout(fds: &mut [PollFd], timeout: Option<TimeSpec>) -> Result<libc::c_int> {
|
|
@@ -58,3 +60,53 @@ pub fn wait_for_events(fd: RawFd, event: PollFlags, time_out: i64) -> Result<lib
|
|
|
|
Ok(ret)
|
|
}
|
|
+
|
|
+/// Read data from file to buf, and return the number of bytes read.
|
|
+pub fn loop_read(file: &mut File, buf: &mut [u8]) -> Result<usize> {
|
|
+ let size = buf.len();
|
|
+ let mut pos = 0;
|
|
+ while pos < size {
|
|
+ let read_size = file.read(&mut buf[pos..]).context(IoSnafu)?;
|
|
+
|
|
+ pos += read_size;
|
|
+ if read_size == 0 {
|
|
+ return Ok(pos);
|
|
+ }
|
|
+ }
|
|
+ Ok(pos)
|
|
+}
|
|
+
|
|
+/// Read data from file to buf. If buf is full, succeeds, otherwise fails.
|
|
+pub fn loop_read_exact(file: &mut File, buf: &mut [u8]) -> Result<()> {
|
|
+ let n = loop_read(file, buf)?;
|
|
+
|
|
+ if n != buf.len() {
|
|
+ return Err(Error::Nix {
|
|
+ source: nix::errno::Errno::EIO,
|
|
+ });
|
|
+ }
|
|
+
|
|
+ Ok(())
|
|
+}
|
|
+
|
|
+#[cfg(test)]
|
|
+mod test {
|
|
+ use super::*;
|
|
+ use std::io::Seek;
|
|
+
|
|
+ #[test]
|
|
+ fn loop_read_test() {
|
|
+ let mut file = std::fs::OpenOptions::new()
|
|
+ .read(true)
|
|
+ .open("/etc/machine-id")
|
|
+ .unwrap();
|
|
+ let mut buf = [0; 38];
|
|
+ assert_eq!(33, loop_read(&mut file, &mut buf).unwrap());
|
|
+ file.rewind().unwrap();
|
|
+ let mut buf = [0; 20];
|
|
+ assert_eq!(20, loop_read(&mut file, &mut buf).unwrap());
|
|
+
|
|
+ let mut buf = [0; 10];
|
|
+ loop_read_exact(&mut file, &mut buf).unwrap();
|
|
+ }
|
|
+}
|
|
diff --git a/libs/basic/src/lib.rs b/libs/basic/src/lib.rs
|
|
index 0f40793d..ead31ea2 100644
|
|
--- a/libs/basic/src/lib.rs
|
|
+++ b/libs/basic/src/lib.rs
|
|
@@ -55,6 +55,8 @@ pub mod os_release;
|
|
pub mod parse;
|
|
#[cfg(feature = "process")]
|
|
pub mod process;
|
|
+#[cfg(feature = "random")]
|
|
+pub mod random_util;
|
|
#[cfg(feature = "rlimit")]
|
|
pub mod rlimit;
|
|
#[cfg(feature = "security")]
|
|
diff --git a/libs/basic/src/random_util.rs b/libs/basic/src/random_util.rs
|
|
new file mode 100644
|
|
index 00000000..831bf8ba
|
|
--- /dev/null
|
|
+++ b/libs/basic/src/random_util.rs
|
|
@@ -0,0 +1,70 @@
|
|
+// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved.
|
|
+//
|
|
+// sysMaster is licensed under Mulan PSL v2.
|
|
+// You can use this software according to the terms and conditions of the Mulan
|
|
+// PSL v2.
|
|
+// You may obtain a copy of Mulan PSL v2 at:
|
|
+// http://license.coscl.org.cn/MulanPSL2
|
|
+// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
|
|
+// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
+// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
+// See the Mulan PSL v2 for more details.
|
|
+
|
|
+//!
|
|
+
|
|
+/// Get random data from getrandom() or '/dev/urandom'
|
|
+pub fn random_bytes(data: &mut [u8]) {
|
|
+ let mut have_grndinsecure = true;
|
|
+
|
|
+ if data.is_empty() {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ let mut l: usize = 0;
|
|
+ loop {
|
|
+ let flag = match have_grndinsecure {
|
|
+ true => libc::GRND_INSECURE,
|
|
+ false => libc::GRND_NONBLOCK,
|
|
+ };
|
|
+ let size = unsafe {
|
|
+ libc::getrandom(
|
|
+ data[l..].as_mut_ptr() as *mut libc::c_void,
|
|
+ data.len() - l,
|
|
+ flag,
|
|
+ )
|
|
+ };
|
|
+
|
|
+ if size > 0 {
|
|
+ l += size as usize;
|
|
+ if l as usize == data.len() {
|
|
+ /* Done reading, success. */
|
|
+ return;
|
|
+ }
|
|
+ continue;
|
|
+ } else if size == 0
|
|
+ || crate::error::errno_is_not_supported(
|
|
+ nix::errno::Errno::from_i32(nix::errno::errno()),
|
|
+ )
|
|
+ || nix::errno::errno() == libc::EAGAIN && !have_grndinsecure
|
|
+ {
|
|
+ /* Weird or No syscall or Will block, but no GRND_INSECURE. Fallback to /dev/urandom. */
|
|
+ break;
|
|
+ } else if nix::errno::errno() == libc::EINVAL && have_grndinsecure {
|
|
+ /* No GRND_INSECURE; fallback to GRND_NONBLOCK. */
|
|
+ have_grndinsecure = false;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* Unexpected, so just give up and fallback to /dev/urandom. */
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ match std::fs::OpenOptions::new().read(true).open("/dev/urandom") {
|
|
+ Err(err) => {
|
|
+ log::error!("Failed to open /dev/urandom, err:{}", err);
|
|
+ }
|
|
+ Ok(mut file) => {
|
|
+ let _ = crate::io_util::loop_read_exact(&mut file, data);
|
|
+ }
|
|
+ };
|
|
+}
|
|
diff --git a/libs/basic/src/uuid.rs b/libs/basic/src/uuid.rs
|
|
index aaea8746..c1bd8030 100644
|
|
--- a/libs/basic/src/uuid.rs
|
|
+++ b/libs/basic/src/uuid.rs
|
|
@@ -46,7 +46,7 @@ pub const GPT_ROOT_NATIVE: Uuid = Uuid([
|
|
]);
|
|
|
|
/// uuid
|
|
-#[derive(PartialEq, Eq, Debug)]
|
|
+#[derive(PartialEq, Eq, Debug, Clone, Hash)]
|
|
pub struct Uuid(pub(super) [u8; 16]);
|
|
|
|
fn unhexchar(c: u8) -> Result<u8, ()> {
|
|
@@ -167,6 +167,33 @@ impl Uuid {
|
|
}
|
|
}
|
|
|
|
+/// get random uuid
|
|
+pub fn randomize() -> Result<Uuid, nix::Error> {
|
|
+ let mut id = Uuid::new();
|
|
+
|
|
+ crate::random_util::random_bytes(&mut id.0);
|
|
+
|
|
+ /* Turn this into a valid v4 UUID, to be nice. Note that we
|
|
+ * only guarantee this for newly generated UUIDs, not for
|
|
+ * pre-existing ones. */
|
|
+
|
|
+ Ok(id128_make_v4_uuid(id))
|
|
+}
|
|
+
|
|
+fn id128_make_v4_uuid(uuid: Uuid) -> Uuid {
|
|
+ /* Stolen from generate_random_uuid() of drivers/char/random.c
|
|
+ * in the kernel sources */
|
|
+
|
|
+ /* Set UUID version to 4 --- truly random generation */
|
|
+ let mut id = uuid;
|
|
+ id.0[6] = (id.0[6] & 0x0F) | 0x40;
|
|
+
|
|
+ /* Set the UUID variant to DCE */
|
|
+ id.0[8] = (id.0[8] & 0x3F) | 0x80;
|
|
+
|
|
+ id
|
|
+}
|
|
+
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
diff --git a/libs/device/src/device.rs b/libs/device/src/device.rs
|
|
index 5a95e0f5..d6a4dc07 100644
|
|
--- a/libs/device/src/device.rs
|
|
+++ b/libs/device/src/device.rs
|
|
@@ -17,6 +17,7 @@ use crate::utils::readlink_value;
|
|
use crate::{error::*, DeviceAction};
|
|
use basic::fs_util::{chmod, open_temporary, touch_file};
|
|
use basic::parse::{device_path_parse_devnum, parse_devnum, parse_ifindex};
|
|
+use basic::uuid::{randomize, Uuid};
|
|
use libc::{
|
|
dev_t, faccessat, gid_t, mode_t, uid_t, F_OK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT,
|
|
S_IRUSR, S_IWUSR,
|
|
@@ -1009,8 +1010,21 @@ impl Device {
|
|
}
|
|
|
|
/// get the trigger uuid of the device
|
|
- pub fn get_trigger_uuid(&self) -> Result<[u8; 8], Error> {
|
|
- todo!()
|
|
+ pub fn get_trigger_uuid(&self) -> Result<Option<Uuid>, Error> {
|
|
+ /* Retrieves the UUID attached to a uevent when triggering it from userspace via
|
|
+ * trigger_with_uuid() or an equivalent interface. Returns ENOENT if the record is not
|
|
+ * caused by a synthetic event and ENODATA if it was but no UUID was specified */
|
|
+ let s = self.get_property_value("SYNTH_UUID")?;
|
|
+
|
|
+ /* SYNTH_UUID=0 is set whenever a device is triggered by userspace without specifying a UUID */
|
|
+ if s == "0" {
|
|
+ return Err(Error::Nix {
|
|
+ msg: format!(""),
|
|
+ source: nix::errno::Errno::ENODATA,
|
|
+ });
|
|
+ }
|
|
+
|
|
+ Ok(Uuid::from_string(&s))
|
|
}
|
|
|
|
/// get the value of specific device sysattr
|
|
@@ -1103,8 +1117,33 @@ impl Device {
|
|
}
|
|
|
|
/// trigger with uuid
|
|
- pub fn trigger_with_uuid(&self, _action: DeviceAction) -> Result<[u8; 8], Error> {
|
|
- todo!()
|
|
+ pub fn trigger_with_uuid(
|
|
+ &self,
|
|
+ action: DeviceAction,
|
|
+ need_uuid: bool,
|
|
+ ) -> Result<Option<Uuid>, Error> {
|
|
+ if !need_uuid {
|
|
+ self.trigger(action)?;
|
|
+ return Ok(None);
|
|
+ }
|
|
+
|
|
+ let s = format!("{}", action);
|
|
+
|
|
+ let id = match randomize() {
|
|
+ Ok(id) => id,
|
|
+ Err(e) => {
|
|
+ return Err(Error::Nix {
|
|
+ msg: "Failed to randomize".to_string(),
|
|
+ source: e,
|
|
+ })
|
|
+ }
|
|
+ };
|
|
+
|
|
+ let j = s + " " + &id.to_string();
|
|
+
|
|
+ self.set_sysattr_value("uevent", Some(&j))?;
|
|
+
|
|
+ Ok(Some(id))
|
|
}
|
|
|
|
/// open device
|
|
diff --git a/libs/device/src/device_enumerator.rs b/libs/device/src/device_enumerator.rs
|
|
index 38956cf5..96cac2d9 100644
|
|
--- a/libs/device/src/device_enumerator.rs
|
|
+++ b/libs/device/src/device_enumerator.rs
|
|
@@ -69,10 +69,10 @@ pub struct DeviceEnumerator {
|
|
|
|
/// match sysattr
|
|
/// key: sysattr, value: match value
|
|
- pub(crate) match_sysattr: RefCell<HashMap<String, HashSet<String>>>,
|
|
+ pub(crate) match_sysattr: RefCell<HashMap<String, String>>,
|
|
/// do not match sysattr
|
|
/// key: sysattr, value: match value
|
|
- pub(crate) not_match_sysattr: RefCell<HashMap<String, HashSet<String>>>,
|
|
+ pub(crate) not_match_sysattr: RefCell<HashMap<String, String>>,
|
|
|
|
/// match property
|
|
/// key: property, value: match value
|
|
@@ -240,46 +240,14 @@ impl DeviceEnumerator {
|
|
) -> Result<(), Error> {
|
|
match whether_match {
|
|
true => {
|
|
- let sysattr_is_none = self.match_sysattr.borrow().get(sysattr).is_none();
|
|
-
|
|
- if sysattr_is_none {
|
|
- self.match_sysattr
|
|
- .borrow_mut()
|
|
- .insert(sysattr.to_string(), HashSet::new());
|
|
-
|
|
- self.match_sysattr
|
|
- .borrow_mut()
|
|
- .get_mut(sysattr)
|
|
- .unwrap()
|
|
- .insert(value.to_string());
|
|
- } else {
|
|
- self.match_sysattr
|
|
- .borrow_mut()
|
|
- .get_mut(sysattr)
|
|
- .unwrap()
|
|
- .insert(value.to_string());
|
|
- }
|
|
+ self.match_sysattr
|
|
+ .borrow_mut()
|
|
+ .insert(sysattr.to_string(), value.to_string());
|
|
}
|
|
false => {
|
|
- let not_match_sysattr_is_none =
|
|
- self.not_match_sysattr.borrow().get(sysattr).is_none();
|
|
-
|
|
- if not_match_sysattr_is_none {
|
|
- self.not_match_sysattr
|
|
- .borrow_mut()
|
|
- .insert(sysattr.to_string(), HashSet::new());
|
|
- self.not_match_sysattr
|
|
- .borrow_mut()
|
|
- .get_mut(sysattr)
|
|
- .unwrap()
|
|
- .insert(value.to_string());
|
|
- } else {
|
|
- self.not_match_sysattr
|
|
- .borrow_mut()
|
|
- .get_mut(sysattr)
|
|
- .unwrap()
|
|
- .insert(value.to_string());
|
|
- }
|
|
+ self.not_match_sysattr
|
|
+ .borrow_mut()
|
|
+ .insert(sysattr.to_string(), value.to_string());
|
|
}
|
|
};
|
|
|
|
@@ -548,12 +516,20 @@ impl DeviceEnumerator {
|
|
/// check whether the value of specific sysattr of a device matches
|
|
pub(crate) fn match_sysattr_value(
|
|
&self,
|
|
- _device: &Device,
|
|
- _sysattr: &str,
|
|
- _patterns: &HashSet<String>,
|
|
+ device: &Device,
|
|
+ sysattr: &str,
|
|
+ patterns: &str,
|
|
) -> Result<bool, Error> {
|
|
- todo!("Device::get_sysattr_value has not been implemented.");
|
|
- // Ok(false)
|
|
+ let value = match device.get_sysattr_value(sysattr) {
|
|
+ Ok(value) => value,
|
|
+ Err(_) => return Ok(false),
|
|
+ };
|
|
+
|
|
+ if patterns.is_empty() {
|
|
+ return Ok(true);
|
|
+ }
|
|
+
|
|
+ self.pattern_match(patterns, &value)
|
|
}
|
|
|
|
/// check whether a device matches conditions according to flags
|
|
@@ -922,9 +898,77 @@ impl DeviceEnumerator {
|
|
}
|
|
|
|
/// scan devices for a single tag
|
|
- pub(crate) fn scan_devices_tag(&self, _tag: &str) -> Result<(), Error> {
|
|
- todo!("scan_devices_tag has not been implemented.");
|
|
- // Ok(())
|
|
+ pub(crate) fn scan_devices_tag(&self, tag: &str) -> Result<(), Error> {
|
|
+ let path = Path::new("/run/devmaster/tags/").join(tag);
|
|
+ let dir = std::fs::read_dir(path);
|
|
+ let tag_dir = match dir {
|
|
+ Ok(d) => d,
|
|
+ Err(e) => {
|
|
+ if e.raw_os_error().unwrap_or_default() == libc::ENOENT {
|
|
+ return Ok(());
|
|
+ } else {
|
|
+ return Err(Error::Nix {
|
|
+ msg: format!("scan_dir failed: can't read directory '{}'", tag),
|
|
+ source: Errno::from_i32(e.raw_os_error().unwrap_or_default()),
|
|
+ });
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+
|
|
+ /* TODO: filter away subsystems? */
|
|
+
|
|
+ let mut ret = Ok(());
|
|
+ for entry in tag_dir {
|
|
+ let entry = match entry {
|
|
+ Ok(e) => e,
|
|
+ Err(e) => {
|
|
+ ret = Err(Error::Nix {
|
|
+ msg: format!(
|
|
+ "scan_dir failed: can't read entries from directory '{}'",
|
|
+ tag
|
|
+ ),
|
|
+ source: Errno::from_i32(e.raw_os_error().unwrap_or_default()),
|
|
+ });
|
|
+ continue;
|
|
+ }
|
|
+ };
|
|
+
|
|
+ let file_name = entry.file_name().to_str().unwrap().to_string();
|
|
+ if file_name.contains('.') {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ let mut device = match Device::from_device_id(&file_name) {
|
|
+ Ok(device) => device,
|
|
+ Err(e) => {
|
|
+ if e.get_errno() != nix::errno::Errno::ENODEV {
|
|
+ /* this is necessarily racy, so ignore missing devices */
|
|
+ ret = Err(e);
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+ };
|
|
+
|
|
+ /* Generated from tag, hence not necessary to check tag again. */
|
|
+ match self.test_matches(&mut device, MatchFlag::ALL & (!MatchFlag::TAG)) {
|
|
+ Ok(flag) => {
|
|
+ if !flag {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ Err(e) => {
|
|
+ ret = Err(e);
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if let Err(e) = self.add_device(Rc::new(RefCell::new(device))) {
|
|
+ ret = Err(e);
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret
|
|
}
|
|
|
|
/// scan devices tags
|
|
@@ -944,7 +988,16 @@ impl DeviceEnumerator {
|
|
|
|
/// parent add child
|
|
pub(crate) fn parent_add_child(&mut self, path: &str, flags: MatchFlag) -> Result<bool, Error> {
|
|
- let device = Rc::new(RefCell::new(Device::from_syspath(path, true)?));
|
|
+ let device = match Device::from_syspath(path, true) {
|
|
+ Ok(dev) => Rc::new(RefCell::new(dev)),
|
|
+ Err(err) => {
|
|
+ if err.get_errno() == nix::errno::Errno::ENODEV {
|
|
+ /* this is necessarily racy, so ignore missing devices */
|
|
+ return Ok(false);
|
|
+ }
|
|
+ return Err(err);
|
|
+ }
|
|
+ };
|
|
|
|
if !self.test_matches(&mut device.borrow_mut(), flags)? {
|
|
return Ok(false);
|
|
--
|
|
2.33.0
|
|
|