github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/service/windows/service_windows.go (about)

     1  // Copyright 2015 Cloudbase Solutions
     2  // Copyright 2015 Canonical Ltd.
     3  // Licensed under the AGPLv3, see LICENCE file for details.
     4  
     5  package windows
     6  
     7  import (
     8  	"io/ioutil"
     9  	"reflect"
    10  	"strings"
    11  	"syscall"
    12  	"unsafe"
    13  
    14  	"github.com/gabriel-samfira/sys/windows"
    15  	"github.com/gabriel-samfira/sys/windows/svc"
    16  	"github.com/gabriel-samfira/sys/windows/svc/mgr"
    17  	"github.com/juju/errors"
    18  
    19  	"github.com/juju/juju/service/common"
    20  	"github.com/juju/juju/service/windows/securestring"
    21  )
    22  
    23  //sys enumServicesStatus(h windows.Handle, dwServiceType uint32, dwServiceState uint32, lpServices uintptr, cbBufSize uint32, pcbBytesNeeded *uint32, lpServicesReturned *uint32, lpResumeHandle *uint32) (err error) [failretval==0] = advapi32.EnumServicesStatusW
    24  
    25  type enumService struct {
    26  	name        *uint16
    27  	displayName *uint16
    28  	Status      windows.SERVICE_STATUS
    29  }
    30  
    31  // Name returns the name of the service stored in enumService.
    32  func (s *enumService) Name() string {
    33  	if s.name != nil {
    34  		return syscall.UTF16ToString((*[1 << 16]uint16)(unsafe.Pointer(s.name))[:])
    35  	}
    36  	return ""
    37  }
    38  
    39  // mgrInterface exposes Mgr methods needed by the windows service package.
    40  type mgrInterface interface {
    41  	CreateService(name, exepath string, c mgr.Config, args ...string) (svcInterface, error)
    42  	OpenService(name string) (svcInterface, error)
    43  }
    44  
    45  // svcInterface exposes mgr.Service methods needed by the windows service package.
    46  type svcInterface interface {
    47  	Config() (mgr.Config, error)
    48  	Control(c svc.Cmd) (svc.Status, error)
    49  	Delete() error
    50  	Query() (svc.Status, error)
    51  	Start(...string) error
    52  }
    53  
    54  // manager is meant to help stub out winsvc for testing
    55  type manager struct {
    56  	m *mgr.Mgr
    57  }
    58  
    59  // CreateService wraps Mgr.CreateService method.
    60  func (m *manager) CreateService(name, exepath string, c mgr.Config, args ...string) (svcInterface, error) {
    61  	return m.m.CreateService(name, exepath, c, args...)
    62  }
    63  
    64  // CreateService wraps Mgr.OpenService method. It returns a svcInterface object.
    65  // This allows us to stub out this module for testing.
    66  func (m *manager) OpenService(name string) (svcInterface, error) {
    67  	return m.m.OpenService(name)
    68  }
    69  
    70  func newManagerConn() (mgrInterface, error) {
    71  	s, err := mgr.Connect()
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	return &manager{m: s}, nil
    76  }
    77  
    78  var newConn = newManagerConn
    79  
    80  // enumServices casts the bytes returned by enumServicesStatus into an array of
    81  // enumService with all the services on the current system
    82  func enumServices(h windows.Handle) ([]enumService, error) {
    83  	var needed uint32
    84  	var returned uint32
    85  	var resume uint32
    86  	var all []enumService
    87  
    88  	for {
    89  		var buf [256]enumService
    90  		err := enumServicesStatus(h, windows.SERVICE_WIN32,
    91  			windows.SERVICE_STATE_ALL, uintptr(unsafe.Pointer(&buf[0])), uint32(unsafe.Sizeof(buf)), &needed, &returned, &resume)
    92  		if err != nil {
    93  			if err == windows.ERROR_MORE_DATA {
    94  				all = append(all, buf[:returned]...)
    95  				continue
    96  			}
    97  			return []enumService{}, err
    98  		}
    99  		all = append(all, buf[:returned]...)
   100  		return all, nil
   101  	}
   102  }
   103  
   104  // getPassword attempts to read the password for the jujud user. We define it as
   105  // a variable to allow us to mock it out for testing
   106  var getPassword = func() (string, error) {
   107  	f, err := ioutil.ReadFile(jujuPasswdFile)
   108  	if err != nil {
   109  		return "", errors.Annotate(err, "Failed to read password file")
   110  	}
   111  	encryptedPasswd := strings.TrimSpace(string(f))
   112  	passwd, err := securestring.Decrypt(encryptedPasswd)
   113  	if err != nil {
   114  		return "", errors.Annotate(err, "Failed to decrypt password")
   115  	}
   116  	return passwd, nil
   117  }
   118  
   119  // listServices returns an array of strings containing all the services on
   120  // the current system. It is defined as a variable to allow us to mock it out
   121  // for testing
   122  var listServices = func() ([]string, error) {
   123  	services := []string{}
   124  	host := syscall.StringToUTF16Ptr(".")
   125  
   126  	sc, err := windows.OpenSCManager(host, nil, windows.SC_MANAGER_ALL_ACCESS)
   127  	if err != nil {
   128  		return services, err
   129  	}
   130  	enum, err := enumServices(sc)
   131  	if err != nil {
   132  		return services, err
   133  	}
   134  	for _, v := range enum {
   135  		services = append(services, v.Name())
   136  	}
   137  	return services, nil
   138  }
   139  
   140  // SvcManager implements ServiceManager interface
   141  type SvcManager struct {
   142  	svc         svcInterface
   143  	mgr         mgrInterface
   144  	serviceConf common.Conf
   145  }
   146  
   147  func (s *SvcManager) query(name string) (svc.State, error) {
   148  	var err error
   149  	s.svc, err = s.mgr.OpenService(name)
   150  	if err != nil {
   151  		return svc.Stopped, errors.Trace(err)
   152  	}
   153  	status, err := s.svc.Query()
   154  	if err != nil {
   155  		return svc.Stopped, errors.Trace(err)
   156  	}
   157  	return status.State, nil
   158  }
   159  
   160  func (s *SvcManager) status(name string) (svc.State, error) {
   161  	status, err := s.query(name)
   162  	if err != nil {
   163  		return svc.Stopped, errors.Trace(err)
   164  	}
   165  	return status, nil
   166  }
   167  
   168  func (s *SvcManager) exists(name string) (bool, error) {
   169  	_, err := s.query(name)
   170  	if err == c_ERROR_SERVICE_DOES_NOT_EXIST {
   171  		return false, nil
   172  	} else if err != nil {
   173  		return false, err
   174  	}
   175  	return true, nil
   176  }
   177  
   178  // Start starts a service.
   179  func (s *SvcManager) Start(name string) error {
   180  	running, err := s.Running(name)
   181  	if err != nil {
   182  		return errors.Trace(err)
   183  	}
   184  	if running {
   185  		return nil
   186  	}
   187  	err = s.svc.Start()
   188  	if err != nil {
   189  		return err
   190  	}
   191  	return nil
   192  }
   193  
   194  func (s *SvcManager) escapeExecPath(exePath string, args []string) string {
   195  	ret := syscall.EscapeArg(exePath)
   196  	for _, v := range args {
   197  		ret += " " + syscall.EscapeArg(v)
   198  	}
   199  	return ret
   200  }
   201  
   202  // Exists checks whether the config of the installed service matches the
   203  // config supplied to this function
   204  func (s *SvcManager) Exists(name string, conf common.Conf) (bool, error) {
   205  	passwd, err := getPassword()
   206  	if err != nil {
   207  		return false, err
   208  	}
   209  
   210  	// We escape and compose BinaryPathName the same way mgr.CreateService does.
   211  	execStart := s.escapeExecPath(conf.ServiceBinary, conf.ServiceArgs)
   212  	cfg := mgr.Config{
   213  		// make this service dependent on WMI service. WMI is needed for almost
   214  		// all installers to work properly, and is needed for all of the advanced windows
   215  		// instrumentation bits (powershell included). Juju agents must start after this
   216  		// service to ensure hooks run properly.
   217  		Dependencies:     []string{"Winmgmt"},
   218  		StartType:        mgr.StartAutomatic,
   219  		DisplayName:      conf.Desc,
   220  		ServiceStartName: jujudUser,
   221  		Password:         passwd,
   222  		BinaryPathName:   execStart,
   223  	}
   224  	currentConfig, err := s.Config(name)
   225  	if err != nil {
   226  		return false, err
   227  	}
   228  
   229  	if reflect.DeepEqual(cfg, currentConfig) {
   230  		return true, nil
   231  	}
   232  	return false, nil
   233  }
   234  
   235  // Stop stops a service.
   236  func (s *SvcManager) Stop(name string) error {
   237  	running, err := s.Running(name)
   238  	if err != nil {
   239  		return errors.Trace(err)
   240  	}
   241  	if !running {
   242  		return nil
   243  	}
   244  	_, err = s.svc.Control(svc.Stop)
   245  	if err != nil {
   246  		return errors.Trace(err)
   247  	}
   248  	return nil
   249  }
   250  
   251  // Delete deletes a service.
   252  func (s *SvcManager) Delete(name string) error {
   253  	exists, err := s.exists(name)
   254  	if err != nil {
   255  		return err
   256  	}
   257  	if !exists {
   258  		return nil
   259  	}
   260  	err = s.svc.Delete()
   261  	if err == c_ERROR_SERVICE_DOES_NOT_EXIST {
   262  		return nil
   263  	} else if err != nil {
   264  		return errors.Trace(err)
   265  	}
   266  	return nil
   267  }
   268  
   269  // Create creates a service with the given config.
   270  func (s *SvcManager) Create(name string, conf common.Conf) error {
   271  	passwd, err := getPassword()
   272  	if err != nil {
   273  		return errors.Trace(err)
   274  	}
   275  	cfg := mgr.Config{
   276  		Dependencies:     []string{"Winmgmt"},
   277  		StartType:        mgr.StartAutomatic,
   278  		DisplayName:      conf.Desc,
   279  		ServiceStartName: jujudUser,
   280  		Password:         passwd,
   281  	}
   282  	// mgr.CreateService actually does correct argument escaping itself. There is no
   283  	// need for quoted strings of any kind passed to this function. It takes in
   284  	// a binary name, and an array or arguments.
   285  	_, err = s.mgr.CreateService(name, conf.ServiceBinary, cfg, conf.ServiceArgs...)
   286  	if err != nil {
   287  		return errors.Trace(err)
   288  	}
   289  	return nil
   290  }
   291  
   292  // Running returns the status of a service.
   293  func (s *SvcManager) Running(name string) (bool, error) {
   294  	status, err := s.status(name)
   295  	if err != nil {
   296  		return false, errors.Trace(err)
   297  	}
   298  	logger.Infof("Service %q Status %v", name, status)
   299  	if status == svc.Running {
   300  		return true, nil
   301  	}
   302  	return false, nil
   303  }
   304  
   305  // Config returns the mgr.Config of the service. This config reflects the actual
   306  // service configuration in Windows.
   307  func (s *SvcManager) Config(name string) (mgr.Config, error) {
   308  	exists, err := s.exists(name)
   309  	if err != nil {
   310  		return mgr.Config{}, err
   311  	}
   312  	if !exists {
   313  		return mgr.Config{}, c_ERROR_SERVICE_DOES_NOT_EXIST
   314  	}
   315  	return s.svc.Config()
   316  }
   317  
   318  var newServiceManager = func() (ServiceManager, error) {
   319  	m, err := newConn()
   320  	if err != nil {
   321  		return nil, errors.Trace(err)
   322  	}
   323  	return &SvcManager{
   324  		mgr: m,
   325  	}, nil
   326  }