tailscale/util/winutil/zsyscall_windows.go
Aaron Klotz db39a43f06 util/winutil: add support for restarting Windows processes in specific sessions
This PR is all about adding functionality that will enable the installer's
upgrade sequence to terminate processes belonging to the previous version,
and then subsequently restart instances belonging to the new version within
the session(s) corresponding to the processes that were killed.

There are multiple parts to this:

* We add support for the Restart Manager APIs, which allow us to query the
  OS for a list of processes locking specific files;
* We add the RestartableProcess and RestartableProcesses types that query
  additional information about the running processes that will allow us
  to correctly restart them in the future. These types also provide the
  ability to terminate the processes.
* We add the StartProcessInSession family of APIs that permit us to create
  new processes within specific sessions. This is needed in order to
  properly attach a new GUI process to the same RDP session and desktop that
  its previously-terminated counterpart would have been running in.
* I tweaked the winutil token APIs again.
* A lot of this stuff is pretty hard to test without a very elaborate
  harness, but I added a unit test for the most complicated part (though it
  requires LocalSystem to run).

Updates https://github.com/tailscale/corp/issues/13998

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
2023-11-30 14:04:27 -08:00

115 lines
4.2 KiB
Go

// Code generated by 'go generate'; DO NOT EDIT.
package winutil
import (
"syscall"
"unsafe"
"github.com/dblohm7/wingoes"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return errERROR_EINVAL
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
modrstrtmgr = windows.NewLazySystemDLL("rstrtmgr.dll")
procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W")
procGetApplicationRestartSettings = modkernel32.NewProc("GetApplicationRestartSettings")
procRegisterApplicationRestart = modkernel32.NewProc("RegisterApplicationRestart")
procRmEndSession = modrstrtmgr.NewProc("RmEndSession")
procRmGetList = modrstrtmgr.NewProc("RmGetList")
procRmJoinSession = modrstrtmgr.NewProc("RmJoinSession")
procRmRegisterResources = modrstrtmgr.NewProc("RmRegisterResources")
procRmStartSession = modrstrtmgr.NewProc("RmStartSession")
)
func queryServiceConfig2(hService windows.Handle, infoLevel uint32, buf *byte, bufLen uint32, bytesNeeded *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procQueryServiceConfig2W.Addr(), 5, uintptr(hService), uintptr(infoLevel), uintptr(unsafe.Pointer(buf)), uintptr(bufLen), uintptr(unsafe.Pointer(bytesNeeded)), 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func getApplicationRestartSettings(process windows.Handle, commandLine *uint16, commandLineLen *uint32, flags *uint32) (ret wingoes.HRESULT) {
r0, _, _ := syscall.Syscall6(procGetApplicationRestartSettings.Addr(), 4, uintptr(process), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(commandLineLen)), uintptr(unsafe.Pointer(flags)), 0, 0)
ret = wingoes.HRESULT(r0)
return
}
func registerApplicationRestart(cmdLineExclExeName *uint16, flags uint32) (ret wingoes.HRESULT) {
r0, _, _ := syscall.Syscall(procRegisterApplicationRestart.Addr(), 2, uintptr(unsafe.Pointer(cmdLineExclExeName)), uintptr(flags), 0)
ret = wingoes.HRESULT(r0)
return
}
func rmEndSession(session _RMHANDLE) (ret error) {
r0, _, _ := syscall.Syscall(procRmEndSession.Addr(), 1, uintptr(session), 0, 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}
func rmGetList(session _RMHANDLE, nProcInfoNeeded *uint32, nProcInfo *uint32, rgAffectedApps *_RM_PROCESS_INFO, pRebootReasons *uint32) (ret error) {
r0, _, _ := syscall.Syscall6(procRmGetList.Addr(), 5, uintptr(session), uintptr(unsafe.Pointer(nProcInfoNeeded)), uintptr(unsafe.Pointer(nProcInfo)), uintptr(unsafe.Pointer(rgAffectedApps)), uintptr(unsafe.Pointer(pRebootReasons)), 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}
func rmJoinSession(pSession *_RMHANDLE, sessionKey *uint16) (ret error) {
r0, _, _ := syscall.Syscall(procRmJoinSession.Addr(), 2, uintptr(unsafe.Pointer(pSession)), uintptr(unsafe.Pointer(sessionKey)), 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}
func rmRegisterResources(session _RMHANDLE, nFiles uint32, rgsFileNames **uint16, nApplications uint32, rgApplications *_RM_UNIQUE_PROCESS, nServices uint32, rgsServiceNames **uint16) (ret error) {
r0, _, _ := syscall.Syscall9(procRmRegisterResources.Addr(), 7, uintptr(session), uintptr(nFiles), uintptr(unsafe.Pointer(rgsFileNames)), uintptr(nApplications), uintptr(unsafe.Pointer(rgApplications)), uintptr(nServices), uintptr(unsafe.Pointer(rgsServiceNames)), 0, 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}
func rmStartSession(pSession *_RMHANDLE, flags uint32, sessionKey *uint16) (ret error) {
r0, _, _ := syscall.Syscall(procRmStartSession.Addr(), 3, uintptr(unsafe.Pointer(pSession)), uintptr(flags), uintptr(unsafe.Pointer(sessionKey)))
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}