pidfile_windows.go

 1//go:build windows
 2
 3package daemon
 4
 5import (
 6	"fmt"
 7	"os"
 8	"strconv"
 9	"strings"
10	"syscall"
11)
12
13// WritePID writes the current process ID to the given path.
14func WritePID(path string) error {
15	return os.WriteFile(path, []byte(strconv.Itoa(os.Getpid())), 0644)
16}
17
18// ReadPID reads the process ID from the given path.
19func ReadPID(path string) (int, error) {
20	data, err := os.ReadFile(path)
21	if err != nil {
22		return 0, err
23	}
24	pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
25	if err != nil {
26		return 0, fmt.Errorf("invalid PID file: %w", err)
27	}
28	return pid, nil
29}
30
31// IsRunning checks if a daemon process is alive using the PID file.
32func IsRunning(path string) (int, bool) {
33	pid, err := ReadPID(path)
34	if err != nil {
35		return 0, false
36	}
37
38	// On Windows, syscall.Kill is not available. We use OpenProcess instead.
39	// We only need PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
40	const PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
41	h, err := syscall.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid))
42	if err != nil {
43		// Process could not be opened, which likely means it doesn't exist
44		return pid, false
45	}
46	defer syscall.CloseHandle(h)
47
48	// Check if the process is still running or has exited
49	var exitCode uint32
50	err = syscall.GetExitCodeProcess(h, &exitCode)
51	if err != nil {
52		return pid, false
53	}
54
55	// STILL_ACTIVE is 259
56	return pid, exitCode == 259
57}
58
59// RemovePID removes the PID file.
60func RemovePID(path string) error {
61	return os.Remove(path)
62}