Install under procd, so OpenWrt counts as a host too

The binary was already static and musl-clean; the only thing standing between
Ham Lab and an OpenWrt box was that setup.go knew systemd and OpenRC and
nothing else, so an install there ended at "nothing starts it at boot."

Adds an embedded procd init script, detected on /sbin/procd + /etc/rc.common
ahead of the OpenRC branch. Both backends write /etc/init.d/hamlab, so
uninstall tells them apart by USE_PROCD=1 rather than by path.

Verified on the new router: procd supervises the instance, S95/K10 symlinks
land, and the hub drives its Docker and stacks like any other node. The
Updates tab needed nothing — apk-tools 3 answers the same query Alpine's does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-26 17:39:04 -07:00
co-authored by Claude Opus 5
parent 4e3c817b2a
commit 4450f1285d
3 changed files with 65 additions and 9 deletions
+25
View File
@@ -0,0 +1,25 @@
#!/bin/sh /etc/rc.common
# Ham Lab web console (written by hamlab install)
USE_PROCD=1
# after network and firewall, before the tail of the boot
START=95
STOP=10
start_service() {
procd_open_instance hamlab
procd_set_param command "@BIN@" serve
procd_set_param env HAMLAB_CONFIG="@CONFIG@" HOME="@HOME@" USER="@USER@"
procd_set_param user "@USER@"
procd_set_param group "@GROUP@"
procd_set_param pidfile /var/run/hamlab.pid
# respawn: settle 3s, 5 retries inside that window, then give up counting
procd_set_param respawn 3600 3 0
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
}
service_triggers() {
procd_add_reload_trigger network
}
+30 -6
View File
@@ -37,9 +37,14 @@ var systemdUnit string
//go:embed hamlab.openrc
var openrcScript string
//go:embed hamlab.procd
var procdScript string
const (
unitPath = "/etc/systemd/system/hamlab.service"
openrcPath = "/etc/init.d/hamlab"
unitPath = "/etc/systemd/system/hamlab.service"
openrcPath = "/etc/init.d/hamlab"
// procd (OpenWrt) uses the same path as OpenRC; the two are told apart by content.
procdPath = "/etc/init.d/hamlab"
sudoersPath = "/etc/sudoers.d/hamlab-quiet"
)
@@ -228,6 +233,19 @@ func Install(args []string, version string) error {
return err
}
}
case exists("/sbin/procd") && exists("/etc/rc.common"):
initSys = "procd"
if err := os.WriteFile(procdPath, []byte(fill(procdScript, vars)), 0o755); err != nil {
return err
}
if err := run(procdPath, "enable"); err != nil {
say("warning: %s enable: %v", procdPath, err)
}
if !o.noStart {
if err := run(procdPath, "restart"); err != nil {
return err
}
}
case exists("/sbin/openrc-run") || exists("/usr/sbin/openrc-run") || have("rc-service"):
initSys = "OpenRC"
if err := os.WriteFile(openrcPath, []byte(fill(openrcScript, vars)), 0o755); err != nil {
@@ -258,7 +276,7 @@ func Install(args []string, version string) error {
url := scheme + "://" + browseAddr(cfg.Listen)
switch {
case initSys == "":
say("\nNo systemd or OpenRC here, so nothing starts it at boot.\nRun it under your own supervisor, as %s:\n HAMLAB_CONFIG=%s %s serve", acct.name, cfgPath, dest)
say("\nNo systemd, procd or OpenRC here, so nothing starts it at boot.\nRun it under your own supervisor, as %s:\n HAMLAB_CONFIG=%s %s serve", acct.name, cfgPath, dest)
case o.noStart:
case healthy(cfg.Listen, cfg.TLS == "auto"):
say("\nHam Lab is up: %s", url)
@@ -294,9 +312,15 @@ func Uninstall(args []string) error {
_ = run("systemctl", "daemon-reload")
}
if b, err := os.ReadFile(openrcPath); err == nil {
dataDir = configDirFrom(string(b), "export HAMLAB_CONFIG=")
_ = run("rc-service", "hamlab", "stop")
_ = run("rc-update", "del", "hamlab", "default")
if strings.Contains(string(b), "USE_PROCD=1") {
dataDir = configDirFrom(string(b), "HAMLAB_CONFIG=")
_ = run(procdPath, "stop")
_ = run(procdPath, "disable")
} else {
dataDir = configDirFrom(string(b), "export HAMLAB_CONFIG=")
_ = run("rc-service", "hamlab", "stop")
_ = run("rc-update", "del", "hamlab", "default")
}
os.Remove(openrcPath)
}
os.Remove(sudoersPath)