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 87 use libc::{uid_t, gid_t}; 88 use std::cell::{Cell, RefCell}; 89 use std::collections::hash_map::Entry::{Occupied, Vacant}; 90 use std::collections::HashMap; 91 use std::ffi::OsStr; 92 use std::sync::Arc; 93 94 use base::{User, Group, all_users}; 95 use 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). 101 pub 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! 118 struct 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... 127 impl Default for UsersCache { default() -> Self128 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 149 impl 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 /// ``` new() -> Self160 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 /// ``` with_all_users() -> Self179 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 201 impl Users for UsersCache { get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>>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 get_user_by_name<S: AsRef<OsStr> + ?Sized>(&self, username: &S) -> Option<Arc<User>>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 get_current_uid(&self) -> uid_t254 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 get_current_username(&self) -> Option<Arc<OsStr>>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 get_effective_uid(&self) -> uid_t267 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 get_effective_username(&self) -> Option<Arc<OsStr>>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 282 impl Groups for UsersCache { get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>>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 get_group_by_name<S: AsRef<OsStr> + ?Sized>(&self, group_name: &S) -> Option<Arc<Group>>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 get_current_gid(&self) -> gid_t335 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 get_current_groupname(&self) -> Option<Arc<OsStr>>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 get_effective_gid(&self) -> gid_t348 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 get_effective_groupname(&self) -> Option<Arc<OsStr>>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 } 361