diff --git a/main.go b/main.go index cb78863..e8f21fd 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "net/http" "os" "strings" + "sync" "time" "github.com/digineo/go-ping" @@ -172,29 +173,38 @@ func startMonitor(cfg *config.Config) (*mon.Monitor, error) { func upsertTargets(globalTargets *targets, resolver *net.Resolver, cfg *config.Config, monitor *mon.Monitor) error { oldTargets := globalTargets.Targets() newTargets := make([]*target, len(cfg.Targets)) + var wg sync.WaitGroup for i, t := range cfg.Targets { - t := &target{ - host: t.Addr, - addresses: make([]net.IPAddr, 0), - delay: time.Duration(10*i) * time.Millisecond, - resolver: resolver, - } - newTargets[i] = t - - err := t.addOrUpdateMonitor(monitor, targetOpts{ - disableIPv4: cfg.Options.DisableIPv4, - disableIPv6: cfg.Options.DisableIPv6, - }) - if err != nil { - return fmt.Errorf("failed to setup target: %w", err) + newTarget := globalTargets.Get(t.Addr) + if newTarget == nil { + newTarget = &target{ + host: t.Addr, + addresses: make([]net.IPAddr, 0), + delay: time.Duration(10*i) * time.Millisecond, + resolver: resolver, + } } - } + newTargets[i] = newTarget + + wg.Add(1) + go func() { + err := newTarget.addOrUpdateMonitor(monitor, targetOpts{ + disableIPv4: cfg.Options.DisableIPv4, + disableIPv6: cfg.Options.DisableIPv6, + }) + if err != nil { + log.Errorf("failed to setup target: %w", err) + } + wg.Done() + }() + } + wg.Wait() globalTargets.SetTargets(newTargets) removed := removedTargets(oldTargets, globalTargets) for _, removedTarget := range removed { - log.Infof("remove target: %s\n", removedTarget.host) + log.Infof("remove target: %s", removedTarget.host) removedTarget.removeFromMonitor(monitor) } return nil @@ -212,12 +222,24 @@ func watchConfig(globalTargets *targets, resolver *net.Resolver, monitor *mon.Mo } for { select { - case <-watcher.Events: + case event := <-watcher.Events: + log.Debugf("Got file inotify event: %s", event) + // If the file is removed, the inotify watcher will lose track of the file. Add it again. + if event.Op == inotify.Remove { + if err = watcher.Add(*configFile); err != nil { + log.Fatalf("failed to renew watch for file: %v", err) + } + } cfg, err := loadConfig() if err != nil { log.Errorf("unable to load config: %v", err) continue } + // We get zero targets if the file was truncated. This happens if an automation tool rewrites + // the complete file, instead of alternating only parts of it. + if len(cfg.Targets) == 0 { + continue + } log.Infof("reloading config file %s", *configFile) if err := upsertTargets(globalTargets, resolver, cfg, monitor); err != nil { log.Errorf("failed to reload config: %v", err) diff --git a/target.go b/target.go index 4865803..c589bb5 100644 --- a/target.go +++ b/target.go @@ -37,6 +37,8 @@ func (t *targets) SetTargets(tar []*target) { } func (t *targets) Contains(tar *target) bool { + t.mutex.RLock() + defer t.mutex.RUnlock() for _, ta := range t.t { if ta.host == tar.host { return true @@ -45,6 +47,17 @@ func (t *targets) Contains(tar *target) bool { return false } +func (t *targets) Get(host string) *target { + t.mutex.RLock() + defer t.mutex.RUnlock() + for _, ta := range t.t { + if ta.host == host { + return ta + } + } + return nil +} + func (t *targets) Targets() []*target { t.mutex.RLock() defer t.mutex.RUnlock()