users/cache.rs
1//! A cache for users and groups provided by the OS.
2//!
3//! Because the users table changes so infrequently, it's common for
4//! short-running programs to cache the results instead of getting the most
5//! up-to-date entries every time. The [`UsersCache`](struct.UsersCache.html)
6//! type helps with this, providing methods that have the same name as the
7//! others in this crate, only they store the results.
8//!
9//! ## Example
10//!
11//! ```no_run
12//! use std::sync::Arc;
13//! use users::{Users, UsersCache};
14//!
15//! let mut cache = UsersCache::new();
16//! let user      = cache.get_user_by_uid(502).expect("User not found");
17//! let same_user = cache.get_user_by_uid(502).unwrap();
18//!
19//! // The two returned values point to the same User
20//! assert!(Arc::ptr_eq(&user, &same_user));
21//! ```
22//!
23//! ## Caching, multiple threads, and mutability
24//!
25//! The `UsersCache` type is caught between a rock and a hard place when it
26//! comes to providing references to users and groups.
27//!
28//! Instead of returning a fresh `User` struct each time, for example, it will
29//! return a reference to the version it currently has in its cache. So you can
30//! ask for User #501 twice, and you’ll get a reference to the same value both
31//! time. Its methods are *idempotent* -- calling one multiple times has the
32//! same effect as calling one once.
33//!
34//! This works fine in theory, but in practice, the cache has to update its own
35//! state somehow: it contains several `HashMap`s that hold the result of user
36//! and group lookups. Rust provides mutability in two ways:
37//!
38//! 1. Have its methods take `&mut self`, instead of `&self`, allowing the
39//!   internal maps to be mutated (“inherited mutability”)
40//! 2. Wrap the internal maps in a `RefCell`, allowing them to be modified
41//!   (“interior mutability”).
42//!
43//! Unfortunately, Rust is also very protective of references to a mutable
44//! value. In this case, switching to `&mut self` would only allow for one user
45//! to be read at a time!
46//!
47//! ```no_run
48//! use users::{Users, Groups, UsersCache};
49//!
50//! let mut cache = UsersCache::new();
51//!
52//! let uid   = cache.get_current_uid();                          // OK...
53//! let user  = cache.get_user_by_uid(uid).unwrap();              // OK...
54//! let group = cache.get_group_by_gid(user.primary_group_id());  // No!
55//! ```
56//!
57//! When we get the `user`, it returns an optional reference (which we unwrap)
58//! to the user’s entry in the cache. This is a reference to something contained
59//! in a mutable value. Then, when we want to get the user’s primary group, it
60//! will return *another* reference to the same mutable value. This is something
61//! that Rust explicitly disallows!
62//!
63//! The compiler wasn’t on our side with Option 1, so let’s try Option 2:
64//! changing the methods back to `&self` instead of `&mut self`, and using
65//! `RefCell`s internally. However, Rust is smarter than this, and knows that
66//! we’re just trying the same trick as earlier. A simplified implementation of
67//! a user cache lookup would look something like this:
68//!
69//! ```text
70//! fn get_user_by_uid(&self, uid: uid_t) -> Option<&User> {
71//!     let users = self.users.borrow_mut();
72//!     users.get(uid)
73//! }
74//! ```
75//!
76//! Rust won’t allow us to return a reference like this because the `Ref` of the
77//! `RefCell` just gets dropped at the end of the method, meaning that our
78//! reference does not live long enough.
79//!
80//! So instead of doing any of that, we use `Arc` everywhere in order to get
81//! around all the lifetime restrictions. Returning reference-counted users and
82//! groups mean that we don’t have to worry about further uses of the cache, as
83//! the values themselves don’t count as being stored *in* the cache anymore. So
84//! it can be queried multiple times or go out of scope and the values it
85//! produces are not affected.
86
87use libc::{uid_t, gid_t};
88use std::cell::{Cell, RefCell};
89use std::collections::hash_map::Entry::{Occupied, Vacant};
90use std::collections::HashMap;
91use std::ffi::OsStr;
92use std::sync::Arc;
93
94use base::{User, Group, all_users};
95use traits::{Users, Groups};
96
97
98/// A producer of user and group instances that caches every result.
99///
100/// For more information, see the [`users::cache` module documentation](index.html).
101pub struct UsersCache {
102    users:  BiMap<uid_t, User>,
103    groups: BiMap<gid_t, Group>,
104
105    uid:  Cell<Option<uid_t>>,
106    gid:  Cell<Option<gid_t>>,
107    euid: Cell<Option<uid_t>>,
108    egid: Cell<Option<gid_t>>,
109}
110
111/// A kinda-bi-directional `HashMap` that associates keys to values, and
112/// then strings back to keys.
113///
114/// It doesn’t go the full route and offer *values*-to-keys lookup, because we
115/// only want to search based on usernames and group names. There wouldn’t be
116/// much point offering a “User to uid” map, as the uid is present in the
117/// `User` struct!
118struct BiMap<K, V> {
119    forward:  RefCell< HashMap<K, Option<Arc<V>>> >,
120    backward: RefCell< HashMap<Arc<OsStr>, Option<K>> >,
121}
122
123
124// Default has to be impl’d manually here, because there’s no
125// Default impl on User or Group, even though those types aren’t
126// needed to produce a default instance of any HashMaps...
127impl Default for UsersCache {
128    fn default() -> Self {
129        Self {
130            users: BiMap {
131                forward:  RefCell::new(HashMap::new()),
132                backward: RefCell::new(HashMap::new()),
133            },
134
135            groups: BiMap {
136                forward:  RefCell::new(HashMap::new()),
137                backward: RefCell::new(HashMap::new()),
138            },
139
140            uid:  Cell::new(None),
141            gid:  Cell::new(None),
142            euid: Cell::new(None),
143            egid: Cell::new(None),
144        }
145    }
146}
147
148
149impl UsersCache {
150
151    /// Creates a new empty cache.
152    ///
153    /// # Examples
154    ///
155    /// ```
156    /// use users::cache::UsersCache;
157    ///
158    /// let cache = UsersCache::new();
159    /// ```
160    pub fn new() -> Self {
161        Self::default()
162    }
163
164    /// Creates a new cache that contains all the users present on the system.
165    ///
166    /// # Safety
167    ///
168    /// This is `unsafe` because we cannot prevent data races if two caches
169    /// were attempted to be initialised on different threads at the same time.
170    /// For more information, see the [`all_users` documentation](../fn.all_users.html).
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use users::cache::UsersCache;
176    ///
177    /// let cache = unsafe { UsersCache::with_all_users() };
178    /// ```
179    pub unsafe fn with_all_users() -> Self {
180        let cache = Self::new();
181
182        for user in all_users() {
183            let uid = user.uid();
184            let user_arc = Arc::new(user);
185            cache.users.forward.borrow_mut().insert(uid, Some(Arc::clone(&user_arc)));
186            cache.users.backward.borrow_mut().insert(Arc::clone(&user_arc.name_arc), Some(uid));
187        }
188
189        cache
190    }
191}
192
193
194// TODO: stop using ‘Arc::from’ with entry API
195// The ‘get_*_by_name’ functions below create a new Arc before even testing if
196// the user exists in the cache, essentially creating an unnecessary Arc.
197// https://internals.rust-lang.org/t/pre-rfc-abandonning-morals-in-the-name-of-performance-the-raw-entry-api/7043/51
198// https://github.com/rust-lang/rfcs/pull/1769
199
200
201impl Users for UsersCache {
202    fn get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>> {
203        let mut users_forward = self.users.forward.borrow_mut();
204
205        let entry = match users_forward.entry(uid) {
206            Vacant(e) => e,
207            Occupied(e) => return e.get().as_ref().map(Arc::clone),
208        };
209
210        if let Some(user) = super::get_user_by_uid(uid) {
211            let newsername = Arc::clone(&user.name_arc);
212            let mut users_backward = self.users.backward.borrow_mut();
213            users_backward.insert(newsername, Some(uid));
214
215            let user_arc = Arc::new(user);
216            entry.insert(Some(Arc::clone(&user_arc)));
217            Some(user_arc)
218        }
219        else {
220            entry.insert(None);
221            None
222        }
223    }
224
225    fn get_user_by_name<S: AsRef<OsStr> + ?Sized>(&self, username: &S) -> Option<Arc<User>> {
226        let mut users_backward = self.users.backward.borrow_mut();
227
228        let entry = match users_backward.entry(Arc::from(username.as_ref())) {
229            Vacant(e) => e,
230            Occupied(e) => {
231                return (*e.get()).and_then(|uid| {
232                    let users_forward = self.users.forward.borrow_mut();
233                    users_forward[&uid].as_ref().map(Arc::clone)
234                })
235            }
236        };
237
238        if let Some(user) = super::get_user_by_name(username) {
239            let uid = user.uid();
240            let user_arc = Arc::new(user);
241
242            let mut users_forward = self.users.forward.borrow_mut();
243            users_forward.insert(uid, Some(Arc::clone(&user_arc)));
244            entry.insert(Some(uid));
245
246            Some(user_arc)
247        }
248        else {
249            entry.insert(None);
250            None
251        }
252    }
253
254    fn get_current_uid(&self) -> uid_t {
255        self.uid.get().unwrap_or_else(|| {
256            let uid = super::get_current_uid();
257            self.uid.set(Some(uid));
258            uid
259        })
260    }
261
262    fn get_current_username(&self) -> Option<Arc<OsStr>> {
263        let uid = self.get_current_uid();
264        self.get_user_by_uid(uid).map(|u| Arc::clone(&u.name_arc))
265    }
266
267    fn get_effective_uid(&self) -> uid_t {
268        self.euid.get().unwrap_or_else(|| {
269            let uid = super::get_effective_uid();
270            self.euid.set(Some(uid));
271            uid
272        })
273    }
274
275    fn get_effective_username(&self) -> Option<Arc<OsStr>> {
276        let uid = self.get_effective_uid();
277        self.get_user_by_uid(uid).map(|u| Arc::clone(&u.name_arc))
278    }
279}
280
281
282impl Groups for UsersCache {
283    fn get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>> {
284        let mut groups_forward = self.groups.forward.borrow_mut();
285
286        let entry = match groups_forward.entry(gid) {
287            Vacant(e) => e,
288            Occupied(e) => return e.get().as_ref().map(Arc::clone),
289        };
290
291        if let Some(group) = super::get_group_by_gid(gid) {
292            let new_group_name = Arc::clone(&group.name_arc);
293            let mut groups_backward = self.groups.backward.borrow_mut();
294            groups_backward.insert(new_group_name, Some(gid));
295
296            let group_arc = Arc::new(group);
297            entry.insert(Some(Arc::clone(&group_arc)));
298            Some(group_arc)
299        }
300        else {
301            entry.insert(None);
302            None
303        }
304    }
305
306    fn get_group_by_name<S: AsRef<OsStr> + ?Sized>(&self, group_name: &S) -> Option<Arc<Group>> {
307        let mut groups_backward = self.groups.backward.borrow_mut();
308
309        let entry = match groups_backward.entry(Arc::from(group_name.as_ref())) {
310            Vacant(e) => e,
311            Occupied(e) => {
312                return (*e.get()).and_then(|gid| {
313                    let groups_forward = self.groups.forward.borrow_mut();
314                    groups_forward[&gid].as_ref().cloned()
315                });
316            }
317        };
318
319        if let Some(group) = super::get_group_by_name(group_name) {
320            let group_arc = Arc::new(group.clone());
321            let gid = group.gid();
322
323            let mut groups_forward = self.groups.forward.borrow_mut();
324            groups_forward.insert(gid, Some(Arc::clone(&group_arc)));
325            entry.insert(Some(gid));
326
327            Some(group_arc)
328        }
329        else {
330            entry.insert(None);
331            None
332        }
333    }
334
335    fn get_current_gid(&self) -> gid_t {
336        self.gid.get().unwrap_or_else(|| {
337            let gid = super::get_current_gid();
338            self.gid.set(Some(gid));
339            gid
340        })
341    }
342
343    fn get_current_groupname(&self) -> Option<Arc<OsStr>> {
344        let gid = self.get_current_gid();
345        self.get_group_by_gid(gid).map(|g| Arc::clone(&g.name_arc))
346    }
347
348    fn get_effective_gid(&self) -> gid_t {
349        self.egid.get().unwrap_or_else(|| {
350            let gid = super::get_effective_gid();
351            self.egid.set(Some(gid));
352            gid
353        })
354    }
355
356    fn get_effective_groupname(&self) -> Option<Arc<OsStr>> {
357        let gid = self.get_effective_gid();
358        self.get_group_by_gid(gid).map(|g| Arc::clone(&g.name_arc))
359    }
360}