diff --git a/cmd/ctrld/cli_router_linux.go b/cmd/ctrld/cli_router_linux.go index 5791a7e..b6a4ffc 100644 --- a/cmd/ctrld/cli_router_linux.go +++ b/cmd/ctrld/cli_router_linux.go @@ -42,12 +42,11 @@ func initRouterCLI() { if platform == "auto" { platform = router.Name() } - switch platform { - case router.DDWrt, router.Merlin, router.OpenWrt, router.Ubios, router.Synology: - default: + if !router.IsSupported(platform) { unsupportedPlatformHelp(cmd) os.Exit(1) } + exe, err := os.Executable() if err != nil { mainLog.Fatal().Msgf("could not find executable path: %v", err) diff --git a/cmd/ctrld/prog.go b/cmd/ctrld/prog.go index f19aeda..1c59530 100644 --- a/cmd/ctrld/prog.go +++ b/cmd/ctrld/prog.go @@ -175,7 +175,7 @@ func (p *prog) setDNS() { switch router.Name() { case router.DDWrt, router.OpenWrt, router.Ubios: // On router, ctrld run as a DNS forwarder, it does not have to change system DNS. - // Except for Merlin, which has WAN DNS setup on boot for NTP. + // Except for Merlin/Tomato, which has WAN DNS setup on boot for NTP. return } if cfg.Listener == nil || cfg.Listener["0"] == nil { diff --git a/internal/router/client_info.go b/internal/router/client_info.go index 3a809b6..04af7ac 100644 --- a/internal/router/client_info.go +++ b/internal/router/client_info.go @@ -22,6 +22,7 @@ var clientInfoFiles = []string{ "/mnt/data/udapi-config/dnsmasq.lease", // UDM Pro "/data/udapi-config/dnsmasq.lease", // UDR "/etc/dhcpd/dhcpd-leases.log", // Synology + "/tmp/var/lib/misc/dnsmasq.leases", // Tomato } func (r *router) watchClientInfoTable() { diff --git a/internal/router/ddwrt.go b/internal/router/ddwrt.go index b0be098..92318b1 100644 --- a/internal/router/ddwrt.go +++ b/internal/router/ddwrt.go @@ -7,9 +7,10 @@ import ( ) const ( - nvramCtrldKeyPrefix = "ctrld_" - nvramCtrldSetupKey = "ctrld_setup" - nvramRCStartupKey = "rc_startup" + nvramCtrldKeyPrefix = "ctrld_" + nvramCtrldSetupKey = "ctrld_setup" + nvramCtrldInstallKey = "ctrld_install" + nvramRCStartupKey = "rc_startup" ) //lint:ignore ST1005 This error is for human. @@ -29,14 +30,14 @@ func setupDDWrt() error { return err } - nvramKvMap := nvramKV() + nvramKvMap := nvramSetupKV() nvramKvMap["dnsmasq_options"] = data - if err := nvramSetup(nvramKvMap); err != nil { + if err := nvramSetKV(nvramKvMap, nvramCtrldSetupKey); err != nil { return err } // Restart dnsmasq service. - if err := ddwrtRestartDNSMasq(); err != nil { + if err := restartDNSMasq(); err != nil { return err } return nil @@ -44,11 +45,11 @@ func setupDDWrt() error { func cleanupDDWrt() error { // Restore old configs. - if err := nvramRestore(nvramKV()); err != nil { + if err := nvramRestore(nvramSetupKV(), nvramCtrldSetupKey); err != nil { return err } // Restart dnsmasq service. - if err := ddwrtRestartDNSMasq(); err != nil { + if err := restartDNSMasq(); err != nil { return err } return nil diff --git a/internal/router/dnsmasq.go b/internal/router/dnsmasq.go index 77f47d7..17c4879 100644 --- a/internal/router/dnsmasq.go +++ b/internal/router/dnsmasq.go @@ -49,7 +49,7 @@ func dnsMasqConf() (string, error) { var sb strings.Builder var tmplText string switch Name() { - case DDWrt, OpenWrt, Ubios, Synology: + case DDWrt, OpenWrt, Ubios, Synology, Tomato: tmplText = dnsMasqConfigContentTmpl case Merlin: tmplText = merlinDNSMasqPostConfTmpl @@ -65,3 +65,21 @@ func dnsMasqConf() (string, error) { } return sb.String(), nil } + +func restartDNSMasq() error { + switch Name() { + case DDWrt: + return ddwrtRestartDNSMasq() + case Merlin: + return merlinRestartDNSMasq() + case OpenWrt: + return openwrtRestartDNSMasq() + case Ubios: + return ubiosRestartDNSMasq() + case Synology: + return synologyRestartDNSMasq() + case Tomato: + return tomatoRestartService(tomatoDNSMasqSvcName) + } + panic("not supported platform") +} diff --git a/internal/router/merlin.go b/internal/router/merlin.go index ca739cb..aab05e7 100644 --- a/internal/router/merlin.go +++ b/internal/router/merlin.go @@ -2,11 +2,16 @@ package router import ( "bytes" + "context" + "errors" "fmt" "os" "os/exec" "strings" + "time" "unicode" + + "tailscale.com/logtail/backoff" ) func setupMerlin() error { @@ -35,11 +40,11 @@ func setupMerlin() error { return err } // Restart dnsmasq service. - if err := merlinRestartDNSMasq(); err != nil { + if err := restartDNSMasq(); err != nil { return err } - if err := nvramSetup(nvramKV()); err != nil { + if err := nvramSetKV(nvramSetupKV(), nvramCtrldSetupKey); err != nil { return err } @@ -48,7 +53,7 @@ func setupMerlin() error { func cleanupMerlin() error { // Restore old configs. - if err := nvramRestore(nvramKV()); err != nil { + if err := nvramRestore(nvramSetupKV(), nvramCtrldSetupKey); err != nil { return err } buf, err := os.ReadFile(merlinDNSMasqPostConfPath) @@ -60,7 +65,7 @@ func cleanupMerlin() error { return err } // Restart dnsmasq service. - if err := merlinRestartDNSMasq(); err != nil { + if err := restartDNSMasq(); err != nil { return err } return nil @@ -87,3 +92,43 @@ func merlinParsePostConf(buf []byte) []byte { } return buf } + +func merlinPreStart() (err error) { + pidFile := "/tmp/ctrld.pid" + + // Remove pid file and trigger dnsmasq restart, so NTP can resolve + // server name and perform time synchronization. + pid, err := os.ReadFile(pidFile) + if err != nil { + return fmt.Errorf("PreStart: os.Readfile: %w", err) + } + if err := os.Remove(pidFile); err != nil { + return fmt.Errorf("PreStart: os.Remove: %w", err) + } + defer func() { + if werr := os.WriteFile(pidFile, pid, 0600); werr != nil { + err = errors.Join(err, werr) + return + } + if rerr := restartDNSMasq(); rerr != nil { + err = errors.Join(err, rerr) + return + } + }() + if err := restartDNSMasq(); err != nil { + return fmt.Errorf("PreStart: restartDNSMasqFn: %w", err) + } + + // Wait until `ntp_ready=1` set. + b := backoff.NewBackoff("PreStart", func(format string, args ...any) {}, 10*time.Second) + for { + out, err := nvram("get", "ntp_ready") + if err != nil { + return fmt.Errorf("PreStart: nvram: %w", err) + } + if out == "1" { + return nil + } + b.BackOff(context.Background(), errors.New("ntp not ready")) + } +} diff --git a/internal/router/nvram.go b/internal/router/nvram.go index b66fcdb..de3400e 100644 --- a/internal/router/nvram.go +++ b/internal/router/nvram.go @@ -26,7 +26,7 @@ NOTE: +https://community.ui.com/questions/Implement-DNSSEC-into-UniFi/951c72b0-4d88-4c86-9174-45417bd2f9ca +https://community.ui.com/questions/Enable-DNSSEC-for-Unifi-Dream-Machine-FW-updates/e68e367c-d09b-4459-9444-18908f7c1ea1 */ -func nvramKV() map[string]string { +func nvramSetupKV() map[string]string { switch Name() { case DDWrt: return map[string]string{ @@ -39,11 +39,28 @@ func nvramKV() map[string]string { return map[string]string{ "dnspriv_enable": "0", // Ensure Merlin native DoT disabled. } + case Tomato: + return map[string]string{ + "dnsmasq_custom": "", // Configuration of dnsmasq set by ctrld, filled by setupTomato. + "dnscrypt_proxy": "0", // Disable DNSCrypt. + "dnssec_enable": "0", // Disable DNSSEC. + "stubby_proxy": "0", // Disable Stubby + } } return nil } -func nvramSetup(m map[string]string) error { +func nvramInstallKV() map[string]string { + switch Name() { + case Tomato: + return map[string]string{ + tomatoNvramScriptWanupKey: "", // script to start ctrld, filled by tomatoSvc.Install method. + } + } + return nil +} + +func nvramSetKV(m map[string]string, setupKey string) error { // Backup current value, store ctrld's configs. for key, value := range m { old, err := nvram("get", key) @@ -58,7 +75,7 @@ func nvramSetup(m map[string]string) error { } } - if out, err := nvram("set", nvramCtrldSetupKey+"=1"); err != nil { + if out, err := nvram("set", setupKey+"=1"); err != nil { return fmt.Errorf("%s: %w", out, err) } // Commit. @@ -68,7 +85,7 @@ func nvramSetup(m map[string]string) error { return nil } -func nvramRestore(m map[string]string) error { +func nvramRestore(m map[string]string, setupKey string) error { // Restore old configs. for key := range m { ctrldKey := nvramCtrldKeyPrefix + key @@ -82,7 +99,7 @@ func nvramRestore(m map[string]string) error { } } - if out, err := nvram("unset", "ctrld_setup"); err != nil { + if out, err := nvram("unset", setupKey); err != nil { return fmt.Errorf("%s: %w", out, err) } // Commit. diff --git a/internal/router/openwrt.go b/internal/router/openwrt.go index 701913b..0fbe5f2 100644 --- a/internal/router/openwrt.go +++ b/internal/router/openwrt.go @@ -40,7 +40,7 @@ func setupOpenWrt() error { return err } // Restart dnsmasq service. - if err := openwrtRestartDNSMasq(); err != nil { + if err := restartDNSMasq(); err != nil { return err } return nil @@ -52,7 +52,7 @@ func cleanupOpenWrt() error { return err } // Restart dnsmasq service. - if err := openwrtRestartDNSMasq(); err != nil { + if err := restartDNSMasq(); err != nil { return err } return nil diff --git a/internal/router/router.go b/internal/router/router.go index 6b33a4c..aea6202 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -2,18 +2,14 @@ package router import ( "bytes" - "context" "errors" - "fmt" "os" "os/exec" "sync" "sync/atomic" - "time" "github.com/fsnotify/fsnotify" "github.com/kardianos/service" - "tailscale.com/logtail/backoff" "github.com/Control-D-Inc/ctrld" ) @@ -24,6 +20,7 @@ const ( Merlin = "merlin" Ubios = "ubios" Synology = "synology" + Tomato = "tomato" ) // ErrNotSupported reports the current router is not supported error. @@ -38,9 +35,18 @@ type router struct { watcher *fsnotify.Watcher } +// IsSupported reports whether the given platform is supported by ctrld. +func IsSupported(platform string) bool { + switch platform { + case DDWrt, Merlin, OpenWrt, Ubios, Synology, Tomato: + return true + } + return false +} + // SupportedPlatforms return all platforms that can be configured to run with ctrld. func SupportedPlatforms() []string { - return []string{DDWrt, Merlin, OpenWrt, Ubios, Synology} + return []string{DDWrt, Merlin, OpenWrt, Ubios, Synology, Tomato} } var configureFunc = map[string]func() error{ @@ -49,13 +55,14 @@ var configureFunc = map[string]func() error{ OpenWrt: setupOpenWrt, Ubios: setupUbiOS, Synology: setupSynology, + Tomato: setupTomato, } // Configure configures things for running ctrld on the router. func Configure(c *ctrld.Config) error { name := Name() switch name { - case DDWrt, Merlin, OpenWrt, Ubios, Synology: + case DDWrt, Merlin, OpenWrt, Ubios, Synology, Tomato: if c.HasUpstreamSendClientInfo() { r := routerPlatform.Load() r.sendClientInfo = true @@ -90,55 +97,22 @@ func ConfigureService(sc *service.Config) error { } case OpenWrt: sc.Option["SysvScript"] = openWrtScript - case Merlin, Ubios, Synology: + case Merlin, Ubios, Synology, Tomato: } return nil } // PreStart blocks until the router is ready for running ctrld. func PreStart() (err error) { - if Name() != Merlin { + // On some routers, NTP may out of sync, so waiting for it to be ready. + switch Name() { + case Merlin: + return merlinPreStart() + case Tomato: + return tomatoPreStart() + default: return nil } - - pidFile := "/tmp/ctrld.pid" - // On Merlin, NTP may out of sync, so waiting for it to be ready. - // - // Remove pid file and trigger dnsmasq restart, so NTP can resolve - // server name and perform time synchronization. - pid, err := os.ReadFile(pidFile) - if err != nil { - return fmt.Errorf("PreStart: os.Readfile: %w", err) - } - if err := os.Remove(pidFile); err != nil { - return fmt.Errorf("PreStart: os.Remove: %w", err) - } - defer func() { - if werr := os.WriteFile(pidFile, pid, 0600); werr != nil { - err = errors.Join(err, werr) - return - } - if rerr := merlinRestartDNSMasq(); rerr != nil { - err = errors.Join(err, rerr) - return - } - }() - if err := merlinRestartDNSMasq(); err != nil { - return fmt.Errorf("PreStart: merlinRestartDNSMasq: %w", err) - } - - // Wait until `ntp_read=1` set. - b := backoff.NewBackoff("PreStart", func(format string, args ...any) {}, 10*time.Second) - for { - out, err := nvram("get", "ntp_ready") - if err != nil { - return fmt.Errorf("PreStart: nvram: %w", err) - } - if out == "1" { - return nil - } - b.BackOff(context.Background(), errors.New("ntp not ready")) - } } // PostInstall performs task after installing ctrld on router. @@ -155,6 +129,8 @@ func PostInstall() error { return postInstallUbiOS() case Synology: return postInstallSynology() + case Tomato: + return postInstallTomato() } return nil } @@ -173,6 +149,8 @@ func Cleanup() error { return cleanupUbiOS() case Synology: return cleanupSynology() + case Tomato: + return cleanupTomato() } return nil } @@ -181,7 +159,7 @@ func Cleanup() error { func ListenAddress() string { name := Name() switch name { - case DDWrt, Merlin, OpenWrt, Ubios, Synology: + case DDWrt, Merlin, OpenWrt, Ubios, Synology, Tomato: return "127.0.0.1:5354" } return "" @@ -210,6 +188,8 @@ func distroName() string { return Ubios case bytes.HasPrefix(unameU(), []byte("synology")): return Synology + case bytes.HasPrefix(unameO(), []byte("Tomato")): + return Tomato } return "" } diff --git a/internal/router/service.go b/internal/router/service.go index e404a2d..d9476e9 100644 --- a/internal/router/service.go +++ b/internal/router/service.go @@ -48,6 +48,15 @@ func init() { }, new: newUbiosService, }, + &linuxSystemService{ + name: "tomato", + detect: func() bool { return Name() == Tomato }, + interactive: func() bool { + is, _ := isInteractive() + return is + }, + new: newTomatoService, + }, } systems = append(systems, service.AvailableSystems()...) service.ChooseSystem(systems...) diff --git a/internal/router/service_tomato.go b/internal/router/service_tomato.go new file mode 100644 index 0000000..8bfcf73 --- /dev/null +++ b/internal/router/service_tomato.go @@ -0,0 +1,278 @@ +package router + +import ( + "bytes" + "errors" + "fmt" + "os" + "os/exec" + "os/signal" + "strings" + "syscall" + "text/template" + + "github.com/kardianos/service" +) + +const tomatoNvramScriptWanupKey = "script_wanup" + +type tomatoSvc struct { + i service.Interface + platform string + *service.Config +} + +func newTomatoService(i service.Interface, platform string, c *service.Config) (service.Service, error) { + s := &tomatoSvc{ + i: i, + platform: platform, + Config: c, + } + return s, nil +} + +func (s *tomatoSvc) String() string { + if len(s.DisplayName) > 0 { + return s.DisplayName + } + return s.Name +} + +func (s *tomatoSvc) Platform() string { + return s.platform +} + +func (s *tomatoSvc) configPath() string { + path, err := os.Executable() + if err != nil { + return "" + } + return path + ".startup" +} + +func (s *tomatoSvc) template() *template.Template { + return template.Must(template.New("").Parse(tomatoSvcScript)) +} + +func (s *tomatoSvc) Install() error { + exePath, err := os.Executable() + if err != nil { + return err + } + + if !strings.HasPrefix(exePath, "/jffs/") { + return errors.New("could not install service outside /jffs") + } + if _, err := nvram("set", "jffs2_on=1"); err != nil { + return err + } + if _, err := nvram("commit"); err != nil { + return err + } + + confPath := s.configPath() + if _, err := os.Stat(confPath); err == nil { + return fmt.Errorf("already installed: %s", confPath) + } + + var to = &struct { + *service.Config + Path string + }{ + s.Config, + exePath, + } + + f, err := os.Create(confPath) + if err != nil { + return fmt.Errorf("os.Create: %w", err) + } + defer f.Close() + + if err := s.template().Execute(f, to); err != nil { + return fmt.Errorf("s.template.Execute: %w", err) + } + + if err = os.Chmod(confPath, 0755); err != nil { + return fmt.Errorf("os.Chmod: startup script: %w", err) + } + + nvramKvMap := nvramInstallKV() + old, err := nvram("get", tomatoNvramScriptWanupKey) + if err != nil { + return fmt.Errorf("nvram: %w", err) + } + nvramKvMap[tomatoNvramScriptWanupKey] = strings.Join([]string{old, s.configPath() + " start"}, "\n") + if err := nvramSetKV(nvramKvMap, nvramCtrldInstallKey); err != nil { + return err + } + return nil +} + +func (s *tomatoSvc) Uninstall() error { + if err := os.Remove(s.configPath()); err != nil { + return fmt.Errorf("os.Remove: %w", err) + } + // Restore old configs. + if err := nvramRestore(nvramInstallKV(), nvramCtrldInstallKey); err != nil { + return err + } + return nil +} + +func (s *tomatoSvc) Logger(errs chan<- error) (service.Logger, error) { + if service.Interactive() { + return service.ConsoleLogger, nil + } + return s.SystemLogger(errs) +} + +func (s *tomatoSvc) SystemLogger(errs chan<- error) (service.Logger, error) { + return newSysLogger(s.Name, errs) +} + +func (s *tomatoSvc) 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, 1) + signal.Notify(sigChan, syscall.SIGTERM, os.Interrupt) + <-sigChan + + return s.i.Stop(s) +} + +func (s *tomatoSvc) 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 *tomatoSvc) Start() error { + return exec.Command(s.configPath(), "start").Run() +} + +func (s *tomatoSvc) Stop() error { + return exec.Command(s.configPath(), "stop").Run() +} + +func (s *tomatoSvc) Restart() error { + return exec.Command(s.configPath(), "restart").Run() +} + +// https://wiki.freshtomato.org/doku.php/freshtomato_zerotier?s[]=%2Aservice%2A +const tomatoSvcScript = `#!/bin/sh + + +NAME="{{.Name}}" +CMD="{{.Path}}{{range .Arguments}} {{.}}{{end}}" +LOG_FILE="/var/log/${NAME}.log" +PID_FILE="/tmp/$NAME.pid" + + +alias elog="logger -t $NAME -s" + + +COND=$1 +[ $# -eq 0 ] && COND="start" + +get_pid() { + cat "$PID_FILE" +} + +is_running() { + [ -f "$PID_FILE" ] && ps | grep -q "^ *$(get_pid) " +} + +start() { + if is_running; then + elog "$NAME is already running." + exit 1 + fi + elog "Starting $NAME Services: " + $CMD & + echo $! > "$PID_FILE" + chmod 600 "$PID_FILE" + if is_running; then + elog "succeeded." + else + elog "failed." + fi +} + + +stop() { + if ! is_running; then + elog "$NAME is not running." + exit 1 + fi + elog "Shutting down $NAME Services: " + kill -SIGTERM "$(get_pid)" + for _ in 1 2 3 4 5; do + if ! is_running; then + if [ -f "$pid_file" ]; then + rm "$pid_file" + fi + exit 0 + fi + printf "." + sleep 2 + done + if ! is_running; then + elog "succeeded." + else + elog "failed." + fi +} + + +do_restart() { + stop + start +} + + +do_status() { + if ! is_running; then + echo "stopped" + else + echo "running" + fi +} + + +case "$COND" in +start) + start + ;; +stop) + stop + ;; +restart) + do_restart + ;; +status) + do_status + ;; +*) + elog "Usage: $0 (start|stop|restart|status)" + ;; +esac +exit 0 +` diff --git a/internal/router/synology.go b/internal/router/synology.go index b221be8..8c1d1d6 100644 --- a/internal/router/synology.go +++ b/internal/router/synology.go @@ -22,7 +22,7 @@ func setupSynology() error { if err := os.WriteFile(synologyDhcpdInfoPath, []byte(`enable="yes"`), 0600); err != nil { return err } - if err := synologyRestartDNSMasq(); err != nil { + if err := restartDNSMasq(); err != nil { return err } return nil @@ -37,7 +37,7 @@ func cleanupSynology() error { } // Restart dnsmasq service. - if err := synologyRestartDNSMasq(); err != nil { + if err := restartDNSMasq(); err != nil { return err } return nil diff --git a/internal/router/tomato.go b/internal/router/tomato.go new file mode 100644 index 0000000..50f13ba --- /dev/null +++ b/internal/router/tomato.go @@ -0,0 +1,108 @@ +package router + +import ( + "context" + "errors" + "fmt" + "os/exec" + "time" + + "tailscale.com/logtail/backoff" +) + +const ( + tomatoDnsCryptProxySvcName = "dnscrypt-proxy" + tomatoStubbySvcName = "stubby" + tomatoDNSMasqSvcName = "dnsmasq" +) + +func setupTomato() error { + // Already setup. + if val, _ := nvram("get", nvramCtrldSetupKey); val == "1" { + return nil + } + + data, err := dnsMasqConf() + if err != nil { + return err + } + + nvramKvMap := nvramSetupKV() + nvramKvMap["dnsmasq_custom"] = data + if err := nvramSetKV(nvramKvMap, nvramCtrldSetupKey); err != nil { + return err + } + + // Restart dnscrypt-proxy service. + if err := tomatoRestartServiceWithKill(tomatoDnsCryptProxySvcName, true); err != nil { + return err + } + // Restart stubby service. + if err := tomatoRestartService(tomatoStubbySvcName); err != nil { + return err + } + // Restart dnsmasq service. + if err := restartDNSMasq(); err != nil { + return err + } + return nil +} + +func postInstallTomato() error { + return nil +} + +func cleanupTomato() error { + // Restore old configs. + if err := nvramRestore(nvramSetupKV(), nvramCtrldSetupKey); err != nil { + return err + } + // Restart dnscrypt-proxy service. + if err := tomatoRestartServiceWithKill(tomatoDnsCryptProxySvcName, true); err != nil { + return err + } + // Restart stubby service. + if err := tomatoRestartService(tomatoStubbySvcName); err != nil { + return err + } + // Restart dnsmasq service. + if err := restartDNSMasq(); err != nil { + return err + } + return nil +} + +func tomatoPreStart() (err error) { + // cleanup to trigger dnsmasq restart, so NTP can resolve + // server name and perform time synchronization. + if err = cleanupTomato(); err != nil { + return err + } + + // Wait until `ntp_ready=1` set. + b := backoff.NewBackoff("PreStart", func(format string, args ...any) {}, 10*time.Second) + for { + out, err := nvram("get", "ntp_ready") + if err != nil { + return fmt.Errorf("PreStart: nvram: %w", err) + } + if out == "1" { + return nil + } + b.BackOff(context.Background(), errors.New("ntp not ready")) + } +} + +func tomatoRestartService(name string) error { + return tomatoRestartServiceWithKill(name, false) +} + +func tomatoRestartServiceWithKill(name string, killBeforeRestart bool) error { + if killBeforeRestart { + _, _ = exec.Command("killall", name).CombinedOutput() + } + if out, err := exec.Command("service", name, "restart").CombinedOutput(); err != nil { + return fmt.Errorf("service restart %s: %s, %w", name, string(out), err) + } + return nil +} diff --git a/internal/router/ubios.go b/internal/router/ubios.go index 80fe04b..8be261f 100644 --- a/internal/router/ubios.go +++ b/internal/router/ubios.go @@ -20,7 +20,7 @@ func setupUbiOS() error { return err } // Restart dnsmasq service. - if err := ubiosRestartDNSMasq(); err != nil { + if err := restartDNSMasq(); err != nil { return err } return nil @@ -32,7 +32,7 @@ func cleanupUbiOS() error { return err } // Restart dnsmasq service. - if err := ubiosRestartDNSMasq(); err != nil { + if err := restartDNSMasq(); err != nil { return err } return nil