Codebase list golang-golang-x-sys / 280f808
windows/svc: add IsWindowsService function CL 244958 includes isWindowsService function that determines if a process is running as a service. The code of the function is based on public .Net implementation. IsAnInteractiveSession function implements similar functionality, but is based on an old Stackoverflow post., which is not as authoritative as code written by Microsoft for their official product. This change copies CL 244958 isWindowsService function into svc package and makes it public. The intention is that future users will preferĀ  IsWindowsService to IsAnInteractiveSession. Also this change adds "Deprecated" comment to IsAnInteractiveSession to point future users to IsWindowsService. Call to IsAnInteractiveSession is also replaced with IsWindowsService inĀ golang.org/x/sys/windows/svc/example package. Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434 Reviewed-on: https://go-review.googlesource.com/c/sys/+/259397 Run-TryBot: Alex Brainman <alex.brainman@gmail.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Trust: Brad Fitzpatrick <bradfitz@golang.org> Trust: Alex Brainman <alex.brainman@gmail.com> Alex Brainman 3 years ago
5 changed file(s) with 135 addition(s) and 3 deletion(s). Raw diff Collapse all Expand all
3535 func main() {
3636 const svcName = "myservice"
3737
38 isIntSess, err := svc.IsAnInteractiveSession()
38 inService, err := svc.IsWindowsService()
3939 if err != nil {
40 log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
40 log.Fatalf("failed to determine if we are running in service: %v", err)
4141 }
42 if !isIntSess {
42 if inService {
4343 runService(svcName, false)
4444 return
4545 }
66 package svc
77
88 import (
9 "errors"
10 "syscall"
11 "unsafe"
12
913 "golang.org/x/sys/windows"
1014 )
1115
2226 // IsAnInteractiveSession determines if calling process is running interactively.
2327 // It queries the process token for membership in the Interactive group.
2428 // http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s
29 //
30 // Deprecated: Use IsWindowsService instead.
2531 func IsAnInteractiveSession() (bool, error) {
2632 interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID)
2733 if err != nil {
5662 }
5763 return false, nil
5864 }
65
66 var (
67 ntdll = windows.NewLazySystemDLL("ntdll.dll")
68 _NtQueryInformationProcess = ntdll.NewProc("NtQueryInformationProcess")
69
70 kernel32 = windows.NewLazySystemDLL("kernel32.dll")
71 _QueryFullProcessImageNameA = kernel32.NewProc("QueryFullProcessImageNameA")
72 )
73
74 // IsWindowsService reports whether the process is currently executing
75 // as a Windows service.
76 func IsWindowsService() (bool, error) {
77 // This code was copied from runtime.isWindowsService function.
78
79 // The below technique looks a bit hairy, but it's actually
80 // exactly what the .NET framework does for the similarly named function:
81 // https://github.com/dotnet/extensions/blob/f4066026ca06984b07e90e61a6390ac38152ba93/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs#L26-L31
82 // Specifically, it looks up whether the parent process has session ID zero
83 // and is called "services".
84 const _CURRENT_PROCESS = ^uintptr(0)
85 // pbi is a PROCESS_BASIC_INFORMATION struct, where we just care about
86 // the 6th pointer inside of it, which contains the pid of the process
87 // parent:
88 // https://github.com/wine-mirror/wine/blob/42cb7d2ad1caba08de235e6319b9967296b5d554/include/winternl.h#L1294
89 var pbi [6]uintptr
90 var pbiLen uint32
91 r0, _, _ := syscall.Syscall6(_NtQueryInformationProcess.Addr(), 5, _CURRENT_PROCESS, 0, uintptr(unsafe.Pointer(&pbi[0])), uintptr(unsafe.Sizeof(pbi)), uintptr(unsafe.Pointer(&pbiLen)), 0)
92 if r0 != 0 {
93 return false, errors.New("NtQueryInformationProcess failed: error=" + itoa(int(r0)))
94 }
95 var psid uint32
96 err := windows.ProcessIdToSessionId(uint32(pbi[5]), &psid)
97 if err != nil {
98 return false, err
99 }
100 if psid != 0 {
101 // parent session id should be 0 for service process
102 return false, nil
103 }
104
105 pproc, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pbi[5]))
106 if err != nil {
107 return false, err
108 }
109 defer windows.CloseHandle(pproc)
110
111 // exeName gets the path to the executable image of the parent process
112 var exeName [261]byte
113 exeNameLen := uint32(len(exeName) - 1)
114 r0, _, e0 := syscall.Syscall6(_QueryFullProcessImageNameA.Addr(), 4, uintptr(pproc), 0, uintptr(unsafe.Pointer(&exeName[0])), uintptr(unsafe.Pointer(&exeNameLen)), 0, 0)
115 if r0 == 0 {
116 if e0 != 0 {
117 return false, e0
118 } else {
119 return false, syscall.EINVAL
120 }
121 }
122 const (
123 servicesLower = "services.exe"
124 servicesUpper = "SERVICES.EXE"
125 )
126 i := int(exeNameLen) - 1
127 j := len(servicesLower) - 1
128 if i < j {
129 return false, nil
130 }
131 for {
132 if j == -1 {
133 return i == -1 || exeName[i] == '\\', nil
134 }
135 if exeName[i] != servicesLower[j] && exeName[i] != servicesUpper[j] {
136 return false, nil
137 }
138 i--
139 j--
140 }
141 }
142
143 func itoa(val int) string { // do it here rather than with fmt to avoid dependency
144 if val < 0 {
145 return "-" + itoa(-val)
146 }
147 var buf [32]byte // big enough for int64
148 i := len(buf) - 1
149 for val >= 10 {
150 buf[i] = byte(val%10 + '0')
151 i--
152 val /= 10
153 }
154 buf[i] = byte(val + '0')
155 return string(buf[i:])
156 }
131131 t.Errorf("%q string does not contain %q", string(out), want)
132132 }
133133 }
134
135 func TestIsAnInteractiveSession(t *testing.T) {
136 isInteractive, err := svc.IsAnInteractiveSession()
137 if err != nil {
138 t.Fatal(err)
139 }
140 if !isInteractive {
141 t.Error("IsAnInteractiveSession retuns false when running interactively.")
142 }
143 }
144
145 func TestIsWindowsService(t *testing.T) {
146 isSvc, err := svc.IsWindowsService()
147 if err != nil {
148 t.Fatal(err)
149 }
150 if isSvc {
151 t.Error("IsWindowsService retuns true when not running in a service.")
152 }
153 }
269269 //sys RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW
270270 //sys RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegQueryValueExW
271271 //sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
272 //sys ProcessIdToSessionId(pid uint32, sessionid *uint32) (err error) = kernel32.ProcessIdToSessionId
272273 //sys GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode
273274 //sys SetConsoleMode(console Handle, mode uint32) (err error) = kernel32.SetConsoleMode
274275 //sys GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) = kernel32.GetConsoleScreenBufferInfo
179179 procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW")
180180 procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW")
181181 procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
182 procProcessIdToSessionId = modkernel32.NewProc("ProcessIdToSessionId")
182183 procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
183184 procSetConsoleMode = modkernel32.NewProc("SetConsoleMode")
184185 procGetConsoleScreenBufferInfo = modkernel32.NewProc("GetConsoleScreenBufferInfo")
19421943 return
19431944 }
19441945
1946 func ProcessIdToSessionId(pid uint32, sessionid *uint32) (err error) {
1947 r1, _, e1 := syscall.Syscall(procProcessIdToSessionId.Addr(), 2, uintptr(pid), uintptr(unsafe.Pointer(sessionid)), 0)
1948 if r1 == 0 {
1949 if e1 != 0 {
1950 err = errnoErr(e1)
1951 } else {
1952 err = syscall.EINVAL
1953 }
1954 }
1955 return
1956 }
1957
19451958 func GetConsoleMode(console Handle, mode *uint32) (err error) {
19461959 r1, _, e1 := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0)
19471960 if r1 == 0 {