mirror of
https://github.com/luckfox-eng29/kvm.git
synced 2026-01-18 03:28:19 +01:00
network enhanecment / refactor (#361)
* chore(network): improve connectivity check * refactor(network): rewrite network and timesync component * feat(display): show cloud connection status * chore: change logging verbosity * chore(websecure): update log message * fix(ota): validate root certificate when downloading update * feat(ui): add network settings tab * fix(display): cloud connecting animation * fix: golintci issues * feat: add network settings tab * feat(timesync): query servers in parallel * refactor(network): move to internal/network package * feat(timesync): add metrics * refactor(log): move log to internal/logging package * refactor(mdms): move mdns to internal/mdns package * feat(developer): add pprof endpoint * feat(logging): add a simple logging streaming endpoint * fix(mdns): do not start mdns until network is up * feat(network): allow users to update network settings from ui * fix(network): handle errors when net.IPAddr is nil * fix(mdns): scopedLogger SIGSEGV * fix(dhcp): watch directory instead of file to catch fsnotify.Create event * refactor(nbd): move platform-specific code to different files * refactor(native): move platform-specific code to different files * chore: fix linter issues * chore(dev_deploy): allow to override PION_LOG_TRACE
This commit is contained in:
12
internal/udhcpc/options.go
Normal file
12
internal/udhcpc/options.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package udhcpc
|
||||
|
||||
func (u *DHCPClient) GetNtpServers() []string {
|
||||
if u.lease == nil {
|
||||
return nil
|
||||
}
|
||||
servers := make([]string, len(u.lease.NTPServers))
|
||||
for i, server := range u.lease.NTPServers {
|
||||
servers[i] = server.String()
|
||||
}
|
||||
return servers
|
||||
}
|
||||
186
internal/udhcpc/parser.go
Normal file
186
internal/udhcpc/parser.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package udhcpc
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Lease struct {
|
||||
// from https://udhcp.busybox.net/README.udhcpc
|
||||
IPAddress net.IP `env:"ip" json:"ip"` // The obtained IP
|
||||
Netmask net.IP `env:"subnet" json:"netmask"` // The assigned subnet mask
|
||||
Broadcast net.IP `env:"broadcast" json:"broadcast"` // The broadcast address for this network
|
||||
TTL int `env:"ipttl" json:"ttl,omitempty"` // The TTL to use for this network
|
||||
MTU int `env:"mtu" json:"mtu,omitempty"` // The MTU to use for this network
|
||||
HostName string `env:"hostname" json:"hostname,omitempty"` // The assigned hostname
|
||||
Domain string `env:"domain" json:"domain,omitempty"` // The domain name of the network
|
||||
BootPNextServer net.IP `env:"siaddr" json:"bootp_next_server,omitempty"` // The bootp next server option
|
||||
BootPServerName string `env:"sname" json:"bootp_server_name,omitempty"` // The bootp server name option
|
||||
BootPFile string `env:"boot_file" json:"bootp_file,omitempty"` // The bootp boot file option
|
||||
Timezone string `env:"timezone" json:"timezone,omitempty"` // Offset in seconds from UTC
|
||||
Routers []net.IP `env:"router" json:"routers,omitempty"` // A list of routers
|
||||
DNS []net.IP `env:"dns" json:"dns_servers,omitempty"` // A list of DNS servers
|
||||
NTPServers []net.IP `env:"ntpsrv" json:"ntp_servers,omitempty"` // A list of NTP servers
|
||||
LPRServers []net.IP `env:"lprsvr" json:"lpr_servers,omitempty"` // A list of LPR servers
|
||||
TimeServers []net.IP `env:"timesvr" json:"_time_servers,omitempty"` // A list of time servers (obsolete)
|
||||
IEN116NameServers []net.IP `env:"namesvr" json:"_name_servers,omitempty"` // A list of IEN 116 name servers (obsolete)
|
||||
LogServers []net.IP `env:"logsvr" json:"_log_servers,omitempty"` // A list of MIT-LCS UDP log servers (obsolete)
|
||||
CookieServers []net.IP `env:"cookiesvr" json:"_cookie_servers,omitempty"` // A list of RFC 865 cookie servers (obsolete)
|
||||
WINSServers []net.IP `env:"wins" json:"_wins_servers,omitempty"` // A list of WINS servers
|
||||
SwapServer net.IP `env:"swapsvr" json:"_swap_server,omitempty"` // The IP address of the client's swap server
|
||||
BootSize int `env:"bootsize" json:"bootsize,omitempty"` // The length in 512 octect blocks of the bootfile
|
||||
RootPath string `env:"rootpath" json:"root_path,omitempty"` // The path name of the client's root disk
|
||||
LeaseTime time.Duration `env:"lease" json:"lease,omitempty"` // The lease time, in seconds
|
||||
DHCPType string `env:"dhcptype" json:"dhcp_type,omitempty"` // DHCP message type (safely ignored)
|
||||
ServerID string `env:"serverid" json:"server_id,omitempty"` // The IP of the server
|
||||
Message string `env:"message" json:"reason,omitempty"` // Reason for a DHCPNAK
|
||||
TFTPServerName string `env:"tftp" json:"tftp,omitempty"` // The TFTP server name
|
||||
BootFileName string `env:"bootfile" json:"bootfile,omitempty"` // The boot file name
|
||||
Uptime time.Duration `env:"uptime" json:"uptime,omitempty"` // The uptime of the device when the lease was obtained, in seconds
|
||||
LeaseExpiry *time.Time `json:"lease_expiry,omitempty"` // The expiry time of the lease
|
||||
isEmpty map[string]bool
|
||||
}
|
||||
|
||||
func (l *Lease) setIsEmpty(m map[string]bool) {
|
||||
l.isEmpty = m
|
||||
}
|
||||
|
||||
func (l *Lease) IsEmpty(key string) bool {
|
||||
return l.isEmpty[key]
|
||||
}
|
||||
|
||||
func (l *Lease) ToJSON() string {
|
||||
json, err := json.Marshal(l)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(json)
|
||||
}
|
||||
|
||||
func (l *Lease) SetLeaseExpiry() (time.Time, error) {
|
||||
if l.Uptime == 0 || l.LeaseTime == 0 {
|
||||
return time.Time{}, fmt.Errorf("uptime or lease time isn't set")
|
||||
}
|
||||
|
||||
// get the uptime of the device
|
||||
|
||||
file, err := os.Open("/proc/uptime")
|
||||
if err != nil {
|
||||
return time.Time{}, fmt.Errorf("failed to open uptime file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var uptime time.Duration
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
text := scanner.Text()
|
||||
parts := strings.Split(text, " ")
|
||||
uptime, err = time.ParseDuration(parts[0] + "s")
|
||||
|
||||
if err != nil {
|
||||
return time.Time{}, fmt.Errorf("failed to parse uptime: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
relativeLeaseRemaining := (l.Uptime + l.LeaseTime) - uptime
|
||||
leaseExpiry := time.Now().Add(relativeLeaseRemaining)
|
||||
|
||||
l.LeaseExpiry = &leaseExpiry
|
||||
|
||||
return leaseExpiry, nil
|
||||
}
|
||||
|
||||
func UnmarshalDHCPCLease(lease *Lease, str string) error {
|
||||
// parse the lease file as a map
|
||||
data := make(map[string]string)
|
||||
for _, line := range strings.Split(str, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
// skip empty lines and comments
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
|
||||
data[key] = value
|
||||
}
|
||||
|
||||
// now iterate over the lease struct and set the values
|
||||
leaseType := reflect.TypeOf(lease).Elem()
|
||||
leaseValue := reflect.ValueOf(lease).Elem()
|
||||
|
||||
valuesParsed := make(map[string]bool)
|
||||
|
||||
for i := 0; i < leaseType.NumField(); i++ {
|
||||
field := leaseValue.Field(i)
|
||||
|
||||
// get the env tag
|
||||
key := leaseType.Field(i).Tag.Get("env")
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
valuesParsed[key] = false
|
||||
|
||||
// get the value from the data map
|
||||
value, ok := data[key]
|
||||
if !ok || value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
switch field.Interface().(type) {
|
||||
case string:
|
||||
field.SetString(value)
|
||||
case int:
|
||||
val, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
field.SetInt(int64(val))
|
||||
case time.Duration:
|
||||
val, err := time.ParseDuration(value + "s")
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
field.Set(reflect.ValueOf(val))
|
||||
case net.IP:
|
||||
ip := net.ParseIP(value)
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
field.Set(reflect.ValueOf(ip))
|
||||
case []net.IP:
|
||||
val := make([]net.IP, 0)
|
||||
for _, ipStr := range strings.Fields(value) {
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
val = append(val, ip)
|
||||
}
|
||||
field.Set(reflect.ValueOf(val))
|
||||
default:
|
||||
return fmt.Errorf("unsupported field `%s` type: %s", key, field.Type().String())
|
||||
}
|
||||
|
||||
valuesParsed[key] = true
|
||||
}
|
||||
|
||||
lease.setIsEmpty(valuesParsed)
|
||||
|
||||
return nil
|
||||
}
|
||||
74
internal/udhcpc/parser_test.go
Normal file
74
internal/udhcpc/parser_test.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package udhcpc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestUnmarshalDHCPCLease(t *testing.T) {
|
||||
lease := &Lease{}
|
||||
err := UnmarshalDHCPCLease(lease, `
|
||||
# generated @ Mon Jan 4 19:31:53 UTC 2021
|
||||
# 19:31:53 up 0 min, 0 users, load average: 0.72, 0.14, 0.04
|
||||
# the date might be inaccurate if the clock is not set
|
||||
ip=192.168.0.240
|
||||
siaddr=192.168.0.1
|
||||
sname=
|
||||
boot_file=
|
||||
subnet=255.255.255.0
|
||||
timezone=
|
||||
router=192.168.0.1
|
||||
timesvr=
|
||||
namesvr=
|
||||
dns=172.19.53.2
|
||||
logsvr=
|
||||
cookiesvr=
|
||||
lprsvr=
|
||||
hostname=
|
||||
bootsize=
|
||||
domain=
|
||||
swapsvr=
|
||||
rootpath=
|
||||
ipttl=
|
||||
mtu=
|
||||
broadcast=
|
||||
ntpsrv=162.159.200.123
|
||||
wins=
|
||||
lease=172800
|
||||
dhcptype=
|
||||
serverid=192.168.0.1
|
||||
message=
|
||||
tftp=
|
||||
bootfile=
|
||||
`)
|
||||
if lease.IPAddress.String() != "192.168.0.240" {
|
||||
t.Fatalf("expected ip to be 192.168.0.240, got %s", lease.IPAddress.String())
|
||||
}
|
||||
if lease.Netmask.String() != "255.255.255.0" {
|
||||
t.Fatalf("expected netmask to be 255.255.255.0, got %s", lease.Netmask.String())
|
||||
}
|
||||
if len(lease.Routers) != 1 {
|
||||
t.Fatalf("expected 1 router, got %d", len(lease.Routers))
|
||||
}
|
||||
if lease.Routers[0].String() != "192.168.0.1" {
|
||||
t.Fatalf("expected router to be 192.168.0.1, got %s", lease.Routers[0].String())
|
||||
}
|
||||
if len(lease.NTPServers) != 1 {
|
||||
t.Fatalf("expected 1 timeserver, got %d", len(lease.NTPServers))
|
||||
}
|
||||
if lease.NTPServers[0].String() != "162.159.200.123" {
|
||||
t.Fatalf("expected timeserver to be 162.159.200.123, got %s", lease.NTPServers[0].String())
|
||||
}
|
||||
if len(lease.DNS) != 1 {
|
||||
t.Fatalf("expected 1 dns, got %d", len(lease.DNS))
|
||||
}
|
||||
if lease.DNS[0].String() != "172.19.53.2" {
|
||||
t.Fatalf("expected dns to be 172.19.53.2, got %s", lease.DNS[0].String())
|
||||
}
|
||||
if lease.LeaseTime != 172800*time.Second {
|
||||
t.Fatalf("expected lease time to be 172800 seconds, got %d", lease.LeaseTime)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
212
internal/udhcpc/proc.go
Normal file
212
internal/udhcpc/proc.go
Normal file
@@ -0,0 +1,212 @@
|
||||
package udhcpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func readFileNoStat(filename string) ([]byte, error) {
|
||||
const maxBufferSize = 1024 * 1024
|
||||
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
reader := io.LimitReader(f, maxBufferSize)
|
||||
return io.ReadAll(reader)
|
||||
}
|
||||
|
||||
func toCmdline(path string) ([]string, error) {
|
||||
data, err := readFileNoStat(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data) < 1 {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
return strings.Split(string(bytes.TrimRight(data, "\x00")), "\x00"), nil
|
||||
}
|
||||
|
||||
func (p *DHCPClient) findUdhcpcProcess() (int, error) {
|
||||
// read procfs for udhcpc processes
|
||||
// we do not use procfs.AllProcs() because we want to avoid the overhead of reading the entire procfs
|
||||
processes, err := os.ReadDir("/proc")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// iterate over the processes
|
||||
for _, d := range processes {
|
||||
// check if file is numeric
|
||||
pid, err := strconv.Atoi(d.Name())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// check if it's a directory
|
||||
if !d.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
cmdline, err := toCmdline(filepath.Join("/proc", d.Name(), "cmdline"))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(cmdline) < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
if cmdline[0] != "udhcpc" {
|
||||
continue
|
||||
}
|
||||
|
||||
cmdlineText := strings.Join(cmdline, " ")
|
||||
|
||||
// check if it's a udhcpc process
|
||||
if strings.Contains(cmdlineText, fmt.Sprintf("-i %s", p.InterfaceName)) {
|
||||
p.logger.Debug().
|
||||
Str("pid", d.Name()).
|
||||
Interface("cmdline", cmdline).
|
||||
Msg("found udhcpc process")
|
||||
return pid, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, errors.New("udhcpc process not found")
|
||||
}
|
||||
|
||||
func (c *DHCPClient) getProcessPid() (int, error) {
|
||||
var pid int
|
||||
if c.pidFile != "" {
|
||||
// try to read the pid file
|
||||
pidHandle, err := os.ReadFile(c.pidFile)
|
||||
if err != nil {
|
||||
c.logger.Warn().Err(err).
|
||||
Str("pidFile", c.pidFile).Msg("failed to read udhcpc pid file")
|
||||
}
|
||||
|
||||
// if it exists, try to read the pid
|
||||
if pidHandle != nil {
|
||||
pidFromFile, err := strconv.Atoi(string(pidHandle))
|
||||
if err != nil {
|
||||
c.logger.Warn().Err(err).
|
||||
Str("pidFile", c.pidFile).Msg("failed to convert pid file to int")
|
||||
}
|
||||
pid = pidFromFile
|
||||
}
|
||||
}
|
||||
|
||||
// if the pid is 0, try to find the pid using procfs
|
||||
if pid == 0 {
|
||||
newPid, err := c.findUdhcpcProcess()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
pid = newPid
|
||||
}
|
||||
|
||||
return pid, nil
|
||||
}
|
||||
|
||||
func (c *DHCPClient) getProcess() *os.Process {
|
||||
pid, err := c.getProcessPid()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
process, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
c.logger.Warn().Err(err).
|
||||
Int("pid", pid).Msg("failed to find process")
|
||||
return nil
|
||||
}
|
||||
|
||||
return process
|
||||
}
|
||||
|
||||
func (c *DHCPClient) GetProcess() *os.Process {
|
||||
if c.process == nil {
|
||||
process := c.getProcess()
|
||||
if process == nil {
|
||||
return nil
|
||||
}
|
||||
c.process = process
|
||||
}
|
||||
|
||||
err := c.process.Signal(syscall.Signal(0))
|
||||
if err != nil && errors.Is(err, os.ErrProcessDone) {
|
||||
oldPid := c.process.Pid
|
||||
|
||||
c.process = nil
|
||||
c.process = c.getProcess()
|
||||
if c.process == nil {
|
||||
c.logger.Error().Msg("failed to find new udhcpc process")
|
||||
return nil
|
||||
}
|
||||
c.logger.Warn().
|
||||
Int("oldPid", oldPid).
|
||||
Int("newPid", c.process.Pid).
|
||||
Msg("udhcpc process pid changed")
|
||||
} else if err != nil {
|
||||
c.logger.Warn().Err(err).
|
||||
Int("pid", c.process.Pid).Msg("udhcpc process is not running")
|
||||
}
|
||||
|
||||
return c.process
|
||||
}
|
||||
|
||||
func (c *DHCPClient) KillProcess() error {
|
||||
process := c.GetProcess()
|
||||
if process == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return process.Kill()
|
||||
}
|
||||
|
||||
func (c *DHCPClient) ReleaseProcess() error {
|
||||
process := c.GetProcess()
|
||||
if process == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return process.Release()
|
||||
}
|
||||
|
||||
func (c *DHCPClient) signalProcess(sig syscall.Signal) error {
|
||||
process := c.GetProcess()
|
||||
if process == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
s := process.Signal(sig)
|
||||
if s != nil {
|
||||
c.logger.Warn().Err(s).
|
||||
Int("pid", process.Pid).
|
||||
Str("signal", sig.String()).
|
||||
Msg("failed to signal udhcpc process")
|
||||
return s
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DHCPClient) Renew() error {
|
||||
return c.signalProcess(syscall.SIGUSR1)
|
||||
}
|
||||
|
||||
func (c *DHCPClient) Release() error {
|
||||
return c.signalProcess(syscall.SIGUSR2)
|
||||
}
|
||||
191
internal/udhcpc/udhcpc.go
Normal file
191
internal/udhcpc/udhcpc.go
Normal file
@@ -0,0 +1,191 @@
|
||||
package udhcpc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
const (
|
||||
DHCPLeaseFile = "/run/udhcpc.%s.info"
|
||||
DHCPPidFile = "/run/udhcpc.%s.pid"
|
||||
)
|
||||
|
||||
type DHCPClient struct {
|
||||
InterfaceName string
|
||||
leaseFile string
|
||||
pidFile string
|
||||
lease *Lease
|
||||
logger *zerolog.Logger
|
||||
process *os.Process
|
||||
onLeaseChange func(lease *Lease)
|
||||
}
|
||||
|
||||
type DHCPClientOptions struct {
|
||||
InterfaceName string
|
||||
PidFile string
|
||||
Logger *zerolog.Logger
|
||||
OnLeaseChange func(lease *Lease)
|
||||
}
|
||||
|
||||
var defaultLogger = zerolog.New(os.Stdout).Level(zerolog.InfoLevel)
|
||||
|
||||
func NewDHCPClient(options *DHCPClientOptions) *DHCPClient {
|
||||
if options.Logger == nil {
|
||||
options.Logger = &defaultLogger
|
||||
}
|
||||
|
||||
l := options.Logger.With().Str("interface", options.InterfaceName).Logger()
|
||||
return &DHCPClient{
|
||||
InterfaceName: options.InterfaceName,
|
||||
logger: &l,
|
||||
leaseFile: fmt.Sprintf(DHCPLeaseFile, options.InterfaceName),
|
||||
pidFile: options.PidFile,
|
||||
onLeaseChange: options.OnLeaseChange,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DHCPClient) getWatchPaths() []string {
|
||||
watchPaths := make(map[string]interface{})
|
||||
watchPaths[filepath.Dir(c.leaseFile)] = nil
|
||||
|
||||
if c.pidFile != "" {
|
||||
watchPaths[filepath.Dir(c.pidFile)] = nil
|
||||
}
|
||||
|
||||
paths := make([]string, 0)
|
||||
for path := range watchPaths {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
// Run starts the DHCP client and watches the lease file for changes.
|
||||
// this isn't a blocking call, and the lease file is reloaded when a change is detected.
|
||||
func (c *DHCPClient) Run() error {
|
||||
err := c.loadLeaseFile()
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-watcher.Events:
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if !event.Has(fsnotify.Write) && !event.Has(fsnotify.Create) {
|
||||
continue
|
||||
}
|
||||
|
||||
if event.Name == c.leaseFile {
|
||||
c.logger.Debug().
|
||||
Str("event", event.Op.String()).
|
||||
Str("path", event.Name).
|
||||
Msg("udhcpc lease file updated, reloading lease")
|
||||
_ = c.loadLeaseFile()
|
||||
}
|
||||
case err, ok := <-watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
c.logger.Error().Err(err).Msg("error watching lease file")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for _, path := range c.getWatchPaths() {
|
||||
err = watcher.Add(path)
|
||||
if err != nil {
|
||||
c.logger.Error().
|
||||
Err(err).
|
||||
Str("path", path).
|
||||
Msg("failed to watch directory")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: update udhcpc pid file
|
||||
// we'll comment this out for now because the pid might change
|
||||
// process := c.GetProcess()
|
||||
// if process == nil {
|
||||
// c.logger.Error().Msg("udhcpc process not found")
|
||||
// }
|
||||
|
||||
// block the goroutine until the lease file is updated
|
||||
<-make(chan struct{})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DHCPClient) loadLeaseFile() error {
|
||||
file, err := os.ReadFile(c.leaseFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := string(file)
|
||||
if data == "" {
|
||||
c.logger.Debug().Msg("udhcpc lease file is empty")
|
||||
return nil
|
||||
}
|
||||
|
||||
lease := &Lease{}
|
||||
err = UnmarshalDHCPCLease(lease, string(file))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isFirstLoad := c.lease == nil
|
||||
c.lease = lease
|
||||
|
||||
if lease.IPAddress == nil {
|
||||
c.logger.Info().
|
||||
Interface("lease", lease).
|
||||
Str("data", string(file)).
|
||||
Msg("udhcpc lease cleared")
|
||||
return nil
|
||||
}
|
||||
|
||||
msg := "udhcpc lease updated"
|
||||
if isFirstLoad {
|
||||
msg = "udhcpc lease loaded"
|
||||
}
|
||||
|
||||
leaseExpiry, err := lease.SetLeaseExpiry()
|
||||
if err != nil {
|
||||
c.logger.Error().Err(err).Msg("failed to get dhcp lease expiry")
|
||||
} else {
|
||||
expiresIn := time.Until(leaseExpiry)
|
||||
c.logger.Info().
|
||||
Interface("expiry", leaseExpiry).
|
||||
Str("expiresIn", expiresIn.String()).
|
||||
Msg("current dhcp lease expiry time calculated")
|
||||
}
|
||||
|
||||
c.onLeaseChange(lease)
|
||||
|
||||
c.logger.Info().
|
||||
Str("ip", lease.IPAddress.String()).
|
||||
Str("leaseTime", lease.LeaseTime.String()).
|
||||
Interface("data", lease).
|
||||
Msg(msg)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DHCPClient) GetLease() *Lease {
|
||||
return c.lease
|
||||
}
|
||||
Reference in New Issue
Block a user