Files
go-telldus-matter/pkg/telldus-daemon/daemon.go
Mats Tornberg 57ba34b929 more
2025-11-22 17:42:05 +01:00

157 lines
3.1 KiB
Go

package daemon
import (
"bufio"
"fmt"
"log"
"os/exec"
"sync"
"syscall"
"time"
"git.k7n.net/mats/go-telldus/pkg/telldus"
)
// Manager handles the telldusd daemon lifecycle
type Manager struct {
cmd *exec.Cmd
mu sync.Mutex
}
// New creates a new daemon manager
func New() *Manager {
return &Manager{}
}
// Start starts the telldusd daemon and captures its output
func (m *Manager) Start() error {
m.mu.Lock()
defer m.mu.Unlock()
if m.cmd != nil && m.cmd.Process != nil {
log.Println("Telldusd already running")
return nil
}
log.Println("Starting telldusd...")
cmd := exec.Command("/usr/local/sbin/telldusd", "--nodaemon")
// Capture stdout
stdout, err := cmd.StdoutPipe()
if err != nil {
return fmt.Errorf("failed to get stdout pipe: %v", err)
}
// Capture stderr
stderr, err := cmd.StderrPipe()
if err != nil {
return fmt.Errorf("failed to get stderr pipe: %v", err)
}
// Start the command
if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start telldusd: %v", err)
}
m.cmd = cmd
// Log stdout in a goroutine
go func() {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
log.Printf("[telldusd] %s", scanner.Text())
}
}()
// Log stderr in a goroutine
go func() {
scanner := bufio.NewScanner(stderr)
for scanner.Scan() {
log.Printf("[telldusd] ERROR: %s", scanner.Text())
}
}()
// Monitor process in a goroutine
go func() {
err := cmd.Wait()
m.mu.Lock()
m.cmd = nil
m.mu.Unlock()
if err != nil {
log.Printf("Telldusd exited with error: %v", err)
} else {
log.Println("Telldusd exited normally")
}
}()
// Give telldusd a moment to start
time.Sleep(500 * time.Millisecond)
log.Println("Telldusd started successfully")
return nil
}
// Stop stops the telldusd daemon
func (m *Manager) Stop() error {
m.mu.Lock()
defer m.mu.Unlock()
if m.cmd == nil || m.cmd.Process == nil {
log.Println("Telldusd not running")
return nil
}
log.Println("Stopping telldusd...")
// Send SIGTERM
if err := m.cmd.Process.Signal(syscall.SIGTERM); err != nil {
log.Printf("Failed to send SIGTERM to telldusd: %v", err)
// Try SIGKILL as fallback
if err := m.cmd.Process.Kill(); err != nil {
return fmt.Errorf("failed to kill telldusd: %v", err)
}
}
// Wait for process to exit (with timeout)
done := make(chan error, 1)
go func() {
done <- m.cmd.Wait()
}()
select {
case <-done:
log.Println("Telldusd stopped successfully")
case <-time.After(5 * time.Second):
log.Println("Telldusd did not stop gracefully, killing...")
m.cmd.Process.Kill()
}
m.cmd = nil
return nil
}
// Restart restarts the telldusd daemon
func (m *Manager) Restart() error {
log.Println("Restarting telldusd due to configuration change...")
if err := m.Stop(); err != nil {
log.Printf("Error stopping telldusd: %v", err)
}
// Give it a moment to fully stop
time.Sleep(1 * time.Second)
// Close and reinitialize telldus library
telldus.Close()
time.Sleep(500 * time.Millisecond)
if err := m.Start(); err != nil {
return err
}
// Reinitialize telldus library
telldus.Init()
log.Println("Telldusd restarted successfully")
return nil
}