feat(frpAct): add Windows/Systemd/Init.d service boot control. BUT WINDOWS STILL HAS BUGS AND LINUX HAS NOT BEEN TESTED.
This commit is contained in:
@@ -7,9 +7,9 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"super-frpc/postLog"
|
||||
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
func GetConfigDir() (string, error) {
|
||||
@@ -30,7 +30,7 @@ func GetFrpcPath() (string, error) {
|
||||
return config.FrpcPath, nil
|
||||
}
|
||||
|
||||
func detectInitSystem() string {
|
||||
func GetInitSystem() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return "windows"
|
||||
}
|
||||
@@ -46,9 +46,9 @@ func detectInitSystem() string {
|
||||
}
|
||||
|
||||
func createBootService(username, instanceName, configPath, runUser string) error {
|
||||
initSystem := detectInitSystem()
|
||||
initType := GetInitSystem()
|
||||
|
||||
switch initSystem {
|
||||
switch initType {
|
||||
case "systemd":
|
||||
return createSystemdService(username, instanceName, configPath, runUser)
|
||||
case "init.d":
|
||||
@@ -240,25 +240,22 @@ func createWindowsBootService(username, instanceName, configPath string) error {
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
||||
command := fmt.Sprintf("\"%s\" -c \"%s\"", frpcPath, configPath)
|
||||
|
||||
key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows\CurrentVersion\Run`, registry.WRITE|registry.WOW64_64KEY)
|
||||
cmd := exec.Command("sc", "create", serviceName, "binPath=", command, "start=", "auto")
|
||||
output, err := cmd.CombinedOutput()
|
||||
postLog.Debug(fmt.Sprintf("[createWindowsBootService] Sc create output: %s", output))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open registry key: %w", err)
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
if err := key.SetStringValue(serviceName, command); err != nil {
|
||||
return fmt.Errorf("failed to set registry value: %w", err)
|
||||
return fmt.Errorf("failed to create Windows service: %s, output: %s", err, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeBootService(username, instanceName string) error {
|
||||
initSystem := detectInitSystem()
|
||||
initType := GetInitSystem()
|
||||
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
||||
|
||||
switch initSystem {
|
||||
switch initType {
|
||||
case "systemd":
|
||||
cmd := exec.Command("systemctl", "disable", serviceName)
|
||||
cmd.CombinedOutput()
|
||||
@@ -277,18 +274,250 @@ func removeBootService(username, instanceName string) error {
|
||||
os.Remove(servicePath)
|
||||
|
||||
case "windows":
|
||||
key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows\CurrentVersion\Run`, registry.WRITE|registry.WOW64_64KEY)
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[removeBootService] Failed to open registry key: %v", err))
|
||||
} else {
|
||||
defer key.Close()
|
||||
if err := key.DeleteValue(serviceName); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[removeBootService] Failed to delete registry value: %v", err))
|
||||
}
|
||||
cmd := exec.Command("sc", "delete", serviceName)
|
||||
postLog.Debug(fmt.Sprintf("[removeBootService] Sc delete command: %s", cmd.String()))
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[removeBootService] Failed to delete Windows service: %s, output: %s", err, output))
|
||||
}
|
||||
default:
|
||||
postLog.Error(fmt.Sprintf("[removeBootService] Unsupported init system: %s", initSystem))
|
||||
postLog.Error(fmt.Sprintf("[removeBootService] Unsupported init system: %s", initType))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsInstanceRunning(instanceName string) bool {
|
||||
initType := GetInitSystem()
|
||||
switch initType {
|
||||
case "windows":
|
||||
cmd := exec.Command("sc", "query", instanceName)
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
postLog.Debug(fmt.Sprintf("[IsInstanceRunning] Sc query output: %s", output))
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[IsInstanceRunning] Failed to check Windows service status: %s, output: %s", err, output))
|
||||
return false
|
||||
}
|
||||
|
||||
outputStr := string(output)
|
||||
if strings.Contains(outputStr, "RUNNING") {
|
||||
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Windows service %s is running", instanceName))
|
||||
return true
|
||||
} else {
|
||||
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Windows service %s is stopped", instanceName))
|
||||
return false
|
||||
}
|
||||
|
||||
case "systemd":
|
||||
cmd := exec.Command("systemctl", "is-active", instanceName)
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[IsInstanceRunning] Failed to check systemd service status: %s, output: %s", err, output))
|
||||
return false
|
||||
}
|
||||
|
||||
status := strings.TrimSpace(string(output))
|
||||
if status == "active" {
|
||||
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Systemd service %s is running", instanceName))
|
||||
return true
|
||||
} else {
|
||||
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Systemd service %s is stopped", instanceName))
|
||||
return false
|
||||
}
|
||||
|
||||
case "init.d":
|
||||
servicePath := fmt.Sprintf("/etc/init.d/%s", instanceName)
|
||||
if _, err := os.Stat(servicePath); err != nil {
|
||||
postLog.Info(fmt.Sprintf("[IsInstanceRunning] Init.d service %s not found", instanceName))
|
||||
return false
|
||||
}
|
||||
|
||||
cmd := exec.Command(servicePath, "status")
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[IsInstanceRunning] Failed to check init.d service status: %s, output: %s", err, output))
|
||||
return false
|
||||
}
|
||||
|
||||
outputStr := strings.TrimSpace(string(output))
|
||||
if strings.Contains(outputStr, "is running") {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
default:
|
||||
postLog.Error(fmt.Sprintf("[IsInstanceRunning] Unsupported init system: %s", initType))
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func GetInstancePid(instanceName string) int {
|
||||
initType := GetInitSystem()
|
||||
switch initType {
|
||||
case "windows":
|
||||
cmd := exec.Command("sc", "query", instanceName, "info")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetInstancePid] Failed to get Windows service info: %s, output: %s", err, output))
|
||||
return 0
|
||||
}
|
||||
outputStr := string(output)
|
||||
lines := strings.Split(outputStr, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "PID") {
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) >= 2 {
|
||||
pidStr := strings.TrimSpace(parts[1])
|
||||
pid, err := strconv.Atoi(pidStr)
|
||||
if err == nil {
|
||||
return pid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
case "systemd":
|
||||
cmd := exec.Command("systemctl", "show", instanceName, "--property", "MainPID")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetInstancePid] Failed to get PID from systemd: %s, output: %s", err, output))
|
||||
return 0
|
||||
}
|
||||
outputStr := strings.TrimSpace(string(output))
|
||||
parts := strings.Split(outputStr, "=")
|
||||
if len(parts) >= 2 {
|
||||
pidStr := strings.TrimSpace(parts[1])
|
||||
if pidStr != "" && pidStr != "0" {
|
||||
pid, err := strconv.Atoi(pidStr)
|
||||
if err == nil {
|
||||
return pid
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
case "init.d":
|
||||
servicePath := fmt.Sprintf("/etc/init.d/%s", instanceName)
|
||||
if _, err := os.Stat(servicePath); err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetInstancePid] Init.d service %s not found", instanceName))
|
||||
return 0
|
||||
}
|
||||
cmd := exec.Command(servicePath, "status")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetInstancePid] Failed to get init.d service status: %s, output: %s", err, output))
|
||||
return 0
|
||||
}
|
||||
outputStr := strings.TrimSpace(string(output))
|
||||
if strings.Contains(outputStr, "is running") {
|
||||
if strings.Contains(outputStr, "pid") {
|
||||
parts := strings.Split(outputStr, "pid")
|
||||
if len(parts) >= 2 {
|
||||
pidStr := strings.TrimSpace(strings.Split(parts[1], ")")[0])
|
||||
pid, err := strconv.Atoi(pidStr)
|
||||
if err == nil {
|
||||
return pid
|
||||
}
|
||||
}
|
||||
}
|
||||
pidFile := fmt.Sprintf("/var/run/%s.pid", instanceName)
|
||||
if content, err := os.ReadFile(pidFile); err == nil {
|
||||
pidStr := strings.TrimSpace(string(content))
|
||||
pid, err := strconv.Atoi(pidStr)
|
||||
if err == nil {
|
||||
return pid
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
default:
|
||||
postLog.Error(fmt.Sprintf("[GetInstancePid] Unsupported init system: %s", initType))
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func StartWindowsService(instanceName string) error {
|
||||
cmd := exec.Command("sc", "start", instanceName)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start Windows service %s: %s, output: %s", instanceName, err, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartSystemdService(instanceName string) error {
|
||||
cmd := exec.Command("systemctl", "start", instanceName)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start systemd service %s: %s, output: %s", instanceName, err, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartInitDService(instanceName string) error {
|
||||
servicePath := fmt.Sprintf("/etc/init.d/%s", instanceName)
|
||||
cmd := exec.Command(servicePath, "start")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start init.d service %s: %s, output: %s", instanceName, err, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StopWindowsService(instanceName string) error {
|
||||
cmd := exec.Command("sc", "stop", instanceName)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stop Windows service %s: %s, output: %s", instanceName, err, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StopSystemdService(instanceName string) error {
|
||||
cmd := exec.Command("systemctl", "stop", instanceName)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stop systemd service %s: %s, output: %s", instanceName, err, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StopInitDService(instanceName string) error {
|
||||
servicePath := fmt.Sprintf("/etc/init.d/%s", instanceName)
|
||||
cmd := exec.Command(servicePath, "stop")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stop init.d service %s: %s, output: %s", instanceName, err, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RestartWindowsService(instanceName string) error {
|
||||
cmd := exec.Command("sc", "restart", instanceName)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to restart Windows service %s: %s, output: %s", instanceName, err, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RestartSystemdService(instanceName string) error {
|
||||
cmd := exec.Command("systemctl", "restart", instanceName)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to restart systemd service %s: %s, output: %s", instanceName, err, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RestartInitDService(instanceName string) error {
|
||||
servicePath := fmt.Sprintf("/etc/init.d/%s", instanceName)
|
||||
cmd := exec.Command(servicePath, "restart")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to restart init.d service %s: %s, output: %s", instanceName, err, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user