github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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`