Skip to content

Commit 43a7000

Browse files
Merge pull request #22 from andrewdavidmackenzie/dalance-pidrusage
dalance-pidrusage and split proc_pid.rs into multiple files. Reorganized tests a bit.
2 parents b01e408 + d80c909 commit 43a7000

File tree

12 files changed

+1083
-778
lines changed

12 files changed

+1083
-778
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
[package]
22
name = "libproc"
3-
version = "0.4.0"
3+
version = "0.5.0"
44
description = "A rust wrapper of libproc to get information about running processes - currently Mac OS X only"
55
authors = ["Andrew Mackenzie <[email protected]>"]
66
repository = "https://github.com/andrewdavidmackenzie/libproc-rs"
77
readme = "README.md"
88
license = "MIT"
9+
edition = "2018"
910

1011
[dependencies]
1112
errno = "^0.1.8"

src/libproc/bsd_info.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
extern crate libc;
2+
3+
use self::libc::{uint32_t, int32_t, uint64_t, c_char, uid_t, gid_t};
4+
5+
use crate::libproc::proc_pid::{PIDInfo, PidInfoFlavor};
6+
7+
// from http://opensource.apple.com//source/xnu/xnu-1504.7.4/bsd/sys/param.h
8+
const MAXCOMLEN : usize = 16;
9+
10+
#[repr(C)]
11+
#[derive(Default)]
12+
pub struct BSDInfo {
13+
pub pbi_flags : uint32_t, // 64bit; emulated etc
14+
pub pbi_status : uint32_t,
15+
pub pbi_xstatus : uint32_t,
16+
pub pbi_pid : uint32_t,
17+
pub pbi_ppid : uint32_t,
18+
pub pbi_uid : uid_t,
19+
pub pbi_gid : gid_t,
20+
pub pbi_ruid : uid_t,
21+
pub pbi_rgid : gid_t,
22+
pub pbi_svuid : uid_t,
23+
pub pbi_svgid : gid_t,
24+
pub rfu_1 : uint32_t, // reserved
25+
pub pbi_comm : [c_char; MAXCOMLEN],
26+
pub pbi_name : [c_char; 2 * MAXCOMLEN], // empty if no name is registered
27+
pub pbi_nfiles : uint32_t,
28+
pub pbi_pgid : uint32_t,
29+
pub pbi_pjobc : uint32_t,
30+
pub e_tdev : uint32_t, // controlling tty dev
31+
pub e_tpgid : uint32_t, // tty process group id
32+
pub pbi_nice : int32_t,
33+
pub pbi_start_tvsec : uint64_t,
34+
pub pbi_start_tvusec : uint64_t
35+
}
36+
37+
impl PIDInfo for BSDInfo {
38+
fn flavor() -> PidInfoFlavor { PidInfoFlavor::TBSDInfo }
39+
}

src/libproc/file_info.rs

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
extern crate libc;
2+
3+
use std::mem;
4+
5+
use crate::libproc::helpers;
6+
use crate::libproc::proc_pid::{ListPIDInfo, PidInfoFlavor};
7+
8+
use self::libc::{c_int, c_void, int32_t, uint32_t};
9+
10+
// this extern block links to the libproc library
11+
// Original signatures of functions can be found at http://opensource.apple.com/source/Libc/Libc-594.9.4/darwin/libproc.c
12+
#[link(name = "proc", kind = "dylib")]
13+
extern {
14+
fn proc_pidfdinfo(pid : c_int, fd : c_int, flavor : c_int, buffer : *mut c_void, buffersize : c_int) -> c_int;
15+
}
16+
17+
pub enum PIDFDInfoFlavor {
18+
VNodeInfo = 1,
19+
VNodePathInfo = 2,
20+
SocketInfo = 3,
21+
PSEMInfo = 4,
22+
PSHMInfo = 5,
23+
PipeInfo = 6,
24+
KQueueInfo = 7,
25+
ATalkInfo = 8
26+
}
27+
28+
pub struct ListFDs;
29+
30+
impl ListPIDInfo for ListFDs {
31+
type Item = ProcFDInfo;
32+
fn flavor() -> PidInfoFlavor { PidInfoFlavor::ListFDs }
33+
}
34+
35+
#[repr(C)]
36+
pub struct ProcFDInfo {
37+
pub proc_fd: int32_t,
38+
pub proc_fdtype: uint32_t,
39+
}
40+
41+
#[derive(Copy, Clone, Debug)]
42+
pub enum ProcFDType {
43+
/// AppleTalk
44+
ATalk = 0,
45+
/// Vnode
46+
VNode = 1,
47+
/// Socket
48+
Socket = 2,
49+
/// POSIX shared memory
50+
PSHM = 3,
51+
/// POSIX semaphore
52+
PSEM = 4,
53+
/// Kqueue
54+
KQueue = 5,
55+
/// Pipe
56+
Pipe = 6,
57+
/// FSEvents
58+
FSEvents = 7,
59+
/// Unknown
60+
Unknown,
61+
}
62+
63+
impl From<uint32_t> for ProcFDType {
64+
fn from(value: uint32_t) -> ProcFDType {
65+
match value {
66+
0 => ProcFDType::ATalk ,
67+
1 => ProcFDType::VNode ,
68+
2 => ProcFDType::Socket ,
69+
3 => ProcFDType::PSHM ,
70+
4 => ProcFDType::PSEM ,
71+
5 => ProcFDType::KQueue ,
72+
6 => ProcFDType::Pipe ,
73+
7 => ProcFDType::FSEvents,
74+
_ => ProcFDType::Unknown ,
75+
}
76+
}
77+
}
78+
79+
// This trait is needed for polymorphism on pidfdinfo types, also abstracting flavor in order to provide
80+
// type-guaranteed flavor correctness
81+
pub trait PIDFDInfo: Default {
82+
fn flavor() -> PIDFDInfoFlavor;
83+
}
84+
85+
/// Returns the information about file descriptors of the process that match pid passed in.
86+
///
87+
/// # Examples
88+
///
89+
/// ```
90+
/// use std::io::Write;
91+
/// use std::net::TcpListener;
92+
/// use libproc::libproc::proc_pid::{listpidinfo, pidinfo, ListThreads};
93+
/// use libproc::libproc::bsd_info::{BSDInfo};
94+
/// use libproc::libproc::net_info::{SocketFDInfo, SocketInfoKind};
95+
/// use libproc::libproc::file_info::{pidfdinfo, ListFDs, ProcFDType};
96+
/// use std::process;
97+
///
98+
/// let pid = process::id() as i32;
99+
///
100+
/// // Open TCP port:8000 to test.
101+
/// let _listener = TcpListener::bind("127.0.0.1:8000");
102+
///
103+
/// if let Ok(info) = pidinfo::<BSDInfo>(pid, 0) {
104+
/// if let Ok(fds) = listpidinfo::<ListFDs>(pid, info.pbi_nfiles as usize) {
105+
/// for fd in &fds {
106+
/// match fd.proc_fdtype.into() {
107+
/// ProcFDType::Socket => {
108+
/// if let Ok(socket) = pidfdinfo::<SocketFDInfo>(pid, fd.proc_fd) {
109+
/// match socket.psi.soi_kind.into() {
110+
/// SocketInfoKind::Tcp => {
111+
/// // access to the member of `soi_proto` is unsafe becasuse of union type.
112+
/// let info = unsafe { socket.psi.soi_proto.pri_tcp };
113+
///
114+
/// // change endian and cut off because insi_lport is network endian and 16bit witdh.
115+
/// let mut port = 0;
116+
/// port |= info.tcpsi_ini.insi_lport >> 8 & 0x00ff;
117+
/// port |= info.tcpsi_ini.insi_lport << 8 & 0xff00;
118+
///
119+
/// // access to the member of `insi_laddr` is unsafe becasuse of union type.
120+
/// let s_addr = unsafe { info.tcpsi_ini.insi_laddr.ina_46.i46a_addr4.s_addr };
121+
///
122+
/// // change endian because insi_laddr is network endian.
123+
/// let mut addr = 0;
124+
/// addr |= s_addr >> 24 & 0x000000ff;
125+
/// addr |= s_addr >> 8 & 0x0000ff00;
126+
/// addr |= s_addr << 8 & 0x00ff0000;
127+
/// addr |= s_addr << 24 & 0xff000000;
128+
///
129+
/// println!("{}.{}.{}.{}:{}", addr >> 24 & 0xff, addr >> 16 & 0xff, addr >> 8 & 0xff, addr & 0xff, port);
130+
/// },
131+
/// _ => (),
132+
/// }
133+
/// }
134+
/// },
135+
/// _ => (),
136+
/// }
137+
/// }
138+
/// }
139+
/// }
140+
/// ```
141+
///
142+
pub fn pidfdinfo<T: PIDFDInfo>(pid : i32, fd: int32_t) -> Result<T, String> {
143+
let flavor = T::flavor() as i32;
144+
let buffer_size = mem::size_of::<T>() as i32;
145+
let mut pidinfo = T::default();
146+
let buffer_ptr = &mut pidinfo as *mut _ as *mut c_void;
147+
let ret: i32;
148+
149+
unsafe {
150+
ret = proc_pidfdinfo(pid, fd, flavor, buffer_ptr, buffer_size);
151+
};
152+
153+
if ret <= 0 {
154+
Err(helpers::get_errno_with_message(ret))
155+
} else {
156+
Ok(pidinfo)
157+
}
158+
}
159+
160+
#[cfg(test)]
161+
mod test {
162+
use crate::libproc::bsd_info::BSDInfo;
163+
use crate::libproc::file_info::{ListFDs, ProcFDType};
164+
use crate::libproc::net_info::{SocketFDInfo, SocketInfoKind};
165+
use crate::libproc::proc_pid::{listpidinfo, pidinfo};
166+
use super::pidfdinfo;
167+
168+
#[test]
169+
fn pidfdinfo_test() {
170+
use std::process;
171+
use std::net::TcpListener;
172+
let pid = process::id() as i32;
173+
174+
let _listener = TcpListener::bind("127.0.0.1:65535");
175+
176+
let info = pidinfo::<BSDInfo>(pid, 0).unwrap();
177+
let fds = listpidinfo::<ListFDs>(pid, info.pbi_nfiles as usize).unwrap();
178+
for fd in fds {
179+
match fd.proc_fdtype.into() {
180+
ProcFDType::Socket => {
181+
let socket = pidfdinfo::<SocketFDInfo>(pid, fd.proc_fd).unwrap();
182+
match socket.psi.soi_kind.into() {
183+
SocketInfoKind::Tcp => unsafe {
184+
let info = socket.psi.soi_proto.pri_tcp;
185+
assert_eq!(socket.psi.soi_protocol, libc::IPPROTO_TCP);
186+
assert_eq!(info.tcpsi_ini.insi_lport as u32, 65535);
187+
}
188+
_ => (),
189+
}
190+
},
191+
_ => (),
192+
}
193+
}
194+
}
195+
}

src/libproc/helpers.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
extern crate errno;
2+
3+
use self::errno::errno;
4+
5+
/*
6+
Helper function to get errno
7+
*/
8+
pub fn get_errno_with_message(ret: i32) -> String {
9+
let e = errno();
10+
let code = e.0 as i32;
11+
format!("return code = {}, errno = {}, message = '{}'", ret, code, e)
12+
}
13+
14+
/*
15+
Helper function that checks the error number and depending on the value converts a returned
16+
buffer to UTF String and returns that as the successful Result
17+
*/
18+
pub fn check_errno(ret: i32, buf: &mut Vec<u8>) -> Result<String, String> {
19+
if ret <= 0 {
20+
Err(get_errno_with_message(ret))
21+
} else {
22+
unsafe {
23+
buf.set_len(ret as usize);
24+
}
25+
26+
match String::from_utf8(buf.to_vec()) {
27+
Ok(return_value) => Ok(return_value),
28+
Err(e) => Err(format!("Invalid UTF-8 sequence: {}", e))
29+
}
30+
}
31+
}

src/libproc/kmesg_buffer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use self::libc::{uint32_t, c_int};
66
use std::{ptr, mem};
77
use std::fmt;
88

9-
use libproc::proc_pid;
9+
use crate::libproc::helpers;
1010

1111
#[cfg(test)]
1212
use std::io;
@@ -79,7 +79,7 @@ pub fn kmsgbuf() -> Result<String, String> {
7979
}
8080

8181
if ret <= 0 {
82-
Err(proc_pid::get_errno_with_message(ret))
82+
Err(helpers::get_errno_with_message(ret))
8383
} else
8484
{
8585
if message_buffer.msg_magic != MSG_MAGIC {

src/libproc/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,11 @@
11
pub mod proc_pid;
2-
pub mod kmesg_buffer;
2+
pub mod kmesg_buffer;
3+
pub mod work_queue_info;
4+
pub mod thread_info;
5+
pub mod task_info;
6+
pub mod bsd_info;
7+
pub mod pid_rusage;
8+
pub mod file_info;
9+
pub mod net_info;
10+
11+
mod helpers;

0 commit comments

Comments
 (0)