@@ -36,7 +36,10 @@ const (
36
36
//go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 "kernel32.dll"
37
37
//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
38
38
//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll"
39
+ //go:cgo_import_dynamic runtime._OpenProcess OpenProcess%3 "kernel32.dll"
39
40
//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"
40
43
//go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll"
41
44
//go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll"
42
45
//go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode%1 "kernel32.dll"
84
87
_SetThreadContext ,
85
88
_LoadLibraryW ,
86
89
_LoadLibraryA ,
90
+ _OpenProcess ,
87
91
_PostQueuedCompletionStatus ,
92
+ _ProcessIdToSessionId ,
93
+ _QueryFullProcessImageNameA ,
88
94
_QueryPerformanceCounter ,
89
95
_QueryPerformanceFrequency ,
90
96
_ResumeThread ,
@@ -129,7 +135,8 @@ var (
129
135
// Load ntdll.dll manually during startup, otherwise Mingw
130
136
// links wrong printf function to cgo executable (see issue
131
137
// 12030 for details).
132
- _NtWaitForSingleObject stdFunction
138
+ _NtWaitForSingleObject stdFunction
139
+ _NtQueryInformationProcess stdFunction
133
140
134
141
// These are from non-kernel32.dll, so we prefer to LoadLibraryEx them.
135
142
_timeBeginPeriod ,
@@ -257,6 +264,7 @@ func loadOptionalSyscalls() {
257
264
throw ("ntdll.dll not found" )
258
265
}
259
266
_NtWaitForSingleObject = windowsFindfunc (n32 , []byte ("NtWaitForSingleObject\000 " ))
267
+ _NtQueryInformationProcess = windowsFindfunc (n32 , []byte ("NtQueryInformationProcess\000 " ))
260
268
261
269
if GOARCH == "arm" {
262
270
_QueryPerformanceCounter = windowsFindfunc (k32 , []byte ("QueryPerformanceCounter\000 " ))
@@ -997,6 +1005,63 @@ func usleep(us uint32) {
997
1005
onosstack (usleep2Addr , 10 * us )
998
1006
}
999
1007
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
+
1000
1065
func ctrlhandler1 (_type uint32 ) uint32 {
1001
1066
var s uint32
1002
1067
@@ -1012,7 +1077,11 @@ func ctrlhandler1(_type uint32) uint32 {
1012
1077
if sigsend (s ) {
1013
1078
return 1
1014
1079
}
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
+ }
1016
1085
return 0
1017
1086
}
1018
1087
0 commit comments