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`