package main import ( "encoding/json" "os" "path/filepath" "time" ) // A check evaluates the saved settings like an apply would (option types and // the router's assertions) without building anything: router-check.service // runs `nix eval` on the configuration and leaves its verdict in // /check/result.json. type CheckResult struct { OK bool `json:"ok"` Error string `json:"error,omitempty"` Etag string `json:"etag"` // of the settings that were evaluated Time time.Time `json:"time"` } func (s System) checkResultPath() string { return filepath.Join(s.StateDir, "check", "result.json") } func (s System) readCheck() (CheckResult, bool) { var r CheckResult data, err := os.ReadFile(s.checkResultPath()) if err != nil || json.Unmarshal(data, &r) != nil { return r, false } return r, true } func (s System) checkRunning() bool { return unitProperty("router-check.service", "ActiveState") == "activating" } // checkSettings starts a check unless one runs or the last result is for the // current settings, and waits a while for it. func (a *app) checkSettings() (any, error) { data, err := os.ReadFile(a.sys.settingsPath()) if err != nil { return nil, err } current := etag(data) if r, ok := a.sys.readCheck(); ok && r.Etag == current && !a.sys.checkRunning() { return a.checkReport(r, true), nil } if !a.sys.checkRunning() { if _, err := command("systemctl", "start", "--no-block", "router-check.service"); err != nil { return nil, err } } for range 50 { time.Sleep(time.Second) if a.sys.checkRunning() { continue } if r, ok := a.sys.readCheck(); ok { return a.checkReport(r, r.Etag == current), nil } break } return CheckResponse{Status: "running", Next: "Still evaluating; call check_settings again in a few seconds."}, nil } func (a *app) checkReport(r CheckResult, current bool) CheckResponse { out := CheckResponse{Status: "done", OK: &r.OK, ForTheSavedSettings: ¤t, CheckedAt: &r.Time} if r.OK { out.Message = "The settings evaluate: the router's assertions pass." } else { out.Error = r.Error } if !current { out.Note = "The settings changed during or after this check; call check_settings again." } return out }