daemon

package
v0.5.6 Latest Latest
Warning

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

Go to latest
Published: Jul 12, 2026 License: AGPL-3.0 Imports: 17 Imported by: 0

Documentation

Overview

Package daemon runs the watch loop as a background process and enforces a single running instance per machine. The lock is a real OS advisory file lock (flock on unix, LockFileEx on Windows) held for the process lifetime, so it is released automatically if the process dies and is immune to pid reuse. The file's contents are the holder's pid and a random per-run token, kept as metadata for stop/status and rechecked before forceful termination.

Index

Constants

View Source
const (
	// LogMaxBytes keeps the active daemon log useful without letting a
	// workstation process consume unbounded disk space.
	LogMaxBytes int64 = 5 << 20
	// LogBackups is the number of complete rotated daemon logs retained beside
	// the active file.
	LogBackups = 3
)
View Source
const (
	// DefaultStopTimeout bounds both the graceful wait and, when requested, the
	// confirmation wait after forceful termination.
	DefaultStopTimeout = 10 * time.Second
)

Variables

View Source
var (
	ErrNotRunning      = errors.New("not running")
	ErrShutdownTimeout = errors.New("graceful shutdown timed out")
	ErrForceTimeout    = errors.New("forced shutdown timed out")
	ErrProcessChanged  = errors.New("daemon identity changed during shutdown")
)
View Source
var ErrAlreadyRunning = errors.New("another akari instance is already running")

ErrAlreadyRunning reports lock contention from another daemon instance.

Functions

func IsRunning

func IsRunning(path string) (bool, error)

IsRunning reports whether an instance currently holds the lock without creating the pidfile or changing its contents. Only lock contention means running; permission, filesystem, lock, and close failures remain visible.

func Start

func Start(self string, watchArgs []string, p Paths) error

Start launches `self watchArgs...` as a detached background process whose output goes to the log file. The child acquires the lock itself; Start waits briefly to confirm an instance is holding it.

func Status

func Status(p Paths) (running bool, pid int, err error)

Status reports whether the watch process is running and its pid. Probe and pidfile read failures are returned to the caller.

Types

type Lock

type Lock struct {
	// contains filtered or unexported fields
}

Lock is a held single-instance lock.

func Acquire

func Acquire(path string) (*Lock, error)

Acquire takes the lock. It returns ErrAlreadyRunning when another live instance holds it; other filesystem and lock failures preserve their causes. The OS releases the lock automatically when this process exits, so there is no stale-lock reclaim logic and no TOCTOU window.

func (*Lock) Release

func (l *Lock) Release() error

Release drops the OS lock by closing the file handle. It deliberately does not remove the pidfile: unlinking after unlocking opens a window in which another process acquires the lock and we then delete the path out from under it, allowing two holders. A lingering unlocked pidfile is harmless because IsRunning probes the live lock, not the file's existence.

func (*Lock) ShutdownContext added in v0.5.5

func (l *Lock) ShutdownContext(parent context.Context) (context.Context, func(), error)

ShutdownContext adds the daemon's local control channel to parent. A valid stop request cancels the returned context, letting the ordinary watch cleanup path release every resource before the process exits.

type Paths

type Paths struct {
	Pidfile string
	Logfile string
}

Paths locates the daemon's pidfile (also the lock) and its log file.

func DefaultPaths

func DefaultPaths() (Paths, error)

DefaultPaths returns the per-user daemon paths under the config directory.

type RotatingLog added in v0.5.5

type RotatingLog struct {
	// contains filtered or unexported fields
}

RotatingLog is the sole writer for a daemon log. Rotation closes the active handle before renaming it, which is required while the daemon is running on Windows. The mutex keeps each Write and its rotation handoff indivisible.

func OpenLog added in v0.5.5

func OpenLog(path string) (*RotatingLog, error)

OpenLog opens the built-in daemon's bounded, owner-only log set.

func (*RotatingLog) Close added in v0.5.5

func (l *RotatingLog) Close() error

Close flushes and closes the active log. It is safe to call more than once.

func (*RotatingLog) Write added in v0.5.5

func (l *RotatingLog) Write(p []byte) (int, error)

Write appends all bytes in order, rotating before a record would cross the size boundary. An unusually large single write is split across bounded files without dropping bytes that still fit within the retained history.

When rotation fails, the record that triggered it is dropped rather than written through: writing through would defeat the disk bound rotation exists to enforce. The drop is not silent, though. It is counted, and as soon as a rotation next succeeds (in this call or a later one), or a plain write finds a drop still unreported, a synthesized notice line is written ahead of the caller's record so an operator reading the log can see how much was lost and why.

type StopOptions added in v0.5.5

type StopOptions struct {
	Timeout time.Duration
	Force   bool
}

StopOptions controls the bounded stop sequence.

type StopResult added in v0.5.5

type StopResult int

StopResult reports how a confirmed stop completed.

const (
	StoppedGracefully StopResult = iota
	StoppedForcefully
)

func Stop

func Stop(p Paths, opts StopOptions) (StopResult, error)

Stop requests graceful shutdown and does not succeed until the daemon lock is free. Force permits escalation after a failed request or timeout; the recorded per-run identity is revalidated immediately before terminating the process.

Jump to

Keyboard shortcuts

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