1use std::env;
21use std::io;
22use std::fs;
23use std::path;
24use libc;
25use serde_json;
26
27use Result;
28use Error;
29
30#[derive(Serialize, Deserialize)]
31pub struct Trace {
32 pid: libc::pid_t,
33 cwd: path::PathBuf,
34 cmd: Vec<String>
35}
36
37impl Trace {
38 pub fn new(pid: libc::pid_t, cwd: path::PathBuf, cmd: Vec<String>) -> Trace {
39 Trace { pid: pid, cwd: cwd, cmd: cmd }
40 }
41
42 pub fn get_pid(&self) -> libc::pid_t {
43 self.pid
44 }
45
46 pub fn get_cwd(&self) -> &path::Path {
47 &self.cwd
48 }
49
50 pub fn get_cmd(&self) -> &[String] {
51 &self.cmd
52 }
53
54 pub fn create(args: &Vec<String>) -> Result<Trace> {
57 let pid: libc::pid_t = unsafe { libc::getpid() };
58 let cwd = env::current_dir()?;
59
60 Ok(Trace::new(pid, cwd, args.clone()))
61 }
62
63 pub fn write(target: &mut io::Write, value: &Trace) -> Result<()> {
65 let result = serde_json::to_writer(target, value)?;
66 Ok(result)
67 }
68
69 pub fn read(source: &mut io::Read) -> Result<Trace> {
71 let result = serde_json::from_reader(source)?;
72 Ok(result)
73 }
74}
75
76
77pub struct TraceDirectory {
78 input: fs::ReadDir
79}
80
81impl TraceDirectory {
82
83 pub fn new(path: &path::Path) -> Result<TraceDirectory> {
85 if path.is_dir() {
86 let input = fs::read_dir(path)?;
87 Ok(TraceDirectory { input: input })
88 } else {
89 Err(Error::RuntimeError("TraceSource should be directory".to_string()))
90 }
91 }
92
93 fn is_execution_trace(path: &path::Path) -> bool {
94 const EXTENSION: &'static str = ".process_start.json";
95
96 path.to_str().map_or(false, |str| { str.ends_with(EXTENSION) })
97 }
98}
99
100impl Iterator for TraceDirectory {
101 type Item = path::PathBuf;
102
103 fn next(&mut self) -> Option<<Self as Iterator>::Item> {
104 match self.input.next() {
105 Some(Ok(entry)) => {
106 let path = entry.path();
107 if path.is_dir() {
108 self.next()
109 } else if TraceDirectory::is_execution_trace(&path) {
110 Some(path.to_path_buf())
111 } else {
112 self.next()
113 }
114 },
115 Some(Err(_)) => self.next(),
116 _ => None
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn is_execution_trace() {
127 let valid = path::Path::new("/tmp/5432.process_start.json");
128 assert!(TraceDirectory::is_execution_trace(valid));
129 let invalid = path::Path::new("/tmp/something.json");
130 assert!(!TraceDirectory::is_execution_trace(invalid));
131 }
132
133 #[test]
134 fn read_works_on_unkown_fields() {
135 let mut data = r#"{
136 "pid": 12,
137 "ppid": 11,
138 "pppid": 10,
139 "cwd": "/usr/home/user",
140 "cmd": [
141 "program",
142 "arg1",
143 "arg2"
144 ]
145 }"#.as_bytes();
146
147 let result = Trace::read(&mut data).unwrap();
148
149 assert_eq!(12i32, result.get_pid());
150 assert_eq!(path::Path::new("/usr/home/user"), result.get_cwd());
151 assert_eq!(["program", "arg1", "arg2"], result.get_cmd());
152 }
153
154 #[test]
155 fn write_and_read_works() {
156 let expected = Trace::new(12i32,
157 path::PathBuf::from("/home/user"),
158 vec!["program".to_string(), "arg".to_string()]);
159
160 let mut data: [u8; 100] = [0; 100];
161 {
162 let mut buffer = &mut data[..];
163 let _result = Trace::write(&mut buffer, &expected).unwrap();
164 }
165 {
166 let end = data.iter().position(|&c| c == 0).unwrap();
167 let mut buffer = &data[..end];
168 let result = Trace::read(&mut buffer).unwrap();
169
170 assert_eq!(expected.get_pid(), result.get_pid());
171 assert_eq!(expected.get_cwd(), result.get_cwd());
172 assert_eq!(expected.get_cmd(), result.get_cmd());
173 }
174 }
175}