Documentation
¶
Overview ¶
Package daemon manages the cloop background daemon process. It stores PID in .cloop/daemon.pid, state in .cloop/daemon.json, and log output in .cloop/daemon.log.
Index ¶
- func IsRunning(workdir string) (bool, int)
- func LogPath(workdir string) string
- func PIDPath(workdir string) string
- func ReadPID(workdir string) int
- func RemovePID(workdir string) error
- func StatePath(workdir string) string
- func Stop(workdir string) error
- func StopWithTimeout(workdir string, grace time.Duration) error
- func WritePID(workdir string, pid int) error
- type State
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsRunning ¶
IsRunning returns true if the daemon is alive (PID file exists and process responds).
func RemovePID ¶
RemovePID removes the PID file (called by worker on clean exit). Returns the underlying os.Remove error so callers can log a true failure (perms, busy filesystem) — a stale PID file blocks the next `daemon start` because IsRunning() will see it and assume the prior worker is alive. ENOENT is expected when the file was already removed (e.g. by Stop) and is returned untouched so callers can filter with os.IsNotExist.
func Stop ¶
Stop sends SIGTERM to the daemon worker and waits for it to exit, escalating to SIGKILL if the worker doesn't terminate within stopGraceTimeout.
Unlike a fire-and-forget signal + os.Remove(pidPath), this:
- Lets the worker run its own cleanup (PID-file removal, final state Save, HTTP-server graceful shutdown) before Stop returns. A subsequent `daemon start` therefore sees a clean slate instead of racing the drain.
- Forcibly cleans up if the worker is wedged, so a stuck daemon can always be replaced.
Returns nil on clean exit; a wrapped error if SIGKILL was needed (the daemon is gone, but the caller should know it didn't shut down cleanly).
func StopWithTimeout ¶
StopWithTimeout is Stop with a caller-supplied grace period. Used by tests to drive the SIGKILL escalation path without waiting the production default.
Types ¶
type State ¶
type State struct {
PID int `json:"pid"`
StartedAt time.Time `json:"started_at"`
LastRunAt time.Time `json:"last_run_at,omitempty"`
NextRunAt time.Time `json:"next_run_at,omitempty"`
RunCount int `json:"run_count"`
TotalTasksCompleted int `json:"total_tasks_completed"`
TotalTasksFailed int `json:"total_tasks_failed"`
Status string `json:"status"` // starting, idle, running, stopped, error
Interval string `json:"interval"`
Provider string `json:"provider,omitempty"`
Model string `json:"model,omitempty"`
LastError string `json:"last_error,omitempty"`
WatchEnabled bool `json:"watch_enabled,omitempty"`
WatchTriggers int `json:"watch_triggers,omitempty"`
}
State tracks the daemon's runtime status, persisted to .cloop/daemon.json.
func Load ¶
Load reads the daemon state from disk; returns nil if not found.
On parse failure (zero-byte file, schema drift, manual edit gone wrong) the corrupt file is quarantined aside as daemon.json.corrupt-<unix> and (nil, nil) is returned. Losing the daemon counters/last-error is preferable to wedging `cloop daemon status` and the orchestrator's heartbeat reads — the bytes are preserved next to the original location for forensics.