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