package main
import (
"flag"
"fmt"
"html/template"
"log"
"net/http"
"os"
"sync"
"time"
)
type State struct {
mu sync.RWMutex
GatewayStatus map[string]bool
GatewayIPs map[string]string
SubnetCount int
LastUpdate int64
ControllerUp bool
Subnets []string
}
var state State
const indexHTML = `
SRM Daemon
SRM Daemon
| Parameter | Value |
{{range $name, $up := .GatewayStatus}}
| Gateway "{{html $name}}" ({{index $.GatewayIPs $name}}) |
{{if $up}}UP{{else}}DOWN{{end}} |
{{end}}
| Current Routes | {{.SubnetCount}} |
| Last Update |
{{if eq .LastUpdate 0}}Never{{else}}{{formatTime .LastUpdate}}{{end}} |
| Controller |
{{if .ControllerUp}}UP{{else}}DOWN{{end}} |
`
func formatTime(ts int64) string {
t := time.UnixMilli(ts)
return t.Format("2006-01-02 15:04:05 MST")
}
func startWebServer(port int) {
funcMap := template.FuncMap{
"formatTime": formatTime,
}
tmpl, err := template.New("index").Funcs(funcMap).Parse(indexHTML)
if err != nil {
log.Fatalf("Failed to parse template: %v", err)
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
state.mu.RLock()
defer state.mu.RUnlock()
w.Header().Set("Content-Type", "text/html; charset=utf-8")
tmpl.Execute(w, &state)
})
addr := fmt.Sprintf(":%d", port)
log.Printf("Web interface listening on %s", addr)
if err := http.ListenAndServe(addr, nil); err != nil {
log.Fatalf("Web server failed: %v", err)
}
}
func main() {
configPath := flag.String("c", "", "Path to config JSON file")
flag.Parse()
if *configPath == "" {
fmt.Fprintln(os.Stderr, "Usage: srmd -c /path/to/config.json")
os.Exit(1)
}
cfg, err := LoadConfig(*configPath)
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
state.GatewayStatus = make(map[string]bool)
state.GatewayIPs = make(map[string]string)
for _, gw := range cfg.Gateway {
state.GatewayStatus[gw.Name] = false
state.GatewayIPs[gw.Name] = gw.Address
}
state.Subnets = []string{}
state.LastUpdate = 0
go startWebServer(cfg.Port)
var lastUpdate int64
var subnets []string
gatewayStates := make(map[string]bool)
for _, gw := range cfg.Gateway {
gatewayStates[gw.Name] = false
}
for {
log.Println("--- Step 2: Checking gateways ---")
prevStates := make(map[string]bool)
for k, v := range gatewayStates {
prevStates[k] = v
}
newStates := make(map[string]bool)
for _, gw := range cfg.Gateway {
up := Ping(gw.Address, cfg.Timeout)
newStates[gw.Name] = up
log.Printf("Gateway %q (%s): %v", gw.Name, gw.Address, up)
}
changed := false
for _, gw := range cfg.Gateway {
if prevStates[gw.Name] != newStates[gw.Name] {
changed = true
break
}
}
gatewayStates = newStates
state.mu.Lock()
for k, v := range gatewayStates {
state.GatewayStatus[k] = v
}
state.mu.Unlock()
if changed {
log.Println("Gateway states changed, updating routes (step 3)")
DoRouteUpdate(cfg, gatewayStates, subnets)
} else {
log.Println("Gateway states unchanged, skipping route update")
}
log.Println("--- Step 4: Getting update from controller ---")
ts, err := GetUpdate(cfg.Controller, cfg.Timeout)
if err != nil {
log.Printf("Controller update failed: %v", err)
state.mu.Lock()
state.ControllerUp = false
state.mu.Unlock()
time.Sleep(time.Duration(cfg.Interval) * time.Second)
continue
}
state.mu.Lock()
state.ControllerUp = true
state.mu.Unlock()
log.Printf("Controller timestamp: %d (lastUpdate: %d)", ts, lastUpdate)
if ts == lastUpdate {
log.Println("Timestamp unchanged, waiting and retrying")
time.Sleep(time.Duration(cfg.Interval) * time.Second)
continue
}
newSubnets, err := GetSubnets(cfg.Controller, cfg.Timeout)
if err != nil {
log.Printf("Failed to get subnets: %v", err)
state.mu.Lock()
state.ControllerUp = false
state.mu.Unlock()
time.Sleep(time.Duration(cfg.Interval) * time.Second)
continue
}
state.mu.Lock()
state.ControllerUp = true
state.mu.Unlock()
subnetsChanged := !stringSliceEqual(subnets, newSubnets)
subnets = newSubnets
state.mu.Lock()
state.Subnets = make([]string, len(subnets))
copy(state.Subnets, subnets)
state.SubnetCount = len(subnets)
state.mu.Unlock()
if subnetsChanged {
log.Printf("Subnets changed (%d subnets), updating lastUpdate", len(subnets))
lastUpdate = ts
state.mu.Lock()
state.LastUpdate = ts
state.mu.Unlock()
DoRouteUpdate(cfg, gatewayStates, subnets)
} else {
log.Println("Subnets unchanged, waiting")
time.Sleep(time.Duration(cfg.Interval) * time.Second)
}
}
}
func stringSliceEqual(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}