Initial commit

Signed-off-by: sisungo <[email protected]>
This commit is contained in:
2026-09-01 13:01:13 +00:00
commit 5163bdc367
34 changed files with 2625 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
[package]
name = "kobject-uevent"
version = "0.1.0"
edition = "2024"
[dependencies]
netlink-sys = { version = "0.9", features = ["tokio_socket"] }
rustc-hash = "2"
+87
View File
@@ -0,0 +1,87 @@
use netlink_sys::{
AsyncSocket, AsyncSocketExt, SocketAddr, TokioSocket, protocols::NETLINK_KOBJECT_UEVENT,
};
use rustc_hash::FxHashMap;
pub const MULTICAST_GROUP_KERNEL: u32 = 1;
pub struct KobjectUeventSocket(TokioSocket);
impl KobjectUeventSocket {
pub fn connect(group: u32) -> std::io::Result<Self> {
let mut socket = TokioSocket::new(NETLINK_KOBJECT_UEVENT)?;
let addr = SocketAddr::new(0, group);
socket.socket_mut().bind(&addr)?;
Ok(Self(socket))
}
pub async fn recv(&self) -> std::io::Result<KobjectUevent> {
let (data, sender) = self.0.recv_from_full().await?;
if sender.port_number() != 0 {
return Err(std::io::ErrorKind::PermissionDenied.into());
}
Ok(KobjectUevent::parse(data.as_slice()))
}
}
#[derive(Debug, Clone)]
pub struct KobjectUevent(FxHashMap<String, String>);
impl KobjectUevent {
pub fn inner(&self) -> &FxHashMap<String, String> {
&self.0
}
pub fn parse(s: &[u8]) -> Self {
let mut map = FxHashMap::default();
for kvp in s.split(|x| *x == b'\0') {
let Some(sep) = kvp.iter().position(|x| *x == b'=') else {
continue;
};
let (key, val) = kvp.split_at(sep);
let val = &val[1..];
map.insert(
String::from_utf8_lossy_owned(key.into()),
String::from_utf8_lossy_owned(val.into()),
);
}
Self(map)
}
pub fn action(&self) -> Option<&str> {
self.0.get("ACTION").map(String::as_str)
}
pub fn devpath(&self) -> Option<&str> {
self.0.get("DEVPATH").map(String::as_str)
}
pub fn subsystem(&self) -> Option<&str> {
self.0.get("SUBSYSTEM").map(String::as_str)
}
pub fn synth_uuid(&self) -> Option<&str> {
self.0.get("SYNTH_UUID").map(String::as_str)
}
pub fn major(&self) -> Option<u32> {
let major = self.0.get("MAJOR")?;
major.parse().ok()
}
pub fn minor(&self) -> Option<u32> {
let minor = self.0.get("MINOR")?;
minor.parse().ok()
}
pub fn devname(&self) -> Option<&str> {
self.0.get("DEVNAME").map(String::as_str)
}
pub fn devmode(&self) -> Option<u16> {
let devmode = self.0.get("DEVMODE")?;
let devmode = devmode.strip_prefix("0")?;
u16::from_str_radix(devmode, 8).ok()
}
pub fn seqnum(&self) -> Option<&str> {
self.0.get("SEQNUM").map(String::as_str)
}
}