pwd_grp/
lib.rs

1#![cfg(unix)]
2#![cfg_attr(
3    all(test, feature = "test-with-lmock"),
4    feature(allocator_api, vec_into_raw_parts)
5)]
6#![doc=include_str!("../README.md")]
7
8// The code here has to deal with a large number of axes of
9// repetion/variation:
10//
11//  * the fields within Passwd and within Group.
12//    These are handled mostly via derive-adhoc.
13//
14//  * the *types* of the fields (and of the overall structures):
15//    These are generally handled by traits such as TryConvertFrom,
16//    FromLibc, and MockToLibc.
17//
18//  * public API: simple functions vs generic mockable interface.
19//    This is mostly just written out twice.
20//    But for get{,e,res}{uid,gid} there's `for_getid_wrappers!`
21//
22//  * public API: mock vs real lookups.
23//    This is the MockablePwdGrpProvider (and SealedProvider)
24//
25//  * Internal mocks for testing, or use real libc (for testing unsafe)
26//    This is the MockableLibc trait and its main definition/
27//    impl in lmockable.rs`
28//
29//  * Different libc functions treated in the same way.
30//    macro_rules macros in lmockable and with_lmocks.
31//
32//  * various Rust string types (Box<[u8]>, String, etc.)
33//    In the public API this is handled by generics and SealedString.
34//    Conversions are handled by TryConvertFrom, From/Into, TryFrom,
35//    and so on.  The conversion code is generally made by
36//    macro_rules macros in convert.rs.
37//
38//  * Lookup by id or name.
39//    Generally written out both ways.
40//
41//  * passwd vs group:
42//    The treatment varies on a case-by-case basis.
43//
44// This crate has quite some macro use.  This is because
45
46use std::cmp;
47use std::convert::{TryFrom, TryInto};
48use std::ffi::{CStr, CString};
49use std::fmt::Debug;
50use std::io;
51use std::io::ErrorKind::{InvalidData, InvalidInput, Other, OutOfMemory};
52use std::mem::MaybeUninit;
53use std::ops::Deref;
54use std::ptr::NonNull;
55
56use libc::{c_char, c_int, c_long, size_t};
57use libc::{gid_t, uid_t};
58
59use derive_deftly::{define_derive_deftly, derive_deftly_adhoc, Deftly};
60use paste::paste;
61use thiserror::Error;
62
63#[macro_use]
64mod conditional;
65#[macro_use]
66pub mod mock;
67#[macro_use]
68mod convert;
69#[macro_use]
70mod generic;
71#[macro_use]
72mod unsafe_;
73#[macro_use]
74mod lmockable;
75
76#[cfg(test)]
77#[macro_use]
78mod test;
79
80pub mod error;
81
82mod private;
83
84use convert::*;
85use error::*;
86use lmockable::*;
87use private::*;
88use unsafe_::*;
89
90pub use generic::*;
91
92type RawSafe = Box<[u8]>;
93
94/// Local convenience alias.
95///
96/// We pun `uid_t` and `gid_t`, which will cause compilation failure
97/// on any platform where they're not the same.
98///
99/// See also
100/// <https://gitlab.torproject.org/tpo/core/rust-pwd-grp/-/issues/1>
101type Id = libc::uid_t;
102
103/// Documentation common to [`Passwd`] and [`Group`]
104//
105// TODO ideally we would somehow factor out all the other attributes.
106// but I don't fancy generating these key structs with macro_rules!.
107// Perhaps #[only_derive_adhoc]...
108//
109// Re the nonexhaustiveness technique, see the docs for NonExhaustive
110macro_rules! common_docs { {} => { r"
111
112Strings are represented as `S`, usually `String`.
113
114
115This struct is non-exhaustive:
116future versions of `pwd-grp` may add fields.
117(For technical reasons this is done with a hidden field
118called `__non_exhaustive`.
119If you use this to bypass the restriction,
120future minor updates to this crate may break your code.)
121
122" } }
123
124/// Information about a user in the password database
125///
126/// Equivalent to `struct passwd`: for full information on the
127/// individual fields, see the manual pages for
128/// [`getpwuid_r(3)`](https://man.freebsd.org/cgi/man.cgi?query=getpwuid&sektion=3)
129/// and
130/// [`passwd(5)`](https://man.freebsd.org/cgi/man.cgi?query=passwd&sektion=5)
131/// on your favorite Unix-style operating system.
132#[doc = common_docs!()]
133#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
134#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
135#[derive(Deftly)]
136#[derive_deftly(TryConvertFrom, FromLibc, Lookup, Blank)]
137#[cfg_attr(all(test, feature = "test-with-lmock"), derive_deftly(MockToLibc))]
138#[deftly(abbrev = "pw")]
139pub struct Passwd<S = String> {
140    pub name: S,
141    pub passwd: S,
142    pub uid: Id,
143    pub gid: Id,
144    pub gecos: S,
145    pub dir: S,
146    pub shell: S,
147    #[doc(hidden)]
148    #[deftly(dummy)]
149    #[cfg_attr(feature = "serde", serde(skip))]
150    // See the doc comment for NonExhaustive
151    pub __non_exhaustive: NonExhaustive,
152}
153
154/// Information about a group in the password database
155///
156/// Equivalent to `struct group`: for full information on
157/// the individual fields, see the manual pages for
158/// [`getgrgid_r(3)`](https://man7.org/linux/man-pages/man3/getgrgid.3.html)
159/// and
160/// [`group(5)`](https://man7.org/linux/man-pages/man5/group.5.html)
161/// on your favorite Unix-style operating system.
162#[doc = common_docs!()]
163#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
164#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
165#[derive(Deftly)]
166#[derive_deftly(TryConvertFrom, FromLibc, Lookup, Blank)]
167#[cfg_attr(all(test, feature = "test-with-lmock"), derive_deftly(MockToLibc))]
168#[deftly(abbrev = "gr")]
169pub struct Group<S = String> {
170    pub name: S,
171    pub passwd: S,
172    pub gid: Id,
173    pub mem: Box<[S]>,
174    #[doc(hidden)]
175    #[deftly(dummy)]
176    #[cfg_attr(feature = "serde", serde(skip))]
177    // See the doc comment for NonExhaustive
178    pub __non_exhaustive: NonExhaustive,
179}
180
181macro_rules! simple {
182    {
183        fn $getfoobar:ident($key:ident: $keytype:ty) -> $out:ty;
184        $example_key:expr, $example_field:ident
185    } => { paste!{
186        /// Look up a
187        #[doc = stringify!([< $out:lower >])]
188        /// entry by
189        #[doc = concat!(stringify!([< $key >]))]
190        ///
191        /// Returns `None` if the entry isn't found,
192        /// or `Err` if an error occurred.
193        ///
194        /// If the entry contains strings that aren't valid UTF-8,
195        /// this is treated as an error.
196        /// If you need to handle non-UTF-8 password/group entries,
197        /// use the generic trait methods on [`PwdGrp`].
198        ///
199        /// ### Example
200        ///
201        #[cfg_attr(target_os = "linux", doc = "```")]
202        #[cfg_attr(not(target_os = "linux"), doc = "```no_run")]
203        #[doc = concat!(
204            "let entry = ",
205            "pwd_grp::", stringify!($getfoobar),
206            "(", stringify!($example_key), ")",
207            ".unwrap().unwrap();"
208        )]
209        #[doc = concat!(
210            "assert_eq!(entry.", stringify!($example_field),", 0);"
211        )]
212        /// ```
213        pub fn $getfoobar(
214            $key: $keytype,
215        ) -> io::Result<Option<$out<String>>> {
216            generic::PwdGrp.$getfoobar($key)
217        }
218    } };
219}
220
221simple! { fn getpwnam(name: impl AsRef<str>) -> Passwd;  "root", uid }
222simple! { fn getpwuid(uid: Id) -> Passwd;                0,      uid }
223simple! { fn getgrnam(name: impl AsRef<str>) -> Group;   "root", gid }
224simple! { fn getgrgid(gid: Id) -> Group;                 0,      gid }
225
226macro_rules! define_getid_simple { {
227    $fn:ident: $id:ident. $field:ident, $doc:literal $( $real:literal )?
228} => {
229    define_getid_simple! { @ $fn: $id. Id, $doc }
230}; {
231    $fn:ident: $id:ident. ($( $f:ident )*), $doc:literal $( $real:literal )?
232} => {
233    define_getid_simple! { @ $fn: $id. (Id, Id, Id), $doc }
234}; {
235    @ $fn:ident: $id:ident. $ret:ty, $doc:literal
236} => { paste!{
237    /// Get the current process's
238    #[doc = $doc]
239
240    pub fn $fn() -> $ret {
241        generic::PwdGrp.$fn()
242    }
243} } }
244
245for_getid_wrappers! { define_getid_simple }
246
247/// Get the current process's supplementary group list
248///
249/// Note that on some operating systems,
250/// the output of this function contains the result of `gettegid()`,
251/// and on some operating systems it does not.
252/// If you are using this function,
253/// you should should generally call `geteuid()` as well.
254///
255/// # Example
256///
257/// ```
258/// let gg = pwd_grp::getgroups().unwrap();
259/// println!("supplementary groups are: {:?}", gg);
260/// ```
261pub fn getgroups() -> io::Result<Vec<Id>> {
262    PwdGrp.getgroups()
263}
264
265#[cfg(not(feature = "minimal-1"))]
266compile_error! { "You must enable at least one pwd-grp crate feature!" }