mirror of
https://github.com/golang/sys.git
synced 2026-02-08 03:36:03 +03:00
windows/svc: add new package to help create and manage Windows services
Change-Id: I58bb446aaa387b31d8a9ff4217793a170b96a7e2 Reviewed-on: https://go-review.googlesource.com/9104 Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
20
windows/eventlog.go
Normal file
20
windows/eventlog.go
Normal file
@@ -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
|
||||
@@ -91,12 +91,56 @@ const (
|
||||
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
|
||||
)
|
||||
|
||||
//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.
|
||||
@@ -286,6 +330,11 @@ type Tokenprimarygroup struct {
|
||||
PrimaryGroup *SID
|
||||
}
|
||||
|
||||
type Tokengroups struct {
|
||||
GroupCount uint32
|
||||
Groups [1]SIDAndAttributes
|
||||
}
|
||||
|
||||
//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
|
||||
@@ -346,6 +395,15 @@ func (t Token) GetTokenUser() (*Tokenuser, error) {
|
||||
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.
|
||||
|
||||
143
windows/service.go
Normal file
143
windows/service.go
Normal file
@@ -0,0 +1,143 @@
|
||||
// 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
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
//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
|
||||
56
windows/svc/debug/log.go
Normal file
56
windows/svc/debug/log.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// 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 debug
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Log interface allows different log implementations to be used.
|
||||
type Log interface {
|
||||
Close() error
|
||||
Info(eid uint32, msg string) error
|
||||
Warning(eid uint32, msg string) error
|
||||
Error(eid uint32, msg string) error
|
||||
}
|
||||
|
||||
// ConsoleLog provides access to the console.
|
||||
type ConsoleLog struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// New creates new ConsoleLog.
|
||||
func New(source string) *ConsoleLog {
|
||||
return &ConsoleLog{Name: source}
|
||||
}
|
||||
|
||||
// Close closes console log l.
|
||||
func (l *ConsoleLog) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *ConsoleLog) report(kind string, eid uint32, msg string) error {
|
||||
s := l.Name + "." + kind + "(" + strconv.Itoa(int(eid)) + "): " + msg + "\n"
|
||||
_, err := os.Stdout.Write([]byte(s))
|
||||
return err
|
||||
}
|
||||
|
||||
// Info writes an information event msg with event id eid to the console l.
|
||||
func (l *ConsoleLog) Info(eid uint32, msg string) error {
|
||||
return l.report("info", eid, msg)
|
||||
}
|
||||
|
||||
// Warning writes an warning event msg with event id eid to the console l.
|
||||
func (l *ConsoleLog) Warning(eid uint32, msg string) error {
|
||||
return l.report("warn", eid, msg)
|
||||
}
|
||||
|
||||
// Error writes an error event msg with event id eid to the console l.
|
||||
func (l *ConsoleLog) Error(eid uint32, msg string) error {
|
||||
return l.report("error", eid, msg)
|
||||
}
|
||||
45
windows/svc/debug/service.go
Normal file
45
windows/svc/debug/service.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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 debug provides facilities to execute svc.Handler on console.
|
||||
//
|
||||
package debug
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows/svc"
|
||||
)
|
||||
|
||||
// Run executes service name by calling appropriate handler function.
|
||||
// The process is running on console, unlike real service. Use Ctrl+C to
|
||||
// send "Stop" command to your service.
|
||||
func Run(name string, handler svc.Handler) error {
|
||||
cmds := make(chan svc.ChangeRequest)
|
||||
changes := make(chan svc.Status)
|
||||
|
||||
sig := make(chan os.Signal)
|
||||
signal.Notify(sig)
|
||||
|
||||
go func() {
|
||||
status := svc.Status{State: svc.Stopped}
|
||||
for {
|
||||
select {
|
||||
case <-sig:
|
||||
cmds <- svc.ChangeRequest{svc.Stop, status}
|
||||
case status = <-changes:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
_, errno := handler.Execute([]string{name}, cmds, changes)
|
||||
if errno != 0 {
|
||||
return syscall.Errno(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
48
windows/svc/event.go
Normal file
48
windows/svc/event.go
Normal file
@@ -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
|
||||
}
|
||||
80
windows/svc/eventlog/install.go
Normal file
80
windows/svc/eventlog/install.go
Normal file
@@ -0,0 +1,80 @@
|
||||
// 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 eventlog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
const (
|
||||
// Log levels.
|
||||
Info = windows.EVENTLOG_INFORMATION_TYPE
|
||||
Warning = windows.EVENTLOG_WARNING_TYPE
|
||||
Error = windows.EVENTLOG_ERROR_TYPE
|
||||
)
|
||||
|
||||
const addKeyName = `SYSTEM\CurrentControlSet\Services\EventLog\Application`
|
||||
|
||||
// Install modifies PC registry to allow logging with an event source src.
|
||||
// It adds all required keys and values to the event log registry key.
|
||||
// Install uses msgFile as the event message file. If useExpandKey is true,
|
||||
// the event message file is installed as REG_EXPAND_SZ value,
|
||||
// otherwise as REG_SZ. Use bitwise of log.Error, log.Warning and
|
||||
// log.Info to specify events supported by the new event source.
|
||||
func Install(src, msgFile string, useExpandKey bool, eventsSupported uint32) error {
|
||||
appkey, err := registry.OpenKey(registry.LOCAL_MACHINE, addKeyName, registry.CREATE_SUB_KEY)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer appkey.Close()
|
||||
|
||||
sk, alreadyExist, err := registry.CreateKey(appkey, src, registry.SET_VALUE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sk.Close()
|
||||
if alreadyExist {
|
||||
return errors.New(addKeyName + `\` + src + " registry key already exists")
|
||||
}
|
||||
|
||||
err = sk.SetDWordValue("CustomSource", 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if useExpandKey {
|
||||
err = sk.SetExpandStringValue("EventMessageFile", msgFile)
|
||||
} else {
|
||||
err = sk.SetStringValue("EventMessageFile", msgFile)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = sk.SetDWordValue("TypesSupported", eventsSupported)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InstallAsEventCreate is the same as Install, but uses
|
||||
// %SystemRoot%\System32\EventCreate.exe as the event message file.
|
||||
func InstallAsEventCreate(src string, eventsSupported uint32) error {
|
||||
return Install(src, "%SystemRoot%\\System32\\EventCreate.exe", true, eventsSupported)
|
||||
}
|
||||
|
||||
// Remove deletes all registry elements installed by the correspondent Install.
|
||||
func Remove(src string) error {
|
||||
appkey, err := registry.OpenKey(registry.LOCAL_MACHINE, addKeyName, registry.SET_VALUE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer appkey.Close()
|
||||
return registry.DeleteKey(appkey, src)
|
||||
}
|
||||
70
windows/svc/eventlog/log.go
Normal file
70
windows/svc/eventlog/log.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// 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 eventlog implements access to Windows event log.
|
||||
//
|
||||
package eventlog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// Log provides access to the system log.
|
||||
type Log struct {
|
||||
Handle windows.Handle
|
||||
}
|
||||
|
||||
// Open retrieves a handle to the specified event log.
|
||||
func Open(source string) (*Log, error) {
|
||||
return OpenRemote("", source)
|
||||
}
|
||||
|
||||
// OpenRemote does the same as Open, but on different computer host.
|
||||
func OpenRemote(host, source string) (*Log, error) {
|
||||
if source == "" {
|
||||
return nil, errors.New("Specify event log source")
|
||||
}
|
||||
var s *uint16
|
||||
if host != "" {
|
||||
s = syscall.StringToUTF16Ptr(host)
|
||||
}
|
||||
h, err := windows.RegisterEventSource(s, syscall.StringToUTF16Ptr(source))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Log{Handle: h}, nil
|
||||
}
|
||||
|
||||
// Close closes event log l.
|
||||
func (l *Log) Close() error {
|
||||
return windows.DeregisterEventSource(l.Handle)
|
||||
}
|
||||
|
||||
func (l *Log) report(etype uint16, eid uint32, msg string) error {
|
||||
ss := []*uint16{syscall.StringToUTF16Ptr(msg)}
|
||||
return windows.ReportEvent(l.Handle, etype, 0, eid, 0, 1, 0, &ss[0], nil)
|
||||
}
|
||||
|
||||
// Info writes an information event msg with event id eid to the end of event log l.
|
||||
// When EventCreate.exe is used, eid must be between 1 and 1000.
|
||||
func (l *Log) Info(eid uint32, msg string) error {
|
||||
return l.report(windows.EVENTLOG_INFORMATION_TYPE, eid, msg)
|
||||
}
|
||||
|
||||
// Warning writes an warning event msg with event id eid to the end of event log l.
|
||||
// When EventCreate.exe is used, eid must be between 1 and 1000.
|
||||
func (l *Log) Warning(eid uint32, msg string) error {
|
||||
return l.report(windows.EVENTLOG_WARNING_TYPE, eid, msg)
|
||||
}
|
||||
|
||||
// Error writes an error event msg with event id eid to the end of event log l.
|
||||
// When EventCreate.exe is used, eid must be between 1 and 1000.
|
||||
func (l *Log) Error(eid uint32, msg string) error {
|
||||
return l.report(windows.EVENTLOG_ERROR_TYPE, eid, msg)
|
||||
}
|
||||
51
windows/svc/eventlog/log_test.go
Normal file
51
windows/svc/eventlog/log_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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 eventlog_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
)
|
||||
|
||||
func TestLog(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode - it modifies system logs")
|
||||
}
|
||||
|
||||
const name = "mylog"
|
||||
const supports = eventlog.Error | eventlog.Warning | eventlog.Info
|
||||
err := eventlog.InstallAsEventCreate(name, supports)
|
||||
if err != nil {
|
||||
t.Fatalf("Install failed: %s", err)
|
||||
}
|
||||
defer func() {
|
||||
err = eventlog.Remove(name)
|
||||
if err != nil {
|
||||
t.Fatalf("Remove failed: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
l, err := eventlog.Open(name)
|
||||
if err != nil {
|
||||
t.Fatalf("Open failed: %s", err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
err = l.Info(1, "info")
|
||||
if err != nil {
|
||||
t.Fatalf("Info failed: %s", err)
|
||||
}
|
||||
err = l.Warning(2, "warning")
|
||||
if err != nil {
|
||||
t.Fatalf("Warning failed: %s", err)
|
||||
}
|
||||
err = l.Error(3, "error")
|
||||
if err != nil {
|
||||
t.Fatalf("Error failed: %s", err)
|
||||
}
|
||||
}
|
||||
22
windows/svc/example/beep.go
Normal file
22
windows/svc/example/beep.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// BUG(brainman): MessageBeep Windows api is broken on Windows 7,
|
||||
// so this example does not beep when runs as service on Windows 7.
|
||||
|
||||
var (
|
||||
beepFunc = syscall.MustLoadDLL("user32.dll").MustFindProc("MessageBeep")
|
||||
)
|
||||
|
||||
func beep() {
|
||||
beepFunc.Call(0xffffffff)
|
||||
}
|
||||
92
windows/svc/example/install.go
Normal file
92
windows/svc/example/install.go
Normal file
@@ -0,0 +1,92 @@
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
)
|
||||
|
||||
func exePath() (string, error) {
|
||||
prog := os.Args[0]
|
||||
p, err := filepath.Abs(prog)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fi, err := os.Stat(p)
|
||||
if err == nil {
|
||||
if !fi.Mode().IsDir() {
|
||||
return p, nil
|
||||
}
|
||||
err = fmt.Errorf("%s is directory", p)
|
||||
}
|
||||
if filepath.Ext(p) == "" {
|
||||
p += ".exe"
|
||||
fi, err := os.Stat(p)
|
||||
if err == nil {
|
||||
if !fi.Mode().IsDir() {
|
||||
return p, nil
|
||||
}
|
||||
err = fmt.Errorf("%s is directory", p)
|
||||
}
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
func installService(name, desc string) error {
|
||||
exepath, err := exePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err == nil {
|
||||
s.Close()
|
||||
return fmt.Errorf("service %s already exists", name)
|
||||
}
|
||||
s, err = m.CreateService(name, exepath, mgr.Config{DisplayName: desc}, "is", "auto-started")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info)
|
||||
if err != nil {
|
||||
s.Delete()
|
||||
return fmt.Errorf("SetupEventLogSource() failed: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeService(name string) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service %s is not installed", name)
|
||||
}
|
||||
defer s.Close()
|
||||
err = s.Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = eventlog.Remove(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
76
windows/svc/example/main.go
Normal file
76
windows/svc/example/main.go
Normal file
@@ -0,0 +1,76 @@
|
||||
// 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
|
||||
|
||||
// Example service program that beeps.
|
||||
//
|
||||
// The program demonstrates how to create Windows service and
|
||||
// install / remove it on a computer. It also shows how to
|
||||
// stop / start / pause / continue any service, and how to
|
||||
// write to event log. It also shows how to use debug
|
||||
// facilities available in debug package.
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/windows/svc"
|
||||
)
|
||||
|
||||
func usage(errmsg string) {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
"%s\n\n"+
|
||||
"usage: %s <command>\n"+
|
||||
" where <command> is one of\n"+
|
||||
" install, remove, debug, start, stop, pause or continue.\n",
|
||||
errmsg, os.Args[0])
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func main() {
|
||||
const svcName = "myservice"
|
||||
|
||||
isIntSess, err := svc.IsAnInteractiveSession()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
|
||||
}
|
||||
if !isIntSess {
|
||||
runService(svcName, false)
|
||||
return
|
||||
}
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
usage("no command specified")
|
||||
}
|
||||
|
||||
cmd := strings.ToLower(os.Args[1])
|
||||
switch cmd {
|
||||
case "debug":
|
||||
runService(svcName, true)
|
||||
return
|
||||
case "install":
|
||||
err = installService(svcName, "my service")
|
||||
case "remove":
|
||||
err = removeService(svcName)
|
||||
case "start":
|
||||
err = startService(svcName)
|
||||
case "stop":
|
||||
err = controlService(svcName, svc.Stop, svc.Stopped)
|
||||
case "pause":
|
||||
err = controlService(svcName, svc.Pause, svc.Paused)
|
||||
case "continue":
|
||||
err = controlService(svcName, svc.Continue, svc.Running)
|
||||
default:
|
||||
usage(fmt.Sprintf("invalid command %s", cmd))
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("failed to %s %s: %v", cmd, svcName, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
62
windows/svc/example/manage.go
Normal file
62
windows/svc/example/manage.go
Normal file
@@ -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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
)
|
||||
|
||||
func startService(name string) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not access service: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
err = s.Start("is", "manual-started")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start service: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func controlService(name string, c svc.Cmd, to svc.State) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not access service: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
status, err := s.Control(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not send control=%d: %v", c, err)
|
||||
}
|
||||
timeout := time.Now().Add(10 * time.Second)
|
||||
for status.State != to {
|
||||
if timeout.Before(time.Now()) {
|
||||
return fmt.Errorf("timeout waiting for service to go to state=%d", to)
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
status, err = s.Query()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not retrieve service status: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
82
windows/svc/example/service.go
Normal file
82
windows/svc/example/service.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/debug"
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
)
|
||||
|
||||
var elog debug.Log
|
||||
|
||||
type myservice struct{}
|
||||
|
||||
func (m *myservice) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
|
||||
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
|
||||
changes <- svc.Status{State: svc.StartPending}
|
||||
fasttick := time.Tick(500 * time.Millisecond)
|
||||
slowtick := time.Tick(2 * time.Second)
|
||||
tick := fasttick
|
||||
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-tick:
|
||||
beep()
|
||||
elog.Info(1, "beep")
|
||||
case c := <-r:
|
||||
switch c.Cmd {
|
||||
case svc.Interrogate:
|
||||
changes <- c.CurrentStatus
|
||||
// Testing deadlock from https://code.google.com/p/winsvc/issues/detail?id=4
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
changes <- c.CurrentStatus
|
||||
case svc.Stop, svc.Shutdown:
|
||||
break loop
|
||||
case svc.Pause:
|
||||
changes <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted}
|
||||
tick = slowtick
|
||||
case svc.Continue:
|
||||
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
||||
tick = fasttick
|
||||
default:
|
||||
elog.Error(1, fmt.Sprintf("unexpected control request #%d", c))
|
||||
}
|
||||
}
|
||||
}
|
||||
changes <- svc.Status{State: svc.StopPending}
|
||||
return
|
||||
}
|
||||
|
||||
func runService(name string, isDebug bool) {
|
||||
var err error
|
||||
if isDebug {
|
||||
elog = debug.New(name)
|
||||
} else {
|
||||
elog, err = eventlog.Open(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
defer elog.Close()
|
||||
|
||||
elog.Info(1, fmt.Sprintf("starting %s service", name))
|
||||
run := svc.Run
|
||||
if isDebug {
|
||||
run = debug.Run
|
||||
}
|
||||
err = run(name, &myservice{})
|
||||
if err != nil {
|
||||
elog.Error(1, fmt.Sprintf("%s service failed: %v", name, err))
|
||||
return
|
||||
}
|
||||
elog.Info(1, fmt.Sprintf("%s service stopped", name))
|
||||
}
|
||||
24
windows/svc/go12.c
Normal file
24
windows/svc/go12.c
Normal file
@@ -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;
|
||||
}
|
||||
11
windows/svc/go12.go
Normal file
11
windows/svc/go12.go
Normal file
@@ -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)
|
||||
31
windows/svc/go13.go
Normal file
31
windows/svc/go13.go
Normal file
@@ -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)
|
||||
}
|
||||
139
windows/svc/mgr/config.go
Normal file
139
windows/svc/mgr/config.go
Normal file
@@ -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)
|
||||
}
|
||||
116
windows/svc/mgr/mgr.go
Normal file
116
windows/svc/mgr/mgr.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// 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"
|
||||
|
||||
"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.
|
||||
// If service StartType is set to StartAutomatic,
|
||||
// args will be passed to svc.Handle.Execute.
|
||||
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
|
||||
}
|
||||
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, windows.SERVICE_WIN32_OWN_PROCESS,
|
||||
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
|
||||
}
|
||||
147
windows/svc/mgr/mgr_test.go
Normal file
147
windows/svc/mgr/mgr_test.go
Normal file
@@ -0,0 +1,147 @@
|
||||
// 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"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
)
|
||||
|
||||
func TestOpenLanManServer(t *testing.T) {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
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 {
|
||||
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)
|
||||
|
||||
remove(t, s)
|
||||
}
|
||||
74
windows/svc/mgr/service.go
Normal file
74
windows/svc/mgr/service.go
Normal file
@@ -0,0 +1,74 @@
|
||||
// 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.
|
||||
|
||||
// TODO(brainman): Use EnumServicesStatus to enumerate services in the specified service control manager database.
|
||||
|
||||
// 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
|
||||
}
|
||||
62
windows/svc/security.go
Normal file
62
windows/svc/security.go
Normal file
@@ -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
|
||||
}
|
||||
316
windows/svc/service.go
Normal file
316
windows/svc/service.go
Normal file
@@ -0,0 +1,316 @@
|
||||
// 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)
|
||||
)
|
||||
|
||||
// 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)
|
||||
)
|
||||
|
||||
// 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
|
||||
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
|
||||
ctlHandlerProc uintptr
|
||||
cSetEvent uintptr
|
||||
cWaitForSingleObject uintptr
|
||||
cRegisterServiceCtrlHandlerW uintptr
|
||||
)
|
||||
|
||||
func init() {
|
||||
k := syscall.MustLoadDLL("kernel32.dll")
|
||||
cSetEvent = k.MustFindProc("SetEvent").Addr()
|
||||
cWaitForSingleObject = k.MustFindProc("WaitForSingleObject").Addr()
|
||||
a := syscall.MustLoadDLL("advapi32.dll")
|
||||
cRegisterServiceCtrlHandlerW = a.MustFindProc("RegisterServiceCtrlHandlerW").Addr()
|
||||
}
|
||||
|
||||
type ctlEvent struct {
|
||||
cmd Cmd
|
||||
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 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
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case r := <-inch:
|
||||
if r.errno != 0 {
|
||||
ec.errno = r.errno
|
||||
break loop
|
||||
}
|
||||
inch = nil
|
||||
outch = cmdsToHandler
|
||||
cmd = r.cmd
|
||||
case outch <- ChangeRequest{cmd, 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) uintptr {
|
||||
e := ctlEvent{cmd: Cmd(ctl)}
|
||||
// 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
|
||||
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
|
||||
ctlHandlerProc, err = newCallback(ctlHandler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go s.run()
|
||||
|
||||
err = windows.StartServiceCtrlDispatcher(&t[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
118
windows/svc/svc_test.go
Normal file
118
windows/svc/svc_test.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
67
windows/svc/sys_386.s
Normal file
67
windows/svc/sys_386.s
Normal file
@@ -0,0 +1,67 @@
|
||||
// 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 ·cRegisterServiceCtrlHandlerW(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) uintptr
|
||||
TEXT ·servicectlhandler(SB),7,$0
|
||||
MOVL ·ctlHandlerProc(SB), CX
|
||||
JMP CX
|
||||
41
windows/svc/sys_amd64.s
Normal file
41
windows/svc/sys_amd64.s
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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
|
||||
MOVQ ·cRegisterServiceCtrlHandlerW(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) uintptr
|
||||
TEXT ·servicectlhandler(SB),7,$0
|
||||
MOVQ ·ctlHandlerProc(SB), AX
|
||||
JMP AX
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go security_windows.go
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go
|
||||
|
||||
type Handle uintptr
|
||||
|
||||
@@ -180,6 +180,9 @@ func NewCallbackCDecl(fn interface{}) uintptr
|
||||
// This function returns 1 byte BOOLEAN rather than the 4 byte BOOL.
|
||||
//sys CreateSymbolicLink(symlinkfilename *uint16, targetfilename *uint16, flags uint32) (err error) [failretval&0xff==0] = CreateSymbolicLinkW
|
||||
//sys CreateHardLink(filename *uint16, existingfilename *uint16, reserved uintptr) (err error) [failretval&0xff==0] = CreateHardLinkW
|
||||
//sys GetCurrentThreadId() (id uint32)
|
||||
//sys CreateEvent(eventAttrs *syscall.SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle Handle, err error) = kernel32.CreateEventW
|
||||
//sys SetEvent(event Handle) (err error) = kernel32.SetEvent
|
||||
|
||||
// syscall interface implementation for other packages
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import "syscall"
|
||||
var _ unsafe.Pointer
|
||||
|
||||
var (
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
modadvapi32 = syscall.NewLazyDLL("advapi32.dll")
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
modshell32 = syscall.NewLazyDLL("shell32.dll")
|
||||
modmswsock = syscall.NewLazyDLL("mswsock.dll")
|
||||
modcrypt32 = syscall.NewLazyDLL("crypt32.dll")
|
||||
@@ -20,6 +20,23 @@ var (
|
||||
modnetapi32 = syscall.NewLazyDLL("netapi32.dll")
|
||||
moduserenv = syscall.NewLazyDLL("userenv.dll")
|
||||
|
||||
procRegisterEventSourceW = modadvapi32.NewProc("RegisterEventSourceW")
|
||||
procDeregisterEventSource = modadvapi32.NewProc("DeregisterEventSource")
|
||||
procReportEventW = modadvapi32.NewProc("ReportEventW")
|
||||
procOpenSCManagerW = modadvapi32.NewProc("OpenSCManagerW")
|
||||
procCloseServiceHandle = modadvapi32.NewProc("CloseServiceHandle")
|
||||
procCreateServiceW = modadvapi32.NewProc("CreateServiceW")
|
||||
procOpenServiceW = modadvapi32.NewProc("OpenServiceW")
|
||||
procDeleteService = modadvapi32.NewProc("DeleteService")
|
||||
procStartServiceW = modadvapi32.NewProc("StartServiceW")
|
||||
procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus")
|
||||
procControlService = modadvapi32.NewProc("ControlService")
|
||||
procStartServiceCtrlDispatcherW = modadvapi32.NewProc("StartServiceCtrlDispatcherW")
|
||||
procSetServiceStatus = modadvapi32.NewProc("SetServiceStatus")
|
||||
procChangeServiceConfigW = modadvapi32.NewProc("ChangeServiceConfigW")
|
||||
procQueryServiceConfigW = modadvapi32.NewProc("QueryServiceConfigW")
|
||||
procChangeServiceConfig2W = modadvapi32.NewProc("ChangeServiceConfig2W")
|
||||
procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W")
|
||||
procGetLastError = modkernel32.NewProc("GetLastError")
|
||||
procLoadLibraryW = modkernel32.NewProc("LoadLibraryW")
|
||||
procFreeLibrary = modkernel32.NewProc("FreeLibrary")
|
||||
@@ -117,6 +134,9 @@ var (
|
||||
procDeviceIoControl = modkernel32.NewProc("DeviceIoControl")
|
||||
procCreateSymbolicLinkW = modkernel32.NewProc("CreateSymbolicLinkW")
|
||||
procCreateHardLinkW = modkernel32.NewProc("CreateHardLinkW")
|
||||
procGetCurrentThreadId = modkernel32.NewProc("GetCurrentThreadId")
|
||||
procCreateEventW = modkernel32.NewProc("CreateEventW")
|
||||
procSetEvent = modkernel32.NewProc("SetEvent")
|
||||
procWSAStartup = modws2_32.NewProc("WSAStartup")
|
||||
procWSACleanup = modws2_32.NewProc("WSACleanup")
|
||||
procWSAIoctl = modws2_32.NewProc("WSAIoctl")
|
||||
@@ -160,11 +180,222 @@ var (
|
||||
procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
|
||||
procGetLengthSid = modadvapi32.NewProc("GetLengthSid")
|
||||
procCopySid = modadvapi32.NewProc("CopySid")
|
||||
procAllocateAndInitializeSid = modadvapi32.NewProc("AllocateAndInitializeSid")
|
||||
procFreeSid = modadvapi32.NewProc("FreeSid")
|
||||
procEqualSid = modadvapi32.NewProc("EqualSid")
|
||||
procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken")
|
||||
procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation")
|
||||
procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
|
||||
)
|
||||
|
||||
func RegisterEventSource(uncServerName *uint16, sourceName *uint16) (handle Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procRegisterEventSourceW.Addr(), 2, uintptr(unsafe.Pointer(uncServerName)), uintptr(unsafe.Pointer(sourceName)), 0)
|
||||
handle = Handle(r0)
|
||||
if handle == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DeregisterEventSource(handle Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDeregisterEventSource.Addr(), 1, uintptr(handle), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ReportEvent(log Handle, etype uint16, category uint16, eventId uint32, usrSId uintptr, numStrings uint16, dataSize uint32, strings **uint16, rawData *byte) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procReportEventW.Addr(), 9, uintptr(log), uintptr(etype), uintptr(category), uintptr(eventId), uintptr(usrSId), uintptr(numStrings), uintptr(dataSize), uintptr(unsafe.Pointer(strings)), uintptr(unsafe.Pointer(rawData)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procOpenSCManagerW.Addr(), 3, uintptr(unsafe.Pointer(machineName)), uintptr(unsafe.Pointer(databaseName)), uintptr(access))
|
||||
handle = Handle(r0)
|
||||
if handle == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CloseServiceHandle(handle Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procCloseServiceHandle.Addr(), 1, uintptr(handle), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func 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) {
|
||||
r0, _, e1 := syscall.Syscall15(procCreateServiceW.Addr(), 13, uintptr(mgr), uintptr(unsafe.Pointer(serviceName)), uintptr(unsafe.Pointer(displayName)), uintptr(access), uintptr(srvType), uintptr(startType), uintptr(errCtl), uintptr(unsafe.Pointer(pathName)), uintptr(unsafe.Pointer(loadOrderGroup)), uintptr(unsafe.Pointer(tagId)), uintptr(unsafe.Pointer(dependencies)), uintptr(unsafe.Pointer(serviceStartName)), uintptr(unsafe.Pointer(password)), 0, 0)
|
||||
handle = Handle(r0)
|
||||
if handle == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func OpenService(mgr Handle, serviceName *uint16, access uint32) (handle Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procOpenServiceW.Addr(), 3, uintptr(mgr), uintptr(unsafe.Pointer(serviceName)), uintptr(access))
|
||||
handle = Handle(r0)
|
||||
if handle == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteService(service Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDeleteService.Addr(), 1, uintptr(service), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func StartService(service Handle, numArgs uint32, argVectors **uint16) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procStartServiceW.Addr(), 3, uintptr(service), uintptr(numArgs), uintptr(unsafe.Pointer(argVectors)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func QueryServiceStatus(service Handle, status *SERVICE_STATUS) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procQueryServiceStatus.Addr(), 2, uintptr(service), uintptr(unsafe.Pointer(status)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ControlService(service Handle, control uint32, status *SERVICE_STATUS) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procControlService.Addr(), 3, uintptr(service), uintptr(control), uintptr(unsafe.Pointer(status)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func StartServiceCtrlDispatcher(serviceTable *SERVICE_TABLE_ENTRY) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procStartServiceCtrlDispatcherW.Addr(), 1, uintptr(unsafe.Pointer(serviceTable)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetServiceStatus(service Handle, serviceStatus *SERVICE_STATUS) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetServiceStatus.Addr(), 2, uintptr(service), uintptr(unsafe.Pointer(serviceStatus)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func 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) {
|
||||
r1, _, e1 := syscall.Syscall12(procChangeServiceConfigW.Addr(), 11, uintptr(service), uintptr(serviceType), uintptr(startType), uintptr(errorControl), uintptr(unsafe.Pointer(binaryPathName)), uintptr(unsafe.Pointer(loadOrderGroup)), uintptr(unsafe.Pointer(tagId)), uintptr(unsafe.Pointer(dependencies)), uintptr(unsafe.Pointer(serviceStartName)), uintptr(unsafe.Pointer(password)), uintptr(unsafe.Pointer(displayName)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func QueryServiceConfig(service Handle, serviceConfig *QUERY_SERVICE_CONFIG, bufSize uint32, bytesNeeded *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procQueryServiceConfigW.Addr(), 4, uintptr(service), uintptr(unsafe.Pointer(serviceConfig)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ChangeServiceConfig2(service Handle, infoLevel uint32, info *byte) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procChangeServiceConfig2W.Addr(), 3, uintptr(service), uintptr(infoLevel), uintptr(unsafe.Pointer(info)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func QueryServiceConfig2(service Handle, infoLevel uint32, buff *byte, buffSize uint32, bytesNeeded *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procQueryServiceConfig2W.Addr(), 5, uintptr(service), uintptr(infoLevel), uintptr(unsafe.Pointer(buff)), uintptr(buffSize), uintptr(unsafe.Pointer(bytesNeeded)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetLastError() (lasterr error) {
|
||||
r0, _, _ := syscall.Syscall(procGetLastError.Addr(), 0, 0, 0, 0)
|
||||
if r0 != 0 {
|
||||
@@ -1357,6 +1588,37 @@ func CreateHardLink(filename *uint16, existingfilename *uint16, reserved uintptr
|
||||
return
|
||||
}
|
||||
|
||||
func GetCurrentThreadId() (id uint32) {
|
||||
r0, _, _ := syscall.Syscall(procGetCurrentThreadId.Addr(), 0, 0, 0, 0)
|
||||
id = uint32(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func CreateEvent(eventAttrs *syscall.SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(eventAttrs)), uintptr(manualReset), uintptr(initialState), uintptr(unsafe.Pointer(name)), 0, 0)
|
||||
handle = Handle(r0)
|
||||
if handle == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetEvent(event Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(event), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func WSAStartup(verreq uint32, data *WSAData) (sockerr error) {
|
||||
r0, _, _ := syscall.Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0)
|
||||
if r0 != 0 {
|
||||
@@ -1848,6 +2110,36 @@ func CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func 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) {
|
||||
r1, _, e1 := syscall.Syscall12(procAllocateAndInitializeSid.Addr(), 11, uintptr(unsafe.Pointer(identAuth)), uintptr(subAuth), uintptr(subAuth0), uintptr(subAuth1), uintptr(subAuth2), uintptr(subAuth3), uintptr(subAuth4), uintptr(subAuth5), uintptr(subAuth6), uintptr(subAuth7), uintptr(unsafe.Pointer(sid)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func FreeSid(sid *SID) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procFreeSid.Addr(), 1, uintptr(unsafe.Pointer(sid)), 0, 0)
|
||||
if r1 != 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func EqualSid(sid1 *SID, sid2 *SID) (isEqual bool) {
|
||||
r0, _, _ := syscall.Syscall(procEqualSid.Addr(), 2, uintptr(unsafe.Pointer(sid1)), uintptr(unsafe.Pointer(sid2)), 0)
|
||||
isEqual = r0 != 0
|
||||
return
|
||||
}
|
||||
|
||||
func OpenProcessToken(h Handle, access uint32, token *Token) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procOpenProcessToken.Addr(), 3, uintptr(h), uintptr(access), uintptr(unsafe.Pointer(token)))
|
||||
if r1 == 0 {
|
||||
|
||||
@@ -8,27 +8,28 @@ import "syscall"
|
||||
|
||||
const (
|
||||
// Windows errors.
|
||||
ERROR_FILE_NOT_FOUND syscall.Errno = 2
|
||||
ERROR_PATH_NOT_FOUND syscall.Errno = 3
|
||||
ERROR_ACCESS_DENIED syscall.Errno = 5
|
||||
ERROR_NO_MORE_FILES syscall.Errno = 18
|
||||
ERROR_HANDLE_EOF syscall.Errno = 38
|
||||
ERROR_NETNAME_DELETED syscall.Errno = 64
|
||||
ERROR_FILE_EXISTS syscall.Errno = 80
|
||||
ERROR_BROKEN_PIPE syscall.Errno = 109
|
||||
ERROR_BUFFER_OVERFLOW syscall.Errno = 111
|
||||
ERROR_INSUFFICIENT_BUFFER syscall.Errno = 122
|
||||
ERROR_MOD_NOT_FOUND syscall.Errno = 126
|
||||
ERROR_PROC_NOT_FOUND syscall.Errno = 127
|
||||
ERROR_ALREADY_EXISTS syscall.Errno = 183
|
||||
ERROR_ENVVAR_NOT_FOUND syscall.Errno = 203
|
||||
ERROR_MORE_DATA syscall.Errno = 234
|
||||
ERROR_OPERATION_ABORTED syscall.Errno = 995
|
||||
ERROR_IO_PENDING syscall.Errno = 997
|
||||
ERROR_NOT_FOUND syscall.Errno = 1168
|
||||
ERROR_PRIVILEGE_NOT_HELD syscall.Errno = 1314
|
||||
WSAEACCES syscall.Errno = 10013
|
||||
WSAECONNRESET syscall.Errno = 10054
|
||||
ERROR_FILE_NOT_FOUND syscall.Errno = 2
|
||||
ERROR_PATH_NOT_FOUND syscall.Errno = 3
|
||||
ERROR_ACCESS_DENIED syscall.Errno = 5
|
||||
ERROR_NO_MORE_FILES syscall.Errno = 18
|
||||
ERROR_HANDLE_EOF syscall.Errno = 38
|
||||
ERROR_NETNAME_DELETED syscall.Errno = 64
|
||||
ERROR_FILE_EXISTS syscall.Errno = 80
|
||||
ERROR_BROKEN_PIPE syscall.Errno = 109
|
||||
ERROR_BUFFER_OVERFLOW syscall.Errno = 111
|
||||
ERROR_INSUFFICIENT_BUFFER syscall.Errno = 122
|
||||
ERROR_MOD_NOT_FOUND syscall.Errno = 126
|
||||
ERROR_PROC_NOT_FOUND syscall.Errno = 127
|
||||
ERROR_ALREADY_EXISTS syscall.Errno = 183
|
||||
ERROR_ENVVAR_NOT_FOUND syscall.Errno = 203
|
||||
ERROR_MORE_DATA syscall.Errno = 234
|
||||
ERROR_OPERATION_ABORTED syscall.Errno = 995
|
||||
ERROR_IO_PENDING syscall.Errno = 997
|
||||
ERROR_SERVICE_SPECIFIC_ERROR syscall.Errno = 1066
|
||||
ERROR_NOT_FOUND syscall.Errno = 1168
|
||||
ERROR_PRIVILEGE_NOT_HELD syscall.Errno = 1314
|
||||
WSAEACCES syscall.Errno = 10013
|
||||
WSAECONNRESET syscall.Errno = 10054
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
Reference in New Issue
Block a user