Add FreeBSD support (#197)
* Add FreeBSD support * freebsd: use service cmd instead of rc script directly
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
// 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"
|
||||
`
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a zlib-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux darwin solaris aix
|
||||
// +build linux darwin solaris aix freebsd
|
||||
|
||||
package service
|
||||
|
||||
|
||||
Reference in New Issue
Block a user