mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-15 04:53:00 +08:00
114 lines
4.4 KiB
Go
114 lines
4.4 KiB
Go
// Package daemon assembles and runs the long-running service.
|
|
//
|
|
// It is the only place that knows the service is a process: the lock that makes
|
|
// it single-instance, the socket it answers on, the snapshot a reader can see
|
|
// while it is stopped, and the helper that starts it again. Everything it
|
|
// coordinates -- configuration, scheduling, the projection -- is built
|
|
// elsewhere and handed in, so this package can be tested against a temporary
|
|
// directory rather than a router.
|
|
package daemon
|
|
|
|
import (
|
|
"io/fs"
|
|
"path/filepath"
|
|
|
|
"github.com/matthewlu070111/smart-srun/core/internal/update"
|
|
)
|
|
|
|
// Modes fixed by spec 02. The runtime directory holds a socket that accepts
|
|
// privileged commands, so it is not world-anything.
|
|
const (
|
|
RuntimeDirMode fs.FileMode = 0o700
|
|
RuntimeFileMode fs.FileMode = 0o600
|
|
)
|
|
|
|
// Paths locates everything the running service owns.
|
|
//
|
|
// A value rather than a set of constants, so a test can point the whole service
|
|
// at a temporary directory. The defaults are the ones spec 02 fixes, and the
|
|
// split between them matters: runtime state lives on tmpfs and is wiped by a
|
|
// reboot, configuration lives on flash and must survive one. Putting a
|
|
// scheduling artifact in the second is how a crash leaves something behind in
|
|
// the user's settings.
|
|
type Paths struct {
|
|
// Runtime is /var/run/smart-srun: 0700, tmpfs, not configuration.
|
|
Runtime string
|
|
// Config is /etc/smart-srun: 0700, flash, the user's own data.
|
|
Config string
|
|
// Optional resource overrides; zero values use the device contract paths.
|
|
BuiltinPresets string
|
|
PresetsCache string
|
|
Log string
|
|
}
|
|
|
|
func DefaultPaths() Paths {
|
|
return Paths{Runtime: "/var/run/smart-srun", Config: "/etc/smart-srun"}
|
|
}
|
|
|
|
// Socket is the local control socket. Unix domain only: spec 03 forbids
|
|
// listening on TCP, because a management port on a router is a management port
|
|
// on the internet the first time somebody opens the firewall.
|
|
func (p Paths) Socket() string { return filepath.Join(p.Runtime, "control.sock") }
|
|
|
|
// Lock is the single-instance lock file.
|
|
func (p Paths) Lock() string { return filepath.Join(p.Runtime, "daemon.lock") }
|
|
|
|
// State is the snapshot a reader can see even while the service is stopped.
|
|
func (p Paths) State() string { return filepath.Join(p.Runtime, "state.json") }
|
|
|
|
func (p Paths) QuietResume() string { return filepath.Join(p.Runtime, "quiet-uplink.json") }
|
|
func (p Paths) ManualPauses() string { return filepath.Join(p.Runtime, "manual-pauses.json") }
|
|
func (p Paths) PresetSchedule() string { return filepath.Join(p.Runtime, "presets-schedule.json") }
|
|
|
|
// UpdateStatus is written by the update worker alone, and read by LuCI from
|
|
// this fixed path while the main service is down.
|
|
func (p Paths) UpdateStatus() string {
|
|
return filepath.Join(p.Runtime, "update-status.json")
|
|
}
|
|
|
|
func (p Paths) ConfigFile() string { return filepath.Join(p.Config, "config.json") }
|
|
func (p Paths) UserPresets() string { return filepath.Join(p.Config, "user-presets.json") }
|
|
|
|
func (p Paths) PresetFile() string {
|
|
if p.BuiltinPresets != "" {
|
|
return p.BuiltinPresets
|
|
}
|
|
return "/usr/share/smart-srun/school-presets.json"
|
|
}
|
|
|
|
// LogFile is the structured event log.
|
|
//
|
|
// Under /tmp rather than Runtime, and deliberately not on flash: spec 02 puts
|
|
// the log on tmpfs because it is the one file this service writes continuously,
|
|
// and a router's flash is the part most likely to wear out.
|
|
func (p Paths) LogFile() string {
|
|
if p.Log != "" {
|
|
return p.Log
|
|
}
|
|
return "/tmp/smart-srun/plugin.log"
|
|
}
|
|
|
|
func (p Paths) PresetCacheFile() string {
|
|
if p.PresetsCache != "" {
|
|
return p.PresetsCache
|
|
}
|
|
return "/tmp/smart-srun/presets-cache.json"
|
|
}
|
|
|
|
// Recovery holds the minimal journals that must survive a reboot -- an
|
|
// interrupted wireless transaction, an interrupted install. It is under Config
|
|
// rather than Runtime for exactly that reason.
|
|
func (p Paths) Recovery() string { return filepath.Join(p.Config, "recovery") }
|
|
|
|
func (p Paths) Update() update.Paths { return update.Paths{Runtime: p.Runtime, Config: p.Config} }
|
|
|
|
// WirelessStaging is where a wireless change is built before it is published.
|
|
//
|
|
// Under Runtime, and deliberately not under Recovery: it is a scratch copy of
|
|
// /etc/config/wireless that only means anything during one transaction, and a
|
|
// reboot wiping it is the correct outcome. The journal that does have to
|
|
// survive is the other one.
|
|
func (p Paths) WirelessStaging() string {
|
|
return filepath.Join(p.Runtime, "wireless-staging")
|
|
}
|