aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/golang.org/x/sys/windows/svc/mgr
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/sys/windows/svc/mgr')
-rw-r--r--vendor/golang.org/x/sys/windows/svc/mgr/config.go139
-rw-r--r--vendor/golang.org/x/sys/windows/svc/mgr/mgr.go162
-rw-r--r--vendor/golang.org/x/sys/windows/svc/mgr/mgr_test.go169
-rw-r--r--vendor/golang.org/x/sys/windows/svc/mgr/service.go72
4 files changed, 542 insertions, 0 deletions
diff --git a/vendor/golang.org/x/sys/windows/svc/mgr/config.go b/vendor/golang.org/x/sys/windows/svc/mgr/config.go
new file mode 100644
index 0000000..0a6edba
--- /dev/null
+++ b/vendor/golang.org/x/sys/windows/svc/mgr/config.go
@@ -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)
+}
diff --git a/vendor/golang.org/x/sys/windows/svc/mgr/mgr.go b/vendor/golang.org/x/sys/windows/svc/mgr/mgr.go
new file mode 100644
index 0000000..76965b5
--- /dev/null
+++ b/vendor/golang.org/x/sys/windows/svc/mgr/mgr.go
@@ -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
+}
diff --git a/vendor/golang.org/x/sys/windows/svc/mgr/mgr_test.go b/vendor/golang.org/x/sys/windows/svc/mgr/mgr_test.go
new file mode 100644
index 0000000..1569a22
--- /dev/null
+++ b/vendor/golang.org/x/sys/windows/svc/mgr/mgr_test.go
@@ -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)
+}
diff --git a/vendor/golang.org/x/sys/windows/svc/mgr/service.go b/vendor/golang.org/x/sys/windows/svc/mgr/service.go
new file mode 100644
index 0000000..fdc46af
--- /dev/null
+++ b/vendor/golang.org/x/sys/windows/svc/mgr/service.go
@@ -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
+}