github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/service/windows/service.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  	"fmt"
     9  	"runtime"
    10  	"strings"
    11  	"syscall"
    12  
    13  	"github.com/juju/errors"
    14  	"github.com/juju/loggo"
    15  	"github.com/juju/utils/shell"
    16  
    17  	"github.com/juju/juju/service/common"
    18  )
    19  
    20  var (
    21  	logger   = loggo.GetLogger("juju.worker.deployer.service")
    22  	renderer = &shell.PowershellRenderer{}
    23  )
    24  
    25  const (
    26  	// c_ERROR_SERVICE_DOES_NOT_EXIST is returned by the OS when trying to open
    27  	// an inexistent service
    28  	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684330%28v=vs.85%29.aspx
    29  	c_ERROR_SERVICE_DOES_NOT_EXIST syscall.Errno = 0x424
    30  
    31  	// c_ERROR_SERVICE_EXISTS is returned by the operating system if the service
    32  	// we are trying to create, already exists
    33  	c_ERROR_SERVICE_EXISTS syscall.Errno = 0x431
    34  
    35  	// This is the user under which juju services start. We chose to use a
    36  	// normal user for this purpose because some installers require a normal
    37  	// user with a proper user profile to actually run. This user is created
    38  	// via userdata, and should exist on all juju bootstrapped systems.
    39  	// Required privileges for this user are:
    40  	// SeAssignPrimaryTokenPrivilege
    41  	// SeServiceLogonRight
    42  	jujudUser = ".\\jujud"
    43  )
    44  
    45  // IsRunning returns whether or not windows is the local init system.
    46  func IsRunning() (bool, error) {
    47  	return runtime.GOOS == "windows", nil
    48  }
    49  
    50  // ListServices returns the name of all installed services on the
    51  // local host.
    52  func ListServices() ([]string, error) {
    53  	return listServices()
    54  }
    55  
    56  // ListCommand returns a command that will list the services on a host.
    57  func ListCommand() string {
    58  	return `(Get-Service).Name`
    59  }
    60  
    61  // ServiceManager exposes methods needed to manage a windows service
    62  type ServiceManager interface {
    63  	// Start starts a service.
    64  	Start(name string) error
    65  	// Stop stops a service.
    66  	Stop(name string) error
    67  	// Delete deletes a service.
    68  	Delete(name string) error
    69  	// Create creates a service with the given config.
    70  	Create(name string, conf common.Conf) error
    71  	// Running returns the status of a service.
    72  	Running(name string) (bool, error)
    73  	// Exists checks whether the config of the installed service matches the
    74  	// config supplied to this function
    75  	Exists(name string, conf common.Conf) (bool, error)
    76  	// ChangeServicePassword can change the password of a service
    77  	// as long as it belongs to the user defined in this package
    78  	ChangeServicePassword(name, newPassword string) error
    79  }
    80  
    81  // Service represents a service running on the current system
    82  type Service struct {
    83  	common.Service
    84  	manager ServiceManager
    85  }
    86  
    87  func newService(name string, conf common.Conf, manager ServiceManager) *Service {
    88  	return &Service{
    89  		Service: common.Service{
    90  			Name: name,
    91  			Conf: conf,
    92  		},
    93  		manager: manager,
    94  	}
    95  }
    96  
    97  // NewService returns a new Service type
    98  func NewService(name string, conf common.Conf) (*Service, error) {
    99  	m, err := NewServiceManager()
   100  	if err != nil {
   101  		return nil, errors.Trace(err)
   102  	}
   103  	return newService(name, conf, m), nil
   104  }
   105  
   106  // Name implements service.Service.
   107  func (s *Service) Name() string {
   108  	return s.Service.Name
   109  }
   110  
   111  // Conf implements service.Service.
   112  func (s *Service) Conf() common.Conf {
   113  	return s.Service.Conf
   114  }
   115  
   116  // Validate checks the service for invalid values.
   117  func (s *Service) Validate() error {
   118  	if err := s.Service.Validate(renderer); err != nil {
   119  		return errors.Trace(err)
   120  	}
   121  
   122  	if s.Service.Conf.Transient {
   123  		return errors.NotSupportedf("transient services")
   124  	}
   125  
   126  	if s.Service.Conf.AfterStopped != "" {
   127  		return errors.NotSupportedf("Conf.AfterStopped")
   128  	}
   129  
   130  	return nil
   131  }
   132  
   133  func (s *Service) Running() (bool, error) {
   134  	if ok, err := s.Installed(); err != nil {
   135  		return false, errors.Trace(err)
   136  	} else if !ok {
   137  		return false, nil
   138  	}
   139  	return s.manager.Running(s.Name())
   140  }
   141  
   142  // Installed returns whether the service is installed
   143  func (s *Service) Installed() (bool, error) {
   144  	services, err := ListServices()
   145  	if err != nil {
   146  		return false, errors.Trace(err)
   147  	}
   148  	for _, val := range services {
   149  		if s.Name() == val {
   150  			return true, nil
   151  		}
   152  	}
   153  	return false, nil
   154  }
   155  
   156  // Exists returns whether the service configuration reflects the
   157  // desired state
   158  func (s *Service) Exists() (bool, error) {
   159  	return s.manager.Exists(s.Name(), s.Conf())
   160  }
   161  
   162  // Start starts the service.
   163  func (s *Service) Start() error {
   164  	logger.Infof("Starting service %q", s.Service.Name)
   165  	running, err := s.Running()
   166  	if err != nil {
   167  		return errors.Trace(err)
   168  	}
   169  	if running {
   170  		logger.Infof("Service %q already running", s.Service.Name)
   171  		return nil
   172  	}
   173  	err = s.manager.Start(s.Name())
   174  	return err
   175  }
   176  
   177  // Stop stops the service.
   178  func (s *Service) Stop() error {
   179  	running, err := s.Running()
   180  	if err != nil {
   181  		return errors.Trace(err)
   182  	}
   183  	if !running {
   184  		return nil
   185  	}
   186  	err = s.manager.Stop(s.Name())
   187  	return err
   188  }
   189  
   190  // Remove deletes the service.
   191  func (s *Service) Remove() error {
   192  	installed, err := s.Installed()
   193  	if err != nil {
   194  		return err
   195  	}
   196  	if !installed {
   197  		return nil
   198  	}
   199  
   200  	err = s.Stop()
   201  	if err != nil {
   202  		return errors.Trace(err)
   203  	}
   204  	err = s.manager.Delete(s.Name())
   205  	return err
   206  }
   207  
   208  // Install installs and starts the service.
   209  func (s *Service) Install() error {
   210  	err := s.Validate()
   211  	if err != nil {
   212  		return errors.Trace(err)
   213  	}
   214  	installed, err := s.Installed()
   215  	if err != nil {
   216  		return errors.Trace(err)
   217  	}
   218  	if installed {
   219  		return errors.Errorf("Service %s already installed", s.Name())
   220  	}
   221  
   222  	logger.Infof("Installing Service %v", s.Name())
   223  	err = s.manager.Create(s.Name(), s.Conf())
   224  	if err != nil {
   225  		return errors.Trace(err)
   226  	}
   227  	return nil
   228  }
   229  
   230  // InstallCommands returns shell commands to install the service.
   231  func (s *Service) InstallCommands() ([]string, error) {
   232  	cmd := fmt.Sprintf(serviceCreateCommandTemplate[1:],
   233  		renderer.Quote(s.Service.Name),
   234  		renderer.Quote(s.Service.Conf.Desc),
   235  		renderer.Quote(s.Service.Conf.ExecStart),
   236  		renderer.Quote(s.Service.Name),
   237  		renderer.Quote(s.Service.Conf.Desc),
   238  		renderer.Quote(s.Service.Conf.ExecStart),
   239  		renderer.Quote(s.Service.Name),
   240  		renderer.Quote(s.Service.Name),
   241  	)
   242  	return strings.Split(cmd, "\n"), nil
   243  }
   244  
   245  // StartCommands returns shell commands to start the service.
   246  func (s *Service) StartCommands() ([]string, error) {
   247  	cmd := fmt.Sprintf(`Start-Service %s`, renderer.Quote(s.Service.Name))
   248  	return []string{cmd}, nil
   249  }
   250  
   251  const serviceCreateCommandTemplate = `
   252  if ($jujuCreds) {
   253    New-Service -Credential $jujuCreds -Name %s -DependsOn Winmgmt -DisplayName %s %s
   254  } else {
   255    New-Service -Name %s -DependsOn Winmgmt -DisplayName %s %s
   256  }
   257  sc.exe failure %s reset=5 actions=restart/1000
   258  sc.exe failureflag %s 1`