mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-03 22:18:39 +00:00
Capitalize the first letter of all log messages throughout the codebase to improve readability and consistency in logging output. Key improvements: - All log messages now start with capital letters - Consistent formatting across all logging statements - Improved readability for debugging and monitoring - Enhanced user experience with better formatted messages Files updated: - CLI commands and service management - Internal client information discovery - Network operations and configuration - DNS resolver and proxy operations - Platform-specific implementations This completes the final phase of the logging improvement project, ensuring all log messages follow consistent capitalization standards for better readability and professional appearance.
154 lines
4.5 KiB
Go
154 lines
4.5 KiB
Go
package cli
|
|
|
|
import (
|
|
"net"
|
|
"net/netip"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
|
|
"github.com/Control-D-Inc/ctrld/internal/resolvconffile"
|
|
)
|
|
|
|
// parseResolvConfNameservers reads the resolv.conf file and returns the nameservers found.
|
|
// Returns nil if no nameservers are found.
|
|
// This function parses the system DNS configuration to understand current nameserver settings
|
|
func (p *prog) parseResolvConfNameservers(path string) ([]string, error) {
|
|
return resolvconffile.NameserversFromFile(path)
|
|
}
|
|
|
|
// watchResolvConf watches any changes to /etc/resolv.conf file,
|
|
// and reverting to the original config set by ctrld.
|
|
// This ensures that DNS settings are not overridden by other applications or system processes
|
|
func (p *prog) watchResolvConf(iface *net.Interface, ns []netip.Addr, setDnsFn func(iface *net.Interface, ns []netip.Addr) error) {
|
|
resolvConfPath := "/etc/resolv.conf"
|
|
// Evaluating symbolics link to watch the target file that /etc/resolv.conf point to.
|
|
// This handles systems where resolv.conf is a symlink to another location
|
|
if rp, _ := filepath.EvalSymlinks(resolvConfPath); rp != "" {
|
|
resolvConfPath = rp
|
|
}
|
|
p.Debug().Msgf("Start watching %s file", resolvConfPath)
|
|
watcher, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
p.Warn().Err(err).Msg("Could not create watcher for /etc/resolv.conf")
|
|
return
|
|
}
|
|
defer watcher.Close()
|
|
|
|
// We watch /etc instead of /etc/resolv.conf directly,
|
|
// see: https://github.com/fsnotify/fsnotify#watching-a-file-doesnt-work-well
|
|
// This is necessary because some systems don't properly notify on file changes
|
|
watchDir := filepath.Dir(resolvConfPath)
|
|
if err := watcher.Add(watchDir); err != nil {
|
|
p.Warn().Err(err).Msgf("Could not add %s to watcher list", watchDir)
|
|
return
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case <-p.dnsWatcherStopCh:
|
|
return
|
|
case <-p.stopCh:
|
|
p.Debug().Msgf("Stopping watcher for %s", resolvConfPath)
|
|
return
|
|
case event, ok := <-watcher.Events:
|
|
if p.recoveryRunning.Load() {
|
|
return
|
|
}
|
|
if !ok {
|
|
return
|
|
}
|
|
if event.Name != resolvConfPath { // skip if not /etc/resolv.conf changes.
|
|
continue
|
|
}
|
|
if event.Has(fsnotify.Write) || event.Has(fsnotify.Create) {
|
|
p.Debug().Msgf("/etc/resolv.conf changes detected, reading changes...")
|
|
|
|
// Convert expected nameservers to strings for comparison
|
|
// This allows us to detect when the resolv.conf has been modified
|
|
expectedNS := make([]string, len(ns))
|
|
for i, addr := range ns {
|
|
expectedNS[i] = addr.String()
|
|
}
|
|
|
|
var foundNS []string
|
|
var err error
|
|
|
|
maxRetries := 1
|
|
for retry := 0; retry < maxRetries; retry++ {
|
|
foundNS, err = p.parseResolvConfNameservers(resolvConfPath)
|
|
if err != nil {
|
|
p.Error().Err(err).Msg("Failed to read resolv.conf content")
|
|
break
|
|
}
|
|
|
|
// If we found nameservers, break out of retry loop
|
|
// This handles cases where the file is being written but not yet complete
|
|
if len(foundNS) > 0 {
|
|
break
|
|
}
|
|
|
|
// Only retry if we found no nameservers
|
|
// This handles temporary file states during updates
|
|
if retry < maxRetries-1 {
|
|
p.Debug().Msgf("resolv.conf has no nameserver entries, retry %d/%d in 2 seconds", retry+1, maxRetries)
|
|
select {
|
|
case <-p.stopCh:
|
|
return
|
|
case <-p.dnsWatcherStopCh:
|
|
return
|
|
case <-time.After(2 * time.Second):
|
|
continue
|
|
}
|
|
} else {
|
|
p.Debug().Msg("resolv.conf remained empty after all retries")
|
|
}
|
|
}
|
|
|
|
// If we found nameservers, check if they match what we expect
|
|
if len(foundNS) > 0 {
|
|
// Check if the nameservers match exactly what we expect
|
|
matches := len(foundNS) == len(expectedNS)
|
|
if matches {
|
|
for i := range foundNS {
|
|
if foundNS[i] != expectedNS[i] {
|
|
matches = false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
p.Debug().
|
|
Strs("found", foundNS).
|
|
Strs("expected", expectedNS).
|
|
Bool("matches", matches).
|
|
Msg("checking nameservers")
|
|
|
|
// Only revert if the nameservers don't match
|
|
if !matches {
|
|
if err := watcher.Remove(watchDir); err != nil {
|
|
p.Error().Err(err).Msg("Failed to pause watcher")
|
|
continue
|
|
}
|
|
|
|
if err := setDnsFn(iface, ns); err != nil {
|
|
p.Error().Err(err).Msg("Failed to revert /etc/resolv.conf changes")
|
|
}
|
|
|
|
if err := watcher.Add(watchDir); err != nil {
|
|
p.Error().Err(err).Msg("Failed to continue running watcher")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case err, ok := <-watcher.Errors:
|
|
if !ok {
|
|
return
|
|
}
|
|
p.Error().Err(err).Msg("Could not get event for /etc/resolv.conf")
|
|
}
|
|
}
|
|
}
|