pub struct ProcConnector { /* private fields */ }Expand description
A safe handle to a Linux Netlink Proc Connector socket.
The socket is created, bound, and subscribed in new(). On drop, the
subscription is cancelled and the file descriptor is closed automatically.
§Examples
use proc_connector::ProcConnector;
let conn = ProcConnector::new().unwrap();
let mut buf = vec![0u8; 4096];
loop {
match conn.recv(&mut buf) {
Ok(event) => println!("got event: {event:?}"),
Err(e) => eprintln!("error: {e}"),
}
}Implementations§
Source§impl ProcConnector
impl ProcConnector
Sourcepub fn recv(&self, buf: &mut [u8]) -> Result<ProcEvent>
pub fn recv(&self, buf: &mut [u8]) -> Result<ProcEvent>
Receive and parse the next process event.
buf is the receive buffer provided by the caller (allocation control).
A buffer of at least 4096 bytes (one page) is recommended.
This method handles all netlink control messages internally:
NLMSG_NOOP→ silently skipped, continue readingNLMSG_DONE(with no payload) → silently skipped, continue reading (The kernel connector protocol usesNLMSG_DONEwith a cn_msg payload for data messages, which are parsed as events.)NLMSG_ERROR(non-zero) → returned asErr(Os(...))NLMSG_OVERRUN→ returned asErr(Overrun)- Valid data → parsed into
ProcEvent
§Errors
See [recv_raw] for system-level errors.
Additionally returns BufferTooSmall if the buffer is too small
to hold even a single netlink header.
§Example
use proc_connector::ProcConnector;
let conn = ProcConnector::new().unwrap();
let mut buf = [0u8; 4096];
loop {
match conn.recv(&mut buf) {
Ok(event) => println!("{event}"),
Err(e) => { eprintln!("{e}"); break; }
}
}Examples found in repository?
21fn main() {
22 // Create connector (requires root / CAP_NET_ADMIN)
23 let conn = ProcConnector::new().expect("failed to create proc connector (try as root)");
24 let mut buf = vec![0u8; 4096];
25
26 println!("listening for process events... (Ctrl+C to stop)");
27
28 // Example 1: blocking recv loop
29 loop {
30 match conn.recv(&mut buf) {
31 Ok(event) => {
32 let now = humantime_or_iso(Instant::now());
33 println!("[{now}] {event}");
34 }
35 Err(e) => {
36 eprintln!("recv error: {e}");
37 break;
38 }
39 }
40 }
41}Sourcepub fn recv_timeout(
&self,
buf: &mut [u8],
timeout: Duration,
) -> Result<Option<ProcEvent>>
pub fn recv_timeout( &self, buf: &mut [u8], timeout: Duration, ) -> Result<Option<ProcEvent>>
Receive and parse the next process event with a timeout.
Returns Ok(None) if the timeout expires before an event is available.
Unlike recv(), this method returns Ok(None) on timeout instead of
blocking indefinitely. It properly loops past netlink control messages
(NLMSG_NOOP, NLMSG_DONE, NLMSG_ERROR-ACK) just like recv() does.
§Errors
See [recv_timeout] for system-level errors.
Examples found in repository?
14fn main() {
15 let conn = ProcConnector::new().expect("failed to create proc connector (try as root)");
16 let mut buf = vec![0u8; 4096];
17
18 println!("polling for events with 2s timeout... (Ctrl+C to stop)");
19
20 // Example: poll-based timeout (no external dependencies needed)
21 loop {
22 match conn.recv_timeout(&mut buf, Duration::from_secs(2)) {
23 Ok(Some(event)) => println!("{event}"),
24 Ok(None) => {
25 // Timeout expired, do other work
26 print!(".");
27 std::io::Write::flush(&mut std::io::stdout()).ok();
28 }
29 Err(e) => {
30 eprintln!("error: {e}");
31 break;
32 }
33 }
34 }
35
36 // The raw fd can be used with tokio::AsyncFd, mio, etc.:
37 let _raw_fd = conn.as_raw_fd();
38 // With tokio:
39 // let async_fd = tokio::io::unix::AsyncFd::new(conn)?;
40 // loop {
41 // let mut guard = async_fd.readable().await?;
42 // match conn.recv(&mut buf) { ... }
43 // guard.clear_ready();
44 // }
45}Source§impl ProcConnector
impl ProcConnector
Sourcepub fn new() -> Result<Self>
pub fn new() -> Result<Self>
Create a new ProcConnector.
This is a convenience constructor that:
- Creates a
PF_NETLINK/SOCK_DGRAMsocket of familyNETLINK_CONNECTOR. - Binds to the
CN_IDX_PROCmulticast group. - Sends a
PROC_CN_MCAST_LISTENsubscription message.
§Errors
Returns Error::Os if any system call fails.
Examples found in repository?
21fn main() {
22 // Create connector (requires root / CAP_NET_ADMIN)
23 let conn = ProcConnector::new().expect("failed to create proc connector (try as root)");
24 let mut buf = vec![0u8; 4096];
25
26 println!("listening for process events... (Ctrl+C to stop)");
27
28 // Example 1: blocking recv loop
29 loop {
30 match conn.recv(&mut buf) {
31 Ok(event) => {
32 let now = humantime_or_iso(Instant::now());
33 println!("[{now}] {event}");
34 }
35 Err(e) => {
36 eprintln!("recv error: {e}");
37 break;
38 }
39 }
40 }
41}More examples
14fn main() {
15 let conn = ProcConnector::new().expect("failed to create proc connector (try as root)");
16 let mut buf = vec![0u8; 4096];
17
18 println!("polling for events with 2s timeout... (Ctrl+C to stop)");
19
20 // Example: poll-based timeout (no external dependencies needed)
21 loop {
22 match conn.recv_timeout(&mut buf, Duration::from_secs(2)) {
23 Ok(Some(event)) => println!("{event}"),
24 Ok(None) => {
25 // Timeout expired, do other work
26 print!(".");
27 std::io::Write::flush(&mut std::io::stdout()).ok();
28 }
29 Err(e) => {
30 eprintln!("error: {e}");
31 break;
32 }
33 }
34 }
35
36 // The raw fd can be used with tokio::AsyncFd, mio, etc.:
37 let _raw_fd = conn.as_raw_fd();
38 // With tokio:
39 // let async_fd = tokio::io::unix::AsyncFd::new(conn)?;
40 // loop {
41 // let mut guard = async_fd.readable().await?;
42 // match conn.recv(&mut buf) { ... }
43 // guard.clear_ready();
44 // }
45}Sourcepub fn subscribe(&self) -> Result<()>
pub fn subscribe(&self) -> Result<()>
(Re-)subscribe to process events.
Sends a PROC_CN_MCAST_LISTEN message via the netlink socket.
This is safe to call multiple times (e.g. after a reconnection).
§Example
let mut conn = ProcConnector::new().unwrap();
conn.subscribe().expect("subscribe");Sourcepub fn unsubscribe(&self) -> Result<()>
pub fn unsubscribe(&self) -> Result<()>
Unsubscribe from process events.
Sends a PROC_CN_MCAST_IGNORE message. Automatically called on drop.
§Example
let conn = ProcConnector::new().unwrap();
conn.unsubscribe().expect("unsubscribe");
// Re-subscribe later:
conn.subscribe().expect("re-subscribe");Sourcepub fn recv_raw(&self, buf: &mut [u8]) -> Result<usize>
pub fn recv_raw(&self, buf: &mut [u8]) -> Result<usize>
Receive a raw netlink message into buf.
On success returns the number of bytes written to buf.
This is a thin wrapper around recv(2). The caller is responsible
for providing a sufficiently large buffer (a page size, 4096 bytes,
is a safe default).
§Errors
InterruptedifEINTR— retry the call.ConnectionClosedif recv returns 0.Osfor other system call errors.
Sourcepub fn recv_raw_timeout(
&self,
buf: &mut [u8],
timeout: Duration,
) -> Result<Option<usize>>
pub fn recv_raw_timeout( &self, buf: &mut [u8], timeout: Duration, ) -> Result<Option<usize>>
Receive a raw netlink message with a timeout.
Returns Ok(None) if the timeout expires before data is available.
Otherwise behaves the same as recv_raw.
Uses poll(2) internally and only calls recv when data is ready.
§Example
let conn = ProcConnector::new().unwrap();
let mut buf = vec![0u8; 4096];
match conn.recv_timeout(&mut buf, Duration::from_secs(5)) {
Ok(Some(event)) => println!("{event}"),
Ok(None) => eprintln!("timeout, no event in 5s"),
Err(e) => eprintln!("error: {e}"),
}Sourcepub fn as_raw_fd(&self) -> RawFd
pub fn as_raw_fd(&self) -> RawFd
Expose the raw file descriptor for integration with async runtimes
(tokio::AsyncFd, mio, etc.).
The returned RawFd remains valid for the lifetime of this
ProcConnector. Do not close it manually.
§Example
let conn = ProcConnector::new().unwrap();
let raw = conn.as_raw_fd();
assert!(raw >= 0);
// Use with tokio:
// let async_fd = tokio::io::unix::AsyncFd::new(conn).unwrap();Examples found in repository?
14fn main() {
15 let conn = ProcConnector::new().expect("failed to create proc connector (try as root)");
16 let mut buf = vec![0u8; 4096];
17
18 println!("polling for events with 2s timeout... (Ctrl+C to stop)");
19
20 // Example: poll-based timeout (no external dependencies needed)
21 loop {
22 match conn.recv_timeout(&mut buf, Duration::from_secs(2)) {
23 Ok(Some(event)) => println!("{event}"),
24 Ok(None) => {
25 // Timeout expired, do other work
26 print!(".");
27 std::io::Write::flush(&mut std::io::stdout()).ok();
28 }
29 Err(e) => {
30 eprintln!("error: {e}");
31 break;
32 }
33 }
34 }
35
36 // The raw fd can be used with tokio::AsyncFd, mio, etc.:
37 let _raw_fd = conn.as_raw_fd();
38 // With tokio:
39 // let async_fd = tokio::io::unix::AsyncFd::new(conn)?;
40 // loop {
41 // let mut guard = async_fd.readable().await?;
42 // match conn.recv(&mut buf) { ... }
43 // guard.clear_ready();
44 // }
45}Sourcepub fn set_nonblocking(&self) -> Result<()>
pub fn set_nonblocking(&self) -> Result<()>
Set the socket to non-blocking mode.
After calling this, recv_raw will return Error::WouldBlock
when no data is available, instead of blocking.
§Errors
Returns Error::Os if fcntl(2) fails.