Pull request: 3226 support service on OpenBSD
Merge in DNS/adguard-home from 3226-openbsd-svc to master
Closes #3226.
Squashed commit of the following:
commit bcf1a31a8343ae4b35c7cadeb45bc7a10863fda2
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Aug 24 17:43:31 2021 +0300
aghos: imp code
commit 3d4060ce6b5a37cf7af05b117b8bc4a49f69b2e8
Merge: 9e9225ec b92db25e
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Aug 24 17:09:00 2021 +0300
Merge branch 'master' into 3226-openbsd-svc
commit 9e9225ecb2af30fe46999b43c0683e4b3c946778
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Aug 24 17:02:52 2021 +0300
home: fix lil bugs
commit 03456f9a09081c6178dca0ac9887590b5d24f333
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Aug 24 16:18:48 2021 +0300
home: imp code
commit 5cdf4fcbae78c07b663190012228003fe94bfdee
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Fri Aug 20 23:32:15 2021 +0300
home: imp code, docs
commit d2a95faa0a7d176cdcba304e7226ebe11c1ce341
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Fri Aug 20 14:01:53 2021 +0300
home: sup service on openbsd
This commit is contained in:
parent
b92db25e6a
commit
16092e8ba9
|
@ -27,7 +27,8 @@ and this project adheres to
|
|||
- Settable timeouts for querying the upstream servers ([#2280]).
|
||||
- Configuration file parameters to change group and user ID on startup on Unix
|
||||
([#2763]).
|
||||
- Experimental OpenBSD support for AMD64 and 64-bit ARM CPUs ([#2439], [#3225]).
|
||||
- Experimental OpenBSD support for AMD64 and 64-bit ARM CPUs ([#2439], [#3225],
|
||||
[#3226]).
|
||||
- Support for custom port in DNS-over-HTTPS profiles for Apple's devices
|
||||
([#3172]).
|
||||
- `darwin/arm64` support ([#2443]).
|
||||
|
@ -118,6 +119,7 @@ and this project adheres to
|
|||
[#3198]: https://github.com/AdguardTeam/AdGuardHome/issues/3198
|
||||
[#3217]: https://github.com/AdguardTeam/AdGuardHome/issues/3217
|
||||
[#3225]: https://github.com/AdguardTeam/AdGuardHome/issues/3225
|
||||
[#3226]: https://github.com/AdguardTeam/AdGuardHome/issues/3226
|
||||
[#3256]: https://github.com/AdguardTeam/AdGuardHome/issues/3256
|
||||
[#3257]: https://github.com/AdguardTeam/AdGuardHome/issues/3257
|
||||
[#3289]: https://github.com/AdguardTeam/AdGuardHome/issues/3289
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
|
@ -59,8 +60,11 @@ func RunCommand(command string, arguments ...string) (int, string, error) {
|
|||
if len(out) > MaxCmdOutputSize {
|
||||
out = out[:MaxCmdOutputSize]
|
||||
}
|
||||
if err != nil {
|
||||
return 1, "", fmt.Errorf("exec.Command(%s) failed: %v: %s", command, err, string(out))
|
||||
|
||||
if errors.As(err, new(*exec.ExitError)) {
|
||||
return cmd.ProcessState.ExitCode(), string(out), nil
|
||||
} else if err != nil {
|
||||
return 1, "", fmt.Errorf("exec.Command(%s) failed: %w: %s", command, err, string(out))
|
||||
}
|
||||
|
||||
return cmd.ProcessState.ExitCode(), string(out), nil
|
||||
|
|
|
@ -116,13 +116,6 @@ func Main(clientBuildFS fs.FS) {
|
|||
}()
|
||||
|
||||
if args.serviceControlAction != "" {
|
||||
// TODO(a.garipov): github.com/kardianos/service doesn't seem to
|
||||
// support OpenBSD currently. Either patch it to do so or make
|
||||
// our own implementation of the service.System interface.
|
||||
if runtime.GOOS == "openbsd" {
|
||||
log.Fatal("service actions are not supported on openbsd, see issue 3226")
|
||||
}
|
||||
|
||||
handleServiceControlAction(args, clientBuildFS)
|
||||
|
||||
return
|
||||
|
|
|
@ -8,8 +8,10 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/version"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/kardianos/service"
|
||||
|
@ -26,33 +28,37 @@ const (
|
|||
serviceDescription = "AdGuard Home: Network-level blocker"
|
||||
)
|
||||
|
||||
// Represents the program that will be launched by a service or daemon
|
||||
// program represents the program that will be launched by as a service or a
|
||||
// daemon.
|
||||
type program struct {
|
||||
clientBuildFS fs.FS
|
||||
opts options
|
||||
}
|
||||
|
||||
// Start should quickly start the program
|
||||
func (p *program) Start(s service.Service) error {
|
||||
// Start should not block. Do the actual work async.
|
||||
// Start implements service.Interface interface for *program.
|
||||
func (p *program) Start(_ service.Service) (err error) {
|
||||
// Start should not block. Do the actual work async.
|
||||
args := p.opts
|
||||
args.runningAsService = true
|
||||
|
||||
go run(args, p.clientBuildFS)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the program
|
||||
func (p *program) Stop(s service.Service) error {
|
||||
// Stop should not block. Return with a few seconds.
|
||||
// Stop implements service.Interface interface for *program.
|
||||
func (p *program) Stop(_ service.Service) error {
|
||||
// Stop should not block. Return with a few seconds.
|
||||
if Context.appSignalChannel == nil {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
Context.appSignalChannel <- syscall.SIGINT
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// svcStatus check the service's status.
|
||||
// svcStatus returns the service's status.
|
||||
//
|
||||
// On OpenWrt, the service utility may not exist. We use our service script
|
||||
// directly in this case.
|
||||
|
@ -87,8 +93,8 @@ func svcAction(s service.Service, action string) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
// Send SIGHUP to a process with ID taken from our pid-file
|
||||
// If pid-file doesn't exist, find our PID using 'ps' command
|
||||
// Send SIGHUP to a process with PID taken from our .pid file. If it doesn't
|
||||
// exist, find our PID using 'ps' command.
|
||||
func sendSigReload() {
|
||||
if runtime.GOOS == "windows" {
|
||||
log.Error("not implemented on windows")
|
||||
|
@ -152,11 +158,16 @@ func sendSigReload() {
|
|||
// it is specified when we register a service, and it indicates to the app
|
||||
// that it is being run as a service/daemon.
|
||||
func handleServiceControlAction(opts options, clientBuildFS fs.FS) {
|
||||
// Call chooseSystem expicitly to introduce OpenBSD support for service
|
||||
// package. It's a noop for other GOOS values.
|
||||
chooseSystem()
|
||||
|
||||
action := opts.serviceControlAction
|
||||
log.Printf("Service control action: %s", action)
|
||||
|
||||
if action == "reload" {
|
||||
sendSigReload()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -164,6 +175,7 @@ func handleServiceControlAction(opts options, clientBuildFS fs.FS) {
|
|||
if err != nil {
|
||||
log.Fatal("Unable to find the path to the current directory")
|
||||
}
|
||||
|
||||
runOpts := opts
|
||||
runOpts.serviceControlAction = "run"
|
||||
svcConfig := &service.Config{
|
||||
|
@ -174,39 +186,39 @@ func handleServiceControlAction(opts options, clientBuildFS fs.FS) {
|
|||
Arguments: serialize(runOpts),
|
||||
}
|
||||
configureService(svcConfig)
|
||||
|
||||
prg := &program{
|
||||
clientBuildFS: clientBuildFS,
|
||||
opts: runOpts,
|
||||
}
|
||||
s, err := service.New(prg, svcConfig)
|
||||
if err != nil {
|
||||
var s service.Service
|
||||
if s, err = service.New(prg, svcConfig); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if action == "status" {
|
||||
switch action {
|
||||
case "status":
|
||||
handleServiceStatusCommand(s)
|
||||
} else if action == "run" {
|
||||
err = s.Run()
|
||||
if err != nil {
|
||||
case "run":
|
||||
if err = s.Run(); err != nil {
|
||||
log.Fatalf("Failed to run service: %s", err)
|
||||
}
|
||||
} else if action == "install" {
|
||||
case "install":
|
||||
initConfigFilename(opts)
|
||||
initWorkingDir(opts)
|
||||
handleServiceInstallCommand(s)
|
||||
} else if action == "uninstall" {
|
||||
case "uninstall":
|
||||
handleServiceUninstallCommand(s)
|
||||
} else {
|
||||
err = svcAction(s, action)
|
||||
if err != nil {
|
||||
default:
|
||||
if err = svcAction(s, action); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Action %s has been done successfully on %s", action, service.ChosenSystem().String())
|
||||
log.Printf("action %s has been done successfully on %s", action, service.ChosenSystem())
|
||||
}
|
||||
|
||||
// handleServiceStatusCommand handles service "status" command
|
||||
// handleServiceStatusCommand handles service "status" command.
|
||||
func handleServiceStatusCommand(s service.Service) {
|
||||
status, errSt := svcStatus(s)
|
||||
if errSt != nil {
|
||||
|
@ -231,15 +243,16 @@ func handleServiceInstallCommand(s service.Service) {
|
|||
}
|
||||
|
||||
if aghos.IsOpenWrt() {
|
||||
// On OpenWrt it is important to run enable after the service installation
|
||||
// Otherwise, the service won't start on the system startup
|
||||
// On OpenWrt it is important to run enable after the service
|
||||
// installation Otherwise, the service won't start on the system
|
||||
// startup.
|
||||
_, err = runInitdCommand("enable")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Start automatically after install
|
||||
// Start automatically after install.
|
||||
err = svcAction(s, "start")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to start the service: %s", err)
|
||||
|
@ -267,14 +280,13 @@ func handleServiceUninstallCommand(s service.Service) {
|
|||
}
|
||||
}
|
||||
|
||||
err := svcAction(s, "uninstall")
|
||||
if err != nil {
|
||||
if err := svcAction(s, "uninstall"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" {
|
||||
// Remove log files on cleanup and log errors.
|
||||
err = os.Remove(launchdStdoutPath)
|
||||
err := os.Remove(launchdStdoutPath)
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
log.Printf("removing stdout file: %s", err)
|
||||
}
|
||||
|
@ -313,6 +325,9 @@ func configureService(c *service.Config) {
|
|||
} else if runtime.GOOS == "freebsd" {
|
||||
c.Option["SysvScript"] = freeBSDScript
|
||||
}
|
||||
|
||||
c.Option["RunComScript"] = openBSDScript
|
||||
c.Option["SvcInfo"] = fmt.Sprintf("%s %s", version.Full(), time.Now())
|
||||
}
|
||||
|
||||
// runInitdCommand runs init.d service command
|
||||
|
@ -551,3 +566,17 @@ command="/usr/sbin/daemon"
|
|||
command_args="-p ${pidfile} -f -r {{.WorkingDirectory}}/{{.Name}}"
|
||||
run_rc_command "$1"
|
||||
`
|
||||
|
||||
const openBSDScript = `#!/bin/sh
|
||||
#
|
||||
# $OpenBSD: {{ .SvcInfo }}
|
||||
|
||||
daemon="{{.Path}}"
|
||||
daemon_flags={{ .Arguments | args }}
|
||||
|
||||
. /etc/rc.d/rc.subr
|
||||
|
||||
rc_bg=YES
|
||||
|
||||
rc_cmd $1
|
||||
`
|
||||
|
|
|
@ -0,0 +1,432 @@
|
|||
//go:build openbsd
|
||||
// +build openbsd
|
||||
|
||||
package home
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"text/template"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/kardianos/service"
|
||||
)
|
||||
|
||||
// OpenBSD Service Implementation
|
||||
//
|
||||
// The file contains OpenBSD implementations for service.System and
|
||||
// service.Service interfaces. It uses the default approach for RunCom-based
|
||||
// services systems, e.g. rc.d script. It's written as if it was in a separate
|
||||
// package and has only one internal dependency.
|
||||
//
|
||||
// TODO(e.burkov): Perhaps, file a PR to github.com/kardianos/service.
|
||||
|
||||
// sysVersion is the version of local service.System interface
|
||||
// implementation.
|
||||
const sysVersion = "openbsd-runcom"
|
||||
|
||||
func chooseSystem() {
|
||||
service.ChooseSystem(openbsdSystem{})
|
||||
}
|
||||
|
||||
// openbsdSystem is the service.System to be used on the OpenBSD.
|
||||
type openbsdSystem struct{}
|
||||
|
||||
// String implements service.System interface for openbsdSystem.
|
||||
func (openbsdSystem) String() string {
|
||||
return sysVersion
|
||||
}
|
||||
|
||||
// Detect implements service.System interface for openbsdSystem.
|
||||
func (openbsdSystem) Detect() (ok bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Interactive implements service.System interface for openbsdSystem.
|
||||
func (openbsdSystem) Interactive() (ok bool) {
|
||||
return os.Getppid() != 1
|
||||
}
|
||||
|
||||
// New implements service.System interface for openbsdSystem.
|
||||
func (openbsdSystem) New(i service.Interface, c *service.Config) (s service.Service, err error) {
|
||||
return &openbsdRunComService{
|
||||
i: i,
|
||||
cfg: c,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// openbsdRunComService is the RunCom-based service.Service to be used on the
|
||||
// OpenBSD.
|
||||
type openbsdRunComService struct {
|
||||
i service.Interface
|
||||
cfg *service.Config
|
||||
}
|
||||
|
||||
// Platform implements service.Service interface for *openbsdRunComService.
|
||||
func (*openbsdRunComService) Platform() (p string) {
|
||||
return "openbsd"
|
||||
}
|
||||
|
||||
// String implements service.Service interface for *openbsdRunComService.
|
||||
func (s *openbsdRunComService) String() string {
|
||||
return stringutil.Coalesce(s.cfg.DisplayName, s.cfg.Name)
|
||||
}
|
||||
|
||||
// getBool returns the value of the given name from kv, assuming the value is a
|
||||
// boolean. If the value isn't found or is not of the type, the defaultValue is
|
||||
// returned.
|
||||
func getBool(kv service.KeyValue, name string, defaultValue bool) (val bool) {
|
||||
var ok bool
|
||||
if val, ok = kv[name].(bool); ok {
|
||||
return val
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// getString returns the value of the given name from kv, assuming the value is
|
||||
// a string. If the value isn't found or is not of the type, the defaultValue
|
||||
// is returned.
|
||||
func getString(kv service.KeyValue, name, defaultValue string) (val string) {
|
||||
var ok bool
|
||||
if val, ok = kv[name].(string); ok {
|
||||
return val
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// getFuncNiladic returns the value of the given name from kv, assuming the
|
||||
// value is a func(). If the value isn't found or is not of the type, the
|
||||
// defaultValue is returned.
|
||||
func getFuncNiladic(kv service.KeyValue, name string, defaultValue func()) (val func()) {
|
||||
var ok bool
|
||||
if val, ok = kv[name].(func()); ok {
|
||||
return val
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
const (
|
||||
// optionUserService is the UserService option name.
|
||||
optionUserService = "UserService"
|
||||
|
||||
// optionUserServiceDefault is the UserService option default value.
|
||||
optionUserServiceDefault = false
|
||||
|
||||
// errNoUserServiceRunCom is returned when the service uses some custom
|
||||
// path to script.
|
||||
errNoUserServiceRunCom errors.Error = "user services are not supported on " + sysVersion
|
||||
)
|
||||
|
||||
// scriptPath returns the absolute path to the script. It's commonly used to
|
||||
// send commands to the service.
|
||||
func (s *openbsdRunComService) scriptPath() (cp string, err error) {
|
||||
if getBool(s.cfg.Option, optionUserService, optionUserServiceDefault) {
|
||||
return "", errNoUserServiceRunCom
|
||||
}
|
||||
|
||||
const scriptPathPref = "/etc/rc.d"
|
||||
|
||||
return filepath.Join(scriptPathPref, s.cfg.Name), nil
|
||||
}
|
||||
|
||||
const (
|
||||
// optionRunComScript is the RunCom script option name.
|
||||
optionRunComScript = "RunComScript"
|
||||
|
||||
// runComScript is the default RunCom script.
|
||||
runComScript = `#!/bin/sh
|
||||
#
|
||||
# $OpenBSD: {{ .SvcInfo }}
|
||||
|
||||
daemon="{{.Path}}"
|
||||
daemon_flags={{ .Arguments | args }}
|
||||
|
||||
. /etc/rc.d/rc.subr
|
||||
|
||||
rc_bg=YES
|
||||
|
||||
rc_cmd $1
|
||||
`
|
||||
)
|
||||
|
||||
// template returns the script template to put into rc.d.
|
||||
func (s *openbsdRunComService) template() (t *template.Template) {
|
||||
tf := map[string]interface{}{
|
||||
"args": func(sl []string) string {
|
||||
return `"` + strings.Join(sl, " ") + `"`
|
||||
},
|
||||
}
|
||||
|
||||
return template.Must(template.New("").Funcs(tf).Parse(getString(
|
||||
s.cfg.Option,
|
||||
optionRunComScript,
|
||||
runComScript,
|
||||
)))
|
||||
}
|
||||
|
||||
// execPath returns the absolute path to the excutable to be run as a service.
|
||||
func (s *openbsdRunComService) execPath() (path string, err error) {
|
||||
if c := s.cfg; c != nil && len(c.Executable) != 0 {
|
||||
return filepath.Abs(c.Executable)
|
||||
}
|
||||
|
||||
if path, err = os.Executable(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Abs(path)
|
||||
}
|
||||
|
||||
// annotate wraps errors.Annotate applying a common error format.
|
||||
func (s *openbsdRunComService) annotate(action string, err error) (annotated error) {
|
||||
return errors.Annotate(err, "%s %s %s service: %w", action, sysVersion, s.cfg.Name)
|
||||
}
|
||||
|
||||
// Install implements service.Service interface for *openbsdRunComService.
|
||||
func (s *openbsdRunComService) Install() (err error) {
|
||||
defer func() { err = s.annotate("installing", err) }()
|
||||
|
||||
if err = s.writeScript(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.configureSysStartup(true)
|
||||
}
|
||||
|
||||
// configureSysStartup adds s into the group of packages started with system.
|
||||
func (s *openbsdRunComService) configureSysStartup(enable bool) (err error) {
|
||||
cmd := "enable"
|
||||
if !enable {
|
||||
cmd = "disable"
|
||||
}
|
||||
|
||||
var code int
|
||||
code, _, err = aghos.RunCommand("rcctl", cmd, s.cfg.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if code != 0 {
|
||||
return fmt.Errorf("rcctl finished with code %d", code)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeScript tries to write the script for the service.
|
||||
func (s *openbsdRunComService) writeScript() (err error) {
|
||||
var scriptPath string
|
||||
if scriptPath, err = s.scriptPath(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = os.Stat(scriptPath); !errors.Is(err, os.ErrNotExist) {
|
||||
return fmt.Errorf("script already exists at %s", scriptPath)
|
||||
}
|
||||
|
||||
var execPath string
|
||||
if execPath, err = s.execPath(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := s.template()
|
||||
f, err := os.Create(scriptPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating rc.d script file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = t.Execute(f, &struct {
|
||||
*service.Config
|
||||
Path string
|
||||
SvcInfo string
|
||||
}{
|
||||
Config: s.cfg,
|
||||
Path: execPath,
|
||||
SvcInfo: getString(s.cfg.Option, "SvcInfo", s.String()),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return errors.Annotate(
|
||||
os.Chmod(scriptPath, 0o755),
|
||||
"changing rc.d script file permissions: %w",
|
||||
)
|
||||
}
|
||||
|
||||
// Uninstall implements service.Service interface for *openbsdRunComService.
|
||||
func (s *openbsdRunComService) Uninstall() (err error) {
|
||||
defer func() { err = s.annotate("uninstalling", err) }()
|
||||
|
||||
if err = s.configureSysStartup(false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var scriptPath string
|
||||
if scriptPath, err = s.scriptPath(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.Remove(scriptPath); errors.Is(err, os.ErrNotExist) {
|
||||
return service.ErrNotInstalled
|
||||
}
|
||||
|
||||
return errors.Annotate(err, "removing rc.d script: %w")
|
||||
}
|
||||
|
||||
// optionRunWait is the name of the option associated with function which waits
|
||||
// for the service to be stopped.
|
||||
const optionRunWait = "RunWait"
|
||||
|
||||
// runWait is the default function to wait for service to be stopped.
|
||||
func runWait() {
|
||||
sigChan := make(chan os.Signal, 3)
|
||||
signal.Notify(sigChan, syscall.SIGTERM, os.Interrupt)
|
||||
<-sigChan
|
||||
}
|
||||
|
||||
// Run implements service.Service interface for *openbsdRunComService.
|
||||
func (s *openbsdRunComService) Run() (err error) {
|
||||
if err = s.i.Start(s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
getFuncNiladic(s.cfg.Option, optionRunWait, runWait)()
|
||||
|
||||
return s.i.Stop(s)
|
||||
}
|
||||
|
||||
// runCom calls the script with the specified cmd.
|
||||
func (s *openbsdRunComService) runCom(cmd string) (out string, err error) {
|
||||
var scriptPath string
|
||||
if scriptPath, err = s.scriptPath(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// TODO(e.burkov): It's possible that os.ErrNotExist is caused by
|
||||
// something different than the service script's non-existence. Keep it
|
||||
// in mind, when replace the aghos.RunCommand.
|
||||
_, out, err = aghos.RunCommand(scriptPath, cmd)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return "", service.ErrNotInstalled
|
||||
}
|
||||
|
||||
return out, err
|
||||
}
|
||||
|
||||
// Status implements service.Service interface for *openbsdRunComService.
|
||||
func (s *openbsdRunComService) Status() (status service.Status, err error) {
|
||||
defer func() { err = s.annotate("getting status of", err) }()
|
||||
|
||||
var out string
|
||||
if out, err = s.runCom("check"); err != nil {
|
||||
return service.StatusUnknown, err
|
||||
}
|
||||
|
||||
name := s.cfg.Name
|
||||
switch out {
|
||||
case fmt.Sprintf("%s(ok)\n", name):
|
||||
return service.StatusRunning, nil
|
||||
case fmt.Sprintf("%s(failed)\n", name):
|
||||
return service.StatusStopped, nil
|
||||
default:
|
||||
return service.StatusUnknown, service.ErrNotInstalled
|
||||
}
|
||||
}
|
||||
|
||||
// Start implements service.Service interface for *openbsdRunComService.
|
||||
func (s *openbsdRunComService) Start() (err error) {
|
||||
_, err = s.runCom("start")
|
||||
|
||||
return s.annotate("starting", err)
|
||||
}
|
||||
|
||||
// Stop implements service.Service interface for *openbsdRunComService.
|
||||
func (s *openbsdRunComService) Stop() (err error) {
|
||||
_, err = s.runCom("stop")
|
||||
|
||||
return s.annotate("stopping", err)
|
||||
}
|
||||
|
||||
// Restart implements service.Service interface for *openbsdRunComService.
|
||||
func (s *openbsdRunComService) Restart() (err error) {
|
||||
if err = s.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.Start()
|
||||
}
|
||||
|
||||
// Logger implements service.Service interface for *openbsdRunComService.
|
||||
func (s *openbsdRunComService) Logger(errs chan<- error) (l service.Logger, err error) {
|
||||
if service.ChosenSystem().Interactive() {
|
||||
return service.ConsoleLogger, nil
|
||||
}
|
||||
|
||||
return s.SystemLogger(errs)
|
||||
}
|
||||
|
||||
// SystemLogger implements service.Service interface for *openbsdRunComService.
|
||||
func (s *openbsdRunComService) SystemLogger(errs chan<- error) (l service.Logger, err error) {
|
||||
return newSysLogger(s.cfg.Name, errs)
|
||||
}
|
||||
|
||||
// newSysLogger returns a stub service.Logger implementation.
|
||||
func newSysLogger(_ string, _ chan<- error) (service.Logger, error) {
|
||||
return sysLogger{}, nil
|
||||
}
|
||||
|
||||
// sysLogger wraps calls of the logging functions understandable for service
|
||||
// interfaces.
|
||||
type sysLogger struct{}
|
||||
|
||||
// Error implements service.Logger interface for sysLogger.
|
||||
func (sysLogger) Error(v ...interface{}) error {
|
||||
log.Error(fmt.Sprint(v...))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Warning implements service.Logger interface for sysLogger.
|
||||
func (sysLogger) Warning(v ...interface{}) error {
|
||||
log.Info("warning: %s", fmt.Sprint(v...))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Info implements service.Logger interface for sysLogger.
|
||||
func (sysLogger) Info(v ...interface{}) error {
|
||||
log.Info(fmt.Sprint(v...))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Errorf implements service.Logger interface for sysLogger.
|
||||
func (sysLogger) Errorf(format string, a ...interface{}) error {
|
||||
log.Error(format, a...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Warningf implements service.Logger interface for sysLogger.
|
||||
func (sysLogger) Warningf(format string, a ...interface{}) error {
|
||||
log.Info("warning: %s", fmt.Sprintf(format, a...))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Infof implements service.Logger interface for sysLogger.
|
||||
func (sysLogger) Infof(format string, a ...interface{}) error {
|
||||
log.Info(format, a...)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
//go:build !openbsd
|
||||
// +build !openbsd
|
||||
|
||||
package home
|
||||
|
||||
func chooseSystem() {}
|
Loading…
Reference in New Issue