refactor(service): move boot service functions to os.go
Extract service management functions from frpAct.go to new os.go file for better code organization and maintainability. Also update README.md to document supported platforms.
This commit is contained in:
@@ -10,6 +10,11 @@ A backend application for managing local frpc instances, allowing users to easil
|
||||
- User permission management (superuser/admin/visitor)
|
||||
- SQLite database for data persistence
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
- `GNU/Linux`
|
||||
- `Windows`
|
||||
|
||||
## Configuration
|
||||
|
||||
Create a `config.json` file in the project root:
|
||||
|
||||
241
frpAct.go
241
frpAct.go
@@ -3,12 +3,10 @@ package main
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"super-frpc/postLog"
|
||||
@@ -679,245 +677,6 @@ func addFrpcProxy(info FrpcProxyInfo) string {
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func GetConfigDir() (string, error) {
|
||||
config, err := GetConfig()
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetConfigDir] Failed to get config: %v", err))
|
||||
return "", err
|
||||
}
|
||||
return config.InstancePath, nil
|
||||
}
|
||||
|
||||
func GetFrpcPath() (string, error) {
|
||||
config, err := GetConfig()
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetFrpcPath] Failed to get config: %v", err))
|
||||
return "", err
|
||||
}
|
||||
return config.FrpcPath, nil
|
||||
}
|
||||
|
||||
func detectInitSystem() string {
|
||||
if _, err := os.Stat("/run/systemd/system"); err == nil {
|
||||
return "systemd"
|
||||
}
|
||||
if _, err := os.Stat("/etc/init.d"); err == nil {
|
||||
return "init.d"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func createBootService(username, instanceName, configPath, runUser string) error {
|
||||
initSystem := detectInitSystem()
|
||||
|
||||
switch initSystem {
|
||||
case "systemd":
|
||||
return createSystemdService(username, instanceName, configPath, runUser)
|
||||
case "init.d":
|
||||
return createInitDService(username, instanceName, configPath, runUser)
|
||||
default:
|
||||
return errors.New("unsupported init system")
|
||||
}
|
||||
}
|
||||
|
||||
func createSystemdService(username, instanceName, configPath, runUser string) error {
|
||||
frpcPath, err := GetFrpcPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
||||
serviceContent := fmt.Sprintf(`[Unit]
|
||||
Description=superfrpc_%s_%s
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=%s -c %s
|
||||
User=%s
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`, username, instanceName, frpcPath, configPath, runUser)
|
||||
|
||||
servicePath := filepath.Join("/etc/systemd/system", serviceName+".service")
|
||||
if err := os.WriteFile(servicePath, []byte(serviceContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to create systemd service file: %w", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command("systemctl", "enable", serviceName)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
os.Remove(servicePath)
|
||||
return fmt.Errorf("failed to enable service: %s, output: %s", err, output)
|
||||
}
|
||||
|
||||
cmd = exec.Command("systemctl", "daemon-reload")
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to reload daemon: %s, output: %s", err, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createInitDService(username, instanceName, configPath, runUser string) error {
|
||||
frpcPath, err := GetFrpcPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
||||
|
||||
var serviceContent string
|
||||
runUserArg := ""
|
||||
if runUser != "" && runUser != "root" {
|
||||
runUserArg = runUser
|
||||
serviceContent = fmt.Sprintf(`#!/bin/sh
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: %s
|
||||
# Required-Start: $network $remote_fs $syslog
|
||||
# Required-Stop: $network $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Description: superfrpc %s %s
|
||||
### END INIT INFO
|
||||
|
||||
NAME="%s"
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||
DAEMON=%s
|
||||
DAEMON_ARGS="-c %s"
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
USER=%s
|
||||
|
||||
[ -x "$DAEMON" ] || exit 0
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n "Starting $NAME: "
|
||||
start-stop-daemon -S -c $USER -b -m -p /var/run/$NAME.pid --start --exec $DAEMON -- $DAEMON_ARGS
|
||||
echo "$NAME."
|
||||
;;
|
||||
stop)
|
||||
echo -n "Stopping $NAME: "
|
||||
start-stop-daemon -K -p /var/run/$NAME.pid
|
||||
rm -f /var/run/$NAME.pid
|
||||
echo "$NAME."
|
||||
;;
|
||||
restart)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
status)
|
||||
if [ -f /var/run/$NAME.pid ]; then
|
||||
echo "$NAME is running (pid $(cat /var/run/$NAME.pid))"
|
||||
else
|
||||
echo "$NAME is not running"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
`, serviceName, username, instanceName, serviceName, frpcPath, configPath, runUserArg)
|
||||
} else {
|
||||
serviceContent = fmt.Sprintf(`#!/bin/sh
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: %s
|
||||
# Required-Start: $network $remote_fs $syslog
|
||||
# Required-Stop: $network $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Description: superfrpc %s %s
|
||||
### END INIT INFO
|
||||
|
||||
NAME="%s"
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||
DAEMON=%s
|
||||
DAEMON_ARGS="-c %s"
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
|
||||
[ -x "$DAEMON" ] || exit 0
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n "Starting $NAME: "
|
||||
start-stop-daemon -S -b -m -p /var/run/$NAME.pid --start --exec $DAEMON -- $DAEMON_ARGS
|
||||
echo "$NAME."
|
||||
;;
|
||||
stop)
|
||||
echo -n "Stopping $NAME: "
|
||||
start-stop-daemon -K -p /var/run/$NAME.pid
|
||||
rm -f /var/run/$NAME.pid
|
||||
echo "$NAME."
|
||||
;;
|
||||
restart)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
status)
|
||||
if [ -f /var/run/$NAME.pid ]; then
|
||||
echo "$NAME is running (pid $(cat /var/run/$NAME.pid))"
|
||||
else
|
||||
echo "$NAME is not running"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
`, serviceName, username, instanceName, serviceName, frpcPath, configPath)
|
||||
}
|
||||
|
||||
servicePath := filepath.Join("/etc/init.d", serviceName)
|
||||
if err := os.WriteFile(servicePath, []byte(serviceContent), 0755); err != nil {
|
||||
return fmt.Errorf("failed to create init.d service file: %w", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command("/etc/init.d", serviceName, "enable")
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
os.Remove(servicePath)
|
||||
return fmt.Errorf("failed to enable service: %s, output: %s", err, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeBootService(username, instanceName string) error {
|
||||
initSystem := detectInitSystem()
|
||||
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
||||
|
||||
switch initSystem {
|
||||
case "systemd":
|
||||
cmd := exec.Command("systemctl", "disable", serviceName)
|
||||
cmd.CombinedOutput()
|
||||
|
||||
servicePath := filepath.Join("/etc/systemd/system", serviceName+".service")
|
||||
os.Remove(servicePath)
|
||||
|
||||
cmd = exec.Command("systemctl", "daemon-reload")
|
||||
cmd.CombinedOutput()
|
||||
|
||||
case "init.d":
|
||||
cmd := exec.Command("/etc/init.d", serviceName, "disable")
|
||||
cmd.CombinedOutput()
|
||||
|
||||
servicePath := filepath.Join("/etc/init.d", serviceName)
|
||||
os.Remove(servicePath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetUserInstances(userID int) ([]FrpcInstance, error) {
|
||||
rows, err := frpcDB.Query(`
|
||||
SELECT id, userID, name, serverAddr, serverPort, auth_method, bootAtStart, runUser, configPath, createdAt
|
||||
|
||||
249
os.go
Normal file
249
os.go
Normal file
@@ -0,0 +1,249 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"super-frpc/postLog"
|
||||
)
|
||||
|
||||
func GetConfigDir() (string, error) {
|
||||
config, err := GetConfig()
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetConfigDir] Failed to get config: %v", err))
|
||||
return "", err
|
||||
}
|
||||
return config.InstancePath, nil
|
||||
}
|
||||
|
||||
func GetFrpcPath() (string, error) {
|
||||
config, err := GetConfig()
|
||||
if err != nil {
|
||||
postLog.Error(fmt.Sprintf("[GetFrpcPath] Failed to get config: %v", err))
|
||||
return "", err
|
||||
}
|
||||
return config.FrpcPath, nil
|
||||
}
|
||||
|
||||
func detectInitSystem() string {
|
||||
if _, err := os.Stat("/run/systemd/system"); err == nil {
|
||||
return "systemd"
|
||||
}
|
||||
if _, err := os.Stat("/etc/init.d"); err == nil {
|
||||
return "init.d"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func createBootService(username, instanceName, configPath, runUser string) error {
|
||||
initSystem := detectInitSystem()
|
||||
|
||||
switch initSystem {
|
||||
case "systemd":
|
||||
return createSystemdService(username, instanceName, configPath, runUser)
|
||||
case "init.d":
|
||||
return createInitDService(username, instanceName, configPath, runUser)
|
||||
default:
|
||||
return errors.New("unsupported init system")
|
||||
}
|
||||
}
|
||||
|
||||
func createSystemdService(username, instanceName, configPath, runUser string) error {
|
||||
frpcPath, err := GetFrpcPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
||||
serviceContent := fmt.Sprintf(`[Unit]
|
||||
Description=superfrpc_%s_%s
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=%s -c %s
|
||||
User=%s
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`, username, instanceName, frpcPath, configPath, runUser)
|
||||
|
||||
servicePath := filepath.Join("/etc/systemd/system", serviceName+".service")
|
||||
if err := os.WriteFile(servicePath, []byte(serviceContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to create systemd service file: %w", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command("systemctl", "enable", serviceName)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
os.Remove(servicePath)
|
||||
return fmt.Errorf("failed to enable service: %s, output: %s", err, output)
|
||||
}
|
||||
|
||||
cmd = exec.Command("systemctl", "daemon-reload")
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to reload daemon: %s, output: %s", err, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createInitDService(username, instanceName, configPath, runUser string) error {
|
||||
frpcPath, err := GetFrpcPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
||||
|
||||
var serviceContent string
|
||||
runUserArg := ""
|
||||
if runUser != "" && runUser != "root" {
|
||||
runUserArg = runUser
|
||||
serviceContent = fmt.Sprintf(`#!/bin/sh
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: %s
|
||||
# Required-Start: $network $remote_fs $syslog
|
||||
# Required-Stop: $network $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Description: superfrpc %s %s
|
||||
### END INIT INFO
|
||||
|
||||
NAME="%s"
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||
DAEMON=%s
|
||||
DAEMON_ARGS="-c %s"
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
USER=%s
|
||||
|
||||
[ -x "$DAEMON" ] || exit 0
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n "Starting $NAME: "
|
||||
start-stop-daemon -S -c $USER -b -m -p /var/run/$NAME.pid --start --exec $DAEMON -- $DAEMON_ARGS
|
||||
echo "$NAME."
|
||||
;;
|
||||
stop)
|
||||
echo -n "Stopping $NAME: "
|
||||
start-stop-daemon -K -p /var/run/$NAME.pid
|
||||
rm -f /var/run/$NAME.pid
|
||||
echo "$NAME."
|
||||
;;
|
||||
restart)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
status)
|
||||
if [ -f /var/run/$NAME.pid ]; then
|
||||
echo "$NAME is running (pid $(cat /var/run/$NAME.pid))"
|
||||
else
|
||||
echo "$NAME is not running"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
`, serviceName, username, instanceName, serviceName, frpcPath, configPath, runUserArg)
|
||||
} else {
|
||||
serviceContent = fmt.Sprintf(`#!/bin/sh
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: %s
|
||||
# Required-Start: $network $remote_fs $syslog
|
||||
# Required-Stop: $network $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Description: superfrpc %s %s
|
||||
### END INIT INFO
|
||||
|
||||
NAME="%s"
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||
DAEMON=%s
|
||||
DAEMON_ARGS="-c %s"
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
|
||||
[ -x "$DAEMON" ] || exit 0
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n "Starting $NAME: "
|
||||
start-stop-daemon -S -b -m -p /var/run/$NAME.pid --start --exec $DAEMON -- $DAEMON_ARGS
|
||||
echo "$NAME."
|
||||
;;
|
||||
stop)
|
||||
echo -n "Stopping $NAME: "
|
||||
start-stop-daemon -K -p /var/run/$NAME.pid
|
||||
rm -f /var/run/$NAME.pid
|
||||
echo "$NAME."
|
||||
;;
|
||||
restart)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
status)
|
||||
if [ -f /var/run/$NAME.pid ]; then
|
||||
echo "$NAME is running (pid $(cat /var/run/$NAME.pid))"
|
||||
else
|
||||
echo "$NAME is not running"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
`, serviceName, username, instanceName, serviceName, frpcPath, configPath)
|
||||
}
|
||||
|
||||
servicePath := filepath.Join("/etc/init.d", serviceName)
|
||||
if err := os.WriteFile(servicePath, []byte(serviceContent), 0755); err != nil {
|
||||
return fmt.Errorf("failed to create init.d service file: %w", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command("/etc/init.d", serviceName, "enable")
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
os.Remove(servicePath)
|
||||
return fmt.Errorf("failed to enable service: %s, output: %s", err, output)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeBootService(username, instanceName string) error {
|
||||
initSystem := detectInitSystem()
|
||||
|
||||
serviceName := fmt.Sprintf("superfrpc_%s_%s", username, instanceName)
|
||||
|
||||
switch initSystem {
|
||||
case "systemd":
|
||||
cmd := exec.Command("systemctl", "disable", serviceName)
|
||||
cmd.CombinedOutput()
|
||||
|
||||
servicePath := filepath.Join("/etc/systemd/system", serviceName+".service")
|
||||
os.Remove(servicePath)
|
||||
|
||||
cmd = exec.Command("systemctl", "daemon-reload")
|
||||
cmd.CombinedOutput()
|
||||
|
||||
case "init.d":
|
||||
cmd := exec.Command("/etc/init.d", serviceName, "disable")
|
||||
cmd.CombinedOutput()
|
||||
|
||||
servicePath := filepath.Join("/etc/init.d", serviceName)
|
||||
os.Remove(servicePath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user