c880387dea
Breaking apart each service reduces the overall number of branches in the code, but increases duplication. Duplication can be taken care of in follow-up commits while still keeping the clarity of linear code. Also expiriment with code that would allow the user to add or remove systems. Still expirimental and not exposed in the API.
176 lines
3.3 KiB
Go
176 lines
3.3 KiB
Go
// Copyright 2015 Daniel Theophanes.
|
|
// Use of this source code is governed by a zlib-style
|
|
// license that can be found in the LICENSE file.package service
|
|
|
|
package service
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"os/signal"
|
|
"text/template"
|
|
"time"
|
|
|
|
"github.com/kardianos/osext"
|
|
)
|
|
|
|
func isSystemd() bool {
|
|
if _, err := os.Stat("/run/systemd/system"); err == nil {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
type systemd struct {
|
|
i Interface
|
|
*Config
|
|
}
|
|
|
|
func newSystemdService(i Interface, c *Config) (Service, error) {
|
|
s := &systemd{
|
|
i: i,
|
|
Config: c,
|
|
}
|
|
|
|
return s, nil
|
|
}
|
|
|
|
func (s *systemd) String() string {
|
|
if len(s.DisplayName) > 0 {
|
|
return s.DisplayName
|
|
}
|
|
return s.Name
|
|
}
|
|
|
|
// Systemd services should be supported, but are not currently.
|
|
var errNoUserServiceSystemd = errors.New("User services are not supported on systemd.")
|
|
|
|
func (s *systemd) configPath() (cp string, err error) {
|
|
if s.Config.UserService {
|
|
err = errNoUserServiceSystemd
|
|
return
|
|
}
|
|
cp = "/etc/systemd/system/" + s.Config.Name + ".service"
|
|
return
|
|
}
|
|
func (s *systemd) template() *template.Template {
|
|
return template.Must(template.New("").Funcs(tf).Parse(systemdScript))
|
|
}
|
|
|
|
func (s *systemd) Install() error {
|
|
confPath, err := s.configPath()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = os.Stat(confPath)
|
|
if err == nil {
|
|
return fmt.Errorf("Init already exists: %s", confPath)
|
|
}
|
|
|
|
f, err := os.Create(confPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
path, err := osext.Executable()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var to = &struct {
|
|
*Config
|
|
Path string
|
|
}{
|
|
s.Config,
|
|
path,
|
|
}
|
|
|
|
err = s.template().Execute(f, to)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = exec.Command("systemctl", "enable", s.Name+".service").Run()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return exec.Command("systemctl", "daemon-reload").Run()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *systemd) Uninstall() error {
|
|
exec.Command("systemctl", "disable", s.Name+".service").Run()
|
|
cp, err := s.configPath()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := os.Remove(cp); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *systemd) Logger(errs chan<- error) (Logger, error) {
|
|
if system.Interactive() {
|
|
return ConsoleLogger, nil
|
|
}
|
|
return s.SystemLogger(errs)
|
|
}
|
|
func (s *systemd) SystemLogger(errs chan<- error) (Logger, error) {
|
|
return newSysLogger(s.Name, errs)
|
|
}
|
|
|
|
func (s *systemd) Run() (err error) {
|
|
err = s.i.Start(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sigChan := make(chan os.Signal, 3)
|
|
|
|
signal.Notify(sigChan, os.Interrupt, os.Kill)
|
|
|
|
<-sigChan
|
|
|
|
return s.i.Stop(s)
|
|
}
|
|
|
|
func (s *systemd) Start() error {
|
|
return exec.Command("systemctl", "start", s.Name+".service").Run()
|
|
}
|
|
|
|
func (s *systemd) Stop() error {
|
|
return exec.Command("systemctl", "stop", s.Name+".service").Run()
|
|
}
|
|
|
|
func (s *systemd) Restart() error {
|
|
err := s.Stop()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
time.Sleep(50 * time.Millisecond)
|
|
return s.Start()
|
|
}
|
|
|
|
const systemdScript = `[Unit]
|
|
Description={{.Description}}
|
|
ConditionFileIsExecutable={{.Path|cmd}}
|
|
|
|
[Service]
|
|
StartLimitInterval=5
|
|
StartLimitBurst=10
|
|
ExecStart={{.Path|cmd}}{{range .Arguments}} {{.|cmd}}{{end}}
|
|
{{if .ChRoot}}RootDirectory={{.ChRoot|cmd}}{{end}}
|
|
{{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmd}}{{end}}
|
|
{{if .UserName}}User={{.UserName}}{{end}}
|
|
Restart=always
|
|
RestartSec=120
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
`
|