Vendor sys, fix drone again
parent
d47d8e6f53
commit
a5bb7b80d5
|
@ -10,10 +10,11 @@ matrix:
|
||||||
# - arm64
|
# - arm64
|
||||||
pipeline:
|
pipeline:
|
||||||
build:
|
build:
|
||||||
image: golang:${GO_VERSION}-alpine
|
image: golang:${GO_VERSION}
|
||||||
pull: true
|
pull: true
|
||||||
commands:
|
commands:
|
||||||
- export FILENAME=${DRONE_REPO_NAME}-${GOOS}-${GOARCH}
|
- export FILENAME=${DRONE_REPO_NAME}-${GOOS}-${GOARCH}
|
||||||
|
- if [ "$GOOS" == "windows" ]; then export FILENAME=$FILENAME.exe; fi
|
||||||
- go version
|
- go version
|
||||||
- ls -lah
|
- ls -lah
|
||||||
- ls vendor/
|
- ls vendor/
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for 386, Windows are implemented in runtime/syscall_windows.goc
|
||||||
|
//
|
||||||
|
|
||||||
|
TEXT ·getprocaddress(SB), 7, $0-8
|
||||||
|
JMP syscall·getprocaddress(SB)
|
||||||
|
|
||||||
|
TEXT ·loadlibrary(SB), 7, $0-4
|
||||||
|
JMP syscall·loadlibrary(SB)
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for amd64, Windows are implemented in runtime/syscall_windows.goc
|
||||||
|
//
|
||||||
|
|
||||||
|
TEXT ·getprocaddress(SB), 7, $0-32
|
||||||
|
JMP syscall·getprocaddress(SB)
|
||||||
|
|
||||||
|
TEXT ·loadlibrary(SB), 7, $0-8
|
||||||
|
JMP syscall·loadlibrary(SB)
|
|
@ -0,0 +1,378 @@
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DLLError describes reasons for DLL load failures.
|
||||||
|
type DLLError struct {
|
||||||
|
Err error
|
||||||
|
ObjName string
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DLLError) Error() string { return e.Msg }
|
||||||
|
|
||||||
|
// Implemented in runtime/syscall_windows.goc; we provide jumps to them in our assembly file.
|
||||||
|
func loadlibrary(filename *uint16) (handle uintptr, err syscall.Errno)
|
||||||
|
func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err syscall.Errno)
|
||||||
|
|
||||||
|
// A DLL implements access to a single DLL.
|
||||||
|
type DLL struct {
|
||||||
|
Name string
|
||||||
|
Handle Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadDLL loads DLL file into memory.
|
||||||
|
//
|
||||||
|
// Warning: using LoadDLL without an absolute path name is subject to
|
||||||
|
// DLL preloading attacks. To safely load a system DLL, use LazyDLL
|
||||||
|
// with System set to true, or use LoadLibraryEx directly.
|
||||||
|
func LoadDLL(name string) (dll *DLL, err error) {
|
||||||
|
namep, err := UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
h, e := loadlibrary(namep)
|
||||||
|
if e != 0 {
|
||||||
|
return nil, &DLLError{
|
||||||
|
Err: e,
|
||||||
|
ObjName: name,
|
||||||
|
Msg: "Failed to load " + name + ": " + e.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d := &DLL{
|
||||||
|
Name: name,
|
||||||
|
Handle: Handle(h),
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustLoadDLL is like LoadDLL but panics if load operation failes.
|
||||||
|
func MustLoadDLL(name string) *DLL {
|
||||||
|
d, e := LoadDLL(name)
|
||||||
|
if e != nil {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindProc searches DLL d for procedure named name and returns *Proc
|
||||||
|
// if found. It returns an error if search fails.
|
||||||
|
func (d *DLL) FindProc(name string) (proc *Proc, err error) {
|
||||||
|
namep, err := BytePtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
a, e := getprocaddress(uintptr(d.Handle), namep)
|
||||||
|
if e != 0 {
|
||||||
|
return nil, &DLLError{
|
||||||
|
Err: e,
|
||||||
|
ObjName: name,
|
||||||
|
Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p := &Proc{
|
||||||
|
Dll: d,
|
||||||
|
Name: name,
|
||||||
|
addr: a,
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustFindProc is like FindProc but panics if search fails.
|
||||||
|
func (d *DLL) MustFindProc(name string) *Proc {
|
||||||
|
p, e := d.FindProc(name)
|
||||||
|
if e != nil {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release unloads DLL d from memory.
|
||||||
|
func (d *DLL) Release() (err error) {
|
||||||
|
return FreeLibrary(d.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Proc implements access to a procedure inside a DLL.
|
||||||
|
type Proc struct {
|
||||||
|
Dll *DLL
|
||||||
|
Name string
|
||||||
|
addr uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr returns the address of the procedure represented by p.
|
||||||
|
// The return value can be passed to Syscall to run the procedure.
|
||||||
|
func (p *Proc) Addr() uintptr {
|
||||||
|
return p.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:uintptrescapes
|
||||||
|
|
||||||
|
// Call executes procedure p with arguments a. It will panic, if more than 15 arguments
|
||||||
|
// are supplied.
|
||||||
|
//
|
||||||
|
// The returned error is always non-nil, constructed from the result of GetLastError.
|
||||||
|
// Callers must inspect the primary return value to decide whether an error occurred
|
||||||
|
// (according to the semantics of the specific function being called) before consulting
|
||||||
|
// the error. The error will be guaranteed to contain windows.Errno.
|
||||||
|
func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
|
||||||
|
switch len(a) {
|
||||||
|
case 0:
|
||||||
|
return syscall.Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
|
||||||
|
case 1:
|
||||||
|
return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)
|
||||||
|
case 2:
|
||||||
|
return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)
|
||||||
|
case 3:
|
||||||
|
return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])
|
||||||
|
case 4:
|
||||||
|
return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
|
||||||
|
case 5:
|
||||||
|
return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
|
||||||
|
case 6:
|
||||||
|
return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
|
||||||
|
case 7:
|
||||||
|
return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
|
||||||
|
case 8:
|
||||||
|
return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)
|
||||||
|
case 9:
|
||||||
|
return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])
|
||||||
|
case 10:
|
||||||
|
return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0)
|
||||||
|
case 11:
|
||||||
|
return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0)
|
||||||
|
case 12:
|
||||||
|
return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11])
|
||||||
|
case 13:
|
||||||
|
return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0)
|
||||||
|
case 14:
|
||||||
|
return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0)
|
||||||
|
case 15:
|
||||||
|
return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14])
|
||||||
|
default:
|
||||||
|
panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A LazyDLL implements access to a single DLL.
|
||||||
|
// It will delay the load of the DLL until the first
|
||||||
|
// call to its Handle method or to one of its
|
||||||
|
// LazyProc's Addr method.
|
||||||
|
type LazyDLL struct {
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// System determines whether the DLL must be loaded from the
|
||||||
|
// Windows System directory, bypassing the normal DLL search
|
||||||
|
// path.
|
||||||
|
System bool
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
dll *DLL // non nil once DLL is loaded
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load loads DLL file d.Name into memory. It returns an error if fails.
|
||||||
|
// Load will not try to load DLL, if it is already loaded into memory.
|
||||||
|
func (d *LazyDLL) Load() error {
|
||||||
|
// Non-racy version of:
|
||||||
|
// if d.dll != nil {
|
||||||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
if d.dll != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// kernel32.dll is special, since it's where LoadLibraryEx comes from.
|
||||||
|
// The kernel already special-cases its name, so it's always
|
||||||
|
// loaded from system32.
|
||||||
|
var dll *DLL
|
||||||
|
var err error
|
||||||
|
if d.Name == "kernel32.dll" {
|
||||||
|
dll, err = LoadDLL(d.Name)
|
||||||
|
} else {
|
||||||
|
dll, err = loadLibraryEx(d.Name, d.System)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-racy version of:
|
||||||
|
// d.dll = dll
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mustLoad is like Load but panics if search fails.
|
||||||
|
func (d *LazyDLL) mustLoad() {
|
||||||
|
e := d.Load()
|
||||||
|
if e != nil {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle returns d's module handle.
|
||||||
|
func (d *LazyDLL) Handle() uintptr {
|
||||||
|
d.mustLoad()
|
||||||
|
return uintptr(d.dll.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProc returns a LazyProc for accessing the named procedure in the DLL d.
|
||||||
|
func (d *LazyDLL) NewProc(name string) *LazyProc {
|
||||||
|
return &LazyProc{l: d, Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLazyDLL creates new LazyDLL associated with DLL file.
|
||||||
|
func NewLazyDLL(name string) *LazyDLL {
|
||||||
|
return &LazyDLL{Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLazySystemDLL is like NewLazyDLL, but will only
|
||||||
|
// search Windows System directory for the DLL if name is
|
||||||
|
// a base name (like "advapi32.dll").
|
||||||
|
func NewLazySystemDLL(name string) *LazyDLL {
|
||||||
|
return &LazyDLL{Name: name, System: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A LazyProc implements access to a procedure inside a LazyDLL.
|
||||||
|
// It delays the lookup until the Addr method is called.
|
||||||
|
type LazyProc struct {
|
||||||
|
Name string
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
l *LazyDLL
|
||||||
|
proc *Proc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find searches DLL for procedure named p.Name. It returns
|
||||||
|
// an error if search fails. Find will not search procedure,
|
||||||
|
// if it is already found and loaded into memory.
|
||||||
|
func (p *LazyProc) Find() error {
|
||||||
|
// Non-racy version of:
|
||||||
|
// if p.proc == nil {
|
||||||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.proc == nil {
|
||||||
|
e := p.l.Load()
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
proc, e := p.l.dll.FindProc(p.Name)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
// Non-racy version of:
|
||||||
|
// p.proc = proc
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mustFind is like Find but panics if search fails.
|
||||||
|
func (p *LazyProc) mustFind() {
|
||||||
|
e := p.Find()
|
||||||
|
if e != nil {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr returns the address of the procedure represented by p.
|
||||||
|
// The return value can be passed to Syscall to run the procedure.
|
||||||
|
// It will panic if the procedure cannot be found.
|
||||||
|
func (p *LazyProc) Addr() uintptr {
|
||||||
|
p.mustFind()
|
||||||
|
return p.proc.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:uintptrescapes
|
||||||
|
|
||||||
|
// Call executes procedure p with arguments a. It will panic, if more than 15 arguments
|
||||||
|
// are supplied. It will also panic if the procedure cannot be found.
|
||||||
|
//
|
||||||
|
// The returned error is always non-nil, constructed from the result of GetLastError.
|
||||||
|
// Callers must inspect the primary return value to decide whether an error occurred
|
||||||
|
// (according to the semantics of the specific function being called) before consulting
|
||||||
|
// the error. The error will be guaranteed to contain windows.Errno.
|
||||||
|
func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
|
||||||
|
p.mustFind()
|
||||||
|
return p.proc.Call(a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var canDoSearchSystem32Once struct {
|
||||||
|
sync.Once
|
||||||
|
v bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func initCanDoSearchSystem32() {
|
||||||
|
// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
|
||||||
|
// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
|
||||||
|
// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
|
||||||
|
// systems that have KB2533623 installed. To determine whether the
|
||||||
|
// flags are available, use GetProcAddress to get the address of the
|
||||||
|
// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
|
||||||
|
// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
|
||||||
|
// flags can be used with LoadLibraryEx."
|
||||||
|
canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func canDoSearchSystem32() bool {
|
||||||
|
canDoSearchSystem32Once.Do(initCanDoSearchSystem32)
|
||||||
|
return canDoSearchSystem32Once.v
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBaseName(name string) bool {
|
||||||
|
for _, c := range name {
|
||||||
|
if c == ':' || c == '/' || c == '\\' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadLibraryEx wraps the Windows LoadLibraryEx function.
|
||||||
|
//
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
|
||||||
|
//
|
||||||
|
// If name is not an absolute path, LoadLibraryEx searches for the DLL
|
||||||
|
// in a variety of automatic locations unless constrained by flags.
|
||||||
|
// See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx
|
||||||
|
func loadLibraryEx(name string, system bool) (*DLL, error) {
|
||||||
|
loadDLL := name
|
||||||
|
var flags uintptr
|
||||||
|
if system {
|
||||||
|
if canDoSearchSystem32() {
|
||||||
|
const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
|
||||||
|
flags = LOAD_LIBRARY_SEARCH_SYSTEM32
|
||||||
|
} else if isBaseName(name) {
|
||||||
|
// WindowsXP or unpatched Windows machine
|
||||||
|
// trying to load "foo.dll" out of the system
|
||||||
|
// folder, but LoadLibraryEx doesn't support
|
||||||
|
// that yet on their system, so emulate it.
|
||||||
|
windir, _ := Getenv("WINDIR") // old var; apparently works on XP
|
||||||
|
if windir == "" {
|
||||||
|
return nil, errString("%WINDIR% not defined")
|
||||||
|
}
|
||||||
|
loadDLL = windir + "\\System32\\" + name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h, err := LoadLibraryEx(loadDLL, 0, flags)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &DLL{Name: name, Handle: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type errString string
|
||||||
|
|
||||||
|
func (s errString) Error() string { return string(s) }
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Windows environment variables.
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
func Getenv(key string) (value string, found bool) {
|
||||||
|
return syscall.Getenv(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Setenv(key, value string) error {
|
||||||
|
return syscall.Setenv(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Clearenv() {
|
||||||
|
syscall.Clearenv()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Environ() []string {
|
||||||
|
return syscall.Environ()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unsetenv(key string) error {
|
||||||
|
return syscall.Unsetenv(key)
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
const (
|
||||||
|
EVENTLOG_SUCCESS = 0
|
||||||
|
EVENTLOG_ERROR_TYPE = 1
|
||||||
|
EVENTLOG_WARNING_TYPE = 2
|
||||||
|
EVENTLOG_INFORMATION_TYPE = 4
|
||||||
|
EVENTLOG_AUDIT_SUCCESS = 8
|
||||||
|
EVENTLOG_AUDIT_FAILURE = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys RegisterEventSource(uncServerName *uint16, sourceName *uint16) (handle Handle, err error) [failretval==0] = advapi32.RegisterEventSourceW
|
||||||
|
//sys DeregisterEventSource(handle Handle) (err error) = advapi32.DeregisterEventSource
|
||||||
|
//sys ReportEvent(log Handle, etype uint16, category uint16, eventId uint32, usrSId uintptr, numStrings uint16, dataSize uint32, strings **uint16, rawData *byte) (err error) = advapi32.ReportEventW
|
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Fork, exec, wait, etc.
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
// EscapeArg rewrites command line argument s as prescribed
|
||||||
|
// in http://msdn.microsoft.com/en-us/library/ms880421.
|
||||||
|
// This function returns "" (2 double quotes) if s is empty.
|
||||||
|
// Alternatively, these transformations are done:
|
||||||
|
// - every back slash (\) is doubled, but only if immediately
|
||||||
|
// followed by double quote (");
|
||||||
|
// - every double quote (") is escaped by back slash (\);
|
||||||
|
// - finally, s is wrapped with double quotes (arg -> "arg"),
|
||||||
|
// but only if there is space or tab inside s.
|
||||||
|
func EscapeArg(s string) string {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return "\"\""
|
||||||
|
}
|
||||||
|
n := len(s)
|
||||||
|
hasSpace := false
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch s[i] {
|
||||||
|
case '"', '\\':
|
||||||
|
n++
|
||||||
|
case ' ', '\t':
|
||||||
|
hasSpace = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hasSpace {
|
||||||
|
n += 2
|
||||||
|
}
|
||||||
|
if n == len(s) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
qs := make([]byte, n)
|
||||||
|
j := 0
|
||||||
|
if hasSpace {
|
||||||
|
qs[j] = '"'
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
slashes := 0
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch s[i] {
|
||||||
|
default:
|
||||||
|
slashes = 0
|
||||||
|
qs[j] = s[i]
|
||||||
|
case '\\':
|
||||||
|
slashes++
|
||||||
|
qs[j] = s[i]
|
||||||
|
case '"':
|
||||||
|
for ; slashes > 0; slashes-- {
|
||||||
|
qs[j] = '\\'
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
qs[j] = '\\'
|
||||||
|
j++
|
||||||
|
qs[j] = s[i]
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
if hasSpace {
|
||||||
|
for ; slashes > 0; slashes-- {
|
||||||
|
qs[j] = '\\'
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
qs[j] = '"'
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
return string(qs[:j])
|
||||||
|
}
|
||||||
|
|
||||||
|
func CloseOnExec(fd Handle) {
|
||||||
|
SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FullPath retrieves the full path of the specified file.
|
||||||
|
func FullPath(name string) (path string, err error) {
|
||||||
|
p, err := UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
n := uint32(100)
|
||||||
|
for {
|
||||||
|
buf := make([]uint16, n)
|
||||||
|
n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if n <= uint32(len(buf)) {
|
||||||
|
return UTF16ToString(buf[:n]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
const (
|
||||||
|
MEM_COMMIT = 0x00001000
|
||||||
|
MEM_RESERVE = 0x00002000
|
||||||
|
MEM_DECOMMIT = 0x00004000
|
||||||
|
MEM_RELEASE = 0x00008000
|
||||||
|
MEM_RESET = 0x00080000
|
||||||
|
MEM_TOP_DOWN = 0x00100000
|
||||||
|
MEM_WRITE_WATCH = 0x00200000
|
||||||
|
MEM_PHYSICAL = 0x00400000
|
||||||
|
MEM_RESET_UNDO = 0x01000000
|
||||||
|
MEM_LARGE_PAGES = 0x20000000
|
||||||
|
|
||||||
|
PAGE_NOACCESS = 0x01
|
||||||
|
PAGE_READONLY = 0x02
|
||||||
|
PAGE_READWRITE = 0x04
|
||||||
|
PAGE_WRITECOPY = 0x08
|
||||||
|
PAGE_EXECUTE_READ = 0x20
|
||||||
|
PAGE_EXECUTE_READWRITE = 0x40
|
||||||
|
PAGE_EXECUTE_WRITECOPY = 0x80
|
||||||
|
)
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows,race
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const raceenabled = true
|
||||||
|
|
||||||
|
func raceAcquire(addr unsafe.Pointer) {
|
||||||
|
runtime.RaceAcquire(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func raceReleaseMerge(addr unsafe.Pointer) {
|
||||||
|
runtime.RaceReleaseMerge(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func raceReadRange(addr unsafe.Pointer, len int) {
|
||||||
|
runtime.RaceReadRange(addr, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func raceWriteRange(addr unsafe.Pointer, len int) {
|
||||||
|
runtime.RaceWriteRange(addr, len)
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows,!race
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const raceenabled = false
|
||||||
|
|
||||||
|
func raceAcquire(addr unsafe.Pointer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func raceReleaseMerge(addr unsafe.Pointer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func raceReadRange(addr unsafe.Pointer, len int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func raceWriteRange(addr unsafe.Pointer, len int) {
|
||||||
|
}
|
|
@ -0,0 +1,476 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
STANDARD_RIGHTS_REQUIRED = 0xf0000
|
||||||
|
STANDARD_RIGHTS_READ = 0x20000
|
||||||
|
STANDARD_RIGHTS_WRITE = 0x20000
|
||||||
|
STANDARD_RIGHTS_EXECUTE = 0x20000
|
||||||
|
STANDARD_RIGHTS_ALL = 0x1F0000
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NameUnknown = 0
|
||||||
|
NameFullyQualifiedDN = 1
|
||||||
|
NameSamCompatible = 2
|
||||||
|
NameDisplay = 3
|
||||||
|
NameUniqueId = 6
|
||||||
|
NameCanonical = 7
|
||||||
|
NameUserPrincipal = 8
|
||||||
|
NameCanonicalEx = 9
|
||||||
|
NameServicePrincipal = 10
|
||||||
|
NameDnsDomain = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
// This function returns 1 byte BOOLEAN rather than the 4 byte BOOL.
|
||||||
|
// http://blogs.msdn.com/b/drnick/archive/2007/12/19/windows-and-upn-format-credentials.aspx
|
||||||
|
//sys TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) [failretval&0xff==0] = secur32.TranslateNameW
|
||||||
|
//sys GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err error) [failretval&0xff==0] = secur32.GetUserNameExW
|
||||||
|
|
||||||
|
// TranslateAccountName converts a directory service
|
||||||
|
// object name from one format to another.
|
||||||
|
func TranslateAccountName(username string, from, to uint32, initSize int) (string, error) {
|
||||||
|
u, e := UTF16PtrFromString(username)
|
||||||
|
if e != nil {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
n := uint32(50)
|
||||||
|
for {
|
||||||
|
b := make([]uint16, n)
|
||||||
|
e = TranslateName(u, from, to, &b[0], &n)
|
||||||
|
if e == nil {
|
||||||
|
return UTF16ToString(b[:n]), nil
|
||||||
|
}
|
||||||
|
if e != ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// do not reorder
|
||||||
|
NetSetupUnknownStatus = iota
|
||||||
|
NetSetupUnjoined
|
||||||
|
NetSetupWorkgroupName
|
||||||
|
NetSetupDomainName
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserInfo10 struct {
|
||||||
|
Name *uint16
|
||||||
|
Comment *uint16
|
||||||
|
UsrComment *uint16
|
||||||
|
FullName *uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) = netapi32.NetUserGetInfo
|
||||||
|
//sys NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (neterr error) = netapi32.NetGetJoinInformation
|
||||||
|
//sys NetApiBufferFree(buf *byte) (neterr error) = netapi32.NetApiBufferFree
|
||||||
|
|
||||||
|
const (
|
||||||
|
// do not reorder
|
||||||
|
SidTypeUser = 1 + iota
|
||||||
|
SidTypeGroup
|
||||||
|
SidTypeDomain
|
||||||
|
SidTypeAlias
|
||||||
|
SidTypeWellKnownGroup
|
||||||
|
SidTypeDeletedAccount
|
||||||
|
SidTypeInvalid
|
||||||
|
SidTypeUnknown
|
||||||
|
SidTypeComputer
|
||||||
|
SidTypeLabel
|
||||||
|
)
|
||||||
|
|
||||||
|
type SidIdentifierAuthority struct {
|
||||||
|
Value [6]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
SECURITY_NULL_SID_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 0}}
|
||||||
|
SECURITY_WORLD_SID_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 1}}
|
||||||
|
SECURITY_LOCAL_SID_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 2}}
|
||||||
|
SECURITY_CREATOR_SID_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 3}}
|
||||||
|
SECURITY_NON_UNIQUE_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 4}}
|
||||||
|
SECURITY_NT_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 5}}
|
||||||
|
SECURITY_MANDATORY_LABEL_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 16}}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SECURITY_NULL_RID = 0
|
||||||
|
SECURITY_WORLD_RID = 0
|
||||||
|
SECURITY_LOCAL_RID = 0
|
||||||
|
SECURITY_CREATOR_OWNER_RID = 0
|
||||||
|
SECURITY_CREATOR_GROUP_RID = 1
|
||||||
|
SECURITY_DIALUP_RID = 1
|
||||||
|
SECURITY_NETWORK_RID = 2
|
||||||
|
SECURITY_BATCH_RID = 3
|
||||||
|
SECURITY_INTERACTIVE_RID = 4
|
||||||
|
SECURITY_LOGON_IDS_RID = 5
|
||||||
|
SECURITY_SERVICE_RID = 6
|
||||||
|
SECURITY_LOCAL_SYSTEM_RID = 18
|
||||||
|
SECURITY_BUILTIN_DOMAIN_RID = 32
|
||||||
|
SECURITY_PRINCIPAL_SELF_RID = 10
|
||||||
|
SECURITY_CREATOR_OWNER_SERVER_RID = 0x2
|
||||||
|
SECURITY_CREATOR_GROUP_SERVER_RID = 0x3
|
||||||
|
SECURITY_LOGON_IDS_RID_COUNT = 0x3
|
||||||
|
SECURITY_ANONYMOUS_LOGON_RID = 0x7
|
||||||
|
SECURITY_PROXY_RID = 0x8
|
||||||
|
SECURITY_ENTERPRISE_CONTROLLERS_RID = 0x9
|
||||||
|
SECURITY_SERVER_LOGON_RID = SECURITY_ENTERPRISE_CONTROLLERS_RID
|
||||||
|
SECURITY_AUTHENTICATED_USER_RID = 0xb
|
||||||
|
SECURITY_RESTRICTED_CODE_RID = 0xc
|
||||||
|
SECURITY_NT_NON_UNIQUE_RID = 0x15
|
||||||
|
)
|
||||||
|
|
||||||
|
// Predefined domain-relative RIDs for local groups.
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa379649(v=vs.85).aspx
|
||||||
|
const (
|
||||||
|
DOMAIN_ALIAS_RID_ADMINS = 0x220
|
||||||
|
DOMAIN_ALIAS_RID_USERS = 0x221
|
||||||
|
DOMAIN_ALIAS_RID_GUESTS = 0x222
|
||||||
|
DOMAIN_ALIAS_RID_POWER_USERS = 0x223
|
||||||
|
DOMAIN_ALIAS_RID_ACCOUNT_OPS = 0x224
|
||||||
|
DOMAIN_ALIAS_RID_SYSTEM_OPS = 0x225
|
||||||
|
DOMAIN_ALIAS_RID_PRINT_OPS = 0x226
|
||||||
|
DOMAIN_ALIAS_RID_BACKUP_OPS = 0x227
|
||||||
|
DOMAIN_ALIAS_RID_REPLICATOR = 0x228
|
||||||
|
DOMAIN_ALIAS_RID_RAS_SERVERS = 0x229
|
||||||
|
DOMAIN_ALIAS_RID_PREW2KCOMPACCESS = 0x22a
|
||||||
|
DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS = 0x22b
|
||||||
|
DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS = 0x22c
|
||||||
|
DOMAIN_ALIAS_RID_INCOMING_FOREST_TRUST_BUILDERS = 0x22d
|
||||||
|
DOMAIN_ALIAS_RID_MONITORING_USERS = 0X22e
|
||||||
|
DOMAIN_ALIAS_RID_LOGGING_USERS = 0x22f
|
||||||
|
DOMAIN_ALIAS_RID_AUTHORIZATIONACCESS = 0x230
|
||||||
|
DOMAIN_ALIAS_RID_TS_LICENSE_SERVERS = 0x231
|
||||||
|
DOMAIN_ALIAS_RID_DCOM_USERS = 0x232
|
||||||
|
DOMAIN_ALIAS_RID_IUSERS = 0x238
|
||||||
|
DOMAIN_ALIAS_RID_CRYPTO_OPERATORS = 0x239
|
||||||
|
DOMAIN_ALIAS_RID_CACHEABLE_PRINCIPALS_GROUP = 0x23b
|
||||||
|
DOMAIN_ALIAS_RID_NON_CACHEABLE_PRINCIPALS_GROUP = 0x23c
|
||||||
|
DOMAIN_ALIAS_RID_EVENT_LOG_READERS_GROUP = 0x23d
|
||||||
|
DOMAIN_ALIAS_RID_CERTSVC_DCOM_ACCESS_GROUP = 0x23e
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) = advapi32.LookupAccountSidW
|
||||||
|
//sys LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) = advapi32.LookupAccountNameW
|
||||||
|
//sys ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||||
|
//sys ConvertStringSidToSid(stringSid *uint16, sid **SID) (err error) = advapi32.ConvertStringSidToSidW
|
||||||
|
//sys GetLengthSid(sid *SID) (len uint32) = advapi32.GetLengthSid
|
||||||
|
//sys CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) = advapi32.CopySid
|
||||||
|
//sys AllocateAndInitializeSid(identAuth *SidIdentifierAuthority, subAuth byte, subAuth0 uint32, subAuth1 uint32, subAuth2 uint32, subAuth3 uint32, subAuth4 uint32, subAuth5 uint32, subAuth6 uint32, subAuth7 uint32, sid **SID) (err error) = advapi32.AllocateAndInitializeSid
|
||||||
|
//sys FreeSid(sid *SID) (err error) [failretval!=0] = advapi32.FreeSid
|
||||||
|
//sys EqualSid(sid1 *SID, sid2 *SID) (isEqual bool) = advapi32.EqualSid
|
||||||
|
|
||||||
|
// The security identifier (SID) structure is a variable-length
|
||||||
|
// structure used to uniquely identify users or groups.
|
||||||
|
type SID struct{}
|
||||||
|
|
||||||
|
// StringToSid converts a string-format security identifier
|
||||||
|
// sid into a valid, functional sid.
|
||||||
|
func StringToSid(s string) (*SID, error) {
|
||||||
|
var sid *SID
|
||||||
|
p, e := UTF16PtrFromString(s)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
e = ConvertStringSidToSid(p, &sid)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
defer LocalFree((Handle)(unsafe.Pointer(sid)))
|
||||||
|
return sid.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupSID retrieves a security identifier sid for the account
|
||||||
|
// and the name of the domain on which the account was found.
|
||||||
|
// System specify target computer to search.
|
||||||
|
func LookupSID(system, account string) (sid *SID, domain string, accType uint32, err error) {
|
||||||
|
if len(account) == 0 {
|
||||||
|
return nil, "", 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
acc, e := UTF16PtrFromString(account)
|
||||||
|
if e != nil {
|
||||||
|
return nil, "", 0, e
|
||||||
|
}
|
||||||
|
var sys *uint16
|
||||||
|
if len(system) > 0 {
|
||||||
|
sys, e = UTF16PtrFromString(system)
|
||||||
|
if e != nil {
|
||||||
|
return nil, "", 0, e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n := uint32(50)
|
||||||
|
dn := uint32(50)
|
||||||
|
for {
|
||||||
|
b := make([]byte, n)
|
||||||
|
db := make([]uint16, dn)
|
||||||
|
sid = (*SID)(unsafe.Pointer(&b[0]))
|
||||||
|
e = LookupAccountName(sys, acc, sid, &n, &db[0], &dn, &accType)
|
||||||
|
if e == nil {
|
||||||
|
return sid, UTF16ToString(db), accType, nil
|
||||||
|
}
|
||||||
|
if e != ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return nil, "", 0, e
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return nil, "", 0, e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String converts sid to a string format
|
||||||
|
// suitable for display, storage, or transmission.
|
||||||
|
func (sid *SID) String() (string, error) {
|
||||||
|
var s *uint16
|
||||||
|
e := ConvertSidToStringSid(sid, &s)
|
||||||
|
if e != nil {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
defer LocalFree((Handle)(unsafe.Pointer(s)))
|
||||||
|
return UTF16ToString((*[256]uint16)(unsafe.Pointer(s))[:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the length, in bytes, of a valid security identifier sid.
|
||||||
|
func (sid *SID) Len() int {
|
||||||
|
return int(GetLengthSid(sid))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy creates a duplicate of security identifier sid.
|
||||||
|
func (sid *SID) Copy() (*SID, error) {
|
||||||
|
b := make([]byte, sid.Len())
|
||||||
|
sid2 := (*SID)(unsafe.Pointer(&b[0]))
|
||||||
|
e := CopySid(uint32(len(b)), sid2, sid)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return sid2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupAccount retrieves the name of the account for this sid
|
||||||
|
// and the name of the first domain on which this sid is found.
|
||||||
|
// System specify target computer to search for.
|
||||||
|
func (sid *SID) LookupAccount(system string) (account, domain string, accType uint32, err error) {
|
||||||
|
var sys *uint16
|
||||||
|
if len(system) > 0 {
|
||||||
|
sys, err = UTF16PtrFromString(system)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n := uint32(50)
|
||||||
|
dn := uint32(50)
|
||||||
|
for {
|
||||||
|
b := make([]uint16, n)
|
||||||
|
db := make([]uint16, dn)
|
||||||
|
e := LookupAccountSid(sys, sid, &b[0], &n, &db[0], &dn, &accType)
|
||||||
|
if e == nil {
|
||||||
|
return UTF16ToString(b), UTF16ToString(db), accType, nil
|
||||||
|
}
|
||||||
|
if e != ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return "", "", 0, e
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return "", "", 0, e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// do not reorder
|
||||||
|
TOKEN_ASSIGN_PRIMARY = 1 << iota
|
||||||
|
TOKEN_DUPLICATE
|
||||||
|
TOKEN_IMPERSONATE
|
||||||
|
TOKEN_QUERY
|
||||||
|
TOKEN_QUERY_SOURCE
|
||||||
|
TOKEN_ADJUST_PRIVILEGES
|
||||||
|
TOKEN_ADJUST_GROUPS
|
||||||
|
TOKEN_ADJUST_DEFAULT
|
||||||
|
|
||||||
|
TOKEN_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED |
|
||||||
|
TOKEN_ASSIGN_PRIMARY |
|
||||||
|
TOKEN_DUPLICATE |
|
||||||
|
TOKEN_IMPERSONATE |
|
||||||
|
TOKEN_QUERY |
|
||||||
|
TOKEN_QUERY_SOURCE |
|
||||||
|
TOKEN_ADJUST_PRIVILEGES |
|
||||||
|
TOKEN_ADJUST_GROUPS |
|
||||||
|
TOKEN_ADJUST_DEFAULT
|
||||||
|
TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY
|
||||||
|
TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
|
||||||
|
TOKEN_ADJUST_PRIVILEGES |
|
||||||
|
TOKEN_ADJUST_GROUPS |
|
||||||
|
TOKEN_ADJUST_DEFAULT
|
||||||
|
TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// do not reorder
|
||||||
|
TokenUser = 1 + iota
|
||||||
|
TokenGroups
|
||||||
|
TokenPrivileges
|
||||||
|
TokenOwner
|
||||||
|
TokenPrimaryGroup
|
||||||
|
TokenDefaultDacl
|
||||||
|
TokenSource
|
||||||
|
TokenType
|
||||||
|
TokenImpersonationLevel
|
||||||
|
TokenStatistics
|
||||||
|
TokenRestrictedSids
|
||||||
|
TokenSessionId
|
||||||
|
TokenGroupsAndPrivileges
|
||||||
|
TokenSessionReference
|
||||||
|
TokenSandBoxInert
|
||||||
|
TokenAuditPolicy
|
||||||
|
TokenOrigin
|
||||||
|
TokenElevationType
|
||||||
|
TokenLinkedToken
|
||||||
|
TokenElevation
|
||||||
|
TokenHasRestrictions
|
||||||
|
TokenAccessInformation
|
||||||
|
TokenVirtualizationAllowed
|
||||||
|
TokenVirtualizationEnabled
|
||||||
|
TokenIntegrityLevel
|
||||||
|
TokenUIAccess
|
||||||
|
TokenMandatoryPolicy
|
||||||
|
TokenLogonSid
|
||||||
|
MaxTokenInfoClass
|
||||||
|
)
|
||||||
|
|
||||||
|
type SIDAndAttributes struct {
|
||||||
|
Sid *SID
|
||||||
|
Attributes uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tokenuser struct {
|
||||||
|
User SIDAndAttributes
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tokenprimarygroup struct {
|
||||||
|
PrimaryGroup *SID
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tokengroups struct {
|
||||||
|
GroupCount uint32
|
||||||
|
Groups [1]SIDAndAttributes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorization Functions
|
||||||
|
//sys checkTokenMembership(tokenHandle Token, sidToCheck *SID, isMember *int32) (err error) = advapi32.CheckTokenMembership
|
||||||
|
//sys OpenProcessToken(h Handle, access uint32, token *Token) (err error) = advapi32.OpenProcessToken
|
||||||
|
//sys GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32, returnedLen *uint32) (err error) = advapi32.GetTokenInformation
|
||||||
|
//sys GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) = userenv.GetUserProfileDirectoryW
|
||||||
|
|
||||||
|
// An access token contains the security information for a logon session.
|
||||||
|
// The system creates an access token when a user logs on, and every
|
||||||
|
// process executed on behalf of the user has a copy of the token.
|
||||||
|
// The token identifies the user, the user's groups, and the user's
|
||||||
|
// privileges. The system uses the token to control access to securable
|
||||||
|
// objects and to control the ability of the user to perform various
|
||||||
|
// system-related operations on the local computer.
|
||||||
|
type Token Handle
|
||||||
|
|
||||||
|
// OpenCurrentProcessToken opens the access token
|
||||||
|
// associated with current process.
|
||||||
|
func OpenCurrentProcessToken() (Token, error) {
|
||||||
|
p, e := GetCurrentProcess()
|
||||||
|
if e != nil {
|
||||||
|
return 0, e
|
||||||
|
}
|
||||||
|
var t Token
|
||||||
|
e = OpenProcessToken(p, TOKEN_QUERY, &t)
|
||||||
|
if e != nil {
|
||||||
|
return 0, e
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close releases access to access token.
|
||||||
|
func (t Token) Close() error {
|
||||||
|
return CloseHandle(Handle(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
// getInfo retrieves a specified type of information about an access token.
|
||||||
|
func (t Token) getInfo(class uint32, initSize int) (unsafe.Pointer, error) {
|
||||||
|
n := uint32(initSize)
|
||||||
|
for {
|
||||||
|
b := make([]byte, n)
|
||||||
|
e := GetTokenInformation(t, class, &b[0], uint32(len(b)), &n)
|
||||||
|
if e == nil {
|
||||||
|
return unsafe.Pointer(&b[0]), nil
|
||||||
|
}
|
||||||
|
if e != ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTokenUser retrieves access token t user account information.
|
||||||
|
func (t Token) GetTokenUser() (*Tokenuser, error) {
|
||||||
|
i, e := t.getInfo(TokenUser, 50)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return (*Tokenuser)(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTokenGroups retrieves group accounts associated with access token t.
|
||||||
|
func (t Token) GetTokenGroups() (*Tokengroups, error) {
|
||||||
|
i, e := t.getInfo(TokenGroups, 50)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return (*Tokengroups)(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTokenPrimaryGroup retrieves access token t primary group information.
|
||||||
|
// A pointer to a SID structure representing a group that will become
|
||||||
|
// the primary group of any objects created by a process using this access token.
|
||||||
|
func (t Token) GetTokenPrimaryGroup() (*Tokenprimarygroup, error) {
|
||||||
|
i, e := t.getInfo(TokenPrimaryGroup, 50)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return (*Tokenprimarygroup)(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserProfileDirectory retrieves path to the
|
||||||
|
// root directory of the access token t user's profile.
|
||||||
|
func (t Token) GetUserProfileDirectory() (string, error) {
|
||||||
|
n := uint32(100)
|
||||||
|
for {
|
||||||
|
b := make([]uint16, n)
|
||||||
|
e := GetUserProfileDirectory(t, &b[0], &n)
|
||||||
|
if e == nil {
|
||||||
|
return UTF16ToString(b), nil
|
||||||
|
}
|
||||||
|
if e != ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMember reports whether the access token t is a member of the provided SID.
|
||||||
|
func (t Token) IsMember(sid *SID) (bool, error) {
|
||||||
|
var b int32
|
||||||
|
if e := checkTokenMembership(t, sid, &b); e != nil {
|
||||||
|
return false, e
|
||||||
|
}
|
||||||
|
return b != 0, nil
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
const (
|
||||||
|
SC_MANAGER_CONNECT = 1
|
||||||
|
SC_MANAGER_CREATE_SERVICE = 2
|
||||||
|
SC_MANAGER_ENUMERATE_SERVICE = 4
|
||||||
|
SC_MANAGER_LOCK = 8
|
||||||
|
SC_MANAGER_QUERY_LOCK_STATUS = 16
|
||||||
|
SC_MANAGER_MODIFY_BOOT_CONFIG = 32
|
||||||
|
SC_MANAGER_ALL_ACCESS = 0xf003f
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenSCManagerW
|
||||||
|
|
||||||
|
const (
|
||||||
|
SERVICE_KERNEL_DRIVER = 1
|
||||||
|
SERVICE_FILE_SYSTEM_DRIVER = 2
|
||||||
|
SERVICE_ADAPTER = 4
|
||||||
|
SERVICE_RECOGNIZER_DRIVER = 8
|
||||||
|
SERVICE_WIN32_OWN_PROCESS = 16
|
||||||
|
SERVICE_WIN32_SHARE_PROCESS = 32
|
||||||
|
SERVICE_WIN32 = SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS
|
||||||
|
SERVICE_INTERACTIVE_PROCESS = 256
|
||||||
|
SERVICE_DRIVER = SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_RECOGNIZER_DRIVER
|
||||||
|
SERVICE_TYPE_ALL = SERVICE_WIN32 | SERVICE_ADAPTER | SERVICE_DRIVER | SERVICE_INTERACTIVE_PROCESS
|
||||||
|
|
||||||
|
SERVICE_BOOT_START = 0
|
||||||
|
SERVICE_SYSTEM_START = 1
|
||||||
|
SERVICE_AUTO_START = 2
|
||||||
|
SERVICE_DEMAND_START = 3
|
||||||
|
SERVICE_DISABLED = 4
|
||||||
|
|
||||||
|
SERVICE_ERROR_IGNORE = 0
|
||||||
|
SERVICE_ERROR_NORMAL = 1
|
||||||
|
SERVICE_ERROR_SEVERE = 2
|
||||||
|
SERVICE_ERROR_CRITICAL = 3
|
||||||
|
|
||||||
|
SC_STATUS_PROCESS_INFO = 0
|
||||||
|
|
||||||
|
SERVICE_STOPPED = 1
|
||||||
|
SERVICE_START_PENDING = 2
|
||||||
|
SERVICE_STOP_PENDING = 3
|
||||||
|
SERVICE_RUNNING = 4
|
||||||
|
SERVICE_CONTINUE_PENDING = 5
|
||||||
|
SERVICE_PAUSE_PENDING = 6
|
||||||
|
SERVICE_PAUSED = 7
|
||||||
|
SERVICE_NO_CHANGE = 0xffffffff
|
||||||
|
|
||||||
|
SERVICE_ACCEPT_STOP = 1
|
||||||
|
SERVICE_ACCEPT_PAUSE_CONTINUE = 2
|
||||||
|
SERVICE_ACCEPT_SHUTDOWN = 4
|
||||||
|
SERVICE_ACCEPT_PARAMCHANGE = 8
|
||||||
|
SERVICE_ACCEPT_NETBINDCHANGE = 16
|
||||||
|
SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 32
|
||||||
|
SERVICE_ACCEPT_POWEREVENT = 64
|
||||||
|
SERVICE_ACCEPT_SESSIONCHANGE = 128
|
||||||
|
|
||||||
|
SERVICE_CONTROL_STOP = 1
|
||||||
|
SERVICE_CONTROL_PAUSE = 2
|
||||||
|
SERVICE_CONTROL_CONTINUE = 3
|
||||||
|
SERVICE_CONTROL_INTERROGATE = 4
|
||||||
|
SERVICE_CONTROL_SHUTDOWN = 5
|
||||||
|
SERVICE_CONTROL_PARAMCHANGE = 6
|
||||||
|
SERVICE_CONTROL_NETBINDADD = 7
|
||||||
|
SERVICE_CONTROL_NETBINDREMOVE = 8
|
||||||
|
SERVICE_CONTROL_NETBINDENABLE = 9
|
||||||
|
SERVICE_CONTROL_NETBINDDISABLE = 10
|
||||||
|
SERVICE_CONTROL_DEVICEEVENT = 11
|
||||||
|
SERVICE_CONTROL_HARDWAREPROFILECHANGE = 12
|
||||||
|
SERVICE_CONTROL_POWEREVENT = 13
|
||||||
|
SERVICE_CONTROL_SESSIONCHANGE = 14
|
||||||
|
|
||||||
|
SERVICE_ACTIVE = 1
|
||||||
|
SERVICE_INACTIVE = 2
|
||||||
|
SERVICE_STATE_ALL = 3
|
||||||
|
|
||||||
|
SERVICE_QUERY_CONFIG = 1
|
||||||
|
SERVICE_CHANGE_CONFIG = 2
|
||||||
|
SERVICE_QUERY_STATUS = 4
|
||||||
|
SERVICE_ENUMERATE_DEPENDENTS = 8
|
||||||
|
SERVICE_START = 16
|
||||||
|
SERVICE_STOP = 32
|
||||||
|
SERVICE_PAUSE_CONTINUE = 64
|
||||||
|
SERVICE_INTERROGATE = 128
|
||||||
|
SERVICE_USER_DEFINED_CONTROL = 256
|
||||||
|
SERVICE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE | SERVICE_USER_DEFINED_CONTROL
|
||||||
|
SERVICE_RUNS_IN_SYSTEM_PROCESS = 1
|
||||||
|
SERVICE_CONFIG_DESCRIPTION = 1
|
||||||
|
SERVICE_CONFIG_FAILURE_ACTIONS = 2
|
||||||
|
|
||||||
|
NO_ERROR = 0
|
||||||
|
|
||||||
|
SC_ENUM_PROCESS_INFO = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
type SERVICE_STATUS struct {
|
||||||
|
ServiceType uint32
|
||||||
|
CurrentState uint32
|
||||||
|
ControlsAccepted uint32
|
||||||
|
Win32ExitCode uint32
|
||||||
|
ServiceSpecificExitCode uint32
|
||||||
|
CheckPoint uint32
|
||||||
|
WaitHint uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type SERVICE_TABLE_ENTRY struct {
|
||||||
|
ServiceName *uint16
|
||||||
|
ServiceProc uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type QUERY_SERVICE_CONFIG struct {
|
||||||
|
ServiceType uint32
|
||||||
|
StartType uint32
|
||||||
|
ErrorControl uint32
|
||||||
|
BinaryPathName *uint16
|
||||||
|
LoadOrderGroup *uint16
|
||||||
|
TagId uint32
|
||||||
|
Dependencies *uint16
|
||||||
|
ServiceStartName *uint16
|
||||||
|
DisplayName *uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type SERVICE_DESCRIPTION struct {
|
||||||
|
Description *uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type SERVICE_STATUS_PROCESS struct {
|
||||||
|
ServiceType uint32
|
||||||
|
CurrentState uint32
|
||||||
|
ControlsAccepted uint32
|
||||||
|
Win32ExitCode uint32
|
||||||
|
ServiceSpecificExitCode uint32
|
||||||
|
CheckPoint uint32
|
||||||
|
WaitHint uint32
|
||||||
|
ProcessId uint32
|
||||||
|
ServiceFlags uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type ENUM_SERVICE_STATUS_PROCESS struct {
|
||||||
|
ServiceName *uint16
|
||||||
|
DisplayName *uint16
|
||||||
|
ServiceStatusProcess SERVICE_STATUS_PROCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys CloseServiceHandle(handle Handle) (err error) = advapi32.CloseServiceHandle
|
||||||
|
//sys CreateService(mgr Handle, serviceName *uint16, displayName *uint16, access uint32, srvType uint32, startType uint32, errCtl uint32, pathName *uint16, loadOrderGroup *uint16, tagId *uint32, dependencies *uint16, serviceStartName *uint16, password *uint16) (handle Handle, err error) [failretval==0] = advapi32.CreateServiceW
|
||||||
|
//sys OpenService(mgr Handle, serviceName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenServiceW
|
||||||
|
//sys DeleteService(service Handle) (err error) = advapi32.DeleteService
|
||||||
|
//sys StartService(service Handle, numArgs uint32, argVectors **uint16) (err error) = advapi32.StartServiceW
|
||||||
|
//sys QueryServiceStatus(service Handle, status *SERVICE_STATUS) (err error) = advapi32.QueryServiceStatus
|
||||||
|
//sys ControlService(service Handle, control uint32, status *SERVICE_STATUS) (err error) = advapi32.ControlService
|
||||||
|
//sys StartServiceCtrlDispatcher(serviceTable *SERVICE_TABLE_ENTRY) (err error) = advapi32.StartServiceCtrlDispatcherW
|
||||||
|
//sys SetServiceStatus(service Handle, serviceStatus *SERVICE_STATUS) (err error) = advapi32.SetServiceStatus
|
||||||
|
//sys ChangeServiceConfig(service Handle, serviceType uint32, startType uint32, errorControl uint32, binaryPathName *uint16, loadOrderGroup *uint16, tagId *uint32, dependencies *uint16, serviceStartName *uint16, password *uint16, displayName *uint16) (err error) = advapi32.ChangeServiceConfigW
|
||||||
|
//sys QueryServiceConfig(service Handle, serviceConfig *QUERY_SERVICE_CONFIG, bufSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceConfigW
|
||||||
|
//sys ChangeServiceConfig2(service Handle, infoLevel uint32, info *byte) (err error) = advapi32.ChangeServiceConfig2W
|
||||||
|
//sys QueryServiceConfig2(service Handle, infoLevel uint32, buff *byte, buffSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceConfig2W
|
||||||
|
//sys EnumServicesStatusEx(mgr Handle, infoLevel uint32, serviceType uint32, serviceState uint32, services *byte, bufSize uint32, bytesNeeded *uint32, servicesReturned *uint32, resumeHandle *uint32, groupName *uint16) (err error) = advapi32.EnumServicesStatusExW
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
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:])
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// event represents auto-reset, initially non-signaled Windows event.
|
||||||
|
// It is used to communicate between go and asm parts of this package.
|
||||||
|
type event struct {
|
||||||
|
h windows.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEvent() (*event, error) {
|
||||||
|
h, err := windows.CreateEvent(nil, 0, 0, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &event{h: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *event) Close() error {
|
||||||
|
return windows.CloseHandle(e.h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *event) Set() error {
|
||||||
|
return windows.SetEvent(e.h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *event) Wait() error {
|
||||||
|
s, err := windows.WaitForSingleObject(e.h, windows.INFINITE)
|
||||||
|
switch s {
|
||||||
|
case windows.WAIT_OBJECT_0:
|
||||||
|
break
|
||||||
|
case windows.WAIT_FAILED:
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
return errors.New("unexpected result from WaitForSingleObject")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
// +build !go1.3
|
||||||
|
|
||||||
|
// copied from pkg/runtime
|
||||||
|
typedef unsigned int uint32;
|
||||||
|
typedef unsigned long long int uint64;
|
||||||
|
#ifdef _64BIT
|
||||||
|
typedef uint64 uintptr;
|
||||||
|
#else
|
||||||
|
typedef uint32 uintptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// from sys_386.s or sys_amd64.s
|
||||||
|
void ·servicemain(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
·getServiceMain(uintptr *r)
|
||||||
|
{
|
||||||
|
*r = (uintptr)·servicemain;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
// +build !go1.3
|
||||||
|
|
||||||
|
package svc
|
||||||
|
|
||||||
|
// from go12.c
|
||||||
|
func getServiceMain(r *uintptr)
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
// +build go1.3
|
||||||
|
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
|
||||||
|
|
||||||
|
// Should be a built-in for unsafe.Pointer?
|
||||||
|
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(uintptr(p) + x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// funcPC returns the entry PC of the function f.
|
||||||
|
// It assumes that f is a func value. Otherwise the behavior is undefined.
|
||||||
|
func funcPC(f interface{}) uintptr {
|
||||||
|
return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
// from sys_386.s and sys_amd64.s
|
||||||
|
func servicectlhandler(ctl uint32) uintptr
|
||||||
|
func servicemain(argc uint32, argv **uint16)
|
||||||
|
|
||||||
|
func getServiceMain(r *uintptr) {
|
||||||
|
*r = funcPC(servicemain)
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package mgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Service start types.
|
||||||
|
StartManual = windows.SERVICE_DEMAND_START // the service must be started manually
|
||||||
|
StartAutomatic = windows.SERVICE_AUTO_START // the service will start by itself whenever the computer reboots
|
||||||
|
StartDisabled = windows.SERVICE_DISABLED // the service cannot be started
|
||||||
|
|
||||||
|
// The severity of the error, and action taken,
|
||||||
|
// if this service fails to start.
|
||||||
|
ErrorCritical = windows.SERVICE_ERROR_CRITICAL
|
||||||
|
ErrorIgnore = windows.SERVICE_ERROR_IGNORE
|
||||||
|
ErrorNormal = windows.SERVICE_ERROR_NORMAL
|
||||||
|
ErrorSevere = windows.SERVICE_ERROR_SEVERE
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(brainman): Password is not returned by windows.QueryServiceConfig, not sure how to get it.
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
ServiceType uint32
|
||||||
|
StartType uint32
|
||||||
|
ErrorControl uint32
|
||||||
|
BinaryPathName string // fully qualified path to the service binary file, can also include arguments for an auto-start service
|
||||||
|
LoadOrderGroup string
|
||||||
|
TagId uint32
|
||||||
|
Dependencies []string
|
||||||
|
ServiceStartName string // name of the account under which the service should run
|
||||||
|
DisplayName string
|
||||||
|
Password string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
func toString(p *uint16) string {
|
||||||
|
if p == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return syscall.UTF16ToString((*[4096]uint16)(unsafe.Pointer(p))[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func toStringSlice(ps *uint16) []string {
|
||||||
|
if ps == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
r := make([]string, 0)
|
||||||
|
for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(ps)); true; i++ {
|
||||||
|
if p[i] == 0 {
|
||||||
|
// empty string marks the end
|
||||||
|
if i <= from {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r = append(r, string(utf16.Decode(p[from:i])))
|
||||||
|
from = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config retrieves service s configuration paramteres.
|
||||||
|
func (s *Service) Config() (Config, error) {
|
||||||
|
var p *windows.QUERY_SERVICE_CONFIG
|
||||||
|
n := uint32(1024)
|
||||||
|
for {
|
||||||
|
b := make([]byte, n)
|
||||||
|
p = (*windows.QUERY_SERVICE_CONFIG)(unsafe.Pointer(&b[0]))
|
||||||
|
err := windows.QueryServiceConfig(s.Handle, p, n, &n)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err.(syscall.Errno) != syscall.ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var p2 *windows.SERVICE_DESCRIPTION
|
||||||
|
n = uint32(1024)
|
||||||
|
for {
|
||||||
|
b := make([]byte, n)
|
||||||
|
p2 = (*windows.SERVICE_DESCRIPTION)(unsafe.Pointer(&b[0]))
|
||||||
|
err := windows.QueryServiceConfig2(s.Handle,
|
||||||
|
windows.SERVICE_CONFIG_DESCRIPTION, &b[0], n, &n)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err.(syscall.Errno) != syscall.ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Config{
|
||||||
|
ServiceType: p.ServiceType,
|
||||||
|
StartType: p.StartType,
|
||||||
|
ErrorControl: p.ErrorControl,
|
||||||
|
BinaryPathName: toString(p.BinaryPathName),
|
||||||
|
LoadOrderGroup: toString(p.LoadOrderGroup),
|
||||||
|
TagId: p.TagId,
|
||||||
|
Dependencies: toStringSlice(p.Dependencies),
|
||||||
|
ServiceStartName: toString(p.ServiceStartName),
|
||||||
|
DisplayName: toString(p.DisplayName),
|
||||||
|
Description: toString(p2.Description),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateDescription(handle windows.Handle, desc string) error {
|
||||||
|
d := windows.SERVICE_DESCRIPTION{toPtr(desc)}
|
||||||
|
return windows.ChangeServiceConfig2(handle,
|
||||||
|
windows.SERVICE_CONFIG_DESCRIPTION, (*byte)(unsafe.Pointer(&d)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig updates service s configuration parameters.
|
||||||
|
func (s *Service) UpdateConfig(c Config) error {
|
||||||
|
err := windows.ChangeServiceConfig(s.Handle, c.ServiceType, c.StartType,
|
||||||
|
c.ErrorControl, toPtr(c.BinaryPathName), toPtr(c.LoadOrderGroup),
|
||||||
|
nil, toStringBlock(c.Dependencies), toPtr(c.ServiceStartName),
|
||||||
|
toPtr(c.Password), toPtr(c.DisplayName))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return updateDescription(s.Handle, c.Description)
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package mgr can be used to manage Windows service programs.
|
||||||
|
// It can be used to install and remove them. It can also start,
|
||||||
|
// stop and pause them. The package can query / change current
|
||||||
|
// service state and config parameters.
|
||||||
|
//
|
||||||
|
package mgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mgr is used to manage Windows service.
|
||||||
|
type Mgr struct {
|
||||||
|
Handle windows.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect establishes a connection to the service control manager.
|
||||||
|
func Connect() (*Mgr, error) {
|
||||||
|
return ConnectRemote("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectRemote establishes a connection to the
|
||||||
|
// service control manager on computer named host.
|
||||||
|
func ConnectRemote(host string) (*Mgr, error) {
|
||||||
|
var s *uint16
|
||||||
|
if host != "" {
|
||||||
|
s = syscall.StringToUTF16Ptr(host)
|
||||||
|
}
|
||||||
|
h, err := windows.OpenSCManager(s, nil, windows.SC_MANAGER_ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Mgr{Handle: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect closes connection to the service control manager m.
|
||||||
|
func (m *Mgr) Disconnect() error {
|
||||||
|
return windows.CloseServiceHandle(m.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toPtr(s string) *uint16 {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return syscall.StringToUTF16Ptr(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// toStringBlock terminates strings in ss with 0, and then
|
||||||
|
// concatenates them together. It also adds extra 0 at the end.
|
||||||
|
func toStringBlock(ss []string) *uint16 {
|
||||||
|
if len(ss) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t := ""
|
||||||
|
for _, s := range ss {
|
||||||
|
if s != "" {
|
||||||
|
t += s + "\x00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t += "\x00"
|
||||||
|
return &utf16.Encode([]rune(t))[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateService installs new service name on the system.
|
||||||
|
// The service will be executed by running exepath binary.
|
||||||
|
// Use config c to specify service parameters.
|
||||||
|
// Any args will be passed as command-line arguments when
|
||||||
|
// the service is started; these arguments are distinct from
|
||||||
|
// the arguments passed to Service.Start or via the "Start
|
||||||
|
// parameters" field in the service's Properties dialog box.
|
||||||
|
func (m *Mgr) CreateService(name, exepath string, c Config, args ...string) (*Service, error) {
|
||||||
|
if c.StartType == 0 {
|
||||||
|
c.StartType = StartManual
|
||||||
|
}
|
||||||
|
if c.ErrorControl == 0 {
|
||||||
|
c.ErrorControl = ErrorNormal
|
||||||
|
}
|
||||||
|
if c.ServiceType == 0 {
|
||||||
|
c.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS
|
||||||
|
}
|
||||||
|
s := syscall.EscapeArg(exepath)
|
||||||
|
for _, v := range args {
|
||||||
|
s += " " + syscall.EscapeArg(v)
|
||||||
|
}
|
||||||
|
h, err := windows.CreateService(m.Handle, toPtr(name), toPtr(c.DisplayName),
|
||||||
|
windows.SERVICE_ALL_ACCESS, c.ServiceType,
|
||||||
|
c.StartType, c.ErrorControl, toPtr(s), toPtr(c.LoadOrderGroup),
|
||||||
|
nil, toStringBlock(c.Dependencies), toPtr(c.ServiceStartName), toPtr(c.Password))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if c.Description != "" {
|
||||||
|
err = updateDescription(h, c.Description)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Service{Name: name, Handle: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenService retrieves access to service name, so it can
|
||||||
|
// be interrogated and controlled.
|
||||||
|
func (m *Mgr) OpenService(name string) (*Service, error) {
|
||||||
|
h, err := windows.OpenService(m.Handle, syscall.StringToUTF16Ptr(name), windows.SERVICE_ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Service{Name: name, Handle: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListServices enumerates services in the specified
|
||||||
|
// service control manager database m.
|
||||||
|
// If the caller does not have the SERVICE_QUERY_STATUS
|
||||||
|
// access right to a service, the service is silently
|
||||||
|
// omitted from the list of services returned.
|
||||||
|
func (m *Mgr) ListServices() ([]string, error) {
|
||||||
|
var err error
|
||||||
|
var bytesNeeded, servicesReturned uint32
|
||||||
|
var buf []byte
|
||||||
|
for {
|
||||||
|
var p *byte
|
||||||
|
if len(buf) > 0 {
|
||||||
|
p = &buf[0]
|
||||||
|
}
|
||||||
|
err = windows.EnumServicesStatusEx(m.Handle, windows.SC_ENUM_PROCESS_INFO,
|
||||||
|
windows.SERVICE_WIN32, windows.SERVICE_STATE_ALL,
|
||||||
|
p, uint32(len(buf)), &bytesNeeded, &servicesReturned, nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != syscall.ERROR_MORE_DATA {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if bytesNeeded <= uint32(len(buf)) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf = make([]byte, bytesNeeded)
|
||||||
|
}
|
||||||
|
if servicesReturned == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
services := (*[1 << 20]windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0]))[:servicesReturned]
|
||||||
|
var names []string
|
||||||
|
for _, s := range services {
|
||||||
|
name := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(s.ServiceName))[:])
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
return names, nil
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package mgr_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows/svc/mgr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOpenLanManServer(t *testing.T) {
|
||||||
|
m, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_ACCESS_DENIED {
|
||||||
|
t.Skip("Skipping test: we don't have rights to manage services.")
|
||||||
|
}
|
||||||
|
t.Fatalf("SCM connection failed: %s", err)
|
||||||
|
}
|
||||||
|
defer m.Disconnect()
|
||||||
|
|
||||||
|
s, err := m.OpenService("LanmanServer")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("OpenService(lanmanserver) failed: %s", err)
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
_, err = s.Config()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Config failed: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func install(t *testing.T, m *mgr.Mgr, name, exepath string, c mgr.Config) {
|
||||||
|
// Sometimes it takes a while for the service to get
|
||||||
|
// removed after previous test run.
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
s, err := m.OpenService(name)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s.Close()
|
||||||
|
|
||||||
|
if i > 10 {
|
||||||
|
t.Fatalf("service %s already exists", name)
|
||||||
|
}
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := m.CreateService(name, exepath, c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CreateService(%s) failed: %v", name, err)
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func depString(d []string) string {
|
||||||
|
if len(d) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
for i := range d {
|
||||||
|
d[i] = strings.ToLower(d[i])
|
||||||
|
}
|
||||||
|
ss := sort.StringSlice(d)
|
||||||
|
ss.Sort()
|
||||||
|
return strings.Join([]string(ss), " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testConfig(t *testing.T, s *mgr.Service, should mgr.Config) mgr.Config {
|
||||||
|
is, err := s.Config()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Config failed: %s", err)
|
||||||
|
}
|
||||||
|
if should.DisplayName != is.DisplayName {
|
||||||
|
t.Fatalf("config mismatch: DisplayName is %q, but should have %q", is.DisplayName, should.DisplayName)
|
||||||
|
}
|
||||||
|
if should.StartType != is.StartType {
|
||||||
|
t.Fatalf("config mismatch: StartType is %v, but should have %v", is.StartType, should.StartType)
|
||||||
|
}
|
||||||
|
if should.Description != is.Description {
|
||||||
|
t.Fatalf("config mismatch: Description is %q, but should have %q", is.Description, should.Description)
|
||||||
|
}
|
||||||
|
if depString(should.Dependencies) != depString(is.Dependencies) {
|
||||||
|
t.Fatalf("config mismatch: Dependencies is %v, but should have %v", is.Dependencies, should.Dependencies)
|
||||||
|
}
|
||||||
|
return is
|
||||||
|
}
|
||||||
|
|
||||||
|
func remove(t *testing.T, s *mgr.Service) {
|
||||||
|
err := s.Delete()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Delete failed: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMyService(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode - it modifies system services")
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = "myservice"
|
||||||
|
|
||||||
|
m, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_ACCESS_DENIED {
|
||||||
|
t.Skip("Skipping test: we don't have rights to manage services.")
|
||||||
|
}
|
||||||
|
t.Fatalf("SCM connection failed: %s", err)
|
||||||
|
}
|
||||||
|
defer m.Disconnect()
|
||||||
|
|
||||||
|
c := mgr.Config{
|
||||||
|
StartType: mgr.StartDisabled,
|
||||||
|
DisplayName: "my service",
|
||||||
|
Description: "my service is just a test",
|
||||||
|
Dependencies: []string{"LanmanServer", "W32Time"},
|
||||||
|
}
|
||||||
|
|
||||||
|
exename := os.Args[0]
|
||||||
|
exepath, err := filepath.Abs(exename)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("filepath.Abs(%s) failed: %s", exename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
install(t, m, name, exepath, c)
|
||||||
|
|
||||||
|
s, err := m.OpenService(name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("service %s is not installed", name)
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
c.BinaryPathName = exepath
|
||||||
|
c = testConfig(t, s, c)
|
||||||
|
|
||||||
|
c.StartType = mgr.StartManual
|
||||||
|
err = s.UpdateConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("UpdateConfig failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testConfig(t, s, c)
|
||||||
|
|
||||||
|
svcnames, err := m.ListServices()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ListServices failed: %v", err)
|
||||||
|
}
|
||||||
|
var myserviceIsInstalled bool
|
||||||
|
for _, sn := range svcnames {
|
||||||
|
if sn == name {
|
||||||
|
myserviceIsInstalled = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !myserviceIsInstalled {
|
||||||
|
t.Errorf("ListServices failed to find %q service", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(t, s)
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package mgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
"golang.org/x/sys/windows/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(brainman): Use EnumDependentServices to enumerate dependent services.
|
||||||
|
|
||||||
|
// Service is used to access Windows service.
|
||||||
|
type Service struct {
|
||||||
|
Name string
|
||||||
|
Handle windows.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete marks service s for deletion from the service control manager database.
|
||||||
|
func (s *Service) Delete() error {
|
||||||
|
return windows.DeleteService(s.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close relinquish access to the service s.
|
||||||
|
func (s *Service) Close() error {
|
||||||
|
return windows.CloseServiceHandle(s.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts service s.
|
||||||
|
// args will be passed to svc.Handler.Execute.
|
||||||
|
func (s *Service) Start(args ...string) error {
|
||||||
|
var p **uint16
|
||||||
|
if len(args) > 0 {
|
||||||
|
vs := make([]*uint16, len(args))
|
||||||
|
for i := range vs {
|
||||||
|
vs[i] = syscall.StringToUTF16Ptr(args[i])
|
||||||
|
}
|
||||||
|
p = &vs[0]
|
||||||
|
}
|
||||||
|
return windows.StartService(s.Handle, uint32(len(args)), p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control sends state change request c to the servce s.
|
||||||
|
func (s *Service) Control(c svc.Cmd) (svc.Status, error) {
|
||||||
|
var t windows.SERVICE_STATUS
|
||||||
|
err := windows.ControlService(s.Handle, uint32(c), &t)
|
||||||
|
if err != nil {
|
||||||
|
return svc.Status{}, err
|
||||||
|
}
|
||||||
|
return svc.Status{
|
||||||
|
State: svc.State(t.CurrentState),
|
||||||
|
Accepts: svc.Accepted(t.ControlsAccepted),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns current status of service s.
|
||||||
|
func (s *Service) Query() (svc.Status, error) {
|
||||||
|
var t windows.SERVICE_STATUS
|
||||||
|
err := windows.QueryServiceStatus(s.Handle, &t)
|
||||||
|
if err != nil {
|
||||||
|
return svc.Status{}, err
|
||||||
|
}
|
||||||
|
return svc.Status{
|
||||||
|
State: svc.State(t.CurrentState),
|
||||||
|
Accepts: svc.Accepted(t.ControlsAccepted),
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer windows.FreeSid(interSid)
|
||||||
|
|
||||||
|
serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer windows.FreeSid(serviceSid)
|
||||||
|
|
||||||
|
t, err := windows.OpenCurrentProcessToken()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer t.Close()
|
||||||
|
|
||||||
|
gs, err := t.GetTokenGroups()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
p := unsafe.Pointer(&gs.Groups[0])
|
||||||
|
groups := (*[2 << 20]windows.SIDAndAttributes)(p)[:gs.GroupCount]
|
||||||
|
for _, g := range groups {
|
||||||
|
if windows.EqualSid(g.Sid, interSid) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if windows.EqualSid(g.Sid, serviceSid) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
|
@ -0,0 +1,363 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package svc provides everything required to build Windows service.
|
||||||
|
//
|
||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State describes service execution state (Stopped, Running and so on).
|
||||||
|
type State uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Stopped = State(windows.SERVICE_STOPPED)
|
||||||
|
StartPending = State(windows.SERVICE_START_PENDING)
|
||||||
|
StopPending = State(windows.SERVICE_STOP_PENDING)
|
||||||
|
Running = State(windows.SERVICE_RUNNING)
|
||||||
|
ContinuePending = State(windows.SERVICE_CONTINUE_PENDING)
|
||||||
|
PausePending = State(windows.SERVICE_PAUSE_PENDING)
|
||||||
|
Paused = State(windows.SERVICE_PAUSED)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cmd represents service state change request. It is sent to a service
|
||||||
|
// by the service manager, and should be actioned upon by the service.
|
||||||
|
type Cmd uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Stop = Cmd(windows.SERVICE_CONTROL_STOP)
|
||||||
|
Pause = Cmd(windows.SERVICE_CONTROL_PAUSE)
|
||||||
|
Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE)
|
||||||
|
Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE)
|
||||||
|
Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN)
|
||||||
|
ParamChange = Cmd(windows.SERVICE_CONTROL_PARAMCHANGE)
|
||||||
|
NetBindAdd = Cmd(windows.SERVICE_CONTROL_NETBINDADD)
|
||||||
|
NetBindRemove = Cmd(windows.SERVICE_CONTROL_NETBINDREMOVE)
|
||||||
|
NetBindEnable = Cmd(windows.SERVICE_CONTROL_NETBINDENABLE)
|
||||||
|
NetBindDisable = Cmd(windows.SERVICE_CONTROL_NETBINDDISABLE)
|
||||||
|
DeviceEvent = Cmd(windows.SERVICE_CONTROL_DEVICEEVENT)
|
||||||
|
HardwareProfileChange = Cmd(windows.SERVICE_CONTROL_HARDWAREPROFILECHANGE)
|
||||||
|
PowerEvent = Cmd(windows.SERVICE_CONTROL_POWEREVENT)
|
||||||
|
SessionChange = Cmd(windows.SERVICE_CONTROL_SESSIONCHANGE)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Accepted is used to describe commands accepted by the service.
|
||||||
|
// Note that Interrogate is always accepted.
|
||||||
|
type Accepted uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
AcceptStop = Accepted(windows.SERVICE_ACCEPT_STOP)
|
||||||
|
AcceptShutdown = Accepted(windows.SERVICE_ACCEPT_SHUTDOWN)
|
||||||
|
AcceptPauseAndContinue = Accepted(windows.SERVICE_ACCEPT_PAUSE_CONTINUE)
|
||||||
|
AcceptParamChange = Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE)
|
||||||
|
AcceptNetBindChange = Accepted(windows.SERVICE_ACCEPT_NETBINDCHANGE)
|
||||||
|
AcceptHardwareProfileChange = Accepted(windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
|
||||||
|
AcceptPowerEvent = Accepted(windows.SERVICE_ACCEPT_POWEREVENT)
|
||||||
|
AcceptSessionChange = Accepted(windows.SERVICE_ACCEPT_SESSIONCHANGE)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Status combines State and Accepted commands to fully describe running service.
|
||||||
|
type Status struct {
|
||||||
|
State State
|
||||||
|
Accepts Accepted
|
||||||
|
CheckPoint uint32 // used to report progress during a lengthy operation
|
||||||
|
WaitHint uint32 // estimated time required for a pending operation, in milliseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeRequest is sent to the service Handler to request service status change.
|
||||||
|
type ChangeRequest struct {
|
||||||
|
Cmd Cmd
|
||||||
|
EventType uint32
|
||||||
|
EventData uintptr
|
||||||
|
CurrentStatus Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler is the interface that must be implemented to build Windows service.
|
||||||
|
type Handler interface {
|
||||||
|
|
||||||
|
// Execute will be called by the package code at the start of
|
||||||
|
// the service, and the service will exit once Execute completes.
|
||||||
|
// Inside Execute you must read service change requests from r and
|
||||||
|
// act accordingly. You must keep service control manager up to date
|
||||||
|
// about state of your service by writing into s as required.
|
||||||
|
// args contains service name followed by argument strings passed
|
||||||
|
// to the service.
|
||||||
|
// You can provide service exit code in exitCode return parameter,
|
||||||
|
// with 0 being "no error". You can also indicate if exit code,
|
||||||
|
// if any, is service specific or not by using svcSpecificEC
|
||||||
|
// parameter.
|
||||||
|
Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// These are used by asm code.
|
||||||
|
goWaitsH uintptr
|
||||||
|
cWaitsH uintptr
|
||||||
|
ssHandle uintptr
|
||||||
|
sName *uint16
|
||||||
|
sArgc uintptr
|
||||||
|
sArgv **uint16
|
||||||
|
ctlHandlerExProc uintptr
|
||||||
|
cSetEvent uintptr
|
||||||
|
cWaitForSingleObject uintptr
|
||||||
|
cRegisterServiceCtrlHandlerExW uintptr
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
k := syscall.MustLoadDLL("kernel32.dll")
|
||||||
|
cSetEvent = k.MustFindProc("SetEvent").Addr()
|
||||||
|
cWaitForSingleObject = k.MustFindProc("WaitForSingleObject").Addr()
|
||||||
|
a := syscall.MustLoadDLL("advapi32.dll")
|
||||||
|
cRegisterServiceCtrlHandlerExW = a.MustFindProc("RegisterServiceCtrlHandlerExW").Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The HandlerEx prototype also has a context pointer but since we don't use
|
||||||
|
// it at start-up time we don't have to pass it over either.
|
||||||
|
type ctlEvent struct {
|
||||||
|
cmd Cmd
|
||||||
|
eventType uint32
|
||||||
|
eventData uintptr
|
||||||
|
errno uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// service provides access to windows service api.
|
||||||
|
type service struct {
|
||||||
|
name string
|
||||||
|
h windows.Handle
|
||||||
|
cWaits *event
|
||||||
|
goWaits *event
|
||||||
|
c chan ctlEvent
|
||||||
|
handler Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func newService(name string, handler Handler) (*service, error) {
|
||||||
|
var s service
|
||||||
|
var err error
|
||||||
|
s.name = name
|
||||||
|
s.c = make(chan ctlEvent)
|
||||||
|
s.handler = handler
|
||||||
|
s.cWaits, err = newEvent()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.goWaits, err = newEvent()
|
||||||
|
if err != nil {
|
||||||
|
s.cWaits.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) close() error {
|
||||||
|
s.cWaits.Close()
|
||||||
|
s.goWaits.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type exitCode struct {
|
||||||
|
isSvcSpecific bool
|
||||||
|
errno uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) updateStatus(status *Status, ec *exitCode) error {
|
||||||
|
if s.h == 0 {
|
||||||
|
return errors.New("updateStatus with no service status handle")
|
||||||
|
}
|
||||||
|
var t windows.SERVICE_STATUS
|
||||||
|
t.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS
|
||||||
|
t.CurrentState = uint32(status.State)
|
||||||
|
if status.Accepts&AcceptStop != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_STOP
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptShutdown != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_SHUTDOWN
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptPauseAndContinue != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_PAUSE_CONTINUE
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptParamChange != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_PARAMCHANGE
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptNetBindChange != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_NETBINDCHANGE
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptHardwareProfileChange != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptPowerEvent != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_POWEREVENT
|
||||||
|
}
|
||||||
|
if status.Accepts&AcceptSessionChange != 0 {
|
||||||
|
t.ControlsAccepted |= windows.SERVICE_ACCEPT_SESSIONCHANGE
|
||||||
|
}
|
||||||
|
if ec.errno == 0 {
|
||||||
|
t.Win32ExitCode = windows.NO_ERROR
|
||||||
|
t.ServiceSpecificExitCode = windows.NO_ERROR
|
||||||
|
} else if ec.isSvcSpecific {
|
||||||
|
t.Win32ExitCode = uint32(windows.ERROR_SERVICE_SPECIFIC_ERROR)
|
||||||
|
t.ServiceSpecificExitCode = ec.errno
|
||||||
|
} else {
|
||||||
|
t.Win32ExitCode = ec.errno
|
||||||
|
t.ServiceSpecificExitCode = windows.NO_ERROR
|
||||||
|
}
|
||||||
|
t.CheckPoint = status.CheckPoint
|
||||||
|
t.WaitHint = status.WaitHint
|
||||||
|
return windows.SetServiceStatus(s.h, &t)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
sysErrSetServiceStatusFailed = uint32(syscall.APPLICATION_ERROR) + iota
|
||||||
|
sysErrNewThreadInCallback
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *service) run() {
|
||||||
|
s.goWaits.Wait()
|
||||||
|
s.h = windows.Handle(ssHandle)
|
||||||
|
argv := (*[100]*int16)(unsafe.Pointer(sArgv))[:sArgc]
|
||||||
|
args := make([]string, len(argv))
|
||||||
|
for i, a := range argv {
|
||||||
|
args[i] = syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(a))[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdsToHandler := make(chan ChangeRequest)
|
||||||
|
changesFromHandler := make(chan Status)
|
||||||
|
exitFromHandler := make(chan exitCode)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ss, errno := s.handler.Execute(args, cmdsToHandler, changesFromHandler)
|
||||||
|
exitFromHandler <- exitCode{ss, errno}
|
||||||
|
}()
|
||||||
|
|
||||||
|
status := Status{State: Stopped}
|
||||||
|
ec := exitCode{isSvcSpecific: true, errno: 0}
|
||||||
|
var outch chan ChangeRequest
|
||||||
|
inch := s.c
|
||||||
|
var cmd Cmd
|
||||||
|
var evtype uint32
|
||||||
|
var evdata uintptr
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case r := <-inch:
|
||||||
|
if r.errno != 0 {
|
||||||
|
ec.errno = r.errno
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
inch = nil
|
||||||
|
outch = cmdsToHandler
|
||||||
|
cmd = r.cmd
|
||||||
|
evtype = r.eventType
|
||||||
|
evdata = r.eventData
|
||||||
|
case outch <- ChangeRequest{cmd, evtype, evdata, status}:
|
||||||
|
inch = s.c
|
||||||
|
outch = nil
|
||||||
|
case c := <-changesFromHandler:
|
||||||
|
err := s.updateStatus(&c, &ec)
|
||||||
|
if err != nil {
|
||||||
|
// best suitable error number
|
||||||
|
ec.errno = sysErrSetServiceStatusFailed
|
||||||
|
if err2, ok := err.(syscall.Errno); ok {
|
||||||
|
ec.errno = uint32(err2)
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
status = c
|
||||||
|
case ec = <-exitFromHandler:
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.updateStatus(&Status{State: Stopped}, &ec)
|
||||||
|
s.cWaits.Set()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCallback(fn interface{}) (cb uintptr, err error) {
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
if r == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cb = 0
|
||||||
|
switch v := r.(type) {
|
||||||
|
case string:
|
||||||
|
err = errors.New(v)
|
||||||
|
case error:
|
||||||
|
err = v
|
||||||
|
default:
|
||||||
|
err = errors.New("unexpected panic in syscall.NewCallback")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return syscall.NewCallback(fn), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BUG(brainman): There is no mechanism to run multiple services
|
||||||
|
// inside one single executable. Perhaps, it can be overcome by
|
||||||
|
// using RegisterServiceCtrlHandlerEx Windows api.
|
||||||
|
|
||||||
|
// Run executes service name by calling appropriate handler function.
|
||||||
|
func Run(name string, handler Handler) error {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
|
||||||
|
tid := windows.GetCurrentThreadId()
|
||||||
|
|
||||||
|
s, err := newService(name, handler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctlHandler := func(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
|
||||||
|
e := ctlEvent{cmd: Cmd(ctl), eventType: evtype, eventData: evdata}
|
||||||
|
// We assume that this callback function is running on
|
||||||
|
// the same thread as Run. Nowhere in MS documentation
|
||||||
|
// I could find statement to guarantee that. So putting
|
||||||
|
// check here to verify, otherwise things will go bad
|
||||||
|
// quickly, if ignored.
|
||||||
|
i := windows.GetCurrentThreadId()
|
||||||
|
if i != tid {
|
||||||
|
e.errno = sysErrNewThreadInCallback
|
||||||
|
}
|
||||||
|
s.c <- e
|
||||||
|
// Always return NO_ERROR (0) for now.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var svcmain uintptr
|
||||||
|
getServiceMain(&svcmain)
|
||||||
|
t := []windows.SERVICE_TABLE_ENTRY{
|
||||||
|
{syscall.StringToUTF16Ptr(s.name), svcmain},
|
||||||
|
{nil, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
goWaitsH = uintptr(s.goWaits.h)
|
||||||
|
cWaitsH = uintptr(s.cWaits.h)
|
||||||
|
sName = t[0].ServiceName
|
||||||
|
ctlHandlerExProc, err = newCallback(ctlHandler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go s.run()
|
||||||
|
|
||||||
|
err = windows.StartServiceCtrlDispatcher(&t[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusHandle returns service status handle. It is safe to call this function
|
||||||
|
// from inside the Handler.Execute because then it is guaranteed to be set.
|
||||||
|
// This code will have to change once multiple services are possible per process.
|
||||||
|
func StatusHandle() windows.Handle {
|
||||||
|
return windows.Handle(ssHandle)
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package svc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows/svc"
|
||||||
|
"golang.org/x/sys/windows/svc/mgr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getState(t *testing.T, s *mgr.Service) svc.State {
|
||||||
|
status, err := s.Query()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Query(%s) failed: %s", s.Name, err)
|
||||||
|
}
|
||||||
|
return status.State
|
||||||
|
}
|
||||||
|
|
||||||
|
func testState(t *testing.T, s *mgr.Service, want svc.State) {
|
||||||
|
have := getState(t, s)
|
||||||
|
if have != want {
|
||||||
|
t.Fatalf("%s state is=%d want=%d", s.Name, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitState(t *testing.T, s *mgr.Service, want svc.State) {
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
have := getState(t, s)
|
||||||
|
if have == want {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if i > 10 {
|
||||||
|
t.Fatalf("%s state is=%d, waiting timeout", s.Name, have)
|
||||||
|
}
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExample(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping test in short mode - it modifies system services")
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = "myservice"
|
||||||
|
|
||||||
|
m, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SCM connection failed: %s", err)
|
||||||
|
}
|
||||||
|
defer m.Disconnect()
|
||||||
|
|
||||||
|
dir, err := ioutil.TempDir("", "svc")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create temp directory: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
exepath := filepath.Join(dir, "a.exe")
|
||||||
|
o, err := exec.Command("go", "build", "-o", exepath, "golang.org/x/sys/windows/svc/example").CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to build service program: %v\n%v", err, string(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := m.OpenService(name)
|
||||||
|
if err == nil {
|
||||||
|
err = s.Delete()
|
||||||
|
if err != nil {
|
||||||
|
s.Close()
|
||||||
|
t.Fatalf("Delete failed: %s", err)
|
||||||
|
}
|
||||||
|
s.Close()
|
||||||
|
}
|
||||||
|
s, err = m.CreateService(name, exepath, mgr.Config{DisplayName: "my service"}, "is", "auto-started")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CreateService(%s) failed: %v", name, err)
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
testState(t, s, svc.Stopped)
|
||||||
|
err = s.Start("is", "manual-started")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Start(%s) failed: %s", s.Name, err)
|
||||||
|
}
|
||||||
|
waitState(t, s, svc.Running)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
// testing deadlock from issues 4.
|
||||||
|
_, err = s.Control(svc.Interrogate)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Control(%s) failed: %s", s.Name, err)
|
||||||
|
}
|
||||||
|
_, err = s.Control(svc.Interrogate)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Control(%s) failed: %s", s.Name, err)
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
_, err = s.Control(svc.Stop)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Control(%s) failed: %s", s.Name, err)
|
||||||
|
}
|
||||||
|
waitState(t, s, svc.Stopped)
|
||||||
|
|
||||||
|
err = s.Delete()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Delete failed: %s", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// func servicemain(argc uint32, argv **uint16)
|
||||||
|
TEXT ·servicemain(SB),7,$0
|
||||||
|
MOVL argc+0(FP), AX
|
||||||
|
MOVL AX, ·sArgc(SB)
|
||||||
|
MOVL argv+4(FP), AX
|
||||||
|
MOVL AX, ·sArgv(SB)
|
||||||
|
|
||||||
|
PUSHL BP
|
||||||
|
PUSHL BX
|
||||||
|
PUSHL SI
|
||||||
|
PUSHL DI
|
||||||
|
|
||||||
|
SUBL $12, SP
|
||||||
|
|
||||||
|
MOVL ·sName(SB), AX
|
||||||
|
MOVL AX, (SP)
|
||||||
|
MOVL $·servicectlhandler(SB), AX
|
||||||
|
MOVL AX, 4(SP)
|
||||||
|
MOVL $0, 8(SP)
|
||||||
|
MOVL ·cRegisterServiceCtrlHandlerExW(SB), AX
|
||||||
|
MOVL SP, BP
|
||||||
|
CALL AX
|
||||||
|
MOVL BP, SP
|
||||||
|
CMPL AX, $0
|
||||||
|
JE exit
|
||||||
|
MOVL AX, ·ssHandle(SB)
|
||||||
|
|
||||||
|
MOVL ·goWaitsH(SB), AX
|
||||||
|
MOVL AX, (SP)
|
||||||
|
MOVL ·cSetEvent(SB), AX
|
||||||
|
MOVL SP, BP
|
||||||
|
CALL AX
|
||||||
|
MOVL BP, SP
|
||||||
|
|
||||||
|
MOVL ·cWaitsH(SB), AX
|
||||||
|
MOVL AX, (SP)
|
||||||
|
MOVL $-1, AX
|
||||||
|
MOVL AX, 4(SP)
|
||||||
|
MOVL ·cWaitForSingleObject(SB), AX
|
||||||
|
MOVL SP, BP
|
||||||
|
CALL AX
|
||||||
|
MOVL BP, SP
|
||||||
|
|
||||||
|
exit:
|
||||||
|
ADDL $12, SP
|
||||||
|
|
||||||
|
POPL DI
|
||||||
|
POPL SI
|
||||||
|
POPL BX
|
||||||
|
POPL BP
|
||||||
|
|
||||||
|
MOVL 0(SP), CX
|
||||||
|
ADDL $12, SP
|
||||||
|
JMP CX
|
||||||
|
|
||||||
|
// I do not know why, but this seems to be the only way to call
|
||||||
|
// ctlHandlerProc on Windows 7.
|
||||||
|
|
||||||
|
// func servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
|
||||||
|
TEXT ·servicectlhandler(SB),7,$0
|
||||||
|
MOVL ·ctlHandlerExProc(SB), CX
|
||||||
|
JMP CX
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// func servicemain(argc uint32, argv **uint16)
|
||||||
|
TEXT ·servicemain(SB),7,$0
|
||||||
|
MOVL CX, ·sArgc(SB)
|
||||||
|
MOVL DX, ·sArgv(SB)
|
||||||
|
|
||||||
|
SUBQ $32, SP // stack for the first 4 syscall params
|
||||||
|
|
||||||
|
MOVQ ·sName(SB), CX
|
||||||
|
MOVQ $·servicectlhandler(SB), DX
|
||||||
|
// BUG(pastarmovj): Figure out a way to pass in context in R8.
|
||||||
|
MOVQ ·cRegisterServiceCtrlHandlerExW(SB), AX
|
||||||
|
CALL AX
|
||||||
|
CMPQ AX, $0
|
||||||
|
JE exit
|
||||||
|
MOVQ AX, ·ssHandle(SB)
|
||||||
|
|
||||||
|
MOVQ ·goWaitsH(SB), CX
|
||||||
|
MOVQ ·cSetEvent(SB), AX
|
||||||
|
CALL AX
|
||||||
|
|
||||||
|
MOVQ ·cWaitsH(SB), CX
|
||||||
|
MOVQ $4294967295, DX
|
||||||
|
MOVQ ·cWaitForSingleObject(SB), AX
|
||||||
|
CALL AX
|
||||||
|
|
||||||
|
exit:
|
||||||
|
ADDQ $32, SP
|
||||||
|
RET
|
||||||
|
|
||||||
|
// I do not know why, but this seems to be the only way to call
|
||||||
|
// ctlHandlerProc on Windows 7.
|
||||||
|
|
||||||
|
// func ·servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
|
||||||
|
TEXT ·servicectlhandler(SB),7,$0
|
||||||
|
MOVQ ·ctlHandlerExProc(SB), AX
|
||||||
|
JMP AX
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package windows contains an interface to the low-level operating system
|
||||||
|
// primitives. OS details vary depending on the underlying system, and
|
||||||
|
// by default, godoc will display the OS-specific documentation for the current
|
||||||
|
// system. If you want godoc to display syscall documentation for another
|
||||||
|
// system, set $GOOS and $GOARCH to the desired system. For example, if
|
||||||
|
// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
|
||||||
|
// to freebsd and $GOARCH to arm.
|
||||||
|
//
|
||||||
|
// The primary use of this package is inside other packages that provide a more
|
||||||
|
// portable interface to the system, such as "os", "time" and "net". Use
|
||||||
|
// those packages rather than this one if you can.
|
||||||
|
//
|
||||||
|
// For details of the functions and data types in this package consult
|
||||||
|
// the manuals for the appropriate operating system.
|
||||||
|
//
|
||||||
|
// These calls return err == nil to indicate success; otherwise
|
||||||
|
// err represents an operating system error describing the failure and
|
||||||
|
// holds a value of type syscall.Errno.
|
||||||
|
package windows // import "golang.org/x/sys/windows"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ByteSliceFromString returns a NUL-terminated slice of bytes
|
||||||
|
// containing the text of s. If s contains a NUL byte at any
|
||||||
|
// location, it returns (nil, syscall.EINVAL).
|
||||||
|
func ByteSliceFromString(s string) ([]byte, error) {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] == 0 {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a := make([]byte, len(s)+1)
|
||||||
|
copy(a, s)
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytePtrFromString returns a pointer to a NUL-terminated array of
|
||||||
|
// bytes containing the text of s. If s contains a NUL byte at any
|
||||||
|
// location, it returns (nil, syscall.EINVAL).
|
||||||
|
func BytePtrFromString(s string) (*byte, error) {
|
||||||
|
a, err := ByteSliceFromString(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &a[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single-word zero for use when we need a valid pointer to 0 bytes.
|
||||||
|
// See mksyscall.pl.
|
||||||
|
var _zero uintptr
|
||||||
|
|
||||||
|
func (ts *Timespec) Unix() (sec int64, nsec int64) {
|
||||||
|
return int64(ts.Sec), int64(ts.Nsec)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tv *Timeval) Unix() (sec int64, nsec int64) {
|
||||||
|
return int64(tv.Sec), int64(tv.Usec) * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts *Timespec) Nano() int64 {
|
||||||
|
return int64(ts.Sec)*1e9 + int64(ts.Nsec)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tv *Timeval) Nano() int64 {
|
||||||
|
return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windows_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testSetGetenv(t *testing.T, key, value string) {
|
||||||
|
err := windows.Setenv(key, value)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Setenv failed to set %q: %v", value, err)
|
||||||
|
}
|
||||||
|
newvalue, found := windows.Getenv(key)
|
||||||
|
if !found {
|
||||||
|
t.Fatalf("Getenv failed to find %v variable (want value %q)", key, value)
|
||||||
|
}
|
||||||
|
if newvalue != value {
|
||||||
|
t.Fatalf("Getenv(%v) = %q; want %q", key, newvalue, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnv(t *testing.T) {
|
||||||
|
testSetGetenv(t, "TESTENV", "AVALUE")
|
||||||
|
// make sure TESTENV gets set to "", not deleted
|
||||||
|
testSetGetenv(t, "TESTENV", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetProcAddressByOrdinal(t *testing.T) {
|
||||||
|
// Attempt calling shlwapi.dll:IsOS, resolving it by ordinal, as
|
||||||
|
// suggested in
|
||||||
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb773795.aspx
|
||||||
|
h, err := windows.LoadLibrary("shlwapi.dll")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load shlwapi.dll: %s", err)
|
||||||
|
}
|
||||||
|
procIsOS, err := windows.GetProcAddressByOrdinal(h, 437)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not find shlwapi.dll:IsOS by ordinal: %s", err)
|
||||||
|
}
|
||||||
|
const OS_NT = 1
|
||||||
|
r, _, _ := syscall.Syscall(procIsOS, 1, OS_NT, 0, 0)
|
||||||
|
if r == 0 {
|
||||||
|
t.Error("shlwapi.dll:IsOS(OS_NT) returned 0, expected non-zero value")
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,107 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package windows_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWin32finddata(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "go-build")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create temp directory: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
path := filepath.Join(dir, "long_name.and_extension")
|
||||||
|
f, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create %v: %v", path, err)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
type X struct {
|
||||||
|
fd windows.Win32finddata
|
||||||
|
got byte
|
||||||
|
pad [10]byte // to protect ourselves
|
||||||
|
|
||||||
|
}
|
||||||
|
var want byte = 2 // it is unlikely to have this character in the filename
|
||||||
|
x := X{got: want}
|
||||||
|
|
||||||
|
pathp, _ := windows.UTF16PtrFromString(path)
|
||||||
|
h, err := windows.FindFirstFile(pathp, &(x.fd))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("FindFirstFile failed: %v", err)
|
||||||
|
}
|
||||||
|
err = windows.FindClose(h)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("FindClose failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.got != want {
|
||||||
|
t.Fatalf("memory corruption: want=%d got=%d", want, x.got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatMessage(t *testing.T) {
|
||||||
|
dll := windows.MustLoadDLL("pdh.dll")
|
||||||
|
|
||||||
|
pdhOpenQuery := func(datasrc *uint16, userdata uint32, query *windows.Handle) (errno uintptr) {
|
||||||
|
r0, _, _ := syscall.Syscall(dll.MustFindProc("PdhOpenQueryW").Addr(), 3, uintptr(unsafe.Pointer(datasrc)), uintptr(userdata), uintptr(unsafe.Pointer(query)))
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
pdhCloseQuery := func(query windows.Handle) (errno uintptr) {
|
||||||
|
r0, _, _ := syscall.Syscall(dll.MustFindProc("PdhCloseQuery").Addr(), 1, uintptr(query), 0, 0)
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
var q windows.Handle
|
||||||
|
name, err := windows.UTF16PtrFromString("no_such_source")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
errno := pdhOpenQuery(name, 0, &q)
|
||||||
|
if errno == 0 {
|
||||||
|
pdhCloseQuery(q)
|
||||||
|
t.Fatal("PdhOpenQuery succeeded, but expected to fail.")
|
||||||
|
}
|
||||||
|
|
||||||
|
const flags uint32 = syscall.FORMAT_MESSAGE_FROM_HMODULE | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS
|
||||||
|
buf := make([]uint16, 300)
|
||||||
|
_, err = windows.FormatMessage(flags, uintptr(dll.Handle), uint32(errno), 0, buf, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("FormatMessage for handle=%x and errno=%x failed: %v", dll.Handle, errno, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func abort(funcname string, err error) {
|
||||||
|
panic(funcname + " failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleLoadLibrary() {
|
||||||
|
h, err := windows.LoadLibrary("kernel32.dll")
|
||||||
|
if err != nil {
|
||||||
|
abort("LoadLibrary", err)
|
||||||
|
}
|
||||||
|
defer windows.FreeLibrary(h)
|
||||||
|
proc, err := windows.GetProcAddress(h, "GetVersion")
|
||||||
|
if err != nil {
|
||||||
|
abort("GetProcAddress", err)
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
|
||||||
|
major := byte(r)
|
||||||
|
minor := uint8(r >> 8)
|
||||||
|
build := uint16(r >> 16)
|
||||||
|
print("windows version ", major, ".", minor, " (Build ", build, ")\n")
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
type WSAData struct {
|
||||||
|
Version uint16
|
||||||
|
HighVersion uint16
|
||||||
|
Description [WSADESCRIPTION_LEN + 1]byte
|
||||||
|
SystemStatus [WSASYS_STATUS_LEN + 1]byte
|
||||||
|
MaxSockets uint16
|
||||||
|
MaxUdpDg uint16
|
||||||
|
VendorInfo *byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type Servent struct {
|
||||||
|
Name *byte
|
||||||
|
Aliases **byte
|
||||||
|
Port uint16
|
||||||
|
Proto *byte
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
type WSAData struct {
|
||||||
|
Version uint16
|
||||||
|
HighVersion uint16
|
||||||
|
MaxSockets uint16
|
||||||
|
MaxUdpDg uint16
|
||||||
|
VendorInfo *byte
|
||||||
|
Description [WSADESCRIPTION_LEN + 1]byte
|
||||||
|
SystemStatus [WSASYS_STATUS_LEN + 1]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type Servent struct {
|
||||||
|
Name *byte
|
||||||
|
Aliases **byte
|
||||||
|
Proto *byte
|
||||||
|
Port uint16
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue