reviewed pid management
This commit is contained in:
+182
-83
@@ -3,123 +3,222 @@ package bus
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const SockName = "control.sock"
|
||||
const PidName = "hyprvoice.pid"
|
||||
const ProtoVer = "0.1"
|
||||
const (
|
||||
SockName = "control.sock"
|
||||
PidName = "hyprvoice.pid"
|
||||
ProtoVer = "0.1"
|
||||
)
|
||||
|
||||
type pidManager struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func newPidManager() (*pidManager, 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()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(dir, "hyprvoice", SockName), nil
|
||||
}
|
||||
|
||||
func getPidPath() (string, error) {
|
||||
dir, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(dir, "hyprvoice", PidName), nil
|
||||
}
|
||||
|
||||
// ~/.cache/hyprvoice/control.sock
|
||||
func SockPath() (string, error) {
|
||||
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 PidPath() (string, error) {
|
||||
dir, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hd := filepath.Join(dir, "hyprvoice")
|
||||
return filepath.Join(hd, PidName), nil
|
||||
return getSockPath()
|
||||
}
|
||||
|
||||
func Listen() (net.Listener, error) {
|
||||
sp, err := SockPath()
|
||||
sm, err := newSocketManager()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(sp), 0o700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = os.Remove(sp) // stale socket from last run
|
||||
return net.Listen("unix", sp)
|
||||
return sm.listen()
|
||||
}
|
||||
|
||||
func Dial() (net.Conn, error) {
|
||||
sp, err := SockPath()
|
||||
sm, err := newSocketManager()
|
||||
if err != nil {
|
||||
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) {
|
||||
c, err := Dial()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", fmt.Errorf("failed to connect to daemon: %w", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
_, err = c.Write([]byte{cmd, '\n'})
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", fmt.Errorf("failed to send command: %w", err)
|
||||
}
|
||||
|
||||
resp, err := bufio.NewReader(c).ReadString('\n')
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func CheckExistingDaemon() error {
|
||||
pidPath, err := PidPath()
|
||||
if err != nil {
|
||||
return err
|
||||
return "", fmt.Errorf("failed to read response: %w", 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 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Try to signal the process to check if it's alive
|
||||
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)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user