Skip to main content

ProcConnector

Struct ProcConnector 

Source
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

Source

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 reading
  • NLMSG_DONE (with no payload) → silently skipped, continue reading (The kernel connector protocol uses NLMSG_DONE with a cn_msg payload for data messages, which are parsed as events.)
  • NLMSG_ERROR (non-zero) → returned as Err(Os(...))
  • NLMSG_OVERRUN → returned as Err(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?
examples/proc_watch.rs (line 30)
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}
Source

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?
examples/async_integration.rs (line 22)
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

Source

pub fn new() -> Result<Self>

Create a new ProcConnector.

This is a convenience constructor that:

  1. Creates a PF_NETLINK / SOCK_DGRAM socket of family NETLINK_CONNECTOR.
  2. Binds to the CN_IDX_PROC multicast group.
  3. Sends a PROC_CN_MCAST_LISTEN subscription message.
§Errors

Returns Error::Os if any system call fails.

Examples found in repository?
examples/proc_watch.rs (line 23)
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
Hide additional examples
examples/async_integration.rs (line 15)
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

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");
Source

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");
Source

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
  • Interrupted if EINTR — retry the call.
  • ConnectionClosed if recv returns 0.
  • Os for other system call errors.
Source

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}"),
}
Source

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?
examples/async_integration.rs (line 37)
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

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.

Trait Implementations§

Source§

impl AsFd for ProcConnector

Source§

fn as_fd(&self) -> BorrowedFd<'_>

Borrows the file descriptor. Read more
Source§

impl AsRawFd for ProcConnector

Source§

fn as_raw_fd(&self) -> RawFd

Extracts the raw file descriptor. Read more
Source§

impl Drop for ProcConnector

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.