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. 4 5//go:build windows 6// +build windows 7 8package svc 9 10import ( 11 "path/filepath" 12 "strings" 13 "unsafe" 14 15 "golang.org/x/sys/windows" 16) 17 18func allocSid(subAuth0 uint32) (*windows.SID, error) { 19 var sid *windows.SID 20 err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY, 21 1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid) 22 if err != nil { 23 return nil, err 24 } 25 return sid, nil 26} 27 28// IsAnInteractiveSession determines if calling process is running interactively. 29// It queries the process token for membership in the Interactive group. 30// http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s 31// 32// Deprecated: Use IsWindowsService instead. 33func IsAnInteractiveSession() (bool, error) { 34 interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID) 35 if err != nil { 36 return false, err 37 } 38 defer windows.FreeSid(interSid) 39 40 serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID) 41 if err != nil { 42 return false, err 43 } 44 defer windows.FreeSid(serviceSid) 45 46 t, err := windows.OpenCurrentProcessToken() 47 if err != nil { 48 return false, err 49 } 50 defer t.Close() 51 52 gs, err := t.GetTokenGroups() 53 if err != nil { 54 return false, err 55 } 56 57 for _, g := range gs.AllGroups() { 58 if windows.EqualSid(g.Sid, interSid) { 59 return true, nil 60 } 61 if windows.EqualSid(g.Sid, serviceSid) { 62 return false, nil 63 } 64 } 65 return false, nil 66} 67 68// IsWindowsService reports whether the process is currently executing 69// as a Windows service. 70func IsWindowsService() (bool, error) { 71 // The below technique looks a bit hairy, but it's actually 72 // exactly what the .NET framework does for the similarly named function: 73 // https://github.com/dotnet/extensions/blob/f4066026ca06984b07e90e61a6390ac38152ba93/src/Hosting/WindowsServices/src/WindowsServiceHelpers.cs#L26-L31 74 // Specifically, it looks up whether the parent process has session ID zero 75 // and is called "services". 76 77 var pbi windows.PROCESS_BASIC_INFORMATION 78 pbiLen := uint32(unsafe.Sizeof(pbi)) 79 err := windows.NtQueryInformationProcess(windows.CurrentProcess(), windows.ProcessBasicInformation, unsafe.Pointer(&pbi), pbiLen, &pbiLen) 80 if err != nil { 81 return false, err 82 } 83 var psid uint32 84 err = windows.ProcessIdToSessionId(uint32(pbi.InheritedFromUniqueProcessId), &psid) 85 if err != nil || psid != 0 { 86 return false, nil 87 } 88 pproc, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pbi.InheritedFromUniqueProcessId)) 89 if err != nil { 90 return false, err 91 } 92 defer windows.CloseHandle(pproc) 93 var exeNameBuf [261]uint16 94 exeNameLen := uint32(len(exeNameBuf) - 1) 95 err = windows.QueryFullProcessImageName(pproc, 0, &exeNameBuf[0], &exeNameLen) 96 if err != nil { 97 return false, err 98 } 99 exeName := windows.UTF16ToString(exeNameBuf[:exeNameLen]) 100 if !strings.EqualFold(filepath.Base(exeName), "services.exe") { 101 return false, nil 102 } 103 system32, err := windows.GetSystemDirectory() 104 if err != nil { 105 return false, err 106 } 107 targetExeName := filepath.Join(system32, "services.exe") 108 return strings.EqualFold(exeName, targetExeName), nil 109} 110