From 799804e455f55cc91da19c5928d45e9c06b0cb56 Mon Sep 17 00:00:00 2001 From: Knight Date: Sun, 14 Aug 2016 13:19:57 +0800 Subject: [PATCH] Add uucore::entries --- src/uucore/Cargo.toml | 3 +- src/uucore/coreopts.rs | 10 +- src/uucore/entries.rs | 245 +++++++++++++++++++++++++++++++++++++++++ src/uucore/lib.rs | 2 + 4 files changed, 254 insertions(+), 6 deletions(-) create mode 100644 src/uucore/entries.rs diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 7a53082a7..77676c251 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -21,8 +21,9 @@ utmpx = ["time", "libc"] c_types = ["libc"] process = ["libc"] signals = [] +entries = ["libc"] wide = [] -default = ["fs", "libc", "utf8", "encoding", "parse_time", "utmpx", "c_types", "process", "signals", "wide"] +default = ["fs", "libc", "utf8", "encoding", "parse_time", "utmpx", "c_types", "process", "entries", "signals", "wide"] [lib] path = "lib.rs" diff --git a/src/uucore/coreopts.rs b/src/uucore/coreopts.rs index b2e2e0084..ec22af9dd 100644 --- a/src/uucore/coreopts.rs +++ b/src/uucore/coreopts.rs @@ -44,11 +44,11 @@ impl<'a> CoreOptions<'a> { crash!(1, "{}", f); } }.unwrap(); - if matches.opt_present("help") { + if matches.opt_present("help") { let usage_str = if self.help_text.display_usage { - format!("\n {}\n\n Reference\n", + format!("\n {}\n\n Reference\n", self.options.usage(self.help_text.summary) - ).replace("Options:", " Options:") + ).replace("Options:", " Options:") } else { String::new() }; print!(" {0} {1} @@ -66,7 +66,7 @@ impl<'a> CoreOptions<'a> { } #[macro_export] -macro_rules! new_coreopts { +macro_rules! new_coreopts { ($syntax: expr, $summary: expr, $long_help: expr) => ( uucore::coreopts::CoreOptions::new(uucore::coreopts::HelpText { name: executable!(), @@ -87,4 +87,4 @@ macro_rules! new_coreopts { display_usage: $display_usage }) ); -} \ No newline at end of file +} diff --git a/src/uucore/entries.rs b/src/uucore/entries.rs new file mode 100644 index 000000000..5dae92dd3 --- /dev/null +++ b/src/uucore/entries.rs @@ -0,0 +1,245 @@ +// This file is part of the uutils coreutils package. +// +// (c) Jian Zeng +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +//! Get password/group file entry +//! +//! # Examples: +//! +//! ``` +//! use uucore::entries::{self, Locate}; +//! assert_eq!("root", entries::uid2usr(0).unwrap()); +//! assert_eq!(0, entries::usr2uid("root").unwrap()); +//! assert!(entries::gid2grp(0).is_ok()); +//! assert!(entries::grp2gid("root").is_ok()); +//! +//! assert!(entries::Passwd::locate(0).is_ok()); +//! assert!(entries::Passwd::locate("0").is_ok()); +//! assert!(entries::Passwd::locate("root").is_ok()); +//! +//! assert!(entries::Group::locate(0).is_ok()); +//! assert!(entries::Group::locate("0").is_ok()); +//! assert!(entries::Group::locate("root").is_ok()); +//! ``` + +#[cfg(any(target_os = "freebsd", target_os = "macos"))] +use libc::time_t; +use libc::{uid_t, gid_t, c_char, c_int}; +use libc::{passwd, group, getpwnam, getpwuid, getgrnam, getgrgid, getgroups}; + +use ::std::ptr; +use ::std::io::ErrorKind; +use ::std::io::Error as IOError; +use ::std::io::Result as IOResult; +use ::std::ffi::{CStr, CString}; +use ::std::borrow::Cow; + +extern "C" { + fn getgrouplist(name: *const c_char, gid: gid_t, groups: *mut gid_t, ngroups: *mut c_int) -> c_int; +} + +pub fn get_groups() -> IOResult> { + let ngroups = unsafe { getgroups(0, ptr::null_mut()) }; + if ngroups == -1 { + return Err(IOError::last_os_error()) + } + let mut groups = Vec::with_capacity(ngroups as usize); + let ngroups = unsafe { getgroups(ngroups, groups.as_mut_ptr()) }; + if ngroups == -1 { + Err(IOError::last_os_error()) + } else { + unsafe { + groups.set_len(ngroups as usize); + } + Ok(groups) + } +} + +pub struct Passwd { + inner: passwd, +} + +macro_rules! cstr2cow { + ($v:expr) => ( + unsafe { CStr::from_ptr($v).to_string_lossy() } + ) +} + +impl Passwd { + /// AKA passwd.pw_name + pub fn name(&self) -> Cow { + cstr2cow!(self.inner.pw_name) + } + + /// AKA passwd.pw_uid + pub fn uid(&self) -> uid_t { + self.inner.pw_uid + } + + /// AKA passwd.pw_gid + pub fn gid(&self) -> gid_t { + self.inner.pw_gid + } + + /// AKA passwd.pw_gecos + pub fn user_info(&self) -> Cow { + cstr2cow!(self.inner.pw_gecos) + } + + /// AKA passwd.pw_shell + pub fn user_shell(&self) -> Cow { + cstr2cow!(self.inner.pw_shell) + } + + /// AKA passwd.pw_dir + pub fn user_dir(&self) -> Cow { + cstr2cow!(self.inner.pw_dir) + } + + /// AKA passwd.pw_passwd + pub fn user_passwd(&self) -> Cow { + cstr2cow!(self.inner.pw_passwd) + } + + /// AKA passwd.pw_class + #[cfg(any(target_os = "freebsd", target_os = "macos"))] + pub fn user_access_class(&self) -> Cow { + cstr2cow!(self.inner.pw_class) + } + + /// AKA passwd.pw_change + #[cfg(any(target_os = "freebsd", target_os = "macos"))] + pub fn passwd_change_time(&self) -> time_t { + self.inner.pw_change + } + + /// AKA passwd.pw_expire + #[cfg(any(target_os = "freebsd", target_os = "macos"))] + pub fn expiration(&self) -> time_t { + self.inner.pw_expire + } + + pub fn as_inner(&self) -> &passwd { + &self.inner + } + + pub fn into_inner(self) -> passwd { + self.inner + } + + pub fn belongs_to(&self) -> Vec { + let mut ngroups: c_int = 8; + let mut groups = Vec::with_capacity(ngroups as usize); + let gid = self.inner.pw_gid; + let name = self.inner.pw_name; + unsafe { + if getgrouplist(name, gid, groups.as_mut_ptr(), &mut ngroups) == -1 { + groups.resize(ngroups as usize, 0); + getgrouplist(name, gid, groups.as_mut_ptr(), &mut ngroups); + } + groups.set_len(ngroups as usize); + } + groups.truncate(ngroups as usize); + groups + } +} + +pub struct Group { + inner: group, +} + +impl Group { + /// AKA group.gr_name + pub fn name(&self) -> Cow { + cstr2cow!(self.inner.gr_name) + } + + /// AKA group.gr_gid + pub fn gid(&self) -> gid_t { + self.inner.gr_gid + } + + pub fn as_inner(&self) -> &group { + &self.inner + } + + pub fn into_inner(self) -> group { + self.inner + } +} + +/// Fetch desired entry. +pub trait Locate { + fn locate(key: K) -> IOResult where Self: ::std::marker::Sized; +} + +macro_rules! f { + ($fnam:ident, $fid:ident, $t:ident, $st:ident) => ( + impl Locate<$t> for $st { + fn locate(k: $t) -> IOResult { + unsafe { + let data = $fid(k); + if !data.is_null() { + Ok($st { + inner: ptr::read(data as *const _) + }) + } else { + Err(IOError::new(ErrorKind::NotFound, format!("No such id: {}", k))) + } + } + } + } + + impl<'a> Locate<&'a str> for $st { + fn locate(k: &'a str) -> IOResult { + if let Ok(id) = k.parse::<$t>() { + let data = unsafe { $fid(id) }; + if !data.is_null() { + Ok($st { + inner: unsafe {ptr::read(data as *const _)} + }) + } else { + Err(IOError::new(ErrorKind::NotFound, format!("No such id: {}", id))) + } + } else { + unsafe { + let data = $fnam(CString::new(k).unwrap().as_ptr()); + if !data.is_null() { + Ok($st { + inner: ptr::read(data as *const _) + }) + } else { + Err(IOError::new(ErrorKind::NotFound, format!("Not found: {}", k))) + } + } + } + } + } + ) +} + +f!(getpwnam, getpwuid, uid_t, Passwd); +f!(getgrnam, getgrgid, gid_t, Group); + +#[inline] +pub fn uid2usr(id: uid_t) -> IOResult { + Passwd::locate(id).map(|p| p.name().into_owned()) +} + +#[inline] +pub fn gid2grp(id: gid_t) -> IOResult { + Group::locate(id).map(|p| p.name().into_owned()) +} + +#[inline] +pub fn usr2uid(name: &str) -> IOResult { + Passwd::locate(name).map(|p| p.uid()) +} + +#[inline] +pub fn grp2gid(name: &str) -> IOResult { + Group::locate(name).map(|p| p.gid()) +} diff --git a/src/uucore/lib.rs b/src/uucore/lib.rs index 86e24a07a..ff45693aa 100644 --- a/src/uucore/lib.rs +++ b/src/uucore/lib.rs @@ -20,6 +20,8 @@ pub mod parse_time; pub mod utmpx; #[cfg(all(unix, feature = "c_types"))] pub mod c_types; +#[cfg(all(unix, feature = "entries"))] +pub mod entries; #[cfg(all(unix, feature = "process"))] pub mod process; #[cfg(all(unix, feature = "signals"))]