Skip to content

Commit 7456a46

Browse files
zx2c4dmitshur
authored andcommitted
[release-branch.go1.14] runtime: detect services in signal handler
The service handler needs to handle CTRL+C-like events -- including those sent by the service manager itself -- using the default Windows implementation if no signal handler from Go is already listening to those events. Ordinarily, the signal handler would call exit(2), but we actually need to allow this to be passed onward to the service handler. So, we detect if we're in a service and skip calling exit(2) in that case, just like we do for shared libraries. Updates #40167. Updates #40074. Fixes #40411. Change-Id: Ia77871737a80e1e94f85b02d26af1fd2f646af96 Reviewed-on: https://go-review.googlesource.com/c/go/+/244958 Run-TryBot: Jason A. Donenfeld <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Alex Brainman <[email protected]>
1 parent 17fd967 commit 7456a46

File tree

1 file changed

+71
-2
lines changed

1 file changed

+71
-2
lines changed

src/runtime/os_windows.go

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ const (
3636
//go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll"
3737
//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
3838
//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll"
39+
//go:cgo_import_dynamic runtime._OpenProcess OpenProcess%3 "kernel32.dll"
3940
//go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll"
41+
//go:cgo_import_dynamic runtime._ProcessIdToSessionId ProcessIdToSessionId%2 "kernel32.dll"
42+
//go:cgo_import_dynamic runtime._QueryFullProcessImageNameA QueryFullProcessImageNameA%4 "kernel32.dll"
4043
//go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll"
4144
//go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll"
4245
//go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode%1 "kernel32.dll"
@@ -84,7 +87,10 @@ var (
8487
_SetThreadContext,
8588
_LoadLibraryW,
8689
_LoadLibraryA,
90+
_OpenProcess,
8791
_PostQueuedCompletionStatus,
92+
_ProcessIdToSessionId,
93+
_QueryFullProcessImageNameA,
8894
_QueryPerformanceCounter,
8995
_QueryPerformanceFrequency,
9096
_ResumeThread,
@@ -129,7 +135,8 @@ var (
129135
// Load ntdll.dll manually during startup, otherwise Mingw
130136
// links wrong printf function to cgo executable (see issue
131137
// 12030 for details).
132-
_NtWaitForSingleObject stdFunction
138+
_NtWaitForSingleObject stdFunction
139+
_NtQueryInformationProcess stdFunction
133140

134141
// These are from non-kernel32.dll, so we prefer to LoadLibraryEx them.
135142
_timeBeginPeriod,
@@ -257,6 +264,7 @@ func loadOptionalSyscalls() {
257264
throw("ntdll.dll not found")
258265
}
259266
_NtWaitForSingleObject = windowsFindfunc(n32, []byte("NtWaitForSingleObject\000"))
267+
_NtQueryInformationProcess = windowsFindfunc(n32, []byte("NtQueryInformationProcess\000"))
260268

261269
if GOARCH == "arm" {
262270
_QueryPerformanceCounter = windowsFindfunc(k32, []byte("QueryPerformanceCounter\000"))
@@ -997,6 +1005,63 @@ func usleep(us uint32) {
9971005
onosstack(usleep2Addr, 10*us)
9981006
}
9991007

1008+
// isWindowsService returns whether the process is currently executing as a
1009+
// Windows service. The below technique looks a bit hairy, but it's actually
1010+
// exactly what the .NET framework does for the similarly named function:
1011+
// https://github.com/dotnet/extensions/blob/f4066026ca06984b07e90e61a6390ac38152ba93/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs#L26-L31
1012+
// Specifically, it looks up whether the parent process has session ID zero
1013+
// and is called "services".
1014+
func isWindowsService() bool {
1015+
const (
1016+
_CURRENT_PROCESS = ^uintptr(0)
1017+
_PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
1018+
)
1019+
// pbi is a PROCESS_BASIC_INFORMATION struct, where we just care about
1020+
// the 6th pointer inside of it, which contains the pid of the process
1021+
// parent:
1022+
// https://github.com/wine-mirror/wine/blob/42cb7d2ad1caba08de235e6319b9967296b5d554/include/winternl.h#L1294
1023+
var pbi [6]uintptr
1024+
var pbiLen uint32
1025+
err := stdcall5(_NtQueryInformationProcess, _CURRENT_PROCESS, 0, uintptr(unsafe.Pointer(&pbi[0])), uintptr(unsafe.Sizeof(pbi)), uintptr(unsafe.Pointer(&pbiLen)))
1026+
if err != 0 {
1027+
return false
1028+
}
1029+
var psid uint32
1030+
err = stdcall2(_ProcessIdToSessionId, pbi[5], uintptr(unsafe.Pointer(&psid)))
1031+
if err == 0 || psid != 0 {
1032+
return false
1033+
}
1034+
pproc := stdcall3(_OpenProcess, _PROCESS_QUERY_LIMITED_INFORMATION, 0, pbi[5])
1035+
if pproc == 0 {
1036+
return false
1037+
}
1038+
defer stdcall1(_CloseHandle, pproc)
1039+
// exeName gets the path to the executable image of the parent process
1040+
var exeName [261]byte
1041+
exeNameLen := uint32(len(exeName) - 1)
1042+
err = stdcall4(_QueryFullProcessImageNameA, pproc, 0, uintptr(unsafe.Pointer(&exeName[0])), uintptr(unsafe.Pointer(&exeNameLen)))
1043+
if err == 0 || exeNameLen == 0 {
1044+
return false
1045+
}
1046+
servicesLower := "services.exe"
1047+
servicesUpper := "SERVICES.EXE"
1048+
i := int(exeNameLen) - 1
1049+
j := len(servicesLower) - 1
1050+
if i < j {
1051+
return false
1052+
}
1053+
for {
1054+
if j == -1 {
1055+
return i == -1 || exeName[i] == '\\'
1056+
}
1057+
if exeName[i] != servicesLower[j] && exeName[i] != servicesUpper[j] {
1058+
return false
1059+
}
1060+
i--
1061+
j--
1062+
}
1063+
}
1064+
10001065
func ctrlhandler1(_type uint32) uint32 {
10011066
var s uint32
10021067

@@ -1012,7 +1077,11 @@ func ctrlhandler1(_type uint32) uint32 {
10121077
if sigsend(s) {
10131078
return 1
10141079
}
1015-
exit(2) // SIGINT, SIGTERM, etc
1080+
if !islibrary && !isarchive && !isWindowsService() {
1081+
// Only exit the program if we don't have a DLL or service.
1082+
// See https://golang.org/issues/35965 and https://golang.org/issues/40167
1083+
exit(2) // SIGINT, SIGTERM, etc
1084+
}
10161085
return 0
10171086
}
10181087

0 commit comments

Comments
 (0)