github.com/Andyfoo/golang/x/sys@v0.0.0-20190901054642-57c1bf301704/windows/svc/mgr/config.go (about)

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build windows
     6  
     7  package mgr
     8  
     9  import (
    10  	"syscall"
    11  	"unicode/utf16"
    12  	"unsafe"
    13  
    14  	"github.com/Andyfoo/golang/x/sys/windows"
    15  )
    16  
    17  const (
    18  	// Service start types.
    19  	StartManual    = windows.SERVICE_DEMAND_START // the service must be started manually
    20  	StartAutomatic = windows.SERVICE_AUTO_START   // the service will start by itself whenever the computer reboots
    21  	StartDisabled  = windows.SERVICE_DISABLED     // the service cannot be started
    22  
    23  	// The severity of the error, and action taken,
    24  	// if this service fails to start.
    25  	ErrorCritical = windows.SERVICE_ERROR_CRITICAL
    26  	ErrorIgnore   = windows.SERVICE_ERROR_IGNORE
    27  	ErrorNormal   = windows.SERVICE_ERROR_NORMAL
    28  	ErrorSevere   = windows.SERVICE_ERROR_SEVERE
    29  )
    30  
    31  // TODO(brainman): Password is not returned by windows.QueryServiceConfig, not sure how to get it.
    32  
    33  type Config struct {
    34  	ServiceType      uint32
    35  	StartType        uint32
    36  	ErrorControl     uint32
    37  	BinaryPathName   string // fully qualified path to the service binary file, can also include arguments for an auto-start service
    38  	LoadOrderGroup   string
    39  	TagId            uint32
    40  	Dependencies     []string
    41  	ServiceStartName string // name of the account under which the service should run
    42  	DisplayName      string
    43  	Password         string
    44  	Description      string
    45  	SidType          uint32 // one of SERVICE_SID_TYPE, the type of sid to use for the service
    46  	DelayedAutoStart bool   // the service is started after other auto-start services are started plus a short delay
    47  }
    48  
    49  func toString(p *uint16) string {
    50  	if p == nil {
    51  		return ""
    52  	}
    53  	return syscall.UTF16ToString((*[4096]uint16)(unsafe.Pointer(p))[:])
    54  }
    55  
    56  func toStringSlice(ps *uint16) []string {
    57  	if ps == nil {
    58  		return nil
    59  	}
    60  	r := make([]string, 0)
    61  	for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(ps)); true; i++ {
    62  		if p[i] == 0 {
    63  			// empty string marks the end
    64  			if i <= from {
    65  				break
    66  			}
    67  			r = append(r, string(utf16.Decode(p[from:i])))
    68  			from = i + 1
    69  		}
    70  	}
    71  	return r
    72  }
    73  
    74  // Config retrieves service s configuration paramteres.
    75  func (s *Service) Config() (Config, error) {
    76  	var p *windows.QUERY_SERVICE_CONFIG
    77  	n := uint32(1024)
    78  	for {
    79  		b := make([]byte, n)
    80  		p = (*windows.QUERY_SERVICE_CONFIG)(unsafe.Pointer(&b[0]))
    81  		err := windows.QueryServiceConfig(s.Handle, p, n, &n)
    82  		if err == nil {
    83  			break
    84  		}
    85  		if err.(syscall.Errno) != syscall.ERROR_INSUFFICIENT_BUFFER {
    86  			return Config{}, err
    87  		}
    88  		if n <= uint32(len(b)) {
    89  			return Config{}, err
    90  		}
    91  	}
    92  
    93  	b, err := s.queryServiceConfig2(windows.SERVICE_CONFIG_DESCRIPTION)
    94  	if err != nil {
    95  		return Config{}, err
    96  	}
    97  	p2 := (*windows.SERVICE_DESCRIPTION)(unsafe.Pointer(&b[0]))
    98  
    99  	b, err = s.queryServiceConfig2(windows.SERVICE_CONFIG_DELAYED_AUTO_START_INFO)
   100  	if err != nil {
   101  		return Config{}, err
   102  	}
   103  	p3 := (*windows.SERVICE_DELAYED_AUTO_START_INFO)(unsafe.Pointer(&b[0]))
   104  	delayedStart := false
   105  	if p3.IsDelayedAutoStartUp != 0 {
   106  		delayedStart = true
   107  	}
   108  
   109  	return Config{
   110  		ServiceType:      p.ServiceType,
   111  		StartType:        p.StartType,
   112  		ErrorControl:     p.ErrorControl,
   113  		BinaryPathName:   toString(p.BinaryPathName),
   114  		LoadOrderGroup:   toString(p.LoadOrderGroup),
   115  		TagId:            p.TagId,
   116  		Dependencies:     toStringSlice(p.Dependencies),
   117  		ServiceStartName: toString(p.ServiceStartName),
   118  		DisplayName:      toString(p.DisplayName),
   119  		Description:      toString(p2.Description),
   120  		DelayedAutoStart: delayedStart,
   121  	}, nil
   122  }
   123  
   124  func updateDescription(handle windows.Handle, desc string) error {
   125  	d := windows.SERVICE_DESCRIPTION{Description: toPtr(desc)}
   126  	return windows.ChangeServiceConfig2(handle,
   127  		windows.SERVICE_CONFIG_DESCRIPTION, (*byte)(unsafe.Pointer(&d)))
   128  }
   129  
   130  func updateSidType(handle windows.Handle, sidType uint32) error {
   131  	return windows.ChangeServiceConfig2(handle, windows.SERVICE_CONFIG_SERVICE_SID_INFO, (*byte)(unsafe.Pointer(&sidType)))
   132  }
   133  
   134  func updateStartUp(handle windows.Handle, isDelayed bool) error {
   135  	var d windows.SERVICE_DELAYED_AUTO_START_INFO
   136  	if isDelayed {
   137  		d.IsDelayedAutoStartUp = 1
   138  	}
   139  	return windows.ChangeServiceConfig2(handle,
   140  		windows.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (*byte)(unsafe.Pointer(&d)))
   141  }
   142  
   143  // UpdateConfig updates service s configuration parameters.
   144  func (s *Service) UpdateConfig(c Config) error {
   145  	err := windows.ChangeServiceConfig(s.Handle, c.ServiceType, c.StartType,
   146  		c.ErrorControl, toPtr(c.BinaryPathName), toPtr(c.LoadOrderGroup),
   147  		nil, toStringBlock(c.Dependencies), toPtr(c.ServiceStartName),
   148  		toPtr(c.Password), toPtr(c.DisplayName))
   149  	if err != nil {
   150  		return err
   151  	}
   152  	err = updateSidType(s.Handle, c.SidType)
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	err = updateStartUp(s.Handle, c.DelayedAutoStart)
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	return updateDescription(s.Handle, c.Description)
   163  }
   164  
   165  // queryServiceConfig2 calls Windows QueryServiceConfig2 with infoLevel parameter and returns retrieved service configuration information.
   166  func (s *Service) queryServiceConfig2(infoLevel uint32) ([]byte, error) {
   167  	n := uint32(1024)
   168  	for {
   169  		b := make([]byte, n)
   170  		err := windows.QueryServiceConfig2(s.Handle, infoLevel, &b[0], n, &n)
   171  		if err == nil {
   172  			return b, nil
   173  		}
   174  		if err.(syscall.Errno) != syscall.ERROR_INSUFFICIENT_BUFFER {
   175  			return nil, err
   176  		}
   177  		if n <= uint32(len(b)) {
   178  			return nil, err
   179  		}
   180  	}
   181  }