@@ -2,6 +2,7 @@ use std::io;
2
2
use std:: io:: prelude:: * ;
3
3
use std:: fs:: { self , File , ReadDir , DirEntry } ;
4
4
use std:: path:: Path ;
5
+ use std:: vec;
5
6
use std:: io:: BufReader ;
6
7
use std:: cmp:: Ordering ;
7
8
use std:: str:: FromStr ;
@@ -16,42 +17,54 @@ use self::status::PidStatus;
16
17
use error:: { ProcError , ProcFile , ProcOper } ;
17
18
use TaskId ;
18
19
19
- fn err_str < T : ToString > ( err : T ) -> String {
20
- err. to_string ( )
21
- }
22
-
23
20
/// A struct containing information about a process.
24
21
///
25
22
/// This struct contains information from various files inside the
26
23
/// /proc/[pid] directory (for the respective pid).
27
24
#[ derive( Debug ) ]
28
25
pub struct Pid {
26
+ /// The tid of this process
27
+ pub pid : TaskId ,
29
28
/// The /proc/[pid]/stat file
30
29
pub stat : Box < PidStat > ,
31
30
/// The /proc/[pid]/status file
32
31
pub status : Box < PidStatus > ,
33
32
/// The /proc/[pid]/cmdline file
34
- pub cmdline : Vec < String >
33
+ pub cmdline : Vec < String > ,
34
+ /// If this is a thread, this is set to true.
35
+ /// Threads will never have tasks attached.
36
+ is_thread : bool ,
37
+ /// Vec of threads under /proc/[pid]/tasks/[tid]
38
+ threads : Option < Vec < Pid > > ,
35
39
}
36
40
37
41
impl Pid {
42
+ /// Create a new Pid struct for a process, given a pid.
38
43
pub fn new ( pid : TaskId ) -> Result < Self , ProcError > {
39
- let proc_dir = format ! ( "/proc/{}" , pid) ;
44
+ let pid_dir = Path :: new ( "/proc" ) ;
45
+ Self :: new_dir ( pid_dir, pid)
46
+ }
47
+
48
+ fn new_dir ( proc_dir : & Path , pid : TaskId ) -> Result < Self , ProcError > {
49
+ let proc_dir = proc_dir. join ( pid. to_string ( ) ) ;
40
50
let pid_stat = try!( PidStat :: new ( & proc_dir) ) ;
41
51
let pid_status = try!( PidStatus :: new ( & proc_dir) ) ;
42
52
let cmdline = try!( Self :: read_cmdline ( & proc_dir) ) ;
43
53
44
- Ok ( Pid {
54
+ Ok ( Pid {
55
+ pid : pid,
45
56
stat : Box :: new ( pid_stat) ,
46
57
status : Box :: new ( pid_status) ,
47
- cmdline : cmdline
58
+ cmdline : cmdline,
59
+ is_thread : false ,
60
+ threads : None ,
48
61
} )
49
62
}
50
63
51
64
/// Given a /proc/[pid] directory, read the respective /proc/[pid]/cmdline
52
65
/// file and return them in a Vec.
53
- fn read_cmdline ( proc_dir : & str ) -> Result < Vec < String > , ProcError > {
54
- File :: open ( Path :: new ( proc_dir) . join ( "cmdline" ) )
66
+ fn read_cmdline ( proc_dir : & Path ) -> Result < Vec < String > , ProcError > {
67
+ File :: open ( proc_dir. join ( "cmdline" ) )
55
68
. map_err ( |e| ProcError :: new_err ( ProcOper :: Opening , ProcFile :: PidCmdline , e) )
56
69
. and_then ( |file| {
57
70
let mut contents = Vec :: new ( ) ;
@@ -86,6 +99,27 @@ impl Pid {
86
99
PidQuery :: NoneQuery => true
87
100
}
88
101
}
102
+
103
+ pub fn tasks ( & mut self ) -> Option < Vec < Pid > > {
104
+ self . tasks_query ( PidQuery :: NoneQuery )
105
+ }
106
+
107
+ // TODO: Work out if this really should return Option<_>
108
+ // or Option<Result<Vec<Pid>>>. Otherwise the error is uncaught.
109
+ pub fn tasks_query ( & self , query : PidQuery ) -> Option < Vec < Pid > > {
110
+ if self . is_thread {
111
+ return None ;
112
+ }
113
+
114
+ PidIter :: new_tid_query ( self . pid , query. clone ( ) ) . unwrap ( )
115
+ . filter ( |p| {
116
+ let query = query. clone ( ) ;
117
+ match * p {
118
+ Ok ( ref pid) => pid. query ( & query) ,
119
+ Err ( _) => true
120
+ }
121
+ } ) . collect :: < Result < Vec < _ > , _ > > ( ) . ok ( )
122
+ }
89
123
}
90
124
91
125
impl PartialEq for Pid {
@@ -113,21 +147,42 @@ impl Ord for Pid {
113
147
/// non-trivial.
114
148
pub struct PidIter {
115
149
dir_iter : ReadDir ,
116
- query : PidQuery
150
+ query : PidQuery ,
117
151
}
118
152
119
153
impl PidIter {
120
154
/// Create a new iterator over all processes in /proc.
121
- pub fn new ( ) -> Result < Self , String > {
155
+ pub fn new ( ) -> Result < Self , ProcError > {
122
156
Self :: new_query ( PidQuery :: NoneQuery )
123
157
}
124
158
125
159
/// Create a new iterator over all processes in /proc, but only yield
126
160
/// processes that match the given query.
127
- pub fn new_query ( query : PidQuery ) -> Result < Self , String > {
161
+ pub fn new_query ( query : PidQuery ) -> Result < Self , ProcError > {
128
162
let proc_dir = Path :: new ( "/proc" ) ;
129
- let dir_iter = try!( fs:: read_dir ( proc_dir) . map_err ( err_str) ) ;
130
- Ok ( PidIter {
163
+ let dir_iter = try!(
164
+ fs:: read_dir ( proc_dir)
165
+ . map_err ( |e|
166
+ ProcError :: new ( ProcOper :: Opening , ProcFile :: ProcDir , Some ( e) , Some ( "PidIter" ) )
167
+ )
168
+ ) ;
169
+ Ok ( PidIter {
170
+ dir_iter : dir_iter,
171
+ query : query,
172
+ } )
173
+ }
174
+
175
+ fn new_tid_query ( pid : TaskId , query : PidQuery ) -> Result < Self , ProcError > {
176
+ let dir_name = format ! ( "/proc/{}/task" , pid) ;
177
+ let task_dir = Path :: new ( & dir_name) ;
178
+ let dir_iter = try!(
179
+ fs:: read_dir ( task_dir)
180
+ . map_err ( |e|
181
+ ProcError :: new ( ProcOper :: Opening , ProcFile :: PidTaskDir ,
182
+ Some ( e) , Some ( "PidIter" ) )
183
+ )
184
+ ) ;
185
+ Ok ( PidIter {
131
186
dir_iter : dir_iter,
132
187
query : query
133
188
} )
@@ -136,19 +191,21 @@ impl PidIter {
136
191
/// Given a DirEntry, try to create a Pid struct, and only return if
137
192
/// it matches the query, and is complete.
138
193
fn proc_dir_filter ( entry_opt : Result < DirEntry , io:: Error > , query : & PidQuery )
139
- -> Option < Result < Pid , String > > {
140
- // TODO: This sucks, find a better way
194
+ -> Option < Result < Pid , ProcError > > {
141
195
let file = entry_opt
142
- . map_err ( err_str)
196
+ . map_err ( |e|
197
+ ProcError :: new ( ProcOper :: Reading , ProcFile :: ProcDir , Some ( e) , Some ( "PidIter" ) )
198
+ )
143
199
. and_then ( |entry|
144
200
entry. file_name ( ) . into_string ( )
145
- . or ( Err ( "Error parsing filename" . to_owned ( ) ) )
201
+ . or ( Err ( ProcError :: new_more ( ProcOper :: Parsing , ProcFile :: ProcDir , Some ( "PidIter" ) ) ) )
146
202
) ;
147
203
148
- if file . is_err ( ) {
149
- return None ;
204
+ if let Err ( e ) = file {
205
+ return Some ( Err ( e ) ) ;
150
206
}
151
207
208
+ // Ensure filename is an integer (skip if not)
152
209
match file. unwrap ( ) . parse ( ) {
153
210
Ok ( pid) => {
154
211
// If an error is not hard (error opening or reading file),
@@ -158,7 +215,7 @@ impl PidIter {
158
215
Ok ( prc) => prc,
159
216
Err ( e) => {
160
217
if e. is_hard ( ) {
161
- return Some ( Err ( e) . map_err ( err_str ) ) ;
218
+ return Some ( Err ( e) ) ;
162
219
} else {
163
220
return None ;
164
221
}
@@ -175,7 +232,7 @@ impl PidIter {
175
232
}
176
233
177
234
impl Iterator for PidIter {
178
- type Item = Result < Pid , String > ;
235
+ type Item = Result < Pid , ProcError > ;
179
236
180
237
fn next ( & mut self ) -> Option < Self :: Item > {
181
238
for entry in self . dir_iter . by_ref ( ) {
@@ -187,12 +244,69 @@ impl Iterator for PidIter {
187
244
None
188
245
}
189
246
190
- // Size may be anywhere from 0 to number of dirs
247
+ /// Size may be anywhere from 0 to number of dirs.
191
248
fn size_hint ( & self ) -> ( usize , Option < usize > ) {
192
249
( 0 , self . dir_iter . size_hint ( ) . 1 )
193
250
}
194
251
}
195
252
253
+ /// An Iterator over threads of processes in the system.
254
+ ///
255
+ /// If a task disappears while scanning it, the partial Pid struct
256
+ /// will not be yielded. An atomic view of processes on the system seems
257
+ /// non-trivial.
258
+ pub struct TidIter {
259
+ pid_iter : PidIter ,
260
+ task_iter : Option < vec:: IntoIter < Pid > > ,
261
+ query : PidQuery ,
262
+ }
263
+
264
+ impl TidIter {
265
+ /// Create a new iterator over all tasks in /proc.
266
+ pub fn new ( ) -> Result < Self , ProcError > {
267
+ println ! ( "{:?}" , 3 ) ;
268
+ Self :: new_query ( PidQuery :: NoneQuery )
269
+ }
270
+
271
+ /// Create a new iterator over all tasks in /proc, but only yield
272
+ /// those that match the given query.
273
+ pub fn new_query ( query : PidQuery ) -> Result < Self , ProcError > {
274
+ Ok ( TidIter {
275
+ pid_iter : try!( PidIter :: new_query ( query. clone ( ) ) ) ,
276
+ task_iter : None ,
277
+ query : query,
278
+ } )
279
+ }
280
+ }
281
+
282
+ impl Iterator for TidIter {
283
+ type Item = Result < Pid , ProcError > ;
284
+
285
+ fn next ( & mut self ) -> Option < Self :: Item > {
286
+ loop {
287
+ if self . task_iter . is_none ( ) {
288
+ let pid = match self . pid_iter . next ( ) {
289
+ Some ( Ok ( pid) ) => pid,
290
+ Some ( Err ( e) ) => { return Some ( Err ( e) ) } ,
291
+ None => { return None ; }
292
+ } ;
293
+ let tasks_vec = pid. tasks_query ( self . query . clone ( ) ) ;
294
+ if let Some ( vec) = tasks_vec {
295
+ self . task_iter = Some ( vec. into_iter ( ) ) ;
296
+ }
297
+ continue ;
298
+ } else {
299
+ let next = self . task_iter . as_mut ( ) . unwrap ( ) . next ( ) ;
300
+ match next {
301
+ Some ( pid) => { return Some ( Ok ( pid) ) ; } ,
302
+ None => { self . task_iter = None ; } ,
303
+ } ;
304
+ }
305
+ }
306
+ }
307
+ }
308
+
309
+ #[ derive( Clone , Debug ) ]
196
310
/// A list of query types for process querying.
197
311
pub enum PidQuery {
198
312
/// Query by pid
0 commit comments