daemon

package
v0.2.14 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 17, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package daemon runs the reporter's upload pass on a fixed interval until it is told to stop.

Each cycle runs reporter.RunOnce and then sends a heartbeat, so the dashboard sees the machine online even when there is nothing new. With Reporter.ScanEnded set, each pass also reads the transcripts that changed since the previous pass, so sessions that start and end between passes are not missed. The scan window is held in memory: the first pass looks back Reporter.ScanLookback, and each later pass starts twice the interval before the previous complete pass began. A failed cycle backs off exponentially. Only one daemon runs per home directory, enforced by a lock on ~/.firekeeper/daemon.lock, and it logs to ~/.firekeeper/daemon.log. Logs carry counts and sanitized errors, never transcript text.

Index

Constants

View Source
const (
	// DefaultInterval is the time between passes when Config.Interval is
	// zero.
	DefaultInterval = 15 * time.Second
	// MinInterval is the shortest interval Run accepts.
	MinInterval = 5 * time.Second
)
View Source
const (
	// LaunchdLabel names the macOS LaunchAgent.
	LaunchdLabel = "dev.firekeeper.daemon"
	// SystemdUnit names the systemd user unit.
	SystemdUnit = "firekeeper.service"
)

Variables

View Source
var ErrLocked = errors.New("another firekeeper daemon is already running")

ErrLocked means another daemon holds the lock for the same home.

View Source
var ErrNoProviders = errors.New("no provider enabled; the daemon uploads nothing without one")

ErrNoProviders means the reporter config would upload nothing.

View Source
var ErrNotInstalled = errors.New("the firekeeper daemon service is not installed")

ErrNotInstalled means no service definition exists and none is loaded.

Functions

func DetectProviders

func DetectProviders(home string, getenv func(string) string, lookPath func(string) (string, error)) []transcript.Provider

DetectProviders finds supported transcript providers without reading their data or launching their CLIs. An existing provider home or executable on PATH is sufficient; providers without transcript readers are deliberately omitted.

func Follow

func Follow(ctx context.Context, path string, offset int64, interval time.Duration, w io.Writer) error

Follow writes what is appended to the log at path after offset until ctx is cancelled, polling every interval. When the daemon rotates the log it finishes the old file and continues at the start of the new one.

func Run

func Run(ctx context.Context, cfg Config) error

Run reports every Interval until ctx is cancelled. Cancelling ctx is a graceful stop: the batch in flight finishes and its offset is saved, no new batch starts, and Run returns nil. Run returns an error only when it cannot start.

func Supported

func Supported(goos string) bool

Supported reports whether goos has a service manager Service knows.

func Tail

func Tail(path string, n int, w io.Writer) (int64, error)

Tail writes the last n lines of the log at path to w. A missing log is reported as os.ErrNotExist. It returns the offset just past what it wrote, for Follow.

Types

type Clock

type Clock interface {
	After(time.Duration) <-chan time.Time
}

Clock schedules the wait between passes. Tests replace it so backoff can be checked without sleeping.

type CommandError

type CommandError struct {
	Name   string
	Code   int
	Output string
}

CommandError is a service-manager command that exited non-zero.

func (*CommandError) Error

func (e *CommandError) Error() string

type Config

type Config struct {
	// Reporter configures each pass. Its Providers allowlist is the
	// daemon's; Run refuses to start without one. Out and Stop are set by
	// Run.
	Reporter reporter.Config
	// Interval is the time between successful passes. Zero means
	// DefaultInterval.
	Interval time.Duration
	// Dir holds daemon.lock and daemon.log. Empty means
	// ~/.firekeeper, using Reporter.Home when set.
	Dir string
	// Out, when set, also receives every log line.
	Out io.Writer

	// RunOnce, Heartbeat, Clock, and Rand replace the real implementations
	// in tests. Nil means reporter.RunOnce, reporter.Heartbeat, the system
	// clock, and math/rand/v2.Float64.
	RunOnce   func(context.Context, reporter.Config) (reporter.Summary, error)
	Heartbeat func(context.Context, reporter.Config, reporter.Summary) error
	Clock     Clock
	Rand      func() float64
}

Config configures Run.

type ExecRunner

type ExecRunner struct{}

ExecRunner runs commands with os/exec, bounded by a timeout.

func (ExecRunner) Run

func (ExecRunner) Run(ctx context.Context, name string, args ...string) ([]byte, error)

Run implements Runner.

type Reporting

type Reporting struct {
	// Installed reports whether the service definition file exists.
	Installed bool
	// Running reports whether the service is running or a daemon holds
	// the lock, such as one started in a terminal.
	Running bool
	// Foreground reports that the running daemon is not the service: the
	// service manager says it is stopped, yet a daemon holds the lock.
	Foreground bool
	// Server is the dashboard URL the daemon reports to: its --server
	// flag when the service has one, else the configured server, else
	// reporter.DefaultServer.
	Server string
	// Providers are the service's --provider flags, or the configured
	// providers when it has none.
	Providers []transcript.Provider
}

Reporting is what this machine can tell about its own daemon without asking it: whether it is installed and running, where it sends sessions, and which providers it uploads. Every field is best effort.

type Runner

type Runner interface {
	Run(ctx context.Context, name string, args ...string) ([]byte, error)
}

Runner runs a service-manager command and returns its combined output. A command that runs and exits non-zero returns a *CommandError. Tests replace it so no real launchctl or systemctl runs.

type Service

type Service struct {
	// GOOS picks the service manager: "darwin" or "linux".
	GOOS string
	// Home is the user's home directory.
	Home string
	// UID is the user's numeric id, for the launchd gui/<uid> domain.
	UID int
	// Executable is the absolute path of the firekeeper binary to run.
	Executable string
	// Args follow "daemon" on the service's command line.
	Args []string
	// Env holds the variables written into the service definition.
	Env map[string]string
	// Runner runs launchctl and systemctl.
	Runner Runner
	// Out receives progress lines.
	Out io.Writer
	// Sleep waits between bootstrap retries. Nil means time.Sleep.
	Sleep func(time.Duration)
}

Service installs, removes, and inspects the daemon as a per-user login service: a LaunchAgent on macOS and a systemd user unit on Linux. It never needs or calls sudo.

func NewService

func NewService(args []string, out io.Writer) (*Service, error)

NewService returns a Service for the current user and platform, with the running binary as its executable and the current environment's provider homes and PATH captured.

func (*Service) Commands

func (s *Service) Commands() [][]string

Commands lists the service-manager commands Install runs after writing the file, for a dry run.

func (*Service) DataDir

func (s *Service) DataDir() string

DataDir is ~/.firekeeper.

func (*Service) File

func (s *Service) File() ([]byte, error)

File returns the service definition Install would write.

func (*Service) Inspect

func (s *Service) Inspect(ctx context.Context, configServer string, configProviders []transcript.Provider) Reporting

Inspect describes the daemon. configServer and configProviders are the merged configuration, used for whatever the service definition does not set. It reads the service file and asks the service manager, so it may run launchctl or systemctl; it never changes anything.

func (*Service) Install

func (s *Service) Install(ctx context.Context) error

Install writes the service definition and starts the service. Running it again replaces the definition and restarts the service with the new one.

func (*Service) InstalledArgs

func (s *Service) InstalledArgs() ([]string, error)

InstalledArgs returns the arguments the installed service passes after "daemon", read back from its definition file. It returns an error wrapping os.ErrNotExist when the service is not installed.

func (*Service) LogPath

func (s *Service) LogPath() string

LogPath is the daemon's own log file.

func (*Service) Path

func (s *Service) Path() string

Path is the service definition file.

func (*Service) StartupLogPath

func (s *Service) StartupLogPath() string

StartupLogPath is where launchd writes the daemon's stdout and stderr.

func (*Service) Status

func (s *Service) Status(ctx context.Context) (Status, error)

Status asks the service manager about the service.

func (*Service) Uninstall

func (s *Service) Uninstall(ctx context.Context, purge bool) error

Uninstall stops the service and removes its definition. With purge it also deletes the reporter's and daemon's files in ~/.firekeeper. It returns ErrNotInstalled when there was nothing to stop or remove, after purging if asked.

type Status

type Status struct {
	// Installed reports whether the service definition file exists.
	Installed bool
	// Loaded reports whether the service manager knows the service.
	Loaded bool
	// Running reports whether the daemon process is up.
	Running bool
	// State is the manager's own word for the service's state.
	State string
	PID   int
	// LastExit is the last exit status, or empty when unknown.
	LastExit string
}

Status describes the installed service.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL