@@ -36,6 +36,8 @@ use std::io::{BufRead, BufReader, Read};
36
36
use std:: net:: { IpAddr , Ipv4Addr , SocketAddr } ;
37
37
use std:: path:: Path ;
38
38
use std:: str:: from_utf8;
39
+ use std:: io:: ErrorKind ;
40
+ use std:: process:: exit;
39
41
40
42
// Issues blocking 0.1 release
41
43
// - Everything marked with BLOCKER
@@ -172,7 +174,7 @@ impl ProcStat {
172
174
// can't parse this file reliably. We can read the command from /proc/[pid]/comm,
173
175
// so we know exactly what to expect, but that would be a pain.
174
176
//
175
- fn read ( pid : u64 ) -> Result < Self , Box < Error > > {
177
+ fn read ( pid : u64 ) -> Result < Self , Box < dyn Error > > {
176
178
// /proc/[pid]/status contains lines of the form
177
179
//
178
180
// Name: bash
@@ -198,7 +200,7 @@ impl ProcStat {
198
200
let key = substrs[ 0 ] . to_string ( ) ;
199
201
let value = substrs[ 1 ] . trim ( ) . to_string ( ) ;
200
202
Ok ( ( key, value) )
201
- } ) . collect :: < Result < HashMap < String , String > , Box < Error > > > ( ) ?;
203
+ } ) . collect :: < Result < HashMap < String , String > , Box < dyn Error > > > ( ) ?;
202
204
203
205
Ok ( ProcStat {
204
206
pid : pid,
@@ -210,40 +212,46 @@ impl ProcStat {
210
212
format ! ( "/proc/{}/status" , pid)
211
213
}
212
214
213
- fn get_field ( & self , field : & str ) -> Result < & str , Box < Error > > {
215
+ fn get_field ( & self , field : & str ) -> Result < & str , Box < dyn Error > > {
214
216
match self . fields . get ( field) {
215
217
Some ( val) => Ok ( val) ,
216
218
None => Err ( From :: from ( ParseError :: in_file (
217
219
"status" ,
218
220
& format ! (
219
- "Missing expected field '{}' file {}" ,
221
+ "Missing expected field '{}' in file {}" ,
220
222
field,
221
223
ProcStat :: status_file( self . pid)
222
224
) ,
223
225
) ) ) ,
224
226
}
225
227
}
226
228
227
- fn ppid ( & self ) -> Result < u64 , Box < Error > > {
229
+ fn ppid ( & self ) -> Result < u64 , Box < dyn Error > > {
228
230
Ok ( self . get_field ( "PPid" ) ?. parse ( ) ?)
229
231
}
230
232
}
231
233
232
- fn print_tree ( pid_of_interest : u64 ) -> Result < ( ) , Box < Error > > {
234
+ fn print_tree ( pid_of_interest : u64 ) -> Result < ( ) , Box < dyn Error > > {
233
235
let mut child_map = HashMap :: new ( ) ; // Map of pid to pids of children
234
236
let mut parent_map = HashMap :: new ( ) ; // Map of pid to pid of parent
235
237
236
238
// Loop over all the processes listed in /proc/, find the parent of each one, and build a map
237
- // from parent to children. There doesn't seem to be more efficient way of doing this reliably.
239
+ // from parent to children. There doesn't seem to be a more efficient way of doing this
240
+ // reliably.
238
241
for entry in fs:: read_dir ( "/proc" ) ? {
239
242
let entry = entry?;
240
243
let filename = entry. file_name ( ) ;
241
244
let filename = filename. to_str ( ) . unwrap ( ) ;
242
245
if let Ok ( pid) = filename. parse :: < u64 > ( ) {
243
246
let ppid = match ProcStat :: read ( pid) {
244
- Ok ( proc_stat) => proc_stat. ppid ( ) ?, // TODO should we print error and continue?
245
- // TODO print error before continuing unless err is file not found, which could
246
- // happen if proc exited
247
+ Ok ( proc_stat) => match proc_stat. ppid ( ) {
248
+ Ok ( ppid) => ppid,
249
+ Err ( e) => {
250
+ eprintln ! ( "{}" , e. to_string( ) ) ;
251
+ continue
252
+ }
253
+ } ,
254
+ // Proc probably exited before we could read its status
247
255
Err ( _) => continue ,
248
256
} ;
249
257
child_map. entry ( ppid) . or_insert ( vec ! [ ] ) . push ( pid) ;
@@ -254,6 +262,10 @@ fn print_tree(pid_of_interest: u64) -> Result<(), Box<Error>> {
254
262
let indent_level = if pid_of_interest == 1 {
255
263
0
256
264
} else {
265
+ if !parent_map. contains_key ( & pid_of_interest) {
266
+ eprintln ! ( "No such pid {}" , pid_of_interest) ;
267
+ exit ( 1 ) ;
268
+ }
257
269
print_parents ( & parent_map, pid_of_interest)
258
270
} ;
259
271
print_children ( & child_map, pid_of_interest, indent_level) ;
@@ -263,18 +275,34 @@ fn print_tree(pid_of_interest: u64) -> Result<(), Box<Error>> {
263
275
264
276
// Print a summary of command line arguments on a single line.
265
277
fn print_cmd_summary ( pid : u64 ) {
266
- let file = File :: open ( format ! ( "/proc/{}/cmdline" , pid) ) . unwrap ( ) ;
267
- for arg in BufReader :: new ( file) . take ( 80 ) . split ( '\0' as u8 ) {
268
- print ! ( "{} " , from_utf8( & arg. unwrap( ) ) . unwrap( ) ) ;
278
+ match File :: open ( format ! ( "/proc/{}/cmdline" , pid) ) {
279
+ Ok ( file) => {
280
+ for arg in BufReader :: new ( file) . take ( 80 ) . split ( '\0' as u8 ) {
281
+ print ! ( "{} " , from_utf8( & arg. unwrap( ) ) . unwrap( ) ) ;
282
+ }
283
+ print ! ( "\n " ) ;
284
+ }
285
+ Err ( ref e) if e. kind ( ) == ErrorKind :: NotFound => {
286
+ println ! ( "<exited>" ) ;
287
+ }
288
+ Err ( e) => {
289
+ println ! ( "<error reading cmdline>" ) ;
290
+ eprintln ! ( "{}" , e. to_string( ) ) ;
291
+ }
269
292
}
270
- print ! ( "\n " ) ;
271
293
}
272
294
273
295
// Returns the current indentation level
274
296
fn print_parents ( parent_map : & HashMap < u64 , u64 > , pid : u64 ) -> u64 {
275
- // TODO need to handle the case where the parent exited before we could read the parent's
276
- // parent.
277
- let ppid = * parent_map. get ( & pid) . unwrap ( ) ;
297
+ let ppid = match parent_map. get ( & pid) {
298
+ Some ( ppid) => * ppid,
299
+ // Some child process listed 'pid' as its parent, but 'pid' exited before we could read its
300
+ // parent. The child of 'pid' will have been re-parented, and the new parent will be 'init'.
301
+ // It's actually a bit more complicated (see find_new_reaper() in the kernel), and there is
302
+ // one case we might want to handle better: when a child is re-parented to another thread in
303
+ // the thread group.
304
+ None => 1
305
+ } ;
278
306
279
307
// We've reached the top of the process tree. Don't bother printing the parent if the parent
280
308
// is pid 1. Typically pid 1 didn't really start the process in question.
@@ -321,7 +349,6 @@ enum PosixFileType {
321
349
// form 'anon_inode:[eventpoll]' TODO better comment
322
350
#[ derive( PartialEq ) ]
323
351
enum AnonFileType {
324
- Bpf ,
325
352
Epoll ,
326
353
Unknown ( String ) ,
327
354
}
@@ -401,7 +428,6 @@ fn print_file_type(file_type: &FileType) -> String {
401
428
FileType :: Posix ( PosixFileType :: Fifo ) => "S_IFIFO" . into ( ) ,
402
429
FileType :: Posix ( PosixFileType :: Unknown ( x) ) => format ! ( "UNKNOWN_TYPE(mode={})" , x) ,
403
430
FileType :: Anon ( AnonFileType :: Epoll ) => "anon_inode(epoll)" . into ( ) ,
404
- FileType :: Anon ( AnonFileType :: Bpf ) => "anon_inode(bpf)" . into ( ) ,
405
431
FileType :: Anon ( AnonFileType :: Unknown ( s) ) => format ! ( "anon_inode({})" , s) ,
406
432
FileType :: Unknown => "UNKNOWN_TYPE" . into ( ) ,
407
433
}
@@ -471,7 +497,13 @@ fn get_flags(pid: u64, fd: u64) -> u64 {
471
497
fn print_file ( pid : u64 , fd : u64 , sockets : & HashMap < u64 , SockInfo > ) {
472
498
let link_path_str = format ! ( "/proc/{}/fd/{}" , pid, fd) ;
473
499
let link_path = Path :: new ( & link_path_str) ;
474
- let stat_info = stat ( link_path) . unwrap ( ) ;
500
+ let stat_info = match stat ( link_path) {
501
+ Err ( e) => {
502
+ eprintln ! ( "failed to stat {}: {}" , & link_path_str, e) ;
503
+ return ;
504
+ } ,
505
+ Ok ( stat_info) => stat_info,
506
+ } ;
475
507
476
508
let file_type = file_type ( stat_info. st_mode , & link_path) ;
477
509
@@ -515,28 +547,14 @@ fn print_file(pid: u64, fd: u64, sockets: &HashMap<u64, SockInfo>) {
515
547
}
516
548
} ,
517
549
_ => {
518
- let path = fs:: read_link ( link_path) . unwrap ( ) ;
519
- print ! ( " {}\n " , path. to_str( ) . unwrap( ) ) ;
550
+ match fs:: read_link ( link_path) {
551
+ Ok ( path) => println ! ( " {}" , path. to_string_lossy( ) ) ,
552
+ Err ( e) => eprintln ! ( "failed to readlink {}: {}" , & link_path_str, e) ,
553
+ }
520
554
}
521
555
}
522
556
}
523
557
524
- // Corresponds to definitions in include/net/tcp_states.h in the kernel
525
- enum TcpSockState {
526
- Established = 1 ,
527
- SynSent ,
528
- SynRecv ,
529
- FinWait1 ,
530
- FinWait2 ,
531
- TimeWait ,
532
- Close ,
533
- CloseWait ,
534
- LastAck ,
535
- Listen ,
536
- Closing ,
537
- NewSynRecv ,
538
- }
539
-
540
558
#[ derive( Debug ) ]
541
559
struct SockInfo {
542
560
family : AddressFamily ,
@@ -682,7 +700,7 @@ fn parse_ipv4_sock_addr(s: &str) -> Result<SocketAddr, ParseError> {
682
700
Ok ( SocketAddr :: new ( IpAddr :: V4 ( addr) , port) )
683
701
}
684
702
685
- fn fetch_sock_info ( pid : u64 ) -> Result < HashMap < u64 , SockInfo > , Box < Error > > {
703
+ fn fetch_sock_info ( pid : u64 ) -> Result < HashMap < u64 , SockInfo > , Box < dyn Error > > {
686
704
let file = File :: open ( format ! ( "/proc/{}/net/unix" , pid) ) . unwrap ( ) ;
687
705
let mut sockets = BufReader :: new ( file)
688
706
. lines ( )
@@ -784,28 +802,41 @@ fn fetch_sock_info(pid: u64) -> Result<HashMap<u64, SockInfo>, Box<Error>> {
784
802
* sockname: AF_INET6 :: port: 8341
785
803
*/
786
804
787
- fn print_files ( pid : u64 ) {
805
+ fn print_files ( pid : u64 ) -> bool {
806
+
807
+ let proc_dir = format ! ( "/proc/{}/" , pid) ;
808
+ if !Path :: new ( & proc_dir) . exists ( ) {
809
+ eprintln ! ( "No such directory {}" , & proc_dir) ;
810
+ return false ;
811
+ }
812
+
788
813
print_proc_summary ( pid) ;
789
814
790
815
// TODO print current rlimit
791
816
792
- // TODO BLOCKER handle permission errors by printing an error instead of just
793
- // not printing anything
794
-
795
817
let sockets = fetch_sock_info ( pid) . unwrap ( ) ;
796
818
797
- if let Ok ( entries) = fs:: read_dir ( format ! ( "/proc/{}/fd/" , pid) ) {
819
+ let fd_dir = format ! ( "/proc/{}/fd/" , pid) ;
820
+ let readdir_res = fs:: read_dir ( & fd_dir) . and_then ( |entries| {
798
821
for entry in entries {
799
- let entry = entry. unwrap ( ) ;
822
+ let entry = entry? ;
800
823
let filename = entry. file_name ( ) ;
801
- let filename = filename. to_str ( ) . unwrap ( ) ;
802
- if let Ok ( fd) = filename. parse :: < u64 > ( ) {
824
+ let filename = filename. to_string_lossy ( ) ;
825
+ if let Ok ( fd) = ( & filename) . parse :: < u64 > ( ) {
803
826
print_file ( pid, fd, & sockets) ;
804
827
} else {
805
- eprint ! ( "Unexpected file /proc/pid/fd/{} found" , filename) ;
828
+ eprint ! ( "Unexpected file /proc/[ pid] /fd/{} found" , & filename) ;
806
829
}
807
- }
830
+ } ;
831
+ Ok ( ( ) )
832
+ } ) ;
833
+
834
+ if let Err ( e) = readdir_res {
835
+ eprintln ! ( "Unable to read {}: {}" , & fd_dir, e) ;
836
+ return false ;
808
837
}
838
+
839
+ return true ;
809
840
}
810
841
811
842
pub fn pargs_main ( ) {
@@ -913,9 +944,14 @@ pub fn pfiles_main() {
913
944
usage_err ( program, opts) ;
914
945
}
915
946
947
+ let mut error = false ;
916
948
for arg in & matches. free {
917
949
let pid = arg. parse :: < u64 > ( ) . unwrap ( ) ;
918
- print_files ( pid) ;
950
+ error = error || !print_files ( pid) ;
951
+ }
952
+
953
+ if error {
954
+ exit ( 1 ) ;
919
955
}
920
956
}
921
957
@@ -956,6 +992,7 @@ pub fn ptree_main() {
956
992
}
957
993
}
958
994
995
+ #[ cfg( test) ]
959
996
mod test {
960
997
use super :: * ;
961
998
use std:: net:: SocketAddr ;
0 commit comments