Skip to content

Commit

Permalink
Progress on detection as a daemon
Browse files Browse the repository at this point in the history
  • Loading branch information
douglaswth committed Jul 4, 2022
1 parent 1e5350b commit 3271c51
Show file tree
Hide file tree
Showing 18 changed files with 434 additions and 77 deletions.
28 changes: 13 additions & 15 deletions cmd/presence/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"goa.design/clue/log"

"douglasthrift.net/presence"
"douglasthrift.net/presence/neighbors"
)

type (
Expand All @@ -12,24 +13,21 @@ type (
}
)

func (c *Check) Run(cli *CLI) error {
func (c *Check) Run(cli *CLI) (err error) {
ctx := cli.Context()
config, err := presence.ParseConfig(cli.Config)
if c.Values {
_, err = presence.ParseConfigWithContext(ctx, cli.Config, wNet)
} else {
_, err = presence.ParseConfig(cli.Config, wNet)
}
if err != nil {
log.Error(ctx, err, log.Fields{"config": cli.Config})
return err
log.Fatal(ctx, err, log.KV{K: "msg", V: "error parsing config"}, log.KV{K: "config", V: cli.Config})
}

if c.Values {
log.Info(ctx, log.Fields{"interval": config.Interval})

as := make([]string, len(config.MACAddresses))
for i, a := range config.MACAddresses {
as[i] = a.String()
}
log.Info(ctx, log.Fields{"mac_addresses": as})

log.Info(ctx, log.Fields{"ping_count": config.PingCount})
_, err = neighbors.NewARP(0)
if err != nil {
log.Fatal(ctx, err, log.KV{K: "msg", V: "error finding dependencies"})
}
return nil

return
}
80 changes: 65 additions & 15 deletions cmd/presence/detect.go
Original file line number Diff line number Diff line change
@@ -1,39 +1,89 @@
package main

import (
"os"
"os/signal"
"syscall"
"time"

"goa.design/clue/log"

"douglasthrift.net/presence"
"douglasthrift.net/presence/neighbors"
)

type (
Detect struct {
Interface string `arg:""`
HardwareAddrs []string `arg:""`
Iterations uint `help:"Only detect for N iterations." placeholder:"N" short:"i"`
}
)

func (d *Detect) Run(cli *CLI) error {
ctx := cli.Context()

ifs := neighbors.Interfaces{d.Interface: true}
hws := make(neighbors.HardwareAddrStates, len(d.HardwareAddrs))
for _, hw := range d.HardwareAddrs {
hws[hw] = neighbors.NewState()
config, err := presence.ParseConfigWithContext(ctx, cli.Config, wNet)
if err != nil {
log.Fatal(ctx, err, log.KV{K: "msg", V: "error parsing config"}, log.KV{K: "config", V: cli.Config})
}

a, err := neighbors.NewARP(1)
arp, err := neighbors.NewARP(config.PingCount)
if err != nil {
return err
log.Fatal(ctx, err, log.KV{K: "msg", V: "error finding dependencies"})
}

ok, err := a.Present(ctx, ifs, hws)
var (
detector = presence.NewDetector(config, arp)
ticker = time.NewTicker(config.Interval)
stop = make(chan os.Signal, 1)
reload = make(chan os.Signal, 1)
i uint
)

err = detector.Detect(ctx)
if err != nil {
return err
log.Error(ctx, err, log.KV{K: "msg", V: "error detecting presence"})
}
log.Info(ctx, log.KV{K: "present", V: ok})
for hw, state := range hws {
log.Info(ctx, log.KV{K: "hw", V: hw}, log.KV{K: "present", V: state.Present()}, log.KV{K: "changed", V: state.Changed()})

if d.Iterations != 0 {
i++
if i >= d.Iterations {
ticker.Stop()
return nil
}
}

signal.Ignore(syscall.SIGHUP)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
signal.Notify(reload, syscall.SIGUSR1)

for {
select {
case <-ticker.C:
err = detector.Detect(ctx)
if err != nil {
log.Error(ctx, err, log.KV{K: "msg", V: "error detecting presence"})
}

if d.Iterations != 0 {
i++
if i >= d.Iterations {
ticker.Stop()
return nil
}
}
case s := <-stop:
log.Print(ctx, log.Fields{"msg": "received stop signal"}, log.Fields{"signal": s})
ticker.Stop()
return nil
case s := <-reload:
log.Print(ctx, log.Fields{"msg": "received reload signal"}, log.Fields{"signal": s})
config, err = presence.ParseConfigWithContext(ctx, cli.Config, wNet)
if err != nil {
log.Error(ctx, err, log.KV{K: "msg", V: "error parsing config"}, log.KV{K: "config", V: cli.Config})
} else {
arp.Count(config.PingCount)
detector.Config(config)
ticker.Reset(config.Interval)
}
}
}
return nil
}
5 changes: 4 additions & 1 deletion cmd/presence/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (

"github.com/alecthomas/kong"
"goa.design/clue/log"

"douglasthrift.net/presence/wrap"
)

type (
Expand All @@ -26,6 +28,7 @@ var (
commit = "none"
date = "unknown"
configPrefix = ""
wNet = wrap.NewNet()
)

func main() {
Expand All @@ -47,7 +50,7 @@ func (cli *CLI) Context() (ctx context.Context) {
if cli.Debug {
ctx = log.Context(ctx, log.WithDebug())
} else {
ctx = log.Context(ctx, log.WithDisableBuffering(func(context.Context) bool { return true }))
ctx = log.Context(ctx)
}
return
}
73 changes: 55 additions & 18 deletions config.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
package presence

import (
"context"
"fmt"
"net"
"os"
"time"

"goa.design/clue/log"
"gopkg.in/yaml.v3"

"douglasthrift.net/presence/wrap"
)

type (
MACAddress struct {
net.HardwareAddr
}

Config struct {
Interval time.Duration `yaml:"interval"`
MACAddresses []MACAddress `yaml:"mac_addresses"`
Interfaces []string `yaml:"interfaces"`
MACAddresses []string `yaml:"mac_addresses"`
PingCount uint `yaml:"ping_count"`
}
)

func ParseConfig(name string) (*Config, error) {
func ParseConfig(name string, wNet wrap.Net) (*Config, error) {
return ParseConfigWithContext(context.Background(), name, wNet)
}

func ParseConfigWithContext(ctx context.Context, name string, wNet wrap.Net) (*Config, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
Expand All @@ -39,23 +44,55 @@ func ParseConfig(name string) (*Config, error) {

if c.Interval < 0 {
return nil, fmt.Errorf("negative interval (%v)", c.Interval)
}
if c.Interval == 0 {
} else if c.Interval == 0 {
c.Interval = 30 * time.Second
}

if len(c.Interfaces) == 0 {
ifs, err := wNet.Interfaces()
if err != nil {
return nil, err
}

c.Interfaces = make([]string, 0, len(ifs))
for _, i := range ifs {
c.Interfaces = append(c.Interfaces, i.Name)
}
} else {
for _, i := range c.Interfaces {
_, err = wNet.InterfaceByName(i)
if err != nil {
return nil, fmt.Errorf("interface %v: %w", i, err)
}
}
}

if len(c.MACAddresses) == 0 {
return nil, fmt.Errorf("no MAC addresses")
}
as := make(map[string]bool, len(c.MACAddresses))
for i, a := range c.MACAddresses {
hw, err := net.ParseMAC(a)
if err != nil {
return nil, err
}

a = hw.String()
if as[a] {
return nil, fmt.Errorf("duplicate MAC address (%v)", a)
}
as[a] = true
c.MACAddresses[i] = a
}

if c.PingCount == 0 {
c.PingCount = 1
}
return c, nil
}

func (a *MACAddress) UnmarshalYAML(value *yaml.Node) (err error) {
var s string
err = value.Decode(&s)
if err != nil {
return
}
log.Print(ctx, log.KV{K: "msg", V: "interval"}, log.KV{K: "value", V: c.Interval})
log.Print(ctx, log.KV{K: "msg", V: "interfaces"}, log.KV{K: "value", V: c.Interfaces})
log.Print(ctx, log.KV{K: "msg", V: "MAC addresses"}, log.KV{K: "value", V: c.MACAddresses})
log.Print(ctx, log.KV{K: "msg", V: "ping count"}, log.KV{K: "value", V: c.PingCount})

a.HardwareAddr, err = net.ParseMAC(s)
return
return c, nil
}

0 comments on commit 3271c51

Please sign in to comment.