Skip to content

Commit

Permalink
refactor: user files deserialization
Browse files Browse the repository at this point in the history
  • Loading branch information
llenotre committed Jul 1, 2024
1 parent 0daf25e commit b10100c
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 247 deletions.
1 change: 0 additions & 1 deletion fdisk/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
9 changes: 4 additions & 5 deletions fdisk/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
8 changes: 4 additions & 4 deletions fdisk/src/partition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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::<usize>())
.transpose()
Expand All @@ -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::<u64>())
.transpose()
Expand All @@ -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::<u64>()
Expand Down
122 changes: 61 additions & 61 deletions login/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Item = u8>) -> CString {
Expand All @@ -23,17 +24,17 @@ fn build_env_var(name: &str, value: impl IntoIterator<Item = u8>) -> 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,
Expand All @@ -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());
Expand All @@ -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 {
Expand All @@ -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");
}
}
2 changes: 1 addition & 1 deletion mkfs/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit b10100c

Please sign in to comment.