]>
code.octet-stream.net Git - netwatcher/blob - src/watch_linux.rs
efc3842482207998e40f36e868e259af4bc44c72
1 use std
::os
::fd
::AsRawFd
;
2 use std
::os
::fd
::OwnedFd
;
4 use nix
::libc
::RTMGRP_IPV4_IFADDR
;
5 use nix
::libc
::RTMGRP_IPV6_IFADDR
;
6 use nix
::libc
::RTMGRP_LINK
;
7 use nix
::sys
::socket
::bind
;
8 use nix
::sys
::socket
::recv
;
9 use nix
::sys
::socket
::socket
;
10 use nix
::sys
::socket
::AddressFamily
;
11 use nix
::sys
::socket
::MsgFlags
;
12 use nix
::sys
::socket
::NetlinkAddr
;
13 use nix
::sys
::socket
::SockFlag
;
14 use nix
::sys
::socket
::SockProtocol
;
15 use nix
::sys
::socket
::SockType
;
21 pub(crate) struct WatchHandle
{
22 // PROBLEM: close() doesn't cancel recv() for a netlink socket
23 // SOLUTION: open a pipe() and use poll() inside the thread to watch for cancellation too
27 pub(crate) fn watch_interfaces
<F
: FnMut(Update
) + Send
+ '
static>(
29 ) -> Result
<WatchHandle
, Error
> {
30 let sockfd
= start_watcher_thread(callback
)?
;
31 Ok(WatchHandle
{ sockfd
})
34 fn start_watcher_thread
<F
: FnMut(Update
) + Send
+ '
static>(mut callback
: F
) -> Result
<OwnedFd
, Error
> {
35 let sockfd
= socket(AddressFamily
::Netlink
, SockType
::Raw
, SockFlag
::empty(), Some(SockProtocol
::NetlinkRoute
))
36 .map_err(|_
| Error
::Internal
)?
; // TODO: proper errors
37 let sa_nl
= NetlinkAddr
::new(0, (RTMGRP_LINK
| RTMGRP_IPV4_IFADDR
| RTMGRP_IPV6_IFADDR
) as u32);
38 bind(sockfd
.as_raw_fd(), &sa_nl
).map_err(|_
| Error
::Internal
)?
; // TODO: proper errors
39 let fd
= sockfd
.as_raw_fd();
40 println
!("netlink socket on fd {}", fd
);
42 std
::thread
::spawn(move || {
43 println
!("watch thread running");
44 let mut prev_list
= List
::default();
45 let mut buf
= [0u8; 4096];
46 let mut handle_update
= move |new_list
: List
| {
47 if new_list
== prev_list
{
51 interfaces
: new_list
.0.clone(),
52 diff
: new_list
.diff_from(&prev_list
),
58 if let Ok(initial
) = crate::list
::list_interfaces() {
59 handle_update(initial
);
62 while let Ok(n
) = recv(fd
, &mut buf
, MsgFlags
::empty()) {
63 println
!("something on the netlink socket: {} bytes", n
);
64 let Ok(new_list
) = crate::list
::list_interfaces() else {
67 handle_update(new_list
);
69 println
!("netlink recv thread terminating");