Files
unitdore/initsys/initsys.go
T
warkanum df9b226c7e feat: OpenRC and runit init backends behind an Init interface
Extract init-neutral unit resolution into service/, add initsys.Init with systemd, openrc and runit backends, selected by 'init:' config or autodetect. OpenRC and runit are root only; rootless units run as system services as that user. Add docs and generated examples for all three.
2026-10-12 20:19:58 +02:00

78 lines
2.2 KiB
Go

// Package initsys selects the init system backend that installs and controls
// unitdore services.
package initsys
import (
"fmt"
"os"
"path/filepath"
"github.com/warkanum/unitdore/config"
"github.com/warkanum/unitdore/openrc"
"github.com/warkanum/unitdore/runit"
"github.com/warkanum/unitdore/systemd"
)
// Backend names.
const (
Systemd = "systemd"
OpenRC = "openrc"
Runit = "runit"
)
// Init is implemented by every init system backend. u.User selects the scope;
// backends that cannot do per-user managers run user units as system services.
type Init interface {
Name() string
ServiceName(u config.Unit, prefix, suffix string) string
UnitPath(u config.Unit, prefix, suffix string) (string, error)
Generate(u config.Unit, prefix, suffix string) (string, error)
Install(u config.Unit, prefix, suffix string) error
Uninstall(u config.Unit, prefix, suffix string) error
Enable(u config.Unit, prefix, suffix string) error
Disable(u config.Unit, prefix, suffix string) error
Start(u config.Unit, prefix, suffix string) error
Stop(u config.Unit, prefix, suffix string) error
// Status returns active | inactive | failed | activating | deactivating | unknown.
Status(u config.Unit, prefix, suffix string) string
IsInstalled(u config.Unit, prefix, suffix string) bool
// SupportsLinger reports whether user units need systemd-style linger.
SupportsLinger() bool
}
// New returns the backend named in cfg.Init, or the detected one.
func New(cfg *config.Config) (Init, error) {
name := cfg.Init
if name == "" {
name = Detect("/")
}
switch name {
case Systemd:
return systemd.Backend{}, nil
case OpenRC:
return openrc.Backend{}, nil
case Runit:
return runit.Backend{ServiceDir: cfg.ServiceDir}, nil
default:
return nil, fmt.Errorf("unknown init %q (want systemd, openrc or runit)", name)
}
}
// Detect guesses the running init system below root. Falls back to systemd.
func Detect(root string) string {
has := func(p string) bool {
_, err := os.Stat(filepath.Join(root, p))
return err == nil
}
switch {
case has("run/systemd/system"):
return Systemd
case has("run/openrc"), has("sbin/openrc-run"):
return OpenRC
case has("run/runit"), has("etc/runit"):
return Runit
default:
return Systemd
}
}