daemon

package
v0.7.4 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2026 License: AGPL-3.0 Imports: 22 Imported by: 0

Documentation

Overview

Package daemon runs 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 (
	// ErrInstallUnsupported is returned when daemon install/uninstall is used
	// on an OS that has no login-agent implementation.
	ErrInstallUnsupported = errors.New("daemon install is only supported on macOS")
	// ErrNotInstalled is returned when uninstall finds no login agent.
	ErrNotInstalled = errors.New("login agent is not installed")
)
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 Restart added in v0.7.4

func Restart(self string, args []string) error

Restart replaces this process with self. args is the full argv, including args[0]. The caller must have released the daemon lock and closed the control socket first. On Unix this is exec and does not return on success. On Windows it detaches a new process and returns so the caller can exit.

func Start

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

Start launches `self args...` 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.

func Uninstall added in v0.7.1

func Uninstall() error

Uninstall removes the macOS login LaunchAgent. A launchd-managed daemon is stopped by the bootout; a process started by daemon start is left running.

Types

type InstallResult added in v0.7.1

type InstallResult struct {
	PlistPath string
	Started   bool
}

InstallResult is the outcome of a successful Install.

func Install added in v0.7.1

func Install(self, configPath string, p Paths) (InstallResult, error)

Install registers a macOS LaunchAgent that runs the periodic-sync daemon at Aqua login. launchd owns that process: the login equivalent of daemon start, without detaching out of the session (a Setsid child would survive logout and then fail the next login with ErrAlreadyRunning). Other OSes return ErrInstallUnsupported.

The installer's PATH is snapshotted into the plist because login agents do not source shell rc files, and sync shells out to git by name.

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