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