package app import ( "fmt" "strings" "time" ) func transportSystemdRunActionUnits( client TransportClient, action string, units []string, aggOut *[]string, aggErr *[]string, ) *transportBackendActionResult { for _, unit := range units { stdout, stderr, exitCode, err := transportRunCommand(transportBackendActionTimeout, "systemctl", action, unit) if s := strings.TrimSpace(stdout); s != "" { *aggOut = append(*aggOut, unit+": "+s) } if s := strings.TrimSpace(stderr); s != "" { *aggErr = append(*aggErr, unit+": "+s) } if (action == "start" || action == "restart") && client.Kind == TransportClientSingBox && transportSystemdUnitMissingError(stdout, stderr, exitCode, err) { appendTraceLineRateLimited( "transport", fmt.Sprintf("systemctl %s unit missing, trying auto-provision: client=%s unit=%s", action, client.ID, unit), 20*time.Second, ) provision := (transportSystemdBackend{}).Provision(client) if s := strings.TrimSpace(provision.Stdout); s != "" { *aggOut = append(*aggOut, "provision: "+s) } if s := strings.TrimSpace(provision.Stderr); s != "" { *aggErr = append(*aggErr, "provision: "+s) } if !provision.OK { msg := strings.TrimSpace(provision.Message) if msg == "" { msg = "auto-provision failed" } return &transportBackendActionResult{ OK: false, Code: provision.Code, Message: msg, ExitCode: provision.ExitCode, Stdout: strings.Join(*aggOut, "\n"), Stderr: strings.Join(*aggErr, "\n"), Retryable: true, } } stdout, stderr, exitCode, err = transportRunCommand(transportBackendActionTimeout, "systemctl", action, unit) if s := strings.TrimSpace(stdout); s != "" { *aggOut = append(*aggOut, unit+": "+s) } if s := strings.TrimSpace(stderr); s != "" { *aggErr = append(*aggErr, unit+": "+s) } } if action == "stop" && transportSystemdStopMissingUnitNoop(stdout, stderr, exitCode, err) { appendTraceLineRateLimited( "transport", fmt.Sprintf("systemctl stop noop: client=%s unit=%s reason=unit-missing", client.ID, unit), 15*time.Second, ) continue } if err != nil || exitCode != 0 { msg := strings.TrimSpace(stderr) if msg == "" { msg = strings.TrimSpace(stdout) } if msg == "" { msg = fmt.Sprintf("systemctl %s %s failed", action, unit) } return &transportBackendActionResult{ OK: false, Code: "TRANSPORT_BACKEND_ACTION_FAILED", Message: msg, ExitCode: exitCode, Stdout: strings.Join(*aggOut, "\n"), Stderr: strings.Join(*aggErr, "\n"), Retryable: action != "stop", } } } return nil }