aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/store/segment.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/store/segment.go')
-rw-r--r--internal/store/segment.go66
1 files changed, 58 insertions, 8 deletions
diff --git a/internal/store/segment.go b/internal/store/segment.go
index 06849749..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
@@ -92,8 +100,41 @@ func (eh *EntryHandle) Load(v *hst.State) (hst.Enablements, error) {
if v != nil {
et, err = entryDecode(f, v)
if err == nil && v.ID != eh.ID {
- err = &hst.AppError{Step: "validate state identifier", Err: os.ErrInvalid,
- Msg: fmt.Sprintf("state entry %s has unexpected id %s", eh.ID.String(), v.ID.String())}
+ err = &hst.AppError{
+ Step: "validate state identifier",
+ Err: os.ErrInvalid,
+ Msg: fmt.Sprintf(
+ "state entry %s has unexpected id %s",
+ eh.ID.String(), v.ID.String(),
+ ),
+ }
+ }
+ 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)
@@ -127,7 +168,10 @@ type Handle struct {
// A non-nil error returned by Lock is of type [hst.AppError].
func (h *Handle) Lock() (unlock func(), err error) {
if unlock, err = h.fileMu.Lock(); err != nil {
- return nil, &hst.AppError{Step: "acquire lock on store segment " + strconv.Itoa(h.Identity), Err: err}
+ return nil, &hst.AppError{
+ Step: "acquire lock on store segment " + strconv.Itoa(h.Identity),
+ Err: err,
+ }
}
return
}
@@ -174,8 +218,11 @@ func (h *Handle) Entries() (iter.Seq[*EntryHandle], int, error) {
// this should never happen
if ent.IsDir() {
- eh.DecodeErr = &hst.AppError{Step: step,
- Err: errors.New("unexpected directory " + strconv.Quote(ent.Name()) + " in store")}
+ eh.DecodeErr = &hst.AppError{
+ Step: step,
+ Err: errors.New("unexpected directory " +
+ strconv.Quote(ent.Name()) + " in store"),
+ }
goto out
}
@@ -186,7 +233,10 @@ func (h *Handle) Entries() (iter.Seq[*EntryHandle], int, error) {
// this either indicates a serious bug or external interference
if err := eh.ID.UnmarshalText([]byte(ent.Name())); err != nil {
- eh.DecodeErr = &hst.AppError{Step: "decode store segment entry", Err: err}
+ eh.DecodeErr = &hst.AppError{
+ Step: "decode store segment entry",
+ Err: err,
+ }
goto out
}