30 pub current_dir: OsString, |
30 pub current_dir: OsString, |
31 pub envs: Vec<(OsString, OsString)>, |
31 pub envs: Vec<(OsString, OsString)>, |
32 } |
32 } |
33 |
33 |
34 /// Parses "S" channel request into command type and spec. |
34 /// Parses "S" channel request into command type and spec. |
35 pub fn parse_command_spec(data: Bytes) -> io::Result<(CommandType, CommandSpec)> { |
35 pub fn parse_command_spec( |
|
36 data: Bytes, |
|
37 ) -> io::Result<(CommandType, CommandSpec)> { |
36 let mut split = data.split(|&c| c == b'\0'); |
38 let mut split = data.split(|&c| c == b'\0'); |
37 let ctype = parse_command_type(split.next().ok_or(new_parse_error("missing type"))?)?; |
39 let ctype = parse_command_type( |
|
40 split.next().ok_or(new_parse_error("missing type"))?, |
|
41 )?; |
38 let command = split.next().ok_or(new_parse_error("missing command"))?; |
42 let command = split.next().ok_or(new_parse_error("missing command"))?; |
39 let current_dir = split.next().ok_or(new_parse_error("missing current dir"))?; |
43 let current_dir = |
|
44 split.next().ok_or(new_parse_error("missing current dir"))?; |
40 |
45 |
41 let mut envs = Vec::new(); |
46 let mut envs = Vec::new(); |
42 for l in split { |
47 for l in split { |
43 let mut s = l.splitn(2, |&c| c == b'='); |
48 let mut s = l.splitn(2, |&c| c == b'='); |
44 let k = s.next().unwrap(); |
49 let k = s.next().unwrap(); |
87 let mut s = l.splitn(2, |&c| c == b' '); |
92 let mut s = l.splitn(2, |&c| c == b' '); |
88 let inst = match (s.next().unwrap(), s.next()) { |
93 let inst = match (s.next().unwrap(), s.next()) { |
89 (b"exit", Some(arg)) => decode_latin1(arg) |
94 (b"exit", Some(arg)) => decode_latin1(arg) |
90 .parse() |
95 .parse() |
91 .map(Instruction::Exit) |
96 .map(Instruction::Exit) |
92 .map_err(|_| new_parse_error(format!("invalid exit code: {:?}", arg)))?, |
97 .map_err(|_| { |
|
98 new_parse_error(format!("invalid exit code: {:?}", arg)) |
|
99 })?, |
93 (b"reconnect", None) => Instruction::Reconnect, |
100 (b"reconnect", None) => Instruction::Reconnect, |
94 (b"redirect", Some(arg)) => { |
101 (b"redirect", Some(arg)) => { |
95 Instruction::Redirect(OsStr::from_bytes(arg).to_owned().into()) |
102 Instruction::Redirect(OsStr::from_bytes(arg).to_owned().into()) |
96 } |
103 } |
97 (b"unlink", Some(arg)) => Instruction::Unlink(OsStr::from_bytes(arg).to_owned().into()), |
104 (b"unlink", Some(arg)) => { |
|
105 Instruction::Unlink(OsStr::from_bytes(arg).to_owned().into()) |
|
106 } |
98 _ => { |
107 _ => { |
99 return Err(new_parse_error(format!("unknown command: {:?}", l))); |
108 return Err(new_parse_error(format!( |
|
109 "unknown command: {:?}", |
|
110 l |
|
111 ))); |
100 } |
112 } |
101 }; |
113 }; |
102 instructions.push(inst); |
114 instructions.push(inst); |
103 } |
115 } |
104 Ok(instructions) |
116 Ok(instructions) |
116 pub fn pack_env_vars_os( |
128 pub fn pack_env_vars_os( |
117 vars: impl IntoIterator<Item = (impl AsRef<OsStr>, impl AsRef<OsStr>)>, |
129 vars: impl IntoIterator<Item = (impl AsRef<OsStr>, impl AsRef<OsStr>)>, |
118 ) -> Bytes { |
130 ) -> Bytes { |
119 let mut vars_iter = vars.into_iter(); |
131 let mut vars_iter = vars.into_iter(); |
120 if let Some((k, v)) = vars_iter.next() { |
132 if let Some((k, v)) = vars_iter.next() { |
121 let mut dst = BytesMut::with_capacity(INITIAL_PACKED_ENV_VARS_CAPACITY); |
133 let mut dst = |
|
134 BytesMut::with_capacity(INITIAL_PACKED_ENV_VARS_CAPACITY); |
122 pack_env_into(&mut dst, k.as_ref(), v.as_ref()); |
135 pack_env_into(&mut dst, k.as_ref(), v.as_ref()); |
123 for (k, v) in vars_iter { |
136 for (k, v) in vars_iter { |
124 dst.reserve(1); |
137 dst.reserve(1); |
125 dst.put_u8(b'\0'); |
138 dst.put_u8(b'\0'); |
126 pack_env_into(&mut dst, k.as_ref(), v.as_ref()); |
139 pack_env_into(&mut dst, k.as_ref(), v.as_ref()); |
143 |
156 |
144 fn decode_latin1(s: impl AsRef<[u8]>) -> String { |
157 fn decode_latin1(s: impl AsRef<[u8]>) -> String { |
145 s.as_ref().iter().map(|&c| c as char).collect() |
158 s.as_ref().iter().map(|&c| c as char).collect() |
146 } |
159 } |
147 |
160 |
148 fn new_parse_error(error: impl Into<Box<dyn error::Error + Send + Sync>>) -> io::Error { |
161 fn new_parse_error( |
|
162 error: impl Into<Box<dyn error::Error + Send + Sync>>, |
|
163 ) -> io::Error { |
149 io::Error::new(io::ErrorKind::InvalidData, error) |
164 io::Error::new(io::ErrorKind::InvalidData, error) |
150 } |
165 } |
151 |
166 |
152 #[cfg(test)] |
167 #[cfg(test)] |
153 mod tests { |
168 mod tests { |
181 |
196 |
182 #[test] |
197 #[test] |
183 fn parse_command_spec_too_short() { |
198 fn parse_command_spec_too_short() { |
184 assert!(parse_command_spec(Bytes::from_static(b"")).is_err()); |
199 assert!(parse_command_spec(Bytes::from_static(b"")).is_err()); |
185 assert!(parse_command_spec(Bytes::from_static(b"pager")).is_err()); |
200 assert!(parse_command_spec(Bytes::from_static(b"pager")).is_err()); |
186 assert!(parse_command_spec(Bytes::from_static(b"pager\0less")).is_err()); |
201 assert!( |
|
202 parse_command_spec(Bytes::from_static(b"pager\0less")).is_err() |
|
203 ); |
187 } |
204 } |
188 |
205 |
189 #[test] |
206 #[test] |
190 fn parse_command_spec_malformed_env() { |
207 fn parse_command_spec_malformed_env() { |
191 assert!(parse_command_spec(Bytes::from_static(b"pager\0less\0/tmp\0HOME")).is_err()); |
208 assert!(parse_command_spec(Bytes::from_static( |
|
209 b"pager\0less\0/tmp\0HOME" |
|
210 )) |
|
211 .is_err()); |
192 } |
212 } |
193 |
213 |
194 #[test] |
214 #[test] |
195 fn parse_command_spec_unknown_type() { |
215 fn parse_command_spec_unknown_type() { |
196 assert!(parse_command_spec(Bytes::from_static(b"paper\0less")).is_err()); |
216 assert!( |
|
217 parse_command_spec(Bytes::from_static(b"paper\0less")).is_err() |
|
218 ); |
197 } |
219 } |
198 |
220 |
199 #[test] |
221 #[test] |
200 fn parse_instructions_good() { |
222 fn parse_instructions_good() { |
201 let src = [ |
223 let src = [ |