Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Add FreeBSD support (#197)
* Add FreeBSD support * freebsd: use service cmd instead of rc script directly
- Loading branch information
Showing
2 changed files
with
221 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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" | ||
| ` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters