github.com/Axway/agent-sdk@v1.1.101/pkg/cmd/service/daemon/daemonlinuxsystemd.go (about) 1 package daemon 2 3 import ( 4 "fmt" 5 "path/filepath" 6 "regexp" 7 "strings" 8 "text/template" 9 10 "github.com/Axway/agent-sdk/pkg/cmd" 11 ) 12 13 const ( 14 systemctl = "systemctl" 15 journalctl = "journalctl" 16 serviceSuffix = ".service" 17 ) 18 19 // systemDRecord - standard record (struct) for linux systemD version of daemon package 20 type systemDRecord struct { 21 name string 22 description string 23 dependencies []string 24 user string 25 group string 26 envFile string 27 workingDir string 28 } 29 30 // Standard service path for systemD daemons 31 func (s *systemDRecord) servicePath() string { 32 return "/etc/systemd/system/" + s.serviceName() 33 } 34 35 // Is a service installed 36 func (s *systemDRecord) isInstalled() bool { 37 38 if _, err := fs.Stat(s.servicePath()); err == nil { 39 return true 40 } 41 42 return false 43 } 44 45 // Check service is running 46 func (s *systemDRecord) checkRunning() (string, bool) { 47 output, err := execCmd(systemctl, "status", s.serviceName()) 48 if err == nil { 49 if matched, err := regexp.MatchString("Active: active", string(output)); err == nil && matched { 50 reg := regexp.MustCompile("Main PID: ([0-9]+)") 51 data := reg.FindStringSubmatch(string(output)) 52 if len(data) > 1 { 53 return "Service (pid " + data[1] + ") is running...", true 54 } 55 return "Service is running...", true 56 } 57 } 58 59 return "Service is stopped", false 60 } 61 62 func (s *systemDRecord) serviceName() string { 63 return s.name + serviceSuffix 64 } 65 66 // Install the service 67 func (s *systemDRecord) install(args ...string) (string, error) { 68 if ok, err := checkPrivileges(); !ok { 69 return failed, err 70 } 71 72 srvPath := s.servicePath() 73 74 if s.isInstalled() { 75 return failed, ErrAlreadyInstalled.FormatError(s.serviceName()) 76 } 77 78 file, err := fs.Create(srvPath) 79 if err != nil { 80 return failed, err 81 } 82 83 execPath, err := executablePath(s.name) 84 if err != nil { 85 file.Close() 86 return failed, err 87 } 88 89 s.workingDir = filepath.Dir(execPath) 90 91 templ, err := template.New("systemDConfig").Parse(systemDConfig) 92 if err != nil { 93 file.Close() 94 return failed, err 95 } 96 97 if s.envFile != "" { 98 args = append(args, fmt.Sprintf("--%s", cmd.EnvFileFlag), s.envFile) 99 } 100 101 if err := templ.Execute( 102 file, 103 &struct { 104 Name, Description, Dependencies, User, Group, Path, WorkingDir, Args string 105 }{ 106 s.name, 107 s.description, 108 strings.Join(s.dependencies, " "), 109 s.user, 110 s.group, 111 execPath, 112 s.workingDir, 113 strings.Join(args, " "), 114 }, 115 ); err != nil { 116 file.Close() 117 return failed, err 118 } 119 120 if _, err := execCmd(systemctl, "daemon-reload"); err != nil { 121 file.Close() 122 return failed, err 123 } 124 125 file.Close() 126 return success, nil 127 } 128 129 // Install the service 130 func (s *systemDRecord) Install(args ...string) (string, error) { 131 installAction := "Install " + s.description + ":" 132 133 msg, err := s.install(args...) 134 return installAction + msg, err 135 } 136 137 // Update the service 138 func (s *systemDRecord) Update(args ...string) (string, error) { 139 updateAction := "Updating " + s.description + ":" 140 141 msg, err := s.remove() 142 if err != nil { 143 return updateAction + msg, err 144 } 145 146 msg, err = s.install(args...) 147 return updateAction + msg, err 148 } 149 150 // Remove the service 151 func (s *systemDRecord) remove() (string, error) { 152 if ok, err := checkPrivileges(); !ok { 153 return failed, err 154 } 155 156 if !s.isInstalled() { 157 return failed, ErrNotInstalled.FormatError(s.serviceName()) 158 } 159 160 if _, ok := s.checkRunning(); ok { 161 return failed, ErrCurrentlyRunning.FormatError(s.serviceName()) 162 } 163 164 if _, err := execCmd(systemctl, "disable", s.serviceName()); err != nil { 165 return failed, err 166 } 167 168 if err := fs.Remove(s.servicePath()); err != nil { 169 return failed, err 170 } 171 172 return success, nil 173 174 } 175 176 // Remove the service 177 func (s *systemDRecord) Remove() (string, error) { 178 removeAction := "Removing " + s.description + ":" 179 180 msg, err := s.remove() 181 return removeAction + msg, err 182 } 183 184 // Start the service 185 func (s *systemDRecord) Start() (string, error) { 186 startAction := "Starting " + s.description + ":" 187 188 if ok, err := checkPrivileges(); !ok { 189 return startAction + failed, err 190 } 191 192 if !s.isInstalled() { 193 return startAction + failed, ErrNotInstalled.FormatError(s.serviceName()) 194 } 195 196 if _, ok := s.checkRunning(); ok { 197 return startAction + failed, ErrAlreadyRunning.FormatError(s.serviceName()) 198 } 199 200 if _, err := execCmd(systemctl, "start", s.serviceName()); err != nil { 201 return startAction + failed, err 202 } 203 204 return startAction + success, nil 205 } 206 207 // Stop the service 208 func (s *systemDRecord) Stop() (string, error) { 209 stopAction := "Stopping " + s.description + ":" 210 211 if ok, err := checkPrivileges(); !ok { 212 return stopAction + failed, err 213 } 214 215 if !s.isInstalled() { 216 return stopAction + failed, ErrNotInstalled.FormatError(s.serviceName()) 217 } 218 219 if _, ok := s.checkRunning(); !ok { 220 return stopAction + failed, ErrAlreadyStopped.FormatError(s.serviceName()) 221 } 222 223 if _, err := execCmd(systemctl, "stop", s.serviceName()); err != nil { 224 return stopAction + failed, err 225 } 226 227 return stopAction + success, nil 228 } 229 230 // Status - Get service status 231 func (s *systemDRecord) Status() (string, error) { 232 233 if ok, err := checkPrivileges(); !ok { 234 return "", err 235 } 236 237 if !s.isInstalled() { 238 return statNotInstalled, ErrNotInstalled.FormatError(s.serviceName()) 239 } 240 241 statusAction, _ := s.checkRunning() 242 243 return statusAction, nil 244 } 245 246 // Logs - Get service logs 247 func (s *systemDRecord) Logs() (string, error) { 248 249 if !s.isInstalled() { 250 return statNotInstalled, ErrNotInstalled.FormatError(s.serviceName()) 251 } 252 253 var data []byte 254 var err error 255 256 // run journalctl with --no-pager (get akk output), -b (logs on current boot only), -u service_name 257 if data, err = execCmd(journalctl, "--no-pager", "-b", "-u", s.serviceName()); err != nil { 258 return "", err 259 } 260 261 dataOutput := fmt.Sprintf("%s\nSee `journalctl -h` for alternative options to the `journalctl -u %s` command", string(data), s.serviceName()) 262 263 return dataOutput, nil 264 } 265 266 // Run - Run service 267 func (s *systemDRecord) Run(e Executable) (string, error) { 268 runAction := "Running " + s.description + ":" 269 e.Run() 270 return runAction + " completed.", nil 271 } 272 273 // Status - Get service status 274 func (s *systemDRecord) Enable() (string, error) { 275 enableAction := "Enabling " + s.description + ":" 276 277 if ok, err := checkPrivileges(); !ok { 278 return enableAction + failed, err 279 } 280 281 if !s.isInstalled() { 282 return enableAction + failed, ErrNotInstalled.FormatError(s.serviceName()) 283 } 284 285 if _, err := execCmd(systemctl, "enable", s.serviceName()); err != nil { 286 return enableAction + failed, err 287 } 288 289 return enableAction + success, nil 290 } 291 292 // GetTemplate - gets service config template 293 func (s *systemDRecord) GetTemplate() string { 294 return systemDConfig 295 } 296 297 // GetServiceName - gets service name 298 func (s *systemDRecord) GetServiceName() string { 299 return s.serviceName() 300 } 301 302 // SetTemplate - sets service config template 303 func (s *systemDRecord) SetTemplate(tplStr string) error { 304 systemDConfig = tplStr 305 return nil 306 } 307 308 // SetEnvFile - sets the envFile that will be used by the service 309 func (s *systemDRecord) SetEnvFile(envFile string) error { 310 // set the absolute path, incase it is relative 311 envFileAbsolute, _ := filepath.Abs(envFile) 312 s.envFile = envFileAbsolute 313 return nil 314 } 315 316 // SetUser - sets the user that will execute the service 317 func (s *systemDRecord) SetUser(user string) error { 318 s.user = user 319 return nil 320 } 321 322 // SetGroup - sets the group that will execute the service 323 func (s *systemDRecord) SetGroup(group string) error { 324 s.group = group 325 return nil 326 } 327 328 var systemDConfig = `[Unit] 329 Description={{.Description}} 330 Requires={{.Dependencies}} 331 After={{.Dependencies}} 332 333 [Service] 334 ExecStart={{.Path}} {{.Args}} 335 User={{.User}} 336 Group={{.Group}} 337 WorkingDirectory={{.WorkingDir}} 338 Restart=on-failure 339 RestartSec=60s 340 341 [Install] 342 WantedBy=multi-user.target 343 `