From 94f9c8d0d12d2e462849cfb2833dd98a13652147 Mon Sep 17 00:00:00 2001 From: Yohan Boogaert Date: Fri, 7 Feb 2025 12:08:17 +0100 Subject: [PATCH] Add `CreateProcessW` to the windows dependencies (#107) --- Cargo.lock | 104 +++++++++++++++-------- Cargo.toml | 1 + src/binding.rs | 129 ---------------------------- src/subprocess.rs | 209 ++++------------------------------------------ 4 files changed, 88 insertions(+), 355 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33bd130..5301fce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "CreateProcessW" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39bfdb6e9fd9660925cde72f8b734c6290b6ff345e87a1daa2774f9c2464fa43" + [[package]] name = "aho-corasick" version = "1.1.3" @@ -52,11 +58,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] @@ -68,14 +75,15 @@ checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "bitflags" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "cargo-temp" version = "0.3.1" dependencies = [ + "CreateProcessW", "anyhow", "clap", "dirs", @@ -97,9 +105,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.26" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" +checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" dependencies = [ "clap_builder", "clap_derive", @@ -107,9 +115,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.26" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" dependencies = [ "anstream", "anstyle", @@ -119,9 +127,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck", "proc-macro2", @@ -215,7 +223,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] @@ -238,9 +258,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "indexmap" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown", @@ -276,9 +296,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "memchr" @@ -288,9 +308,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "option-ext" @@ -322,7 +342,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror", ] @@ -358,9 +378,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" -version = "0.38.43" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags", "errno", @@ -406,9 +426,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -417,13 +437,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.15.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ "cfg-if", "fastrand", - "getrandom", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -451,9 +471,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" dependencies = [ "serde", "serde_spanned", @@ -472,9 +492,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" dependencies = [ "indexmap", "serde", @@ -485,9 +505,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "utf8parse" @@ -501,6 +521,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -642,13 +671,22 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.24" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f" dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + [[package]] name = "xdg" version = "2.5.2" diff --git a/Cargo.toml b/Cargo.toml index 66adac4..fb47180 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,3 +28,4 @@ xdg = "2.5" [target.'cfg(windows)'.dependencies] dirs = "5.0" +CreateProcessW = "0.1" diff --git a/src/binding.rs b/src/binding.rs index b0402e8..1272b5a 100644 --- a/src/binding.rs +++ b/src/binding.rs @@ -1,136 +1,7 @@ -#![allow(clippy::upper_case_acronyms, non_snake_case, non_camel_case_types)] - -use std::{ffi::c_void, mem::size_of, ptr::null_mut}; - -// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject#parameters -pub(crate) const INFINITE: u32 = 0xFFFFFFFF; -// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject#return-value -pub(crate) const WAIT_OBJECT_0: u32 = 0x00000000; -// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess#remarks -pub(crate) const STATUS_PENDING: u32 = 0x00000103; - // https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#BOOL pub(crate) type BOOL = i32; -// https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#DWORD -pub(crate) type DWORD = u32; -// https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#PCWSTR -pub(crate) type PCWSTR = *const u16; -// https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#PDWORD -pub(crate) type PDWORD = *mut u32; -// https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#PVOID -type PVOID = *mut c_void; -// https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#PWSTR -pub(crate) type PWSTR = *mut u16; -// https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#UINT -pub(crate) type UINT = u32; - -// https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#HANDLE -type HANDLE = *mut c_void; -// https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#PBYTE -type PBYTE = *mut u8; -// https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#WORD -type WORD = u16; - -// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_information -#[repr(C)] -pub(crate) struct PROCESS_INFORMATION { - pub hProcess: HANDLE, - pub hThread: HANDLE, - dwProcessId: DWORD, - dwThreadId: DWORD, -} - -impl Default for PROCESS_INFORMATION { - fn default() -> Self { - Self { - hProcess: null_mut(), - hThread: null_mut(), - dwProcessId: 0, - dwThreadId: 0, - } - } -} - -// https://learn.microsoft.com/en-us/windows/win32/api/wtypesbase/ns-wtypesbase-security_attributes -#[repr(C)] -pub(crate) struct SECURITY_ATTRIBUTES { - nLength: DWORD, - lpSecurityDescriptor: PVOID, - bInheritHandle: BOOL, -} - -// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow -#[repr(C)] -pub(crate) struct STARTUPINFOW { - cb: DWORD, - lpReserved: PWSTR, - lpDesktop: PWSTR, - lpTitle: PWSTR, - dwX: DWORD, - dwY: DWORD, - dwXSize: DWORD, - dwYSize: DWORD, - dwXCountChars: DWORD, - dwYCountChars: DWORD, - dwFillAttribute: DWORD, - dwFlags: DWORD, - wShowWindow: WORD, - cbReserved2: WORD, - lpReserved2: PBYTE, - hStdInput: HANDLE, - hStdOutput: HANDLE, - hStdError: HANDLE, -} - -impl Default for STARTUPINFOW { - fn default() -> Self { - Self { - cb: size_of::() as DWORD, - lpReserved: null_mut(), - lpDesktop: null_mut(), - lpTitle: null_mut(), - dwX: 0, - dwY: 0, - dwXSize: 0, - dwYSize: 0, - dwXCountChars: 0, - dwYCountChars: 0, - dwFillAttribute: 0, - dwFlags: 0, - wShowWindow: 0, - cbReserved2: 0, - lpReserved2: null_mut(), - hStdInput: null_mut(), - hStdOutput: null_mut(), - hStdError: null_mut(), - } - } -} extern "system" { - // https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle - pub(crate) fn CloseHandle(hObject: HANDLE) -> BOOL; - // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw - pub(crate) fn CreateProcessW( - lpApplicationName: PCWSTR, - lpCommandLine: PWSTR, - lpProcessAttributes: *mut SECURITY_ATTRIBUTES, - lpThreadAttributes: *mut SECURITY_ATTRIBUTES, - bInheritHandles: BOOL, - dwCreationFlags: DWORD, - lpEnvironment: PVOID, - lpCurrentDirectory: PCWSTR, - lpStartupInfo: *const STARTUPINFOW, - lpProcessInformation: *mut PROCESS_INFORMATION, - ) -> BOOL; // https://learn.microsoft.com/en-us/windows/console/freeconsole pub(crate) fn FreeConsole() -> BOOL; - // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess - pub(crate) fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: PDWORD) -> BOOL; - // https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror - pub(crate) fn GetLastError() -> DWORD; - // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess - pub(crate) fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) -> BOOL; - // https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject - pub(crate) fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; } diff --git a/src/subprocess.rs b/src/subprocess.rs index bfbc481..3874527 100644 --- a/src/subprocess.rs +++ b/src/subprocess.rs @@ -3,6 +3,14 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; +#[cfg(unix)] +use std::{env, process::Stdio, time}; + +#[cfg(unix)] +pub use std::process::{Child, Command}; +#[cfg(windows)] +pub use CreateProcessW::{Child, Command}; + #[derive(Serialize, Deserialize)] pub struct SubProcess { pub command: String, @@ -24,27 +32,27 @@ impl SubProcess { #[cfg(unix)] { let mut process = - Command::new(std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string())); + Command::new(env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string())); process .current_dir(self.working_dir.as_deref().unwrap_or(tmp_dir)) .args(["-c", &self.command]) - .stdin(std::process::Stdio::null()); + .stdin(Stdio::null()); if !self.foreground { if !self.stdout.unwrap_or(false) { - process.stdout(std::process::Stdio::null()); + process.stdout(Stdio::null()); } if !self.stderr.unwrap_or(false) { - process.stderr(std::process::Stdio::null()); + process.stderr(Stdio::null()); } } else { if !self.stdout.unwrap_or(true) { - process.stdout(std::process::Stdio::null()); + process.stdout(Stdio::null()); } if !self.stderr.unwrap_or(true) { - process.stderr(std::process::Stdio::null()); + process.stderr(Stdio::null()); } } @@ -101,7 +109,7 @@ pub fn kill_subprocesses(subprocesses: &mut [Child]) -> Result<()> { for subprocess in subprocesses.iter_mut() { { - let now = std::time::Instant::now(); + let now = time::Instant::now(); unsafe { libc::kill( @@ -114,7 +122,7 @@ pub fn kill_subprocesses(subprocesses: &mut [Child]) -> Result<()> { } while now.elapsed().as_secs() < 2 { - std::thread::sleep(std::time::Duration::from_millis(200)); + std::thread::sleep(time::Duration::from_millis(200)); if let Ok(Some(_)) = subprocess.try_wait() { break; } @@ -135,188 +143,3 @@ pub fn kill_subprocesses(subprocesses: &mut [Child]) -> Result<()> { Ok(()) } - -#[cfg(unix)] -pub type Child = std::process::Child; -#[cfg(windows)] -pub use windows::Child; - -#[cfg(unix)] -type Command = std::process::Command; -#[cfg(windows)] -pub use windows::Command; - -#[cfg(windows)] -mod windows { - use crate::binding::{ - CloseHandle, CreateProcessW, GetExitCodeProcess, GetLastError, TerminateProcess, - WaitForSingleObject, BOOL, DWORD, INFINITE, PCWSTR, PDWORD, PROCESS_INFORMATION, PWSTR, - STARTUPINFOW, STATUS_PENDING, UINT, WAIT_OBJECT_0, - }; - use anyhow::{bail, Result}; - use std::{ - ffi::{OsStr, OsString}, - iter::once, - os::windows::ffi::OsStrExt, - path::{Path, PathBuf}, - ptr::{null, null_mut}, - }; - - pub struct Command { - command: OsString, - inherit_handles: bool, - current_directory: Option, - } - - impl Command { - pub fn new(command: impl Into) -> Self { - Self { - command: command.into(), - inherit_handles: false, - current_directory: None, - } - } - - pub fn inherit_handles(&mut self, inherit: bool) -> &mut Self { - self.inherit_handles = inherit; - self - } - - pub fn current_dir(&mut self, dir: impl Into) -> &mut Self { - self.current_directory = Some(dir.into()); - self - } - - pub fn spawn(&mut self) -> Result { - Child::new( - self.command.as_ref(), - self.inherit_handles, - self.current_directory.as_deref(), - ) - } - - pub fn status(&mut self) -> Result { - self.spawn()?.wait() - } - } - - pub struct Child { - process_information: PROCESS_INFORMATION, - } - - impl Child { - fn new( - command: &OsStr, - inherit_handles: bool, - current_directory: Option<&Path>, - ) -> Result { - let startup_info = STARTUPINFOW::default(); - let mut process_info = PROCESS_INFORMATION::default(); - - let process_creation_flags = 0 as DWORD; - - let current_directory_ptr = current_directory - .map(|path| { - let wide_path: Vec = - path.as_os_str().encode_wide().chain(once(0)).collect(); - wide_path.as_ptr() - }) - .unwrap_or(null_mut()); - - let command = command.encode_wide().collect::>(); - - let res = unsafe { - CreateProcessW( - null(), - command.as_ptr() as PWSTR, - null_mut(), - null_mut(), - inherit_handles as BOOL, - process_creation_flags as DWORD, - null_mut(), - current_directory_ptr as PCWSTR, - &startup_info, - &mut process_info, - ) - }; - - if res != 0 { - Ok(Self { - process_information: process_info, - }) - } else { - bail!("Cannot create process (code {:#x})", unsafe { - GetLastError() - }) - } - } - - pub fn kill(&self) -> Result<()> { - let res = unsafe { TerminateProcess(self.process_information.hProcess, 0 as UINT) }; - - if res != 0 { - Ok(()) - } else { - bail!("Cannot kill process (code {:#x})", unsafe { - GetLastError() - }) - } - } - - pub fn wait(&self) -> Result { - let mut exit_code = 0; - - let wait = unsafe { - WaitForSingleObject(self.process_information.hProcess, INFINITE) == WAIT_OBJECT_0 - }; - - if wait { - let res = unsafe { - GetExitCodeProcess(self.process_information.hProcess, &mut exit_code as PDWORD) - }; - - if res != 0 { - unsafe { - CloseHandle(self.process_information.hProcess); - CloseHandle(self.process_information.hThread); - } - - Ok(exit_code) - } else { - bail!("cannot get exit status (code {:#x})", unsafe { - GetLastError() - }) - } - } else { - bail!("cannot wait process (code {:#x})", unsafe { - GetLastError() - }) - } - } - - pub fn try_wait(&self) -> Result> { - let mut exit_code: u32 = 0; - - let res = unsafe { - GetExitCodeProcess(self.process_information.hProcess, &mut exit_code as PDWORD) - }; - - if res != 0 { - if exit_code == STATUS_PENDING { - Ok(None) - } else { - unsafe { - CloseHandle(self.process_information.hProcess); - CloseHandle(self.process_information.hThread); - } - - Ok(Some(exit_code)) - } - } else { - bail!("cannot get exit status (code {:#x})", unsafe { - GetLastError() - }) - } - } - } -}