1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
14 "golang.org/x/sys/windows"
17 func allocSid(subAuth0 uint32) (*windows.SID, error) {
19 err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY,
20 1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid)
27 // IsAnInteractiveSession determines if calling process is running interactively.
28 // It queries the process token for membership in the Interactive group.
29 // http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s
31 // Deprecated: Use IsWindowsService instead.
32 func IsAnInteractiveSession() (bool, error) {
33 interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID)
37 defer windows.FreeSid(interSid)
39 serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID)
43 defer windows.FreeSid(serviceSid)
45 t, err := windows.OpenCurrentProcessToken()
51 gs, err := t.GetTokenGroups()
56 for _, g := range gs.AllGroups() {
57 if windows.EqualSid(g.Sid, interSid) {
60 if windows.EqualSid(g.Sid, serviceSid) {
68 ntdll = windows.NewLazySystemDLL("ntdll.dll")
69 _NtQueryInformationProcess = ntdll.NewProc("NtQueryInformationProcess")
71 kernel32 = windows.NewLazySystemDLL("kernel32.dll")
72 _QueryFullProcessImageNameA = kernel32.NewProc("QueryFullProcessImageNameA")
75 // IsWindowsService reports whether the process is currently executing
76 // as a Windows service.
77 func IsWindowsService() (bool, error) {
78 // This code was copied from runtime.isWindowsService function.
80 // The below technique looks a bit hairy, but it's actually
81 // exactly what the .NET framework does for the similarly named function:
82 // https://github.com/dotnet/extensions/blob/f4066026ca06984b07e90e61a6390ac38152ba93/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs#L26-L31
83 // Specifically, it looks up whether the parent process has session ID zero
84 // and is called "services".
85 const _CURRENT_PROCESS = ^uintptr(0)
86 // pbi is a PROCESS_BASIC_INFORMATION struct, where we just care about
87 // the 6th pointer inside of it, which contains the pid of the process
89 // https://github.com/wine-mirror/wine/blob/42cb7d2ad1caba08de235e6319b9967296b5d554/include/winternl.h#L1294
92 r0, _, _ := syscall.Syscall6(_NtQueryInformationProcess.Addr(), 5, _CURRENT_PROCESS, 0, uintptr(unsafe.Pointer(&pbi[0])), uintptr(unsafe.Sizeof(pbi)), uintptr(unsafe.Pointer(&pbiLen)), 0)
94 return false, errors.New("NtQueryInformationProcess failed: error=" + itoa(int(r0)))
97 err := windows.ProcessIdToSessionId(uint32(pbi[5]), &psid)
102 // parent session id should be 0 for service process
106 pproc, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pbi[5]))
110 defer windows.CloseHandle(pproc)
112 // exeName gets the path to the executable image of the parent process
113 var exeName [261]byte
114 exeNameLen := uint32(len(exeName) - 1)
115 r0, _, e0 := syscall.Syscall6(_QueryFullProcessImageNameA.Addr(), 4, uintptr(pproc), 0, uintptr(unsafe.Pointer(&exeName[0])), uintptr(unsafe.Pointer(&exeNameLen)), 0, 0)
120 return false, syscall.EINVAL
124 servicesLower = "services.exe"
125 servicesUpper = "SERVICES.EXE"
127 i := int(exeNameLen) - 1
128 j := len(servicesLower) - 1
134 return i == -1 || exeName[i] == '\\', nil
136 if exeName[i] != servicesLower[j] && exeName[i] != servicesUpper[j] {
144 func itoa(val int) string { // do it here rather than with fmt to avoid dependency
146 return "-" + itoa(-val)
148 var buf [32]byte // big enough for int64
151 buf[i] = byte(val%10 + '0')
155 buf[i] = byte(val + '0')
156 return string(buf[i:])