103 lines
2.6 KiB
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)
|
|
}
|