221 lines
3.9 KiB
Go
221 lines
3.9 KiB
Go
|
// Copyright 2019 Daniel Theophanes.
|
||
|
// Use of this source code is governed by a zlib-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package service
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"os/signal"
|
||
|
"syscall"
|
||
|
"text/template"
|
||
|
)
|
||
|
|
||
|
const version = "freebsd"
|
||
|
|
||
|
type freebsdSystem struct{}
|
||
|
|
||
|
func (freebsdSystem) String() string {
|
||
|
return version
|
||
|
}
|
||
|
func (freebsdSystem) Detect() bool {
|
||
|
return true
|
||
|
}
|
||
|
func (freebsdSystem) Interactive() bool {
|
||
|
return interactive
|
||
|
}
|
||
|
func (freebsdSystem) New(i Interface, c *Config) (Service, error) {
|
||
|
s := &freebsdService{
|
||
|
i: i,
|
||
|
Config: c,
|
||
|
}
|
||
|
|
||
|
return s, nil
|
||
|
}
|
||
|
|
||
|
func init() {
|
||
|
ChooseSystem(freebsdSystem{})
|
||
|
}
|
||
|
|
||
|
var interactive = false
|
||
|
|
||
|
func init() {
|
||
|
var err error
|
||
|
interactive, err = isInteractive()
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func isInteractive() (bool, error) {
|
||
|
return os.Getenv("IS_DAEMON") != "1", nil
|
||
|
}
|
||
|
|
||
|
type freebsdService struct {
|
||
|
i Interface
|
||
|
*Config
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) String() string {
|
||
|
if len(s.DisplayName) > 0 {
|
||
|
return s.DisplayName
|
||
|
}
|
||
|
return s.Name
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) Platform() string {
|
||
|
return version
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) template() *template.Template {
|
||
|
functions := template.FuncMap{
|
||
|
"bool": func(v bool) string {
|
||
|
if v {
|
||
|
return "true"
|
||
|
}
|
||
|
return "false"
|
||
|
},
|
||
|
}
|
||
|
|
||
|
customConfig := s.Option.string(optionSysvScript, "")
|
||
|
|
||
|
if customConfig != "" {
|
||
|
return template.Must(template.New("").Funcs(functions).Parse(customConfig))
|
||
|
} else {
|
||
|
return template.Must(template.New("").Funcs(functions).Parse(rcScript))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) configPath() (cp string, err error) {
|
||
|
cp = "/usr/local/etc/rc.d/" + s.Config.Name
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) Install() error {
|
||
|
path, err := s.execPath()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// write start script
|
||
|
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()
|
||
|
|
||
|
var to = &struct {
|
||
|
*Config
|
||
|
Path string
|
||
|
}{
|
||
|
s.Config,
|
||
|
path,
|
||
|
}
|
||
|
|
||
|
err = s.template().Execute(f, to)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if err = os.Chmod(confPath, 0755); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) Uninstall() error {
|
||
|
cp, err := s.configPath()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return os.Remove(cp)
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) Status() (Status, error) {
|
||
|
cp, err := s.configPath()
|
||
|
if err != nil {
|
||
|
return StatusUnknown, err
|
||
|
}
|
||
|
|
||
|
if _, err = os.Stat(cp); os.IsNotExist(err) {
|
||
|
return StatusStopped, ErrNotInstalled
|
||
|
}
|
||
|
|
||
|
status, _, err := runCommand("service", false, s.Name, "status")
|
||
|
if status == 1 {
|
||
|
return StatusStopped, nil
|
||
|
} else if err != nil {
|
||
|
return StatusUnknown, err
|
||
|
}
|
||
|
return StatusRunning, nil
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) Start() error {
|
||
|
return run("service", s.Name, "start")
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) Stop() error {
|
||
|
return run("service", s.Name, "stop")
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) Restart() error {
|
||
|
return run("service", s.Name, "restart")
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) Run() error {
|
||
|
var err error
|
||
|
|
||
|
err = s.i.Start(s)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
s.Option.funcSingle(optionRunWait, func() {
|
||
|
var sigChan = make(chan os.Signal, 3)
|
||
|
signal.Notify(sigChan, syscall.SIGTERM, os.Interrupt)
|
||
|
<-sigChan
|
||
|
})()
|
||
|
|
||
|
return s.i.Stop(s)
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) Logger(errs chan<- error) (Logger, error) {
|
||
|
if interactive {
|
||
|
return ConsoleLogger, nil
|
||
|
}
|
||
|
return s.SystemLogger(errs)
|
||
|
}
|
||
|
|
||
|
func (s *freebsdService) SystemLogger(errs chan<- error) (Logger, error) {
|
||
|
return newSysLogger(s.Name, errs)
|
||
|
}
|
||
|
|
||
|
var rcScript = `#!/bin/sh
|
||
|
|
||
|
# PROVIDE: {{.Name}}
|
||
|
# REQUIRE: SERVERS
|
||
|
# KEYWORD: shutdown
|
||
|
|
||
|
. /etc/rc.subr
|
||
|
|
||
|
name="{{.Name}}"
|
||
|
{{.Name}}_env="IS_DAEMON=1"
|
||
|
pidfile="/var/run/${name}.pid"
|
||
|
command="/usr/sbin/daemon"
|
||
|
daemon_args="-P ${pidfile} -r -t \"${name}: daemon\"{{if .WorkingDirectory}} -c {{.WorkingDirectory}}{{end}}"
|
||
|
command_args="${daemon_args} {{.Path}}{{range .Arguments}} {{.}}{{end}}"
|
||
|
|
||
|
run_rc_command "$1"
|
||
|
`
|