mirror of
https://github.com/herdrdev/herdr.git
synced 2026-09-14 18:03:41 +08:00
+75
-5
@@ -350,8 +350,10 @@ fn windows_powershell_streaming_application_command(path: &str, args: &[&str]) -
|
||||
.map(|arg| crate::platform::quote_windows_command_line_arg(arg))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
// Start-Process -Wait waits for descendants, including a cold-started server.
|
||||
// Retain the handle so Windows PowerShell 5.1 keeps the application's exit code.
|
||||
windows_powershell_script_command(&format!(
|
||||
"$process = Start-Process -FilePath {} -ArgumentList {} -NoNewWindow -Wait -PassThru -ErrorAction Stop; exit $process.ExitCode",
|
||||
"$process = Start-Process -FilePath {} -ArgumentList {} -NoNewWindow -PassThru -ErrorAction Stop; $null = $process.Handle; $process.WaitForExit(); exit $process.ExitCode",
|
||||
crate::platform::quote_powershell_arg(path),
|
||||
crate::platform::quote_powershell_arg(&command_line),
|
||||
))
|
||||
@@ -3615,6 +3617,74 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn windows_bridge_returns_application_exit_while_descendant_is_running() {
|
||||
let pid_file = std::env::temp_dir().join(format!(
|
||||
"herdr bridge descendant {}-{}.pid",
|
||||
std::process::id(),
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.expect("current time")
|
||||
.as_nanos()
|
||||
));
|
||||
let script = format!(
|
||||
"$child = Start-Process powershell.exe -ArgumentList '-NoProfile -NonInteractive -Command Start-Sleep -Seconds 30' -NoNewWindow -PassThru; Set-Content -LiteralPath {} -Value $child.Id; exit 23",
|
||||
crate::platform::quote_powershell_arg(&pid_file.to_string_lossy())
|
||||
);
|
||||
let encoded = base64::engine::general_purpose::STANDARD.encode(
|
||||
script
|
||||
.encode_utf16()
|
||||
.flat_map(u16::to_le_bytes)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
let command = windows_powershell_streaming_application_command(
|
||||
"powershell.exe",
|
||||
&["-NoProfile", "-NonInteractive", "-EncodedCommand", &encoded],
|
||||
);
|
||||
let mut launcher = Command::new("powershell.exe");
|
||||
launcher
|
||||
.args(["-NoProfile", "-NonInteractive", "-EncodedCommand"])
|
||||
.arg(command.split_whitespace().last().unwrap())
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null());
|
||||
crate::platform::configure_background_command(&mut launcher);
|
||||
let mut launcher = launcher.spawn().expect("launch Windows bridge command");
|
||||
let deadline = Instant::now() + Duration::from_secs(10);
|
||||
let status = loop {
|
||||
let status = launcher.try_wait().expect("poll bridge launcher");
|
||||
if status.is_some() || Instant::now() >= deadline {
|
||||
break status;
|
||||
}
|
||||
thread::sleep(Duration::from_millis(20));
|
||||
};
|
||||
if status.is_none() {
|
||||
let mut cleanup = Command::new("taskkill.exe");
|
||||
cleanup.args(["/PID", &launcher.id().to_string(), "/T", "/F"]);
|
||||
crate::platform::configure_background_command(&mut cleanup);
|
||||
let _ = cleanup.output();
|
||||
let _ = launcher.kill();
|
||||
}
|
||||
let _ = launcher.wait();
|
||||
// Clean up the launcher before a missing PID can fail the test.
|
||||
let descendant = fs::read_to_string(&pid_file);
|
||||
let _ = fs::remove_file(pid_file);
|
||||
let descendant = descendant.expect("descendant PID");
|
||||
let descendant = descendant.trim().parse::<u32>().expect("numeric PID");
|
||||
let mut cleanup = Command::new("powershell.exe");
|
||||
cleanup
|
||||
.args(["-NoProfile", "-NonInteractive", "-Command"])
|
||||
.arg(format!("Stop-Process -Id {descendant} -ErrorAction Stop"));
|
||||
crate::platform::configure_background_command(&mut cleanup);
|
||||
let descendant_was_running = cleanup.status().expect("stop test descendant").success();
|
||||
assert!(
|
||||
descendant_was_running,
|
||||
"descendant must outlive the application"
|
||||
);
|
||||
assert_eq!(status.and_then(|status| status.code()), Some(23));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn windows_remote_commands_use_one_encoded_powershell_grammar() {
|
||||
fn decode(command: &str) -> String {
|
||||
@@ -3661,22 +3731,22 @@ mod tests {
|
||||
(
|
||||
"direct bridge",
|
||||
executable.bridge_command("agents"),
|
||||
"$process = Start-Process -FilePath herdr.exe -ArgumentList '--session agents remote-client-bridge' -NoNewWindow -Wait -PassThru -ErrorAction Stop; exit $process.ExitCode",
|
||||
"$process = Start-Process -FilePath herdr.exe -ArgumentList '--session agents remote-client-bridge' -NoNewWindow -PassThru -ErrorAction Stop; $null = $process.Handle; $process.WaitForExit(); exit $process.ExitCode",
|
||||
),
|
||||
(
|
||||
"API bridge with explicit default session",
|
||||
remote_api_bridge_command(&RemoteHerdr::for_platform(RemotePlatform { os: "windows", arch: "x86_64" }), "default", false),
|
||||
"$process = Start-Process -FilePath herdr.exe -ArgumentList '--session default remote-api-bridge' -NoNewWindow -Wait -PassThru -ErrorAction Stop; exit $process.ExitCode",
|
||||
"$process = Start-Process -FilePath herdr.exe -ArgumentList '--session default remote-api-bridge' -NoNewWindow -PassThru -ErrorAction Stop; $null = $process.Handle; $process.WaitForExit(); exit $process.ExitCode",
|
||||
),
|
||||
(
|
||||
"API bridge capability probe",
|
||||
remote_api_bridge_command(&RemoteHerdr::for_platform(RemotePlatform { os: "windows", arch: "x86_64" }), "agents", true),
|
||||
"$process = Start-Process -FilePath herdr.exe -ArgumentList '--session agents remote-api-bridge --check' -NoNewWindow -Wait -PassThru -ErrorAction Stop; exit $process.ExitCode",
|
||||
"$process = Start-Process -FilePath herdr.exe -ArgumentList '--session agents remote-api-bridge --check' -NoNewWindow -PassThru -ErrorAction Stop; $null = $process.Handle; $process.WaitForExit(); exit $process.ExitCode",
|
||||
),
|
||||
(
|
||||
"saved bridge with closed stdin",
|
||||
executable.saved_bridge_command("agents"),
|
||||
"$process = Start-Process -FilePath herdr.exe -ArgumentList '--session agents remote-client-bridge' -NoNewWindow -Wait -PassThru -ErrorAction Stop; exit $process.ExitCode",
|
||||
"$process = Start-Process -FilePath herdr.exe -ArgumentList '--session agents remote-client-bridge' -NoNewWindow -PassThru -ErrorAction Stop; $null = $process.Handle; $process.WaitForExit(); exit $process.ExitCode",
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user