mirror of
https://github.com/Alexander-D-Karpov/netfetch.git
synced 2026-03-16 22:07:03 +03:00
199 lines
4.6 KiB
Go
199 lines
4.6 KiB
Go
package daemon
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"syscall"
|
|
)
|
|
|
|
type Daemon struct {
|
|
exePath string
|
|
configPath string
|
|
port int
|
|
webPort int
|
|
pidFile string
|
|
systemdUnit string
|
|
}
|
|
|
|
func New(exePath, configPath string, port, webPort int) *Daemon {
|
|
return &Daemon{
|
|
exePath: exePath,
|
|
configPath: configPath,
|
|
port: port,
|
|
webPort: webPort,
|
|
pidFile: filepath.Join(os.TempDir(), "netfetch.pid"),
|
|
systemdUnit: "netfetch.service",
|
|
}
|
|
}
|
|
|
|
func (d *Daemon) Start() error {
|
|
if d.IsRunning() {
|
|
return fmt.Errorf("daemon is already running")
|
|
}
|
|
|
|
// Get user's home directory
|
|
homeDir, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get home directory: %v", err)
|
|
}
|
|
|
|
// Create .netfetch directory if it doesn't exist
|
|
logDir := filepath.Join(homeDir, ".netfetch")
|
|
if err := os.MkdirAll(logDir, 0755); err != nil {
|
|
return fmt.Errorf("failed to create log directory: %v", err)
|
|
}
|
|
|
|
// Open log file in user's directory
|
|
logFile, err := os.OpenFile(
|
|
filepath.Join(logDir, "netfetch.log"),
|
|
os.O_APPEND|os.O_CREATE|os.O_WRONLY,
|
|
0644,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open log file: %v", err)
|
|
}
|
|
|
|
// Get server binary path (in the same directory as the CLI binary)
|
|
serverBinary := filepath.Join(filepath.Dir(d.exePath), "netfetchd")
|
|
if _, err := os.Stat(serverBinary); os.IsNotExist(err) {
|
|
return fmt.Errorf("server binary 'netfetchd' not found in %s", filepath.Dir(d.exePath))
|
|
}
|
|
|
|
// Start the daemon process
|
|
cmd := exec.Command(serverBinary,
|
|
"--web-port", fmt.Sprintf("%d", d.webPort),
|
|
"--config", d.configPath,
|
|
)
|
|
|
|
// Detach from parent process
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
Setsid: true,
|
|
}
|
|
|
|
cmd.Stdout = logFile
|
|
cmd.Stderr = logFile
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
return fmt.Errorf("failed to start daemon: %v", err)
|
|
}
|
|
|
|
// Write PID file
|
|
if err := os.WriteFile(d.pidFile, []byte(fmt.Sprintf("%d", cmd.Process.Pid)), 0644); err != nil {
|
|
return fmt.Errorf("failed to write PID file: %v", err)
|
|
}
|
|
|
|
fmt.Printf("Daemon started (PID: %d)\n", cmd.Process.Pid)
|
|
return nil
|
|
}
|
|
|
|
func (d *Daemon) Stop() error {
|
|
pid, err := d.getPID()
|
|
if err != nil {
|
|
return fmt.Errorf("daemon is not running")
|
|
}
|
|
|
|
process, err := os.FindProcess(pid)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find process: %v", err)
|
|
}
|
|
|
|
if err := process.Signal(syscall.SIGTERM); err != nil {
|
|
return fmt.Errorf("failed to stop daemon: %v", err)
|
|
}
|
|
|
|
err = os.Remove(d.pidFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("Daemon stopped")
|
|
return nil
|
|
}
|
|
|
|
func (d *Daemon) Status() error {
|
|
if !d.IsRunning() {
|
|
fmt.Println("Daemon is not running")
|
|
return nil
|
|
}
|
|
|
|
pid, _ := d.getPID()
|
|
fmt.Printf("Daemon is running (PID: %d)\n", pid)
|
|
return nil
|
|
}
|
|
|
|
func (d *Daemon) IsRunning() bool {
|
|
pid, err := d.getPID()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
process, err := os.FindProcess(pid)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return process.Signal(syscall.Signal(0)) == nil
|
|
}
|
|
|
|
func (d *Daemon) getPID() (int, error) {
|
|
data, err := os.ReadFile(d.pidFile)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var pid int
|
|
if _, err := fmt.Sscanf(string(data), "%d", &pid); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return pid, nil
|
|
}
|
|
|
|
func (d *Daemon) InstallSystemd() error {
|
|
serviceContent := fmt.Sprintf(`[Unit]
|
|
Description=NetFetch System Information Service
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=forking
|
|
ExecStart=%s -d start --port %d --web-port %d --config %s
|
|
ExecStop=%s -d stop
|
|
PIDFile=%s
|
|
Restart=on-failure
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
`, d.exePath, d.port, d.webPort, d.configPath, d.exePath, d.pidFile)
|
|
|
|
unitPath := filepath.Join("/etc/systemd/system", d.systemdUnit)
|
|
if err := os.WriteFile(unitPath, []byte(serviceContent), 0644); err != nil {
|
|
return fmt.Errorf("failed to write systemd unit file: %v", err)
|
|
}
|
|
|
|
if err := exec.Command("systemctl", "daemon-reload").Run(); err != nil {
|
|
return fmt.Errorf("failed to reload systemd: %v", err)
|
|
}
|
|
|
|
fmt.Println("Systemd service installed successfully")
|
|
fmt.Println("To start the service:")
|
|
fmt.Println(" sudo systemctl start netfetch")
|
|
fmt.Println("To enable on boot:")
|
|
fmt.Println(" sudo systemctl enable netfetch")
|
|
return nil
|
|
}
|
|
|
|
func (d *Daemon) UninstallSystemd() error {
|
|
unitPath := filepath.Join("/etc/systemd/system", d.systemdUnit)
|
|
if err := os.Remove(unitPath); err != nil && !os.IsNotExist(err) {
|
|
return fmt.Errorf("failed to remove systemd unit file: %v", err)
|
|
}
|
|
|
|
if err := exec.Command("systemctl", "daemon-reload").Run(); err != nil {
|
|
return fmt.Errorf("failed to reload systemd: %v", err)
|
|
}
|
|
|
|
fmt.Println("Systemd service uninstalled successfully")
|
|
return nil
|
|
}
|