update
This commit is contained in:
156
pkg/telldus-daemon/daemon.go
Normal file
156
pkg/telldus-daemon/daemon.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"app/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
|
||||
}
|
||||
63
pkg/telldus-daemon/watcher.go
Normal file
63
pkg/telldus-daemon/watcher.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"log"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
// Watcher watches for configuration file changes
|
||||
type Watcher struct {
|
||||
configPath string
|
||||
onReloadFunc func() error
|
||||
}
|
||||
|
||||
// NewWatcher creates a new config file watcher
|
||||
func NewWatcher(configPath string, onReload func() error) *Watcher {
|
||||
return &Watcher{
|
||||
configPath: configPath,
|
||||
onReloadFunc: onReload,
|
||||
}
|
||||
}
|
||||
|
||||
// Watch starts watching for changes to the configuration file
|
||||
func (w *Watcher) Watch() error {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
// Watch the parent directory since file operations might replace the file
|
||||
configDir := filepath.Dir(w.configPath)
|
||||
if err := watcher.Add(configDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Watching for changes to %s", w.configPath)
|
||||
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-watcher.Events:
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
// Check if the event is for our config file
|
||||
if event.Name == w.configPath && (event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create) {
|
||||
log.Printf("Configuration file changed: %s", event.Op.String())
|
||||
// Call reload callback if provided
|
||||
if w.onReloadFunc != nil {
|
||||
if err := w.onReloadFunc(); err != nil {
|
||||
log.Printf("Failed to reload devices: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
case err, ok := <-watcher.Errors:
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
log.Printf("File watcher error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user