Mercurial > crates > nonstick
comparison src/libpam/question.rs @ 89:dd3e9c4bcde3
Simplify memory management in Questions.
When we're sending Questions to the client, we don't need them to be
C-managed, we just need the pointers going to the right place.
This replaces a bunch of Question management cruft with Vecs and Boxes.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Fri, 13 Jun 2025 05:22:48 -0400 |
parents | 05291b601f0a |
children | efc2b56c8928 |
comparison
equal
deleted
inserted
replaced
88:c9fc7e6257d3 | 89:dd3e9c4bcde3 |
---|---|
9 use crate::libpam::{memory, pam_ffi}; | 9 use crate::libpam::{memory, pam_ffi}; |
10 use crate::ErrorCode; | 10 use crate::ErrorCode; |
11 use crate::Result; | 11 use crate::Result; |
12 use num_enum::{IntoPrimitive, TryFromPrimitive}; | 12 use num_enum::{IntoPrimitive, TryFromPrimitive}; |
13 use std::ffi::{c_void, CStr}; | 13 use std::ffi::{c_void, CStr}; |
14 use std::{iter, ptr, slice}; | 14 use std::{ptr, slice}; |
15 | 15 |
16 /// Abstraction of a collection of questions to be sent in a PAM conversation. | 16 /// Abstraction of a collection of questions to be sent in a PAM conversation. |
17 /// | 17 /// |
18 /// The PAM C API conversation function looks like this: | 18 /// The PAM C API conversation function looks like this: |
19 /// | 19 /// |
30 /// is treated as a pointer-to-pointers, like `int argc, char **argv`. | 30 /// is treated as a pointer-to-pointers, like `int argc, char **argv`. |
31 /// (In this situation, the value of `Questions.indirect` is | 31 /// (In this situation, the value of `Questions.indirect` is |
32 /// the pointer passed to `pam_conv`.) | 32 /// the pointer passed to `pam_conv`.) |
33 /// | 33 /// |
34 /// ```text | 34 /// ```text |
35 /// ╔═ Questions ═╗ points to ┌─ Indirect ─┐ ╔═ Question ═╗ | 35 /// points to ┌───────────────┐ ╔═ Question ═╗ |
36 /// ║ indirect ┄┄┄╫┄┄┄┄┄┄┄┄┄┄┄> │ base[0] ┄┄┄┼┄┄┄┄┄> ║ style ║ | 36 /// questions ┄┄┄┄┄┄┄┄┄┄> │ questions[0] ┄┼┄┄┄┄> ║ style ║ |
37 /// ║ count ║ │ base[1] ┄┄┄┼┄┄┄╮ ║ data ┄┄┄┄┄┄╫┄┄> ... | 37 /// │ questions[1] ┄┼┄┄┄╮ ║ data ┄┄┄┄┄┄╫┄┄> ... |
38 /// ╚═════════════╝ │ ... │ ┆ ╚════════════╝ | 38 /// │ ... │ ┆ ╚════════════╝ |
39 /// ┆ | 39 /// ┆ |
40 /// ┆ ╔═ Question ═╗ | 40 /// ┆ ╔═ Question ═╗ |
41 /// ╰┄┄> ║ style ║ | 41 /// ╰┄┄> ║ style ║ |
42 /// ║ data ┄┄┄┄┄┄╫┄┄> ... | 42 /// ║ data ┄┄┄┄┄┄╫┄┄> ... |
43 /// ╚════════════╝ | 43 /// ╚════════════╝ |
44 /// ``` | 44 /// ``` |
45 /// | 45 /// |
46 /// On OpenPAM and other compatible implementations (like Solaris), | 46 /// On OpenPAM and other compatible implementations (like Solaris), |
47 /// `messages` is a pointer-to-pointer-to-array. This appears to be | 47 /// `messages` is a pointer-to-pointer-to-array. This appears to be |
48 /// the correct implementation as required by the XSSO specification. | 48 /// the correct implementation as required by the XSSO specification. |
49 /// | 49 /// |
50 /// ```text | 50 /// ```text |
51 /// ╔═ Questions ═╗ points to ┌─ Indirect ─┐ ╔═ Question[] ═╗ | 51 /// points to ┌─────────────┐ ╔═ Question[] ═╗ |
52 /// ║ indirect ┄┄┄╫┄┄┄┄┄┄┄┄┄┄┄> │ base ┄┄┄┄┄┄┼┄┄┄┄┄> ║ style ║ | 52 /// questions ┄┄┄┄┄┄┄┄┄┄> │ *questions ┄┼┄┄┄┄┄> ║ style ║ |
53 /// ║ count ║ └────────────┘ ║ data ┄┄┄┄┄┄┄┄╫┄┄> ... | 53 /// └─────────────┘ ║ data ┄┄┄┄┄┄┄┄╫┄┄> ... |
54 /// ╚═════════════╝ ╟──────────────╢ | 54 /// ╟──────────────╢ |
55 /// ║ style ║ | 55 /// ║ style ║ |
56 /// ║ data ┄┄┄┄┄┄┄┄╫┄┄> ... | 56 /// ║ data ┄┄┄┄┄┄┄┄╫┄┄> ... |
57 /// ╟──────────────╢ | 57 /// ╟──────────────╢ |
58 /// ║ ... ║ | 58 /// ║ ... ║ |
59 /// ``` | 59 /// ``` |
60 #[derive(Debug)] | 60 pub trait QuestionsTrait { |
61 pub struct GenericQuestions<I: IndirectTrait> { | 61 /// Allocates memory for this indirector and all its members. |
62 /// An indirection to the questions themselves, stored on the C heap. | 62 fn new(messages: &[Message]) -> Result<Self> |
63 indirect: *mut I, | 63 where |
64 /// The number of questions. | 64 Self: Sized; |
65 count: usize, | 65 |
66 } | 66 /// Gets the pointer that is passed . |
67 | 67 fn ptr(&self) -> *const *const Question; |
68 impl<I: IndirectTrait> GenericQuestions<I> { | 68 |
69 /// Stores the provided questions on the C heap. | 69 /// Converts a pointer into a borrowed list of Questions. |
70 pub fn new(messages: &[Message]) -> Result<Self> { | |
71 let count = messages.len(); | |
72 let mut ret = Self { | |
73 indirect: I::alloc(count), | |
74 count, | |
75 }; | |
76 // Even if we fail partway through this, all our memory will be freed. | |
77 for (question, message) in iter::zip(ret.iter_mut(), messages) { | |
78 question.try_fill(message)? | |
79 } | |
80 Ok(ret) | |
81 } | |
82 | |
83 /// The pointer to the thing with the actual list. | |
84 pub fn indirect(&self) -> *const *const Question { | |
85 self.indirect.cast() | |
86 } | |
87 | |
88 pub fn iter(&self) -> impl Iterator<Item = &Question> { | |
89 // SAFETY: we're iterating over an amount we know. | |
90 unsafe { (*self.indirect).iter(self.count) } | |
91 } | |
92 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Question> { | |
93 // SAFETY: we're iterating over an amount we know. | |
94 unsafe { (*self.indirect).iter_mut(self.count) } | |
95 } | |
96 } | |
97 | |
98 impl<I: IndirectTrait> Drop for GenericQuestions<I> { | |
99 fn drop(&mut self) { | |
100 // SAFETY: We are valid and have a valid pointer. | |
101 // Once we're done, everything will be safe. | |
102 unsafe { | |
103 if let Some(indirect) = self.indirect.as_mut() { | |
104 indirect.free_contents(self.count) | |
105 } | |
106 memory::free(self.indirect); | |
107 self.indirect = ptr::null_mut(); | |
108 } | |
109 } | |
110 } | |
111 | |
112 /// The trait that each of the `Indirect` implementations implement. | |
113 /// | |
114 /// Basically a slice but with more meat. | |
115 pub trait IndirectTrait { | |
116 /// Converts a pointer into a borrowed `Self`. | |
117 /// | 70 /// |
118 /// # Safety | 71 /// # Safety |
119 /// | 72 /// |
120 /// You have to provide a valid pointer. | 73 /// You have to provide a valid pointer. |
121 unsafe fn borrow_ptr<'a>(ptr: *const *const Question) -> Option<&'a Self> | 74 unsafe fn borrow_ptr<'a>( |
122 where | 75 ptr: *const *const Question, |
123 Self: Sized, | 76 count: usize, |
124 { | 77 ) -> impl Iterator<Item = &'a Question>; |
125 ptr.cast::<Self>().as_ref() | 78 } |
126 } | 79 |
127 | |
128 /// Allocates memory for this indirector and all its members. | |
129 fn alloc(count: usize) -> *mut Self; | |
130 | |
131 /// Returns an iterator yielding the given number of messages. | |
132 /// | |
133 /// # Safety | |
134 /// | |
135 /// You have to provide the right count. | |
136 unsafe fn iter(&self, count: usize) -> impl Iterator<Item = &Question>; | |
137 | |
138 /// Returns a mutable iterator yielding the given number of messages. | |
139 /// | |
140 /// # Safety | |
141 /// | |
142 /// You have to provide the right count. | |
143 unsafe fn iter_mut(&mut self, count: usize) -> impl Iterator<Item = &mut Question>; | |
144 | |
145 /// Frees everything this points to. | |
146 /// | |
147 /// # Safety | |
148 /// | |
149 /// You have to pass the right size. | |
150 unsafe fn free_contents(&mut self, count: usize); | |
151 } | |
152 | |
153 /// An indirect reference to messages. | |
154 /// | |
155 /// This is kept separate to provide a place where we can separate | |
156 /// the pointer-to-pointer-to-list from pointer-to-list-of-pointers. | |
157 #[cfg(pam_impl = "linux-pam")] | 80 #[cfg(pam_impl = "linux-pam")] |
158 pub type Indirect = LinuxPamIndirect; | 81 pub type Questions = LinuxPamQuestions; |
159 | 82 |
160 /// An indirect reference to messages. | |
161 /// | |
162 /// This is kept separate to provide a place where we can separate | |
163 /// the pointer-to-pointer-to-list from pointer-to-list-of-pointers. | |
164 #[cfg(not(pam_impl = "linux-pam"))] | 83 #[cfg(not(pam_impl = "linux-pam"))] |
165 pub type Indirect = StandardIndirect; | 84 pub type Questions = XSsoQuestions; |
166 | 85 |
167 pub type Questions = GenericQuestions<Indirect>; | 86 /// The XSSO standard version of the pointer train to questions. |
168 | |
169 /// The XSSO standard version of the indirection layer between Question and Questions. | |
170 #[derive(Debug)] | 87 #[derive(Debug)] |
171 #[repr(C)] | 88 #[repr(C)] |
172 pub struct StandardIndirect { | 89 pub struct XSsoQuestions { |
173 base: *mut Question, | 90 /// Points to the memory address where the meat of `questions` is. |
91 /// **The memory layout of Vec is not specified**, and we need to return | |
92 /// a pointer to the pointer, hence we have to store it here. | |
93 pointer: *const Question, | |
94 questions: Vec<Question>, | |
174 _marker: Immovable, | 95 _marker: Immovable, |
175 } | 96 } |
176 | 97 |
177 impl IndirectTrait for StandardIndirect { | 98 impl XSsoQuestions { |
178 fn alloc(count: usize) -> *mut Self { | 99 fn len(&self) -> usize { |
179 let questions = memory::calloc(count); | 100 self.questions.len() |
180 let me_ptr: *mut Self = memory::calloc(1); | 101 } |
181 // SAFETY: We just allocated this, and we're putting a valid pointer in. | 102 fn iter_mut(&mut self) -> impl Iterator<Item = &mut Question> { |
182 unsafe { | 103 self.questions.iter_mut() |
183 let me = &mut *me_ptr; | 104 } |
184 me.base = questions; | 105 } |
185 } | 106 |
186 me_ptr | 107 impl QuestionsTrait for XSsoQuestions { |
187 } | 108 fn new(messages: &[Message]) -> Result<Self> { |
188 | 109 let questions: Result<Vec<_>> = messages.iter().map(Question::try_from).collect(); |
189 unsafe fn iter(&self, count: usize) -> impl Iterator<Item = &Question> { | 110 let questions = questions?; |
190 (0..count).map(|idx| &*self.base.add(idx)) | 111 Ok(Self { |
191 } | 112 pointer: questions.as_ptr(), |
192 | 113 questions, |
193 unsafe fn iter_mut(&mut self, count: usize) -> impl Iterator<Item = &mut Question> { | 114 _marker: Default::default(), |
194 (0..count).map(|idx| &mut *self.base.add(idx)) | 115 }) |
195 } | 116 } |
196 | 117 |
197 unsafe fn free_contents(&mut self, count: usize) { | 118 fn ptr(&self) -> *const *const Question { |
198 let msgs = slice::from_raw_parts_mut(self.base, count); | 119 &self.pointer as *const *const Question |
199 for msg in msgs { | 120 } |
200 msg.clear() | 121 |
201 } | 122 unsafe fn borrow_ptr<'a>( |
202 memory::free(self.base); | 123 ptr: *const *const Question, |
203 self.base = ptr::null_mut() | 124 count: usize, |
204 } | 125 ) -> impl Iterator<Item = &'a Question> { |
205 } | 126 slice::from_raw_parts(*ptr, count).iter() |
206 | 127 } |
207 /// The Linux version of the indirection layer between Question and Questions. | 128 } |
129 | |
130 /// The Linux version of the pointer train to questions. | |
208 #[derive(Debug)] | 131 #[derive(Debug)] |
209 #[repr(C)] | 132 #[repr(C)] |
210 pub struct LinuxPamIndirect { | 133 pub struct LinuxPamQuestions { |
211 base: [*mut Question; 0], | 134 #[allow(clippy::vec_box)] // we need to do this. |
135 /// The place where the questions are. | |
136 questions: Vec<Box<Question>>, | |
212 _marker: Immovable, | 137 _marker: Immovable, |
213 } | 138 } |
214 | 139 |
215 impl IndirectTrait for LinuxPamIndirect { | 140 impl LinuxPamQuestions { |
216 fn alloc(count: usize) -> *mut Self { | 141 fn len(&self) -> usize { |
217 // SAFETY: We're only allocating, and when we're done, | 142 self.questions.len() |
218 // everything will be in a known-good state. | 143 } |
219 let me_ptr: *mut Self = memory::calloc::<*mut Question>(count).cast(); | 144 |
220 unsafe { | 145 fn iter_mut(&mut self) -> impl Iterator<Item = &mut Question> { |
221 let me = &mut *me_ptr; | 146 self.questions.iter_mut().map(AsMut::as_mut) |
222 let ptr_list = slice::from_raw_parts_mut(me.base.as_mut_ptr(), count); | 147 } |
223 for entry in ptr_list { | 148 } |
224 *entry = memory::calloc(1); | 149 |
225 } | 150 impl QuestionsTrait for LinuxPamQuestions { |
226 } | 151 fn new(messages: &[Message]) -> Result<Self> { |
227 me_ptr | 152 let questions: Result<_> = messages |
228 } | 153 .iter() |
229 | 154 .map(|msg| Question::try_from(msg).map(Box::new)) |
230 unsafe fn iter(&self, count: usize) -> impl Iterator<Item = &Question> { | 155 .collect(); |
231 (0..count).map(|idx| &**self.base.as_ptr().add(idx)) | 156 Ok(Self { |
232 } | 157 questions: questions?, |
233 | 158 _marker: Default::default(), |
234 unsafe fn iter_mut(&mut self, count: usize) -> impl Iterator<Item = &mut Question> { | 159 }) |
235 (0..count).map(|idx| &mut **self.base.as_mut_ptr().add(idx)) | 160 } |
236 } | 161 |
237 | 162 fn ptr(&self) -> *const *const Question { |
238 unsafe fn free_contents(&mut self, count: usize) { | 163 self.questions.as_ptr().cast() |
239 let msgs = slice::from_raw_parts_mut(self.base.as_mut_ptr(), count); | 164 } |
240 for msg in msgs { | 165 |
241 if let Some(msg) = msg.as_mut() { | 166 unsafe fn borrow_ptr<'a>( |
242 msg.clear(); | 167 ptr: *const *const Question, |
243 } | 168 count: usize, |
244 memory::free(*msg); | 169 ) -> impl Iterator<Item = &'a Question> { |
245 *msg = ptr::null_mut(); | 170 slice::from_raw_parts(ptr.cast::<&Question>(), count) |
246 } | 171 .iter() |
172 .copied() | |
247 } | 173 } |
248 } | 174 } |
249 | 175 |
250 /// The C enum values for messages shown to the user. | 176 /// The C enum values for messages shown to the user. |
251 #[derive(Debug, PartialEq, TryFromPrimitive, IntoPrimitive)] | 177 #[derive(Debug, PartialEq, TryFromPrimitive, IntoPrimitive)] |
252 #[repr(u32)] | 178 #[repr(u32)] |
253 pub enum Style { | 179 enum Style { |
254 /// Requests information from the user; will be masked when typing. | 180 /// Requests information from the user; will be masked when typing. |
255 PromptEchoOff = pam_ffi::PAM_PROMPT_ECHO_OFF, | 181 PromptEchoOff = pam_ffi::PAM_PROMPT_ECHO_OFF, |
256 /// Requests information from the user; will not be masked. | 182 /// Requests information from the user; will not be masked. |
257 PromptEchoOn = pam_ffi::PAM_PROMPT_ECHO_ON, | 183 PromptEchoOn = pam_ffi::PAM_PROMPT_ECHO_ON, |
258 /// An error message. | 184 /// An error message. |
268 /// A Linux-PAM extension. | 194 /// A Linux-PAM extension. |
269 #[cfg(feature = "linux-pam-extensions")] | 195 #[cfg(feature = "linux-pam-extensions")] |
270 BinaryPrompt = pam_ffi::PAM_BINARY_PROMPT, | 196 BinaryPrompt = pam_ffi::PAM_BINARY_PROMPT, |
271 } | 197 } |
272 | 198 |
273 impl Default for Question { | 199 impl Question { |
274 fn default() -> Self { | 200 /// Gets this message's data pointer as a string. |
275 Self { | 201 /// |
276 style: Default::default(), | 202 /// # Safety |
277 data: ptr::null_mut(), | 203 /// |
278 _marker: Default::default(), | 204 /// It's up to you to pass this only on types with a string value. |
205 unsafe fn string_data(&self) -> Result<&str> { | |
206 if self.data.is_null() { | |
207 Ok("") | |
208 } else { | |
209 CStr::from_ptr(self.data.cast()) | |
210 .to_str() | |
211 .map_err(|_| ErrorCode::ConversationError) | |
279 } | 212 } |
280 } | 213 } |
281 } | 214 |
282 | 215 /// Gets this message's data pointer as borrowed binary data. |
283 impl Question { | 216 unsafe fn binary_data(&self) -> (&[u8], u8) { |
284 /// Replaces the contents of this question with the question | 217 self.data |
285 /// from the message. | 218 .cast::<CBinaryData>() |
286 /// | 219 .as_ref() |
287 /// If the message is not valid (invalid message type, bad contents, etc.), | 220 .map(Into::into) |
288 /// this will fail. | 221 .unwrap_or_default() |
289 pub fn try_fill(&mut self, msg: &Message) -> Result<()> { | 222 } |
223 } | |
224 | |
225 impl TryFrom<&Message<'_>> for Question { | |
226 type Error = ErrorCode; | |
227 fn try_from(msg: &Message) -> Result<Self> { | |
290 let alloc = |style, text| Ok((style, memory::malloc_str(text)?.cast())); | 228 let alloc = |style, text| Ok((style, memory::malloc_str(text)?.cast())); |
291 // We will only allocate heap data if we have a valid input. | 229 // We will only allocate heap data if we have a valid input. |
292 let (style, data): (_, *mut c_void) = match *msg { | 230 let (style, data): (_, *mut c_void) = match *msg { |
293 Message::MaskedPrompt(p) => alloc(Style::PromptEchoOff, p.question()), | 231 Message::MaskedPrompt(p) => alloc(Style::PromptEchoOff, p.question()), |
294 Message::Prompt(p) => alloc(Style::PromptEchoOn, p.question()), | 232 Message::Prompt(p) => alloc(Style::PromptEchoOn, p.question()), |
302 CBinaryData::alloc(p.question())?.cast(), | 240 CBinaryData::alloc(p.question())?.cast(), |
303 )), | 241 )), |
304 #[cfg(not(feature = "linux-pam-extensions"))] | 242 #[cfg(not(feature = "linux-pam-extensions"))] |
305 Message::RadioPrompt(_) | Message::BinaryPrompt(_) => Err(ErrorCode::ConversationError), | 243 Message::RadioPrompt(_) | Message::BinaryPrompt(_) => Err(ErrorCode::ConversationError), |
306 }?; | 244 }?; |
307 // Now that we know everything is valid, fill ourselves in. | 245 Ok(Self { |
308 self.clear(); | 246 style: style.into(), |
309 self.style = style.into(); | 247 data, |
310 self.data = data; | 248 _marker: Default::default(), |
311 Ok(()) | 249 }) |
312 } | 250 } |
313 | 251 } |
314 /// Gets this message's data pointer as a string. | 252 |
315 /// | 253 impl Drop for Question { |
316 /// # Safety | 254 fn drop(&mut self) { |
317 /// | |
318 /// It's up to you to pass this only on types with a string value. | |
319 unsafe fn string_data(&self) -> Result<&str> { | |
320 if self.data.is_null() { | |
321 Ok("") | |
322 } else { | |
323 CStr::from_ptr(self.data.cast()) | |
324 .to_str() | |
325 .map_err(|_| ErrorCode::ConversationError) | |
326 } | |
327 } | |
328 | |
329 /// Gets this message's data pointer as borrowed binary data. | |
330 unsafe fn binary_data(&self) -> (&[u8], u8) { | |
331 self.data | |
332 .cast::<CBinaryData>() | |
333 .as_ref() | |
334 .map(Into::into) | |
335 .unwrap_or_default() | |
336 } | |
337 | |
338 /// Zeroes out the data stored here. | |
339 fn clear(&mut self) { | |
340 // SAFETY: We either created this data or we got it from PAM. | 255 // SAFETY: We either created this data or we got it from PAM. |
341 // After this function is done, it will be zeroed out. | 256 // After this function is done, it will be zeroed out. |
342 unsafe { | 257 unsafe { |
343 // This is nice-to-have. We'll try to zero out the data | 258 // This is nice-to-have. We'll try to zero out the data |
344 // in the Question. If it's not a supported format, we skip it. | 259 // in the Question. If it's not a supported format, we skip it. |
407 macro_rules! tests { ($fn_name:ident<$typ:ident>) => { | 322 macro_rules! tests { ($fn_name:ident<$typ:ident>) => { |
408 mod $fn_name { | 323 mod $fn_name { |
409 use super::super::*; | 324 use super::super::*; |
410 #[test] | 325 #[test] |
411 fn standard() { | 326 fn standard() { |
412 let interrogation = GenericQuestions::<$typ>::new(&[ | 327 let interrogation = <$typ>::new(&[ |
413 MaskedQAndA::new("hocus pocus").message(), | 328 MaskedQAndA::new("hocus pocus").message(), |
414 QAndA::new("what").message(), | 329 QAndA::new("what").message(), |
415 QAndA::new("who").message(), | 330 QAndA::new("who").message(), |
416 InfoMsg::new("hey").message(), | 331 InfoMsg::new("hey").message(), |
417 ErrorMsg::new("gasp").message(), | 332 ErrorMsg::new("gasp").message(), |
418 ]) | 333 ]) |
419 .unwrap(); | 334 .unwrap(); |
420 let indirect = interrogation.indirect(); | 335 let indirect = interrogation.ptr(); |
421 | 336 |
422 let remade = unsafe { $typ::borrow_ptr(indirect) }.unwrap(); | 337 let remade = unsafe { $typ::borrow_ptr(indirect, interrogation.len()) }; |
423 let messages: Vec<OwnedMessage> = unsafe { remade.iter(interrogation.count) } | 338 let messages: Vec<OwnedMessage> = remade |
424 .map(TryInto::try_into) | 339 .map(TryInto::try_into) |
425 .collect::<Result<_>>() | 340 .collect::<Result<_>>() |
426 .unwrap(); | 341 .unwrap(); |
427 let [masked, what, who, hey, gasp] = messages.try_into().unwrap(); | 342 let [masked, what, who, hey, gasp] = messages.try_into().unwrap(); |
428 assert_matches!(masked => OwnedMessage::MaskedPrompt, "hocus pocus"); | 343 assert_matches!(masked => OwnedMessage::MaskedPrompt, "hocus pocus"); |
434 | 349 |
435 #[test] | 350 #[test] |
436 #[cfg(not(feature = "linux-pam-extensions"))] | 351 #[cfg(not(feature = "linux-pam-extensions"))] |
437 fn no_linux_extensions() { | 352 fn no_linux_extensions() { |
438 use crate::conv::{BinaryQAndA, RadioQAndA}; | 353 use crate::conv::{BinaryQAndA, RadioQAndA}; |
439 GenericQuestions::<$typ>::new(&[ | 354 <$typ>::new(&[ |
440 BinaryQAndA::new((&[5, 4, 3, 2, 1], 66)).message(), | 355 BinaryQAndA::new((&[5, 4, 3, 2, 1], 66)).message(), |
441 RadioQAndA::new("you must choose").message(), | 356 RadioQAndA::new("you must choose").message(), |
442 ]).unwrap_err(); | 357 ]).unwrap_err(); |
443 } | 358 } |
444 | 359 |
445 #[test] | 360 #[test] |
446 #[cfg(feature = "linux-pam-extensions")] | 361 #[cfg(feature = "linux-pam-extensions")] |
447 fn linux_extensions() { | 362 fn linux_extensions() { |
448 let interrogation = GenericQuestions::<$typ>::new(&[ | 363 let interrogation = <$typ>::new(&[ |
449 BinaryQAndA::new((&[5, 4, 3, 2, 1], 66)).message(), | 364 BinaryQAndA::new((&[5, 4, 3, 2, 1], 66)).message(), |
450 RadioQAndA::new("you must choose").message(), | 365 RadioQAndA::new("you must choose").message(), |
451 ]).unwrap(); | 366 ]).unwrap(); |
452 let indirect = interrogation.indirect(); | 367 let indirect = interrogation.ptr(); |
453 | 368 |
454 let remade = unsafe { $typ::borrow_ptr(indirect) }.unwrap(); | 369 let remade = unsafe { $typ::borrow_ptr(indirect, interrogation.len()) }; |
455 let messages: Vec<OwnedMessage> = unsafe { remade.iter(interrogation.count) } | 370 let messages: Vec<OwnedMessage> = unsafe { remade } |
456 .map(TryInto::try_into) | 371 .map(TryInto::try_into) |
457 .collect::<Result<_>>() | 372 .collect::<Result<_>>() |
458 .unwrap(); | 373 .unwrap(); |
459 let [bin, choose] = messages.try_into().unwrap(); | 374 let [bin, choose] = messages.try_into().unwrap(); |
460 assert_matches!(bin => OwnedMessage::BinaryPrompt, (&[5, 4, 3, 2, 1][..], 66)); | 375 assert_matches!(bin => OwnedMessage::BinaryPrompt, (&[5, 4, 3, 2, 1][..], 66)); |
461 assert_matches!(choose => OwnedMessage::RadioPrompt, "you must choose"); | 376 assert_matches!(choose => OwnedMessage::RadioPrompt, "you must choose"); |
462 } | 377 } |
463 } | 378 } |
464 }} | 379 }} |
465 | 380 |
466 tests!(test_xsso<StandardIndirect>); | 381 tests!(test_xsso<XSsoQuestions>); |
467 tests!(test_linux<LinuxPamIndirect>); | 382 tests!(test_linux<LinuxPamQuestions>); |
468 } | 383 } |