Detach managed processes from run console
This commit is contained in:
@@ -22,17 +22,23 @@ import (
|
|||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
const managedProcessHelperFlag = "--run-managed-process-helper"
|
const (
|
||||||
|
managedProcessHelperFlag = "--run-managed-process-helper"
|
||||||
|
managedProcessDetachedFlags = windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP | windows.CREATE_BREAKAWAY_FROM_JOB
|
||||||
|
)
|
||||||
|
|
||||||
func configureManagedProcessCommand(cmd *exec.Cmd) {
|
func configureManagedProcessCommand(cmd *exec.Cmd) {
|
||||||
// Run may itself be launched by a service or task scheduler job that
|
// Run may itself be launched by a service or task scheduler job that
|
||||||
// terminates its process tree on shutdown. The helper is the durable
|
// terminates its process tree on shutdown. The helper is the durable
|
||||||
// owner of the game process, so ask Windows to keep it outside that job.
|
// owner of the game process, so ask Windows to keep it outside that job.
|
||||||
// The helper itself does not need a console: its stdout/stderr are already
|
// The helper itself does not need a console: its stdout/stderr are already
|
||||||
// durable files. CREATE_NEW_CONSOLE creates a second hidden conhost in a
|
// durable files. Detaching also prevents an operator Ctrl+C used to restart
|
||||||
// non-interactive Task Scheduler session and prevents the child pseudo
|
// Run from propagating into the helper, cmd wrapper, or game process whose
|
||||||
// console from initializing on Windows Server.
|
// logs the next Run instance must resume tailing.
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: windows.CREATE_NO_WINDOW | windows.CREATE_BREAKAWAY_FROM_JOB, HideWindow: true}
|
// CREATE_NEW_CONSOLE creates a second hidden conhost in a non-interactive
|
||||||
|
// Task Scheduler session and prevents the child pseudo console from
|
||||||
|
// initializing on Windows Server.
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: managedProcessDetachedFlags, HideWindow: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Windows console output is collected by a child helper that owns the
|
// Windows console output is collected by a child helper that owns the
|
||||||
@@ -532,7 +538,7 @@ func runManagedShellWithPipes(command ProcessCommand, stopEventName string, stdo
|
|||||||
}
|
}
|
||||||
startup := &windows.StartupInfo{Cb: uint32(unsafe.Sizeof(windows.StartupInfo{})), Flags: windows.STARTF_USESTDHANDLES | windows.STARTF_USESHOWWINDOW, ShowWindow: windows.SW_HIDE, StdInput: inputRead, StdOutput: stdoutWrite, StdErr: stderrWrite}
|
startup := &windows.StartupInfo{Cb: uint32(unsafe.Sizeof(windows.StartupInfo{})), Flags: windows.STARTF_USESTDHANDLES | windows.STARTF_USESHOWWINDOW, ShowWindow: windows.SW_HIDE, StdInput: inputRead, StdOutput: stdoutWrite, StdErr: stderrWrite}
|
||||||
var processInfo windows.ProcessInformation
|
var processInfo windows.ProcessInformation
|
||||||
if err := windows.CreateProcess(applicationName, &commandLine[0], nil, nil, true, windows.CREATE_BREAKAWAY_FROM_JOB|windows.CREATE_NO_WINDOW|windows.CREATE_UNICODE_ENVIRONMENT, environment, workDir, startup, &processInfo); err != nil {
|
if err := windows.CreateProcess(applicationName, &commandLine[0], nil, nil, true, managedProcessDetachedFlags|windows.CREATE_UNICODE_ENVIRONMENT, environment, workDir, startup, &processInfo); err != nil {
|
||||||
closeOnError()
|
closeOnError()
|
||||||
return 1, fmt.Errorf("start managed shell: %w", err)
|
return 1, fmt.Errorf("start managed shell: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfigureManagedProcessCommandPreservesInheritedConsole(t *testing.T) {
|
func TestConfigureManagedProcessCommandDetachesFromRunConsole(t *testing.T) {
|
||||||
cmd := exec.Command("cmd.exe")
|
cmd := exec.Command("cmd.exe")
|
||||||
configureManagedProcessCommand(cmd)
|
configureManagedProcessCommand(cmd)
|
||||||
if cmd.SysProcAttr == nil {
|
if cmd.SysProcAttr == nil {
|
||||||
@@ -21,9 +21,9 @@ func TestConfigureManagedProcessCommandPreservesInheritedConsole(t *testing.T) {
|
|||||||
if !cmd.SysProcAttr.HideWindow {
|
if !cmd.SysProcAttr.HideWindow {
|
||||||
t.Fatal("expected managed console window to stay hidden")
|
t.Fatal("expected managed console window to stay hidden")
|
||||||
}
|
}
|
||||||
expected := uint32(windows.CREATE_NO_WINDOW | windows.CREATE_BREAKAWAY_FROM_JOB)
|
expected := uint32(windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP | windows.CREATE_BREAKAWAY_FROM_JOB)
|
||||||
if cmd.SysProcAttr.CreationFlags != expected {
|
if cmd.SysProcAttr.CreationFlags != expected {
|
||||||
t.Fatalf("expected a hidden breakaway helper without a console, got %#x", cmd.SysProcAttr.CreationFlags)
|
t.Fatalf("expected a detached breakaway helper isolated from Ctrl+C, got %#x", cmd.SysProcAttr.CreationFlags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user