Documentation
¶
Overview ¶
Package daemon provides lifecycle management for the grepai watch daemon.
This package handles PID file management, process spawning, and process lifecycle operations for running grepai watch in background mode.
Basic Usage ¶
Start a background process:
logDir, _ := daemon.GetDefaultLogDir()
pid, exitCh, err := daemon.SpawnBackground(logDir, []string{"watch"})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Started with PID %d\n", pid)
// exitCh receives when child exits (detects early failures)
Check if the process is running:
pid, err := daemon.GetRunningPID(logDir)
if err != nil {
log.Fatal(err)
}
if pid > 0 {
fmt.Printf("Watcher is running (PID %d)\n", pid)
}
Stop the process:
daemon.StopProcess(pid) daemon.RemovePIDFile(logDir)
PID File Format ¶
The PID file contains a single line with the process ID as a decimal integer. This format is stable and will not change in future versions. If additional metadata is needed, it will be stored in separate files (e.g., grepai-watch.meta).
Platform Support ¶
Cross-platform support for Unix-like systems (Linux, macOS) and Windows. Platform-specific behavior is implemented in daemon_unix.go and daemon_windows.go.
Thread Safety ¶
PID file writes use file locking (flock) to prevent race conditions when multiple processes attempt to start simultaneously.
Package daemon provides workspace daemon management for multi-project indexing.
Index ¶
- func GetDefaultLogDir() (string, error)
- func GetRunningPID(logDir string) (int, error)
- func GetRunningWorkspacePID(logDir, workspaceName string) (int, error)
- func GetRunningWorktreePID(logDir, worktreeID string) (int, error)
- func GetWorkspaceLogFile(logDir, workspaceName string) string
- func GetWorkspacePIDFile(logDir, workspaceName string) string
- func GetWorkspaceReadyFile(logDir, workspaceName string) string
- func GetWorktreeLogFile(logDir, worktreeID string) string
- func GetWorktreePIDFile(logDir, worktreeID string) string
- func GetWorktreeReadyFile(logDir, worktreeID string) string
- func IsProcessRunning(pid int) bool
- func IsReady(logDir string) bool
- func IsWorkspaceReady(logDir, workspaceName string) bool
- func IsWorktreeReady(logDir, worktreeID string) bool
- func ReadPIDFile(logDir string) (int, error)
- func ReadWorkspacePIDFile(logDir, workspaceName string) (int, error)
- func ReadWorktreePIDFile(logDir, worktreeID string) (int, error)
- func RemovePIDFile(logDir string) error
- func RemoveReadyFile(logDir string) error
- func RemoveWorkspacePIDFile(logDir, workspaceName string) error
- func RemoveWorkspaceReadyFile(logDir, workspaceName string) error
- func RemoveWorktreePIDFile(logDir, worktreeID string) error
- func RemoveWorktreeReadyFile(logDir, worktreeID string) error
- func SpawnBackground(logDir string, args []string) (int, <-chan struct{}, error)
- func SpawnWorkspaceBackground(logDir, workspaceName string, extraArgs []string) (int, <-chan struct{}, error)
- func SpawnWorktreeBackground(logDir, worktreeID string, extraArgs []string) (int, <-chan struct{}, error)
- func StopChannel() <-chan struct{}
- func StopProcess(pid int) error
- func WritePIDFile(logDir string) error
- func WriteReadyFile(logDir string) error
- func WriteWorkspacePIDFile(logDir, workspaceName string) error
- func WriteWorkspaceReadyFile(logDir, workspaceName string) error
- func WriteWorktreePIDFile(logDir, worktreeID string) error
- func WriteWorktreeReadyFile(logDir, worktreeID string) error
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GetDefaultLogDir ¶
GetDefaultLogDir returns the OS-specific default log directory.
Platform-specific defaults:
- Linux: $XDG_STATE_HOME/grepai/logs or ~/.local/state/grepai/logs
- macOS: ~/Library/Logs/grepai
- Windows: %LOCALAPPDATA%\grepai\logs
This function is typically called once at startup to determine where PID files and logs should be stored. Use the --log-dir flag to override the default.
Returns an absolute path to the log directory. The directory may not exist yet; callers should create it with os.MkdirAll if needed.
Example ¶
ExampleGetDefaultLogDir demonstrates how to get the OS-specific default log directory.
package main
import (
"fmt"
"log"
"github.com/Boshommi/grepai/daemon"
)
func main() {
logDir, err := daemon.GetDefaultLogDir()
if err != nil {
log.Fatal(err)
}
fmt.Println("Log directory determined")
// On macOS: ~/Library/Logs/grepai
// On Linux: ~/.local/state/grepai/logs (or $XDG_STATE_HOME/grepai/logs)
// On Windows: %LOCALAPPDATA%\grepai\logs
_ = logDir
}
func GetRunningPID ¶
GetRunningPID returns the PID of the running watcher process, or 0 if not running. Automatically cleans up stale PID files (where the process no longer exists). This is a convenience function that combines ReadPIDFile, IsProcessRunning, and stale PID cleanup in one call.
Example ¶
ExampleGetRunningPID demonstrates how to check if a watcher is running and automatically clean up stale PID files.
package main
import (
"fmt"
"log"
"github.com/Boshommi/grepai/daemon"
)
func main() {
logDir := "/tmp/grepai-logs"
// Check if a watcher is running
pid, err := daemon.GetRunningPID(logDir)
if err != nil {
log.Fatal(err)
}
if pid == 0 {
fmt.Println("No watcher running")
} else {
fmt.Printf("Watcher running with PID %d\n", pid)
}
}
func GetRunningWorkspacePID ¶
GetRunningWorkspacePID returns the PID of the running workspace watcher, or 0 if not running.
func GetRunningWorktreePID ¶
GetRunningWorktreePID returns the PID of the running worktree watcher, or 0 if not running.
func GetWorkspaceLogFile ¶
GetWorkspaceLogFile returns the path to the log file for a workspace.
func GetWorkspacePIDFile ¶
GetWorkspacePIDFile returns the path to the PID file for a workspace.
func GetWorkspaceReadyFile ¶
GetWorkspaceReadyFile returns the path to the ready file for a workspace.
func GetWorktreeLogFile ¶
GetWorktreeLogFile returns the path to the log file for a worktree.
func GetWorktreePIDFile ¶
GetWorktreePIDFile returns the path to the PID file for a worktree.
func GetWorktreeReadyFile ¶
GetWorktreeReadyFile returns the path to the ready file for a worktree.
func IsProcessRunning ¶
IsProcessRunning checks if a process with the given PID is running on Unix systems. Uses signal(0) which returns an error if the process doesn't exist or we don't have permission.
func IsWorkspaceReady ¶
IsWorkspaceReady checks if the workspace ready marker file exists.
func IsWorktreeReady ¶
IsWorktreeReady checks if the worktree ready marker file exists.
func ReadPIDFile ¶
ReadPIDFile reads the process ID from the PID file in the given logDir.
Return values:
- (0, nil): No PID file exists (watcher not running or not started yet)
- (pid, nil): PID file exists and contains a valid process ID
- (0, error): PID file exists but is corrupt, unreadable, or has wrong permissions
Note: This function does NOT check if the process is actually running. Use GetRunningPID() for automatic stale PID detection and cleanup, or call IsProcessRunning(pid) to check the process status manually.
Example ¶
ExampleReadPIDFile demonstrates how to read the PID file directly. For most use cases, prefer GetRunningPID which also handles stale PIDs.
package main
import (
"fmt"
"log"
"github.com/Boshommi/grepai/daemon"
)
func main() {
logDir := "/tmp/grepai-logs"
// Read the PID file
pid, err := daemon.ReadPIDFile(logDir)
if err != nil {
log.Fatal(err)
}
if pid == 0 {
fmt.Println("No PID file found")
} else {
// Check if the process is actually running
if daemon.IsProcessRunning(pid) {
fmt.Printf("Process %d is running\n", pid)
} else {
fmt.Printf("Process %d is not running (stale PID)\n", pid)
}
}
}
func ReadWorkspacePIDFile ¶
ReadWorkspacePIDFile reads the process ID from the workspace PID file.
func ReadWorktreePIDFile ¶
ReadWorktreePIDFile reads the process ID from the worktree PID file.
func RemovePIDFile ¶
RemovePIDFile removes the PID file and its associated lock file.
func RemoveReadyFile ¶
RemoveReadyFile removes the ready marker file.
func RemoveWorkspacePIDFile ¶
RemoveWorkspacePIDFile removes the workspace PID file and its lock file.
func RemoveWorkspaceReadyFile ¶
RemoveWorkspaceReadyFile removes the ready marker file for a workspace.
func RemoveWorktreePIDFile ¶
RemoveWorktreePIDFile removes the worktree PID file and its lock file.
func RemoveWorktreeReadyFile ¶
RemoveWorktreeReadyFile removes the ready marker file for a worktree.
func SpawnBackground ¶
SpawnBackground re-executes the current binary as a background process.
The function spawns a detached child process with:
- stdout/stderr redirected to logDir/grepai-watch.log
- stdin set to nil (no input)
- GREPAI_BACKGROUND=1 environment variable set
- process group detachment (Unix only)
Args should be the command-line arguments to pass to the child process (e.g., []string{"watch"} for "grepai watch").
Returns the child PID and an exit channel. The exit channel receives when the child process terminates, enabling callers to detect early failures without relying on kill(0) which cannot distinguish zombie processes.
Example ¶
ExampleSpawnBackground demonstrates how to start a background process. This is a simplified example; real usage should include error handling and startup verification.
package main
import (
"fmt"
"log"
"path/filepath"
"github.com/Boshommi/grepai/daemon"
)
func main() {
logDir := "/tmp/grepai-logs"
// Spawn background process (exitCh signals when child exits)
pid, _, err := daemon.SpawnBackground(logDir, []string{"watch"})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Started background watcher with PID %d\n", pid)
fmt.Printf("Logs: %s\n", filepath.Join(logDir, "grepai-watch.log"))
// In real usage, poll for IsReady() and check exitCh to verify successful startup
}
func SpawnWorkspaceBackground ¶
func SpawnWorkspaceBackground(logDir, workspaceName string, extraArgs []string) (int, <-chan struct{}, error)
SpawnWorkspaceBackground re-executes the current binary for workspace watch in background.
func SpawnWorktreeBackground ¶
func SpawnWorktreeBackground(logDir, worktreeID string, extraArgs []string) (int, <-chan struct{}, error)
SpawnWorktreeBackground re-executes the current binary for worktree watch in background.
func StopChannel ¶
func StopChannel() <-chan struct{}
StopChannel returns a channel that never fires on Unix. Signal-based shutdown is handled via os/signal, so no additional mechanism is needed.
func StopProcess ¶
StopProcess sends SIGINT to the process with the given PID.
Example ¶
ExampleStopProcess demonstrates how to gracefully stop a background process.
package main
import (
"fmt"
"log"
"github.com/Boshommi/grepai/daemon"
)
func main() {
logDir := "/tmp/grepai-logs"
// Get the running PID
pid, err := daemon.GetRunningPID(logDir)
if err != nil {
log.Fatal(err)
}
if pid == 0 {
fmt.Println("No process to stop")
return
}
// Send interrupt signal
if err := daemon.StopProcess(pid); err != nil {
log.Fatal(err)
}
fmt.Printf("Sent interrupt signal to PID %d\n", pid)
// In real usage, poll IsProcessRunning to wait for shutdown
// Then call RemovePIDFile to clean up
}
func WritePIDFile ¶
WritePIDFile writes the current process ID to the PID file. Uses file locking to prevent race conditions when multiple processes attempt to start simultaneously. The lock is held for the lifetime of the process (released by the OS on exit).
PID file format: single line containing process ID as decimal integer. This format is stable and will not change. If additional metadata is needed in the future, it will be stored in a separate file (e.g., grepai-watch.meta).
Example ¶
ExampleWritePIDFile demonstrates how to write the current process PID to a file. This is typically called after daemonizing to record the background process PID.
package main
import (
"fmt"
"log"
"github.com/Boshommi/grepai/daemon"
)
func main() {
logDir := "/tmp/grepai-logs"
// Write PID file for current process
if err := daemon.WritePIDFile(logDir); err != nil {
log.Fatal(err)
}
fmt.Println("PID file written")
// Clean up when done
defer daemon.RemovePIDFile(logDir)
}
func WriteReadyFile ¶
WriteReadyFile writes the ready marker file to indicate the daemon has successfully initialized and is ready to serve. This should be called after all initialization is complete (embedder, store, initial scan).
func WriteWorkspacePIDFile ¶
WriteWorkspacePIDFile writes the current process ID to the workspace PID file.
func WriteWorkspaceReadyFile ¶
WriteWorkspaceReadyFile writes the ready marker file for a workspace.
func WriteWorktreePIDFile ¶
WriteWorktreePIDFile writes the current process ID to the worktree PID file.
func WriteWorktreeReadyFile ¶
WriteWorktreeReadyFile writes the ready marker file for a worktree.
Types ¶
This section is empty.