diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-08-30 18:46:43 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-08-30 18:46:43 +0900 |
| commit | f656968350be67b3458db01a8d75a8216f3238a8 (patch) | |
| tree | 74ec7b67f66c642804323f4e59c2f796e3758f85 /internal/store/segment.go | |
| parent | d4fab811d3857c36f1d8271a940ffca113e012b7 (diff) | |
internal/store: destroy stale instances
This recovers from inconsistent state on power loss and /tmp/ is not wiped on startup, or if hakurei somehow crashes.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/store/segment.go')
| -rw-r--r-- | internal/store/segment.go | 39 |
1 files changed, 37 insertions, 2 deletions
diff --git a/internal/store/segment.go b/internal/store/segment.go index 32c8a141..8b7dd0d6 100644 --- a/internal/store/segment.go +++ b/internal/store/segment.go @@ -7,6 +7,7 @@ import ( "os" "strconv" "sync" + "syscall" "hakurei.app/check" "hakurei.app/hst" @@ -76,13 +77,20 @@ func (eh *EntryHandle) save(state *hst.State) error { return err } +// KillFunc is the function signature of syscall.Kill. +type KillFunc func(pid int, sig syscall.Signal) (err error) + // Load loads and validates the state entry header, and returns the // [hst.Enablements] byte. For a non-nil v, the full state payload is decoded -// and stored in the value pointed to by v. +// and stored in the value pointed to by v, and if kill is non-nil, the presence +// of the monitoring process is checked, and a stale entry is destroyed. // // Load validates the embedded [hst.Config] value. A non-nil error returned by // Load is of type [hst.AppError]. -func (eh *EntryHandle) Load(v *hst.State) (hst.Enablements, error) { +func (eh *EntryHandle) Load( + v *hst.State, + kill KillFunc, +) (hst.Enablements, error) { f, err := eh.open(os.O_RDONLY, 0) if err != nil { return 0, err @@ -101,6 +109,33 @@ func (eh *EntryHandle) Load(v *hst.State) (hst.Enablements, error) { ), } } + if kill != nil { + errno := kill(v.PID, 0) + if errno != nil { + if !errors.Is(errno, syscall.ESRCH) { + err = &hst.AppError{ + Step: "check monitor process", + Err: errno, + } + } else { + if err = eh.Destroy(); err != nil { + err = &hst.AppError{ + Step: "destroy stale entry", + Err: err, + } + } else { + err = &hst.AppError{ + Step: "load stale entry", + Err: errno, + Msg: fmt.Sprintf( + "stale entry %s", + eh.ID.String(), + ), + } + } + } + } + } } else { et, err = entryDecodeHeader(f) } |
