use crate::{
command::{Arg, Command, CommandResult},
server::{ConnectionState, ThrusshSession},
};
use async_trait::async_trait;
use bitflags::bitflags;
use thrussh::ChannelId;
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
struct ToPrint: u8 {
const KERNEL_NAME = 0b0000_0001;
const NODE_NAME = 0b0000_0010;
const KERNEL_RELEASE = 0b0000_0100;
const KERNEL_VERSION = 0b0000_1000;
const MACHINE = 0b0001_0000;
const PROCESSOR = 0b0010_0000;
const PLATFORM = 0b0100_0000;
const OPERATING_SYSTEM = 0b1000_0000;
}
}
const VERSION_STRING: &str = "uname (GNU coreutils) 8.32
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by David MacKenzie.
";
pub const HELP_STRING: &str = "Usage: uname [OPTION]...
Print certain system information. With no OPTION, same as -s.
-a, --all print all information, in the following order,
except omit -p and -i if unknown:
-s, --kernel-name print the kernel name
-n, --nodename print the network node hostname
-r, --kernel-release print the kernel release
-v, --kernel-version print the kernel version
-m, --machine print the machine hardware name
-p, --processor print the processor type (non-portable)
-i, --hardware-platform print the hardware platform (non-portable)
-o, --operating-system print the operating system
--help display this help and exit
--version output version information and exit
GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/uname>
or available locally via: info '(coreutils) uname invocation'
";
#[derive(Debug, Clone)]
pub struct Uname {}
#[async_trait]
impl Command for Uname {
async fn new<S: ThrusshSession + Send>(
_connection: &mut ConnectionState,
params: &[String],
channel: ChannelId,
session: &mut S,
) -> CommandResult<Self> {
let (out, exit_code) = execute(params);
session.data(channel, out.into());
CommandResult::Exit(exit_code)
}
async fn stdin<S: ThrusshSession + Send>(
self,
_connection: &mut ConnectionState,
_channel: ChannelId,
_data: &[u8],
_session: &mut S,
) -> CommandResult<Self> {
CommandResult::Exit(0)
}
}
pub fn execute(params: &[String]) -> (String, u32) {
let mut to_print = ToPrint::empty();
let mut filter_unknown = false;
for param in super::argparse(params) {
to_print |= match param {
Arg::Short('a') | Arg::Long("all") => {
filter_unknown = true;
ToPrint::all()
}
Arg::Short('s') | Arg::Long("kernel-name") => ToPrint::KERNEL_NAME,
Arg::Short('n') | Arg::Long("nodename") => ToPrint::NODE_NAME,
Arg::Short('r') | Arg::Long("kernel-release") => ToPrint::KERNEL_RELEASE,
Arg::Short('v') | Arg::Long("kernel-version") => ToPrint::KERNEL_VERSION,
Arg::Short('m') | Arg::Long("machine") => ToPrint::MACHINE,
Arg::Short('p') | Arg::Long("processor") => ToPrint::PROCESSOR,
Arg::Short('i') | Arg::Long("hardware-platform") => ToPrint::PLATFORM,
Arg::Short('o') | Arg::Long("operating-system") => ToPrint::OPERATING_SYSTEM,
Arg::Long("help") => return (HELP_STRING.to_string(), 0),
Arg::Long("version") => return (VERSION_STRING.to_string(), 0),
Arg::Operand(operand) => {
return (
format!(
"uname: extra operand '{operand}'\nTry 'uname --help' for more information.\n"
),
1,
);
}
Arg::Short(s) => {
return (
format!(
"uname: invalid option -- '{s}'\nTry 'uname --help' for more information.\n"
),
1,
);
}
Arg::Long(s) => {
return (
format!(
"uname: unrecognized option '--{s}'\nTry 'uname --help' for more information.\n"
),
1,
);
}
};
}
if to_print.is_empty() {
to_print |= ToPrint::KERNEL_NAME;
}
let mut out = String::with_capacity(105);
macro_rules! write {
($v:expr) => {
if !out.is_empty() {
out.push(' ');
}
out.push_str($v);
};
}
if to_print.contains(ToPrint::KERNEL_NAME) {
write!("Linux");
}
if to_print.contains(ToPrint::NODE_NAME) {
write!("cd5079c0d642");
}
if to_print.contains(ToPrint::KERNEL_RELEASE) {
write!("5.15.49");
}
if to_print.contains(ToPrint::KERNEL_VERSION) {
write!("#1 SMP PREEMPT Tue Sep 13 07:51:32 UTC 2022");
}
if to_print.contains(ToPrint::MACHINE) {
write!("x86_64");
}
if to_print.contains(ToPrint::PROCESSOR) && !filter_unknown {
write!("unknown");
}
if to_print.contains(ToPrint::PLATFORM) && !filter_unknown {
write!("unknown");
}
if to_print.contains(ToPrint::OPERATING_SYSTEM) {
write!("GNU/Linux");
}
out.push('\n');
(out, 0)
}
#[cfg(test)]
mod test {
use crate::command::uname::execute;
use test_case::test_case;
#[test_case("", 0; "none")]
#[test_case("-a", 0; "all")]
#[test_case("-snrvmpio", 0; "all separate")]
#[test_case("-asnrvmpio", 0; "all separate with all")]
#[test_case("-sn", 0; "subset")]
#[test_case("-sn --fake", 1; "unknown long arg param")]
#[test_case("-sn -z", 1; "unknown short arg param")]
#[test_case("-sn oper", 1; "unknown operand")]
fn snapshot(input: &str, expected_exit_code: u32) {
let input_parsed = shlex::split(input).unwrap();
let (output, actual_exit_code) = execute(&input_parsed);
insta::assert_display_snapshot!(input, output);
assert_eq!(actual_exit_code, expected_exit_code);
}
}