Improved id and whoami.

Details

- New implementation of `id` inspired by BSD's one.
- Refactoring on `whoami`.
- Added few utility functions in `userutils` for getting processes user and group
real/effective ID and names.

Added docs do `getty`, `login`, `passwd`, `su`, and `sudo`.

Details

- The docs are from BSD's ones.
- Also added `ArgParser` to all of them and added a -h/--help flag
to all of them.
- This is the start of a revision of all of those commands.
This commit is contained in:
Jose Narvaez
2017-07-17 18:20:58 +01:00
parent e70be16a48
commit ef5fdc82a0
8 changed files with 617 additions and 29 deletions
+37
View File
@@ -1,11 +1,35 @@
#![deny(warnings)]
extern crate syscall;
extern crate arg_parser;
use std::process::Command;
use std::{env, process, str};
use std::io::{self, Write};
use arg_parser::ArgParser;
const MAN_PAGE: &'static str = /* @MANSTART{login} */ r#"
NAME
getty - set terminal mode
SYNOPSIS
getty
DESCRIPTION
The getty utility is called by init(8) to open and initialize the tty line,
read a login name, and invoke login(1).
OPTIONS
-h
--help
Display this help and exit.
AUTHOR
Written by Jeremy Soller.
"#;
fn set_tty(tty: &str) -> syscall::Result<()> {
let stdin = syscall::open(tty, syscall::flag::O_RDONLY)?;
let stdout = syscall::open(tty, syscall::flag::O_WRONLY)?;
@@ -39,6 +63,19 @@ fn daemon(clear: bool) {
}
pub fn main() {
let mut stdout = io::stdout();
let mut parser = ArgParser::new(1)
.add_flag(&["h", "help"]);
parser.parse(env::args());
// Shows the help
if parser.found("help") {
let _ = stdout.write_all(MAN_PAGE.as_bytes());
let _ = stdout.flush();
process::exit(0);
}
let mut tty_option = None;
let mut clear = true;
for arg in env::args().skip(1) {
+215 -3
View File
@@ -1,9 +1,221 @@
extern crate arg_parser;
extern crate extra;
extern crate syscall;
extern crate userutils;
use std::borrow::Borrow;
use std::hash::Hash;
use std::env;
use std::io::{self, Write, Stderr, StdoutLock};
use std::process::exit;
use extra::io::fail;
use extra::option::OptionalExt;
use arg_parser::{ArgParser, Param};
use userutils::{get_egid, get_gid, get_euid, get_uid, get_user, get_group};
const HELP_INFO: &'static str = "Try id --help for more information.\n";
const MAN_PAGE: &'static str = /* @MANSTART{id} */ r#"
NAME
id - display user identity
SYNOPSIS
id
id -g [-nr]
id -u [-nr]
id [ -h | --help ]
DESCRIPTION
The id utility displays the user and group names and numeric IDs,
of the calling process, to the standard output.
OPTIONS
-G
Display the different group IDs (effective and real)
as white-space separated numbers, in no particular order.
-g
Display the effective group ID as a number.
-n Display the name of the user or group ID for the -g and -u
options instead of the number.
-u
Display the effective user ID as a number.
-a
Ignored for compatibility with other id implementations.
-h
--help
Display this help and exit.
EXIT STATUS
The whoami utility exits 0 on success, and >0 if an error occurs.
AUTHOR
Written by Jose Narvaez.
"#; /* @MANEND */
pub fn main() {
let uid = syscall::getuid().expect("id: failed to get UID");
let gid = syscall::getgid().expect("id: failed to get GID");
println!("uid={}({}) gid={}({})", uid, env::var("USER").unwrap_or(String::new()), gid, "");
let stdout = io::stdout();
let mut stdout = stdout.lock();
let mut stderr = io::stderr();
let mut parser = ArgParser::new(1)
.add_flag(&["h", "help"])
.add_flag(&["a"])
.add_flag(&["G"])
.add_flag(&["g"])
.add_flag(&["u"])
.add_flag(&["n"])
.add_flag(&["r"]);
parser.parse(env::args());
// Shows the help
if parser.found("help") {
print_msg(MAN_PAGE, &mut stdout, &mut stderr);
exit(0);
}
// Unrecognized flags
if let Err(err) = parser.found_invalid() {
stderr.write_all(err.as_bytes()).try(&mut stderr);
print_msg(HELP_INFO, &mut stdout, &mut stderr);
exit(1);
}
// Display the different group IDs (effective and real)
// as white-space separated numbers, in no particular order.
if parser.found(&'G') {
if any_of_found(&parser, &[&'g', &'u']) {
let msg = "id: -G option must be used without others\n";
print_msg(msg, &mut stdout, &mut stderr);
print_msg(HELP_INFO, &mut stdout, &mut stderr);
exit(1);
}
let egid = get_egid(&mut stderr);
let gid = get_gid(&mut stderr);
print_msg(&format!("{} {}\n", egid, gid), &mut stdout, &mut stderr);
exit(0);
}
// Check if people passed both -g -u which are mutually exclusive
if parser.found(&'u') && parser.found(&'g') {
let msg = "id: specify either -u or -g but not both\n";
print_msg(msg, &mut stdout, &mut stderr);
print_msg(HELP_INFO, &mut stdout, &mut stderr);
exit(1);
}
// Display effective/real process user ID UNIX user name
if parser.found(&'u') && parser.found(&'n') {
// Did they pass -r? F so, we show the real
let uid = if parser.found(&'r') {
get_uid(&mut stderr)
} else {
get_euid(&mut stderr)
};
get_user(uid, &mut stderr).map(|user| {
print_msg(&format!("{}\n", user), &mut stdout, &mut stderr);
exit(0);
}).or_else(|| {
fail(&format!("id: no user found for uid: {}", uid), &mut stderr)
});
}
// Display real user ID
if parser.found(&'u') && parser.found(&'r') {
let uid = get_uid(&mut stderr);
print_msg(&format!("{}\n", uid), &mut stdout, &mut stderr);
exit(0);
}
// Display effective user ID
if parser.found(&'u') {
let euid = get_euid(&mut stderr);
print_msg(&format!("{}\n", euid), &mut stdout, &mut stderr);
exit(0);
}
// Display effective/real process group ID UNIX group name
if parser.found(&'g') && parser.found(&'n') {
// Did they pass -r? If so we show the real one
let gid = if parser.found(&'r') {
get_gid(&mut stderr)
} else {
get_egid(&mut stderr)
};
get_group(gid, &mut stderr).map(|group| {
print_msg(&format!("{}\n", group), &mut stdout, &mut stderr);
exit(0);
}).or_else(|| {
fail(&format!("id: no group found for gid: {}", gid), &mut stderr)
});
}
// Display the real group ID
if parser.found(&'g') && parser.found(&'r') {
let gid = get_gid(&mut stderr);
print_msg(&format!("{}\n", gid), &mut stdout, &mut stderr);
exit(0);
}
// Display effective group ID
if parser.found(&'g') {
let egid = get_egid(&mut stderr);
print_msg(&format!("{}\n", egid), &mut stdout, &mut stderr);
exit(0);
}
// -n does not apply if there is no -u or -g
if parser.found(&'n') && none_of_found(&parser, &[&'u', &'g']) {
let msg = "id: the -n option must be used with either -u or -g\n";
fail(msg, &mut stderr);
}
// -r does not apply if there is no -u or -g
if parser.found(&'r') && none_of_found(&parser, &[&'u', &'g']) {
let msg = "id: the -r option must be used with either -u or -g\n";
fail(msg, &mut stderr);
}
// We get everything we can and show that
let euid = get_euid(&mut stderr);
let egid = get_egid(&mut stderr);
let user = get_user(euid, &mut stderr).unwrap_or_else(|| {
fail(&format!("id: no user found for uid: {}", euid), &mut stderr);
});
let group = get_group(egid, &mut stderr).unwrap_or_else(|| {
fail(&format!("id: no group found for gid: {}", euid), &mut stderr);
});
let msg = format!("uid={}({}) gid={}({})\n", euid, user, egid, group);
print_msg(&msg, &mut stdout, &mut stderr);
exit(0);
}
pub fn any_of_found<P: Hash + Eq + ?Sized>(parser: &ArgParser, flags: &[&P]) -> bool
where Param: Borrow<P>
{
for flag in flags {
if parser.found(*flag) { return true }
}
false
}
fn none_of_found<P: Hash + Eq + ?Sized>(parser: &ArgParser, flags: &[&P]) -> bool
where Param: Borrow<P>
{
!any_of_found(parser, flags)
}
fn print_msg(msg: &str, stdout: &mut StdoutLock, stderr: &mut Stderr) {
stdout.write_all(msg.as_bytes()).try(stderr);
stdout.flush().try(stderr);
}
+35 -1
View File
@@ -3,19 +3,53 @@
extern crate liner;
extern crate termion;
extern crate userutils;
extern crate arg_parser;
use std::fs::File;
use std::io::{self, Read, Write};
use std::os::unix::process::CommandExt;
use std::process::Command;
use std::process::{self, Command};
use std::env;
use std::str;
use arg_parser::ArgParser;
use termion::input::TermRead;
use userutils::Passwd;
const MAN_PAGE: &'static str = /* @MANSTART{login} */ r#"
NAME
login - log into the computer
SYNOPSIS
login
DESCRIPTION
The login utility logs users (and pseudo-users) into the computer system.
OPTIONS
-h
--help
Display this help and exit.
AUTHOR
Written by Jeremy Soller.
"#;
pub fn main() {
let mut stdout = io::stdout();
let mut parser = ArgParser::new(1)
.add_flag(&["h", "help"]);
parser.parse(env::args());
// Shows the help
if parser.found("help") {
let _ = stdout.write_all(MAN_PAGE.as_bytes());
let _ = stdout.flush();
process::exit(0);
}
if let Ok(mut issue) = File::open("/etc/issue") {
io::copy(&mut issue, &mut stdout).unwrap();
let _ = stdout.flush();
+39 -1
View File
@@ -1,3 +1,4 @@
extern crate arg_parser;
extern crate rand;
extern crate syscall;
extern crate termion;
@@ -7,15 +8,52 @@ use rand::{Rng, OsRng};
use std::{env, io};
use std::fs::File;
use std::io::{Read, Write};
use std::process;
use arg_parser::ArgParser;
use termion::input::TermRead;
use userutils::Passwd;
const MAN_PAGE: &'static str = /* @MANSTART{passwd} */ r#"
NAME
passwd - modify a user's password
SYNOPSIS
passwd
passwd [ -h | --help ]
DESCRIPTION
The passwd utility changes the user's local password. If the user is not
the super-user, passwd first prompts for the current password and will
not continue unless the correct password is entered.
OPTIONS
-h
--help
Display this help and exit.
AUTHOR
Written by Jeremy Soller.
"#;
fn main() {
let stdin = io::stdin();
let mut stdin = stdin.lock();
let stdout = io::stdout();
let mut stdout = stdout.lock();
let mut parser = ArgParser::new(1)
.add_flag(&["h", "help"]);
parser.parse(env::args());
// Shows the help
if parser.found("help") {
let _ = stdout.write_all(MAN_PAGE.as_bytes());
let _ = stdout.flush();
process::exit(0);
}
let uid = syscall::getuid().unwrap() as u32;
let mut passwd_string = String::new();
@@ -87,7 +125,7 @@ fn main() {
if let Some(confirm_password) = stdin.read_passwd(&mut stdout).unwrap() {
stdout.write(b"\n").unwrap();
let _ = stdout.flush();
if new_password == confirm_password {
let salt = format!("{:X}", OsRng::new().unwrap().next_u64());
writeln!(stdout, "{}", userutils::Passwd::encode(&new_password, &salt)).unwrap();
+37 -1
View File
@@ -1,23 +1,59 @@
extern crate syscall;
extern crate termion;
extern crate userutils;
extern crate arg_parser;
use std::env;
use std::fs::File;
use std::io::{self, Read, Write};
use std::os::unix::process::CommandExt;
use std::process::Command;
use std::process::{self, Command};
use std::str;
use arg_parser::ArgParser;
use termion::input::TermRead;
use userutils::Passwd;
const MAN_PAGE: &'static str = /* @MANSTART{su} */ r#"
NAME
su - substitute user identity
SYNOPSIS
su
su command
su [ -h | --help ]
DESCRIPTION
The su utility requests appropriate user credentials via PAM and switches to
that user ID (the default user is the superuser). A shell is then executed.
OPTIONS
-h
--help
Display this help and exit.
AUTHOR
Written by Jeremy Soller.
"#;
pub fn main() {
let stdin = io::stdin();
let mut stdin = stdin.lock();
let stdout = io::stdout();
let mut stdout = stdout.lock();
let mut parser = ArgParser::new(1)
.add_flag(&["h", "help"]);
parser.parse(env::args());
// Shows the help
if parser.found("help") {
let _ = stdout.write_all(MAN_PAGE.as_bytes());
let _ = stdout.flush();
process::exit(0);
}
let mut user = env::args().nth(1).unwrap_or(String::new());
if user.is_empty() {
user = String::from("root");
+41
View File
@@ -1,3 +1,5 @@
extern crate arg_parser;
extern crate extra;
extern crate syscall;
extern crate termion;
extern crate userutils;
@@ -8,9 +10,37 @@ use std::io::{self, Read, Write};
use std::os::unix::process::CommandExt;
use std::process::{self, Command};
use arg_parser::ArgParser;
use termion::input::TermRead;
use userutils::{Passwd, Group};
const MAN_PAGE: &'static str = /* @MANSTART{sudo} */ r#"
NAME
sudo - execute a command as another user
SYNOPSIS
sudo command
sudo [ -h | --help ]
DESCRIPTION
The sudo utility allows a permitted user to execute a command as the
superuser or another user, as specified by the security policy.
OPTIONS
-h
--help
Display this help and exit.
EXIT STATUS
Upon successful execution of a command, the exit status from sudo will
be the exit status of the program that was executed. In case of error
the exit status will be >0.
AUTHOR
Written by Jeremy Soller.
"#;
pub fn main() {
let stdin = io::stdin();
let mut stdin = stdin.lock();
@@ -19,6 +49,17 @@ pub fn main() {
let stderr = io::stderr();
let mut stderr = stderr.lock();
let mut parser = ArgParser::new(1)
.add_flag(&["h", "help"]);
parser.parse(env::args());
// Shows the help
if parser.found("help") {
let _ = stdout.write_all(MAN_PAGE.as_bytes());
let _ = stdout.flush();
process::exit(0);
}
let mut args = env::args().skip(1);
match args.next() {
None => {
+15 -23
View File
@@ -5,13 +5,12 @@ extern crate extra;
extern crate syscall;
extern crate userutils;
use std::io::{self, Read, Write};
use std::io::{self, Write};
use std::process::exit;
use std::fs::File;
use std::env;
use arg_parser::ArgParser;
use extra::option::OptionalExt;
use userutils::Passwd;
use userutils::{get_euid, get_user};
const MAN_PAGE: &'static str = /* @MANSTART{whoami} */ r#"
NAME
@@ -35,8 +34,6 @@ AUTHOR
Written by Jose Narvaez.
"#; /* @MANEND */
const PASSWD_FILE: &'static str = "/etc/passwd";
fn main() {
let stdout = io::stdout();
let mut stdout = stdout.lock();
@@ -52,23 +49,18 @@ fn main() {
exit(0);
}
match syscall::geteuid() {
Ok(euid) => {
let mut passwd_string = String::new();
if let Ok(mut file) = File::open(PASSWD_FILE) {
let _ = file.read_to_string(&mut passwd_string);
}
for line in passwd_string.lines() {
if let Ok(passwd) = Passwd::parse(line) {
if euid == passwd.uid as usize {
stdout.write_all(format!("{}\n", passwd.name).as_bytes()).try(&mut stderr);
stdout.flush().try(&mut stderr);
exit(0);
}
}
}
},
Err(_) => exit(1)
let euid = get_euid(&mut stderr);
let user = match get_user(euid, &mut stderr) {
Some(user) => user,
None => {
let msg = format!("whoami: no user found for uid: {}", euid);
stdout.write_all(msg.as_bytes()).try(&mut stderr);
stdout.flush().try(&mut stderr);
exit(1);
}
};
stdout.write_all(format!("{}\n", user).as_bytes()).try(&mut stderr);
stdout.flush().try(&mut stderr);
exit(0);
}