mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-05-27 12:52:27 +02:00
5ce92abf1f
This commit reverts changes from v1.4.5 to v1.4.7, to prepare for v2.0.0 branch codes. Changes includes in these releases have been included in v2.0.0 branch already. Details: Revert "feat: add --rfc1918 flag for explicit LAN client support" This reverts commit0e3f764299. Revert "Upgrade quic-go to v0.54.0" This reverts commite52402eb0c. Revert "docs: add known issues documentation for Darwin 15.5 upgrade issue" This reverts commit2133f31854. Revert "start mobile library with provision id and custom hostname." This reverts commita198a5cd65. Revert "Add OPNsense new lease file" This reverts commit7af29cfbc0. Revert ".github/workflows: bump go version to 1.24.x" This reverts commitce1a165348. Revert "fix: ensure upstream health checks can handle large DNS responses" This reverts commitfd48e6d795. Revert "refactor(prog): move network monitoring outside listener loop" This reverts commitd71d1341b6. Revert "fix: correct Windows API constants to fix domain join detection" This reverts commit21855df4af. Revert "refactor: move network monitoring to separate goroutine" This reverts commit66e2d3a40a. Revert "refactor: extract empty string filtering to reusable function" This reverts commit36a7423634. Revert "cmd/cli: ignore empty positional argument for start command" This reverts commite616091249. Revert "Avoiding Windows runners file locking issue" This reverts commit0948161529. Revert "refactor: split selfUpgradeCheck into version check and upgrade execution" This reverts commitce29b5d217. Revert "internal/router: support Ubios 4.3+" This reverts commitde24fa293e. Revert "internal/router: support Merlin Guest Network Pro VLAN" This reverts commit6663925c4d.
342 lines
8.5 KiB
Go
342 lines
8.5 KiB
Go
package router
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
"text/template"
|
|
"time"
|
|
|
|
"github.com/kardianos/service"
|
|
)
|
|
|
|
// This is a copy of https://github.com/kardianos/service/blob/v1.2.1/service_sysv_linux.go,
|
|
// with modification for supporting ubios v1 init system.
|
|
|
|
// Keep in sync with ubios.ubiosDNSMasqConfigPath
|
|
const ubiosDNSMasqConfigPath = "/run/dnsmasq.conf.d/zzzctrld.conf"
|
|
|
|
type ubiosSvc struct {
|
|
i service.Interface
|
|
platform string
|
|
*service.Config
|
|
}
|
|
|
|
func newUbiosService(i service.Interface, platform string, c *service.Config) (service.Service, error) {
|
|
s := &ubiosSvc{
|
|
i: i,
|
|
platform: platform,
|
|
Config: c,
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
func (s *ubiosSvc) String() string {
|
|
if len(s.DisplayName) > 0 {
|
|
return s.DisplayName
|
|
}
|
|
return s.Name
|
|
}
|
|
|
|
func (s *ubiosSvc) Platform() string {
|
|
return s.platform
|
|
}
|
|
|
|
func (s *ubiosSvc) configPath() string {
|
|
return "/etc/init.d/" + s.Config.Name
|
|
}
|
|
|
|
func (s *ubiosSvc) execPath() (string, error) {
|
|
if len(s.Executable) != 0 {
|
|
return filepath.Abs(s.Executable)
|
|
}
|
|
return os.Executable()
|
|
}
|
|
|
|
func (s *ubiosSvc) template() *template.Template {
|
|
return template.Must(template.New("").Funcs(tf).Parse(ubiosSvcScript))
|
|
}
|
|
|
|
func (s *ubiosSvc) Install() error {
|
|
confPath := s.configPath()
|
|
if _, err := os.Stat(confPath); err == nil {
|
|
return fmt.Errorf("init already exists: %s", confPath)
|
|
}
|
|
|
|
f, err := os.Create(confPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create config path: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
path, err := s.execPath()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get exec path: %w", err)
|
|
}
|
|
|
|
var to = &struct {
|
|
*service.Config
|
|
Path string
|
|
DnsMasqConfPath string
|
|
}{
|
|
s.Config,
|
|
path,
|
|
ubiosDNSMasqConfigPath,
|
|
}
|
|
|
|
if err := s.template().Execute(f, to); err != nil {
|
|
return fmt.Errorf("failed to create init script: %w", err)
|
|
}
|
|
|
|
if err := f.Close(); err != nil {
|
|
return fmt.Errorf("failed to save init script: %w", err)
|
|
}
|
|
|
|
if err = os.Chmod(confPath, 0755); err != nil {
|
|
return fmt.Errorf("failed to set init script executable: %w", err)
|
|
}
|
|
|
|
// Enable on boot
|
|
script, err := os.CreateTemp("", "ctrld_boot.service")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create boot service tmp file: %w", err)
|
|
}
|
|
defer script.Close()
|
|
|
|
svcConfig := *to.Config
|
|
svcConfig.Arguments = os.Args[1:]
|
|
to.Config = &svcConfig
|
|
if err := template.Must(template.New("").Funcs(tf).Parse(ubiosBootSystemdService)).Execute(script, &to); err != nil {
|
|
return fmt.Errorf("failed to create boot service file: %w", err)
|
|
}
|
|
if err := script.Close(); err != nil {
|
|
return fmt.Errorf("failed to save boot service file: %w", err)
|
|
}
|
|
|
|
// Copy the boot script to container and start.
|
|
cmd := exec.Command("podman", "cp", "--pause=false", script.Name(), "unifi-os:/lib/systemd/system/ctrld-boot.service")
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("failed to copy boot script, out: %s, err: %v", string(out), err)
|
|
}
|
|
cmd = exec.Command("podman", "exec", "unifi-os", "systemctl", "enable", "--now", "ctrld-boot.service")
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("failed to start ctrld boot script, out: %s, err: %v", string(out), err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *ubiosSvc) Uninstall() error {
|
|
if err := os.Remove(s.configPath()); err != nil {
|
|
return err
|
|
}
|
|
// Remove ctrld-boot service inside unifi-os container.
|
|
cmd := exec.Command("podman", "exec", "unifi-os", "systemctl", "disable", "ctrld-boot.service")
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("failed to disable ctrld-boot service, out: %s, err: %v", string(out), err)
|
|
}
|
|
cmd = exec.Command("podman", "exec", "unifi-os", "rm", "/lib/systemd/system/ctrld-boot.service")
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("failed to remove ctrld-boot service file, out: %s, err: %v", string(out), err)
|
|
}
|
|
cmd = exec.Command("podman", "exec", "unifi-os", "systemctl", "daemon-reload")
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("failed to reload systemd service, out: %s, err: %v", string(out), err)
|
|
}
|
|
cmd = exec.Command("podman", "exec", "unifi-os", "systemctl", "reset-failed")
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("failed to reset-failed systemd service, out: %s, err: %v", string(out), err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *ubiosSvc) Logger(errs chan<- error) (service.Logger, error) {
|
|
if service.Interactive() {
|
|
return service.ConsoleLogger, nil
|
|
}
|
|
return s.SystemLogger(errs)
|
|
}
|
|
|
|
func (s *ubiosSvc) SystemLogger(errs chan<- error) (service.Logger, error) {
|
|
return newSysLogger(s.Name, errs)
|
|
}
|
|
|
|
func (s *ubiosSvc) Run() (err error) {
|
|
err = s.i.Start(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if interactice, _ := isInteractive(); !interactice {
|
|
signal.Ignore(syscall.SIGHUP)
|
|
}
|
|
|
|
var sigChan = make(chan os.Signal, 3)
|
|
signal.Notify(sigChan, syscall.SIGTERM, os.Interrupt)
|
|
<-sigChan
|
|
|
|
return s.i.Stop(s)
|
|
}
|
|
|
|
func (s *ubiosSvc) Status() (service.Status, error) {
|
|
if _, err := os.Stat(s.configPath()); os.IsNotExist(err) {
|
|
return service.StatusUnknown, service.ErrNotInstalled
|
|
}
|
|
out, err := exec.Command(s.configPath(), "status").CombinedOutput()
|
|
if err != nil {
|
|
return service.StatusUnknown, err
|
|
}
|
|
switch string(bytes.TrimSpace(out)) {
|
|
case "Running":
|
|
return service.StatusRunning, nil
|
|
default:
|
|
return service.StatusStopped, nil
|
|
}
|
|
}
|
|
|
|
func (s *ubiosSvc) Start() error {
|
|
return exec.Command(s.configPath(), "start").Run()
|
|
}
|
|
|
|
func (s *ubiosSvc) Stop() error {
|
|
return exec.Command(s.configPath(), "stop").Run()
|
|
}
|
|
|
|
func (s *ubiosSvc) Restart() error {
|
|
err := s.Stop()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
time.Sleep(50 * time.Millisecond)
|
|
return s.Start()
|
|
}
|
|
|
|
const ubiosBootSystemdService = `[Unit]
|
|
Description=Run ctrld On Startup UDM
|
|
Wants=network-online.target
|
|
After=network-online.target
|
|
Wants=unifi-mongodb
|
|
After=unifi-mongodb
|
|
StartLimitIntervalSec=500
|
|
StartLimitBurst=5
|
|
|
|
[Service]
|
|
Restart=on-failure
|
|
RestartSec=5s
|
|
ExecStart=/sbin/ssh-proxy '[ -f "{{.DnsMasqConfPath}}" ] || {{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}'
|
|
RemainAfterExit=true
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
`
|
|
|
|
const ubiosSvcScript = `#!/bin/sh
|
|
# For RedHat and cousins:
|
|
# chkconfig: - 99 01
|
|
# description: {{.Description}}
|
|
# processname: {{.Path}}
|
|
|
|
### BEGIN INIT INFO
|
|
# Provides: {{.Path}}
|
|
# Required-Start:
|
|
# Required-Stop:
|
|
# Default-Start: 2 3 4 5
|
|
# Default-Stop: 0 1 6
|
|
# Short-Description: {{.DisplayName}}
|
|
# Description: {{.Description}}
|
|
### END INIT INFO
|
|
|
|
cmd="{{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}"
|
|
|
|
name=$(basename $(readlink -f $0))
|
|
pid_file="/var/run/$name.pid"
|
|
stdout_log="/var/log/$name.log"
|
|
stderr_log="/var/log/$name.err"
|
|
|
|
[ -e /etc/sysconfig/$name ] && . /etc/sysconfig/$name
|
|
|
|
get_pid() {
|
|
cat "$pid_file"
|
|
}
|
|
|
|
is_running() {
|
|
[ -f "$pid_file" ] && cat /proc/$(get_pid)/stat > /dev/null 2>&1
|
|
}
|
|
|
|
case "$1" in
|
|
start)
|
|
if is_running; then
|
|
echo "Already started"
|
|
else
|
|
echo "Starting $name"
|
|
{{if .WorkingDirectory}}cd '{{.WorkingDirectory}}'{{end}}
|
|
$cmd >> "$stdout_log" 2>> "$stderr_log" &
|
|
echo $! > "$pid_file"
|
|
if ! is_running; then
|
|
echo "Unable to start, see $stdout_log and $stderr_log"
|
|
exit 1
|
|
fi
|
|
fi
|
|
;;
|
|
stop)
|
|
if is_running; then
|
|
echo -n "Stopping $name.."
|
|
kill $(get_pid)
|
|
for i in $(seq 1 10)
|
|
do
|
|
if ! is_running; then
|
|
break
|
|
fi
|
|
echo -n "."
|
|
sleep 1
|
|
done
|
|
echo
|
|
if is_running; then
|
|
echo "Not stopped; may still be shutting down or shutdown may have failed"
|
|
exit 1
|
|
else
|
|
echo "Stopped"
|
|
if [ -f "$pid_file" ]; then
|
|
rm "$pid_file"
|
|
fi
|
|
fi
|
|
else
|
|
echo "Not running"
|
|
fi
|
|
;;
|
|
restart)
|
|
$0 stop
|
|
if is_running; then
|
|
echo "Unable to stop, will not attempt to start"
|
|
exit 1
|
|
fi
|
|
$0 start
|
|
;;
|
|
status)
|
|
if is_running; then
|
|
echo "Running"
|
|
else
|
|
echo "Stopped"
|
|
exit 1
|
|
fi
|
|
;;
|
|
*)
|
|
echo "Usage: $0 {start|stop|restart|status}"
|
|
exit 1
|
|
;;
|
|
esac
|
|
exit 0
|
|
`
|
|
|
|
var tf = map[string]interface{}{
|
|
"cmd": func(s string) string {
|
|
return `"` + strings.Replace(s, `"`, `\"`, -1) + `"`
|
|
},
|
|
"cmdEscape": func(s string) string {
|
|
return strings.Replace(s, " ", `\x20`, -1)
|
|
},
|
|
}
|