Mercurial > crates > nonstick
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libpam/environ.rs Tue Jun 24 04:25:25 2025 -0400 @@ -0,0 +1,166 @@ +#![allow(unused_variables)] // for now +use crate::environ::EnvironMapMut; +use crate::libpam::memory::CHeapString; +use crate::{EnvironMap, LibPamHandle}; +use std::ffi::{c_char, OsStr, OsString}; +use std::os::unix::ffi::OsStrExt; +use std::ptr::NonNull; +use std::{iter, ptr}; +use crate::libpam::memory; + +pub struct LibPamEnviron<'a> { + source: &'a LibPamHandle, +} + +pub struct LibPamEnvironMut<'a> { + source: &'a mut LibPamHandle, +} + +impl<'a> LibPamEnviron<'a> { + pub fn new(source: &'a LibPamHandle) -> Self { + Self { source } + } +} + +impl<'a> LibPamEnvironMut<'a> { + pub fn new(source: &'a mut LibPamHandle) -> Self { + Self { source } + } + + fn immut(&self) -> LibPamEnviron { + LibPamEnviron { + source: self.source, + } + } +} + +impl EnvironMap for LibPamEnviron<'_> { + fn get(&self, val: &OsStr) -> Option<&OsStr> { + todo!() + } + + fn iter(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> { + iter::from_fn(|| todo!()) + } +} + +impl EnvironMap for LibPamEnvironMut<'_> { + fn get(&self, val: &OsStr) -> Option<&OsStr> { + todo!() + } + + fn iter(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> { + iter::from_fn(|| todo!()) + } +} + +impl EnvironMapMut for LibPamEnvironMut<'_> { + fn insert(&mut self, key: &OsStr, val: &OsStr) -> Option<OsString> { + todo!() + } + + fn remove(&mut self, key: &OsStr) -> Option<OsString> { + todo!() + } +} + +struct EnvList { + /// Pointer to the start of the environment variable list. + /// + /// This can't be a `CHeapBox` because it's not just a single + /// `Option<EnvVar>`. + vars: NonNull<Option<EnvVar>>, +} + +impl EnvList { + unsafe fn from_ptr(ptr: NonNull<*mut c_char>) -> Self { + Self{vars: ptr.cast()} + } + + fn iter(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> { + let mut current = self.vars; + iter::from_fn(move || { + match unsafe {current.as_ref()} { + None => None, + Some(item) => { + let ret = item.as_kv(); + current = unsafe {current.add(1) }; + Some(ret) + } + } + }) + } +} + +impl Drop for EnvList { + fn drop(&mut self) { + // SAFETY: We own this pointer, and we know it's valid environment data + // from PAM. + unsafe { + let mut var = self.vars; + while let Some(var_ref) = var.as_mut() { + ptr::drop_in_place(var_ref as *mut EnvVar); + var = var.add(1); + } + memory::free(self.vars.as_ptr()) + } + } +} + +struct EnvVar(CHeapString); + +impl EnvVar { + fn as_kv(&self) -> (&OsStr, &OsStr) { + let bytes = self.0.to_bytes(); + let mut split = bytes.splitn(2, |&b| b == b'='); + ( + OsStr::from_bytes(split.next().unwrap_or_default()), + OsStr::from_bytes(split.next().unwrap_or_default()), + ) + } +} + +#[cfg(test)] +mod tests { + use crate::libpam::memory::CHeapBox; + use super::*; + + #[test] + fn test_split_kv() { + fn test(input: &str, key: &str, value: &str) { + let data = CHeapString::new(input).unwrap(); + let key = OsStr::from_bytes(key.as_bytes()); + let value = OsStr::from_bytes(value.as_bytes()); + + assert_eq!(EnvVar(data).as_kv(), (key, value)); + } + test("THIS=that", "THIS", "that"); + test("THESE=those, no one=knows", "THESE", "those, no one=knows"); + test("HERE=", "HERE", ""); + test("SOME", "SOME", ""); + test("", "", ""); + } + + #[test] + fn test_iter() { + let bx = CHeapBox::new([ + Some("ONE=two"), + Some("BIRDS=birds=birds"), + Some("me"), + Some("you="), + None, + ].map(|txt| txt.map(|txt| CHeapString::new(txt).unwrap()))).unwrap(); + let env_ptr = CHeapBox::into_ptr(bx); + + let envs = unsafe {EnvList::from_ptr(env_ptr.cast())}; + let bytes = |data: &'static str| OsStr::from_bytes(data.as_ref()); + let result: Vec<_> = envs.iter().collect(); + assert_eq!(vec![ + (bytes("ONE"), bytes("two")), + (bytes("BIRDS"), bytes("birds=birds")), + (bytes("me"), bytes("")), + (bytes("you"), bytes("")), + ], + result); + } +}