package app import ( "fmt" "strings" "time" ) type transportSystemdProvisionInputs struct { unit string primaryCmd string cmdSource string sshOverlay bool sshUnit string sshCmd string primaryTuning transportSystemdServiceTuning sshTuning transportSystemdServiceTuning primaryHardening transportSystemdHardening sshHardening transportSystemdHardening } func buildTransportSystemdProvisionInputs( client TransportClient, ) (transportSystemdProvisionInputs, []string, []string, *transportBackendActionResult) { unit := transportBackendUnit(client) if !validSystemdUnitName(unit) { res := transportBackendActionResult{ OK: false, Code: "TRANSPORT_BACKEND_UNIT_REQUIRED", Message: "valid systemd unit is required", ExitCode: -1, } return transportSystemdProvisionInputs{}, nil, nil, &res } preOut, preErr, failure := runTransportSystemdProvisionPreflight(client) if failure != nil { return transportSystemdProvisionInputs{}, preOut, preErr, failure } primaryCmd, cmdSource, err := resolveTransportPrimaryExecStart(client) if err != nil { res := transportBackendActionResult{ OK: false, Code: "TRANSPORT_BACKEND_PROVISION_CONFIG_REQUIRED", Message: err.Error(), ExitCode: -1, } return transportSystemdProvisionInputs{}, preOut, preErr, &res } if transportNetnsEnabled(client) { primaryCmd = transportWrapExecWithNetns(client, primaryCmd) } sshOverlay := transportDNSTTSSHTunnelEnabled(client) sshUnit := strings.TrimSpace(transportDNSTTSSHUnit(client)) sshCmd := strings.TrimSpace(transportConfigString(client.Config, "ssh_exec_start")) if sshOverlay && !validSystemdUnitName(sshUnit) { res := transportBackendActionResult{ OK: false, Code: "TRANSPORT_BACKEND_PROVISION_CONFIG_REQUIRED", Message: "valid config.ssh_unit is required when ssh overlay enabled", ExitCode: -1, } return transportSystemdProvisionInputs{}, preOut, preErr, &res } if sshOverlay && sshCmd == "" { overlayCmd, overlayErr := buildTransportSSHOverlayCommand(client.Config) if overlayErr != nil { res := transportBackendActionResult{ OK: false, Code: "TRANSPORT_BACKEND_PROVISION_CONFIG_REQUIRED", Message: overlayErr.Error(), ExitCode: -1, } return transportSystemdProvisionInputs{}, preOut, preErr, &res } sshCmd = overlayCmd } in := transportSystemdProvisionInputs{ unit: unit, primaryCmd: primaryCmd, cmdSource: cmdSource, sshOverlay: sshOverlay, sshUnit: sshUnit, sshCmd: sshCmd, primaryTuning: transportSystemdServiceTuningFromConfig(client.Config, ""), sshTuning: transportSystemdServiceTuningFromConfig(client.Config, "ssh_"), primaryHardening: transportSystemdHardeningFromConfig(client.Config, ""), sshHardening: transportSystemdHardeningFromConfig(client.Config, "ssh_"), } return in, preOut, preErr, nil } func runTransportSystemdProvisionPreflight( client TransportClient, ) ([]string, []string, *transportBackendActionResult) { preOut := make([]string, 0, 2) preErr := make([]string, 0, 2) if client.Kind != TransportClientSingBox { return preOut, preErr, nil } migMsg, migErr := transportMaybeMigrateSingBoxDNSConfig(client) if s := strings.TrimSpace(migMsg); s != "" { preOut = append(preOut, "dns-migrate: "+s) appendTraceLine("transport", fmt.Sprintf("singbox dns migrate: client=%s %s", client.ID, s)) } if migErr == nil { return preOut, preErr, nil } preErr = append(preErr, "dns-migrate: "+migErr.Error()) appendTraceLineRateLimited("transport", fmt.Sprintf("singbox dns migrate warning: client=%s err=%v", client.ID, migErr), 60*time.Second) if transportSingBoxDNSMigrationStrict(client) { res := transportBackendActionResult{ OK: false, Code: "TRANSPORT_BACKEND_SINGBOX_DNS_MIGRATE_FAILED", Message: migErr.Error(), ExitCode: -1, Stdout: strings.Join(preOut, "\n"), Stderr: strings.Join(preErr, "\n"), } return preOut, preErr, &res } return preOut, preErr, nil }