comparison src/libpam/environ.rs @ 98:b87100c5eed4

Start on environment variables, and make pointers nicer. This starts work on the PAM environment handling, and in so doing, introduces the CHeapBox and CHeapString structs. These are analogous to Box and CString, but they're located on the C heap rather than being Rust-managed memory. This is because environment variables deal with even more pointers and it turns out we can lose a lot of manual freeing using homemade smart pointers.
author Paul Fisher <paul@pfish.zone>
date Tue, 24 Jun 2025 04:25:25 -0400
parents
children 3f11b8d30f63
comparison
equal deleted inserted replaced
97:efe2f5f8b5b2 98:b87100c5eed4
1 #![allow(unused_variables)] // for now
2 use crate::environ::EnvironMapMut;
3 use crate::libpam::memory::CHeapString;
4 use crate::{EnvironMap, LibPamHandle};
5 use std::ffi::{c_char, OsStr, OsString};
6 use std::os::unix::ffi::OsStrExt;
7 use std::ptr::NonNull;
8 use std::{iter, ptr};
9 use crate::libpam::memory;
10
11 pub struct LibPamEnviron<'a> {
12 source: &'a LibPamHandle,
13 }
14
15 pub struct LibPamEnvironMut<'a> {
16 source: &'a mut LibPamHandle,
17 }
18
19 impl<'a> LibPamEnviron<'a> {
20 pub fn new(source: &'a LibPamHandle) -> Self {
21 Self { source }
22 }
23 }
24
25 impl<'a> LibPamEnvironMut<'a> {
26 pub fn new(source: &'a mut LibPamHandle) -> Self {
27 Self { source }
28 }
29
30 fn immut(&self) -> LibPamEnviron {
31 LibPamEnviron {
32 source: self.source,
33 }
34 }
35 }
36
37 impl EnvironMap for LibPamEnviron<'_> {
38 fn get(&self, val: &OsStr) -> Option<&OsStr> {
39 todo!()
40 }
41
42 fn iter(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> {
43 iter::from_fn(|| todo!())
44 }
45 }
46
47 impl EnvironMap for LibPamEnvironMut<'_> {
48 fn get(&self, val: &OsStr) -> Option<&OsStr> {
49 todo!()
50 }
51
52 fn iter(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> {
53 iter::from_fn(|| todo!())
54 }
55 }
56
57 impl EnvironMapMut for LibPamEnvironMut<'_> {
58 fn insert(&mut self, key: &OsStr, val: &OsStr) -> Option<OsString> {
59 todo!()
60 }
61
62 fn remove(&mut self, key: &OsStr) -> Option<OsString> {
63 todo!()
64 }
65 }
66
67 struct EnvList {
68 /// Pointer to the start of the environment variable list.
69 ///
70 /// This can't be a `CHeapBox` because it's not just a single
71 /// `Option<EnvVar>`.
72 vars: NonNull<Option<EnvVar>>,
73 }
74
75 impl EnvList {
76 unsafe fn from_ptr(ptr: NonNull<*mut c_char>) -> Self {
77 Self{vars: ptr.cast()}
78 }
79
80 fn iter(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> {
81 let mut current = self.vars;
82 iter::from_fn(move || {
83 match unsafe {current.as_ref()} {
84 None => None,
85 Some(item) => {
86 let ret = item.as_kv();
87 current = unsafe {current.add(1) };
88 Some(ret)
89 }
90 }
91 })
92 }
93 }
94
95 impl Drop for EnvList {
96 fn drop(&mut self) {
97 // SAFETY: We own this pointer, and we know it's valid environment data
98 // from PAM.
99 unsafe {
100 let mut var = self.vars;
101 while let Some(var_ref) = var.as_mut() {
102 ptr::drop_in_place(var_ref as *mut EnvVar);
103 var = var.add(1);
104 }
105 memory::free(self.vars.as_ptr())
106 }
107 }
108 }
109
110 struct EnvVar(CHeapString);
111
112 impl EnvVar {
113 fn as_kv(&self) -> (&OsStr, &OsStr) {
114 let bytes = self.0.to_bytes();
115 let mut split = bytes.splitn(2, |&b| b == b'=');
116 (
117 OsStr::from_bytes(split.next().unwrap_or_default()),
118 OsStr::from_bytes(split.next().unwrap_or_default()),
119 )
120 }
121 }
122
123 #[cfg(test)]
124 mod tests {
125 use crate::libpam::memory::CHeapBox;
126 use super::*;
127
128 #[test]
129 fn test_split_kv() {
130 fn test(input: &str, key: &str, value: &str) {
131 let data = CHeapString::new(input).unwrap();
132 let key = OsStr::from_bytes(key.as_bytes());
133 let value = OsStr::from_bytes(value.as_bytes());
134
135 assert_eq!(EnvVar(data).as_kv(), (key, value));
136 }
137 test("THIS=that", "THIS", "that");
138 test("THESE=those, no one=knows", "THESE", "those, no one=knows");
139 test("HERE=", "HERE", "");
140 test("SOME", "SOME", "");
141 test("", "", "");
142 }
143
144 #[test]
145 fn test_iter() {
146 let bx = CHeapBox::new([
147 Some("ONE=two"),
148 Some("BIRDS=birds=birds"),
149 Some("me"),
150 Some("you="),
151 None,
152 ].map(|txt| txt.map(|txt| CHeapString::new(txt).unwrap()))).unwrap();
153 let env_ptr = CHeapBox::into_ptr(bx);
154
155 let envs = unsafe {EnvList::from_ptr(env_ptr.cast())};
156 let bytes = |data: &'static str| OsStr::from_bytes(data.as_ref());
157 let result: Vec<_> = envs.iter().collect();
158 assert_eq!(vec![
159 (bytes("ONE"), bytes("two")),
160 (bytes("BIRDS"), bytes("birds=birds")),
161 (bytes("me"), bytes("")),
162 (bytes("you"), bytes("")),
163 ],
164 result);
165 }
166 }