mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 10:27:26 +08:00
* initial auto-update * working link, update, and testing docker container and scripts * refactor packages/folders * move files to proper folders * fix typos Closes #421 * optimize rpm imports * optimize channel imports * Refactor split command to allow testing of split operations Closes #338 * modify split and test for folder changes * remove alias * fix docker warning about FROM and AS being in different letter case Closes #422 * QClient Account Command * Display transaction details and confirmation prompts for transfer and merge commands * build qclient docker improvements * update build args for mpfr.so.6 * update install and node commands * remove NodeConfig check for qclient node commands * udpate * working node commands * update commands * move utils and rename package --------- Co-authored-by: Vasyl Tretiakov <vasyl.tretiakov@gmail.com> Co-authored-by: littleblackcloud <163544315+littleblackcloud@users.noreply.github.com> Co-authored-by: 0xOzgur <29779769+0xOzgur@users.noreply.github.com> Co-authored-by: Cassandra Heart <7929478+CassOnMars@users.noreply.github.com>
494 lines
14 KiB
Go
494 lines
14 KiB
Go
package node
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"text/template"
|
|
|
|
"github.com/spf13/cobra"
|
|
"source.quilibrium.com/quilibrium/monorepo/client/utils"
|
|
)
|
|
|
|
// nodeServiceCmd represents the command to manage the Quilibrium node service
|
|
var nodeServiceCmd = &cobra.Command{
|
|
Use: "service [command]",
|
|
Short: "Manage the Quilibrium node service",
|
|
Long: `Manage the Quilibrium node service.
|
|
Available commands:
|
|
start Start the node service
|
|
stop Stop the node service
|
|
restart Restart the node service
|
|
status Check the status of the node service
|
|
enable Enable the node service to start on boot
|
|
disable Disable the node service from starting on boot
|
|
install Install the service for the current OS
|
|
|
|
Examples:
|
|
# Start the node service
|
|
qclient node service start
|
|
|
|
# Check service status
|
|
qclient node service status
|
|
|
|
# Enable service to start on boot
|
|
qclient node service enable`,
|
|
Args: cobra.ExactArgs(1),
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
command := args[0]
|
|
switch command {
|
|
case "start":
|
|
startService()
|
|
case "stop":
|
|
stopService()
|
|
case "restart":
|
|
restartService()
|
|
case "status":
|
|
checkServiceStatus()
|
|
case "enable":
|
|
enableService()
|
|
case "disable":
|
|
disableService()
|
|
case "reload":
|
|
reloadService()
|
|
case "install":
|
|
installService()
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "Unknown command: %s\n", command)
|
|
fmt.Fprintf(os.Stderr, "Available commands: start, stop, restart, status, enable, disable, reload, install\n")
|
|
os.Exit(1)
|
|
}
|
|
},
|
|
}
|
|
|
|
// installService installs the appropriate service configuration for the current OS
|
|
func installService() {
|
|
|
|
if err := utils.CheckAndRequestSudo("Installing service requires root privileges"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
return
|
|
}
|
|
|
|
fmt.Fprintf(os.Stdout, "Installing Quilibrium node service for %s...\n", OsType)
|
|
|
|
if OsType == "darwin" {
|
|
// launchctl is already installed on macOS by default, so no need to check for it
|
|
installMacOSService()
|
|
} else if OsType == "linux" {
|
|
// systemd is not installed on linux by default, so we need to check for it
|
|
if !utils.CheckForSystemd() {
|
|
// install systemd if not found
|
|
installSystemd()
|
|
}
|
|
if err := createSystemdServiceFile(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error creating systemd service file: %v\n", err)
|
|
return
|
|
}
|
|
} else {
|
|
fmt.Fprintf(os.Stderr, "Error: Unsupported operating system: %s\n", OsType)
|
|
return
|
|
}
|
|
|
|
fmt.Fprintf(os.Stdout, "Quilibrium node service installed successfully\n")
|
|
}
|
|
|
|
func installSystemd() {
|
|
fmt.Fprintf(os.Stdout, "Installing systemd...\n")
|
|
updateCmd := exec.Command("sudo", "apt-get", "update")
|
|
updateCmd.Stdout = nil
|
|
updateCmd.Stderr = nil
|
|
if err := updateCmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error updating package lists: %v\n", err)
|
|
return
|
|
}
|
|
|
|
installCmd := exec.Command("sudo", "apt-get", "install", "-y", "systemd")
|
|
installCmd.Stdout = nil
|
|
installCmd.Stderr = nil
|
|
if err := installCmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error installing systemd: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// startService starts the Quilibrium node service
|
|
func startService() {
|
|
if err := utils.CheckAndRequestSudo("Starting service requires root privileges"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
return
|
|
}
|
|
|
|
if OsType == "darwin" {
|
|
// MacOS launchd command
|
|
cmd := exec.Command("sudo", "launchctl", "start", fmt.Sprintf("com.quilibrium.%s", ServiceName))
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error starting service: %v\n", err)
|
|
return
|
|
}
|
|
} else {
|
|
// Linux systemd command
|
|
cmd := exec.Command("sudo", "systemctl", "start", ServiceName)
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error starting service: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(os.Stdout, "Started Quilibrium node service\n")
|
|
}
|
|
|
|
// stopService stops the Quilibrium node service
|
|
func stopService() {
|
|
if err := utils.CheckAndRequestSudo("Stopping service requires root privileges"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
return
|
|
}
|
|
|
|
if OsType == "darwin" {
|
|
// MacOS launchd command
|
|
cmd := exec.Command("sudo", "launchctl", "stop", fmt.Sprintf("com.quilibrium.%s", ServiceName))
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error stopping service: %v\n", err)
|
|
return
|
|
}
|
|
} else {
|
|
// Linux systemd command
|
|
cmd := exec.Command("sudo", "systemctl", "stop", ServiceName)
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error stopping service: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(os.Stdout, "Stopped Quilibrium node service\n")
|
|
}
|
|
|
|
// restartService restarts the Quilibrium node service
|
|
func restartService() {
|
|
if err := utils.CheckAndRequestSudo("Restarting service requires root privileges"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
return
|
|
}
|
|
|
|
if OsType == "darwin" {
|
|
// MacOS launchd command - stop then start
|
|
stopCmd := exec.Command("sudo", "launchctl", "stop", fmt.Sprintf("com.quilibrium.%s", ServiceName))
|
|
if err := stopCmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error stopping service: %v\n", err)
|
|
return
|
|
}
|
|
|
|
startCmd := exec.Command("sudo", "launchctl", "start", fmt.Sprintf("com.quilibrium.%s", ServiceName))
|
|
if err := startCmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error starting service: %v\n", err)
|
|
return
|
|
}
|
|
} else {
|
|
// Linux systemd command
|
|
cmd := exec.Command("sudo", "systemctl", "restart", ServiceName)
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error restarting service: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(os.Stdout, "Restarted Quilibrium node service\n")
|
|
}
|
|
|
|
// reloadService reloads the Quilibrium node service
|
|
func reloadService() {
|
|
if err := utils.CheckAndRequestSudo("Reloading service requires root privileges"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
return
|
|
}
|
|
|
|
if OsType == "darwin" {
|
|
// MacOS launchd command - unload then load
|
|
plistPath := fmt.Sprintf("/Library/LaunchDaemons/com.quilibrium.%s.plist", ServiceName)
|
|
unloadCmd := exec.Command("sudo", "launchctl", "unload", plistPath)
|
|
if err := unloadCmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error unloading service: %v\n", err)
|
|
return
|
|
}
|
|
|
|
loadCmd := exec.Command("sudo", "launchctl", "load", plistPath)
|
|
if err := loadCmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error loading service: %v\n", err)
|
|
return
|
|
}
|
|
|
|
fmt.Fprintf(os.Stdout, "Reloaded launchd service\n")
|
|
} else {
|
|
// Linux systemd command
|
|
cmd := exec.Command("sudo", "systemctl", "daemon-reload")
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error reloading service: %v\n", err)
|
|
return
|
|
}
|
|
|
|
fmt.Fprintf(os.Stdout, "Reloaded systemd service\n")
|
|
}
|
|
}
|
|
|
|
// checkServiceStatus checks the status of the Quilibrium node service
|
|
func checkServiceStatus() {
|
|
if err := utils.CheckAndRequestSudo("Checking service status requires root privileges"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
return
|
|
}
|
|
|
|
if OsType == "darwin" {
|
|
// MacOS launchd command
|
|
cmd := exec.Command("sudo", "launchctl", "list", fmt.Sprintf("com.quilibrium.%s", ServiceName))
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error checking service status: %v\n", err)
|
|
}
|
|
} else {
|
|
// Linux systemd command
|
|
cmd := exec.Command("sudo", "systemctl", "status", ServiceName)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error checking service status: %v\n", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// enableService enables the Quilibrium node service to start on boot
|
|
func enableService() {
|
|
if err := utils.CheckAndRequestSudo("Enabling service requires root privileges"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
return
|
|
}
|
|
|
|
if OsType == "darwin" {
|
|
// MacOS launchd command - load with -w flag to enable at boot
|
|
plistPath := fmt.Sprintf("/Library/LaunchDaemons/com.quilibrium.%s.plist", ServiceName)
|
|
cmd := exec.Command("sudo", "launchctl", "load", "-w", plistPath)
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error enabling service: %v\n", err)
|
|
return
|
|
}
|
|
} else {
|
|
// Linux systemd command
|
|
cmd := exec.Command("sudo", "systemctl", "enable", ServiceName)
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error enabling service: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(os.Stdout, "Enabled Quilibrium node service to start on boot\n")
|
|
}
|
|
|
|
// disableService disables the Quilibrium node service from starting on boot
|
|
func disableService() {
|
|
if err := utils.CheckAndRequestSudo("Disabling service requires root privileges"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
return
|
|
}
|
|
|
|
if OsType == "darwin" {
|
|
// MacOS launchd command - unload with -w flag to disable at boot
|
|
plistPath := fmt.Sprintf("/Library/LaunchDaemons/com.quilibrium.%s.plist", ServiceName)
|
|
cmd := exec.Command("sudo", "launchctl", "unload", "-w", plistPath)
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error disabling service: %v\n", err)
|
|
return
|
|
}
|
|
} else {
|
|
// Linux systemd command
|
|
cmd := exec.Command("sudo", "systemctl", "disable", ServiceName)
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error disabling service: %v\n", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
fmt.Fprintf(os.Stdout, "Disabled Quilibrium node service from starting on boot\n")
|
|
}
|
|
|
|
func createService() {
|
|
// Create systemd service file
|
|
if OsType == "linux" {
|
|
if err := createSystemdServiceFile(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Warning: Failed to create systemd service file: %v\n", err)
|
|
}
|
|
} else if OsType == "darwin" {
|
|
installMacOSService()
|
|
} else {
|
|
fmt.Fprintf(os.Stderr, "Warning: Background service file creation not supported on %s\n", OsType)
|
|
return
|
|
}
|
|
}
|
|
|
|
// createSystemdServiceFile creates the systemd service file with environment file support
|
|
func createSystemdServiceFile() error {
|
|
if !utils.CheckForSystemd() {
|
|
installSystemd()
|
|
}
|
|
|
|
// Check if we need sudo privileges
|
|
if err := utils.CheckAndRequestSudo("Creating systemd service file requires root privileges"); err != nil {
|
|
return fmt.Errorf("failed to get sudo privileges: %w", err)
|
|
}
|
|
|
|
// Create environment file content
|
|
envContent := `# Quilibrium Node Environment`
|
|
|
|
// Write environment file
|
|
envPath := filepath.Join(utils.RootQuilibriumPath, "quilibrium.env")
|
|
if err := os.WriteFile(envPath, []byte(envContent), 0640); err != nil {
|
|
return fmt.Errorf("failed to create environment file: %w", err)
|
|
}
|
|
|
|
// Set ownership of environment file
|
|
chownCmd := utils.ChownPath(envPath, NodeUser, false)
|
|
if chownCmd != nil {
|
|
return fmt.Errorf("failed to set environment file ownership: %w", chownCmd)
|
|
}
|
|
|
|
// Create systemd service file content
|
|
serviceContent := fmt.Sprintf(`[Unit]
|
|
Description=Quilibrium Node Service
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=quilibrium
|
|
EnvironmentFile=/opt/quilibrium/config/quilibrium.env
|
|
ExecStart=/usr/local/bin/quilibrium-node --config %s
|
|
Restart=on-failure
|
|
RestartSec=10
|
|
LimitNOFILE=65535
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
`, ConfigDirs+"/default")
|
|
|
|
// Write service file
|
|
servicePath := "/etc/systemd/system/quilibrium-node.service"
|
|
if err := utils.WriteFileAuto(servicePath, serviceContent); err != nil {
|
|
return fmt.Errorf("failed to create service file: %w", err)
|
|
}
|
|
|
|
// Reload systemd daemon
|
|
reloadCmd := exec.Command("sudo", "systemctl", "daemon-reload")
|
|
if err := reloadCmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to reload systemd daemon: %w", err)
|
|
}
|
|
|
|
fmt.Fprintf(os.Stdout, "Created systemd service file at %s\n", servicePath)
|
|
fmt.Fprintf(os.Stdout, "Created environment file at %s\n", envPath)
|
|
return nil
|
|
}
|
|
|
|
// installMacOSService installs a launchd service on macOS
|
|
func installMacOSService() {
|
|
fmt.Println("Installing launchd service for Quilibrium node...")
|
|
|
|
// Create plist file content
|
|
plistTemplate := `<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>Label</key>
|
|
<string>{{.Label}}</string>
|
|
<key>ProgramArguments</key>
|
|
<array>
|
|
<string>/usr/local/bin/quilibrium-node</string>
|
|
<string>--config</string>
|
|
<string>/opt/quilibrium/config/</string>
|
|
</array>
|
|
<key>EnvironmentVariables</key>
|
|
<dict>
|
|
<key>QUILIBRIUM_DATA_DIR</key>
|
|
<string>{{.DataPath}}</string>
|
|
<key>QUILIBRIUM_LOG_LEVEL</key>
|
|
<string>info</string>
|
|
<key>QUILIBRIUM_LISTEN_GRPC_MULTIADDR</key>
|
|
<string>/ip4/127.0.0.1/tcp/8337</string>
|
|
<key>QUILIBRIUM_LISTEN_REST_MULTIADDR</key>
|
|
<string>/ip4/127.0.0.1/tcp/8338</string>
|
|
<key>QUILIBRIUM_STATS_MULTIADDR</key>
|
|
<string>/dns/stats.quilibrium.com/tcp/443</string>
|
|
<key>QUILIBRIUM_NETWORK_ID</key>
|
|
<string>0</string>
|
|
<key>QUILIBRIUM_DEBUG</key>
|
|
<string>false</string>
|
|
<key>QUILIBRIUM_SIGNATURE_CHECK</key>
|
|
<string>true</string>
|
|
</dict>
|
|
<key>RunAtLoad</key>
|
|
<true/>
|
|
<key>KeepAlive</key>
|
|
<true/>
|
|
<key>StandardErrorPath</key>
|
|
<string>{{.LogPath}}/node.err</string>
|
|
<key>StandardOutPath</key>
|
|
<string>{{.LogPath}}/node.log</string>
|
|
</dict>
|
|
</plist>`
|
|
|
|
// Prepare template data
|
|
data := struct {
|
|
Label string
|
|
DataPath string
|
|
ServiceName string
|
|
LogPath string
|
|
}{
|
|
Label: fmt.Sprintf("com.quilibrium.node"),
|
|
DataPath: utils.NodeDataPath,
|
|
ServiceName: "node",
|
|
LogPath: logPath,
|
|
}
|
|
|
|
// Parse and execute template
|
|
tmpl, err := template.New("plist").Parse(plistTemplate)
|
|
if err != nil {
|
|
fmt.Printf("Error creating plist template: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Determine plist file path
|
|
var plistPath = fmt.Sprintf("/Library/LaunchDaemons/%s.plist", data.Label)
|
|
|
|
// Write plist file
|
|
file, err := os.Create(plistPath)
|
|
if err != nil {
|
|
fmt.Printf("Error creating plist file: %v\n", err)
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
if err := tmpl.Execute(file, data); err != nil {
|
|
fmt.Printf("Error writing plist file: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Set correct permissions
|
|
chownCmd := exec.Command("chown", "root:wheel", plistPath)
|
|
if err := chownCmd.Run(); err != nil {
|
|
fmt.Printf("Warning: Failed to change ownership of plist file: %v\n", err)
|
|
}
|
|
|
|
// Load the service
|
|
var loadCmd = exec.Command("launchctl", "load", "-w", plistPath)
|
|
|
|
if err := loadCmd.Run(); err != nil {
|
|
fmt.Printf("Error loading service: %v\n", err)
|
|
fmt.Println("You may need to load the service manually.")
|
|
}
|
|
|
|
fmt.Printf("Launchd service installed successfully as %s\n", plistPath)
|
|
fmt.Println("\nTo start the service:")
|
|
fmt.Printf(" sudo launchctl start %s\n", data.Label)
|
|
fmt.Println("\nTo stop the service:")
|
|
fmt.Printf(" sudo launchctl stop %s\n", data.Label)
|
|
fmt.Println("\nTo view service logs:")
|
|
fmt.Printf(" cat %s/%s.log\n", data.LogPath, data.ServiceName)
|
|
}
|