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}