79 lines
2.4 KiB
Go
79 lines
2.4 KiB
Go
// SPDX-License-Identifier: MIT
|
|
|
|
// Package state persists the runtime snapshot prx needs to describe and undo
|
|
// the currently applied routing configuration.
|
|
package state
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"prx/internal/fsx"
|
|
)
|
|
|
|
// State is the on-disk snapshot.
|
|
type State struct {
|
|
Enabled bool `json:"enabled"`
|
|
WANInterface string `json:"wan_interface"`
|
|
Gateway4 string `json:"gateway_v4,omitempty"`
|
|
Gateway6 string `json:"gateway_v6,omitempty"`
|
|
Mark uint32 `json:"mark"`
|
|
TableV4 int `json:"table_v4"`
|
|
TableV6 int `json:"table_v6"`
|
|
PriorityV4 int `json:"priority_v4"`
|
|
PriorityV6 int `json:"priority_v6"`
|
|
BypassTableV4 int `json:"bypass_table_v4"`
|
|
BypassTableV6 int `json:"bypass_table_v6"`
|
|
BypassPrioV4 int `json:"bypass_priority_v4"`
|
|
BypassPrioV6 int `json:"bypass_priority_v6"`
|
|
BypassCIDRs []string `json:"bypass_cidrs,omitempty"`
|
|
EngineManaged bool `json:"engine_managed"`
|
|
EngineWasRunning bool `json:"engine_was_running"`
|
|
EnabledAt time.Time `json:"enabled_at"`
|
|
Version string `json:"version,omitempty"`
|
|
}
|
|
|
|
// Load reads the state. A missing file yields a zero (disabled) state.
|
|
//
|
|
// The returned state is never nil, even alongside an error, so callers cannot
|
|
// dereference a nil pointer after ignoring a read failure.
|
|
func Load(path string) (*State, error) {
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return &State{}, nil
|
|
}
|
|
return &State{}, fmt.Errorf("read state %s: %w", path, err)
|
|
}
|
|
var s State
|
|
if err := json.Unmarshal(raw, &s); err != nil {
|
|
return &State{}, fmt.Errorf("parse state %s: %w", path, err)
|
|
}
|
|
return &s, nil
|
|
}
|
|
|
|
// Save writes the state atomically, creating the parent directory.
|
|
func Save(path string, s *State) error {
|
|
raw, err := json.MarshalIndent(s, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("encode state: %w", err)
|
|
}
|
|
raw = append(raw, '\n')
|
|
if err := fsx.WriteFileAtomic(path, raw, 0o644); err != nil {
|
|
return fmt.Errorf("write state %s: %w", path, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Remove deletes the state file. A missing file is not an error.
|
|
func Remove(path string) error {
|
|
err := os.Remove(path)
|
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|