theseus/bundled-addons/pithos/core/winctrl.js

34 lines
1.8 KiB
JavaScript
Raw Normal View History

// A gentle stop for s3d on Windows. child.kill() there is TerminateProcess:
// s3d dies wherever it is, and one stopped in the middle of an upload had
// lost buffered files whose database rows still said "waiting". s3d shuts
// down cleanly on an interrupt, so this attaches a short PowerShell to s3d's
// own (hidden) console and sends Ctrl+Break there.
//
// Ctrl+Break, not Ctrl+C: Windows disables Ctrl+C for a process started in
// a new process group, and Go reads both as os.Interrupt. A spawned console
// program gets a console of its own (Node passes CREATE_NO_WINDOW), so the
// event reaches s3d and not Pithos. It ends the PowerShell too, which is
// why only "could not attach" (2) and "could not send" (3) count as failure.
import { execFile } from 'node:child_process';
const SCRIPT = (pid) => [
"Add-Type -Name K -Namespace PithosCtrl -MemberDefinition '",
'[DllImport("kernel32.dll")] public static extern bool FreeConsole();',
'[DllImport("kernel32.dll")] public static extern bool AttachConsole(uint pid);',
'[DllImport("kernel32.dll")] public static extern bool GenerateConsoleCtrlEvent(uint ev, uint group);',
"';",
'[PithosCtrl.K]::FreeConsole() | Out-Null;',
`if (-not [PithosCtrl.K]::AttachConsole(${Number(pid) >>> 0})) { exit 2 };`,
'if ([PithosCtrl.K]::GenerateConsoleCtrlEvent(1, 0)) { exit 0 } else { exit 3 }',
].join(' ');
// Resolves true when Ctrl+Break was sent to the console of `pid`.
export function sendBreak(pid) {
if (process.platform !== 'win32' || !pid) return Promise.resolve(false);
return new Promise((resolve) => {
execFile('powershell.exe', ['-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', SCRIPT(pid)],
{ windowsHide: true, timeout: 20_000 }, (err) => resolve(!err || (err.code !== 2 && err.code !== 3 && !err.killed)));
});
}