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

174 lines
5.0 KiB
Go

package app
import (
"fmt"
"os"
"strings"
)
func (transportSystemdBackend) Cleanup(client TransportClient) transportBackendActionResult {
unit := transportBackendUnit(client)
if !validSystemdUnitName(unit) {
return transportBackendActionResult{
OK: false,
Code: "TRANSPORT_BACKEND_UNIT_REQUIRED",
Message: "valid systemd unit is required for cleanup",
ExitCode: -1,
}
}
units := []string{unit}
if transportSystemdSingBoxUsesTemplateInstance(client, unit) {
units = transportSystemdAppendUniqueUnits(units, transportSystemdLegacySingBoxUnitCandidates(client, unit))
}
if transportDNSTTSSHTunnelEnabled(client) {
sshUnit := transportDNSTTSSHUnit(client)
if !validSystemdUnitName(sshUnit) {
return transportBackendActionResult{
OK: false,
Code: "TRANSPORT_BACKEND_UNIT_REQUIRED",
Message: "valid config.ssh_unit is required for cleanup",
ExitCode: -1,
}
}
units = append(units, sshUnit)
}
ownedUnits := make([]string, 0, len(units))
out := make([]string, 0, len(units)*2)
errOut := make([]string, 0, len(units)*2)
for _, u := range units {
path := transportSystemdUnitPath(u)
dropInMode := false
dropInDir := ""
if transportSystemdSingBoxUsesTemplateInstance(client, u) {
path = transportSystemdUnitDropInPath(u, transportSingBoxInstanceDropIn)
dropInMode = true
dropInDir = transportSystemdUnitDropInDir(u)
}
owned, err := transportSystemdUnitOwnedByClient(path, client.ID)
if err != nil {
if os.IsNotExist(err) {
continue
}
errOut = append(errOut, fmt.Sprintf("%s ownership check failed: %v", u, err))
continue
}
if !owned {
// Safety: never touch units we did not provision for this client id.
continue
}
ownedUnits = append(ownedUnits, u)
stdout, stderr, code, runErr := transportRunCommand(transportBackendActionTimeout, "systemctl", "stop", u)
if s := strings.TrimSpace(stdout); s != "" {
out = append(out, u+" stop: "+s)
}
if s := strings.TrimSpace(stderr); s != "" {
errOut = append(errOut, u+" stop: "+s)
}
if runErr != nil || code != 0 {
errOut = append(errOut, fmt.Sprintf("%s stop failed (exit=%d)", u, code))
}
stdout, stderr, code, runErr = transportRunCommand(transportBackendActionTimeout, "systemctl", "disable", u)
if s := strings.TrimSpace(stdout); s != "" {
out = append(out, u+" disable: "+s)
}
if s := strings.TrimSpace(stderr); s != "" {
errOut = append(errOut, u+" disable: "+s)
}
if runErr != nil || code != 0 {
errOut = append(errOut, fmt.Sprintf("%s disable failed (exit=%d)", u, code))
}
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
errOut = append(errOut, fmt.Sprintf("%s remove failed: %v", u, err))
}
if dropInMode && strings.TrimSpace(dropInDir) != "" {
if err := os.Remove(dropInDir); err != nil && !os.IsNotExist(err) {
msg := strings.ToLower(strings.TrimSpace(err.Error()))
if !strings.Contains(msg, "directory not empty") {
errOut = append(errOut, fmt.Sprintf("%s drop-in dir remove failed: %v", u, err))
}
}
}
}
if len(ownedUnits) > 0 {
stdout, stderr, code, runErr := transportRunCommand(transportBackendActionTimeout, "systemctl", "daemon-reload")
if s := strings.TrimSpace(stdout); s != "" {
out = append(out, "daemon-reload: "+s)
}
if s := strings.TrimSpace(stderr); s != "" {
errOut = append(errOut, "daemon-reload: "+s)
}
if runErr != nil || code != 0 {
errOut = append(errOut, fmt.Sprintf("daemon-reload failed (exit=%d)", code))
}
for _, u := range ownedUnits {
_, stderr, code, runErr := transportRunCommand(transportBackendActionTimeout, "systemctl", "reset-failed", u)
if s := strings.TrimSpace(stderr); s != "" {
errOut = append(errOut, u+" reset-failed: "+s)
}
if runErr != nil || code != 0 {
errOut = append(errOut, fmt.Sprintf("%s reset-failed failed (exit=%d)", u, code))
}
}
}
if transportNetnsEnabled(client) {
msg, err := transportCleanupNetnsForClient(client)
if s := strings.TrimSpace(msg); s != "" {
out = append(out, "netns: "+s)
}
if err != nil {
errOut = append(errOut, "netns: "+err.Error())
}
}
msg := fmt.Sprintf("cleanup done for %d managed units", len(ownedUnits))
if len(ownedUnits) == 0 {
msg = "no managed unit artifacts found"
}
if len(errOut) > 0 {
return transportBackendActionResult{
OK: false,
Code: "TRANSPORT_BACKEND_CLEANUP_FAILED",
Message: msg,
ExitCode: -1,
Stdout: strings.Join(out, "\n"),
Stderr: strings.Join(errOut, "\n"),
Retryable: true,
}
}
return transportBackendActionResult{
OK: true,
ExitCode: 0,
Message: msg,
Stdout: strings.Join(out, "\n"),
}
}
func transportSystemdAppendUniqueUnits(dst []string, candidates []string) []string {
for _, candidate := range candidates {
u := strings.TrimSpace(candidate)
if u == "" {
continue
}
already := false
for _, existing := range dst {
if strings.EqualFold(strings.TrimSpace(existing), u) {
already = true
break
}
}
if !already {
dst = append(dst, u)
}
}
return dst
}