Various improvements and bug fixes

- Watch more events for lease file changes
 - Improving network up detection by using bootstrap IPv6 along side
   IPv4 one.
 - Emitting log to notice user that ctrld is starting.
 - Using systemd wrapper to provide correct status.
 - Restoring DNS on stop on Windows.
This commit is contained in:
Cuong Manh Le
2023-08-11 16:13:59 +00:00
committed by Cuong Manh Le
parent 0c096d5f07
commit 4896563e3c
9 changed files with 63 additions and 44 deletions

View File

@@ -429,6 +429,7 @@ func initCLI() {
{s.Install, false},
{s.Start, true},
}
mainLog.Load().Notice().Msg("Starting service")
if doTasks(tasks) {
if err := p.router.Install(sc); err != nil {
mainLog.Load().Warn().Err(err).Msg("post installation failed, please check system/service log for details error")
@@ -450,12 +451,7 @@ func initCLI() {
uninstall(p, s)
os.Exit(1)
}
// On Linux, Darwin, Freebsd, ctrld set DNS on startup, because the DNS setting could be
// reset after rebooting. On windows, we only need to set once here. See prog.preRun in
// prog_*.go file for dedicated code on each platform. (1)
if runtime.GOOS == "windows" {
p.setDNS()
}
p.setDNS()
}
},
}
@@ -524,10 +520,7 @@ func initCLI() {
initLogging()
if doTasks([]task{{s.Stop, true}}) {
p.router.Cleanup()
// See comment (1) in startCmd.
if runtime.GOOS != "windows" {
p.resetDNS()
}
p.resetDNS()
mainLog.Load().Notice().Msg("Service stopped")
}
},

View File

@@ -7,6 +7,7 @@ import (
"net"
"net/url"
"os"
"runtime"
"strconv"
"sync"
"syscall"
@@ -63,6 +64,19 @@ func (p *prog) Start(s service.Service) error {
return nil
}
func (p *prog) preRun() {
if !service.Interactive() {
p.setDNS()
}
if runtime.GOOS == "darwin" {
p.onStopped = append(p.onStopped, func() {
if !service.Interactive() {
p.resetDNS()
}
})
}
}
func (p *prog) run() {
// Wait the caller to signal that we can do our logic.
<-p.waitCh

View File

@@ -4,17 +4,6 @@ import (
"github.com/kardianos/service"
)
func (p *prog) preRun() {
if !service.Interactive() {
p.setDNS()
}
p.onStopped = append(p.onStopped, func() {
if !service.Interactive() {
p.resetDNS()
}
})
}
func setDependencies(svc *service.Config) {}
func setWorkingDirectory(svc *service.Config, dir string) {

View File

@@ -6,12 +6,6 @@ import (
"github.com/kardianos/service"
)
func (p *prog) preRun() {
if !service.Interactive() {
p.setDNS()
}
}
func setDependencies(svc *service.Config) {
// TODO(cuonglm): remove once https://github.com/kardianos/service/issues/359 fixed.
_ = os.MkdirAll("/usr/local/etc/rc.d", 0755)

View File

@@ -13,12 +13,6 @@ func init() {
}
}
func (p *prog) preRun() {
if !service.Interactive() {
p.setDNS()
}
}
func setDependencies(svc *service.Config) {
svc.Dependencies = []string{
"Wants=network-online.target",

View File

@@ -4,8 +4,6 @@ package main
import "github.com/kardianos/service"
func (p *prog) preRun() {}
func setDependencies(svc *service.Config) {}
func setWorkingDirectory(svc *service.Config, dir string) {

View File

@@ -26,6 +26,8 @@ func newService(i service.Interface, c *service.Config) (service.Service, error)
return &sysV{s}, nil
case s.Platform() == "unix-systemv":
return &sysV{s}, nil
case s.Platform() == "linux-systemd":
return &systemd{s}, nil
}
return s, nil
}
@@ -105,6 +107,20 @@ func (s *procd) Status() (service.Status, error) {
return service.StatusRunning, nil
}
// procd wraps a service.Service, and provide status command to
// report the status correctly.
type systemd struct {
service.Service
}
func (s *systemd) Status() (service.Status, error) {
out, _ := exec.Command("systemctl", "status", "ctrld").CombinedOutput()
if bytes.Contains(out, []byte("/FAILURE)")) {
return service.StatusStopped, nil
}
return s.Service.Status()
}
type task struct {
f func() error
abortOnError bool

View File

@@ -53,7 +53,7 @@ func (d *dhcp) watchChanges() {
if !ok {
return
}
if event.Has(fsnotify.Write) {
if event.Has(fsnotify.Write) || event.Has(fsnotify.Rename) || event.Has(fsnotify.Chmod) || event.Has(fsnotify.Remove) {
format := clientInfoFiles[event.Name]
if err := d.readLeaseFile(event.Name, format); err != nil && !os.IsNotExist(err) {
ctrld.ProxyLogger.Load().Err(err).Str("file", event.Name).Msg("leases file changed but failed to update client info")

View File

@@ -4,8 +4,11 @@ import (
"context"
"errors"
"net"
"os"
"os/signal"
"sync"
"sync/atomic"
"syscall"
"time"
"tailscale.com/logtail/backoff"
@@ -13,17 +16,17 @@ import (
const (
controldIPv6Test = "ipv6.controld.io"
bootstrapDNS = "76.76.2.0:53"
v4BootstrapDNS = "76.76.2.0:53"
v6BootstrapDNS = "[2606:1a40::]:53"
)
var Dialer = &net.Dialer{
Resolver: &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{
Timeout: 10 * time.Second,
}
return d.DialContext(ctx, "udp", bootstrapDNS)
d := ParallelDialer{}
d.Timeout = 10 * time.Second
return d.DialContext(ctx, "udp", []string{v4BootstrapDNS, v6BootstrapDNS})
},
},
}
@@ -59,14 +62,32 @@ func supportListenIPv6Local() bool {
}
func probeStack() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
<-sigs
cancel()
}()
b := backoff.NewBackoff("probeStack", func(format string, args ...any) {}, 5*time.Second)
for {
if _, err := probeStackDialer.Dial("udp", bootstrapDNS); err == nil {
if _, err := probeStackDialer.DialContext(ctx, "udp", v4BootstrapDNS); err == nil {
hasNetworkUp = true
break
} else {
b.BackOff(context.Background(), err)
}
if _, err := probeStackDialer.DialContext(ctx, "udp", v6BootstrapDNS); err == nil {
hasNetworkUp = true
break
}
select {
case <-ctx.Done():
return
default:
}
b.BackOff(context.Background(), errors.New("network is down"))
}
canListenIPv6Local = supportListenIPv6Local()
}