Files
elmprodvpn/selective-vpn-api/app/routes_handlers_ops_core.go

103 lines
2.6 KiB
Go

package app
import (
"encoding/json"
"fmt"
"os"
"strings"
"syscall"
"time"
)
// ---------------------------------------------------------------------
// EN: `routesClear` contains core logic for routes clear.
// RU: `routesClear` - содержит основную логику для routes clear.
// ---------------------------------------------------------------------
func routesClear() cmdResult {
return withRoutesOpLock("routes clear", routesClearUnlocked)
}
func routesClearUnlocked() cmdResult {
cacheMeta, cacheErr := saveRoutesClearCache()
stdout, stderr, _, err := runCommand("ip", "rule", "show")
if err == nil && stdout != "" {
removeTrafficRulesForTable()
}
_, _, _, _ = runCommand("ip", "route", "flush", "table", routesTableName())
_, _, _, _ = runCommand("nft", "flush", "set", "inet", "agvpn", "agvpn4")
_, _, _, _ = runCommand("nft", "flush", "set", "inet", "agvpn", "agvpn_dyn4")
iface := strings.TrimSpace(cacheMeta.Iface)
if iface == "" {
iface, _ = resolveTrafficIface(loadTrafficModeState().PreferredIface)
}
_ = writeStatusSnapshot(0, iface)
res := cmdResult{
OK: true,
Message: "routes cleared",
ExitCode: 0,
Stdout: stdout,
Stderr: stderr,
}
if cacheErr != nil {
res.Message = fmt.Sprintf("%s (cache warning: %v)", res.Message, cacheErr)
} else {
res.Message = fmt.Sprintf(
"%s (cache saved: agvpn4=%d agvpn_dyn4=%d routes=%d iface=%s at=%s)",
res.Message,
cacheMeta.IPCount,
cacheMeta.DynIPCount,
cacheMeta.RouteCount,
ifaceOrDash(cacheMeta.Iface),
cacheMeta.CreatedAt,
)
}
return res
}
func withRoutesOpLock(opName string, fn func() cmdResult) cmdResult {
lock, err := os.OpenFile(lockFile, os.O_CREATE|os.O_RDWR, 0o644)
if err != nil {
return cmdResult{
OK: false,
Message: fmt.Sprintf("%s lock open error: %v", opName, err),
}
}
defer lock.Close()
if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
return cmdResult{
OK: false,
Message: fmt.Sprintf("%s skipped: routes operation already running", opName),
}
}
defer syscall.Flock(int(lock.Fd()), syscall.LOCK_UN)
return fn()
}
func writeStatusSnapshot(ipCount int, iface string) error {
if ipCount < 0 {
ipCount = 0
}
iface = strings.TrimSpace(iface)
if iface == "" {
iface = "-"
}
st := Status{
Timestamp: time.Now().UTC().Format(time.RFC3339),
IPCount: ipCount,
DomainCount: countDomainsFromMap(lastIPsMapPath),
Iface: iface,
Table: routesTableName(),
Mark: MARK,
}
data, err := json.MarshalIndent(st, "", " ")
if err != nil {
return err
}
return os.WriteFile(statusFilePath, data, 0o644)
}