From 14b2cc59a290407a6f1cb3daba59069429d9665b Mon Sep 17 00:00:00 2001 From: Olivier Poitrey Date: Sun, 7 Jun 2020 11:28:44 -0700 Subject: [PATCH] Add FreeBSD support (#197) * Add FreeBSD support * freebsd: use service cmd instead of rc script directly --- service_freebsd.go | 220 +++++++++++++++++++++++++++++++++++++++++++++ service_unix.go | 2 +- 2 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 service_freebsd.go diff --git a/service_freebsd.go b/service_freebsd.go new file mode 100644 index 0000000..66bb574 --- /dev/null +++ b/service_freebsd.go @@ -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" +` diff --git a/service_unix.go b/service_unix.go index b98189f..6959622 100644 --- a/service_unix.go +++ b/service_unix.go @@ -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