reviewed pid management

This commit is contained in:
LeonardoTrapani
2025-08-07 21:09:45 +02:00
parent 1acd007be0
commit 4d62dac99e
+177 -78
View File
@@ -3,123 +3,222 @@ package bus
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"log"
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"syscall"
) )
const SockName = "control.sock" const (
const PidName = "hyprvoice.pid" SockName = "control.sock"
const ProtoVer = "0.1" PidName = "hyprvoice.pid"
ProtoVer = "0.1"
)
// ~/.cache/hyprvoice/control.sock type pidManager struct {
func SockPath() (string, error) { path string
dir, err := os.UserCacheDir()
if err != nil {
return "", err
}
hd := filepath.Join(dir, "hyprvoice")
return filepath.Join(hd, SockName), nil
} }
// ~/.cache/hyprvoice/hyprvoice.pid func newPidManager() (*pidManager, error) {
func PidPath() (string, error) { pidPath, err := getPidPath()
if err != nil {
return nil, fmt.Errorf("failed to get PID path: %w", err)
}
return &pidManager{path: pidPath}, nil
}
func (pm *pidManager) checkExisting() error {
log.Printf("Checking for existing daemon at: %s", pm.path)
pidData, err := os.ReadFile(pm.path)
if os.IsNotExist(err) {
log.Printf("No PID file found, daemon not running")
return nil
}
if err != nil {
return fmt.Errorf("error reading PID file: %w", err)
}
log.Printf("Found PID file with content: %s", string(pidData))
pid, err := strconv.Atoi(string(pidData))
if err != nil {
log.Printf("Invalid PID in file, removing stale PID file: %v", err)
pm.removeStaleFile()
return nil
}
if pm.isProcessAlive(pid) {
log.Printf("Process %d is alive, daemon already running", pid)
return fmt.Errorf("daemon already running with PID %d", pid)
}
log.Printf("Process %d not alive, removing stale PID file", pid)
pm.removeStaleFile()
return nil
}
func (pm *pidManager) create() error {
if err := os.MkdirAll(filepath.Dir(pm.path), 0o700); err != nil {
return fmt.Errorf("failed to create PID directory: %w", err)
}
pid := os.Getpid()
log.Printf("Creating PID file at %s with PID %d", pm.path, pid)
err := os.WriteFile(pm.path, []byte(strconv.Itoa(pid)), 0o600)
if err != nil {
return fmt.Errorf("failed to write PID file: %w", err)
}
return nil
}
func (pm *pidManager) remove() error {
log.Printf("Removing PID file: %s", pm.path)
if err := os.Remove(pm.path); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed to remove PID file: %w", err)
}
return nil
}
func (pm *pidManager) isProcessAlive(pid int) bool {
log.Printf("Checking if process %d is alive", pid)
proc, err := os.FindProcess(pid)
if err != nil {
log.Printf("Process %d not found: %v", pid, err)
return false
}
err = proc.Signal(syscall.Signal(0))
if err != nil {
log.Printf("Process %d not alive (signal failed: %v)", pid, err)
return false
}
return true
}
func (pm *pidManager) removeStaleFile() {
if err := os.Remove(pm.path); err != nil && !os.IsNotExist(err) {
log.Printf("Warning: failed to remove stale PID file: %v", err)
}
}
type socketManager struct {
path string
}
func newSocketManager() (*socketManager, error) {
sockPath, err := getSockPath()
if err != nil {
return nil, fmt.Errorf("failed to get socket path: %w", err)
}
return &socketManager{path: sockPath}, nil
}
func (sm *socketManager) listen() (net.Listener, error) {
if err := os.MkdirAll(filepath.Dir(sm.path), 0o700); err != nil {
return nil, fmt.Errorf("failed to create socket directory: %w", err)
}
os.Remove(sm.path)
listener, err := net.Listen("unix", sm.path)
if err != nil {
return nil, fmt.Errorf("failed to listen on socket %s: %w", sm.path, err)
}
return listener, nil
}
func (sm *socketManager) dial() (net.Conn, error) {
conn, err := net.Dial("unix", sm.path)
if err != nil {
return nil, fmt.Errorf("failed to dial socket %s: %w", sm.path, err)
}
return conn, nil
}
func getSockPath() (string, error) {
dir, err := os.UserCacheDir() dir, err := os.UserCacheDir()
if err != nil { if err != nil {
return "", err return "", err
} }
hd := filepath.Join(dir, "hyprvoice") return filepath.Join(dir, "hyprvoice", SockName), nil
return filepath.Join(hd, PidName), nil }
func getPidPath() (string, error) {
dir, err := os.UserCacheDir()
if err != nil {
return "", err
}
return filepath.Join(dir, "hyprvoice", PidName), nil
}
func SockPath() (string, error) {
return getSockPath()
} }
func Listen() (net.Listener, error) { func Listen() (net.Listener, error) {
sp, err := SockPath() sm, err := newSocketManager()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := os.MkdirAll(filepath.Dir(sp), 0o700); err != nil { return sm.listen()
return nil, err
}
_ = os.Remove(sp) // stale socket from last run
return net.Listen("unix", sp)
} }
func Dial() (net.Conn, error) { func Dial() (net.Conn, error) {
sp, err := SockPath() sm, err := newSocketManager()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return net.Dial("unix", sp) return sm.dial()
}
func CheckExistingDaemon() error {
pm, err := newPidManager()
if err != nil {
return err
}
return pm.checkExisting()
}
func CreatePidFile() error {
pm, err := newPidManager()
if err != nil {
return err
}
return pm.create()
}
func RemovePidFile() error {
pm, err := newPidManager()
if err != nil {
return err
}
return pm.remove()
} }
func SendCommand(cmd byte) (string, error) { func SendCommand(cmd byte) (string, error) {
c, err := Dial() c, err := Dial()
if err != nil { if err != nil {
return "", err return "", fmt.Errorf("failed to connect to daemon: %w", err)
} }
defer c.Close() defer c.Close()
_, err = c.Write([]byte{cmd, '\n'}) _, err = c.Write([]byte{cmd, '\n'})
if err != nil { if err != nil {
return "", err return "", fmt.Errorf("failed to send command: %w", err)
} }
resp, err := bufio.NewReader(c).ReadString('\n') resp, err := bufio.NewReader(c).ReadString('\n')
return resp, err
}
func CheckExistingDaemon() error {
pidPath, err := PidPath()
if err != nil {
return err
}
pidData, err := os.ReadFile(pidPath)
if os.IsNotExist(err) {
return nil // no existing daemon
}
if err != nil {
return err
}
pid, err := strconv.Atoi(string(pidData))
if err != nil {
return nil // invalid pid file, assume stale
}
// Check if process exists
proc, err := os.FindProcess(pid)
if err != nil { if err != nil {
return nil return "", fmt.Errorf("failed to read response: %w", err)
} }
// Try to signal the process to check if it's alive return resp, nil
if err := proc.Signal(os.Signal(nil)); err != nil {
return nil // process not alive, stale pid file
}
return fmt.Errorf("daemon already running with PID %d", pid)
}
func CreatePidFile() error {
pidPath, err := PidPath()
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(pidPath), 0o700); err != nil {
return err
}
pid := os.Getpid()
return os.WriteFile(pidPath, []byte(strconv.Itoa(pid)), 0o600)
}
func RemovePidFile() error {
pidPath, err := PidPath()
if err != nil {
return err
}
return os.Remove(pidPath)
} }