Vendor dependencies

Let's see how I like this workflow.
This commit is contained in:
John Doty 2022-12-19 08:27:18 -08:00
parent 34d1830413
commit 9c435dc440
7500 changed files with 1665121 additions and 99 deletions

1
vendor/users/.cargo-checksum.json vendored Normal file
View file

@ -0,0 +1 @@
{"files":{"Cargo.lock":"c9ada6d2bfa84d46192e0e6fa32447593b3bff0d665fe16c767abbbdd2c65ca4","Cargo.toml":"da29cc763ec004970957799df1ebcdcc51e8f854522bdbe73980b95d80d2b32a","Justfile":"1293564ae4d6639392bd045b0cb850cea433f9d376f12723eebc17fa4be0ae26","LICENCE":"ac84d716b3ca37857b9465476a7d6adc3684a774bc775ada8318c550187ed2b5","README.md":"7376a66fd7955c3115eabe65b70acc3a3c0a9038d830331748623f96220ba72a","examples/example.rs":"1500d9c04605096ef9928883f7ae07f48a98da166007f3a91f3803818a0ac0e6","examples/groups.rs":"ba6f7307aa0f204e387451a1e457a0f93628d253d5f607ac9e370e8307726f50","examples/list.rs":"c64574b89f84ba144d7601a6c66762fbb0e23d0a81d21ea97e3e16a564e6ac4b","examples/os.rs":"46d3217736c6d2b63ca107cfa1a7425ef574191d0b374fb81b918918f0bb9809","examples/switching.rs":"6584c8f06a3c3820bdcfc9cd4e4e8842915c559822b237a0213a5b284be0e782","examples/threading.rs":"681b760bf3f976d6eb82e87f428eecc7a456014c4e5236d5b7d475d124c6259e","src/base.rs":"f65d28fb398b871316e34ce3bc4f737c67ebd7c140d2fd1dc3de47ced9a53679","src/cache.rs":"dd934d88e1059348760b6f32e933888d4fc9f85d5a15cd152b48e444c1c1adc4","src/lib.rs":"2d2805dc46e4f718f62395f8dd65349fc0169d70452d5f6d8996d951bb6fac94","src/mock.rs":"0973ab8f55b02668f0866994546c1c32ca619c24a74f28a82c5dd1422da10f9b","src/switch.rs":"7354e65c0acbdabfefa3261586201a942df8585542612eaf8978d58585351d99","src/traits.rs":"9af80b4cb6cea0ad4b6caceb6602fbf27a6ae49f7a02df768f285463664a716a"},"package":"24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"}

42
vendor/users/Cargo.lock generated vendored Normal file
View file

@ -0,0 +1,42 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "users"
version = "0.11.0"
dependencies = [
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
"checksum libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)" = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"

39
vendor/users/Cargo.toml vendored Normal file
View file

@ -0,0 +1,39 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "users"
version = "0.11.0"
authors = ["Benjamin Sago <ogham@bsago.me>"]
exclude = ["/.rustfmt.toml", "/.travis.yml"]
description = "Library for accessing Unix users and groups"
documentation = "https://docs.rs/users/"
readme = "README.md"
license = "MIT"
repository = "https://github.com/ogham/rust-users"
[dependencies.libc]
version = "0.2"
[dependencies.log]
version = "0.4"
optional = true
default_features = false
[dev-dependencies.env_logger]
version = "0.7"
features = []
default_features = false
[features]
cache = []
default = ["cache", "mock", "logging"]
logging = ["log"]
mock = []

36
vendor/users/Justfile vendored Normal file
View file

@ -0,0 +1,36 @@
all: build test
all-release: build-release test-release
MIN_RUST := "1.31.0"
# compiles the code
build:
cargo +{{MIN_RUST}} build
cargo +stable build
# compiles the code in release mode
build-release:
cargo +{{MIN_RUST}} build --release --verbose
cargo +stable build --release --verbose
# compiles the code with every combination of feature flags
build-features:
cargo +{{MIN_RUST}} hack build --feature-powerset
cargo +stable hack build --feature-powerset
# runs unit tests
test:
cargo +{{MIN_RUST}} test --all -- --quiet
cargo +stable test --all -- --quiet
# runs unit tests in release mode
test-release:
cargo +{{MIN_RUST}} test --all --release --verbose
cargo +stable test --all --release --verbose
# runs unit tests with every combination of feature flags
test-features:
cargo +{{MIN_RUST}} hack test --feature-powerset --lib -- --quiet
cargo +stable hack test --feature-powerset --lib -- --quiet

21
vendor/users/LICENCE vendored Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Benjamin Sago
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

167
vendor/users/README.md vendored Normal file
View file

@ -0,0 +1,167 @@
# rust-users [![users on crates.io][crates-badge]][crates-url] [![Minimum Rust Version 1.31.0][rustc-badge]][rustc-url] [![Build status][travis-badge]][travis-url]
[crates-badge]: https://meritbadge.herokuapp.com/users
[crates-url]: https://crates.io/crates/users
[travis-badge]: https://travis-ci.org/ogham/rust-users.svg?branch=master
[travis-url]: https://travis-ci.org/github/ogham/rust-users
[rustc-badge]: https://img.shields.io/badge/rustc-1.31+-lightgray.svg
[rustc-url]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html
This is a library for accessing Unix users and groups.
It supports getting the system users and groups, storing them in a cache, and creating your own mock tables.
### [View the Rustdoc](https://docs.rs/users)
# Installation
This crate works with [Cargo](https://crates.io). Add the following to your `Cargo.toml` dependencies section:
```toml
[dependencies]
users = "0.11"
```
The earliest version of Rust that this crate is tested against is [Rust v1.31.0][rustc-url].
# Usage
In Unix, each user has an individual *user ID*, and each process has an *effective user ID* that says which users permissions it is using.
Furthermore, users can be the members of *groups*, which also have names and IDs.
This functionality is exposed in libc, the C standard library, but as an unsafe Rust interface.
This wrapper library provides a safe interface, using `User` and `Group` types and functions such as `get_user_by_id` instead of low-level pointers and strings.
It also offers basic caching functionality.
It does not (yet) offer *editing* functionality; the values returned are read-only.
## Users
The function `get_current_uid` returns a `uid_t` value representing the user currently running the program, and the `get_user_by_uid` function scans the users database and returns a `User` with the users information.
This function returns `None` when there is no user for that ID.
A `User` has the following accessors:
- **uid:** The users ID
- **name:** The users name
- **primary_group:** The ID of this users primary group
Here is a complete example that prints out the current users name:
```rust
use users::{get_user_by_uid, get_current_uid};
let user = get_user_by_uid(get_current_uid()).unwrap();
println!("Hello, {}!", user.name());
```
This code assumes (with `unwrap()`) that the user hasnt been deleted after the program has started running.
For arbitrary user IDs, this is **not** a safe assumption: its possible to delete a user while its running a program, or is the owner of files, or for that user to have never existed.
So always check the return values!
There is also a `get_current_username` function, as its such a common operation that it deserves special treatment.
## Caching
Despite the above warning, the users and groups database rarely changes.
While a short program may only need to get user information once, a long-running one may need to re-query the database many times, and a medium-length one may get away with caching the values to save on redundant system calls.
For this reason, this crate offers a caching interface to the database, which offers the same functionality while holding on to every result, caching the information so it can be re-used.
To introduce a cache, create a new `UsersCache` and call the same methods on it.
For example:
```rust
use users::{Users, Groups, UsersCache};
let mut cache = UsersCache::new();
let uid = cache.get_current_uid();
let user = cache.get_user_by_uid(uid).unwrap();
println!("Hello again, {}!", user.name());
```
This cache is **only additive**: its not possible to drop it, or erase selected entries, as when the database may have been modified, its best to start entirely afresh.
So to accomplish this, just start using a new `UsersCache`.
## Groups
Finally, its possible to get groups in a similar manner.
A `Group` has the following accessors:
- **gid:** The groups ID
- **name:** The groups name
And again, a complete example:
```rust
use users::{Users, Groups, UsersCache};
let mut cache = UsersCache::new();
let group = cache.get_group_by_name("admin").expect("No such group 'admin'!");
println!("The '{}' group has the ID {}", group.name(), group.gid());
```
## Logging
The `logging` feature, which is on by default, uses the `log` crate to record all interactions with the operating system at Trace log level.
## Caveats
You should be prepared for the users and groups tables to be completely broken: IDs shouldnt be assumed to map to actual users and groups, and usernames and group names arent guaranteed to map either!
# Mockable users and groups
When youre testing your code, you dont want to actually rely on the system actually having various users and groups present - its much better to have a custom set of users that are *guaranteed* to be there, so you can test against them.
The `mock` module allows you to create these custom users and groups definitions, then access them using the same `Users` trait as in the main library, with few changes to your code.
## Creating mock users
The only thing a mock users table needs to know in advance is the UID of the current user.
Aside from that, you can add users and groups with `add_user` and `add_group` to the table:
```rust
use std::sync::Arc;
use users::mock::{MockUsers, User, Group};
use users::os::unix::{UserExt, GroupExt};
let mut users = MockUsers::with_current_uid(1000);
let bobbins = User::new(1000, "Bobbins", 1000).with_home_dir("/home/bobbins");
users.add_user(bobbins);
users.add_group(Group::new(100, "funkyppl"));
```
The exports get re-exported into the mock module, for simpler `use` lines.
## Using mock users
To set your program up to use either type of `Users` table, make your functions and structs accept a generic parameter that implements the `Users` trait.
Then, you can pass in a value of either OS or Mock type.
Heres a complete example:
```rust
use std::sync::Arc;
use users::{Users, UsersCache, User};
use users::os::unix::UserExt;
use users::mock::MockUsers;
fn print_current_username<U: Users>(users: &mut U) {
println!("Current user: {:?}", users.get_current_username());
}
let mut users = MockUsers::with_current_uid(1001);
users.add_user(User::new(1001, "fred", 101));
print_current_username(&mut users);
let mut actual_users = UsersCache::new();
print_current_username(&mut actual_users);
```

20
vendor/users/examples/example.rs vendored Normal file
View file

@ -0,0 +1,20 @@
extern crate users;
use users::{Users, Groups, UsersCache};
extern crate env_logger;
fn main() {
env_logger::init();
let cache = UsersCache::new();
let current_uid = cache.get_current_uid();
println!("Your UID is {}", current_uid);
let you = cache.get_user_by_uid(current_uid).expect("No entry for current user!");
println!("Your username is {}", you.name().to_string_lossy());
let primary_group = cache.get_group_by_gid(you.primary_group_id()).expect("No entry for your primary group!");
println!("Your primary group has ID {} and name {}", primary_group.gid(), primary_group.name().to_string_lossy());
}

31
vendor/users/examples/groups.rs vendored Normal file
View file

@ -0,0 +1,31 @@
extern crate users;
use users::{Users, Group, UsersCache, get_user_groups, group_access_list};
extern crate env_logger;
fn main() {
env_logger::init();
let cache = UsersCache::new();
let user = cache.get_user_by_uid(cache.get_current_uid())
.expect("No current user?");
let mut groups: Vec<Group> = get_user_groups(user.name(), user.primary_group_id())
.expect("No user groups?");
groups.sort_by(|a, b| a.gid().cmp(&b.gid()));
for group in groups {
println!("Group {} has name {}", group.gid(), group.name().to_string_lossy());
}
let mut groups = group_access_list()
.expect("Group access list");
groups.sort_by(|a, b| a.gid().cmp(&b.gid()));
println!("\nGroup access list:");
for group in groups {
println!("Group {} has name {}", group.gid(), group.name().to_string_lossy());
}
}

16
vendor/users/examples/list.rs vendored Normal file
View file

@ -0,0 +1,16 @@
extern crate users;
use users::{User, all_users};
extern crate env_logger;
fn main() {
env_logger::init();
let mut users: Vec<User> = unsafe { all_users() }.collect();
users.sort_by(|a, b| a.uid().cmp(&b.uid()));
for user in users {
println!("User {} has name {}", user.uid(), user.name().to_string_lossy());
}
}

38
vendor/users/examples/os.rs vendored Normal file
View file

@ -0,0 +1,38 @@
extern crate users;
use users::{Users, Groups, UsersCache};
use users::os::unix::{UserExt, GroupExt};
//use users::os::bsd::UserExt as BSDUserExt;
extern crate env_logger;
fn main() {
env_logger::init();
let cache = UsersCache::new();
let current_uid = cache.get_current_uid();
println!("Your UID is {}", current_uid);
let you = cache.get_user_by_uid(current_uid).expect("No entry for current user!");
println!("Your username is {}", you.name().to_string_lossy());
println!("Your shell is {}", you.shell().display());
println!("Your home directory is {}", you.home_dir().display());
// The two fields below are only available on BSD systems.
// Linux systems dont have the fields in their `passwd` structs!
//println!("Your password change timestamp is {}", you.password_change_time());
//println!("Your password expiry timestamp is {}", you.password_expire_time());
let primary_group = cache.get_group_by_gid(you.primary_group_id()).expect("No entry for your primary group!");
println!("Your primary group has ID {} and name {}", primary_group.gid(), primary_group.name().to_string_lossy());
if primary_group.members().is_empty() {
println!("There are no other members of that group.");
}
else {
for username in primary_group.members() {
println!("User {} is also a member of that group.", username.to_string_lossy());
}
}
}

29
vendor/users/examples/switching.rs vendored Normal file
View file

@ -0,0 +1,29 @@
extern crate users;
use users::{get_current_uid, get_current_gid, get_effective_uid, get_effective_gid, uid_t};
use users::switch::switch_user_group;
use std::mem::drop;
extern crate env_logger;
const SAMPLE_ID: uid_t = 502;
fn main() {
env_logger::init();
println!("\nInitial values:");
print_state();
println!("\nValues after switching:");
let guard = switch_user_group(SAMPLE_ID, SAMPLE_ID);
print_state();
println!("\nValues after switching back:");
drop(guard);
print_state();
}
fn print_state() {
println!("Current UID/GID: {}/{}", get_current_uid(), get_current_gid());
println!("Effective UID/GID: {}/{}", get_effective_uid(), get_effective_gid());
}

65
vendor/users/examples/threading.rs vendored Normal file
View file

@ -0,0 +1,65 @@
//! This example demonstrates how to use a `UsersCache` cache in a
//! multi-threaded situation. The cache uses `RefCell`s internally, so it
//! is distinctly not thread-safe. Instead, youll need to place it within
//! some kind of lock in order to have threads access it one-at-a-time.
//!
//! It queries all the users it can find in the range 500..510. This is the
//! default uid range on my Apple laptop -- Linux starts counting from 1000,
//! but I cant include both in the range! It spawns one thread per user to
//! query, with each thread accessing the same cache.
//!
//! Then, afterwards, it retrieves references to the users that had been
//! cached earlier.
// For extra fun, try uncommenting some of the lines of code below, making
// the code try to access the users cache *without* a Mutex, and see it
// spew compile errors at you.
extern crate users;
use users::{Users, UsersCache, uid_t};
extern crate env_logger;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::thread;
const LO: uid_t = 500;
const HI: uid_t = 510;
fn main() {
env_logger::init();
// For thread-safely, our users cache needs to be within a Mutex, so
// only one thread can access it once. This Mutex needs to be within an
// Arc, so multiple threads can access the Mutex.
let cache = Arc::new(Mutex::new(UsersCache::new()));
// let cache = UsersCache::empty_cache();
// Loop over the range and query all the users in the range. Although we
// could use the `&User` values returned, we just ignore them.
for uid in LO .. HI {
let cache = Arc::clone(&cache);
thread::spawn(move || {
let cache = cache.lock().unwrap(); // Unlock the mutex
let _ = cache.get_user_by_uid(uid); // Query our users cache!
});
}
// Wait for all the threads to finish.
thread::sleep(Duration::from_millis(100));
// Loop over the same range and print out all the users we find.
// These users will be retrieved from the cache.
for uid in LO .. HI {
let cache = cache.lock().unwrap(); // Re-unlock the mutex
if let Some(u) = cache.get_user_by_uid(uid) { // Re-query our cache!
println!("User #{} is {}", u.uid(), u.name().to_string_lossy())
}
else {
println!("User #{} does not exist", uid);
}
}
}

1269
vendor/users/src/base.rs vendored Normal file

File diff suppressed because it is too large Load diff

360
vendor/users/src/cache.rs vendored Normal file
View file

@ -0,0 +1,360 @@
//! A cache for users and groups provided by the OS.
//!
//! Because the users table changes so infrequently, it's common for
//! short-running programs to cache the results instead of getting the most
//! up-to-date entries every time. The [`UsersCache`](struct.UsersCache.html)
//! type helps with this, providing methods that have the same name as the
//! others in this crate, only they store the results.
//!
//! ## Example
//!
//! ```no_run
//! use std::sync::Arc;
//! use users::{Users, UsersCache};
//!
//! let mut cache = UsersCache::new();
//! let user = cache.get_user_by_uid(502).expect("User not found");
//! let same_user = cache.get_user_by_uid(502).unwrap();
//!
//! // The two returned values point to the same User
//! assert!(Arc::ptr_eq(&user, &same_user));
//! ```
//!
//! ## Caching, multiple threads, and mutability
//!
//! The `UsersCache` type is caught between a rock and a hard place when it
//! comes to providing references to users and groups.
//!
//! Instead of returning a fresh `User` struct each time, for example, it will
//! return a reference to the version it currently has in its cache. So you can
//! ask for User #501 twice, and youll get a reference to the same value both
//! time. Its methods are *idempotent* -- calling one multiple times has the
//! same effect as calling one once.
//!
//! This works fine in theory, but in practice, the cache has to update its own
//! state somehow: it contains several `HashMap`s that hold the result of user
//! and group lookups. Rust provides mutability in two ways:
//!
//! 1. Have its methods take `&mut self`, instead of `&self`, allowing the
//! internal maps to be mutated (“inherited mutability”)
//! 2. Wrap the internal maps in a `RefCell`, allowing them to be modified
//! (“interior mutability”).
//!
//! Unfortunately, Rust is also very protective of references to a mutable
//! value. In this case, switching to `&mut self` would only allow for one user
//! to be read at a time!
//!
//! ```no_run
//! use users::{Users, Groups, UsersCache};
//!
//! let mut cache = UsersCache::new();
//!
//! let uid = cache.get_current_uid(); // OK...
//! let user = cache.get_user_by_uid(uid).unwrap(); // OK...
//! let group = cache.get_group_by_gid(user.primary_group_id()); // No!
//! ```
//!
//! When we get the `user`, it returns an optional reference (which we unwrap)
//! to the users entry in the cache. This is a reference to something contained
//! in a mutable value. Then, when we want to get the users primary group, it
//! will return *another* reference to the same mutable value. This is something
//! that Rust explicitly disallows!
//!
//! The compiler wasnt on our side with Option 1, so lets try Option 2:
//! changing the methods back to `&self` instead of `&mut self`, and using
//! `RefCell`s internally. However, Rust is smarter than this, and knows that
//! were just trying the same trick as earlier. A simplified implementation of
//! a user cache lookup would look something like this:
//!
//! ```text
//! fn get_user_by_uid(&self, uid: uid_t) -> Option<&User> {
//! let users = self.users.borrow_mut();
//! users.get(uid)
//! }
//! ```
//!
//! Rust wont allow us to return a reference like this because the `Ref` of the
//! `RefCell` just gets dropped at the end of the method, meaning that our
//! reference does not live long enough.
//!
//! So instead of doing any of that, we use `Arc` everywhere in order to get
//! around all the lifetime restrictions. Returning reference-counted users and
//! groups mean that we dont have to worry about further uses of the cache, as
//! the values themselves dont count as being stored *in* the cache anymore. So
//! it can be queried multiple times or go out of scope and the values it
//! produces are not affected.
use libc::{uid_t, gid_t};
use std::cell::{Cell, RefCell};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::sync::Arc;
use base::{User, Group, all_users};
use traits::{Users, Groups};
/// A producer of user and group instances that caches every result.
///
/// For more information, see the [`users::cache` module documentation](index.html).
pub struct UsersCache {
users: BiMap<uid_t, User>,
groups: BiMap<gid_t, Group>,
uid: Cell<Option<uid_t>>,
gid: Cell<Option<gid_t>>,
euid: Cell<Option<uid_t>>,
egid: Cell<Option<gid_t>>,
}
/// A kinda-bi-directional `HashMap` that associates keys to values, and
/// then strings back to keys.
///
/// It doesnt go the full route and offer *values*-to-keys lookup, because we
/// only want to search based on usernames and group names. There wouldnt be
/// much point offering a “User to uid” map, as the uid is present in the
/// `User` struct!
struct BiMap<K, V> {
forward: RefCell< HashMap<K, Option<Arc<V>>> >,
backward: RefCell< HashMap<Arc<OsStr>, Option<K>> >,
}
// Default has to be impld manually here, because theres no
// Default impl on User or Group, even though those types arent
// needed to produce a default instance of any HashMaps...
impl Default for UsersCache {
fn default() -> Self {
Self {
users: BiMap {
forward: RefCell::new(HashMap::new()),
backward: RefCell::new(HashMap::new()),
},
groups: BiMap {
forward: RefCell::new(HashMap::new()),
backward: RefCell::new(HashMap::new()),
},
uid: Cell::new(None),
gid: Cell::new(None),
euid: Cell::new(None),
egid: Cell::new(None),
}
}
}
impl UsersCache {
/// Creates a new empty cache.
///
/// # Examples
///
/// ```
/// use users::cache::UsersCache;
///
/// let cache = UsersCache::new();
/// ```
pub fn new() -> Self {
Self::default()
}
/// Creates a new cache that contains all the users present on the system.
///
/// # Safety
///
/// This is `unsafe` because we cannot prevent data races if two caches
/// were attempted to be initialised on different threads at the same time.
/// For more information, see the [`all_users` documentation](../fn.all_users.html).
///
/// # Examples
///
/// ```
/// use users::cache::UsersCache;
///
/// let cache = unsafe { UsersCache::with_all_users() };
/// ```
pub unsafe fn with_all_users() -> Self {
let cache = Self::new();
for user in all_users() {
let uid = user.uid();
let user_arc = Arc::new(user);
cache.users.forward.borrow_mut().insert(uid, Some(Arc::clone(&user_arc)));
cache.users.backward.borrow_mut().insert(Arc::clone(&user_arc.name_arc), Some(uid));
}
cache
}
}
// TODO: stop using Arc::from with entry API
// The get_*_by_name functions below create a new Arc before even testing if
// the user exists in the cache, essentially creating an unnecessary Arc.
// https://internals.rust-lang.org/t/pre-rfc-abandonning-morals-in-the-name-of-performance-the-raw-entry-api/7043/51
// https://github.com/rust-lang/rfcs/pull/1769
impl Users for UsersCache {
fn get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>> {
let mut users_forward = self.users.forward.borrow_mut();
let entry = match users_forward.entry(uid) {
Vacant(e) => e,
Occupied(e) => return e.get().as_ref().map(Arc::clone),
};
if let Some(user) = super::get_user_by_uid(uid) {
let newsername = Arc::clone(&user.name_arc);
let mut users_backward = self.users.backward.borrow_mut();
users_backward.insert(newsername, Some(uid));
let user_arc = Arc::new(user);
entry.insert(Some(Arc::clone(&user_arc)));
Some(user_arc)
}
else {
entry.insert(None);
None
}
}
fn get_user_by_name<S: AsRef<OsStr> + ?Sized>(&self, username: &S) -> Option<Arc<User>> {
let mut users_backward = self.users.backward.borrow_mut();
let entry = match users_backward.entry(Arc::from(username.as_ref())) {
Vacant(e) => e,
Occupied(e) => {
return (*e.get()).and_then(|uid| {
let users_forward = self.users.forward.borrow_mut();
users_forward[&uid].as_ref().map(Arc::clone)
})
}
};
if let Some(user) = super::get_user_by_name(username) {
let uid = user.uid();
let user_arc = Arc::new(user);
let mut users_forward = self.users.forward.borrow_mut();
users_forward.insert(uid, Some(Arc::clone(&user_arc)));
entry.insert(Some(uid));
Some(user_arc)
}
else {
entry.insert(None);
None
}
}
fn get_current_uid(&self) -> uid_t {
self.uid.get().unwrap_or_else(|| {
let uid = super::get_current_uid();
self.uid.set(Some(uid));
uid
})
}
fn get_current_username(&self) -> Option<Arc<OsStr>> {
let uid = self.get_current_uid();
self.get_user_by_uid(uid).map(|u| Arc::clone(&u.name_arc))
}
fn get_effective_uid(&self) -> uid_t {
self.euid.get().unwrap_or_else(|| {
let uid = super::get_effective_uid();
self.euid.set(Some(uid));
uid
})
}
fn get_effective_username(&self) -> Option<Arc<OsStr>> {
let uid = self.get_effective_uid();
self.get_user_by_uid(uid).map(|u| Arc::clone(&u.name_arc))
}
}
impl Groups for UsersCache {
fn get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>> {
let mut groups_forward = self.groups.forward.borrow_mut();
let entry = match groups_forward.entry(gid) {
Vacant(e) => e,
Occupied(e) => return e.get().as_ref().map(Arc::clone),
};
if let Some(group) = super::get_group_by_gid(gid) {
let new_group_name = Arc::clone(&group.name_arc);
let mut groups_backward = self.groups.backward.borrow_mut();
groups_backward.insert(new_group_name, Some(gid));
let group_arc = Arc::new(group);
entry.insert(Some(Arc::clone(&group_arc)));
Some(group_arc)
}
else {
entry.insert(None);
None
}
}
fn get_group_by_name<S: AsRef<OsStr> + ?Sized>(&self, group_name: &S) -> Option<Arc<Group>> {
let mut groups_backward = self.groups.backward.borrow_mut();
let entry = match groups_backward.entry(Arc::from(group_name.as_ref())) {
Vacant(e) => e,
Occupied(e) => {
return (*e.get()).and_then(|gid| {
let groups_forward = self.groups.forward.borrow_mut();
groups_forward[&gid].as_ref().cloned()
});
}
};
if let Some(group) = super::get_group_by_name(group_name) {
let group_arc = Arc::new(group.clone());
let gid = group.gid();
let mut groups_forward = self.groups.forward.borrow_mut();
groups_forward.insert(gid, Some(Arc::clone(&group_arc)));
entry.insert(Some(gid));
Some(group_arc)
}
else {
entry.insert(None);
None
}
}
fn get_current_gid(&self) -> gid_t {
self.gid.get().unwrap_or_else(|| {
let gid = super::get_current_gid();
self.gid.set(Some(gid));
gid
})
}
fn get_current_groupname(&self) -> Option<Arc<OsStr>> {
let gid = self.get_current_gid();
self.get_group_by_gid(gid).map(|g| Arc::clone(&g.name_arc))
}
fn get_effective_gid(&self) -> gid_t {
self.egid.get().unwrap_or_else(|| {
let gid = super::get_effective_gid();
self.egid.set(Some(gid));
gid
})
}
fn get_effective_groupname(&self) -> Option<Arc<OsStr>> {
let gid = self.get_effective_gid();
self.get_group_by_gid(gid).map(|g| Arc::clone(&g.name_arc))
}
}

153
vendor/users/src/lib.rs vendored Normal file
View file

@ -0,0 +1,153 @@
#![warn(missing_copy_implementations)]
#![warn(missing_docs)]
#![warn(nonstandard_style)]
#![warn(trivial_numeric_casts)]
#![warn(unreachable_pub)]
#![warn(unused)]
//! This is a library for getting information on Unix users and groups. It
//! supports getting the system users, and creating your own mock tables.
//!
//! In Unix, each user has an individual *user ID*, and each process has an
//! *effective user ID* that says which users permissions it is using.
//! Furthermore, users can be the members of *groups*, which also have names and
//! IDs. This functionality is exposed in libc, the C standard library, but as
//! an unsafe Rust interface. This wrapper library provides a safe interface,
//! using [`User`](struct.user.html) and [`Group`](struct.group.html) types
//! and functions such as [`get_user_by_uid`](fn.get_user_by_uid.html) instead
//! of low-level pointers and strings. It also offers basic caching
//! functionality.
//!
//! It does not (yet) offer *editing* functionality; the values returned are
//! read-only.
//!
//!
//! ## Users
//!
//! The function [`get_current_uid`](fn.get_current_uid.html) returns a
//! `uid_t` value representing the user currently running the program, and the
//! [`get_user_by_uid`](fn.get_user_by_uid.html) function scans the users
//! database and returns a `User` with the users information. This function
//! returns `None` when there is no user for that ID. The `uid_t` type is
//! re-exported from the libc crate.
//!
//! A [`User`](struct.User.html) value has the following accessors:
//!
//! - **uid:** The users ID
//! - **name:** The users name
//! - **primary_group:** The ID of this users primary group
//!
//! Here is a complete example that prints out the current users name:
//!
//! ```
//! use users::{get_user_by_uid, get_current_uid};
//!
//! let user = get_user_by_uid(get_current_uid()).unwrap();
//! println!("Hello, {}!", user.name().to_string_lossy());
//! ```
//!
//! This code assumes (with `unwrap()`) that the user hasnt been deleted after
//! the program has started running. For arbitrary user IDs, this is **not** a
//! safe assumption: its possible to delete a user while its running a
//! program, or is the owner of files, or for that user to have never existed.
//! So always check the return values!
//!
//! There is also a [`get_current_username`](fn.get_current_username.html)
//! function, as its such a common operation that it deserves special
//! treatment.
//!
//!
//! ## Caching
//!
//! Despite the above warning, the users and groups database rarely changes.
//! While a short program may only need to get user information once, a
//! long-running one may need to re-query the database many times, and a
//! medium-length one may get away with caching the values to save on redundant
//! system calls.
//!
//! For this reason, this crate offers a caching interface to the database,
//! which offers the same functionality while holding on to every result,
//! caching the information so it can be re-used.
//!
//! To introduce a cache, create a new
//! [`UsersCache`](cache/struct.UsersCache.html). It has functions with the
//! same names as the ones from earlier. For example:
//!
//! ```
//! use users::{Users, Groups, UsersCache};
//!
//! let mut cache = UsersCache::new();
//! let uid = cache.get_current_uid();
//! let user = cache.get_user_by_uid(uid).unwrap();
//! println!("Hello again, {}!", user.name().to_string_lossy());
//! ```
//!
//! This cache is **only additive**: its not possible to drop it, or erase
//! selected entries, as when the database may have been modified, its best to
//! start entirely afresh. So to accomplish this, just start using a new
//! `UsersCache`.
//!
//!
//! ## Groups
//!
//! Finally, its possible to get groups in a similar manner.
//! A [`Group`](struct.Group.html) has the following accessors:
//!
//! - **gid:** The groups ID
//! - **name:** The groups name
//!
//! And again, a complete example:
//!
//! ```no_run
//! use users::{Users, Groups, UsersCache};
//!
//! let mut cache = UsersCache::new();
//! let group = cache.get_group_by_name("admin").expect("No such group 'admin'!");
//! println!("The '{}' group has the ID {}", group.name().to_string_lossy(), group.gid());
//! ```
//!
//!
//! ## Logging
//!
//! The `logging` feature, which is on by default, uses the `log` crate to
//! record all interactions with the operating system.
//!
//!
//! ## Caveats
//!
//! You should be prepared for the users and groups tables to be completely
//! broken: IDs shouldnt be assumed to map to actual users and groups, and
//! usernames and group names arent guaranteed to map either!
//!
//! Use the [`mock`](mock/index.html) module to create custom tables to test
//! your code for these edge cases.
extern crate libc;
pub use libc::{uid_t, gid_t};
mod base;
pub use base::{User, Group, os};
pub use base::{get_user_by_uid, get_user_by_name};
pub use base::{get_group_by_gid, get_group_by_name};
pub use base::{get_current_uid, get_current_username};
pub use base::{get_effective_uid, get_effective_username};
pub use base::{get_current_gid, get_current_groupname};
pub use base::{get_effective_gid, get_effective_groupname};
pub use base::{get_user_groups, group_access_list};
pub use base::{all_users};
#[cfg(feature = "cache")]
pub mod cache;
#[cfg(feature = "cache")]
pub use cache::UsersCache;
#[cfg(feature = "mock")]
pub mod mock;
pub mod switch;
mod traits;
pub use traits::{Users, Groups};

237
vendor/users/src/mock.rs vendored Normal file
View file

@ -0,0 +1,237 @@
//! Mockable users and groups.
//!
//! When youre testing your code, you dont want to actually rely on the
//! system actually having various users and groups present - its much better
//! to have a custom set of users that are *guaranteed* to be there, so you can
//! test against them.
//!
//! This module allows you to create these custom users and groups
//! definitions, then access them using the same `Users` trait as in the main
//! library, with few changes to your code.
//!
//!
//! ## Creating Mock Users
//!
//! The only thing a mock users table needs to know in advance is the UID of
//! the current user. Aside from that, you can add users and groups with
//! `add_user` and `add_group` to the table:
//!
//! ```
//! use std::sync::Arc;
//! use users::mock::{MockUsers, User, Group};
//! use users::os::unix::{UserExt, GroupExt};
//!
//! let mut users = MockUsers::with_current_uid(1000);
//! let bobbins = User::new(1000, "Bobbins", 1000).with_home_dir("/home/bobbins");
//! users.add_user(bobbins);
//! users.add_group(Group::new(100, "funkyppl"));
//! ```
//!
//! The exports get re-exported into the mock module, for simpler `use` lines.
//!
//!
//! ## Using Mock Users
//!
//! To set your program up to use either type of `Users` table, make your
//! functions and structs accept a generic parameter that implements the `Users`
//! trait. Then, you can pass in a value of either Cache or Mock type.
//!
//! Heres a complete example:
//!
//! ```
//! use std::sync::Arc;
//! use users::{Users, UsersCache, User};
//! use users::os::unix::UserExt;
//! use users::mock::MockUsers;
//!
//! fn print_current_username<U: Users>(users: &mut U) {
//! println!("Current user: {:?}", users.get_current_username());
//! }
//!
//! let mut users = MockUsers::with_current_uid(1001);
//! users.add_user(User::new(1001, "fred", 101));
//! print_current_username(&mut users);
//!
//! let mut actual_users = UsersCache::new();
//! print_current_username(&mut actual_users);
//! ```
use std::collections::HashMap;
use std::ffi::OsStr;
use std::sync::Arc;
pub use libc::{uid_t, gid_t};
pub use base::{User, Group};
pub use traits::{Users, Groups};
/// A mocking users table that you can add your own users and groups to.
pub struct MockUsers {
users: HashMap<uid_t, Arc<User>>,
groups: HashMap<gid_t, Arc<Group>>,
uid: uid_t,
}
impl MockUsers {
/// Create a new, empty mock users table.
pub fn with_current_uid(current_uid: uid_t) -> Self {
Self {
users: HashMap::new(),
groups: HashMap::new(),
uid: current_uid,
}
}
/// Add a user to the users table.
pub fn add_user(&mut self, user: User) -> Option<Arc<User>> {
self.users.insert(user.uid(), Arc::new(user))
}
/// Add a group to the groups table.
pub fn add_group(&mut self, group: Group) -> Option<Arc<Group>> {
self.groups.insert(group.gid(), Arc::new(group))
}
}
impl Users for MockUsers {
fn get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>> {
self.users.get(&uid).cloned()
}
fn get_user_by_name<S: AsRef<OsStr> + ?Sized>(&self, username: &S) -> Option<Arc<User>> {
self.users.values().find(|u| u.name() == username.as_ref()).cloned()
}
fn get_current_uid(&self) -> uid_t {
self.uid
}
fn get_current_username(&self) -> Option<Arc<OsStr>> {
self.users.get(&self.uid).map(|u| Arc::clone(&u.name_arc))
}
fn get_effective_uid(&self) -> uid_t {
self.uid
}
fn get_effective_username(&self) -> Option<Arc<OsStr>> {
self.users.get(&self.uid).map(|u| Arc::clone(&u.name_arc))
}
}
impl Groups for MockUsers {
fn get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>> {
self.groups.get(&gid).cloned()
}
fn get_group_by_name<S: AsRef<OsStr> + ?Sized>(&self, group_name: &S) -> Option<Arc<Group>> {
self.groups.values().find(|g| g.name() == group_name.as_ref()).cloned()
}
fn get_current_gid(&self) -> uid_t {
self.uid
}
fn get_current_groupname(&self) -> Option<Arc<OsStr>> {
self.groups.get(&self.uid).map(|u| Arc::clone(&u.name_arc))
}
fn get_effective_gid(&self) -> uid_t {
self.uid
}
fn get_effective_groupname(&self) -> Option<Arc<OsStr>> {
self.groups.get(&self.uid).map(|u| Arc::clone(&u.name_arc))
}
}
#[cfg(test)]
mod test {
use super::MockUsers;
use base::{User, Group};
use traits::{Users, Groups};
use std::ffi::OsStr;
use std::sync::Arc;
#[test]
fn current_username() {
let mut users = MockUsers::with_current_uid(1337);
users.add_user(User::new(1337, "fred", 101));
assert_eq!(Some(Arc::from(OsStr::new("fred"))),
users.get_current_username())
}
#[test]
fn no_current_username() {
let users = MockUsers::with_current_uid(1337);
assert_eq!(None, users.get_current_username())
}
#[test]
fn uid() {
let mut users = MockUsers::with_current_uid(0);
users.add_user(User::new(1337, "fred", 101));
assert_eq!(Some(Arc::from(OsStr::new("fred"))),
users.get_user_by_uid(1337).map(|u| Arc::clone(&u.name_arc)))
}
#[test]
fn username() {
let mut users = MockUsers::with_current_uid(1337);
users.add_user(User::new(1440, "fred", 101));
assert_eq!(Some(1440),
users.get_user_by_name("fred").map(|u| u.uid()))
}
#[test]
fn no_username() {
let mut users = MockUsers::with_current_uid(1337);
users.add_user(User::new(1337, "fred", 101));
assert_eq!(None,
users.get_user_by_name("criminy").map(|u| u.uid()))
}
#[test]
fn no_uid() {
let users = MockUsers::with_current_uid(0);
assert_eq!(None,
users.get_user_by_uid(1337).map(|u| Arc::clone(&u.name_arc)))
}
#[test]
fn gid() {
let mut users = MockUsers::with_current_uid(0);
users.add_group(Group::new(1337, "fred"));
assert_eq!(Some(Arc::from(OsStr::new("fred"))),
users.get_group_by_gid(1337).map(|g| Arc::clone(&g.name_arc)))
}
#[test]
fn group_name() {
let mut users = MockUsers::with_current_uid(0);
users.add_group(Group::new(1337, "fred"));
assert_eq!(Some(1337),
users.get_group_by_name("fred").map(|g| g.gid()))
}
#[test]
fn no_group_name() {
let mut users = MockUsers::with_current_uid(0);
users.add_group(Group::new(1337, "fred"));
assert_eq!(None,
users.get_group_by_name("santa").map(|g| g.gid()))
}
#[test]
fn no_gid() {
let users = MockUsers::with_current_uid(0);
assert_eq!(None,
users.get_group_by_gid(1337).map(|g| Arc::clone(&g.name_arc)))
}
}

265
vendor/users/src/switch.rs vendored Normal file
View file

@ -0,0 +1,265 @@
//! Functions for switching the running processs user or group.
use std::io;
use libc::{uid_t, gid_t, c_int};
use base::{get_effective_uid, get_effective_gid};
// NOTE: for whatever reason, it seems these are not available in libc on BSD platforms, so they
// need to be included manually
extern {
fn setreuid(ruid: uid_t, euid: uid_t) -> c_int;
fn setregid(rgid: gid_t, egid: gid_t) -> c_int;
}
/// Sets the **current user** for the running process to the one with the
/// given user ID.
///
/// Typically, trying to switch to anyone other than the user already running
/// the process requires root privileges.
///
/// # libc functions used
///
/// - [`setuid`](https://docs.rs/libc/*/libc/fn.setuid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during the
/// `setuid` call.
///
/// # Examples
///
/// ```no_run
/// use users::switch::set_current_uid;
///
/// set_current_uid(1001);
/// // current user ID is 1001
/// ```
pub fn set_current_uid(uid: uid_t) -> io::Result<()> {
match unsafe { libc::setuid(uid) } {
0 => Ok(()),
-1 => Err(io::Error::last_os_error()),
n => unreachable!("setuid returned {}", n)
}
}
/// Sets the **current group** for the running process to the one with the
/// given group ID.
///
/// Typically, trying to switch to any group other than the group already
/// running the process requires root privileges.
///
/// # libc functions used
///
/// - [`setgid`](https://docs.rs/libc/*/libc/fn.setgid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during the
/// `setgid` call.
///
/// # Examples
///
/// ```no_run
/// use users::switch::set_current_gid;
///
/// set_current_gid(1001);
/// // current group ID is 1001
/// ```
pub fn set_current_gid(gid: gid_t) -> io::Result<()> {
match unsafe { libc::setgid(gid) } {
0 => Ok(()),
-1 => Err(io::Error::last_os_error()),
n => unreachable!("setgid returned {}", n)
}
}
/// Sets the **effective user** for the running process to the one with the
/// given user ID.
///
/// Typically, trying to switch to anyone other than the user already running
/// the process requires root privileges.
///
/// # libc functions used
///
/// - [`seteuid`](https://docs.rs/libc/*/libc/fn.seteuid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during the
/// `seteuid` call.
///
/// # Examples
///
/// ```no_run
/// use users::switch::set_effective_uid;
///
/// set_effective_uid(1001);
/// // current effective user ID is 1001
/// ```
pub fn set_effective_uid(uid: uid_t) -> io::Result<()> {
match unsafe { libc::seteuid(uid) } {
0 => Ok(()),
-1 => Err(io::Error::last_os_error()),
n => unreachable!("seteuid returned {}", n)
}
}
/// Sets the **effective group** for the running process to the one with the
/// given group ID.
///
/// Typically, trying to switch to any group other than the group already
/// running the process requires root privileges.
///
/// # libc functions used
///
/// - [`setegid`](https://docs.rs/libc/*/libc/fn.setegid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during the
/// `setegid` call.
///
/// # Examples
///
/// ```no_run
/// use users::switch::set_effective_gid;
///
/// set_effective_gid(1001);
/// // current effective group ID is 1001
/// ```
pub fn set_effective_gid(gid: gid_t) -> io::Result<()> {
match unsafe { libc::setegid(gid) } {
0 => Ok(()),
-1 => Err(io::Error::last_os_error()),
n => unreachable!("setegid returned {}", n)
}
}
/// Sets both the **current user** and the **effective user** for the running
/// process to the ones with the given user IDs.
///
/// Typically, trying to switch to anyone other than the user already running
/// the process requires root privileges.
///
/// # libc functions used
///
/// - [`setreuid`](https://docs.rs/libc/*/libc/fn.setreuid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during the
/// `setreuid` call.
///
/// # Examples
///
/// ```no_run
/// use users::switch::set_both_uid;
///
/// set_both_uid(1001, 1001);
/// // current user ID and effective user ID are 1001
/// ```
pub fn set_both_uid(ruid: uid_t, euid: uid_t) -> io::Result<()> {
match unsafe { setreuid(ruid, euid) } {
0 => Ok(()),
-1 => Err(io::Error::last_os_error()),
n => unreachable!("setreuid returned {}", n)
}
}
/// Sets both the **current group** and the **effective group** for the
/// running process to the ones with the given group IDs.
///
/// Typically, trying to switch to any group other than the group already
/// running the process requires root privileges.
///
/// # libc functions used
///
/// - [`setregid`](https://docs.rs/libc/*/libc/fn.setregid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during the
/// `setregid` call.
///
/// # Examples
///
/// ```no_run
/// use users::switch::set_both_gid;
///
/// set_both_gid(1001, 1001);
/// // current user ID and effective group ID are 1001
/// ```
pub fn set_both_gid(rgid: gid_t, egid: gid_t) -> io::Result<()> {
match unsafe { setregid(rgid, egid) } {
0 => Ok(()),
-1 => Err(io::Error::last_os_error()),
n => unreachable!("setregid returned {}", n)
}
}
/// Guard returned from a `switch_user_group` call.
pub struct SwitchUserGuard {
uid: uid_t,
gid: gid_t,
}
impl Drop for SwitchUserGuard {
fn drop(&mut self) {
set_effective_gid(self.gid).expect("Failed to set effective gid");
set_effective_uid(self.uid).expect("Failed to set effective uid");
}
}
/// Sets the **effective user** and the **effective group** for the current
/// scope.
///
/// Typically, trying to switch to any user or group other than the ones already
/// running the process requires root privileges.
///
/// # Security considerations
///
/// - Because Rust does not guarantee running the destructor, its a good idea
/// to call [`std::mem::drop`](https://doc.rust-lang.org/std/mem/fn.drop.html)
/// on the guard manually in security-sensitive situations.
/// - This function switches the group before the user to prevent the users
/// privileges being dropped before trying to change the group (look up
/// `POS36-C`).
/// - This function will panic upon failing to set either walue, so the
/// program does not continue executing with too many privileges.
///
/// # libc functions used
///
/// - [`seteuid`](https://docs.rs/libc/*/libc/fn.seteuid.html)
/// - [`setegid`](https://docs.rs/libc/*/libc/fn.setegid.html)
///
/// # Errors
///
/// This function will return `Err` when an I/O error occurs during either
/// the `seteuid` or `setegid` calls.
///
/// # Examples
///
/// ```no_run
/// use users::switch::switch_user_group;
/// use std::mem::drop;
///
/// {
/// let guard = switch_user_group(1001, 1001);
/// // current and effective user and group IDs are 1001
/// drop(guard);
/// }
/// // back to the old values
/// ```
pub fn switch_user_group(uid: uid_t, gid: gid_t) -> io::Result<SwitchUserGuard> {
let current_state = SwitchUserGuard {
gid: get_effective_gid(),
uid: get_effective_uid(),
};
set_effective_gid(gid)?;
set_effective_uid(uid)?;
Ok(current_state)
}

51
vendor/users/src/traits.rs vendored Normal file
View file

@ -0,0 +1,51 @@
use std::ffi::OsStr;
use std::sync::Arc;
use libc::{uid_t, gid_t};
use base::{User, Group};
/// Trait for producers of users.
pub trait Users {
/// Returns a `User` if one exists for the given user ID; otherwise, returns `None`.
fn get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>>;
/// Returns a `User` if one exists for the given username; otherwise, returns `None`.
fn get_user_by_name<S: AsRef<OsStr> + ?Sized>(&self, username: &S) -> Option<Arc<User>>;
/// Returns the user ID for the user running the process.
fn get_current_uid(&self) -> uid_t;
/// Returns the username of the user running the process.
fn get_current_username(&self) -> Option<Arc<OsStr>>;
/// Returns the effective user id.
fn get_effective_uid(&self) -> uid_t;
/// Returns the effective username.
fn get_effective_username(&self) -> Option<Arc<OsStr>>;
}
/// Trait for producers of groups.
pub trait Groups {
/// Returns a `Group` if one exists for the given group ID; otherwise, returns `None`.
fn get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>>;
/// Returns a `Group` if one exists for the given groupname; otherwise, returns `None`.
fn get_group_by_name<S: AsRef<OsStr> + ?Sized>(&self, group_name: &S) -> Option<Arc<Group>>;
/// Returns the group ID for the user running the process.
fn get_current_gid(&self) -> gid_t;
/// Returns the group name of the user running the process.
fn get_current_groupname(&self) -> Option<Arc<OsStr>>;
/// Returns the effective group id.
fn get_effective_gid(&self) -> gid_t;
/// Returns the effective group name.
fn get_effective_groupname(&self) -> Option<Arc<OsStr>>;
}