[sys] windows/svc: reimplement IsAnInteractiveSession

72 views
Skip to first unread message

Alex Brainman (Gerrit)

unread,
Oct 3, 2020, 6:38:07 PM10/3/20
to Jason A. Donenfeld, Brad Fitzpatrick, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Alex Brainman would like Jason A. Donenfeld and Brad Fitzpatrick to review this change.

View Change

windows/svc: reimplement IsAnInteractiveSession

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.

Current IsAnInteractiveSession implementation is based on a
Stackoverflow post, which is not as authoritative as code written by
Microsoft for their official product.

This change replaces IsAnInteractiveSession with a code similar to
CL 244958.

Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
---
M windows/svc/security.go
M windows/svc/svc_test.go
M windows/syscall_windows.go
M windows/zsyscall_windows.go
4 files changed, 104 insertions(+), 31 deletions(-)

diff --git a/windows/svc/security.go b/windows/svc/security.go
index 6502599..d3888aa 100644
--- a/windows/svc/security.go
+++ b/windows/svc/security.go
@@ -7,53 +7,102 @@
package svc

import (
+ "errors"
+ "syscall"
+ "unsafe"
+
"golang.org/x/sys/windows"
)

-func allocSid(subAuth0 uint32) (*windows.SID, error) {
- var sid *windows.SID
- err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY,
- 1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid)
- if err != nil {
- return nil, err
- }
- return sid, nil
-}
+var (
+ ntdll = windows.NewLazySystemDLL("ntdll.dll")
+ _NtQueryInformationProcess = ntdll.NewProc("NtQueryInformationProcess")
+
+ kernel32 = windows.NewLazySystemDLL("kernel32.dll")
+ _QueryFullProcessImageNameA = kernel32.NewProc("QueryFullProcessImageNameA")
+)

// IsAnInteractiveSession determines if calling process is running interactively.
-// It queries the process token for membership in the Interactive group.
-// http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s
func IsAnInteractiveSession() (bool, error) {
- interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID)
+ ret, err := isWindowsService()
+ return !ret, err
+}
+
+// isWindowsService returns whether the process is currently executing as a
+// Windows service. The below technique looks a bit hairy, but it's actually
+// exactly what the .NET framework does for the similarly named function:
+// https://github.com/dotnet/extensions/blob/f4066026ca06984b07e90e61a6390ac38152ba93/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs#L26-L31
+// Specifically, it looks up whether the parent process has session ID zero
+// and is called "services".
+func isWindowsService() (bool, error) {
+ const _CURRENT_PROCESS = ^uintptr(0)
+ // pbi is a PROCESS_BASIC_INFORMATION struct, where we just care about
+ // the 6th pointer inside of it, which contains the pid of the process
+ // parent:
+ // https://github.com/wine-mirror/wine/blob/42cb7d2ad1caba08de235e6319b9967296b5d554/include/winternl.h#L1294
+ var pbi [6]uintptr
+ var pbiLen uint32
+ r0, _, _ := syscall.Syscall6(_NtQueryInformationProcess.Addr(), 5, _CURRENT_PROCESS, 0, uintptr(unsafe.Pointer(&pbi[0])), uintptr(unsafe.Sizeof(pbi)), uintptr(unsafe.Pointer(&pbiLen)), 0)
+ if r0 != 0 {
+ return false, errors.New("NtQueryInformationProcess failed: error=" + itoa(int(r0)))
+ }
+ var psid uint32
+ err := windows.ProcessIdToSessionId(uint32(pbi[5]), &psid)
if err != nil {
return false, err
}
- defer windows.FreeSid(interSid)
+ if psid != 0 {
+ // parent session id should be 0 for service process
+ return false, nil
+ }

- serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID)
+ pproc, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pbi[5]))
if err != nil {
return false, err
}
- defer windows.FreeSid(serviceSid)
+ defer windows.CloseHandle(pproc)

- t, err := windows.OpenCurrentProcessToken()
- if err != nil {
- return false, err
- }
- defer t.Close()
-
- gs, err := t.GetTokenGroups()
- if err != nil {
- return false, err
- }
-
- for _, g := range gs.AllGroups() {
- if windows.EqualSid(g.Sid, interSid) {
- return true, nil
+ // exeName gets the path to the executable image of the parent process
+ var exeName [261]byte
+ exeNameLen := uint32(len(exeName) - 1)
+ r0, _, e0 := syscall.Syscall6(_QueryFullProcessImageNameA.Addr(), 4, uintptr(pproc), 0, uintptr(unsafe.Pointer(&exeName[0])), uintptr(unsafe.Pointer(&exeNameLen)), 0, 0)
+ if r0 == 0 {
+ if e0 != 0 {
+ return false, e0
+ } else {
+ return false, syscall.EINVAL
}
- if windows.EqualSid(g.Sid, serviceSid) {
+ }
+ servicesLower := "services.exe"
+ servicesUpper := "SERVICES.EXE"
+ i := int(exeNameLen) - 1
+ j := len(servicesLower) - 1
+ if i < j {
+ return false, nil
+ }
+ for {
+ if j == -1 {
+ return i == -1 || exeName[i] == '\\', nil
+ }
+ if exeName[i] != servicesLower[j] && exeName[i] != servicesUpper[j] {
return false, nil
}
+ i--
+ j--
}
- return false, nil
+}
+
+func itoa(val int) string { // do it here rather than with fmt to avoid dependency
+ if val < 0 {
+ return "-" + itoa(-val)
+ }
+ var buf [32]byte // big enough for int64
+ i := len(buf) - 1
+ for val >= 10 {
+ buf[i] = byte(val%10 + '0')
+ i--
+ val /= 10
+ }
+ buf[i] = byte(val + '0')
+ return string(buf[i:])
}
diff --git a/windows/svc/svc_test.go b/windows/svc/svc_test.go
index 3a80e48..130f046 100644
--- a/windows/svc/svc_test.go
+++ b/windows/svc/svc_test.go
@@ -132,3 +132,13 @@
t.Errorf("%q string does not contain %q", string(out), want)
}
}
+
+func TestIsAnInteractiveSession(t *testing.T) {
+ isInteractive, err := svc.IsAnInteractiveSession()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !isInteractive {
+ t.Error("TestIsAnInteractiveSession retuns false when running interactively.")
+ }
+}
diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go
index 2aa29e8..37dcc57 100644
--- a/windows/syscall_windows.go
+++ b/windows/syscall_windows.go
@@ -270,6 +270,7 @@
//sys RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW
//sys RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegQueryValueExW
//sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
+//sys ProcessIdToSessionId(pid uint32, sessionid *uint32) (err error) = kernel32.ProcessIdToSessionId
//sys GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode
//sys SetConsoleMode(console Handle, mode uint32) (err error) = kernel32.SetConsoleMode
//sys GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) = kernel32.GetConsoleScreenBufferInfo
diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go
index 347f13d..121c658 100644
--- a/windows/zsyscall_windows.go
+++ b/windows/zsyscall_windows.go
@@ -180,6 +180,7 @@
procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW")
procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW")
procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
+ procProcessIdToSessionId = modkernel32.NewProc("ProcessIdToSessionId")
procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
procSetConsoleMode = modkernel32.NewProc("SetConsoleMode")
procGetConsoleScreenBufferInfo = modkernel32.NewProc("GetConsoleScreenBufferInfo")
@@ -1942,6 +1943,18 @@
return
}

+func ProcessIdToSessionId(pid uint32, sessionid *uint32) (err error) {
+ r1, _, e1 := syscall.Syscall(procProcessIdToSessionId.Addr(), 2, uintptr(pid), uintptr(unsafe.Pointer(sessionid)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
func GetConsoleMode(console Handle, mode *uint32) (err error) {
r1, _, e1 := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0)
if r1 == 0 {

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 1
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
Gerrit-MessageType: newchange

Alex Brainman (Gerrit)

unread,
Oct 3, 2020, 6:41:03 PM10/3/20
to goph...@pubsubhelper.golang.org, Jason A. Donenfeld, Brad Fitzpatrick, golang-co...@googlegroups.com

Patch set 1:Run-TryBot +1Trust +1

View Change

1 comment:

  • Patchset:

    • Patch Set #1:

      Jason,

      I hope you like this change. Brad prefers your copy of .Net code to what I found on the Stackoverflow long time ago.

      Alex

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 1
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Comment-Date: Sat, 03 Oct 2020 22:40:56 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment

Brad Fitzpatrick (Gerrit)

unread,
Oct 3, 2020, 10:19:49 PM10/3/20
to Alex Brainman, goph...@pubsubhelper.golang.org, Go Bot, Jason A. Donenfeld, Brad Fitzpatrick, golang-co...@googlegroups.com

View Change

1 comment:

    • // IsAnInteractiveSession determines if calling process is running interactively.

    • func IsAnInteractiveSession() (bool, error) {
      ret, err := isWindowsService()

      The docs and the implementation don't (naively) seem to agree with each other.

      If I were looking for a function that reported whether I was being run as a service, it's not obvious to me (or from these docs) that "is an interactive session" was the inverse of "running as a service".

      That is, are there are only two contexts that a Windows program can run in: service vs interactive?

      It's clear to me that a service is not interactive, but I can think of other types of process (e.g. a non-GUI subprocess of a GUI process) that are neither interactive nor services. Did "IsAnInteractiveSession" previously return true for those, but won't now?

      This CL deletes the docs with the stackoverflow link that even suggested this was about service-or-not. I guess this is in the "svc" package, but some more explicit docs would be nice.

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 1
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Alex Brainman <alex.b...@gmail.com>
Gerrit-Comment-Date: Sun, 04 Oct 2020 02:19:43 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Gerrit-MessageType: comment

Alex Brainman (Gerrit)

unread,
Oct 3, 2020, 11:47:18 PM10/3/20
to goph...@pubsubhelper.golang.org, Go Bot, Jason A. Donenfeld, Brad Fitzpatrick, golang-co...@googlegroups.com

View Change

2 comments:

  • Patchset:

  • File windows/svc/security.go:

    • Patch Set #1, Line 25:

      // IsAnInteractiveSession determines if calling process is running interactively.
      func IsAnInteractiveSession() (bool, error) {
      ret, err := isWindowsService()

    • The docs and the implementation don't (naively) seem to agree with each other. […]

      Before I start adding docs, here is another suggestion.

      We can just add new IsWindowsService function instead.

      And leave IsAnInteractiveSession function alone. We will also add DEPRETIATED in IsAnInteractiveSession docs, and make them refer to new IsWindowsService instead.

      The downside of this approach, is that, obviously, none of existing people code will use IsWindowsService. I don't see this as a big deal, because IsAnInteractiveSession actually works properly. I have not anyone report issue with IsAnInteractiveSession.

      I agree that IsAnInteractiveSession is not a good name for a package about Windows service. I take the blame for it. I just copied the code from Stackoverflow, and kept the name.

      We can also just drop this CL altogether and continue to use IsAnInteractiveSession as before.

      Let me know.

      Thank you.

      Alex

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 1
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Comment-Date: Sun, 04 Oct 2020 03:47:11 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Brad Fitzpatrick <brad...@golang.org>
Gerrit-MessageType: comment

Brad Fitzpatrick (Gerrit)

unread,
Oct 4, 2020, 12:04:48 AM10/4/20
to Alex Brainman, goph...@pubsubhelper.golang.org, Go Bot, Jason A. Donenfeld, Brad Fitzpatrick, golang-co...@googlegroups.com

View Change

1 comment:

  • File windows/svc/security.go:

    • Patch Set #1, Line 25:

      // IsAnInteractiveSession determines if calling process is running interactively.
      func IsAnInteractiveSession() (bool, error) {
      ret, err := isWindowsService()

    • Before I start adding docs, here is another suggestion. […]

      Adding a new function like "RunningInService" sounds fine (the code in the Go runtime is what I want to use and trust, so copying it to a public place like this package is good), but the downside to having two functions (even if one is deprecated) is we'd then want to document a bit what the difference between them are.

      Documenting the old one as "basically the opposite of the other one, but kinda a different implementation, I dunno" isn't great documentation, but might be sufficient if you can find some slightly better explanation.

      Also, the main reason I want to use the Go runtime implementation is because I understand and trust it, since it's the same implementation in dotnet.

      Maybe for the old documentation, you can just explain in English exactly how it works implementation-wise. But looking at the implementation, I feel like you should do two passes over the SID list first and first return false if it contains service, and then do a pass looking for interactive (to return true), and then finally return false as the fallback.

      Otherwise for a process in both interactive and service (such a services with the "Interact with Desktop" bit: https://docs.microsoft.com/en-us/windows/win32/services/interactive-services), the order in the list seems to matter, which seems random-ish.

      So I'd fix that implementation and say that it "reports true if the current process is both a member of the Interactive group and not a member of the service group", perhaps with polished wording.

      Then adding RunningInService isn't redundant too.

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 1
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Alex Brainman <alex.b...@gmail.com>
Gerrit-Comment-Date: Sun, 04 Oct 2020 04:04:41 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Alex Brainman <alex.b...@gmail.com>

Alex Brainman (Gerrit)

unread,
Oct 4, 2020, 12:28:53 AM10/4/20
to goph...@pubsubhelper.golang.org, Go Bot, Jason A. Donenfeld, Brad Fitzpatrick, golang-co...@googlegroups.com

View Change

1 comment:

  • File windows/svc/security.go:

    • Patch Set #1, Line 25:

      // IsAnInteractiveSession determines if calling process is running interactively.
      func IsAnInteractiveSession() (bool, error) {
      ret, err := isWindowsService()

    • Adding a new function like "RunningInService" sounds fine (the code in the Go runtime is what I want to use and trust, so copying it to a public place like this package is good), but the downside to having two functions (even if one is deprecated) is we'd then want to document a bit what the difference between them are.

    • Documenting the old one as "basically the opposite of the other one, but kinda a different implementation, I dunno" isn't great documentation, but might be sufficient if you can find some slightly better explanation.

      Also, the main reason I want to use the Go runtime implementation is because I understand and trust it, since it's the same implementation in dotnet.

      Maybe for the old documentation, you can just explain in English exactly how it works implementation-wise. But looking at the implementation, I feel like you should do two passes over the SID list first and first return false if it contains service, and then do a pass looking for interactive (to return true), and then finally return false as the fallback.

      Otherwise for a process in both interactive and service (such a services with the "Interact with Desktop" bit: https://docs.microsoft.com/en-us/windows/win32/services/interactive-services), the order in the list seems to matter, which seems random-ish.

      So I'd fix that implementation and say that it "reports true if the current process is both a member of the Interactive group and not a member of the service group", perhaps with polished wording.

      Then adding RunningInService isn't redundant too.

    • I am not sure that changing IsAnInteractiveSession implementation is a good idea - whatever that change is. We don't know what code we will break. And it is not easy to debug services, so breaking code that uses IsAnInteractiveSession is double bad.

      And personally I never used IsAnInteractiveSession for anything but to determine, if running in a Windows service. I think that extending IsAnInteractiveSession usage just because its name means something else is not good idea.

      I am happy to add more documentation to IsAnInteractiveSession, maybe I will find a way to explain why we need new function.

      Do you want new function called svc.IsWindowsService or svc.RunningInService or svc.IsRunningInService or something else? We better come up with a good name this time around.

      I am not worried about depreciating IsAnInteractiveSession. This package public API is not very large. So people will read the documentation.

      Alex

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 1
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Comment-Date: Sun, 04 Oct 2020 04:28:46 +0000

Alex Brainman (Gerrit)

unread,
Oct 4, 2020, 2:26:39 AM10/4/20
to Jason A. Donenfeld, Go Bot, Brad Fitzpatrick, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Alex Brainman uploaded patch set #2 to this change.

View Change

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
---
M windows/svc/example/main.go

M windows/svc/security.go
M windows/svc/svc_test.go
M windows/syscall_windows.go
M windows/zsyscall_windows.go
5 files changed, 130 insertions(+), 3 deletions(-)

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 2
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
Gerrit-MessageType: newpatchset

Alex Brainman (Gerrit)

unread,
Oct 4, 2020, 2:27:08 AM10/4/20
to goph...@pubsubhelper.golang.org, Go Bot, Jason A. Donenfeld, Brad Fitzpatrick, golang-co...@googlegroups.com

Patch set 2:Run-TryBot +1

View Change

1 comment:

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 2
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Comment-Date: Sun, 04 Oct 2020 06:27:01 +0000

Jason A. Donenfeld (Gerrit)

unread,
Oct 4, 2020, 5:07:26 AM10/4/20
to Alex Brainman, goph...@pubsubhelper.golang.org, Go Bot, Brad Fitzpatrick, golang-co...@googlegroups.com

View Change

1 comment:

  • Patchset:

    • Patch Set #2:

      What are you actually trying to use this for? I'm not totally convinced that IsWindowsService, even as .NET uses it, is foolproof.

      What's your end goal?

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 2
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Alex Brainman <alex.b...@gmail.com>
Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Comment-Date: Sun, 04 Oct 2020 09:07:18 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Gerrit-MessageType: comment

Brad Fitzpatrick (Gerrit)

unread,
Oct 5, 2020, 12:18:43 AM10/5/20
to Alex Brainman, goph...@pubsubhelper.golang.org, Go Bot, Jason A. Donenfeld, Brad Fitzpatrick, golang-co...@googlegroups.com

View Change

1 comment:

  • File windows/svc/security.go:

    • Patch Set #2, Line 76: // Windows service. The below technique looks a bit hairy, but it's actually

      Move this second sentence "The below technique" into the body of the function instead so it's not part of the godoc.

      You can also reference the Go runtime function from the comment inside the func.

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 2
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Alex Brainman <alex.b...@gmail.com>
Gerrit-Comment-Date: Mon, 05 Oct 2020 04:18:37 +0000

Brad Fitzpatrick (Gerrit)

unread,
Oct 5, 2020, 12:21:34 AM10/5/20
to Alex Brainman, goph...@pubsubhelper.golang.org, Go Bot, Jason A. Donenfeld, Brad Fitzpatrick, golang-co...@googlegroups.com

View Change

1 comment:

  • Patchset:

    • Patch Set #2:

      What are you actually trying to use this for? I'm not totally convinced that IsWindowsService, even […]

      Alex wants a process to be able to determine at start-up whether it's being run "by hand" (e.g. cmd.exe) or as a service (so it can do the whole svc.Handler thing), without resorting to the user passing a flag to say what mode to run as.

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 2
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Alex Brainman <alex.b...@gmail.com>
Gerrit-Comment-Date: Mon, 05 Oct 2020 04:21:29 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-MessageType: comment

Alex Brainman (Gerrit)

unread,
Oct 5, 2020, 1:45:20 AM10/5/20
to goph...@pubsubhelper.golang.org, Go Bot, Jason A. Donenfeld, Brad Fitzpatrick, golang-co...@googlegroups.com

View Change

3 comments:

  • Patchset:

    • Patch Set #2:

      Alex wants a process to be able to determine at start-up whether it's being run "by hand" (e.g. cmd. […]

      Jason, I have single executable that I want to run as both command line app and as a Windows service. Command line app and the service, obviously, need to do different things / execute different code. So I need some way to determine if my process is running as command line app or as a service.

      I plan to call svc.IsWindowsService from my executable to determine, if the process is started as a service or as command line / GUI app.

      Something similar to what we already do in golang.org/x/sys/windows/svc/example.

      I always used golang.org/x/sys/windows/svc.IsAnInteractiveSession for the same purpose, but Brad prefers your version from CL 244958.

      Your CL 244958 runtime.isWindowsService serves the same purpose, but we cannot access private runtime.isWindowsService function from our normal code. So I copied it here.

    • Patch Set #2:

      Thank you fro review.

      Alex

  • File windows/svc/security.go:

    • Move this second sentence "The below technique" into the body of the function instead so it's not pa […]

      Done

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 2
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Comment-Date: Mon, 05 Oct 2020 05:45:12 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Jason A. Donenfeld <Ja...@zx2c4.com>

Alex Brainman (Gerrit)

unread,
Oct 5, 2020, 1:45:28 AM10/5/20
to Jason A. Donenfeld, Go Bot, Brad Fitzpatrick, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Alex Brainman uploaded patch set #3 to this change.

View Change

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
---
M windows/svc/example/main.go
M windows/svc/security.go
M windows/svc/svc_test.go
M windows/syscall_windows.go
M windows/zsyscall_windows.go
5 files changed, 132 insertions(+), 3 deletions(-)

To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: sys
Gerrit-Branch: master
Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
Gerrit-Change-Number: 259397
Gerrit-PatchSet: 3
Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
Gerrit-MessageType: newpatchset

Alex Brainman (Gerrit)

unread,
Oct 5, 2020, 1:45:49 AM10/5/20
to goph...@pubsubhelper.golang.org, Go Bot, Jason A. Donenfeld, Brad Fitzpatrick, golang-co...@googlegroups.com

Patch set 3:Run-TryBot +1

View Change

    To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

    Gerrit-Project: sys
    Gerrit-Branch: master
    Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
    Gerrit-Change-Number: 259397
    Gerrit-PatchSet: 3
    Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
    Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Go Bot <go...@golang.org>
    Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
    Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
    Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Comment-Date: Mon, 05 Oct 2020 05:45:42 +0000
    Gerrit-HasComments: No
    Gerrit-Has-Labels: Yes
    Gerrit-MessageType: comment

    Brad Fitzpatrick (Gerrit)

    unread,
    Oct 7, 2020, 12:34:53 PM10/7/20
    to Alex Brainman, goph...@pubsubhelper.golang.org, Ian Lance Taylor, Brad Fitzpatrick, Go Bot, Jason A. Donenfeld, golang-co...@googlegroups.com

    Patch set 3:Code-Review +1Trust +1

    View Change

    3 comments:

    • File windows/svc/security.go:

      • Patch Set #3, Line 75: // IsWindowsService returns whether the process is currently executing

        Go style is "reports whether"

      • Patch Set #3, Line 83: // This code was copied from runtime.isWindowsService function.

        move this comment to the top of the func body, followed by a blank line.

      • Patch Set #3, Line 122: servicesLower := "services.exe"

        slight preference for these being const, but if you want to match runtime exactly, that's fine too.

        Except IIRC I also left this comment on the runtime CL that added this.

    To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

    Gerrit-Project: sys
    Gerrit-Branch: master
    Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
    Gerrit-Change-Number: 259397
    Gerrit-PatchSet: 3
    Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
    Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Go Bot <go...@golang.org>
    Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
    Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
    Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
    Gerrit-Attention: Alex Brainman <alex.b...@gmail.com>
    Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
    Gerrit-Comment-Date: Wed, 07 Oct 2020 16:34:47 +0000

    Ian Lance Taylor (Gerrit)

    unread,
    Oct 7, 2020, 12:48:42 PM10/7/20
    to Alex Brainman, goph...@pubsubhelper.golang.org, Brad Fitzpatrick, Go Bot, Jason A. Donenfeld, golang-co...@googlegroups.com

    Patch set 3:Code-Review +2

    View Change

    1 comment:

    To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

    Gerrit-Project: sys
    Gerrit-Branch: master
    Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
    Gerrit-Change-Number: 259397
    Gerrit-PatchSet: 3
    Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
    Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Go Bot <go...@golang.org>
    Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
    Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
    Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
    Gerrit-Attention: Alex Brainman <alex.b...@gmail.com>
    Gerrit-Comment-Date: Wed, 07 Oct 2020 16:48:38 +0000

    Alex Brainman (Gerrit)

    unread,
    Oct 8, 2020, 2:27:50 AM10/8/20
    to goph...@pubsubhelper.golang.org, Ian Lance Taylor, Brad Fitzpatrick, Go Bot, Jason A. Donenfeld, golang-co...@googlegroups.com

    View Change

    4 comments:

      • Thank you for review.

        Alex

    • File windows/svc/security.go:

      • Patch Set #3, Line 75: // IsWindowsService returns whether the process is currently executing

        Go style is "reports whether"

      • Done

      • Patch Set #3, Line 83: // This code was copied from runtime.isWindowsService function.

        move this comment to the top of the func body, followed by a blank line.

      • Done

      • slight preference for these being const, but if you want to match runtime exactly, that's fine too. […]

        Done here.

        Too bad for runtime CL. This code only exists on release-branch. I doubt we will be allowed to adjust our release-branch commit.

    To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

    Gerrit-Project: sys
    Gerrit-Branch: master
    Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
    Gerrit-Change-Number: 259397
    Gerrit-PatchSet: 3
    Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
    Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Go Bot <go...@golang.org>
    Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
    Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
    Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
    Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Comment-Date: Thu, 08 Oct 2020 06:27:45 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No

    Alex Brainman (Gerrit)

    unread,
    Oct 8, 2020, 2:28:03 AM10/8/20
    to Jason A. Donenfeld, Go Bot, Brad Fitzpatrick, Ian Lance Taylor, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

    Alex Brainman uploaded patch set #4 to this change.

    View Change

    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
    ---
    M windows/svc/example/main.go
    M windows/svc/security.go
    M windows/svc/svc_test.go
    M windows/syscall_windows.go
    M windows/zsyscall_windows.go
    5 files changed, 135 insertions(+), 3 deletions(-)

    To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

    Gerrit-Project: sys
    Gerrit-Branch: master
    Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
    Gerrit-Change-Number: 259397
    Gerrit-PatchSet: 4
    Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
    Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
    Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-Reviewer: Go Bot <go...@golang.org>
    Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
    Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
    Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
    Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
    Gerrit-MessageType: newpatchset

    Alex Brainman (Gerrit)

    unread,
    Oct 8, 2020, 2:28:24 AM10/8/20
    to goph...@pubsubhelper.golang.org, Ian Lance Taylor, Brad Fitzpatrick, Go Bot, Jason A. Donenfeld, golang-co...@googlegroups.com

    Patch set 4:Run-TryBot +1

    View Change

      To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: sys
      Gerrit-Branch: master
      Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
      Gerrit-Change-Number: 259397
      Gerrit-PatchSet: 4
      Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
      Gerrit-Reviewer: Go Bot <go...@golang.org>
      Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
      Gerrit-Attention: Jason A. Donenfeld <Ja...@zx2c4.com>
      Gerrit-Attention: Brad Fitzpatrick <brad...@golang.org>
      Gerrit-Comment-Date: Thu, 08 Oct 2020 06:28:19 +0000

      Alex Brainman (Gerrit)

      unread,
      Oct 8, 2020, 2:31:31 AM10/8/20
      to goph...@pubsubhelper.golang.org, golang-...@googlegroups.com, Go Bot, Ian Lance Taylor, Brad Fitzpatrick, Jason A. Donenfeld, golang-co...@googlegroups.com

      Alex Brainman submitted this change.

      View Change

      Approvals: Ian Lance Taylor: Looks good to me, approved Brad Fitzpatrick: Trusted Alex Brainman: Trusted; Run TryBots Go Bot: TryBots succeeded
      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.b...@gmail.com>
      TryBot-Result: Go Bot <go...@golang.org>
      Reviewed-by: Ian Lance Taylor <ia...@golang.org>
      Trust: Brad Fitzpatrick <brad...@golang.org>
      Trust: Alex Brainman <alex.b...@gmail.com>

      ---
      M windows/svc/example/main.go
      M windows/svc/security.go
      M windows/svc/svc_test.go
      M windows/syscall_windows.go
      M windows/zsyscall_windows.go
      5 files changed, 135 insertions(+), 3 deletions(-)

      diff --git a/windows/svc/example/main.go b/windows/svc/example/main.go
      index dc96c08..9bf9bbd 100644
      --- a/windows/svc/example/main.go
      +++ b/windows/svc/example/main.go
      @@ -36,11 +36,11 @@
      func main() {
      const svcName = "myservice"

      - isIntSess, err := svc.IsAnInteractiveSession()
      + inService, err := svc.IsWindowsService()
      if err != nil {
      - log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
      + log.Fatalf("failed to determine if we are running in service: %v", err)
      }
      - if !isIntSess {
      + if inService {
      runService(svcName, false)
      return
      }
      diff --git a/windows/svc/security.go b/windows/svc/security.go
      index 6502599..da6df1d 100644
      --- a/windows/svc/security.go
      +++ b/windows/svc/security.go
      @@ -7,6 +7,10 @@

      package svc

      import (
      + "errors"
      + "syscall"
      + "unsafe"
      +
      "golang.org/x/sys/windows"
      )

      @@ -23,6 +27,8 @@

      // IsAnInteractiveSession determines if calling process is running interactively.
       // It queries the process token for membership in the Interactive group.
       // http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s
      +//
      +// Deprecated: Use IsWindowsService instead.
      func IsAnInteractiveSession() (bool, error) {
      interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID)
      if err != nil {
      @@ -57,3 +63,95 @@
      }
      return false, nil
      }
      +

      +var (
      + ntdll = windows.NewLazySystemDLL("ntdll.dll")
      + _NtQueryInformationProcess = ntdll.NewProc("NtQueryInformationProcess")
      +
      + kernel32 = windows.NewLazySystemDLL("kernel32.dll")
      + _QueryFullProcessImageNameA = kernel32.NewProc("QueryFullProcessImageNameA")
      +)
      +
      +// IsWindowsService reports whether the process is currently executing
      +// as a Windows service.
      +func IsWindowsService() (bool, error) {
      + // This code was copied from runtime.isWindowsService function.
      +
      + // The below technique looks a bit hairy, but it's actually
      + // exactly what the .NET framework does for the similarly named function:
      + // https://github.com/dotnet/extensions/blob/f4066026ca06984b07e90e61a6390ac38152ba93/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs#L26-L31
      + // Specifically, it looks up whether the parent process has session ID zero
      + // and is called "services".

      + const _CURRENT_PROCESS = ^uintptr(0)
      + // pbi is a PROCESS_BASIC_INFORMATION struct, where we just care about
      + // the 6th pointer inside of it, which contains the pid of the process
      + // parent:
      + // https://github.com/wine-mirror/wine/blob/42cb7d2ad1caba08de235e6319b9967296b5d554/include/winternl.h#L1294
      + var pbi [6]uintptr
      + var pbiLen uint32
      + r0, _, _ := syscall.Syscall6(_NtQueryInformationProcess.Addr(), 5, _CURRENT_PROCESS, 0, uintptr(unsafe.Pointer(&pbi[0])), uintptr(unsafe.Sizeof(pbi)), uintptr(unsafe.Pointer(&pbiLen)), 0)
      + if r0 != 0 {
      + return false, errors.New("NtQueryInformationProcess failed: error=" + itoa(int(r0)))
      + }
      + var psid uint32
      + err := windows.ProcessIdToSessionId(uint32(pbi[5]), &psid)
      +	if err != nil {
      +		return false, err
      + }

      + if psid != 0 {
      + // parent session id should be 0 for service process
      + return false, nil
      + }
      +

      + pproc, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pbi[5]))
      +	if err != nil {
      +		return false, err
      + }
      + defer windows.CloseHandle(pproc)
      +

      + // exeName gets the path to the executable image of the parent process
      + var exeName [261]byte
      + exeNameLen := uint32(len(exeName) - 1)
      + r0, _, e0 := syscall.Syscall6(_QueryFullProcessImageNameA.Addr(), 4, uintptr(pproc), 0, uintptr(unsafe.Pointer(&exeName[0])), uintptr(unsafe.Pointer(&exeNameLen)), 0, 0)
      + if r0 == 0 {
      + if e0 != 0 {
      + return false, e0
      + } else {
      + return false, syscall.EINVAL
      +		}
      + }
      + const (

      + servicesLower = "services.exe"
      + servicesUpper = "SERVICES.EXE"
      +	)

      + i := int(exeNameLen) - 1
      + j := len(servicesLower) - 1
      + if i < j {
      + return false, nil
      + }
      + for {
      + if j == -1 {
      + return i == -1 || exeName[i] == '\\', nil
      + }
      + if exeName[i] != servicesLower[j] && exeName[i] != servicesUpper[j] {
      + return false, nil
      + }
      +		i--
      + j--
      + }

      +}
      +
      +func itoa(val int) string { // do it here rather than with fmt to avoid dependency
      + if val < 0 {
      + return "-" + itoa(-val)
      + }
      + var buf [32]byte // big enough for int64
      + i := len(buf) - 1
      + for val >= 10 {
      + buf[i] = byte(val%10 + '0')
      + i--
      + val /= 10
      + }
      + buf[i] = byte(val + '0')
      + return string(buf[i:])
      +}
      diff --git a/windows/svc/svc_test.go b/windows/svc/svc_test.go
      index 3a80e48..7e87053 100644
      --- a/windows/svc/svc_test.go
      +++ b/windows/svc/svc_test.go
      @@ -132,3 +132,23 @@

      t.Errorf("%q string does not contain %q", string(out), want)
      }
      }
      +
      +func TestIsAnInteractiveSession(t *testing.T) {
      + isInteractive, err := svc.IsAnInteractiveSession()
      + if err != nil {
      + t.Fatal(err)
      + }
      + if !isInteractive {
      +		t.Error("IsAnInteractiveSession retuns false when running interactively.")
      + }
      +}
      +
      +func TestIsWindowsService(t *testing.T) {
      + isSvc, err := svc.IsWindowsService()

      + if err != nil {
      + t.Fatal(err)
      + }
      +	if isSvc {
      + t.Error("IsWindowsService retuns true when not running in a service.")

      + }
      +}
      diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go
      index 30ef641..65ca6d2 100644

      --- a/windows/syscall_windows.go
      +++ b/windows/syscall_windows.go
      @@ -270,6 +270,7 @@
      //sys RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW
      //sys RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegQueryValueExW
      //sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
      +//sys ProcessIdToSessionId(pid uint32, sessionid *uint32) (err error) = kernel32.ProcessIdToSessionId
      //sys GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode
      //sys SetConsoleMode(console Handle, mode uint32) (err error) = kernel32.SetConsoleMode
      //sys GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) = kernel32.GetConsoleScreenBufferInfo
      diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go
      index f1ae76b..f6cef2b 100644

      --- a/windows/zsyscall_windows.go
      +++ b/windows/zsyscall_windows.go
      @@ -180,6 +180,7 @@
      procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW")
      procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW")
      procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId")
      + procProcessIdToSessionId = modkernel32.NewProc("ProcessIdToSessionId")
      procGetConsoleMode = modkernel32.NewProc("GetConsoleMode")
      procSetConsoleMode = modkernel32.NewProc("SetConsoleMode")
      procGetConsoleScreenBufferInfo = modkernel32.NewProc("GetConsoleScreenBufferInfo")
      @@ -1943,6 +1944,18 @@

      return
      }

      +func ProcessIdToSessionId(pid uint32, sessionid *uint32) (err error) {
      + r1, _, e1 := syscall.Syscall(procProcessIdToSessionId.Addr(), 2, uintptr(pid), uintptr(unsafe.Pointer(sessionid)), 0)
      + if r1 == 0 {
      + if e1 != 0 {
      + err = errnoErr(e1)
      + } else {
      + err = syscall.EINVAL
      + }
      + }
      + return
      +}
      +
      func GetConsoleMode(console Handle, mode *uint32) (err error) {
      r1, _, e1 := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0)
      if r1 == 0 {

      To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: sys
      Gerrit-Branch: master
      Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
      Gerrit-Change-Number: 259397
      Gerrit-PatchSet: 5
      Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
      Gerrit-Reviewer: Go Bot <go...@golang.org>
      Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
      Gerrit-MessageType: merged

      Alex Brainman (Gerrit)

      unread,
      Feb 20, 2021, 3:06:06 AM2/20/21
      to goph...@pubsubhelper.golang.org, Go Bot, Ian Lance Taylor, Brad Fitzpatrick, Jason A. Donenfeld, golang-co...@googlegroups.com

      Patch set 5:Run-TryBot +1Trust +1

      View Change

      2 comments:

      • Patchset:

        • Patch Set #5:

          Pinging Jason.

          Can you help me, please. See below.

          Thank you.

      • File windows/svc/security.go:

        • Patch Set #5, Line 99: return false, err

          Jason.

          I am trying to use this code. I am running this function in the process on my desktop.

          I SOMETIMES get ERROR_INVALID_PARAMETER returned by ProcessIdToSessionId.

          So it appears that pbi[5] is incorrectly set.

          Why are we using NtQueryInformationProcess in the first place? Can we just use os.Getpid here instead? I tried to print both os.Getpid returned value and pbi[5], and they look different.

          Why am I getting ERROR_INVALID_PARAMETER returned from ProcessIdToSessionId?

      To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: sys
      Gerrit-Branch: master
      Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
      Gerrit-Change-Number: 259397
      Gerrit-PatchSet: 5
      Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
      Gerrit-Reviewer: Go Bot <go...@golang.org>
      Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
      Gerrit-Comment-Date: Sat, 20 Feb 2021 08:06:00 +0000

      Alex Brainman (Gerrit)

      unread,
      Feb 20, 2021, 3:40:59 AM2/20/21
      to goph...@pubsubhelper.golang.org, Go Bot, Ian Lance Taylor, Brad Fitzpatrick, Jason A. Donenfeld, golang-co...@googlegroups.com

      Patch set 5:Run-TryBot +1Trust +1

      View Change

      2 comments:

      • Patchset:

      • File windows/svc/security.go:

        • I just re-read the comment above. pbi[5] is process *parent* pid.

          So that explains why ProcessIdToSessionId - parent process, probably, exited.

          We should return "false", here instead of returning error.

          Just like you did in

          https://go-review.googlesource.com/c/go/+/244958/

      To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: sys
      Gerrit-Branch: master
      Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
      Gerrit-Change-Number: 259397
      Gerrit-PatchSet: 5
      Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
      Gerrit-Reviewer: Go Bot <go...@golang.org>
      Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
      Gerrit-Comment-Date: Sat, 20 Feb 2021 08:40:52 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: Yes
      Comment-In-Reply-To: Alex Brainman <alex.b...@gmail.com>
      Gerrit-MessageType: comment

      Jason A. Donenfeld (Gerrit)

      unread,
      Feb 20, 2021, 7:22:06 AM2/20/21
      to Alex Brainman, goph...@pubsubhelper.golang.org, Go Bot, Ian Lance Taylor, Brad Fitzpatrick, golang-co...@googlegroups.com

      Attention is currently required from: Alex Brainman.

      View Change

      5 comments:

      • Patchset:

        • Patch Set #5:

          This was merged prematurely and contains problems. Please address my comments in a follow-up commit.

      • File windows/svc/security.go:

        • Patch Set #5, Line 94: return false, errors.New("NtQueryInformationProcess failed: error=" + itoa(int(r0)))

          Use fmt.Errorf with %x.

        • I just re-read the comment above. pbi[5] is process *parent* pid. […]

          Is there a reason why you're not using my code more verbatim? I can re-do all the work I did before that I've already paged out of my brain, but it seems like it'd be easier to just start with what I did before, and then only modify parts one at a time that you a) understand and b) can verify work. This way we don't have regressions that take additional work to figure out.

        • Patch Set #5, Line 136: if exeName[i] != servicesLower[j] && exeName[i] != servicesUpper[j] {

          The reason there's this manual compare loop is because I originally implemented this in runtime, which can't access packages like strings. However, this code here should just be a strings.EqualsFold or whatever it's called.

        • Patch Set #5, Line 144: func itoa(val int) string { // do it here rather than with fmt to avoid dependency

          This does not belong in x/sys. See earlier comment.

      To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: sys
      Gerrit-Branch: master
      Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
      Gerrit-Change-Number: 259397
      Gerrit-PatchSet: 5
      Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
      Gerrit-Reviewer: Go Bot <go...@golang.org>
      Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
      Gerrit-Attention: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Comment-Date: Sat, 20 Feb 2021 12:22:00 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No

      Jason A. Donenfeld (Gerrit)

      unread,
      Feb 20, 2021, 7:26:16 AM2/20/21
      to Alex Brainman, goph...@pubsubhelper.golang.org, Go Bot, Ian Lance Taylor, Brad Fitzpatrick, golang-co...@googlegroups.com

      Attention is currently required from: Alex Brainman.

      View Change

      3 comments:

      • Patchset:

        • Patch Set #5:

          More nits.

          I'm going to open a revert CL for this, as this seems to be really quite rough, with manual syscalls and stuff in there.

      • File windows/svc/security.go:

        • Patch Set #5, Line 92: r0, _, _ := syscall.Syscall6(_NtQueryInformationProcess.Addr(), 5, _CURRENT_PROCESS, 0, uintptr(unsafe.Pointer(&pbi[0])), uintptr(unsafe.Sizeof(pbi)), uintptr(unsafe.Pointer(&pbiLen)), 0)

          Shouldn't NtQueryInformationProcess and QueryFullProcessImageNameA just get added to x/sys like normal?

          And don't we already a variable for currentprocess in x/sys?

        • Patch Set #5, Line 115: r0, _, e0 := syscall.Syscall6(_QueryFullProcessImageNameA.Addr(), 4, uintptr(pproc), 0, uintptr(unsafe.Pointer(&exeName[0])), uintptr(unsafe.Pointer(&exeNameLen)), 0, 0)

          Again, this should be moved to x/sys.

      To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: sys
      Gerrit-Branch: master
      Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
      Gerrit-Change-Number: 259397
      Gerrit-PatchSet: 5
      Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
      Gerrit-Reviewer: Go Bot <go...@golang.org>
      Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
      Gerrit-Attention: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Comment-Date: Sat, 20 Feb 2021 12:26:11 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Gerrit-MessageType: comment

      Jason A. Donenfeld (Gerrit)

      unread,
      Feb 20, 2021, 7:27:00 AM2/20/21
      to Alex Brainman, goph...@pubsubhelper.golang.org, Go Bot, Ian Lance Taylor, Brad Fitzpatrick, golang-co...@googlegroups.com

      Attention is currently required from: Alex Brainman.

      Jason A. Donenfeld has created a revert of this change.

      View Change

      To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: sys
      Gerrit-Branch: master
      Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
      Gerrit-Change-Number: 259397
      Gerrit-PatchSet: 5
      Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
      Gerrit-Reviewer: Go Bot <go...@golang.org>
      Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
      Gerrit-Attention: Alex Brainman <alex.b...@gmail.com>
      Gerrit-MessageType: revert

      Alex Brainman (Gerrit)

      unread,
      Feb 20, 2021, 10:46:27 PM2/20/21
      to goph...@pubsubhelper.golang.org, Go Bot, Ian Lance Taylor, Brad Fitzpatrick, Jason A. Donenfeld, golang-co...@googlegroups.com

      Patch set 5:Run-TryBot +1Trust +1

      View Change

      8 comments:

      • Patchset:

        • Patch Set #5:

          This was merged prematurely and contains problems. Please address my comments in a follow-up commit.

          Thanks for your comments.

          You had 4 days to comment on this CL. But then I received +2 so I submitted.

          If you knew this CL contained problems, why did not you comment earlier?

          I will address your comments in the new CL.

        • Patch Set #5:

          Jason,

          Here is the CL that fixes the problem I discovered.

          https://go-review.googlesource.com/c/sys/+/294729

          I will address all your other comments in a separate CL or CLs (once CL 294729 is submitted). I don't want to mix bug fix with unrelated changes.

          Let me know, if you have a problem with that.

          Thank you.

          Alex

      • File windows/svc/security.go:

        • Patch Set #5, Line 92: r0, _, _ := syscall.Syscall6(_NtQueryInformationProcess.Addr(), 5, _CURRENT_PROCESS, 0, uintptr(unsafe.Pointer(&pbi[0])), uintptr(unsafe.Sizeof(pbi)), uintptr(unsafe.Pointer(&pbiLen)), 0)

        • Shouldn't NtQueryInformationProcess and QueryFullProcessImageNameA just get added to x/sys like norm […]

          Ack

        • Patch Set #5, Line 94: return false, errors.New("NtQueryInformationProcess failed: error=" + itoa(int(r0)))

          Use fmt.Errorf with %x.

        • Ack

        • Is there a reason why you're not using my code more verbatim? I can re-do all the work I did before […]

          I did the best I could do at a time.

          Let's fix it instead of trying to understand why I made mistakes.

        • Patch Set #5, Line 115: r0, _, e0 := syscall.Syscall6(_QueryFullProcessImageNameA.Addr(), 4, uintptr(pproc), 0, uintptr(unsafe.Pointer(&exeName[0])), uintptr(unsafe.Pointer(&exeNameLen)), 0, 0)

          Again, this should be moved to x/sys.

        • Ack

        • The reason there's this manual compare loop is because I originally implemented this in runtime, whi […]

          Ack

        • Patch Set #5, Line 144: func itoa(val int) string { // do it here rather than with fmt to avoid dependency

          This does not belong in x/sys. See earlier comment.

        • Ack

      To view, visit change 259397. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: sys
      Gerrit-Branch: master
      Gerrit-Change-Id: I4a33b7f590ee8161d1134d8e83668e9da4e6b434
      Gerrit-Change-Number: 259397
      Gerrit-PatchSet: 5
      Gerrit-Owner: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Alex Brainman <alex.b...@gmail.com>
      Gerrit-Reviewer: Brad Fitzpatrick <brad...@golang.org>
      Gerrit-Reviewer: Go Bot <go...@golang.org>
      Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Reviewer: Jason A. Donenfeld <Ja...@zx2c4.com>
      Gerrit-Comment-Date: Sun, 21 Feb 2021 03:46:20 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: Yes
      Comment-In-Reply-To: Jason A. Donenfeld <Ja...@zx2c4.com>
      Reply all
      Reply to author
      Forward
      0 new messages