1mod binding;
2use binding::{
3 cn_msg, nlmsghdr, proc_cn_mcast_op, sockaddr_nl, CN_IDX_PROC, NETLINK_CONNECTOR,
4 PROC_CN_MCAST_LISTEN,
5};
6use libc;
7use std::collections::VecDeque;
8use std::io::{Error, Result};
9
10#[inline]
13fn nlmsg_align(len: usize) -> usize {
14 (len + 3) & !3
15}
16
17#[inline]
18fn nlmsg_hdrlen() -> usize {
19 nlmsg_align(std::mem::size_of::<nlmsghdr>())
20}
21
22#[inline]
23fn nlmsg_length(len: usize) -> usize {
24 len + nlmsg_hdrlen()
25}
26
27#[derive(Debug)]
29pub enum PidEvent {
30 Exec(libc::c_int),
32 Fork(libc::c_int),
34 Coredump(libc::c_int),
36 Exit(libc::c_int),
38}
39
40#[derive(Debug)]
43pub struct PidMonitor {
44 fd: libc::c_int,
45 id: u32,
46 queue: VecDeque<PidEvent>,
47}
48
49impl PidMonitor {
50 pub fn new() -> Result<PidMonitor> {
52 PidMonitor::from_id(std::process::id())
53 }
54
55 pub fn from_id(id: u32) -> Result<PidMonitor> {
58 let fd = unsafe {
59 libc::socket(
60 libc::PF_NETLINK,
61 libc::SOCK_DGRAM,
62 NETLINK_CONNECTOR as i32,
65 )
66 };
67 let mut nl = unsafe { std::mem::zeroed::<sockaddr_nl>() };
68 nl.nl_pid = id;
69 nl.nl_family = libc::AF_NETLINK as u16;
71 nl.nl_groups = CN_IDX_PROC;
72 if unsafe {
73 libc::bind(
74 fd,
75 &nl as *const sockaddr_nl as _,
76 std::mem::size_of_val(&nl) as _,
77 )
78 } < 0
79 {
80 return Err(Error::last_os_error());
81 }
82 let mut monitor = PidMonitor {
83 fd,
84 id,
85 queue: VecDeque::new(),
86 };
87 monitor.listen()?;
88 return Ok(monitor);
89 }
90
91 fn listen(&mut self) -> Result<()> {
93 let val = true as libc::c_int;
94 if unsafe {
95 libc::setsockopt(
96 self.fd,
97 libc::SOL_NETLINK,
98 binding::NETLINK_NO_ENOBUFS as i32,
99 &val as *const libc::c_int as _,
100 std::mem::size_of_val(&val) as _,
101 )
102 } < 0
103 {
104 return Err(std::io::Error::last_os_error());
105 }
106 let mut iov_vec = Vec::<libc::iovec>::new();
107 let mut msghdr: nlmsghdr = unsafe { std::mem::zeroed() };
109 msghdr.nlmsg_len =
110 nlmsg_length(std::mem::size_of::<cn_msg>() + std::mem::size_of::<proc_cn_mcast_op>())
111 as u32;
112 msghdr.nlmsg_pid = self.id;
113 msghdr.nlmsg_type = binding::NLMSG_DONE as u16;
115 iov_vec.push(libc::iovec {
116 iov_len: std::mem::size_of::<nlmsghdr>(),
117 iov_base: &msghdr as *const nlmsghdr as _,
118 });
119 let mut cnmesg: cn_msg = unsafe { std::mem::zeroed() };
121 cnmesg.id.idx = CN_IDX_PROC;
122 cnmesg.id.val = binding::CN_VAL_PROC;
123 cnmesg.len = std::mem::size_of::<proc_cn_mcast_op>() as u16;
124 iov_vec.push(libc::iovec {
125 iov_len: std::mem::size_of::<cn_msg>(),
126 iov_base: &cnmesg as *const cn_msg as _,
127 });
128 let op = PROC_CN_MCAST_LISTEN;
129 iov_vec.push(libc::iovec {
130 iov_len: std::mem::size_of_val(&op),
131 iov_base: &op as *const proc_cn_mcast_op as _,
132 });
133 if unsafe { libc::writev(self.fd, iov_vec.as_ptr() as _, 3) } < 0 {
134 Err(Error::last_os_error())
135 } else {
136 Ok(())
137 }
138 }
139
140 fn get_events(&mut self) -> Result<()> {
142 let page_size = std::cmp::min(unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) as usize }, 8192);
143 let mut buffer = Vec::<u32>::with_capacity(page_size);
144 let buff_size = buffer.capacity();
145 unsafe {
146 buffer.set_len(buff_size);
147 }
148 let len = unsafe { libc::recv(self.fd, buffer.as_mut_ptr() as _, buff_size * 4, 0) };
149 if len < 0 {
150 return Err(Error::last_os_error());
151 }
152 let mut header = buffer.as_ptr() as *const nlmsghdr;
153 let mut len = len as usize;
154 loop {
155 if len < nlmsg_hdrlen() {
157 break;
158 }
159 let msg_len = unsafe { (*header).nlmsg_len } as usize;
160 if len < msg_len {
161 break;
162 }
163 let msg_type = unsafe { (*header).nlmsg_type } as u32;
164 match msg_type {
165 binding::NLMSG_ERROR | binding::NLMSG_NOOP => continue,
166 _ => {
167 if let Some(pidevent) = unsafe { parse_msg(header) } {
168 self.queue.push_back(pidevent)
169 }
170 }
171 };
172 let aligned_len = nlmsg_align(msg_len);
174 header = (header as usize + aligned_len) as *const nlmsghdr;
175 match len.checked_sub(aligned_len) {
176 Some(v) => len = v,
177 None => break,
178 };
179 }
180 Ok(())
181 }
182
183 pub fn recv(&mut self) -> Option<PidEvent> {
185 if self.queue.is_empty() {
186 match self.get_events() {
187 Ok(_) => self.queue.pop_front(),
188 Err(_) => None,
189 }
190 } else {
191 self.queue.pop_front()
192 }
193 }
194}
195
196unsafe fn parse_msg(header: *const nlmsghdr) -> Option<PidEvent> {
197 let msg = (header as usize + nlmsg_length(0)) as *const cn_msg;
198 if (*msg).id.idx != binding::CN_IDX_PROC || (*msg).id.val != binding::CN_VAL_PROC {
199 return None;
200 };
201 let proc_ev = (*msg).data.as_ptr() as *const binding::proc_event;
202 match (*proc_ev).what {
203 binding::PROC_EVENT_FORK => {
204 let pid = (*proc_ev).event_data.fork.child_pid;
205 Some(PidEvent::Fork(pid))
206 }
207 binding::PROC_EVENT_EXEC => {
208 let pid = (*proc_ev).event_data.exec.process_pid;
209 Some(PidEvent::Exec(pid))
210 }
211 binding::PROC_EVENT_EXIT => {
212 let pid = (*proc_ev).event_data.exit.process_pid;
213 Some(PidEvent::Exit(pid))
214 }
215 binding::PROC_EVENT_COREDUMP => {
216 let pid = (*proc_ev).event_data.coredump.process_pid;
217 Some(PidEvent::Coredump(pid))
218 }
219 _ => None,
220 }
221}
222
223impl Drop for PidMonitor {
224 fn drop(&mut self) {
225 unsafe { libc::close(self.fd) };
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232 #[test]
233 fn it_works() {}
234}