github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 // c_ERROR_ACCESS_DENIED is returned by the operating system if access is denied 34 // to that service. 35 c_ERROR_ACCESS_DENIED syscall.Errno = 0x5 36 37 // This is the user under which juju services start. We chose to use a 38 // normal user for this purpose because some installers require a normal 39 // user with a proper user profile to actually run. This user is created 40 // via userdata, and should exist on all juju bootstrapped systems. 41 // Required privileges for this user are: 42 // SeAssignPrimaryTokenPrivilege 43 // SeServiceLogonRight 44 jujudUser = ".\\jujud" 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 // ChangeServicePassword can change the password of a service 79 // as long as it belongs to the user defined in this package 80 ChangeServicePassword(name, newPassword string) error 81 } 82 83 // Service represents a service running on the current system 84 type Service struct { 85 common.Service 86 manager ServiceManager 87 } 88 89 func newService(name string, conf common.Conf, manager ServiceManager) *Service { 90 return &Service{ 91 Service: common.Service{ 92 Name: name, 93 Conf: conf, 94 }, 95 manager: manager, 96 } 97 } 98 99 // NewService returns a new Service type 100 func NewService(name string, conf common.Conf) (*Service, error) { 101 m, err := NewServiceManager() 102 if err != nil { 103 return nil, errors.Trace(err) 104 } 105 return newService(name, conf, m), nil 106 } 107 108 // Name implements service.Service. 109 func (s *Service) Name() string { 110 return s.Service.Name 111 } 112 113 // Conf implements service.Service. 114 func (s *Service) Conf() common.Conf { 115 return s.Service.Conf 116 } 117 118 // Validate checks the service for invalid values. 119 func (s *Service) Validate() error { 120 if err := s.Service.Validate(renderer); err != nil { 121 return errors.Trace(err) 122 } 123 124 if s.Service.Conf.Transient { 125 return errors.NotSupportedf("transient services") 126 } 127 128 if s.Service.Conf.AfterStopped != "" { 129 return errors.NotSupportedf("Conf.AfterStopped") 130 } 131 132 return nil 133 } 134 135 func (s *Service) Running() (bool, error) { 136 if ok, err := s.Installed(); err != nil { 137 return false, errors.Trace(err) 138 } else if !ok { 139 return false, nil 140 } 141 return s.manager.Running(s.Name()) 142 } 143 144 // Installed returns whether the service is installed 145 func (s *Service) Installed() (bool, error) { 146 services, err := ListServices() 147 if err != nil { 148 return false, errors.Trace(err) 149 } 150 for _, val := range services { 151 if s.Name() == val { 152 return true, nil 153 } 154 } 155 return false, nil 156 } 157 158 // Exists returns whether the service configuration reflects the 159 // desired state 160 func (s *Service) Exists() (bool, error) { 161 return s.manager.Exists(s.Name(), s.Conf()) 162 } 163 164 // Start starts the service. 165 func (s *Service) Start() error { 166 logger.Infof("Starting service %q", s.Service.Name) 167 running, err := s.Running() 168 if err != nil { 169 return errors.Trace(err) 170 } 171 if running { 172 logger.Infof("Service %q already running", s.Service.Name) 173 return nil 174 } 175 err = s.manager.Start(s.Name()) 176 return err 177 } 178 179 // Stop stops the service. 180 func (s *Service) Stop() error { 181 running, err := s.Running() 182 if err != nil { 183 return errors.Trace(err) 184 } 185 if !running { 186 return nil 187 } 188 err = s.manager.Stop(s.Name()) 189 return err 190 } 191 192 // Remove deletes the service. 193 func (s *Service) Remove() error { 194 installed, err := s.Installed() 195 if err != nil { 196 return err 197 } 198 if !installed { 199 return nil 200 } 201 202 err = s.Stop() 203 if err != nil { 204 return errors.Trace(err) 205 } 206 err = s.manager.Delete(s.Name()) 207 return err 208 } 209 210 // Install installs and starts the service. 211 func (s *Service) Install() error { 212 err := s.Validate() 213 if err != nil { 214 return errors.Trace(err) 215 } 216 installed, err := s.Installed() 217 if err != nil { 218 return errors.Trace(err) 219 } 220 if installed { 221 return errors.Errorf("Service %s already installed", s.Service.Name) 222 } 223 224 logger.Infof("Installing Service %v", s.Name) 225 err = s.manager.Create(s.Name(), s.Conf()) 226 if err != nil { 227 return errors.Trace(err) 228 } 229 return nil 230 } 231 232 // InstallCommands returns shell commands to install the service. 233 func (s *Service) InstallCommands() ([]string, error) { 234 cmd := fmt.Sprintf(serviceInstallCommands[1:], 235 renderer.Quote(s.Service.Name), 236 renderer.Quote(s.Service.Conf.Desc), 237 renderer.Quote(s.Service.Conf.ExecStart), 238 renderer.Quote(s.Service.Name), 239 renderer.Quote(s.Service.Name), 240 ) 241 return strings.Split(cmd, "\n"), nil 242 } 243 244 // StartCommands returns shell commands to start the service. 245 func (s *Service) StartCommands() ([]string, error) { 246 cmd := fmt.Sprintf(`Start-Service %s`, renderer.Quote(s.Service.Name)) 247 return []string{cmd}, nil 248 } 249 250 const serviceInstallCommands = ` 251 New-Service -Credential $jujuCreds -Name %s -DependsOn Winmgmt -DisplayName %s %s 252 sc.exe failure %s reset=5 actions=restart/1000 253 sc.exe failureflag %s 1`