1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-30 04:27:45 +00:00

hostname: Cleanup

- Attach context to I/O errors
- Make flags override each other
- Support invalid unicode as argument
- Call WsaCleanup() even on panic
- Do not use deprecated std::mem::uninitialized()
This commit is contained in:
Jan Verbeek 2021-08-28 13:47:31 +02:00
parent e9d63519dd
commit 3f37ddbd22
2 changed files with 73 additions and 65 deletions

View file

@ -68,6 +68,7 @@ structs
substr substr
splitn splitn
trunc trunc
uninit
# * uutils # * uutils
basenc basenc
@ -277,6 +278,7 @@ ULONG
ULONGLONG ULONGLONG
UNLEN UNLEN
WCHAR WCHAR
WSADATA
errhandlingapi errhandlingapi
fileapi fileapi
handleapi handleapi

View file

@ -10,18 +10,13 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use clap::{crate_version, App, Arg, ArgMatches};
use std::collections::hash_set::HashSet; use std::collections::hash_set::HashSet;
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
use std::str; use std::str;
#[cfg(windows)]
use uucore::error::UUsageError;
use uucore::error::{UResult, USimpleError};
#[cfg(windows)] use clap::{crate_version, App, Arg, ArgMatches};
use winapi::shared::minwindef::MAKEWORD;
#[cfg(windows)] use uucore::error::{FromIo, UResult};
use winapi::um::winsock2::{WSACleanup, WSAStartup};
static ABOUT: &str = "Display or set the system's host name."; static ABOUT: &str = "Display or set the system's host name.";
@ -31,45 +26,52 @@ static OPT_FQDN: &str = "fqdn";
static OPT_SHORT: &str = "short"; static OPT_SHORT: &str = "short";
static OPT_HOST: &str = "host"; static OPT_HOST: &str = "host";
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
#![allow(clippy::let_and_return)]
#[cfg(windows)] #[cfg(windows)]
mod wsa {
use std::io;
use winapi::shared::minwindef::MAKEWORD;
use winapi::um::winsock2::{WSACleanup, WSAStartup, WSADATA};
pub(super) struct WsaHandle(());
pub(super) fn start() -> io::Result<WsaHandle> {
let err = unsafe {
let mut data = std::mem::MaybeUninit::<WSADATA>::uninit();
WSAStartup(MAKEWORD(2, 2), data.as_mut_ptr())
};
if err != 0 {
Err(io::Error::from_raw_os_error(err))
} else {
Ok(WsaHandle(()))
}
}
impl Drop for WsaHandle {
fn drop(&mut self) {
unsafe { unsafe {
#[allow(deprecated)] // This possibly returns an error but we can't handle it
let mut data = std::mem::uninitialized(); let _err = WSACleanup();
if WSAStartup(MAKEWORD(2, 2), &mut data as *mut _) != 0 {
return Err(UUsageError::new(
1,
"Failed to start Winsock 2.2".to_string(),
));
} }
} }
let result = execute(args);
#[cfg(windows)]
unsafe {
WSACleanup();
} }
result
} }
fn usage() -> String { fn usage() -> String {
format!("{0} [OPTION]... [HOSTNAME]", uucore::execution_phrase()) format!("{0} [OPTION]... [HOSTNAME]", uucore::execution_phrase())
} }
fn execute(args: impl uucore::Args) -> UResult<()> { #[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage(); let usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args); let matches = uu_app().usage(&usage[..]).get_matches_from(args);
match matches.value_of(OPT_HOST) { #[cfg(windows)]
let _handle = wsa::start().map_err_context(|| "failed to start Winsock".to_owned())?;
match matches.value_of_os(OPT_HOST) {
None => display_hostname(&matches), None => display_hostname(&matches),
Some(host) => { Some(host) => hostname::set(host).map_err_context(|| "failed to set hostname".to_owned()),
if let Err(err) = hostname::set(host) {
return Err(USimpleError::new(1, format!("{}", err)));
} else {
Ok(())
}
}
} }
} }
@ -81,44 +83,53 @@ pub fn uu_app() -> App<'static, 'static> {
Arg::with_name(OPT_DOMAIN) Arg::with_name(OPT_DOMAIN)
.short("d") .short("d")
.long("domain") .long("domain")
.overrides_with_all(&[OPT_DOMAIN, OPT_IP_ADDRESS, OPT_FQDN, OPT_SHORT])
.help("Display the name of the DNS domain if possible"), .help("Display the name of the DNS domain if possible"),
) )
.arg( .arg(
Arg::with_name(OPT_IP_ADDRESS) Arg::with_name(OPT_IP_ADDRESS)
.short("i") .short("i")
.long("ip-address") .long("ip-address")
.overrides_with_all(&[OPT_DOMAIN, OPT_IP_ADDRESS, OPT_FQDN, OPT_SHORT])
.help("Display the network address(es) of the host"), .help("Display the network address(es) of the host"),
) )
// TODO: support --long
.arg( .arg(
Arg::with_name(OPT_FQDN) Arg::with_name(OPT_FQDN)
.short("f") .short("f")
.long("fqdn") .long("fqdn")
.overrides_with_all(&[OPT_DOMAIN, OPT_IP_ADDRESS, OPT_FQDN, OPT_SHORT])
.help("Display the FQDN (Fully Qualified Domain Name) (default)"), .help("Display the FQDN (Fully Qualified Domain Name) (default)"),
) )
.arg(Arg::with_name(OPT_SHORT).short("s").long("short").help( .arg(
"Display the short hostname (the portion before the first dot) if \ Arg::with_name(OPT_SHORT)
possible", .short("s")
)) .long("short")
.overrides_with_all(&[OPT_DOMAIN, OPT_IP_ADDRESS, OPT_FQDN, OPT_SHORT])
.help("Display the short hostname (the portion before the first dot) if possible"),
)
.arg(Arg::with_name(OPT_HOST)) .arg(Arg::with_name(OPT_HOST))
} }
fn display_hostname(matches: &ArgMatches) -> UResult<()> { fn display_hostname(matches: &ArgMatches) -> UResult<()> {
let hostname = hostname::get().unwrap().into_string().unwrap(); let hostname = hostname::get()
.map_err_context(|| "failed to get hostname".to_owned())?
.to_string_lossy()
.into_owned();
if matches.is_present(OPT_IP_ADDRESS) { if matches.is_present(OPT_IP_ADDRESS) {
// XXX: to_socket_addrs needs hostname:port so append a dummy port and remove it later. // XXX: to_socket_addrs needs hostname:port so append a dummy port and remove it later.
// This was originally supposed to use std::net::lookup_host, but that seems to be // This was originally supposed to use std::net::lookup_host, but that seems to be
// deprecated. Perhaps we should use the dns-lookup crate? // deprecated. Perhaps we should use the dns-lookup crate?
let hostname = hostname + ":1"; let hostname = hostname + ":1";
match hostname.to_socket_addrs() { let addresses = hostname
Ok(addresses) => { .to_socket_addrs()
.map_err_context(|| "failed to resolve socket addresses".to_owned())?;
let mut hashset = HashSet::new(); let mut hashset = HashSet::new();
let mut output = String::new(); let mut output = String::new();
for addr in addresses { for addr in addresses {
// XXX: not sure why this is necessary... // XXX: not sure why this is necessary...
if !hashset.contains(&addr) { if !hashset.contains(&addr) {
let mut ip = format!("{}", addr); let mut ip = addr.to_string();
if ip.ends_with(":1") { if ip.ends_with(":1") {
let len = ip.len(); let len = ip.len();
ip.truncate(len - 2); ip.truncate(len - 2);
@ -134,11 +145,6 @@ fn display_hostname(matches: &ArgMatches) -> UResult<()> {
} }
Ok(()) Ok(())
}
Err(f) => {
return Err(USimpleError::new(1, format!("{}", f)));
}
}
} else { } else {
if matches.is_present(OPT_SHORT) || matches.is_present(OPT_DOMAIN) { if matches.is_present(OPT_SHORT) || matches.is_present(OPT_DOMAIN) {
let mut it = hostname.char_indices().filter(|&ci| ci.1 == '.'); let mut it = hostname.char_indices().filter(|&ci| ci.1 == '.');