message: relocate from container
All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Sandbox (push) Successful in 2m22s
Test / Hpkg (push) Successful in 4m2s
Test / Sandbox (race detector) (push) Successful in 4m28s
Test / Hakurei (race detector) (push) Successful in 5m21s
Test / Hakurei (push) Successful in 2m9s
Test / Flake checks (push) Successful in 1m29s
All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Sandbox (push) Successful in 2m22s
Test / Hpkg (push) Successful in 4m2s
Test / Sandbox (race detector) (push) Successful in 4m28s
Test / Hakurei (race detector) (push) Successful in 5m21s
Test / Hakurei (push) Successful in 2m9s
Test / Flake checks (push) Successful in 1m29s
This package is quite useful. This change allows it to be imported without importing container. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
df9b77b077
commit
87b5c30ef6
@ -15,17 +15,17 @@ import (
|
||||
"time"
|
||||
|
||||
"hakurei.app/command"
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/internal"
|
||||
"hakurei.app/internal/app"
|
||||
"hakurei.app/internal/app/state"
|
||||
"hakurei.app/message"
|
||||
"hakurei.app/system/dbus"
|
||||
)
|
||||
|
||||
func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningErrs, out io.Writer) command.Command {
|
||||
func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErrs, out io.Writer) command.Command {
|
||||
var (
|
||||
flagVerbose bool
|
||||
flagJSON bool
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"hakurei.app/command"
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func TestHelp(t *testing.T) {
|
||||
@ -69,7 +69,7 @@ Flags:
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
out := new(bytes.Buffer)
|
||||
c := buildCommand(t.Context(), container.NewMsg(nil), new(earlyHardeningErrs), out)
|
||||
c := buildCommand(t.Context(), message.NewMsg(nil), new(earlyHardeningErrs), out)
|
||||
if err := c.Parse(tc.args); !errors.Is(err, command.ErrHelp) && !errors.Is(err, flag.ErrHelp) {
|
||||
t.Errorf("Parse: error = %v; want %v",
|
||||
err, command.ErrHelp)
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"syscall"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -31,7 +32,7 @@ func main() {
|
||||
|
||||
log.SetPrefix("hakurei: ")
|
||||
log.SetFlags(0)
|
||||
msg := container.NewMsg(log.Default())
|
||||
msg := message.NewMsg(log.Default())
|
||||
|
||||
early := earlyHardeningErrs{
|
||||
yamaLSM: container.SetPtracer(0),
|
||||
|
@ -10,13 +10,13 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/internal/app"
|
||||
"hakurei.app/internal/app/state"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func tryPath(msg container.Msg, name string) (config *hst.Config) {
|
||||
func tryPath(msg message.Msg, name string) (config *hst.Config) {
|
||||
var r io.Reader
|
||||
config = new(hst.Config)
|
||||
|
||||
@ -49,7 +49,7 @@ func tryPath(msg container.Msg, name string) (config *hst.Config) {
|
||||
return
|
||||
}
|
||||
|
||||
func tryFd(msg container.Msg, name string) io.ReadCloser {
|
||||
func tryFd(msg message.Msg, name string) io.ReadCloser {
|
||||
if v, err := strconv.Atoi(name); err != nil {
|
||||
if !errors.Is(err, strconv.ErrSyntax) {
|
||||
msg.Verbosef("name cannot be interpreted as int64: %v", err)
|
||||
@ -68,7 +68,7 @@ func tryFd(msg container.Msg, name string) io.ReadCloser {
|
||||
}
|
||||
}
|
||||
|
||||
func tryShort(msg container.Msg, name string) (config *hst.Config, entry *state.State) {
|
||||
func tryShort(msg message.Msg, name string) (config *hst.Config, entry *state.State) {
|
||||
likePrefix := false
|
||||
if len(name) <= 32 {
|
||||
likePrefix = true
|
||||
|
@ -11,10 +11,10 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/internal/app"
|
||||
"hakurei.app/internal/app/state"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func printShowSystem(output io.Writer, short, flagJSON bool) {
|
||||
@ -56,7 +56,7 @@ func printShowInstance(
|
||||
|
||||
if err := config.Validate(); err != nil {
|
||||
valid = false
|
||||
if m, ok := container.GetErrorMessage(err); ok {
|
||||
if m, ok := message.GetMessage(err); ok {
|
||||
mustPrint(output, "Error: "+m+"!\n\n")
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,10 @@ import (
|
||||
"syscall"
|
||||
|
||||
"hakurei.app/command"
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -24,7 +24,7 @@ var (
|
||||
func main() {
|
||||
log.SetPrefix("hpkg: ")
|
||||
log.SetFlags(0)
|
||||
msg := container.NewMsg(log.Default())
|
||||
msg := message.NewMsg(log.Default())
|
||||
|
||||
if err := os.Setenv("SHELL", pathShell.String()); err != nil {
|
||||
log.Fatalf("cannot set $SHELL: %v", err)
|
||||
|
@ -7,10 +7,10 @@ import (
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
const bash = "bash"
|
||||
@ -52,7 +52,7 @@ func lookPath(file string) string {
|
||||
|
||||
var beforeRunFail = new(atomic.Pointer[func()])
|
||||
|
||||
func mustRun(msg container.Msg, name string, arg ...string) {
|
||||
func mustRun(msg message.Msg, name string, arg ...string) {
|
||||
msg.Verbosef("spawning process: %q %q", name, arg)
|
||||
cmd := exec.Command(name, arg...)
|
||||
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
|
@ -9,14 +9,14 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/internal"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
var hakureiPath = internal.MustHakureiPath()
|
||||
|
||||
func mustRunApp(ctx context.Context, msg container.Msg, config *hst.Config, beforeFail func()) {
|
||||
func mustRunApp(ctx context.Context, msg message.Msg, config *hst.Config, beforeFail func()) {
|
||||
var (
|
||||
cmd *exec.Cmd
|
||||
st io.WriteCloser
|
||||
|
@ -5,15 +5,15 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func withNixDaemon(
|
||||
ctx context.Context,
|
||||
msg container.Msg,
|
||||
msg message.Msg,
|
||||
action string, command []string, net bool, updateConfig func(config *hst.Config) *hst.Config,
|
||||
app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func(),
|
||||
) {
|
||||
@ -64,7 +64,7 @@ func withNixDaemon(
|
||||
|
||||
func withCacheDir(
|
||||
ctx context.Context,
|
||||
msg container.Msg,
|
||||
msg message.Msg,
|
||||
action string, command []string, workDir *check.Absolute,
|
||||
app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) {
|
||||
mustRunAppDropShell(ctx, msg, &hst.Config{
|
||||
@ -102,7 +102,7 @@ func withCacheDir(
|
||||
}, dropShell, beforeFail)
|
||||
}
|
||||
|
||||
func mustRunAppDropShell(ctx context.Context, msg container.Msg, config *hst.Config, dropShell bool, beforeFail func()) {
|
||||
func mustRunAppDropShell(ctx context.Context, msg message.Msg, config *hst.Config, dropShell bool, beforeFail func()) {
|
||||
if dropShell {
|
||||
if config.Container != nil {
|
||||
config.Container.Args = []string{bash, "-l"}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func init() { gob.Register(new(AutoRootOp)) }
|
||||
@ -81,7 +82,7 @@ func (r *AutoRootOp) String() string {
|
||||
}
|
||||
|
||||
// IsAutoRootBindable returns whether a dir entry name is selected for AutoRoot.
|
||||
func IsAutoRootBindable(msg Msg, name string) bool {
|
||||
func IsAutoRootBindable(msg message.Msg, name string) bool {
|
||||
switch name {
|
||||
case "proc", "dev", "tmp", "mnt", "etc":
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"hakurei.app/container/bits"
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/container/stub"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func TestAutoRootOp(t *testing.T) {
|
||||
@ -195,7 +196,7 @@ func TestIsAutoRootBindable(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var msg Msg
|
||||
var msg message.Msg
|
||||
if tc.log {
|
||||
msg = &kstub{nil, stub.New(t, func(s *stub.Stub[syscallDispatcher]) syscallDispatcher { panic("unreachable") }, stub.Expect{Calls: []stub.Call{
|
||||
call("verbose", stub.ExpectArgs{[]any{"got unexpected root entry"}}, nil, nil),
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/container/seccomp"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -52,7 +53,7 @@ type (
|
||||
|
||||
cmd *exec.Cmd
|
||||
ctx context.Context
|
||||
msg Msg
|
||||
msg message.Msg
|
||||
Params
|
||||
}
|
||||
|
||||
@ -396,9 +397,9 @@ func (p *Container) ProcessState() *os.ProcessState {
|
||||
}
|
||||
|
||||
// New returns the address to a new instance of [Container] that requires further initialisation before use.
|
||||
func New(ctx context.Context, msg Msg) *Container {
|
||||
func New(ctx context.Context, msg message.Msg) *Container {
|
||||
if msg == nil {
|
||||
msg = NewMsg(nil)
|
||||
msg = message.NewMsg(nil)
|
||||
}
|
||||
|
||||
p := &Container{ctx: ctx, msg: msg, Params: Params{Ops: new(Ops)}}
|
||||
@ -409,7 +410,7 @@ func New(ctx context.Context, msg Msg) *Container {
|
||||
}
|
||||
|
||||
// NewCommand calls [New] and initialises the [Params.Path] and [Params.Args] fields.
|
||||
func NewCommand(ctx context.Context, msg Msg, pathname *check.Absolute, name string, args ...string) *Container {
|
||||
func NewCommand(ctx context.Context, msg message.Msg, pathname *check.Absolute, name string, args ...string) *Container {
|
||||
z := New(ctx, msg)
|
||||
z.Path = pathname
|
||||
z.Args = append([]string{name}, args...)
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"hakurei.app/container/vfs"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/ldd"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func TestStartError(t *testing.T) {
|
||||
@ -152,13 +153,13 @@ func TestStartError(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("msg", func(t *testing.T) {
|
||||
if got, ok := container.GetErrorMessage(tc.err); !ok {
|
||||
if got, ok := message.GetMessage(tc.err); !ok {
|
||||
if tc.msg != "" {
|
||||
t.Errorf("GetErrorMessage: err does not implement MessageError")
|
||||
t.Errorf("GetMessage: err does not implement MessageError")
|
||||
}
|
||||
return
|
||||
} else if got != tc.msg {
|
||||
t.Errorf("GetErrorMessage: %q, want %q", got, tc.msg)
|
||||
t.Errorf("GetMessage: %q, want %q", got, tc.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -545,7 +546,7 @@ func testContainerCancel(
|
||||
}
|
||||
|
||||
func TestContainerString(t *testing.T) {
|
||||
msg := container.NewMsg(nil)
|
||||
msg := message.NewMsg(nil)
|
||||
c := container.NewCommand(t.Context(), msg, check.MustAbs("/run/current-system/sw/bin/ldd"), "ldd", "/usr/bin/env")
|
||||
c.SeccompFlags |= seccomp.AllowMultiarch
|
||||
c.SeccompRules = seccomp.Preset(
|
||||
@ -711,7 +712,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
func helperNewContainerLibPaths(ctx context.Context, libPaths *[]*check.Absolute, args ...string) (c *container.Container) {
|
||||
msg := container.NewMsg(nil)
|
||||
msg := message.NewMsg(nil)
|
||||
c = container.NewCommand(ctx, msg, absHelperInnerPath, "helper", args...)
|
||||
c.Env = append(c.Env, envDoCheck+"=1")
|
||||
c.Bind(check.MustAbs(os.Args[0]), absHelperInnerPath, 0)
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"syscall"
|
||||
|
||||
"hakurei.app/container/seccomp"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
type osFile interface {
|
||||
@ -37,7 +38,7 @@ type syscallDispatcher interface {
|
||||
setNoNewPrivs() error
|
||||
|
||||
// lastcap provides [LastCap].
|
||||
lastcap(msg Msg) uintptr
|
||||
lastcap(msg message.Msg) uintptr
|
||||
// capset provides capset.
|
||||
capset(hdrp *capHeader, datap *[2]capData) error
|
||||
// capBoundingSetDrop provides capBoundingSetDrop.
|
||||
@ -52,9 +53,9 @@ type syscallDispatcher interface {
|
||||
receive(key string, e any, fdp *uintptr) (closeFunc func() error, err error)
|
||||
|
||||
// bindMount provides procPaths.bindMount.
|
||||
bindMount(msg Msg, source, target string, flags uintptr) error
|
||||
bindMount(msg message.Msg, source, target string, flags uintptr) error
|
||||
// remount provides procPaths.remount.
|
||||
remount(msg Msg, target string, flags uintptr) error
|
||||
remount(msg message.Msg, target string, flags uintptr) error
|
||||
// mountTmpfs provides mountTmpfs.
|
||||
mountTmpfs(fsname, target string, flags uintptr, size int, perm os.FileMode) error
|
||||
// ensureFile provides ensureFile.
|
||||
@ -122,11 +123,11 @@ type syscallDispatcher interface {
|
||||
wait4(pid int, wstatus *syscall.WaitStatus, options int, rusage *syscall.Rusage) (wpid int, err error)
|
||||
|
||||
// printf provides the Printf method of [log.Logger].
|
||||
printf(msg Msg, format string, v ...any)
|
||||
printf(msg message.Msg, format string, v ...any)
|
||||
// fatal provides the Fatal method of [log.Logger]
|
||||
fatal(msg Msg, v ...any)
|
||||
fatal(msg message.Msg, v ...any)
|
||||
// fatalf provides the Fatalf method of [log.Logger]
|
||||
fatalf(msg Msg, format string, v ...any)
|
||||
fatalf(msg message.Msg, format string, v ...any)
|
||||
}
|
||||
|
||||
// direct implements syscallDispatcher on the current kernel.
|
||||
@ -140,7 +141,7 @@ func (direct) setPtracer(pid uintptr) error { return SetPtracer(pid) }
|
||||
func (direct) setDumpable(dumpable uintptr) error { return SetDumpable(dumpable) }
|
||||
func (direct) setNoNewPrivs() error { return SetNoNewPrivs() }
|
||||
|
||||
func (direct) lastcap(msg Msg) uintptr { return LastCap(msg) }
|
||||
func (direct) lastcap(msg message.Msg) uintptr { return LastCap(msg) }
|
||||
func (direct) capset(hdrp *capHeader, datap *[2]capData) error { return capset(hdrp, datap) }
|
||||
func (direct) capBoundingSetDrop(cap uintptr) error { return capBoundingSetDrop(cap) }
|
||||
func (direct) capAmbientClearAll() error { return capAmbientClearAll() }
|
||||
@ -150,10 +151,10 @@ func (direct) receive(key string, e any, fdp *uintptr) (func() error, error) {
|
||||
return Receive(key, e, fdp)
|
||||
}
|
||||
|
||||
func (direct) bindMount(msg Msg, source, target string, flags uintptr) error {
|
||||
func (direct) bindMount(msg message.Msg, source, target string, flags uintptr) error {
|
||||
return hostProc.bindMount(msg, source, target, flags)
|
||||
}
|
||||
func (direct) remount(msg Msg, target string, flags uintptr) error {
|
||||
func (direct) remount(msg message.Msg, target string, flags uintptr) error {
|
||||
return hostProc.remount(msg, target, flags)
|
||||
}
|
||||
func (k direct) mountTmpfs(fsname, target string, flags uintptr, size int, perm os.FileMode) error {
|
||||
@ -221,6 +222,6 @@ func (direct) wait4(pid int, wstatus *syscall.WaitStatus, options int, rusage *s
|
||||
return syscall.Wait4(pid, wstatus, options, rusage)
|
||||
}
|
||||
|
||||
func (direct) printf(msg Msg, format string, v ...any) { msg.GetLogger().Printf(format, v...) }
|
||||
func (direct) fatal(msg Msg, v ...any) { msg.GetLogger().Fatal(v...) }
|
||||
func (direct) fatalf(msg Msg, format string, v ...any) { msg.GetLogger().Fatalf(format, v...) }
|
||||
func (direct) printf(msg message.Msg, format string, v ...any) { msg.GetLogger().Printf(format, v...) }
|
||||
func (direct) fatal(msg message.Msg, v ...any) { msg.GetLogger().Fatal(v...) }
|
||||
func (direct) fatalf(msg message.Msg, format string, v ...any) { msg.GetLogger().Fatalf(format, v...) }
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
|
||||
"hakurei.app/container/seccomp"
|
||||
"hakurei.app/container/stub"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
type opValidTestCase struct {
|
||||
@ -329,7 +330,7 @@ func (k *kstub) setDumpable(dumpable uintptr) error {
|
||||
}
|
||||
|
||||
func (k *kstub) setNoNewPrivs() error { k.Helper(); return k.Expects("setNoNewPrivs").Err }
|
||||
func (k *kstub) lastcap(msg Msg) uintptr {
|
||||
func (k *kstub) lastcap(msg message.Msg) uintptr {
|
||||
k.Helper()
|
||||
k.checkMsg(msg)
|
||||
return k.Expects("lastcap").Ret.(uintptr)
|
||||
@ -409,7 +410,7 @@ func (k *kstub) receive(key string, e any, fdp *uintptr) (closeFunc func() error
|
||||
return
|
||||
}
|
||||
|
||||
func (k *kstub) bindMount(msg Msg, source, target string, flags uintptr) error {
|
||||
func (k *kstub) bindMount(msg message.Msg, source, target string, flags uintptr) error {
|
||||
k.Helper()
|
||||
k.checkMsg(msg)
|
||||
return k.Expects("bindMount").Error(
|
||||
@ -418,7 +419,7 @@ func (k *kstub) bindMount(msg Msg, source, target string, flags uintptr) error {
|
||||
stub.CheckArg(k.Stub, "flags", flags, 2))
|
||||
}
|
||||
|
||||
func (k *kstub) remount(msg Msg, target string, flags uintptr) error {
|
||||
func (k *kstub) remount(msg message.Msg, target string, flags uintptr) error {
|
||||
k.Helper()
|
||||
k.checkMsg(msg)
|
||||
return k.Expects("remount").Error(
|
||||
@ -702,7 +703,7 @@ func (k *kstub) wait4(pid int, wstatus *syscall.WaitStatus, options int, rusage
|
||||
return
|
||||
}
|
||||
|
||||
func (k *kstub) printf(_ Msg, format string, v ...any) {
|
||||
func (k *kstub) printf(_ message.Msg, format string, v ...any) {
|
||||
k.Helper()
|
||||
if k.Expects("printf").Error(
|
||||
stub.CheckArg(k.Stub, "format", format, 0),
|
||||
@ -711,7 +712,7 @@ func (k *kstub) printf(_ Msg, format string, v ...any) {
|
||||
}
|
||||
}
|
||||
|
||||
func (k *kstub) fatal(_ Msg, v ...any) {
|
||||
func (k *kstub) fatal(_ message.Msg, v ...any) {
|
||||
k.Helper()
|
||||
if k.Expects("fatal").Error(
|
||||
stub.CheckArgReflect(k.Stub, "v", v, 0)) != nil {
|
||||
@ -720,7 +721,7 @@ func (k *kstub) fatal(_ Msg, v ...any) {
|
||||
panic(stub.PanicExit)
|
||||
}
|
||||
|
||||
func (k *kstub) fatalf(_ Msg, format string, v ...any) {
|
||||
func (k *kstub) fatalf(_ message.Msg, format string, v ...any) {
|
||||
k.Helper()
|
||||
if k.Expects("fatalf").Error(
|
||||
stub.CheckArg(k.Stub, "format", format, 0),
|
||||
@ -730,7 +731,7 @@ func (k *kstub) fatalf(_ Msg, format string, v ...any) {
|
||||
panic(stub.PanicExit)
|
||||
}
|
||||
|
||||
func (k *kstub) checkMsg(msg Msg) {
|
||||
func (k *kstub) checkMsg(msg message.Msg) {
|
||||
k.Helper()
|
||||
var target *kstub
|
||||
|
||||
|
@ -3,6 +3,8 @@ package container
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -10,7 +12,7 @@ var (
|
||||
executableOnce sync.Once
|
||||
)
|
||||
|
||||
func copyExecutable(msg Msg) {
|
||||
func copyExecutable(msg message.Msg) {
|
||||
if name, err := os.Executable(); err != nil {
|
||||
msg.BeforeExit()
|
||||
msg.GetLogger().Fatalf("cannot read executable path: %v", err)
|
||||
@ -19,7 +21,7 @@ func copyExecutable(msg Msg) {
|
||||
}
|
||||
}
|
||||
|
||||
func MustExecutable(msg Msg) string {
|
||||
func MustExecutable(msg message.Msg) string {
|
||||
executableOnce.Do(func() { copyExecutable(msg) })
|
||||
return executable
|
||||
}
|
||||
|
@ -5,11 +5,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func TestExecutable(t *testing.T) {
|
||||
for i := 0; i < 16; i++ {
|
||||
if got := container.MustExecutable(container.NewMsg(nil)); got != os.Args[0] {
|
||||
if got := container.MustExecutable(message.NewMsg(nil)); got != os.Args[0] {
|
||||
t.Errorf("MustExecutable: %q, want %q",
|
||||
got, os.Args[0])
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/container/seccomp"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -61,7 +62,7 @@ type (
|
||||
setupState struct {
|
||||
nonrepeatable uintptr
|
||||
*Params
|
||||
Msg
|
||||
message.Msg
|
||||
}
|
||||
)
|
||||
|
||||
@ -95,14 +96,14 @@ type initParams struct {
|
||||
}
|
||||
|
||||
// Init is called by [TryArgv0] if the current process is the container init.
|
||||
func Init(msg Msg) {
|
||||
func Init(msg message.Msg) {
|
||||
if msg == nil {
|
||||
panic("attempting to call initEntrypoint with nil msg")
|
||||
}
|
||||
initEntrypoint(direct{}, msg)
|
||||
}
|
||||
|
||||
func initEntrypoint(k syscallDispatcher, msg Msg) {
|
||||
func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
||||
k.lockOSThread()
|
||||
|
||||
if k.getpid() != 1 {
|
||||
@ -125,7 +126,7 @@ func initEntrypoint(k syscallDispatcher, msg Msg) {
|
||||
k.fatal(msg, "invalid setup descriptor")
|
||||
}
|
||||
if errors.Is(err, ErrReceiveEnv) {
|
||||
k.fatal(msg, "HAKUREI_SETUP not set")
|
||||
k.fatal(msg, setupEnv+" not set")
|
||||
}
|
||||
|
||||
k.fatalf(msg, "cannot decode init setup payload: %v", err)
|
||||
@ -448,11 +449,11 @@ const initName = "init"
|
||||
|
||||
// TryArgv0 calls [Init] if the last element of argv0 is "init".
|
||||
// If a nil msg is passed, the system logger is used instead.
|
||||
func TryArgv0(msg Msg) {
|
||||
func TryArgv0(msg message.Msg) {
|
||||
if msg == nil {
|
||||
log.SetPrefix(initName + ": ")
|
||||
log.SetFlags(0)
|
||||
msg = NewMsg(log.Default())
|
||||
msg = message.NewMsg(log.Default())
|
||||
}
|
||||
|
||||
if len(os.Args) > 0 && path.Base(os.Args[0]) == initName {
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
. "syscall"
|
||||
|
||||
"hakurei.app/container/vfs"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -87,7 +88,7 @@ const (
|
||||
)
|
||||
|
||||
// bindMount mounts source on target and recursively applies flags if MS_REC is set.
|
||||
func (p *procPaths) bindMount(msg Msg, source, target string, flags uintptr) error {
|
||||
func (p *procPaths) bindMount(msg message.Msg, source, target string, flags uintptr) error {
|
||||
// syscallDispatcher.bindMount and procPaths.remount must not be called from this function
|
||||
|
||||
if err := p.k.mount(source, target, FstypeNULL, MS_SILENT|MS_BIND|flags&MS_REC, zeroString); err != nil {
|
||||
@ -97,7 +98,7 @@ func (p *procPaths) bindMount(msg Msg, source, target string, flags uintptr) err
|
||||
}
|
||||
|
||||
// remount applies flags on target, recursively if MS_REC is set.
|
||||
func (p *procPaths) remount(msg Msg, target string, flags uintptr) error {
|
||||
func (p *procPaths) remount(msg message.Msg, target string, flags uintptr) error {
|
||||
// syscallDispatcher methods bindMount, remount must not be called from this function
|
||||
|
||||
var targetFinal string
|
||||
@ -159,7 +160,7 @@ func (p *procPaths) remount(msg Msg, target string, flags uintptr) error {
|
||||
}
|
||||
|
||||
// remountWithFlags remounts mount point described by [vfs.MountInfoNode].
|
||||
func remountWithFlags(k syscallDispatcher, msg Msg, n *vfs.MountInfoNode, mf uintptr) error {
|
||||
func remountWithFlags(k syscallDispatcher, msg message.Msg, n *vfs.MountInfoNode, mf uintptr) error {
|
||||
// syscallDispatcher methods bindMount, remount must not be called from this function
|
||||
|
||||
kf, unmatched := n.Flags()
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -23,7 +24,7 @@ const (
|
||||
kernelCapLastCapPath = fhs.ProcSys + "kernel/cap_last_cap"
|
||||
)
|
||||
|
||||
func mustReadSysctl(msg Msg) {
|
||||
func mustReadSysctl(msg message.Msg) {
|
||||
sysctlOnce.Do(func() {
|
||||
if v, err := os.ReadFile(kernelOverflowuidPath); err != nil {
|
||||
msg.GetLogger().Fatalf("cannot read %q: %v", kernelOverflowuidPath, err)
|
||||
@ -45,6 +46,6 @@ func mustReadSysctl(msg Msg) {
|
||||
})
|
||||
}
|
||||
|
||||
func OverflowUid(msg Msg) int { mustReadSysctl(msg); return kernelOverflowuid }
|
||||
func OverflowGid(msg Msg) int { mustReadSysctl(msg); return kernelOverflowgid }
|
||||
func LastCap(msg Msg) uintptr { mustReadSysctl(msg); return uintptr(kernelCapLastCap) }
|
||||
func OverflowUid(msg message.Msg) int { mustReadSysctl(msg); return kernelOverflowuid }
|
||||
func OverflowGid(msg message.Msg) int { mustReadSysctl(msg); return kernelOverflowgid }
|
||||
func LastCap(msg message.Msg) uintptr { mustReadSysctl(msg); return uintptr(kernelCapLastCap) }
|
||||
|
@ -12,12 +12,13 @@ import (
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/helper/proc"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
// New initialises a Helper instance with wt as the null-terminated argument writer.
|
||||
func New(
|
||||
ctx context.Context,
|
||||
msg container.Msg,
|
||||
msg message.Msg,
|
||||
pathname *check.Absolute, name string,
|
||||
wt io.WriterTo,
|
||||
stat bool,
|
||||
|
@ -5,8 +5,8 @@ import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func TestBadInterfaceError(t *testing.T) {
|
||||
@ -26,10 +26,10 @@ func TestBadInterfaceError(t *testing.T) {
|
||||
if gotError := tc.err.Error(); gotError != tc.want {
|
||||
t.Errorf("Error: %s, want %s", gotError, tc.want)
|
||||
}
|
||||
if gotMessage, ok := container.GetErrorMessage(tc.err); !ok {
|
||||
t.Error("GetErrorMessage: ok = false")
|
||||
if gotMessage, ok := message.GetMessage(tc.err); !ok {
|
||||
t.Error("GetMessage: ok = false")
|
||||
} else if gotMessage != tc.want {
|
||||
t.Errorf("GetErrorMessage: %s, want %s", gotMessage, tc.want)
|
||||
t.Errorf("GetMessage: %s, want %s", gotMessage, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ import (
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/stub"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func TestAppError(t *testing.T) {
|
||||
@ -65,14 +65,14 @@ func TestAppError(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("message", func(t *testing.T) {
|
||||
gotMessage, gotMessageOk := container.GetErrorMessage(tc.err)
|
||||
gotMessage, gotMessageOk := message.GetMessage(tc.err)
|
||||
if want := tc.message != "\x00"; gotMessageOk != want {
|
||||
t.Errorf("GetErrorMessage: ok = %v, want %v", gotMessage, want)
|
||||
t.Errorf("GetMessage: ok = %v, want %v", gotMessage, want)
|
||||
}
|
||||
|
||||
if gotMessageOk {
|
||||
if gotMessage != tc.message {
|
||||
t.Errorf("GetErrorMessage: %s, want %s", gotMessage, tc.message)
|
||||
t.Errorf("GetMessage: %s, want %s", gotMessage, tc.message)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -6,13 +6,13 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/internal/app/state"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
// Main runs an app according to [hst.Config] and terminates. Main does not return.
|
||||
func Main(ctx context.Context, msg container.Msg, config *hst.Config) {
|
||||
func Main(ctx context.Context, msg message.Msg, config *hst.Config) {
|
||||
var id state.ID
|
||||
if err := state.NewAppID(&id); err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -23,12 +23,13 @@ import (
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/internal/app/state"
|
||||
"hakurei.app/message"
|
||||
"hakurei.app/system"
|
||||
"hakurei.app/system/acl"
|
||||
)
|
||||
|
||||
func TestApp(t *testing.T) {
|
||||
msg := container.NewMsg(nil)
|
||||
msg := message.NewMsg(nil)
|
||||
msg.SwapVerbose(testing.Verbose())
|
||||
|
||||
testCases := []struct {
|
||||
@ -744,8 +745,8 @@ func (k *stubNixOS) cmdOutput(cmd *exec.Cmd) ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (k *stubNixOS) overflowUid(container.Msg) int { return 65534 }
|
||||
func (k *stubNixOS) overflowGid(container.Msg) int { return 65534 }
|
||||
func (k *stubNixOS) overflowUid(message.Msg) int { return 65534 }
|
||||
func (k *stubNixOS) overflowGid(message.Msg) int { return 65534 }
|
||||
|
||||
func (k *stubNixOS) mustHsuPath() *check.Absolute { return m("/proc/nonexistent/hsu") }
|
||||
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/internal"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
// osFile represents [os.File].
|
||||
@ -53,9 +54,9 @@ type syscallDispatcher interface {
|
||||
cmdOutput(cmd *exec.Cmd) ([]byte, error)
|
||||
|
||||
// overflowUid provides [container.OverflowUid].
|
||||
overflowUid(msg container.Msg) int
|
||||
overflowUid(msg message.Msg) int
|
||||
// overflowGid provides [container.OverflowGid].
|
||||
overflowGid(msg container.Msg) int
|
||||
overflowGid(msg message.Msg) int
|
||||
|
||||
// mustHsuPath provides [internal.MustHsuPath].
|
||||
mustHsuPath() *check.Absolute
|
||||
@ -90,8 +91,8 @@ func (direct) lookupGroupId(name string) (gid string, err error) {
|
||||
|
||||
func (direct) cmdOutput(cmd *exec.Cmd) ([]byte, error) { return cmd.Output() }
|
||||
|
||||
func (direct) overflowUid(msg container.Msg) int { return container.OverflowUid(msg) }
|
||||
func (direct) overflowGid(msg container.Msg) int { return container.OverflowGid(msg) }
|
||||
func (direct) overflowUid(msg message.Msg) int { return container.OverflowUid(msg) }
|
||||
func (direct) overflowGid(msg message.Msg) int { return container.OverflowGid(msg) }
|
||||
|
||||
func (direct) mustHsuPath() *check.Absolute { return internal.MustHsuPath() }
|
||||
|
||||
|
@ -4,8 +4,8 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
type panicDispatcher struct{}
|
||||
@ -21,7 +21,7 @@ func (panicDispatcher) tempdir() string { panic("unreachab
|
||||
func (panicDispatcher) evalSymlinks(string) (string, error) { panic("unreachable") }
|
||||
func (panicDispatcher) lookupGroupId(string) (string, error) { panic("unreachable") }
|
||||
func (panicDispatcher) cmdOutput(*exec.Cmd) ([]byte, error) { panic("unreachable") }
|
||||
func (panicDispatcher) overflowUid(container.Msg) int { panic("unreachable") }
|
||||
func (panicDispatcher) overflowGid(container.Msg) int { panic("unreachable") }
|
||||
func (panicDispatcher) overflowUid(message.Msg) int { panic("unreachable") }
|
||||
func (panicDispatcher) overflowGid(message.Msg) int { panic("unreachable") }
|
||||
func (panicDispatcher) mustHsuPath() *check.Absolute { panic("unreachable") }
|
||||
func (panicDispatcher) fatalf(string, ...any) { panic("unreachable") }
|
||||
|
@ -8,9 +8,9 @@ import (
|
||||
"os/user"
|
||||
"sync/atomic"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/internal/app/state"
|
||||
"hakurei.app/message"
|
||||
"hakurei.app/system"
|
||||
)
|
||||
|
||||
@ -37,7 +37,7 @@ type outcome struct {
|
||||
syscallDispatcher
|
||||
}
|
||||
|
||||
func (k *outcome) finalise(ctx context.Context, msg container.Msg, id *state.ID, config *hst.Config) error {
|
||||
func (k *outcome) finalise(ctx context.Context, msg message.Msg, id *state.ID, config *hst.Config) error {
|
||||
if ctx == nil || id == nil {
|
||||
// unreachable
|
||||
panic("invalid call to finalise")
|
||||
|
@ -9,9 +9,9 @@ import (
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
// Hsu caches responses from cmd/hsu.
|
||||
@ -74,7 +74,7 @@ func (h *Hsu) ID() (int, error) {
|
||||
func (h *Hsu) MustID() int { return h.MustIDMsg(nil) }
|
||||
|
||||
// MustIDMsg implements MustID with a custom [container.Msg].
|
||||
func (h *Hsu) MustIDMsg(msg container.Msg) int {
|
||||
func (h *Hsu) MustIDMsg(msg message.Msg) int {
|
||||
id, err := h.ID()
|
||||
if err == nil {
|
||||
return id
|
||||
@ -87,7 +87,7 @@ func (h *Hsu) MustIDMsg(msg container.Msg) int {
|
||||
}
|
||||
os.Exit(1)
|
||||
return -0xdeadbeef
|
||||
} else if m, ok := container.GetErrorMessage(err); ok {
|
||||
} else if m, ok := message.GetMessage(err); ok {
|
||||
log.Fatal(m)
|
||||
return -0xdeadbeef
|
||||
} else {
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/internal/app/state"
|
||||
"hakurei.app/message"
|
||||
"hakurei.app/system"
|
||||
"hakurei.app/system/acl"
|
||||
)
|
||||
@ -61,7 +62,7 @@ type outcomeState struct {
|
||||
// Copied via populateLocal.
|
||||
k syscallDispatcher
|
||||
// Copied via populateLocal.
|
||||
msg container.Msg
|
||||
msg message.Msg
|
||||
}
|
||||
|
||||
// valid checks outcomeState to be safe for use with outcomeOp.
|
||||
@ -75,7 +76,7 @@ func (s *outcomeState) valid() bool {
|
||||
|
||||
// populateEarly populates exported fields via syscallDispatcher.
|
||||
// This must only be called from the priv side.
|
||||
func (s *outcomeState) populateEarly(k syscallDispatcher, msg container.Msg, config *hst.Config) {
|
||||
func (s *outcomeState) populateEarly(k syscallDispatcher, msg message.Msg, config *hst.Config) {
|
||||
s.Shim = &shimParams{PrivPID: os.Getpid(), Verbose: msg.IsVerbose(), Ops: fromConfig(config)}
|
||||
|
||||
// enforce bounds and default early
|
||||
@ -98,7 +99,7 @@ func (s *outcomeState) populateEarly(k syscallDispatcher, msg container.Msg, con
|
||||
|
||||
// populateLocal populates unexported fields from transmitted exported fields.
|
||||
// These fields are cheaper to recompute per-process.
|
||||
func (s *outcomeState) populateLocal(k syscallDispatcher, msg container.Msg) error {
|
||||
func (s *outcomeState) populateLocal(k syscallDispatcher, msg message.Msg) error {
|
||||
if !s.valid() || k == nil || msg == nil {
|
||||
return newWithMessage("impossible outcome state reached")
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/internal"
|
||||
"hakurei.app/internal/app/state"
|
||||
"hakurei.app/message"
|
||||
"hakurei.app/system"
|
||||
)
|
||||
|
||||
@ -40,7 +41,7 @@ type mainState struct {
|
||||
cmdWait chan error
|
||||
|
||||
k *outcome
|
||||
container.Msg
|
||||
message.Msg
|
||||
uintptr
|
||||
}
|
||||
|
||||
@ -207,7 +208,7 @@ func (ms mainState) fatal(fallback string, ferr error) {
|
||||
}
|
||||
|
||||
// main carries out outcome and terminates. main does not return.
|
||||
func (k *outcome) main(msg container.Msg) {
|
||||
func (k *outcome) main(msg message.Msg) {
|
||||
if !k.active.CompareAndSwap(false, true) {
|
||||
panic("outcome: attempted to run twice")
|
||||
}
|
||||
@ -312,10 +313,10 @@ func (k *outcome) main(msg container.Msg) {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// printMessageError prints the error message according to [container.GetErrorMessage],
|
||||
// printMessageError prints the error message according to [message.GetMessage],
|
||||
// or fallback prepended to err if an error message is not available.
|
||||
func printMessageError(fallback string, err error) {
|
||||
m, ok := container.GetErrorMessage(err)
|
||||
m, ok := message.GetMessage(err)
|
||||
if !ok {
|
||||
log.Println(fallback, err)
|
||||
return
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"hakurei.app/container/bits"
|
||||
"hakurei.app/container/seccomp"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
//#include "shim-signal.h"
|
||||
@ -57,7 +58,7 @@ func (p *shimParams) valid() bool {
|
||||
func ShimMain() {
|
||||
log.SetPrefix("shim: ")
|
||||
log.SetFlags(0)
|
||||
msg := container.NewMsg(log.Default())
|
||||
msg := message.NewMsg(log.Default())
|
||||
|
||||
if err := container.SetDumpable(container.SUID_DUMP_DISABLE); err != nil {
|
||||
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||
@ -81,7 +82,7 @@ func ShimMain() {
|
||||
closeSetup = f
|
||||
|
||||
if err = state.populateLocal(direct{}, msg); err != nil {
|
||||
if m, ok := container.GetErrorMessage(err); ok {
|
||||
if m, ok := message.GetMessage(err); ok {
|
||||
log.Fatal(m)
|
||||
} else {
|
||||
log.Fatalf("cannot populate local state: %v", err)
|
||||
@ -114,7 +115,7 @@ func ShimMain() {
|
||||
}
|
||||
for _, op := range state.Shim.Ops {
|
||||
if err := op.toContainer(&stateParams); err != nil {
|
||||
if m, ok := container.GetErrorMessage(err); ok {
|
||||
if m, ok := message.GetMessage(err); ok {
|
||||
log.Fatal(m)
|
||||
} else {
|
||||
log.Fatalf("cannot create container state: %v", err)
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/container/seccomp"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/message"
|
||||
"hakurei.app/system/dbus"
|
||||
)
|
||||
|
||||
@ -299,7 +300,7 @@ func resolveRoot(c *hst.ContainerConfig) (rootfs hst.FilesystemConfig, filesyste
|
||||
}
|
||||
|
||||
// evalSymlinks calls syscallDispatcher.evalSymlinks but discards errors unwrapping to [fs.ErrNotExist].
|
||||
func evalSymlinks(msg container.Msg, k syscallDispatcher, v *string) error {
|
||||
func evalSymlinks(msg message.Msg, k syscallDispatcher, v *string) error {
|
||||
if p, err := k.evalSymlinks(*v); err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
|
@ -11,8 +11,8 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
// fine-grained locking and access
|
||||
@ -22,7 +22,7 @@ type multiStore struct {
|
||||
// initialised backends
|
||||
backends *sync.Map
|
||||
|
||||
msg container.Msg
|
||||
msg message.Msg
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
@ -280,7 +280,7 @@ func (b *multiBackend) close() error {
|
||||
}
|
||||
|
||||
// NewMulti returns an instance of the multi-file store.
|
||||
func NewMulti(msg container.Msg, runDir string) Store {
|
||||
func NewMulti(msg message.Msg, runDir string) Store {
|
||||
return &multiStore{
|
||||
msg: msg,
|
||||
base: path.Join(runDir, "state"),
|
||||
|
@ -4,10 +4,10 @@ import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/internal/app/state"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func TestMulti(t *testing.T) {
|
||||
testStore(t, state.NewMulti(container.NewMsg(log.New(log.Writer(), "multi: ", 0)), t.TempDir()))
|
||||
testStore(t, state.NewMulti(message.NewMsg(log.New(log.Writer(), "multi: ", 0)), t.TempDir()))
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/container/fhs"
|
||||
"hakurei.app/container/seccomp"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -25,7 +26,7 @@ var (
|
||||
msgStaticGlibc = []byte("not a dynamic executable")
|
||||
)
|
||||
|
||||
func Exec(ctx context.Context, msg container.Msg, p string) ([]*Entry, error) {
|
||||
func Exec(ctx context.Context, msg message.Msg, p string) ([]*Entry, error) {
|
||||
c, cancel := context.WithTimeout(ctx, lddTimeout)
|
||||
defer cancel()
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
package container
|
||||
// Package message provides interfaces and a base implementation for extended reporting on top of [log.Logger]
|
||||
package message
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -6,19 +7,19 @@ import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// MessageError is an error with a user-facing message.
|
||||
type MessageError interface {
|
||||
// Error is an error with a user-facing message.
|
||||
type Error interface {
|
||||
// Message returns a user-facing error message.
|
||||
Message() string
|
||||
|
||||
error
|
||||
}
|
||||
|
||||
// GetErrorMessage returns whether an error implements [MessageError], and the message if it does.
|
||||
func GetErrorMessage(err error) (string, bool) {
|
||||
var e MessageError
|
||||
// GetMessage returns whether an error implements [Error], and the message if it does.
|
||||
func GetMessage(err error) (string, bool) {
|
||||
var e Error
|
||||
if !errors.As(err, &e) || e == nil {
|
||||
return zeroString, false
|
||||
return "", false
|
||||
}
|
||||
return e.Message(), true
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package container_test
|
||||
package message_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/stub"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func TestMessageError(t *testing.T) {
|
||||
@ -29,12 +30,12 @@ func TestMessageError(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, ok := container.GetErrorMessage(tc.err)
|
||||
got, ok := message.GetMessage(tc.err)
|
||||
if got != tc.want {
|
||||
t.Errorf("GetErrorMessage: %q, want %q", got, tc.want)
|
||||
t.Errorf("GetMessage: %q, want %q", got, tc.want)
|
||||
}
|
||||
if ok != tc.wantOk {
|
||||
t.Errorf("GetErrorMessage: ok = %v, want %v", ok, tc.wantOk)
|
||||
t.Errorf("GetMessage: ok = %v, want %v", ok, tc.wantOk)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -46,9 +47,9 @@ func TestDefaultMsg(t *testing.T) {
|
||||
|
||||
t.Run("logger", func(t *testing.T) {
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
got := container.NewMsg(nil).GetLogger()
|
||||
got := message.NewMsg(nil).GetLogger()
|
||||
|
||||
if out := got.Writer().(*container.Suspendable).Downstream; out != log.Writer() {
|
||||
if out := got.Writer().(*message.Suspendable).Downstream; out != log.Writer() {
|
||||
t.Errorf("GetLogger: Downstream = %#v", out)
|
||||
}
|
||||
|
||||
@ -59,13 +60,13 @@ func TestDefaultMsg(t *testing.T) {
|
||||
|
||||
t.Run("takeover", func(t *testing.T) {
|
||||
l := log.New(io.Discard, "\x00", 0xdeadbeef)
|
||||
got := container.NewMsg(l)
|
||||
got := message.NewMsg(l)
|
||||
|
||||
if logger := got.GetLogger(); logger != l {
|
||||
t.Errorf("GetLogger: %#v, want %#v", logger, l)
|
||||
}
|
||||
|
||||
if ds := l.Writer().(*container.Suspendable).Downstream; ds != io.Discard {
|
||||
if ds := l.Writer().(*message.Suspendable).Downstream; ds != io.Discard {
|
||||
t.Errorf("GetLogger: Downstream = %#v", ds)
|
||||
}
|
||||
})
|
||||
@ -78,93 +79,93 @@ func TestDefaultMsg(t *testing.T) {
|
||||
pt, next []byte
|
||||
err error
|
||||
|
||||
f func(t *testing.T, msg container.Msg)
|
||||
f func(t *testing.T, msg message.Msg)
|
||||
}{
|
||||
{"zero verbose", nil, nil, nil, func(t *testing.T, msg container.Msg) {
|
||||
{"zero verbose", nil, nil, nil, func(t *testing.T, msg message.Msg) {
|
||||
if msg.IsVerbose() {
|
||||
t.Error("IsVerbose unexpected true")
|
||||
}
|
||||
}},
|
||||
|
||||
{"swap false", nil, nil, nil, func(t *testing.T, msg container.Msg) {
|
||||
{"swap false", nil, nil, nil, func(t *testing.T, msg message.Msg) {
|
||||
if msg.SwapVerbose(false) {
|
||||
t.Error("SwapVerbose unexpected true")
|
||||
}
|
||||
}},
|
||||
{"write discard", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
|
||||
{"write discard", nil, nil, nil, func(_ *testing.T, msg message.Msg) {
|
||||
msg.Verbose("\x00")
|
||||
msg.Verbosef("\x00")
|
||||
}},
|
||||
{"verbose false", nil, nil, nil, func(t *testing.T, msg container.Msg) {
|
||||
{"verbose false", nil, nil, nil, func(t *testing.T, msg message.Msg) {
|
||||
if msg.IsVerbose() {
|
||||
t.Error("IsVerbose unexpected true")
|
||||
}
|
||||
}},
|
||||
|
||||
{"swap true", nil, nil, nil, func(t *testing.T, msg container.Msg) {
|
||||
{"swap true", nil, nil, nil, func(t *testing.T, msg message.Msg) {
|
||||
if msg.SwapVerbose(true) {
|
||||
t.Error("SwapVerbose unexpected true")
|
||||
}
|
||||
}},
|
||||
{"write verbose", []byte("test: \x00\n"), nil, nil, func(_ *testing.T, msg container.Msg) {
|
||||
{"write verbose", []byte("test: \x00\n"), nil, nil, func(_ *testing.T, msg message.Msg) {
|
||||
msg.Verbose("\x00")
|
||||
}},
|
||||
{"write verbosef", []byte(`test: "\x00"` + "\n"), nil, nil, func(_ *testing.T, msg container.Msg) {
|
||||
{"write verbosef", []byte(`test: "\x00"` + "\n"), nil, nil, func(_ *testing.T, msg message.Msg) {
|
||||
msg.Verbosef("%q", "\x00")
|
||||
}},
|
||||
{"verbose true", nil, nil, nil, func(t *testing.T, msg container.Msg) {
|
||||
{"verbose true", nil, nil, nil, func(t *testing.T, msg message.Msg) {
|
||||
if !msg.IsVerbose() {
|
||||
t.Error("IsVerbose unexpected false")
|
||||
}
|
||||
}},
|
||||
|
||||
{"resume noop", nil, nil, nil, func(t *testing.T, msg container.Msg) {
|
||||
{"resume noop", nil, nil, nil, func(t *testing.T, msg message.Msg) {
|
||||
if msg.Resume() {
|
||||
t.Error("Resume unexpected success")
|
||||
}
|
||||
}},
|
||||
{"beforeExit noop", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
|
||||
{"beforeExit noop", nil, nil, nil, func(_ *testing.T, msg message.Msg) {
|
||||
msg.BeforeExit()
|
||||
}},
|
||||
|
||||
{"beforeExit suspend", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
|
||||
{"beforeExit suspend", nil, nil, nil, func(_ *testing.T, msg message.Msg) {
|
||||
msg.Suspend()
|
||||
}},
|
||||
{"beforeExit message", []byte("test: beforeExit reached on suspended output\n"), nil, nil, func(_ *testing.T, msg container.Msg) {
|
||||
{"beforeExit message", []byte("test: beforeExit reached on suspended output\n"), nil, nil, func(_ *testing.T, msg message.Msg) {
|
||||
msg.BeforeExit()
|
||||
}},
|
||||
{"post beforeExit resume noop", nil, nil, nil, func(t *testing.T, msg container.Msg) {
|
||||
{"post beforeExit resume noop", nil, nil, nil, func(t *testing.T, msg message.Msg) {
|
||||
if msg.Resume() {
|
||||
t.Error("Resume unexpected success")
|
||||
}
|
||||
}},
|
||||
|
||||
{"suspend", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
|
||||
{"suspend", nil, nil, nil, func(_ *testing.T, msg message.Msg) {
|
||||
msg.Suspend()
|
||||
}},
|
||||
{"suspend write", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
|
||||
{"suspend write", nil, nil, nil, func(_ *testing.T, msg message.Msg) {
|
||||
msg.GetLogger().Print("\x00")
|
||||
}},
|
||||
{"resume error", []byte("test: \x00\n"), []byte("test: cannot dump buffer on resume: unique error 0 injected by the test suite\n"), stub.UniqueError(0), func(t *testing.T, msg container.Msg) {
|
||||
{"resume error", []byte("test: \x00\n"), []byte("test: cannot dump buffer on resume: unique error 0 injected by the test suite\n"), stub.UniqueError(0), func(t *testing.T, msg message.Msg) {
|
||||
if !msg.Resume() {
|
||||
t.Error("Resume unexpected failure")
|
||||
}
|
||||
}},
|
||||
|
||||
{"suspend drop", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
|
||||
{"suspend drop", nil, nil, nil, func(_ *testing.T, msg message.Msg) {
|
||||
msg.Suspend()
|
||||
}},
|
||||
{"suspend write fill", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
|
||||
{"suspend write fill", nil, nil, nil, func(_ *testing.T, msg message.Msg) {
|
||||
msg.GetLogger().Print(strings.Repeat("\x00", suspendBufMax))
|
||||
}},
|
||||
{"resume dropped", append([]byte("test: "), bytes.Repeat([]byte{0}, suspendBufMax-6)...), []byte("test: dropped 7 bytes while output is suspended\n"), nil, func(t *testing.T, msg container.Msg) {
|
||||
{"resume dropped", append([]byte("test: "), bytes.Repeat([]byte{0}, suspendBufMax-6)...), []byte("test: dropped 7 bytes while output is suspended\n"), nil, func(t *testing.T, msg message.Msg) {
|
||||
if !msg.Resume() {
|
||||
t.Error("Resume unexpected failure")
|
||||
}
|
||||
}},
|
||||
}
|
||||
|
||||
msg := container.NewMsg(log.New(&dw, "test: ", 0))
|
||||
msg := message.NewMsg(log.New(&dw, "test: ", 0))
|
||||
for _, step := range steps {
|
||||
// these share the same writer, so cannot be subtests
|
||||
t.Logf("running step %q", step.name)
|
@ -1,4 +1,4 @@
|
||||
package container
|
||||
package message
|
||||
|
||||
import (
|
||||
"bytes"
|
@ -1,4 +1,4 @@
|
||||
package container_test
|
||||
package message_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -8,8 +8,8 @@ import (
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/stub"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func TestSuspendable(t *testing.T) {
|
||||
@ -74,7 +74,7 @@ func TestSuspendable(t *testing.T) {
|
||||
|
||||
var dw expectWriter
|
||||
|
||||
w := container.Suspendable{Downstream: &dw}
|
||||
w := message.Suspendable{Downstream: &dw}
|
||||
for _, step := range steps {
|
||||
// these share the same writer, so cannot be subtests
|
||||
t.Logf("writing step %q", step.name)
|
@ -11,8 +11,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/helper"
|
||||
"hakurei.app/message"
|
||||
"hakurei.app/system/dbus"
|
||||
)
|
||||
|
||||
@ -93,9 +93,9 @@ func testProxyFinaliseStartWaitCloseString(t *testing.T, useSandbox bool) {
|
||||
|
||||
t.Run("invalid start", func(t *testing.T) {
|
||||
if !useSandbox {
|
||||
p = dbus.NewDirect(t.Context(), container.NewMsg(nil), nil, nil)
|
||||
p = dbus.NewDirect(t.Context(), message.NewMsg(nil), nil, nil)
|
||||
} else {
|
||||
p = dbus.New(t.Context(), container.NewMsg(nil), nil, nil)
|
||||
p = dbus.New(t.Context(), message.NewMsg(nil), nil, nil)
|
||||
}
|
||||
|
||||
if err := p.Start(); !errors.Is(err, syscall.ENOTRECOVERABLE) {
|
||||
@ -128,9 +128,9 @@ func testProxyFinaliseStartWaitCloseString(t *testing.T, useSandbox bool) {
|
||||
defer cancel()
|
||||
output := new(strings.Builder)
|
||||
if !useSandbox {
|
||||
p = dbus.NewDirect(ctx, container.NewMsg(nil), final, output)
|
||||
p = dbus.NewDirect(ctx, message.NewMsg(nil), final, output)
|
||||
} else {
|
||||
p = dbus.New(ctx, container.NewMsg(nil), final, output)
|
||||
p = dbus.New(ctx, message.NewMsg(nil), final, output)
|
||||
}
|
||||
|
||||
t.Run("invalid wait", func(t *testing.T) {
|
||||
|
@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
// NewDirect returns a new instance of [Proxy] with its sandbox disabled.
|
||||
func NewDirect(ctx context.Context, msg container.Msg, final *Final, output io.Writer) *Proxy {
|
||||
func NewDirect(ctx context.Context, msg message.Msg, final *Final, output io.Writer) *Proxy {
|
||||
p := New(ctx, msg, final, output)
|
||||
p.useSandbox = false
|
||||
return p
|
||||
|
@ -6,9 +6,9 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/helper"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
// ProxyName is the file name or path to the proxy program.
|
||||
@ -19,7 +19,7 @@ var ProxyName = "xdg-dbus-proxy"
|
||||
type Proxy struct {
|
||||
helper helper.Helper
|
||||
ctx context.Context
|
||||
msg container.Msg
|
||||
msg message.Msg
|
||||
|
||||
cancel context.CancelCauseFunc
|
||||
cause func() error
|
||||
@ -100,6 +100,6 @@ func Finalise(sessionBus, systemBus ProxyPair, session, system *hst.BusConfig) (
|
||||
}
|
||||
|
||||
// New returns a new instance of [Proxy].
|
||||
func New(ctx context.Context, msg container.Msg, final *Final, output io.Writer) *Proxy {
|
||||
func New(ctx context.Context, msg message.Msg, final *Final, output io.Writer) *Proxy {
|
||||
return &Proxy{name: ProxyName, ctx: ctx, msg: msg, final: final, output: output, useSandbox: true}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
// OpError is returned by [I.Commit] and [I.Revert].
|
||||
@ -70,14 +71,14 @@ func printJoinedError(println func(v ...any), fallback string, err error) {
|
||||
error
|
||||
}
|
||||
if !errors.As(err, &joinErr) {
|
||||
if m, ok := container.GetErrorMessage(err); ok {
|
||||
if m, ok := message.GetMessage(err); ok {
|
||||
println(m)
|
||||
} else {
|
||||
println(fallback, err)
|
||||
}
|
||||
} else {
|
||||
for _, err = range joinErr.Unwrap() {
|
||||
if m, ok := container.GetErrorMessage(err); ok {
|
||||
if m, ok := message.GetMessage(err); ok {
|
||||
println(m)
|
||||
} else {
|
||||
println(err.Error())
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
func TestOpError(t *testing.T) {
|
||||
@ -64,13 +65,13 @@ func TestOpError(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("msg", func(t *testing.T) {
|
||||
if got, ok := container.GetErrorMessage(tc.err); !ok {
|
||||
if got, ok := message.GetMessage(tc.err); !ok {
|
||||
if tc.msg != "" {
|
||||
t.Errorf("GetErrorMessage: err does not implement MessageError")
|
||||
t.Errorf("GetMessage: err does not implement MessageError")
|
||||
}
|
||||
return
|
||||
} else if got != tc.msg {
|
||||
t.Errorf("GetErrorMessage: %q, want %q", got, tc.msg)
|
||||
t.Errorf("GetMessage: %q, want %q", got, tc.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -6,8 +6,8 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -68,7 +68,7 @@ func TypeString(e hst.Enablement) string {
|
||||
}
|
||||
|
||||
// New returns the address of a new [I] targeting uid.
|
||||
func New(ctx context.Context, msg container.Msg, uid int) (sys *I) {
|
||||
func New(ctx context.Context, msg message.Msg, uid int) (sys *I) {
|
||||
if ctx == nil || msg == nil || uid < 0 {
|
||||
panic("invalid call to New")
|
||||
}
|
||||
@ -89,7 +89,7 @@ type I struct {
|
||||
// the behaviour of Revert is only defined for up to one call
|
||||
reverted bool
|
||||
|
||||
msg container.Msg
|
||||
msg message.Msg
|
||||
syscallDispatcher
|
||||
}
|
||||
|
||||
|
@ -8,10 +8,10 @@ import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/container/stub"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/message"
|
||||
"hakurei.app/system/internal/xcb"
|
||||
)
|
||||
|
||||
@ -74,7 +74,7 @@ func TestNew(t *testing.T) {
|
||||
t.Errorf("recover: %v, want %v", r, want)
|
||||
}
|
||||
}()
|
||||
New(nil, container.NewMsg(nil), 0)
|
||||
New(nil, message.NewMsg(nil), 0)
|
||||
})
|
||||
|
||||
t.Run("msg", func(t *testing.T) {
|
||||
@ -94,11 +94,11 @@ func TestNew(t *testing.T) {
|
||||
t.Errorf("recover: %v, want %v", r, want)
|
||||
}
|
||||
}()
|
||||
New(t.Context(), container.NewMsg(nil), -1)
|
||||
New(t.Context(), message.NewMsg(nil), -1)
|
||||
})
|
||||
})
|
||||
|
||||
sys := New(t.Context(), container.NewMsg(nil), 0xdeadbeef)
|
||||
sys := New(t.Context(), message.NewMsg(nil), 0xdeadbeef)
|
||||
if sys.ctx == nil {
|
||||
t.Error("New: ctx = nil")
|
||||
}
|
||||
@ -115,51 +115,51 @@ func TestEqual(t *testing.T) {
|
||||
want bool
|
||||
}{
|
||||
{"simple UID",
|
||||
New(t.Context(), container.NewMsg(nil), 150),
|
||||
New(t.Context(), container.NewMsg(nil), 150),
|
||||
New(t.Context(), message.NewMsg(nil), 150),
|
||||
New(t.Context(), message.NewMsg(nil), 150),
|
||||
true},
|
||||
|
||||
{"simple UID differ",
|
||||
New(t.Context(), container.NewMsg(nil), 150),
|
||||
New(t.Context(), container.NewMsg(nil), 151),
|
||||
New(t.Context(), message.NewMsg(nil), 150),
|
||||
New(t.Context(), message.NewMsg(nil), 151),
|
||||
false},
|
||||
|
||||
{"simple UID nil",
|
||||
New(t.Context(), container.NewMsg(nil), 150),
|
||||
New(t.Context(), message.NewMsg(nil), 150),
|
||||
nil,
|
||||
false},
|
||||
|
||||
{"op length mismatch",
|
||||
New(t.Context(), container.NewMsg(nil), 150).
|
||||
New(t.Context(), message.NewMsg(nil), 150).
|
||||
ChangeHosts("chronos"),
|
||||
New(t.Context(), container.NewMsg(nil), 150).
|
||||
New(t.Context(), message.NewMsg(nil), 150).
|
||||
ChangeHosts("chronos").
|
||||
Ensure(m("/run"), 0755),
|
||||
false},
|
||||
|
||||
{"op value mismatch",
|
||||
New(t.Context(), container.NewMsg(nil), 150).
|
||||
New(t.Context(), message.NewMsg(nil), 150).
|
||||
ChangeHosts("chronos").
|
||||
Ensure(m("/run"), 0644),
|
||||
New(t.Context(), container.NewMsg(nil), 150).
|
||||
New(t.Context(), message.NewMsg(nil), 150).
|
||||
ChangeHosts("chronos").
|
||||
Ensure(m("/run"), 0755),
|
||||
false},
|
||||
|
||||
{"op type mismatch",
|
||||
New(t.Context(), container.NewMsg(nil), 150).
|
||||
New(t.Context(), message.NewMsg(nil), 150).
|
||||
ChangeHosts("chronos").
|
||||
CopyFile(new([]byte), m("/home/ophestra/xdg/config/pulse/cookie"), 0, 256),
|
||||
New(t.Context(), container.NewMsg(nil), 150).
|
||||
New(t.Context(), message.NewMsg(nil), 150).
|
||||
ChangeHosts("chronos").
|
||||
Ensure(m("/run"), 0755),
|
||||
false},
|
||||
|
||||
{"op equals",
|
||||
New(t.Context(), container.NewMsg(nil), 150).
|
||||
New(t.Context(), message.NewMsg(nil), 150).
|
||||
ChangeHosts("chronos").
|
||||
Ensure(m("/run"), 0755),
|
||||
New(t.Context(), container.NewMsg(nil), 150).
|
||||
New(t.Context(), message.NewMsg(nil), 150).
|
||||
ChangeHosts("chronos").
|
||||
Ensure(m("/run"), 0755),
|
||||
true},
|
||||
|
Loading…
x
Reference in New Issue
Block a user