diff --git a/main.go b/main.go index 2db28d3..6286e1b 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "encoding/xml" "flag" "fmt" + "io" "lux/crypto" "lux/host" "lux/node" @@ -13,6 +14,7 @@ import ( "net" "net/netip" "os" + "os/exec" "os/signal" "strings" "syscall" @@ -34,6 +36,12 @@ type LogConfig struct { LogPath string `xml:",innerxml"` } +type UpdateHookConfig struct { + XMLName xml.Name `xml:"hook"` + HostID string `xml:"id"` + Script string `xml:"script"` +} + type NodeConfig struct { XMLName xml.Name `xml:"node"` KeyStore string `xml:"keystore"` @@ -53,6 +61,8 @@ type NodeConfig struct { DNS []string `xml:"dns"` Log LogConfig `xml:"log"` + + Hooks []UpdateHookConfig `xml:"hook"` } func setupLogging(log LogConfig) { @@ -124,6 +134,40 @@ func bootstrapNode() { var log = logging.MustGetLogger("main") +type hookUpdateSubscriber struct { + Hooks map[proto.LuxID]UpdateHookConfig +} + +func (subscriber *hookUpdateSubscriber) HandleStateUpdate(state node.LuxHostState) { + hook, ok := subscriber.Hooks[state.HostId] + if !ok { + return + } + + // spawn executable and pipe xml host state into it's stdin + xmlBytes, err := xml.Marshal(&rpc.LuxRpcHost{ + HostID: state.HostId.String(), + Hostname: state.State.Hostname, + State: state.State.IntoRpc(), + }) + if err != nil { + log.Errorf("failed to marshal host update for hook: %v\n", err) + return + } + + buffer := bytes.Buffer{} + buffer.Write(xmlBytes) + + cmd := exec.Command(hook.Script) + cmd.Stdin = &buffer + cmd.Stdout = io.Discard + cmd.Stderr = io.Discard + + if err := cmd.Run(); err != nil { + log.Warningf("failed to execute script %s: %v\n", hook.Script, err) + } +} + func nodeMain() { xmlBytes, err := os.ReadFile(configPath) if err != nil { @@ -234,6 +278,21 @@ func nodeMain() { } } + // add update hooks + hook := hookUpdateSubscriber{ + Hooks: make(map[proto.LuxID]UpdateHookConfig), + } + for _, item := range config.Hooks { + id, err := proto.ParseLuxID(item.HostID) + if err != nil { + log.Criticalf("failed to parse hook host id %s: %v\n", item.HostID, err) + os.Exit(1) + } + + hook.Hooks[id] = item + } + node.AddSubscriber(&hook) + // start node node.Start()