diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2021-05-25 16:14:32 +0200 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2021-06-18 11:28:50 +0200 |
commit | 1636a8c94b345c1af61d6bf786b33722c2ec2b07 (patch) | |
tree | ec6a3020c23bc0f9beb3913512c19acdbc88eb63 /manager/uiprocess.go | |
parent | main: log CLI to stderr/stdout (diff) | |
download | wireguard-windows-1636a8c94b345c1af61d6bf786b33722c2ec2b07.tar.xz wireguard-windows-1636a8c94b345c1af61d6bf786b33722c2ec2b07.zip |
manager: manually use CreateProcess for launching UI process
Go's standard library for this is buggy (PID races, handle races) and
requires passing NUL, which we don't really care about for Windows.
Simplify and speed up process creation by only passing exactly what we
need.
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'manager/uiprocess.go')
-rw-r--r-- | manager/uiprocess.go | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/manager/uiprocess.go b/manager/uiprocess.go new file mode 100644 index 00000000..80ac8b30 --- /dev/null +++ b/manager/uiprocess.go @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. + */ + +package manager + +import ( + "errors" + "runtime" + "sync/atomic" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +type uiProcess struct { + handle uintptr +} + +func launchUIProcess(executable string, args []string, workingDirectory string, handles []windows.Handle, token windows.Token) (*uiProcess, error) { + executable16, err := windows.UTF16PtrFromString(executable) + if err != nil { + return nil, err + } + args16, err := windows.UTF16PtrFromString(windows.ComposeCommandLine(args)) + if err != nil { + return nil, err + } + workingDirectory16, err := windows.UTF16PtrFromString(workingDirectory) + if err != nil { + return nil, err + } + var environmentBlock *uint16 + err = windows.CreateEnvironmentBlock(&environmentBlock, token, false) + if err != nil { + return nil, err + } + defer windows.DestroyEnvironmentBlock(environmentBlock) + attributeList, err := windows.NewProcThreadAttributeList(1) + if err != nil { + return nil, err + } + defer attributeList.Delete() + si := &windows.StartupInfoEx{ + StartupInfo: windows.StartupInfo{Cb: uint32(unsafe.Sizeof(windows.StartupInfoEx{}))}, + ProcThreadAttributeList: attributeList.List(), + } + if len(handles) == 0 { + handles = []windows.Handle{0} + } + attributeList.Update(windows.PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&handles[0]), uintptr(len(handles))*unsafe.Sizeof(handles[0])) + pi := new(windows.ProcessInformation) + err = windows.CreateProcessAsUser(token, executable16, args16, nil, nil, true, windows.CREATE_DEFAULT_ERROR_MODE|windows.CREATE_UNICODE_ENVIRONMENT|windows.EXTENDED_STARTUPINFO_PRESENT, environmentBlock, workingDirectory16, &si.StartupInfo, pi) + if err != nil { + return nil, err + } + windows.CloseHandle(pi.Thread) + uiProc := &uiProcess{handle: uintptr(pi.Process)} + runtime.SetFinalizer(uiProc, (*uiProcess).release) + return uiProc, nil +} + +func (p *uiProcess) release() error { + handle := windows.Handle(atomic.SwapUintptr(&p.handle, uintptr(windows.InvalidHandle))) + if handle == windows.InvalidHandle { + return nil + } + err := windows.CloseHandle(handle) + if err != nil { + return err + } + runtime.SetFinalizer(p, nil) + return nil +} + +func (p *uiProcess) Wait() (uint32, error) { + handle := windows.Handle(atomic.LoadUintptr(&p.handle)) + s, err := windows.WaitForSingleObject(handle, syscall.INFINITE) + switch s { + case windows.WAIT_OBJECT_0: + case windows.WAIT_FAILED: + return 0, err + default: + return 0, errors.New("unexpected result from WaitForSingleObject") + } + var exitCode uint32 + err = windows.GetExitCodeProcess(handle, &exitCode) + if err != nil { + return 0, err + } + p.release() + return exitCode, nil +} + +func (p *uiProcess) Kill() error { + return windows.TerminateProcess(windows.Handle(atomic.LoadUintptr(&p.handle)), 1) +} |