1094 lines
34 KiB
Diff
1094 lines
34 KiB
Diff
|
|
From abc6eb6912fa8113ebe6238d95edbe920c703a30 Mon Sep 17 00:00:00 2001
|
||
|
|
From: huyubiao <huyubiao@huawei.com>
|
||
|
|
Date: Sun, 22 Oct 2023 14:42:13 +0800
|
||
|
|
Subject: [PATCH 004/103] feature(devctl): add devctl info
|
||
|
|
|
||
|
|
---
|
||
|
|
exts/devmaster/src/bin/devctl/main.rs | 91 ++-
|
||
|
|
.../src/bin/devctl/subcmds/devctl_info.rs | 680 ++++++++++++++++++
|
||
|
|
.../src/bin/devctl/subcmds/devctl_utils.rs | 29 +
|
||
|
|
exts/devmaster/src/bin/devctl/subcmds/mod.rs | 2 +
|
||
|
|
exts/devmaster/src/lib/rules/node.rs | 4 +-
|
||
|
|
libs/basic/src/fd_util.rs | 26 +-
|
||
|
|
libs/device/src/device.rs | 30 +-
|
||
|
|
libs/device/src/device_enumerator.rs | 2 +-
|
||
|
|
8 files changed, 838 insertions(+), 26 deletions(-)
|
||
|
|
create mode 100644 exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs
|
||
|
|
create mode 100644 exts/devmaster/src/bin/devctl/subcmds/devctl_utils.rs
|
||
|
|
|
||
|
|
diff --git a/exts/devmaster/src/bin/devctl/main.rs b/exts/devmaster/src/bin/devctl/main.rs
|
||
|
|
index 5fac776b..56d6d3b4 100644
|
||
|
|
--- a/exts/devmaster/src/bin/devctl/main.rs
|
||
|
|
+++ b/exts/devmaster/src/bin/devctl/main.rs
|
||
|
|
@@ -23,10 +23,13 @@ use log::init_log_to_console_syslog;
|
||
|
|
use log::Level;
|
||
|
|
use std::{io::Write, os::unix::net::UnixStream};
|
||
|
|
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>;
|
||
|
|
+
|
||
|
|
/// parse program arguments
|
||
|
|
#[derive(Parser, Debug)]
|
||
|
|
#[clap(author, version, about, long_about = None)]
|
||
|
|
@@ -39,16 +42,63 @@ struct Args {
|
||
|
|
/// Kinds of subcommands
|
||
|
|
#[derive(Parser, Debug)]
|
||
|
|
enum SubCmd {
|
||
|
|
- /// Monitor device events from kernel and userspace
|
||
|
|
+ /// Query sysfs or the devmaster database
|
||
|
|
#[clap(display_order = 1)]
|
||
|
|
+ Info {
|
||
|
|
+ #[clap(short, long, possible_values(&["name", "symlink", "path", "property", "env", "all"]), help(
|
||
|
|
+ "Query device information:\n\
|
||
|
|
+ name Name of device node\n\
|
||
|
|
+ symlink Pointing to node\n\
|
||
|
|
+ path sysfs device path\n\
|
||
|
|
+ property or env The device properties\n\
|
||
|
|
+ all All values\n")
|
||
|
|
+ )]
|
||
|
|
+ query: Option<String>,
|
||
|
|
+
|
||
|
|
+ /// Print all key matches walking along the chain
|
||
|
|
+ /// of parent devices
|
||
|
|
+ #[clap(short, long)]
|
||
|
|
+ attribute_walk: bool,
|
||
|
|
+
|
||
|
|
+ /// Print major:minor of device containing this file
|
||
|
|
+ #[clap(short, long)]
|
||
|
|
+ device_id_of_file: Option<String>,
|
||
|
|
+
|
||
|
|
+ /// Export key/value pairs
|
||
|
|
+ #[clap(short('x'), long)]
|
||
|
|
+ export: bool,
|
||
|
|
+
|
||
|
|
+ /// Export the key name with a prefix
|
||
|
|
+ #[clap(short('P'), long)]
|
||
|
|
+ export_prefix: Option<String>,
|
||
|
|
+
|
||
|
|
+ /// Export the content of the devmaster database
|
||
|
|
+ #[clap(short('e'), long)]
|
||
|
|
+ export_db: bool,
|
||
|
|
+
|
||
|
|
+ /// Clean up the devmaster database
|
||
|
|
+ #[clap(short, long)]
|
||
|
|
+ cleanup_db: bool,
|
||
|
|
+
|
||
|
|
+ /// Prepend dev directory to path names
|
||
|
|
+ #[clap(short, long)]
|
||
|
|
+ root: bool,
|
||
|
|
+
|
||
|
|
+ ///
|
||
|
|
+ #[clap(required = false)]
|
||
|
|
+ devices: Vec<String>,
|
||
|
|
+ },
|
||
|
|
+
|
||
|
|
+ /// Monitor device events from kernel and userspace
|
||
|
|
+ #[clap(display_order = 2)]
|
||
|
|
Monitor {},
|
||
|
|
|
||
|
|
/// Kill all devmaster workers
|
||
|
|
- #[clap(display_order = 2)]
|
||
|
|
+ #[clap(display_order = 3)]
|
||
|
|
Kill {},
|
||
|
|
|
||
|
|
/// Trigger a fake device action, then the kernel will report an uevent
|
||
|
|
- #[clap(display_order = 3)]
|
||
|
|
+ #[clap(display_order = 4)]
|
||
|
|
Trigger {
|
||
|
|
/// the kind of device action to trigger
|
||
|
|
#[clap(short, long)]
|
||
|
|
@@ -72,7 +122,7 @@ enum SubCmd {
|
||
|
|
},
|
||
|
|
|
||
|
|
/// Test builtin command on a device
|
||
|
|
- #[clap(display_order = 4)]
|
||
|
|
+ #[clap(display_order = 5)]
|
||
|
|
TestBuiltin {
|
||
|
|
/// device action
|
||
|
|
#[clap(short, long)]
|
||
|
|
@@ -85,7 +135,7 @@ enum SubCmd {
|
||
|
|
syspath: String,
|
||
|
|
},
|
||
|
|
/// Test builtin command on a device
|
||
|
|
- #[clap(display_order = 5)]
|
||
|
|
+ #[clap(display_order = 6)]
|
||
|
|
Hwdb {
|
||
|
|
/// update the hardware database
|
||
|
|
#[clap(short('u'), long)]
|
||
|
|
@@ -114,16 +164,41 @@ fn subcommand_kill() {
|
||
|
|
stream.write_all(b"kill ").unwrap();
|
||
|
|
}
|
||
|
|
|
||
|
|
-fn main() {
|
||
|
|
+fn main() -> Result<()> {
|
||
|
|
let argv: Vec<String> = std::env::args().collect();
|
||
|
|
if invoked_as(argv, "devmaster") {
|
||
|
|
- return run_daemon();
|
||
|
|
+ run_daemon();
|
||
|
|
+ return Ok(());
|
||
|
|
}
|
||
|
|
|
||
|
|
init_log_to_console_syslog("devctl", Level::Debug);
|
||
|
|
let args = Args::parse();
|
||
|
|
|
||
|
|
match args.subcmd {
|
||
|
|
+ SubCmd::Info {
|
||
|
|
+ query,
|
||
|
|
+ attribute_walk,
|
||
|
|
+ device_id_of_file,
|
||
|
|
+ export,
|
||
|
|
+ export_prefix,
|
||
|
|
+ export_db,
|
||
|
|
+ cleanup_db,
|
||
|
|
+ root,
|
||
|
|
+ devices,
|
||
|
|
+ } => {
|
||
|
|
+ return InfoArgs::new(
|
||
|
|
+ query,
|
||
|
|
+ attribute_walk,
|
||
|
|
+ device_id_of_file,
|
||
|
|
+ export,
|
||
|
|
+ export_prefix,
|
||
|
|
+ export_db,
|
||
|
|
+ cleanup_db,
|
||
|
|
+ root,
|
||
|
|
+ devices,
|
||
|
|
+ )
|
||
|
|
+ .subcommand_info()
|
||
|
|
+ }
|
||
|
|
SubCmd::Monitor {} => subcommand_monitor(),
|
||
|
|
SubCmd::Kill {} => subcommand_kill(),
|
||
|
|
SubCmd::Trigger {
|
||
|
|
@@ -147,4 +222,6 @@ fn main() {
|
||
|
|
root,
|
||
|
|
} => subcommand_hwdb(update, test, path, usr, strict, root),
|
||
|
|
}
|
||
|
|
+
|
||
|
|
+ Ok(())
|
||
|
|
}
|
||
|
|
diff --git a/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs b/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs
|
||
|
|
new file mode 100644
|
||
|
|
index 00000000..0fa76751
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs
|
||
|
|
@@ -0,0 +1,680 @@
|
||
|
|
+// 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.
|
||
|
|
+
|
||
|
|
+//! subcommand for devctl trigger
|
||
|
|
+
|
||
|
|
+use crate::subcmds::devctl_utils;
|
||
|
|
+use basic::fd_util::{dot_or_dot_dot, xopendirat};
|
||
|
|
+use device::{device_enumerator::DeviceEnumerator, Device};
|
||
|
|
+use nix::dir::Dir;
|
||
|
|
+use nix::fcntl::{AtFlags, OFlag};
|
||
|
|
+use nix::sys::stat::fstatat;
|
||
|
|
+use nix::sys::stat::Mode;
|
||
|
|
+use nix::unistd::{unlinkat, UnlinkatFlags};
|
||
|
|
+use std::fs;
|
||
|
|
+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,
|
||
|
|
+ Path,
|
||
|
|
+ Symlink,
|
||
|
|
+ Property,
|
||
|
|
+ All,
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+struct QueryProperty {
|
||
|
|
+ export: bool,
|
||
|
|
+ export_prefix: Option<String>,
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+impl QueryProperty {
|
||
|
|
+ fn new(export: bool, export_prefix: Option<String>) -> Self {
|
||
|
|
+ QueryProperty {
|
||
|
|
+ export,
|
||
|
|
+ export_prefix,
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+struct SysAttr {
|
||
|
|
+ name: String,
|
||
|
|
+ value: String,
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+pub struct InfoArgs {
|
||
|
|
+ query: Option<String>,
|
||
|
|
+ attribute_walk: bool,
|
||
|
|
+ device_id_of_file: Option<String>,
|
||
|
|
+ export: bool,
|
||
|
|
+ export_prefix: Option<String>,
|
||
|
|
+ export_db: bool,
|
||
|
|
+ cleanup_db: bool,
|
||
|
|
+ root: bool,
|
||
|
|
+ devices: Vec<String>,
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+impl InfoArgs {
|
||
|
|
+ #[allow(clippy::too_many_arguments)]
|
||
|
|
+ pub fn new(
|
||
|
|
+ query: Option<String>,
|
||
|
|
+ attribute_walk: bool,
|
||
|
|
+ device_id_of_file: Option<String>,
|
||
|
|
+ export: bool,
|
||
|
|
+ export_prefix: Option<String>,
|
||
|
|
+ export_db: bool,
|
||
|
|
+ cleanup_db: bool,
|
||
|
|
+ root: bool,
|
||
|
|
+ devices: Vec<String>,
|
||
|
|
+ ) -> Self {
|
||
|
|
+ InfoArgs {
|
||
|
|
+ query,
|
||
|
|
+ attribute_walk,
|
||
|
|
+ device_id_of_file,
|
||
|
|
+ export,
|
||
|
|
+ export_prefix,
|
||
|
|
+ export_db,
|
||
|
|
+ cleanup_db,
|
||
|
|
+ root,
|
||
|
|
+ devices,
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ /// subcommand for hwdb a fake device action, then the kernel will report an uevent
|
||
|
|
+ pub fn subcommand_info(&self) -> Result<()> {
|
||
|
|
+ let mut devs = Vec::new();
|
||
|
|
+
|
||
|
|
+ let mut arg_export = false;
|
||
|
|
+ if self.export || self.export_prefix.is_some() {
|
||
|
|
+ arg_export = true;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if self.export_db {
|
||
|
|
+ return export_devices();
|
||
|
|
+ }
|
||
|
|
+ if self.cleanup_db {
|
||
|
|
+ return cleanup_db();
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Some(name) = self.device_id_of_file.as_ref() {
|
||
|
|
+ if !self.devices.is_empty() {
|
||
|
|
+ log::error!("Positional arguments are not allowed with -d/--device-id-of-file.");
|
||
|
|
+ return Err(nix::Error::EINVAL);
|
||
|
|
+ }
|
||
|
|
+ return self.stat_device(name, arg_export);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ let mut query_type = QueryType::All;
|
||
|
|
+ self.parse_query_type(&mut query_type)?;
|
||
|
|
+
|
||
|
|
+ devs.extend(&self.devices);
|
||
|
|
+ if devs.is_empty() {
|
||
|
|
+ log::error!("A device name or path is required");
|
||
|
|
+ return Err(nix::Error::EINVAL);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if self.attribute_walk && devs.len() > 1 {
|
||
|
|
+ log::error!("Only one device may be specified with -a/--attribute-walk");
|
||
|
|
+ return Err(nix::Error::EINVAL);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ let mut r: Result<()> = Ok(());
|
||
|
|
+ for dev in &self.devices {
|
||
|
|
+ let device = match devctl_utils::find_device(dev, "") {
|
||
|
|
+ Ok(d) => d,
|
||
|
|
+ Err(e) => {
|
||
|
|
+ if e == nix::Error::EINVAL {
|
||
|
|
+ log::error!("Bad argument {:?}, expected an absolute path in /dev/ or /sys/ or a unit name", dev);
|
||
|
|
+ } else {
|
||
|
|
+ log::error!("Unknown device {:?}", dev);
|
||
|
|
+ }
|
||
|
|
+ continue;
|
||
|
|
+ }
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
+ if self.query.is_some() {
|
||
|
|
+ let query_property = QueryProperty::new(arg_export, self.export_prefix.clone());
|
||
|
|
+ r = self.query_device(&query_type, device, query_property);
|
||
|
|
+ } else if self.attribute_walk {
|
||
|
|
+ r = print_device_chain(device);
|
||
|
|
+ } else {
|
||
|
|
+ log::error!("unknown action");
|
||
|
|
+ return Err(nix::Error::EINVAL);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ r
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ fn parse_query_type(&self, query_type: &mut QueryType) -> Result<()> {
|
||
|
|
+ match &self.query {
|
||
|
|
+ Some(q) => {
|
||
|
|
+ if q == "property" || q == "env" {
|
||
|
|
+ *query_type = QueryType::Property;
|
||
|
|
+ } else if q == "name" {
|
||
|
|
+ *query_type = QueryType::Name;
|
||
|
|
+ } else if q == "symlink" {
|
||
|
|
+ *query_type = QueryType::Symlink;
|
||
|
|
+ } else if q == "path" {
|
||
|
|
+ *query_type = QueryType::Path;
|
||
|
|
+ } else if q == "all" {
|
||
|
|
+ *query_type = QueryType::All;
|
||
|
|
+ } else {
|
||
|
|
+ log::error!("unknown query type");
|
||
|
|
+ return Err(nix::Error::EINVAL);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ None => *query_type = QueryType::All,
|
||
|
|
+ }
|
||
|
|
+ Ok(())
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ fn stat_device(&self, name: &str, export: bool) -> Result<()> {
|
||
|
|
+ let metadata = match fs::metadata(name) {
|
||
|
|
+ Ok(metadata) => metadata,
|
||
|
|
+ Err(err) => {
|
||
|
|
+ log::error!("Failed to get metadata:{:?} err:{:?}", name, err);
|
||
|
|
+ return Err(nix::Error::EINVAL);
|
||
|
|
+ }
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
+ if export {
|
||
|
|
+ match &self.export_prefix {
|
||
|
|
+ Some(p) => {
|
||
|
|
+ println!("{}MAJOR={}", p, nix::sys::stat::major(metadata.dev()));
|
||
|
|
+ println!("{}MINOR={}", p, nix::sys::stat::minor(metadata.dev()));
|
||
|
|
+ }
|
||
|
|
+ None => {
|
||
|
|
+ println!("INFO_MAJOR={}", nix::sys::stat::major(metadata.dev()));
|
||
|
|
+ println!("INFO_MINOR={}", nix::sys::stat::minor(metadata.dev()));
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ } else {
|
||
|
|
+ println!(
|
||
|
|
+ "{}:{}",
|
||
|
|
+ nix::sys::stat::major(metadata.dev()),
|
||
|
|
+ nix::sys::stat::minor(metadata.dev())
|
||
|
|
+ );
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ Ok(())
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ fn query_device(
|
||
|
|
+ &self,
|
||
|
|
+ query: &QueryType,
|
||
|
|
+ device: Device,
|
||
|
|
+ property: QueryProperty,
|
||
|
|
+ ) -> Result<()> {
|
||
|
|
+ match query {
|
||
|
|
+ QueryType::Name => {
|
||
|
|
+ let node = match device.get_devname() {
|
||
|
|
+ Ok(node) => node,
|
||
|
|
+ Err(err) => {
|
||
|
|
+ log::error!("No device node found");
|
||
|
|
+ return Err(err.get_errno());
|
||
|
|
+ }
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
+ if !self.root {
|
||
|
|
+ println!(
|
||
|
|
+ "{}",
|
||
|
|
+ Path::new(&node)
|
||
|
|
+ .strip_prefix("/dev/")
|
||
|
|
+ .unwrap()
|
||
|
|
+ .to_str()
|
||
|
|
+ .unwrap()
|
||
|
|
+ );
|
||
|
|
+ } else {
|
||
|
|
+ println!("{}", node);
|
||
|
|
+ }
|
||
|
|
+ Ok(())
|
||
|
|
+ }
|
||
|
|
+ QueryType::Symlink => {
|
||
|
|
+ let mut devlinks_str = String::new();
|
||
|
|
+ for devlink in &device.devlink_iter() {
|
||
|
|
+ if !self.root {
|
||
|
|
+ devlinks_str += Path::new(&devlink)
|
||
|
|
+ .strip_prefix("/dev/")
|
||
|
|
+ .unwrap()
|
||
|
|
+ .to_str()
|
||
|
|
+ .unwrap();
|
||
|
|
+ } else {
|
||
|
|
+ devlinks_str += devlink;
|
||
|
|
+ }
|
||
|
|
+ devlinks_str += " ";
|
||
|
|
+ }
|
||
|
|
+ devlinks_str = devlinks_str.trim_end().to_string();
|
||
|
|
+
|
||
|
|
+ println!("{}", devlinks_str);
|
||
|
|
+ Ok(())
|
||
|
|
+ }
|
||
|
|
+ QueryType::Path => {
|
||
|
|
+ let devpath = match device.get_devpath() {
|
||
|
|
+ Ok(devpath) => devpath,
|
||
|
|
+ Err(err) => {
|
||
|
|
+ log::error!("Failed to get device path");
|
||
|
|
+ return Err(err.get_errno());
|
||
|
|
+ }
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
+ println!("{}", devpath);
|
||
|
|
+ Ok(())
|
||
|
|
+ }
|
||
|
|
+ QueryType::Property => {
|
||
|
|
+ for (key, value) in &device.property_iter() {
|
||
|
|
+ if property.export {
|
||
|
|
+ match &property.export_prefix {
|
||
|
|
+ Some(export_prefix) => println!("{}{}='{}'", export_prefix, key, value),
|
||
|
|
+ None => println!("{}='{}'", key, value),
|
||
|
|
+ }
|
||
|
|
+ } else {
|
||
|
|
+ println!("{}={}", key, value);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ Ok(())
|
||
|
|
+ }
|
||
|
|
+ QueryType::All => {
|
||
|
|
+ print_record(device, "");
|
||
|
|
+ Ok(())
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+fn print_device_chain(device: Device) -> Result<()> {
|
||
|
|
+ println!(
|
||
|
|
+ "\n\
|
||
|
|
+ Devctl info starts with the device specified by the devpath and then\n\
|
||
|
|
+ walks up the chain of parent devices. It prints for every device\n\
|
||
|
|
+ found, all possible attributes in the devmaster rules key format.\n\
|
||
|
|
+ A rule to match, can be composed by the attributes of the device\n\
|
||
|
|
+ and the attributes from one single parent device.\n\
|
||
|
|
+ "
|
||
|
|
+ );
|
||
|
|
+
|
||
|
|
+ print_all_attributes(&device, false)?;
|
||
|
|
+
|
||
|
|
+ let mut child = device;
|
||
|
|
+ while let Ok(parent) = child.get_parent() {
|
||
|
|
+ print_all_attributes(&parent.borrow(), true)?;
|
||
|
|
+
|
||
|
|
+ child = parent.borrow().clone();
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ Ok(())
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+fn print_all_attributes(device: &Device, is_parent: bool) -> Result<()> {
|
||
|
|
+ let mut devpath = String::from("");
|
||
|
|
+ let mut sysname = String::from("");
|
||
|
|
+ let mut subsystem = String::from("");
|
||
|
|
+ let mut driver = String::from("");
|
||
|
|
+
|
||
|
|
+ if let Ok(value) = device.get_devpath() {
|
||
|
|
+ devpath = value;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Ok(value) = device.get_sysname() {
|
||
|
|
+ sysname = value;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Ok(value) = device.get_subsystem() {
|
||
|
|
+ subsystem = value;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Ok(value) = device.get_driver() {
|
||
|
|
+ driver = value;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if is_parent {
|
||
|
|
+ println!(" looking at parent device '{}':", devpath);
|
||
|
|
+ println!(" KERNELS=={:?}", sysname);
|
||
|
|
+ println!(" SUBSYSTEMS=={:?}", subsystem);
|
||
|
|
+ println!(" DRIVERS=={:?}", driver);
|
||
|
|
+ } else {
|
||
|
|
+ println!(" looking at device '{}':", devpath);
|
||
|
|
+ println!(" KERNEL=={:?}", sysname);
|
||
|
|
+ println!(" SUBSYSTEM=={:?}", subsystem);
|
||
|
|
+ println!(" DRIVER=={:?}", driver);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ let mut sysattrs: Vec<SysAttr> = Vec::new();
|
||
|
|
+
|
||
|
|
+ let iter = device.sysattr_iter();
|
||
|
|
+ for name in &iter {
|
||
|
|
+ if skip_attribute(name) {
|
||
|
|
+ continue;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ let value = match device.get_sysattr_value(name) {
|
||
|
|
+ Ok(value) => {
|
||
|
|
+ /* skip any values that look like a path */
|
||
|
|
+ if value.starts_with('/') {
|
||
|
|
+ continue;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ /* skip nonprintable attributes */
|
||
|
|
+ if !value
|
||
|
|
+ .chars()
|
||
|
|
+ .all(|c| 0 != unsafe { libc::isprint(c as i32) })
|
||
|
|
+ {
|
||
|
|
+ continue;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ value
|
||
|
|
+ }
|
||
|
|
+ Err(e) => {
|
||
|
|
+ if e.get_errno() == nix::Error::EACCES || e.get_errno() == nix::Error::EPERM {
|
||
|
|
+ "(not readable)".to_string()
|
||
|
|
+ } else {
|
||
|
|
+ continue;
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
+ sysattrs.push(SysAttr {
|
||
|
|
+ name: name.to_string(),
|
||
|
|
+ value,
|
||
|
|
+ });
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ sysattrs.sort_by(|a, b| a.name.cmp(&b.name));
|
||
|
|
+
|
||
|
|
+ for sysattr in sysattrs {
|
||
|
|
+ if is_parent {
|
||
|
|
+ println!(" ATTRS{{{}}}=={:?}", sysattr.name, sysattr.value);
|
||
|
|
+ } else {
|
||
|
|
+ println!(" ATTR{{{}}}=={:?}", sysattr.name, sysattr.value);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ println!();
|
||
|
|
+
|
||
|
|
+ Ok(())
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+fn skip_attribute(name: &str) -> bool {
|
||
|
|
+ /* Those are either displayed separately or should not be shown at all. */
|
||
|
|
+ if name.contains("uevent")
|
||
|
|
+ || name.contains("dev")
|
||
|
|
+ || name.contains("modalias")
|
||
|
|
+ || name.contains("resource")
|
||
|
|
+ || name.contains("driver")
|
||
|
|
+ || name.contains("subsystem")
|
||
|
|
+ || name.contains("module")
|
||
|
|
+ {
|
||
|
|
+ return true;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ false
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+fn print_record(device: Device, prefix: &str) {
|
||
|
|
+ if let Ok(devpath) = device.get_devpath() {
|
||
|
|
+ println!("{}P: {}", prefix, devpath);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Ok(sysname) = device.get_sysname() {
|
||
|
|
+ println!("{}M: {}", prefix, sysname);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Ok(sysnum) = device.get_sysnum() {
|
||
|
|
+ println!("{}R: {}", prefix, sysnum);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ let mut subsys = String::from("");
|
||
|
|
+ if let Ok(subsystem) = device.get_subsystem() {
|
||
|
|
+ subsys = subsystem.clone();
|
||
|
|
+ println!("{}U: {}", prefix, subsystem);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Ok(devnum) = device.get_devnum() {
|
||
|
|
+ if &subsys == "block" {
|
||
|
|
+ println!(
|
||
|
|
+ "{}D: b {}:{}",
|
||
|
|
+ prefix,
|
||
|
|
+ nix::sys::stat::major(devnum),
|
||
|
|
+ nix::sys::stat::minor(devnum)
|
||
|
|
+ );
|
||
|
|
+ } else {
|
||
|
|
+ println!(
|
||
|
|
+ "{}D: c {}:{}",
|
||
|
|
+ prefix,
|
||
|
|
+ nix::sys::stat::major(devnum),
|
||
|
|
+ nix::sys::stat::minor(devnum)
|
||
|
|
+ );
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Ok(ifindex) = device.get_ifindex() {
|
||
|
|
+ println!("{}I: {}", prefix, ifindex);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Ok(devname) = device.get_devname() {
|
||
|
|
+ let val = Path::new(&devname)
|
||
|
|
+ .strip_prefix("/dev/")
|
||
|
|
+ .unwrap()
|
||
|
|
+ .to_str()
|
||
|
|
+ .unwrap();
|
||
|
|
+ println!("{}N: {}", prefix, val);
|
||
|
|
+ if let Ok(i) = device.get_devlink_priority() {
|
||
|
|
+ println!("{}L: {}", prefix, i);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ for link in &device.devlink_iter() {
|
||
|
|
+ let val = Path::new(&link)
|
||
|
|
+ .strip_prefix("/dev/")
|
||
|
|
+ .unwrap()
|
||
|
|
+ .to_str()
|
||
|
|
+ .unwrap();
|
||
|
|
+ println!("{}S: {}", prefix, val);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Ok(q) = device.get_diskseq() {
|
||
|
|
+ println!("{}Q: {}", prefix, q);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Ok(driver) = device.get_driver() {
|
||
|
|
+ println!("{}V: {}", prefix, driver);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ for (key, val) in &device.property_iter() {
|
||
|
|
+ println!("{}E: {}={}", prefix, key, val);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if prefix.is_empty() {
|
||
|
|
+ println!();
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+fn export_devices() -> Result<()> {
|
||
|
|
+ let mut e = DeviceEnumerator::new();
|
||
|
|
+
|
||
|
|
+ if let Err(err) = e.allow_uninitialized() {
|
||
|
|
+ log::error!("Failed to set allowing uninitialized flag");
|
||
|
|
+ return Err(err.get_errno());
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Err(err) = e.scan_devices() {
|
||
|
|
+ log::error!("Failed to scan devices");
|
||
|
|
+ return Err(err.get_errno());
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ for device in e.iter() {
|
||
|
|
+ print_record(device.borrow().clone(), "");
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ Ok(())
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+fn cleanup_db() -> Result<()> {
|
||
|
|
+ if let Ok(mut dir1) = Dir::open("/run/devmaster/data", OFlag::O_DIRECTORY, Mode::empty()) {
|
||
|
|
+ cleanup_dir(&mut dir1, libc::S_ISVTX, 1);
|
||
|
|
+
|
||
|
|
+ if let Ok(mut dir2) = Dir::open("/run/devmaster/links", OFlag::O_DIRECTORY, Mode::empty()) {
|
||
|
|
+ cleanup_dirs_after_db_cleanup(&mut dir2, &dir1);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if let Ok(mut dir3) = Dir::open("/run/devmaster/tags", OFlag::O_DIRECTORY, Mode::empty()) {
|
||
|
|
+ cleanup_dirs_after_db_cleanup(&mut dir3, &dir1);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ /* static_node-tags is not currently implemented */
|
||
|
|
+ if let Ok(mut dir) = Dir::open(
|
||
|
|
+ "/run/devmaster/static_node-tags",
|
||
|
|
+ OFlag::O_DIRECTORY,
|
||
|
|
+ Mode::empty(),
|
||
|
|
+ ) {
|
||
|
|
+ cleanup_dir(&mut dir, 0, 2);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ /* Do not remove /run/devmaster/watch. It will be handled by devmaster well on restart.
|
||
|
|
+ * And should not be removed by external program when devmaster is running. */
|
||
|
|
+
|
||
|
|
+ Ok(())
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+fn cleanup_dir(dir: &mut Dir, mask: libc::mode_t, depth: i32) {
|
||
|
|
+ if depth <= 0 {
|
||
|
|
+ return;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ let dir_raw_fd = dir.as_raw_fd();
|
||
|
|
+ for entry in dir.iter() {
|
||
|
|
+ let dent = match entry {
|
||
|
|
+ Ok(dent) => dent,
|
||
|
|
+ Err(_) => continue,
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
+ if dot_or_dot_dot(dent.file_name().to_str().unwrap()) {
|
||
|
|
+ continue;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ let stats = match fstatat(dir_raw_fd, dent.file_name(), AtFlags::AT_SYMLINK_NOFOLLOW) {
|
||
|
|
+ Ok(stats) => stats,
|
||
|
|
+ Err(_) => continue,
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
+ if (stats.st_mode & mask) != 0 {
|
||
|
|
+ continue;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if stats.st_mode & libc::S_IFMT == libc::S_IFDIR {
|
||
|
|
+ match xopendirat(
|
||
|
|
+ dir_raw_fd,
|
||
|
|
+ dent.file_name().to_str().unwrap(),
|
||
|
|
+ OFlag::O_NOFOLLOW,
|
||
|
|
+ ) {
|
||
|
|
+ Ok(mut subdir) => cleanup_dir(&mut subdir, mask, depth - 1),
|
||
|
|
+ Err(e) => log::error!(
|
||
|
|
+ "Failed to open subdirectory {:?}, err{:?}, ignoring",
|
||
|
|
+ dent.file_name(),
|
||
|
|
+ e
|
||
|
|
+ ),
|
||
|
|
+ }
|
||
|
|
+ let _ = unlinkat(Some(dir_raw_fd), dent.file_name(), UnlinkatFlags::RemoveDir);
|
||
|
|
+ } else {
|
||
|
|
+ let _ = unlinkat(
|
||
|
|
+ Some(dir_raw_fd),
|
||
|
|
+ dent.file_name(),
|
||
|
|
+ UnlinkatFlags::NoRemoveDir,
|
||
|
|
+ );
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+fn cleanup_dirs_after_db_cleanup(dir: &mut Dir, datadir: &Dir) {
|
||
|
|
+ let dir_raw_fd = dir.as_raw_fd();
|
||
|
|
+ for entry in dir.iter() {
|
||
|
|
+ let dent = match entry {
|
||
|
|
+ Ok(dent) => dent,
|
||
|
|
+ Err(_) => continue,
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
+ if dot_or_dot_dot(dent.file_name().to_str().unwrap()) {
|
||
|
|
+ continue;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ let stats = match fstatat(dir_raw_fd, dent.file_name(), AtFlags::AT_SYMLINK_NOFOLLOW) {
|
||
|
|
+ Ok(stats) => stats,
|
||
|
|
+ Err(_) => continue,
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
+ if stats.st_mode & libc::S_IFMT == libc::S_IFDIR {
|
||
|
|
+ match xopendirat(
|
||
|
|
+ dir_raw_fd,
|
||
|
|
+ dent.file_name().to_str().unwrap(),
|
||
|
|
+ OFlag::O_NOFOLLOW,
|
||
|
|
+ ) {
|
||
|
|
+ Ok(mut subdir) => cleanup_dir_after_db_cleanup(&mut subdir, datadir),
|
||
|
|
+ Err(e) => log::error!(
|
||
|
|
+ "Failed to open subdirectory {:?}, err{:?}, ignoring",
|
||
|
|
+ dent.file_name(),
|
||
|
|
+ e
|
||
|
|
+ ),
|
||
|
|
+ }
|
||
|
|
+ let _ = unlinkat(Some(dir_raw_fd), dent.file_name(), UnlinkatFlags::RemoveDir);
|
||
|
|
+ } else {
|
||
|
|
+ let _ = unlinkat(
|
||
|
|
+ Some(dir_raw_fd),
|
||
|
|
+ dent.file_name(),
|
||
|
|
+ UnlinkatFlags::NoRemoveDir,
|
||
|
|
+ );
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+/*
|
||
|
|
+ * Assume that dir is a directory with file names matching devmaster data base
|
||
|
|
+ * entries for devices in /run/devmaster/data (such as "b8:16"), and removes
|
||
|
|
+ * all files except those that haven't been deleted in /run/devmaster/data
|
||
|
|
+ * (i.e. they were skipped during db cleanup because of the db_persist flag).
|
||
|
|
+ */
|
||
|
|
+fn cleanup_dir_after_db_cleanup(dir: &mut Dir, datadir: &Dir) {
|
||
|
|
+ let dir_raw_fd = dir.as_raw_fd();
|
||
|
|
+ for entry in dir.iter() {
|
||
|
|
+ let dent = match entry {
|
||
|
|
+ Ok(dent) => dent,
|
||
|
|
+ Err(_) => continue,
|
||
|
|
+ };
|
||
|
|
+
|
||
|
|
+ if dot_or_dot_dot(dent.file_name().to_str().unwrap()) {
|
||
|
|
+ continue;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if unsafe {
|
||
|
|
+ libc::faccessat(
|
||
|
|
+ datadir.as_raw_fd(),
|
||
|
|
+ dent.file_name().as_ptr(),
|
||
|
|
+ libc::F_OK,
|
||
|
|
+ libc::AT_SYMLINK_NOFOLLOW,
|
||
|
|
+ )
|
||
|
|
+ } >= 0
|
||
|
|
+ {
|
||
|
|
+ /* The corresponding devmaster database file still exists.
|
||
|
|
+ * Assuming the persistent flag is set for the database. */
|
||
|
|
+ continue;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ let _ = unlinkat(
|
||
|
|
+ Some(dir_raw_fd),
|
||
|
|
+ dent.file_name(),
|
||
|
|
+ UnlinkatFlags::NoRemoveDir,
|
||
|
|
+ );
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
diff --git a/exts/devmaster/src/bin/devctl/subcmds/devctl_utils.rs b/exts/devmaster/src/bin/devctl/subcmds/devctl_utils.rs
|
||
|
|
new file mode 100644
|
||
|
|
index 00000000..63b7a98e
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/exts/devmaster/src/bin/devctl/subcmds/devctl_utils.rs
|
||
|
|
@@ -0,0 +1,29 @@
|
||
|
|
+use device::Device;
|
||
|
|
+
|
||
|
|
+type Result<T> = std::result::Result<T, nix::Error>;
|
||
|
|
+
|
||
|
|
+/// find device by path or unit name
|
||
|
|
+pub fn find_device(id: &str, prefix: &str) -> Result<Device> {
|
||
|
|
+ if id.is_empty() {
|
||
|
|
+ return Err(nix::Error::EINVAL);
|
||
|
|
+ }
|
||
|
|
+ if let Ok(device) = Device::from_path(id) {
|
||
|
|
+ return Ok(device);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if !prefix.is_empty() && !id.starts_with(prefix) {
|
||
|
|
+ let path = prefix.to_string() + id;
|
||
|
|
+
|
||
|
|
+ if let Ok(device) = Device::from_path(&path) {
|
||
|
|
+ return Ok(device);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ /* Check if the argument looks like a device unit name. */
|
||
|
|
+ find_device_from_unit(id)
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+/// dbus and device unit is not currently implemented
|
||
|
|
+fn find_device_from_unit(_unit_name: &str) -> Result<Device> {
|
||
|
|
+ todo!()
|
||
|
|
+}
|
||
|
|
diff --git a/exts/devmaster/src/bin/devctl/subcmds/mod.rs b/exts/devmaster/src/bin/devctl/subcmds/mod.rs
|
||
|
|
index e1e5ad37..3d79a88d 100644
|
||
|
|
--- a/exts/devmaster/src/bin/devctl/subcmds/mod.rs
|
||
|
|
+++ b/exts/devmaster/src/bin/devctl/subcmds/mod.rs
|
||
|
|
@@ -14,6 +14,8 @@
|
||
|
|
//!
|
||
|
|
|
||
|
|
pub(crate) mod devctl_hwdb;
|
||
|
|
+pub(crate) mod devctl_info;
|
||
|
|
pub(crate) mod devctl_monitor;
|
||
|
|
pub(crate) mod devctl_test_builtin;
|
||
|
|
pub(crate) mod devctl_trigger;
|
||
|
|
+pub(self) mod devctl_utils;
|
||
|
|
diff --git a/exts/devmaster/src/lib/rules/node.rs b/exts/devmaster/src/lib/rules/node.rs
|
||
|
|
index 85005ed7..45b8b36b 100644
|
||
|
|
--- a/exts/devmaster/src/lib/rules/node.rs
|
||
|
|
+++ b/exts/devmaster/src/lib/rules/node.rs
|
||
|
|
@@ -35,7 +35,7 @@
|
||
|
|
use crate::{error::*, log_dev, log_dev_option};
|
||
|
|
use basic::fs_util::path_simplify;
|
||
|
|
use basic::fs_util::{fchmod_and_chown, futimens_opath, symlink};
|
||
|
|
-use basic::{fd_util::opendirat, fs_util::remove_dir_until};
|
||
|
|
+use basic::{fd_util::xopendirat, fs_util::remove_dir_until};
|
||
|
|
use cluFlock::ExclusiveFlock;
|
||
|
|
use device::Device;
|
||
|
|
use libc::{mode_t, S_IFBLK, S_IFCHR, S_IFLNK, S_IFMT};
|
||
|
|
@@ -584,7 +584,7 @@ pub(crate) fn find_prioritized_devnode(
|
||
|
|
dev: Rc<RefCell<Device>>,
|
||
|
|
dirfd: i32,
|
||
|
|
) -> Result<Option<String>> {
|
||
|
|
- let mut dir = opendirat(dirfd, OFlag::O_NOFOLLOW)
|
||
|
|
+ let mut dir = xopendirat(dirfd, ".", OFlag::O_NOFOLLOW)
|
||
|
|
.context(BasicSnafu)
|
||
|
|
.log_error(&format!("failed to opendirat '{}'", dirfd))?;
|
||
|
|
|
||
|
|
diff --git a/libs/basic/src/fd_util.rs b/libs/basic/src/fd_util.rs
|
||
|
|
index 1e83ef8d..8715891c 100644
|
||
|
|
--- a/libs/basic/src/fd_util.rs
|
||
|
|
+++ b/libs/basic/src/fd_util.rs
|
||
|
|
@@ -14,6 +14,7 @@
|
||
|
|
use crate::error::*;
|
||
|
|
use libc::off_t;
|
||
|
|
use nix::{
|
||
|
|
+ dir::Dir,
|
||
|
|
errno::Errno,
|
||
|
|
fcntl::{openat, FcntlArg, FdFlag, OFlag},
|
||
|
|
ioctl_read,
|
||
|
|
@@ -160,11 +161,15 @@ pub fn fd_get_diskseq(fd: RawFd) -> Result<u64> {
|
||
|
|
Ok(diskseq)
|
||
|
|
}
|
||
|
|
|
||
|
|
-/// open the directory at fd
|
||
|
|
-pub fn opendirat(dirfd: i32, flags: OFlag) -> Result<nix::dir::Dir> {
|
||
|
|
+/// open the directory at dirfd
|
||
|
|
+pub fn xopendirat(dirfd: i32, name: &str, flags: OFlag) -> Result<nix::dir::Dir> {
|
||
|
|
+ if dirfd == libc::AT_FDCWD && flags.is_empty() {
|
||
|
|
+ return Dir::open(name, flags, Mode::empty()).context(NixSnafu);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
let nfd = openat(
|
||
|
|
dirfd,
|
||
|
|
- ".",
|
||
|
|
+ name,
|
||
|
|
OFlag::O_RDONLY | OFlag::O_NONBLOCK | OFlag::O_DIRECTORY | OFlag::O_CLOEXEC | flags,
|
||
|
|
Mode::empty(),
|
||
|
|
)
|
||
|
|
@@ -183,6 +188,10 @@ pub fn file_offset_beyond_memory_size(x: off_t) -> bool {
|
||
|
|
x as u64 > usize::MAX as u64
|
||
|
|
}
|
||
|
|
|
||
|
|
+/// "." or ".." directory
|
||
|
|
+pub fn dot_or_dot_dot(name: &str) -> bool {
|
||
|
|
+ name == "." || name == ".."
|
||
|
|
+}
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use crate::fd_util::{stat_is_char, stat_is_reg};
|
||
|
|
@@ -196,7 +205,7 @@ mod tests {
|
||
|
|
path::Path,
|
||
|
|
};
|
||
|
|
|
||
|
|
- use super::opendirat;
|
||
|
|
+ use super::{dot_or_dot_dot, xopendirat};
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_stats() {
|
||
|
|
@@ -228,7 +237,7 @@ mod tests {
|
||
|
|
File::create("/tmp/test_opendirat/entry1").unwrap();
|
||
|
|
|
||
|
|
let dirfd = open("/tmp/test_opendirat", OFlag::O_DIRECTORY, Mode::empty()).unwrap();
|
||
|
|
- let mut dir = opendirat(dirfd, OFlag::O_NOFOLLOW).unwrap();
|
||
|
|
+ let mut dir = xopendirat(dirfd, ".", OFlag::O_NOFOLLOW).unwrap();
|
||
|
|
|
||
|
|
for e in dir.iter() {
|
||
|
|
let _ = e.unwrap();
|
||
|
|
@@ -236,4 +245,11 @@ mod tests {
|
||
|
|
|
||
|
|
remove_dir_all("/tmp/test_opendirat").unwrap();
|
||
|
|
}
|
||
|
|
+
|
||
|
|
+ #[test]
|
||
|
|
+ fn test_dot_or_dot_dot() {
|
||
|
|
+ assert!(dot_or_dot_dot("."));
|
||
|
|
+ assert!(dot_or_dot_dot(".."));
|
||
|
|
+ assert!(!dot_or_dot_dot("/"));
|
||
|
|
+ }
|
||
|
|
}
|
||
|
|
diff --git a/libs/device/src/device.rs b/libs/device/src/device.rs
|
||
|
|
index fe1795c0..5b1eff1f 100644
|
||
|
|
--- a/libs/device/src/device.rs
|
||
|
|
+++ b/libs/device/src/device.rs
|
||
|
|
@@ -18,8 +18,8 @@ use crate::{error::*, DeviceAction};
|
||
|
|
use basic::fs_util::{open_temporary, touch_file};
|
||
|
|
use basic::parse::{device_path_parse_devnum, parse_devnum, parse_ifindex};
|
||
|
|
use libc::{
|
||
|
|
- c_char, 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,
|
||
|
|
+ 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,
|
||
|
|
};
|
||
|
|
use nix::dir::Dir;
|
||
|
|
use nix::errno::{self, Errno};
|
||
|
|
@@ -30,6 +30,7 @@ use snafu::ResultExt;
|
||
|
|
use std::cell::{Ref, RefCell};
|
||
|
|
use std::collections::hash_set::Iter;
|
||
|
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||
|
|
+use std::ffi::CString;
|
||
|
|
use std::fs::{self, rename, OpenOptions, ReadDir};
|
||
|
|
use std::fs::{create_dir_all, File};
|
||
|
|
use std::io::{Read, Write};
|
||
|
|
@@ -2458,13 +2459,13 @@ impl Device {
|
||
|
|
|
||
|
|
let property_tags_outdated = *self.property_tags_outdated.borrow();
|
||
|
|
if property_tags_outdated {
|
||
|
|
- let all_tags: String = {
|
||
|
|
+ let mut all_tags: String = {
|
||
|
|
let all_tags = self.all_tags.borrow();
|
||
|
|
- let tags_vec = all_tags.iter().map(|s| s.as_str()).collect::<Vec<_>>();
|
||
|
|
- tags_vec.join(":")
|
||
|
|
+ all_tags.iter().map(|s| format!(":{}", s)).collect()
|
||
|
|
};
|
||
|
|
|
||
|
|
if !all_tags.is_empty() {
|
||
|
|
+ all_tags.push(':');
|
||
|
|
self.add_property_internal("TAGS", &all_tags)
|
||
|
|
.map_err(|e| Error::Nix {
|
||
|
|
msg: format!("properties_prepare failed: {}", e),
|
||
|
|
@@ -2472,13 +2473,13 @@ impl Device {
|
||
|
|
})?;
|
||
|
|
}
|
||
|
|
|
||
|
|
- let current_tags: String = {
|
||
|
|
+ let mut current_tags: String = {
|
||
|
|
let current_tags = self.current_tags.borrow();
|
||
|
|
- let tags_vec = current_tags.iter().map(|s| s.as_str()).collect::<Vec<_>>();
|
||
|
|
- tags_vec.join(":")
|
||
|
|
+ current_tags.iter().map(|s| format!(":{}", s)).collect()
|
||
|
|
};
|
||
|
|
|
||
|
|
if !current_tags.is_empty() {
|
||
|
|
+ current_tags.push(':');
|
||
|
|
self.add_property_internal("CURRENT_TAGS", ¤t_tags)
|
||
|
|
.map_err(|e| Error::Nix {
|
||
|
|
msg: format!("properties_prepare failed: {}", e),
|
||
|
|
@@ -2900,9 +2901,16 @@ impl Device {
|
||
|
|
};
|
||
|
|
|
||
|
|
if !subdir.is_empty() {
|
||
|
|
- if unsafe { faccessat(dir.as_raw_fd(), "uevent".as_ptr() as *const c_char, F_OK, 0) }
|
||
|
|
- >= 0
|
||
|
|
- {
|
||
|
|
+ let uevent_str = match CString::new("uevent") {
|
||
|
|
+ Ok(uevent_str) => uevent_str,
|
||
|
|
+ Err(e) => {
|
||
|
|
+ return Err(Error::Nix {
|
||
|
|
+ msg: format!("failed to new CString({:?}) '{}'", "uevent", e),
|
||
|
|
+ source: nix::Error::EINVAL,
|
||
|
|
+ })
|
||
|
|
+ }
|
||
|
|
+ };
|
||
|
|
+ if unsafe { faccessat(dir.as_raw_fd(), uevent_str.as_ptr(), F_OK, 0) } >= 0 {
|
||
|
|
/* skip child device */
|
||
|
|
return Ok(());
|
||
|
|
}
|
||
|
|
diff --git a/libs/device/src/device_enumerator.rs b/libs/device/src/device_enumerator.rs
|
||
|
|
index b0f98655..38956cf5 100644
|
||
|
|
--- a/libs/device/src/device_enumerator.rs
|
||
|
|
+++ b/libs/device/src/device_enumerator.rs
|
||
|
|
@@ -1143,7 +1143,7 @@ impl DeviceEnumerator {
|
||
|
|
}
|
||
|
|
|
||
|
|
/// scan devices
|
||
|
|
- pub(crate) fn scan_devices(&mut self) -> Result<(), Error> {
|
||
|
|
+ pub fn scan_devices(&mut self) -> Result<(), Error> {
|
||
|
|
if *self.scan_up_to_date.borrow() && *self.etype.borrow() == DeviceEnumerationType::Devices
|
||
|
|
{
|
||
|
|
return Ok(());
|
||
|
|
--
|
||
|
|
2.33.0
|
||
|
|
|