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+ }
0 commit comments