diff --git a/fdisk/src/lib.rs b/fdisk/src/lib.rs index 7de5598..37483f4 100644 --- a/fdisk/src/lib.rs +++ b/fdisk/src/lib.rs @@ -1,6 +1,5 @@ //! The `fdisk` library allows to use the functions of the fdisk command. -#![feature(exclusive_range_pattern)] #![feature(iter_array_chunks)] pub mod crc32; diff --git a/fdisk/src/main.rs b/fdisk/src/main.rs index 9254f0c..6fe59eb 100644 --- a/fdisk/src/main.rs +++ b/fdisk/src/main.rs @@ -3,7 +3,6 @@ //! The `sfdisk` is also implemented in the same program, it has the purpose as `fdisk`, except it //! uses scripting instead of prompting. -#![feature(exclusive_range_pattern)] #![feature(iter_array_chunks)] mod crc32; @@ -196,7 +195,7 @@ fn handle_cmd(cmd: &str, disk_path: &Path, disk: &mut Disk) { "m" => print_cmd_help(), "I" => { - if let Some(script_path) = prompt(Some("Enter script file name: "), false) { + if let Some(script_path) = prompt("Enter script file name: ", false) { let script_path = PathBuf::from(script_path); match import_script(disk, &script_path) { @@ -207,7 +206,7 @@ fn handle_cmd(cmd: &str, disk_path: &Path, disk: &mut Disk) { } "O" => { - if let Some(script_path) = prompt(Some("Enter script file name: "), false) { + if let Some(script_path) = prompt("Enter script file name: ", false) { let script_path = PathBuf::from(script_path); match export_script(disk, &script_path) { @@ -220,7 +219,7 @@ fn handle_cmd(cmd: &str, disk_path: &Path, disk: &mut Disk) { "w" => { // TODO ask only if modifications have been made let prompt_str = format!("Write changes to `{}`? (y/n) ", disk_path.display()); - let confirm = prompt(Some(&prompt_str), false) + let confirm = prompt(&prompt_str, false) .map(|s| s == "y") .unwrap_or(false); if !confirm { @@ -327,7 +326,7 @@ fn main() { .unwrap() // TODO handle error .unwrap(); // TODO handle error - while let Some(cmd) = prompt(Some("Command (m for help): "), false) { + while let Some(cmd) = prompt("Command (m for help): ", false) { handle_cmd(&cmd, disk_path, &mut disk); } } else { diff --git a/fdisk/src/partition.rs b/fdisk/src/partition.rs index d78e228..4d8567f 100644 --- a/fdisk/src/partition.rs +++ b/fdisk/src/partition.rs @@ -818,7 +818,7 @@ impl PartitionTableType { println!(" p primary (TODO primary, TODO extended, TODO free)"); // TODO println!(" e extended (container for logical partitions)"); // TODO - let extended = prompt(Some("Select (default p): "), false) + let extended = prompt("Select (default p): ", false) .map(|s| s == "e") // TODO handle invalid prompt (other than `p` and `e`) .unwrap_or(false); (extended, 4) @@ -831,7 +831,7 @@ impl PartitionTableType { let first = 1; // TODO get from disk let prompt_str = format!("Partition number ({first}-{max_partition_count}, default {first}): "); - let partition_number = prompt(Some(&prompt_str), false) + let partition_number = prompt(&prompt_str, false) .filter(|s| !s.is_empty()) .map(|s| s.parse::()) .transpose() @@ -844,7 +844,7 @@ impl PartitionTableType { let prompt_str = format!( "First sector ({first_available}-{last_available}, default {first_available}): ", ); - let start = prompt(Some(&prompt_str), false) + let start = prompt(&prompt_str, false) .filter(|s| !s.is_empty()) .map(|s| s.parse::()) .transpose() @@ -855,7 +855,7 @@ impl PartitionTableType { let prompt_str = format!( "Last sector, +/-sectors or +/-size{{K,M,G,T,P}} ({start}-{last_available}, default {last_available}): ", ); - let end = prompt(Some(&prompt_str), false) + let end = prompt(&prompt_str, false) .map(|s| { // TODO parse suffix s.parse::() diff --git a/login/src/main.rs b/login/src/main.rs index 30dd8f0..2d03776 100644 --- a/login/src/main.rs +++ b/login/src/main.rs @@ -3,17 +3,18 @@ #![feature(never_type)] #![feature(os_str_display)] -use std::ffi::CString; +use std::ffi::{CString, OsString}; +use std::fmt::Formatter; use std::os::unix::ffi::OsStrExt; -use std::path::Path; use std::process::exit; use std::ptr::null; use std::time::Duration; -use std::{env, io, iter}; +use std::{env, fmt, fs, io, iter}; use utils::prompt::prompt; use utils::user; -use utils::user::User; +use utils::user::{Shadow, User, PASSWD_PATH, SHADOW_PATH}; use utils::util; +use utils::util::get_hostname; /// Builds an environment variable in the form: name=value fn build_env_var(name: &str, value: impl IntoIterator) -> CString { @@ -23,17 +24,17 @@ fn build_env_var(name: &str, value: impl IntoIterator) -> CString { .cloned() .chain(iter::once(b'=')) .chain(value) + .chain(iter::once(b'\0')) .collect(); - // TODO handle when the value contains a nul-byte? - CString::new(data).unwrap() + CString::from_vec_with_nul(data).unwrap() } /// Switches to the given user after login is successful. /// /// Arguments: -/// - `logname` is the name of the user used to login. +/// - `logname` is the name of the user used to log in. /// - `user` is the user to switch to. -fn switch_user(logname: &str, user: &User) -> io::Result { +fn switch_user(logname: &str, user: User) -> io::Result { let User { login_name, uid, @@ -42,24 +43,23 @@ fn switch_user(logname: &str, user: &User) -> io::Result { interpreter, .. } = user; - // Prepare environment let term = env::var_os("TERM").unwrap_or_else(|| { // TODO fetch from the terminal "linux".into() }); - let shell = if !interpreter.is_empty() { - interpreter - } else { - "/bin/sh" + let shell = match interpreter { + "" => "/bin/sh", + i => i, }; let path = match uid { 0 => "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin", _ => "/usr/local/bin:/bin:/usr/bin", }; let mail = "/var/spool/mail/".bytes().chain(login_name.bytes()); - - // Build variables + // Prepare `execve` arguments + let bin = CString::new(shell).unwrap(); // TODO handle error? + let argv = [bin.as_ptr(), null()]; let env_home = build_env_var("HOME", home.as_os_str().as_bytes().iter().cloned()); let env_user = build_env_var("USER", login_name.bytes()); let env_logname = build_env_var("LOGNAME", logname.bytes()); @@ -77,15 +77,10 @@ fn switch_user(logname: &str, user: &User) -> io::Result { env_mail.as_ptr(), null(), ]; - - let bin = CString::new(shell).unwrap(); // TODO handle error? - let argv = [bin.as_ptr(), null()]; - // Set current user - user::set(*uid, *gid)?; + user::set(uid, gid)?; // Set current working directory env::set_current_dir(home)?; - // Execute interpreter let res = unsafe { libc::execve(bin.as_ptr(), argv.as_ptr(), envp.as_ptr()) }; if res >= 0 { @@ -96,55 +91,60 @@ fn switch_user(logname: &str, user: &User) -> io::Result { } } +/// The login prompt. +struct LoginPrompt(OsString); + +impl fmt::Display for LoginPrompt { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{} login: ", self.0.display()) + } +} + fn main() { let _args = env::args(); // TODO Parse and use - + let login_prompt = LoginPrompt(get_hostname()); loop { println!(); - - // Get user prompt - let user_prompt = format!("{} login: ", util::get_hostname().display()); - - // Prompt login and password - let login = prompt(Some(&user_prompt), false).unwrap_or_else(|| exit(1)); - let pass = prompt(None, true).unwrap_or_else(|| exit(1)); - - // Read users lists - let passwd = user::read_passwd(Path::new(user::PASSWD_PATH)).unwrap_or_else(|e| { - eprintln!("Cannot read passwd file: {e}"); - exit(1); - }); - let shadow = user::read_shadow(&Path::new(user::SHADOW_PATH)).ok(); - - // Get user from prompted login - let user_entry = passwd.into_iter().find(|e| e.login_name == login); - - let interval = Duration::from_millis(1000); - util::exec_wait(interval, || { - if let Some(user_entry) = user_entry { - // Checking password against user entry - let correct = user_entry.check_password(&pass).unwrap_or_else(|| { - if let Some(shadow) = shadow { - shadow - .into_iter() - .filter(|e| e.login_name == login) - .map(|e| e.check_password(&pass)) - .next() - .unwrap_or(false) - } else { - false - } - }); - - if correct { - switch_user(&login, &user_entry).unwrap_or_else(|e| { - eprintln!("login: {e}"); + // Prompt for login and password + let login = prompt(&login_prompt, false).unwrap_or_else(|| exit(1)); + let pass = prompt("Password: ", true).unwrap_or_else(|| exit(1)); + // Check + util::exec_wait(Duration::from_millis(1000), || { + // Get user from prompted login + let users_buff = fs::read_to_string(PASSWD_PATH).unwrap_or_else(|e| { + eprintln!("login: cannot read passwd file: {e}"); + exit(1); + }); + let user_entry = User::deserialize(&users_buff) + .filter_map(Result::ok) + .find(|e| e.login_name == login); + let Some(user_entry) = user_entry else { + return; + }; + // Check password against user entry + let correct = match user_entry.check_password(&pass) { + Some(c) => c, + // The passwd file does not have the password. Fallback onto the shadow file + None => { + let shadow_buff = fs::read_to_string(SHADOW_PATH).unwrap_or_else(|e| { + eprintln!("login: cannot read passwd file: {e}"); exit(1); }); + let c = Shadow::deserialize(&shadow_buff) + .filter_map(Result::ok) + .any(|e| e.login_name == login && e.check_password(&pass)); + c } + }; + if !correct { + return; } + // Correct, setup session + switch_user(&login, user_entry).unwrap_or_else(|e| { + eprintln!("login: cannot initialize session: {e}"); + exit(1); + }); }); - eprintln!("Login incorrect"); } } diff --git a/mkfs/src/main.rs b/mkfs/src/main.rs index a1fa781..366bb36 100644 --- a/mkfs/src/main.rs +++ b/mkfs/src/main.rs @@ -108,7 +108,7 @@ fn main() { ); // TODO print details on fs (use factory) - let confirm = prompt(Some("Proceed anyway? (y/N) "), false) + let confirm = prompt("Proceed anyway? (y/N) ", false) .map(|s| s.to_lowercase() == "y") .unwrap_or(false); if !confirm { diff --git a/ps/src/main.rs b/ps/src/main.rs index c981f3c..f707ad2 100644 --- a/ps/src/main.rs +++ b/ps/src/main.rs @@ -8,38 +8,33 @@ use format::DisplayFormat; use format::FormatParser; use process::Process; use process::ProcessIterator; -use std::env; -use std::path::PathBuf; use std::process::exit; +use std::{env, fs, io}; +use utils::user::{get_egid, get_euid, Group, User, PASSWD_PATH}; // TODO Implement every arguments // TODO Implement environment variables // TODO i18n -extern "C" { - fn geteuid() -> u32; - fn getegid() -> u32; -} - /// Enumeration of selectors used to accept or reject a process in the list. enum Selector { - /// Selects processes attached to a terminal (-a). + /// Selects processes attached to a terminal (`-a`). Terminal, - /// Selects all processes (-A, -e). + /// Selects all processes (`-A`, `-e`). All, - /// Selects all processes except session leaders (-d). + /// Selects all processes except session leaders (`-d`). NoLeaders, - /// Selects all processes whose session leader effective group ID corresponds (-g). + /// Selects all processes whose session leader effective group ID corresponds (`-g`). Gid(u32), - /// Selects all processes whose real group ID corresponds (-G). + /// Selects all processes whose real group ID corresponds (`-G`). Rgid(u32), - /// Selects processes whose PID corresponds (-p). + /// Selects processes whose PID corresponds (`-p`). Pid(u32), - /// Selects processes attached to the given TTY (-t). + /// Selects processes attached to the given TTY (`-t`). Term(String), - /// Selects processes whose effective user ID corresponds (-u). + /// Selects processes whose effective user ID corresponds (`-u`). Uid(u32), - /// Selects processes whose real user ID corresponds (-U). + /// Selects processes whose real user ID corresponds (`-U`). Ruid(u32), } @@ -49,16 +44,13 @@ impl Selector { match self { Self::Terminal => proc.tty.is_some(), Self::All => true, - Self::NoLeaders => { // TODO true } - Self::Gid(gid) => proc.gid == *gid, Self::Rgid(rgid) => proc.rgid == *rgid, Self::Pid(pid) => proc.pid == *pid, - Self::Term(tty) => { if let Some(t) = &proc.tty { t == tty || t == &format!("tty{}", tty) @@ -66,7 +58,6 @@ impl Selector { false } } - Self::Uid(uid) => proc.uid == *uid, Self::Ruid(ruid) => proc.ruid == *ruid, } @@ -86,23 +77,28 @@ fn print_usage() { /// Prints an error, then exits. fn error(msg: &str) -> ! { - eprintln!("error: {}", msg); - + eprintln!("error: {msg}"); print_usage(); exit(1); } +// TODO When a PID, UID, GID... is specified, use files' metadata to avoid reading /// Parses arguments and returns the selectors list and format. -fn parse_args() -> (Vec, DisplayFormat) { +fn parse_args() -> io::Result<(Vec, DisplayFormat)> { // Results let mut selectors = Vec::new(); let mut format = DisplayFormat::new(); let mut default_format = true; - // Reading users and groups lists - let users = - utils::user::read_passwd(&PathBuf::from(utils::user::PASSWD_PATH)).unwrap_or(vec![]); - let groups = utils::user::read_group(&PathBuf::from(utils::user::GROUP_PATH)).unwrap_or(vec![]); + // Read users and groups lists + let users_buff = fs::read_to_string(PASSWD_PATH)?; + let users: Vec<_> = User::deserialize(&users_buff) + .filter_map(Result::ok) + .collect(); + let groups_buff = fs::read_to_string(PASSWD_PATH)?; + let groups: Vec<_> = Group::deserialize(&groups_buff) + .filter_map(Result::ok) + .collect(); // TODO -l and -f let mut args = env::args().skip(1); @@ -169,8 +165,7 @@ fn parse_args() -> (Vec, DisplayFormat) { }, }); } else { - let uid = unsafe { geteuid() }; - selectors.push(Selector::Uid(uid)); + selectors.push(Selector::Uid(get_euid())); } } @@ -206,8 +201,7 @@ fn parse_args() -> (Vec, DisplayFormat) { }, ); } else { - let gid = unsafe { getegid() }; - selectors.push(Selector::Gid(gid)); + selectors.push(Selector::Gid(get_egid())); } } @@ -236,10 +230,8 @@ fn parse_args() -> (Vec, DisplayFormat) { // If no selector is specified, use defaults if selectors.is_empty() { - let curr_euid = unsafe { geteuid() }; - // TODO Select only processes that share the same controlling terminal - selectors.push(Selector::Uid(curr_euid)); + selectors.push(Selector::Uid(get_euid())); } // If no format is specified, use default @@ -247,42 +239,34 @@ fn parse_args() -> (Vec, DisplayFormat) { format = DisplayFormat::default(); } - (selectors, format) + Ok((selectors, format)) } fn main() { - let (selectors, format) = parse_args(); - - // Printing header - if format.can_print() { - println!("{}", format); - } - - // Creating the process iterator + let (selectors, format) = match parse_args() { + Ok(args) => args, + Err(e) => { + eprintln!("error: cannot parse arguments: {e}"); + exit(1); + } + }; + // Create the process iterator let proc_iter = match ProcessIterator::new() { Ok(i) => i, Err(e) => { - eprintln!("error: cannot read processes list: {}", e); + eprintln!("error: cannot read processes list: {e}"); exit(1); } }; - - // TODO When a PID, UID, GID... is specified, use files' metadata to avoid reading - - // Filtering processes according to arguments - // A process is accepted if it matches at least one selector (union) - let proc_iter = proc_iter.filter(|proc| { - for s in &selectors { - if s.is_accepted(proc) { - return true; - } - } - - false - }); - - // Printing processes - for proc in proc_iter { - println!("{}", proc.display(&format)); + // Print header + if format.can_print() { + println!("{format}"); } + // Print processes + proc_iter + .filter(|proc| { + // A process is accepted if it matches at least one selector (union) + selectors.iter().any(|s| s.is_accepted(proc)) + }) + .for_each(|proc| println!("{}", proc.display(&format))); } diff --git a/su/src/main.rs b/su/src/main.rs index 4ea847d..216268d 100644 --- a/su/src/main.rs +++ b/su/src/main.rs @@ -1,4 +1,4 @@ -//! `su` is a command allowing to run an other command with a substitute user and group ID. +//! `su` is a command allowing to run another command with a substitute user and group ID. use std::env; use std::process::exit; @@ -6,12 +6,12 @@ use std::process::Command; use utils::prompt::prompt; -/// Structure representing the command's arguments. +/// The command's arguments. #[derive(Default)] struct Args<'s> { /// The user which executes the command. If None, using root. user: Option<&'s str>, - /// The group which executtes the command. If None, using root. + /// The group which executes the command. If None, using root. group: Option<&'s str>, /// The shell to execute. If None, using the default. @@ -66,7 +66,7 @@ fn main() { // TODO Read user's entry let shell = args.shell.unwrap_or("TODO"); - let _pass = prompt(None, true); + let _pass = prompt("Password: ", true); let correct = false; // TODO Check password against user's if correct { diff --git a/utils/src/prompt.rs b/utils/src/prompt.rs index 424f363..edf023d 100644 --- a/utils/src/prompt.rs +++ b/utils/src/prompt.rs @@ -9,10 +9,10 @@ use libc::ICANON; use libc::STDIN_FILENO; use libc::TCSANOW; use libc::VMIN; -use std::io; use std::io::BufRead; use std::io::Write; use std::mem::MaybeUninit; +use std::{fmt, io}; // TODO Add line edition /// Show a prompt. This function returns when a newline is received. @@ -20,8 +20,7 @@ use std::mem::MaybeUninit; /// Arguments: /// - `prompt` is the prompt's text. If `None`, the function uses the default text. /// - `hidden` tells whether the input is hidden. -pub fn prompt(prompt: Option<&str>, hidden: bool) -> Option { - let prompt = prompt.unwrap_or("Password: "); +pub fn prompt(prompt: P, hidden: bool) -> Option { // Save termios state let saved_termios = unsafe { let mut t: termios = MaybeUninit::zeroed().assume_init(); diff --git a/utils/src/user.rs b/utils/src/user.rs index 52567a4..67c2f56 100644 --- a/utils/src/user.rs +++ b/utils/src/user.rs @@ -3,16 +3,12 @@ use argon2::password_hash::SaltString; use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier}; +use libc::{gid_t, uid_t}; use rand_core::OsRng; -use std::error::Error; use std::fmt::Formatter; -use std::fs::File; use std::fs::OpenOptions; -use std::io::BufRead; -use std::io::BufReader; use std::io::Write; use std::path::Path; -use std::path::PathBuf; use std::{fmt, io}; /// The path to the passwd file. @@ -42,6 +38,10 @@ pub fn check_password(hash: &str, pass: &str) -> bool { .is_ok() } +/// An error occurring when meeting an invalid entry. +#[derive(Debug)] +pub struct InvalidEntry; + /// Wrapper for [`Option`] allowing to display a value if [`Some`], or nothing if [`None`]. struct OptionDisplay(Option); @@ -55,24 +55,46 @@ impl fmt::Display for OptionDisplay { } /// A system user, present in the `passwd` file. -pub struct User { +pub struct User<'s> { /// The user's login name. - pub login_name: String, + pub login_name: &'s str, /// The user's encrypted password. If `x`, the password is located in the shadow file. - pub password: String, + pub password: &'s str, /// The user ID. pub uid: u32, /// The user's group ID. pub gid: u32, /// User comment. - pub comment: String, + pub comment: &'s str, /// User's home path. - pub home: PathBuf, + pub home: &'s Path, /// User's command interpreter. - pub interpreter: String, + pub interpreter: &'s str, } -impl User { +impl User<'_> { + /// Deserializes entries from the given buffer `buf`. + pub fn deserialize(buf: &str) -> impl Iterator> { + buf.split('\n') + .map(|line| { + let mut vals = line.split(':'); + let ent = User { + login_name: vals.next()?, + password: vals.next()?, + uid: vals.next()?.parse().ok()?, + gid: vals.next()?.parse().ok()?, + comment: vals.next()?, + home: Path::new(vals.next()?), + interpreter: vals.next()?, + }; + if vals.next().is_some() { + return None; + } + Some(ent) + }) + .map(|ent| ent.ok_or(InvalidEntry)) + } + /// Check the given (not hashed) password `pass` against the current entry. /// /// If the function returns None, the callee must use the shadow entry. @@ -84,7 +106,7 @@ impl User { } } -impl fmt::Display for User { +impl fmt::Display for User<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln!( f, @@ -101,11 +123,11 @@ impl fmt::Display for User { } /// A shadow entry, present in the `shadow` file. -pub struct Shadow { +pub struct Shadow<'s> { /// The user's login name. - pub login_name: String, + pub login_name: &'s str, /// The user's encrypted password. - pub password: String, + pub password: &'s str, /// The date of the last password change in number of days since the Unix Epoch. pub last_change: u32, /// The minimum number of days to wait before the user becomes usable. @@ -123,17 +145,41 @@ pub struct Shadow { /// denied. pub account_expiration: Option, /// Reserved field. - pub reserved: String, + pub reserved: &'s str, } -impl Shadow { +impl Shadow<'_> { + /// Deserializes entries from the given buffer `buf`. + pub fn deserialize(buf: &str) -> impl Iterator> { + buf.split('\n') + .map(|line| { + let mut vals = line.split(':'); + let ent = Shadow { + login_name: vals.next()?, + password: vals.next()?, + last_change: vals.next()?.parse().unwrap_or(0), + minimum_age: vals.next()?.parse().ok(), + maximum_age: vals.next()?.parse().ok(), + warning_period: vals.next()?.parse().ok(), + inactivity_period: vals.next()?.parse().ok(), + account_expiration: vals.next()?.parse().ok(), + reserved: vals.next()?, + }; + if vals.next().is_some() { + return None; + } + Some(ent) + }) + .map(|ent| ent.ok_or(InvalidEntry)) + } + /// Check the given (not hashed) password `pass` against `self`. pub fn check_password(&self, pass: &str) -> bool { check_password(&self.password, pass) } } -impl fmt::Display for Shadow { +impl fmt::Display for Shadow<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln!( f, @@ -152,18 +198,39 @@ impl fmt::Display for Shadow { } /// A system group, present in `group`. -pub struct Group { +pub struct Group<'s> { /// The group's name. - pub group_name: String, + pub group_name: &'s str, /// The encrypted group's password. - pub password: String, + pub password: &'s str, /// The group's ID. pub gid: u32, - /// The list of users member of this group, comma-separated. - pub users_list: String, + /// The list of user members of this group, comma-separated. + pub users_list: &'s str, +} + +impl Group<'_> { + /// Deserializes entries from the given buffer `buf`. + pub fn deserialize(buf: &str) -> impl Iterator> { + buf.split('\n') + .map(|line| { + let mut vals = line.split(':'); + let ent = Group { + group_name: vals.next()?, + password: vals.next()?, + gid: vals.next()?.parse().ok()?, + users_list: vals.next()?, + }; + if vals.next().is_some() { + return None; + } + Some(ent) + }) + .map(|ent| ent.ok_or(InvalidEntry)) + } } -impl fmt::Display for Group { +impl fmt::Display for Group<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln!( f, @@ -173,14 +240,6 @@ impl fmt::Display for Group { } } -/// Reads and parses the file at path `path`. -fn read(path: &Path) -> io::Result>>> { - let file = File::open(path)?; - Ok(BufReader::new(file) - .lines() - .map(|l| Ok(l?.split(':').map(str::to_owned).collect::>()))) -} - /// Writes the file at path `path` with data `data`. pub fn write, E: fmt::Display>(path: &Path, data: I) -> io::Result<()> { let mut file = OpenOptions::new() @@ -194,81 +253,14 @@ pub fn write, E: fmt::Display>(path: &Path, data: I) - Ok(()) } -/// Reads the passwd file. -/// -/// `path` is the path to the file. -pub fn read_passwd(path: &Path) -> Result, Box> { - read(path)? - .into_iter() - .enumerate() - .map(|(i, data)| { - let data = data?; - if data.len() != 7 { - return Err(format!("Invalid entry on line `{}`", i + 1).into()); - } - - Ok(User { - login_name: data[0].clone(), - password: data[1].clone(), - uid: data[2].parse::<_>()?, - gid: data[3].parse::<_>()?, - comment: data[4].clone(), - home: data[5].clone().into(), - interpreter: data[6].clone(), - }) - }) - .collect() -} - -/// Reads the shadow file. -/// -/// `path` is the path to the file. -pub fn read_shadow(path: &Path) -> Result, Box> { - read(path)? - .into_iter() - .enumerate() - .map(|(i, data)| { - let data = data?; - if data.len() != 9 { - return Err(format!("Invalid entry on line `{}`", i + 1).into()); - } - - Ok(Shadow { - login_name: data[0].clone(), - password: data[1].clone(), - last_change: data[2].parse::<_>().unwrap_or(0), - minimum_age: data[3].parse::<_>().ok(), - maximum_age: data[4].parse::<_>().ok(), - warning_period: data[5].parse::<_>().ok(), - inactivity_period: data[6].parse::<_>().ok(), - account_expiration: data[7].parse::<_>().ok(), - reserved: data[8].clone(), - }) - }) - .collect() +/// Returns the current effective UID. +pub fn get_euid() -> uid_t { + unsafe { libc::geteuid() } } -/// Reads the group file. -/// -/// `path` is the path to the file. -pub fn read_group(path: &Path) -> Result, Box> { - read(path)? - .into_iter() - .enumerate() - .map(|(i, data)| { - let data = data?; - if data.len() != 4 { - return Err(format!("Invalid entry on line `{}`", i + 1).into()); - } - - Ok(Group { - group_name: data[0].clone(), - password: data[1].clone(), - gid: data[2].parse::<_>()?, - users_list: data[3].clone(), - }) - }) - .collect() +/// Returns the current effective GID. +pub fn get_egid() -> gid_t { + unsafe { libc::getegid() } } /// Sets the current user.