aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/system
diff options
context:
space:
mode:
Diffstat (limited to 'internal/system')
-rw-r--r--internal/system/dispatcher_test.go2
-rw-r--r--internal/system/system.go53
-rw-r--r--internal/system/system_test.go34
3 files changed, 58 insertions, 31 deletions
diff --git a/internal/system/dispatcher_test.go b/internal/system/dispatcher_test.go
index dd42b965..bfb8df72 100644
--- a/internal/system/dispatcher_test.go
+++ b/internal/system/dispatcher_test.go
@@ -208,7 +208,7 @@ func checkOpMeta(t *testing.T, testCases []opMetaTestCase) {
// InternalNew initialises [I] with a stub syscallDispatcher.
func InternalNew(t *testing.T, want stub.Expect, uid int) (*I, *stub.Stub[syscallDispatcher]) {
k := &kstub{stub.New(t, func(s *stub.Stub[syscallDispatcher]) syscallDispatcher { return &kstub{s} }, want)}
- sys := New(t.Context(), k, uid)
+ sys := New(t.Context(), k, uid, nil)
sys.syscallDispatcher = k
return sys, k.Stub
}
diff --git a/internal/system/system.go b/internal/system/system.go
index 1462f818..340e23ec 100644
--- a/internal/system/system.go
+++ b/internal/system/system.go
@@ -6,7 +6,9 @@ import (
"errors"
"strings"
+ "hakurei.app/check"
"hakurei.app/hst"
+ "hakurei.app/internal/lockedfile"
"hakurei.app/message"
)
@@ -67,14 +69,6 @@ func TypeString(e hst.Enablements) string {
}
}
-// New returns the address of a new [I] targeting uid.
-func New(ctx context.Context, msg message.Msg, uid int) (sys *I) {
- if ctx == nil || msg == nil || uid < 0 {
- panic("invalid call to New")
- }
- return &I{ctx: ctx, msg: msg, uid: uid, syscallDispatcher: direct{}}
-}
-
// An I provides deferred operating system interaction. [I] must not be copied.
// Methods of [I] must not be used concurrently.
type I struct {
@@ -84,15 +78,32 @@ type I struct {
ops []Op
ctx context.Context
- // the behaviour of Commit is only defined for up to one call
- committed bool
- // the behaviour of Revert is only defined for up to one call
- reverted bool
+ // The behaviour of Commit and Revert is only defined for at most one call.
+ committed, reverted bool
+ // Optional Commit and Revert big lock.
+ mu *lockedfile.Mutex
msg message.Msg
syscallDispatcher
}
+// New returns the address of a new [I] targeting uid.
+func New(
+ ctx context.Context,
+ msg message.Msg,
+ uid int,
+ lock *check.Absolute,
+) (sys *I) {
+ if ctx == nil || msg == nil || uid < 0 {
+ panic("invalid call to New")
+ }
+ v := I{ctx: ctx, msg: msg, uid: uid, syscallDispatcher: direct{}}
+ if lock != nil {
+ v.mu = lockedfile.MutexAt(lock.String())
+ }
+ return &v
+}
+
func (sys *I) UID() int { return sys.uid }
// Equal returns whether all [Op] instances held by sys matches that of target.
@@ -118,9 +129,18 @@ func (sys *I) Commit() error {
if sys.committed {
panic("attempting to commit twice")
}
+
+ if sys.mu != nil {
+ if unlock, err := sys.mu.Lock(); err != nil {
+ return err
+ } else {
+ defer unlock()
+ }
+ }
sys.committed = true
- sp := New(sys.ctx, sys.msg, sys.uid)
+ sp := New(sys.ctx, sys.msg, sys.uid, nil)
+ // lock is still held on revert
sp.syscallDispatcher = sys.syscallDispatcher
sp.ops = make([]Op, 0, len(sys.ops)) // prevent copies during commits
defer func() {
@@ -153,6 +173,13 @@ func (sys *I) Revert(ec *Criteria) error {
if sys.reverted {
panic("attempting to revert twice")
}
+ if sys.mu != nil {
+ if unlock, err := sys.mu.Lock(); err != nil {
+ return err
+ } else {
+ defer unlock()
+ }
+ }
sys.reverted = true
// collect errors
diff --git a/internal/system/system_test.go b/internal/system/system_test.go
index e466c171..8d863826 100644
--- a/internal/system/system_test.go
+++ b/internal/system/system_test.go
@@ -82,7 +82,7 @@ func TestNew(t *testing.T) {
t.Errorf("recover: %v, want %v", r, want)
}
}()
- New(nil, message.New(nil), 0)
+ New(nil, message.New(nil), 0, nil)
})
t.Run("msg", func(t *testing.T) {
@@ -92,7 +92,7 @@ func TestNew(t *testing.T) {
t.Errorf("recover: %v, want %v", r, want)
}
}()
- New(t.Context(), nil, 0)
+ New(t.Context(), nil, 0, nil)
})
t.Run("uid", func(t *testing.T) {
@@ -102,11 +102,11 @@ func TestNew(t *testing.T) {
t.Errorf("recover: %v, want %v", r, want)
}
}()
- New(t.Context(), message.New(nil), -1)
+ New(t.Context(), message.New(nil), -1, nil)
})
})
- sys := New(t.Context(), message.New(nil), 0xbeef)
+ sys := New(t.Context(), message.New(nil), 0xbeef, nil)
if sys.ctx == nil {
t.Error("New: ctx = nil")
}
@@ -125,51 +125,51 @@ func TestEqual(t *testing.T) {
want bool
}{
{"simple UID",
- New(t.Context(), message.New(nil), 150),
- New(t.Context(), message.New(nil), 150),
+ New(t.Context(), message.New(nil), 150, nil),
+ New(t.Context(), message.New(nil), 150, nil),
true},
{"simple UID differ",
- New(t.Context(), message.New(nil), 150),
- New(t.Context(), message.New(nil), 151),
+ New(t.Context(), message.New(nil), 150, nil),
+ New(t.Context(), message.New(nil), 151, nil),
false},
{"simple UID nil",
- New(t.Context(), message.New(nil), 150),
+ New(t.Context(), message.New(nil), 150, nil),
nil,
false},
{"op length mismatch",
- New(t.Context(), message.New(nil), 150).
+ New(t.Context(), message.New(nil), 150, nil).
ChangeHosts("chronos"),
- New(t.Context(), message.New(nil), 150).
+ New(t.Context(), message.New(nil), 150, nil).
ChangeHosts("chronos").
Ensure(m("/run"), 0755, false),
false},
{"op value mismatch",
- New(t.Context(), message.New(nil), 150).
+ New(t.Context(), message.New(nil), 150, nil).
ChangeHosts("chronos").
Ensure(m("/run"), 0644, false),
- New(t.Context(), message.New(nil), 150).
+ New(t.Context(), message.New(nil), 150, nil).
ChangeHosts("chronos").
Ensure(m("/run"), 0755, false),
false},
{"op type mismatch",
- New(t.Context(), message.New(nil), 150).
+ New(t.Context(), message.New(nil), 150, nil).
ChangeHosts("chronos").
Wayland(m("/proc/nonexistent/dst"), m("/proc/nonexistent/src"), "\x00", "\x00"),
- New(t.Context(), message.New(nil), 150).
+ New(t.Context(), message.New(nil), 150, nil).
ChangeHosts("chronos").
Ensure(m("/run"), 0755, false),
false},
{"op equals",
- New(t.Context(), message.New(nil), 150).
+ New(t.Context(), message.New(nil), 150, nil).
ChangeHosts("chronos").
Ensure(m("/run"), 0755, false),
- New(t.Context(), message.New(nil), 150).
+ New(t.Context(), message.New(nil), 150, nil).
ChangeHosts("chronos").
Ensure(m("/run"), 0755, false),
true},