pwd_grp/
lmockable.rs

1//! Define `MockableLibc` and implement it for `RealLibc`
2//!
3//! The output looks rather like this:
4//!
5//! ```text
6//! trait MockableLibc: Copy + Deref<Target=MockableLibcFunctions> {}
7//!
8//! type LibcFn_getpwnam_r = unsafe extern "C" fn(...) -> c_int;
9//!
10//! struct MockableLibcFunctions {
11//!     getpwnam_r: LibcFn_getpwnam_r,
12//!    ..
13//! }
14//!
15//! impl Deref for MockableLibc { ...
16//!     ...
17//!        static M = MockableLibcFunctions {
18//!            getpwnam_r: libc::getpwnam_r,
19//!        }
20//! ```
21//!
22//! ### SAFETY
23//!
24//! This approach guarantees that the type and value of `mlibc.getpwnam_r`
25//! are precisely those of `libc::getpwnam_r`.
26//!
27//! This is very important for the safety and correctness
28//! of the implementation,
29//! and also ensures that the tests with mocked libc are faithful.
30//!
31//! ### Alternative approaches
32//!
33//! We could have just had `getpwnam_inner` take
34//! `mlibc: &'static MockableLibcFunctions`
35//! but that might well result in a reified pointer.
36//!
37//! The present approach means the call site is monomorphised,
38//! and hopefully the Deref and constant returned value are inlined
39//! and vanish.
40
41use super::*;
42
43#[derive(Copy, Clone)]
44pub(crate) struct RealLibc;
45
46if_cfg_getresuid! {
47  #[allow(non_camel_case_types)]
48  pub(crate) type LibcFn_getresid<I> =
49    unsafe extern "C" fn(ruid: *mut I, euid: *mut I, suid: *mut I) -> c_int;
50}
51
52#[allow(non_camel_case_types)]
53pub(crate) type LibcFn_getgroups =
54    unsafe extern "C" fn(ngroups_max: c_int, groups: *mut gid_t) -> c_int;
55
56macro_rules! define_lmockable_type { {
57    $function:ident, $passwd:ident, $key:ty
58} => { paste!{
59        #[allow(non_camel_case_types)]
60        pub(crate) type [< LibcFn_ $function >] = unsafe extern "C" fn(
61            $key,
62            *mut libc::$passwd,
63            *mut c_char,
64            size_t,
65            *mut *mut libc::$passwd,
66        ) -> c_int;
67} } }
68
69define_lmockable_type!(getpwnam, passwd, *const c_char);
70define_lmockable_type!(getpwuid, passwd, uid_t);
71define_lmockable_type!(getgrnam, group, *const c_char);
72define_lmockable_type!(getgrgid, group, gid_t);
73
74#[derive(Deftly)]
75#[derive_deftly_adhoc]
76pub(crate) struct MockableLibcFunctions {
77    pub(crate) sysconf: unsafe extern "C" fn(c_int) -> c_long,
78    pub(crate) getuid: unsafe extern "C" fn() -> uid_t,
79    pub(crate) geteuid: unsafe extern "C" fn() -> uid_t,
80    pub(crate) getgid: unsafe extern "C" fn() -> gid_t,
81    pub(crate) getegid: unsafe extern "C" fn() -> gid_t,
82    // ought to be if_cfg_getresuid
83    #[cfg(not(any(target_os = "macos", target_os = "netbsd")))]
84    pub(crate) getresuid: LibcFn_getresid<uid_t>,
85    // ought to be if_cfg_getresuid
86    #[cfg(not(any(target_os = "macos", target_os = "netbsd")))]
87    pub(crate) getresgid: LibcFn_getresid<gid_t>,
88    pub(crate) getgroups: LibcFn_getgroups,
89    pub(crate) getpwnam_r: LibcFn_getpwnam,
90    pub(crate) getpwuid_r: LibcFn_getpwuid,
91    pub(crate) getgrnam_r: LibcFn_getgrnam,
92    pub(crate) getgrgid_r: LibcFn_getgrgid,
93}
94
95derive_deftly_adhoc! {
96    MockableLibcFunctions expect items:
97
98    impl Deref for RealLibc {
99        type Target = MockableLibcFunctions;
100
101        fn deref(&self) -> &MockableLibcFunctions {
102            static M: MockableLibcFunctions = MockableLibcFunctions {
103                $(
104                    $fname: libc::$fname,
105                )
106            };
107            &M
108        }
109    }
110}
111
112pub(crate) trait MockableLibc:
113    Copy + Deref<Target = MockableLibcFunctions>
114{
115}
116
117impl MockableLibc for RealLibc {}