From 69f9889753a23f1c2274f14258adc892af8d2f14 Mon Sep 17 00:00:00 2001 From: armada Date: Fri, 23 Oct 2026 20:27:15 +0300 Subject: [PATCH 1/3] logname: add Windows support via GetUserNameW Fixes #11056 Co-Authored-By: Claude Opus 5.5 --- Cargo.lock | 1 + Cargo.toml | 2 +- src/uu/logname/Cargo.toml | 10 +++++++++- src/uu/logname/src/logname.rs | 22 +++++++++++++++++++++- tests/by-util/test_logname.rs | 2 +- 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 348d5ee24b6..3093a9e2602 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3627,6 +3627,7 @@ dependencies = [ "fluent", "libc", "uucore", + "windows-sys 0.60.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index da8dfb30ec4..3e9ca9ef66b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -577,6 +577,7 @@ feat_Tier1 = [ "env", "hostname", "kill", + "logname", "nohup", "nproc", "more", @@ -636,7 +637,6 @@ feat_require_unix_core = [ "groups", "id", "install", - "logname", "mkfifo", "mknod", "stat", diff --git a/src/uu/logname/Cargo.toml b/src/uu/logname/Cargo.toml index edc1b0e90f2..8e01add67fa 100644 --- a/src/uu/logname/Cargo.toml +++ b/src/uu/logname/Cargo.toml @@ -19,9 +19,17 @@ doctest = false [dependencies] clap = { workspace = true } fluent = { workspace = true } -libc = { workspace = true } uucore = { workspace = true } +[target.'cfg(unix)'.dependencies] +libc = { workspace = true } + +[target.'cfg(windows)'.dependencies] +windows-sys = { workspace = true, features = [ + "Win32_Foundation", + "Win32_System_WindowsProgramming", +] } + [lints] workspace = true diff --git a/src/uu/logname/src/logname.rs b/src/uu/logname/src/logname.rs index 0ecfe134a7c..eea89c1f4ee 100644 --- a/src/uu/logname/src/logname.rs +++ b/src/uu/logname/src/logname.rs @@ -6,12 +6,13 @@ // spell-checker:ignore (ToDO) getlogin userlogin use clap::Command; -use core::ffi::CStr; use std::io::{Write, stdout}; use uucore::translate; use uucore::{error::UResult, show_error}; +#[cfg(unix)] fn get_userlogin() -> Option { + use core::ffi::CStr; let login_ptr = unsafe { libc::getlogin() }; if login_ptr.is_null() { None @@ -20,6 +21,25 @@ fn get_userlogin() -> Option { } } +#[cfg(windows)] +fn get_userlogin() -> Option { + use std::ffi::OsString; + use std::os::windows::ffi::OsStringExt; + use windows_sys::Win32::System::WindowsProgramming::GetUserNameW; + // UNLEN (256) + terminating NUL + let mut buffer = [0_u16; 257]; + let mut len = buffer.len() as u32; + if unsafe { GetUserNameW(buffer.as_mut_ptr(), &raw mut len) } == 0 || len < 2 { + return None; + } + // `len` includes the terminating NUL + Some( + OsString::from_wide(&buffer[..len as usize - 1]) + .to_string_lossy() + .into_owned(), + ) +} + #[uucore::main(no_signals)] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let _ = uucore::clap_localization::handle_clap_result(uu_app(), args)?; diff --git a/tests/by-util/test_logname.rs b/tests/by-util/test_logname.rs index c3ff0458670..6d3d90abe55 100644 --- a/tests/by-util/test_logname.rs +++ b/tests/by-util/test_logname.rs @@ -48,7 +48,7 @@ fn test_output_format() { } result.success(); assert!( - Regex::new(r"^\w+\n$") + Regex::new(r"^[^\r\n]+\n$") .unwrap() .is_match(result.stdout_str()), "unexpected logname output: {:?}", From 9dc8bd0af6bae5ed0b79fd9e9fd784f1543f52d8 Mon Sep 17 00:00:00 2001 From: armada Date: Sat, 24 Oct 2026 11:47:54 +0300 Subject: [PATCH 2/3] logname: report the session's logged-on user on Windows Use WTSQuerySessionInformationW(WTSUserName) instead of GetUserNameW so logname reports the user logged on to the session, not the process token's user, matching getlogin on Unix and differing from whoami. Co-Authored-By: Claude Opus 5.5 --- src/uu/logname/Cargo.toml | 2 +- src/uu/logname/src/logname.rs | 40 ++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/uu/logname/Cargo.toml b/src/uu/logname/Cargo.toml index 8e01add67fa..5a6cc302073 100644 --- a/src/uu/logname/Cargo.toml +++ b/src/uu/logname/Cargo.toml @@ -27,7 +27,7 @@ libc = { workspace = true } [target.'cfg(windows)'.dependencies] windows-sys = { workspace = true, features = [ "Win32_Foundation", - "Win32_System_WindowsProgramming", + "Win32_System_RemoteDesktop", ] } [lints] diff --git a/src/uu/logname/src/logname.rs b/src/uu/logname/src/logname.rs index eea89c1f4ee..759c43ca12e 100644 --- a/src/uu/logname/src/logname.rs +++ b/src/uu/logname/src/logname.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) getlogin userlogin +// spell-checker:ignore (ToDO) getlogin userlogin runas use clap::Command; use std::io::{Write, stdout}; @@ -25,19 +25,35 @@ fn get_userlogin() -> Option { fn get_userlogin() -> Option { use std::ffi::OsString; use std::os::windows::ffi::OsStringExt; - use windows_sys::Win32::System::WindowsProgramming::GetUserNameW; - // UNLEN (256) + terminating NUL - let mut buffer = [0_u16; 257]; - let mut len = buffer.len() as u32; - if unsafe { GetUserNameW(buffer.as_mut_ptr(), &raw mut len) } == 0 || len < 2 { + use windows_sys::Win32::System::RemoteDesktop::{ + WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSFreeMemory, WTSQuerySessionInformationW, + WTSUserName, + }; + // Ask for the user logged on to this session rather than the process token's user + // (`GetUserNameW`, as `whoami` does), so `runas` or a service account still reports + // the person who logged in, like `getlogin` does on Unix. + let mut buffer = std::ptr::null_mut(); + let mut bytes = 0_u32; + // SAFETY: on success the buffer holds `bytes` bytes, a NUL-terminated UTF-16 string. + let ok = unsafe { + WTSQuerySessionInformationW( + WTS_CURRENT_SERVER_HANDLE, + WTS_CURRENT_SESSION, + WTSUserName, + &raw mut buffer, + &raw mut bytes, + ) + }; + if ok == 0 || buffer.is_null() { return None; } - // `len` includes the terminating NUL - Some( - OsString::from_wide(&buffer[..len as usize - 1]) - .to_string_lossy() - .into_owned(), - ) + // `bytes` includes the terminating NUL + let len = (bytes as usize / 2).saturating_sub(1); + // SAFETY: `buffer` is valid for `len` u16s and is freed right after copying. + let name = OsString::from_wide(unsafe { std::slice::from_raw_parts(buffer, len) }); + unsafe { WTSFreeMemory(buffer.cast()) }; + // Session 0 (services) has no logged-on user and returns an empty name. + (!name.is_empty()).then(|| name.to_string_lossy().into_owned()) } #[uucore::main(no_signals)] From 1baea5e9afa12fe19dd2d025b5a2fcb6659a5d6f Mon Sep 17 00:00:00 2001 From: armada Date: Sun, 25 Oct 2026 20:27:05 +0300 Subject: [PATCH 3/3] logname: add SAFETY comment for WTSFreeMemory --- src/uu/logname/src/logname.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/uu/logname/src/logname.rs b/src/uu/logname/src/logname.rs index 759c43ca12e..0a036791db9 100644 --- a/src/uu/logname/src/logname.rs +++ b/src/uu/logname/src/logname.rs @@ -51,6 +51,8 @@ fn get_userlogin() -> Option { let len = (bytes as usize / 2).saturating_sub(1); // SAFETY: `buffer` is valid for `len` u16s and is freed right after copying. let name = OsString::from_wide(unsafe { std::slice::from_raw_parts(buffer, len) }); + // SAFETY: `buffer` was allocated by `WTSQuerySessionInformationW`, is freed exactly once + // and is not used afterwards; `name` owns its own copy. unsafe { WTSFreeMemory(buffer.cast()) }; // Session 0 (services) has no logged-on user and returns an empty name. (!name.is_empty()).then(|| name.to_string_lossy().into_owned())